mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
Added restart and shutdown buttons
This commit is contained in:
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@@ -1,11 +1,19 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Current File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "Main File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"python": "/bin/python3",
|
||||
"program": "${workspaceFolder}/MIND.py"
|
||||
"program": "${workspaceFolder}/MIND.py",
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
11
MIND.py
11
MIND.py
@@ -6,9 +6,10 @@ The main file where MIND is started from
|
||||
"""
|
||||
|
||||
import logging
|
||||
from os import makedirs, urandom
|
||||
from os import execv, makedirs, urandom
|
||||
from os.path import dirname, isfile
|
||||
from shutil import move
|
||||
from sys import argv
|
||||
|
||||
from flask import Flask, render_template, request
|
||||
from waitress.server import create_server
|
||||
@@ -17,7 +18,8 @@ from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
||||
from backend.db import DBConnection, ThreadedTaskDispatcher, close_db, setup_db
|
||||
from backend.helpers import check_python_version, folder_path
|
||||
from backend.reminders import ReminderHandler
|
||||
from frontend.api import admin_api, admin_api_prefix, api, api_prefix
|
||||
from frontend.api import (APIVariables, admin_api, admin_api_prefix, api,
|
||||
api_prefix)
|
||||
from frontend.ui import UIVariables, ui
|
||||
|
||||
HOST = '0.0.0.0'
|
||||
@@ -116,6 +118,7 @@ def MIND() -> None:
|
||||
port=PORT,
|
||||
threads=THREADS
|
||||
)
|
||||
APIVariables.server_instance = server
|
||||
logging.info(f'MIND running on http://{HOST}:{PORT}{URL_PREFIX}')
|
||||
# =================
|
||||
server.run()
|
||||
@@ -123,6 +126,10 @@ def MIND() -> None:
|
||||
|
||||
reminder_handler.stop_handling()
|
||||
|
||||
if APIVariables.restart:
|
||||
logging.info('Restarting MIND')
|
||||
execv(__file__, argv)
|
||||
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -23,8 +23,7 @@ class DB_Singleton(type):
|
||||
def __call__(cls, *args, **kwargs):
|
||||
i = f'{cls}{current_thread()}'
|
||||
if (i not in cls._instances
|
||||
or cls._instances[i].closed):
|
||||
logging.debug(f'Creating singleton instance: {i}')
|
||||
or cls._instances[i].closed):
|
||||
cls._instances[i] = super(DB_Singleton, cls).__call__(*args, **kwargs)
|
||||
|
||||
return cls._instances[i]
|
||||
@@ -34,33 +33,34 @@ class ThreadedTaskDispatcher(OldThreadedTaskDispatcher):
|
||||
super().handler_thread(thread_no)
|
||||
i = f'{DBConnection}{current_thread()}'
|
||||
if i in DB_Singleton._instances and not DB_Singleton._instances[i].closed:
|
||||
logging.debug(f'Closing singleton instance: {i}')
|
||||
DB_Singleton._instances[i].close()
|
||||
return
|
||||
|
||||
def shutdown(self, cancel_pending: bool = True, timeout: int = 5) -> bool:
|
||||
print()
|
||||
logging.info('Shutting down MIND...')
|
||||
super().shutdown(cancel_pending, timeout)
|
||||
DBConnection(20.0).close()
|
||||
return
|
||||
logging.info('Shutting down MIND')
|
||||
result = super().shutdown(cancel_pending, timeout)
|
||||
return result
|
||||
|
||||
class DBConnection(Connection, metaclass=DB_Singleton):
|
||||
file = ''
|
||||
|
||||
def __init__(self, timeout: float) -> None:
|
||||
logging.debug(f'Opening database connection for {current_thread()}')
|
||||
logging.debug(f'Creating connection {self}')
|
||||
super().__init__(self.file, timeout=timeout)
|
||||
super().cursor().execute("PRAGMA foreign_keys = ON;")
|
||||
self.closed = False
|
||||
return
|
||||
|
||||
def close(self) -> None:
|
||||
logging.debug(f'Closing database connection for {current_thread()}')
|
||||
logging.debug(f'Closing connection {self}')
|
||||
self.closed = True
|
||||
super().close()
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<{self.__class__.__name__}; {current_thread().name}; {id(self)}>'
|
||||
|
||||
def get_db(output_type: Union[Type[dict], Type[tuple]]=tuple):
|
||||
"""Get a database cursor instance. Coupled to Flask's g.
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import logging
|
||||
from dataclasses import dataclass
|
||||
from io import BytesIO
|
||||
from os import urandom
|
||||
from threading import Timer
|
||||
from time import time as epoch_time
|
||||
from typing import Any, Callable, Dict, Tuple
|
||||
from typing import Any, Callable, Dict, Tuple, Union
|
||||
|
||||
from flask import g, request, send_file
|
||||
from waitress.server import BaseWSGIServer
|
||||
|
||||
from backend.custom_exceptions import (AccessUnauthorized, APIKeyExpired,
|
||||
APIKeyInvalid, InvalidKeyValue,
|
||||
@@ -45,6 +47,19 @@ from frontend.input_validation import (AllowNewAccountsVariable, ColorVariable,
|
||||
# General variables and functions
|
||||
#===================
|
||||
|
||||
class APIVariables:
|
||||
server_instance: Union[BaseWSGIServer, None] = None
|
||||
restart: bool = False
|
||||
|
||||
def shutdown_server() -> None:
|
||||
APIVariables.server_instance.close()
|
||||
APIVariables.server_instance.task_dispatcher.shutdown()
|
||||
APIVariables.server_instance._map.clear()
|
||||
return
|
||||
|
||||
shutdown_server_thread = Timer(1.0, shutdown_server)
|
||||
shutdown_server_thread.name = "InternalStateHandler"
|
||||
|
||||
@dataclass
|
||||
class ApiKeyEntry:
|
||||
exp: int
|
||||
@@ -564,6 +579,27 @@ def api_get_static_reminder(inputs: Dict[str, Any], s_id: int):
|
||||
# Admin panel endpoints
|
||||
#===================
|
||||
|
||||
@admin_api.route(
|
||||
'/shutdown',
|
||||
'Shut down the application',
|
||||
methods=['POST']
|
||||
)
|
||||
@endpoint_wrapper
|
||||
def api_shutdown():
|
||||
shutdown_server_thread.start()
|
||||
return return_api({})
|
||||
|
||||
@admin_api.route(
|
||||
'/restart',
|
||||
'Restart the application',
|
||||
methods=['POST']
|
||||
)
|
||||
@endpoint_wrapper
|
||||
def api_restart():
|
||||
APIVariables.restart = True
|
||||
shutdown_server_thread.start()
|
||||
return return_api({})
|
||||
|
||||
@api.route(
|
||||
'/settings',
|
||||
'Get the admin settings',
|
||||
|
||||
@@ -145,16 +145,20 @@ h2 {
|
||||
|
||||
.add-user-container,
|
||||
.database-container {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#add-user-button,
|
||||
#download-db-button {
|
||||
margin-top: 2rem;
|
||||
#download-db-button,
|
||||
#restart-button,
|
||||
#shutdown-button {
|
||||
|
||||
width: min(15rem, 100%);
|
||||
height: 2rem;
|
||||
@@ -166,7 +170,9 @@ h2 {
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
#download-db-button {
|
||||
#download-db-button,
|
||||
#restart-button,
|
||||
#shutdown-button {
|
||||
height: unset;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ const user_inputs = {
|
||||
password: document.querySelector('#new-password-input')
|
||||
};
|
||||
|
||||
const power_buttons = {
|
||||
restart: document.querySelector('#restart-button'),
|
||||
shutdown: document.querySelector('#shutdown-button')
|
||||
};
|
||||
|
||||
function checkLogin() {
|
||||
fetch(`${url_prefix}/api/auth/status?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
@@ -175,6 +180,32 @@ function loadUsers() {
|
||||
});
|
||||
};
|
||||
|
||||
function restart_app() {
|
||||
power_buttons.restart.innerText = 'Restarting...';
|
||||
fetch(`${url_prefix}/api/admin/restart?api_key=${api_key}`, {
|
||||
method: "POST"
|
||||
})
|
||||
.then(response =>
|
||||
setTimeout(
|
||||
() => window.location.reload(),
|
||||
1000
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
function shutdown_app() {
|
||||
power_buttons.shutdown.innerText = 'Shutting down...';
|
||||
fetch(`${url_prefix}/api/admin/shutdown?api_key=${api_key}`, {
|
||||
method: "POST"
|
||||
})
|
||||
.then(response =>
|
||||
setTimeout(
|
||||
() => window.location.reload(),
|
||||
1000
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// code run on load
|
||||
|
||||
checkLogin();
|
||||
@@ -187,3 +218,5 @@ document.querySelector('#add-user-button').onclick = e => toggleAddUser();
|
||||
document.querySelector('#add-user-form').action = 'javascript:addUser()';
|
||||
document.querySelector('#download-db-button').onclick = e =>
|
||||
window.location.href = `${url_prefix}/api/admin/database?api_key=${api_key}`;
|
||||
power_buttons.restart.onclick = e => restart_app();
|
||||
power_buttons.shutdown.onclick = e => shutdown_app();
|
||||
|
||||
@@ -123,6 +123,11 @@
|
||||
<div class="database-container">
|
||||
<button id="download-db-button">Download Database</button>
|
||||
</div>
|
||||
<h2>Power</h2>
|
||||
<div class="database-container">
|
||||
<button id="restart-button">Restart</button>
|
||||
<button id="shutdown-button">Shutdown</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user