mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
23
MIND.py
23
MIND.py
@@ -1,13 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from os import makedirs, urandom
|
||||
from os.path import abspath, dirname, join, isfile
|
||||
from os.path import abspath, dirname, isfile, join
|
||||
from shutil import move
|
||||
from sys import version_info
|
||||
|
||||
from flask import Flask, render_template, request
|
||||
from flask import Flask
|
||||
from waitress.server import create_server
|
||||
from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
||||
|
||||
from backend.db import DBConnection, close_db, setup_db
|
||||
from frontend.api import api, reminder_handler
|
||||
@@ -15,6 +17,7 @@ from frontend.ui import ui
|
||||
|
||||
HOST = '0.0.0.0'
|
||||
PORT = '8080'
|
||||
URL_PREFIX = '' # Must either be empty or start with '/' e.g. '/mind'
|
||||
THREADS = 10
|
||||
DB_FILENAME = 'db', 'MIND.db'
|
||||
|
||||
@@ -39,15 +42,10 @@ def _create_app() -> Flask:
|
||||
app.config['SECRET_KEY'] = urandom(32)
|
||||
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
|
||||
app.config['JSON_SORT_KEYS'] = False
|
||||
app.config['APPLICATION_ROOT'] = URL_PREFIX
|
||||
app.wsgi_app = DispatcherMiddleware(Flask(__name__), {URL_PREFIX: app.wsgi_app})
|
||||
|
||||
# Add error handlers
|
||||
@app.errorhandler(404)
|
||||
def not_found(e):
|
||||
if request.path.startswith('/api'):
|
||||
return {'error': 'Not Found', 'result': {}}, 404
|
||||
else:
|
||||
return render_template('page_not_found.html')
|
||||
|
||||
@app.errorhandler(400)
|
||||
def bad_request(e):
|
||||
return {'error': 'Bad request', 'result': {}}, 400
|
||||
@@ -76,8 +74,13 @@ def MIND() -> None:
|
||||
# Check python version
|
||||
if (version_info.major < 3) or (version_info.major == 3 and version_info.minor < 7):
|
||||
print('Error: the minimum python version required is python3.7 (currently ' + version_info.major + '.' + version_info.minor + '.' + version_info.micro + ')')
|
||||
exit(1)
|
||||
|
||||
# Register web server
|
||||
# We need to get the value to ui.py but MIND.py imports from ui.py so we get an import loop.
|
||||
# To go around this, we abuse the fact that the logging module is a singleton.
|
||||
# We add an attribute to the logging module and in ui.py get the value this way.
|
||||
logging.URL_PREFIX = URL_PREFIX
|
||||
app = _create_app()
|
||||
with app.app_context():
|
||||
if isfile(_folder_path('db', 'Noted.db')):
|
||||
@@ -93,7 +96,7 @@ def MIND() -> None:
|
||||
|
||||
# Create waitress server and run
|
||||
server = create_server(app, host=HOST, port=PORT, threads=THREADS)
|
||||
print(f'MIND running on http://{HOST}:{PORT}/')
|
||||
print(f'MIND running on http://{HOST}:{PORT}{URL_PREFIX}')
|
||||
server.run()
|
||||
print(f'\nShutting down MIND...')
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import Union
|
||||
|
||||
from flask import g
|
||||
|
||||
__DATABASE_VERSION__ = 3
|
||||
__DATABASE_VERSION__ = 4
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
@@ -90,6 +90,15 @@ def migrate_db(current_db_version: int) -> None:
|
||||
ADD color VARCHAR(7);
|
||||
""")
|
||||
current_db_version = 3
|
||||
|
||||
if current_db_version == 3:
|
||||
# V3 -> V4
|
||||
cursor.executescript("""
|
||||
UPDATE reminders
|
||||
SET repeat_quantity = repeat_quantity || 's'
|
||||
WHERE repeat_quantity NOT LIKE '%s';
|
||||
""")
|
||||
current_db_version = 4
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from datetime import datetime
|
||||
from sqlite3 import IntegrityError
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
from time import time as epoch_time
|
||||
from typing import List, Literal
|
||||
|
||||
from apprise import Apprise
|
||||
@@ -24,7 +23,7 @@ filter_function = lambda query, p: (
|
||||
|
||||
def _find_next_time(
|
||||
original_time: int,
|
||||
repeat_quantity: Literal["year", "month", "week", "day", "hours", "minutes"],
|
||||
repeat_quantity: Literal["years", "months", "weeks", "days", "hours", "minutes"],
|
||||
repeat_interval: int
|
||||
) -> int:
|
||||
td = relativedelta(**{repeat_quantity: repeat_interval})
|
||||
@@ -176,7 +175,7 @@ class Reminder:
|
||||
time: int = None,
|
||||
notification_service: int = None,
|
||||
text: str = None,
|
||||
repeat_quantity: Literal["year", "month", "week", "day", "hours", "minutes"] = None,
|
||||
repeat_quantity: Literal["years", "months", "weeks", "days", "hours", "minutes"] = None,
|
||||
repeat_interval: int = None,
|
||||
color: str = None
|
||||
) -> dict:
|
||||
@@ -187,7 +186,7 @@ class Reminder:
|
||||
time (int): The new UTC epoch timestamp the the reminder should be send. Defaults to None.
|
||||
notification_service (int): The new id of the notification service to use to send the reminder. Defaults to None.
|
||||
text (str, optional): The new body of the reminder. Defaults to None.
|
||||
repeat_quantity (Literal["year", "month", "week", "day", "hours", "minutes"], optional): The new quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_quantity (Literal["years", "months", "weeks", "days", "hours", "minutes"], optional): The new quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_interval (int, optional): The new amount of repeat_quantity, like "5" (hours). Defaults to None.
|
||||
color (str, optional): The new hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
@@ -358,7 +357,7 @@ class Reminders:
|
||||
time: int,
|
||||
notification_service: int,
|
||||
text: str = '',
|
||||
repeat_quantity: Literal["year", "month", "week", "day", "hours", "minutes"] = None,
|
||||
repeat_quantity: Literal["years", "months", "weeks", "days", "hours", "minutes"] = None,
|
||||
repeat_interval: int = None,
|
||||
color: str = None
|
||||
) -> Reminder:
|
||||
@@ -369,7 +368,7 @@ class Reminders:
|
||||
time (int): The UTC epoch timestamp the the reminder should be send.
|
||||
notification_service (int): The id of the notification service to use to send the reminder.
|
||||
text (str, optional): The body of the reminder. Defaults to ''.
|
||||
repeat_quantity (Literal["year", "month", "week", "day", "hours", "minutes"], optional): The quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_quantity (Literal["years", "months", "weeks", "days", "hours", "minutes"], optional): The quantity of the repeat specified for the reminder. Defaults to None.
|
||||
repeat_interval (int, optional): The amount of repeat_quantity, like "5" (hours). Defaults to None.
|
||||
color (str, optional): The hex code of the color of the reminder, which is shown in the web-ui. Defaults to None.
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ def extract_key(values: dict, key: str, check_existence: bool=True) -> Any:
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key == 'repeat_quantity':
|
||||
if not value in ("year", "month", "week", "day", "hours", "minutes"):
|
||||
if not value in ("years", "months", "weeks", "days", "hours", "minutes"):
|
||||
raise InvalidKeyValue(key, value)
|
||||
|
||||
elif key in ('username', 'password', 'new_password', 'title', 'url',
|
||||
@@ -120,6 +120,10 @@ def extract_key(values: dict, key: str, check_existence: bool=True) -> Any:
|
||||
|
||||
return value
|
||||
|
||||
@api.errorhandler(404)
|
||||
def not_found(e):
|
||||
return return_api({}, 'Not Found', 404)
|
||||
|
||||
#===================
|
||||
# Authentication endpoints
|
||||
#===================
|
||||
@@ -410,7 +414,7 @@ def api_reminders_list():
|
||||
time (required): the UTC epoch timestamp that the reminder should be sent at
|
||||
notification_service (required): the id of the notification service to use to send the notification
|
||||
text: the body of the reminder
|
||||
repeat_quantity ('year', 'month', 'week', 'day', 'hours', 'minutes'): The quantity of the repeat_interval
|
||||
repeat_quantity ('years', 'months', 'weeks', 'days', 'hours', 'minutes'): The quantity of the repeat_interval
|
||||
repeat_interval: The number of the interval
|
||||
color: The hex code of the color of the reminder, which is shown in the web-ui
|
||||
Returns:
|
||||
@@ -523,7 +527,7 @@ def api_get_reminder(r_id: int):
|
||||
time: The new UTC epoch timestamp the the reminder should be send.
|
||||
notification_service: The new id of the notification service to use to send the reminder.
|
||||
text: The new body of the reminder.
|
||||
repeat_quantity ('year', 'month', 'week', 'day', 'hours', 'minutes'): The new quantity of the repeat_interval.
|
||||
repeat_quantity ('years', 'months', 'weeks', 'days', 'hours', 'minutes'): The new quantity of the repeat_interval.
|
||||
repeat_interval: The new number of the interval.
|
||||
color: The new hex code of the color of the reminder, which is shown in the web-ui.
|
||||
Returns:
|
||||
|
||||
@@ -35,7 +35,7 @@ function addReminder() {
|
||||
data['repeat_interval'] = type_buttons['repeat-interval'].value
|
||||
};
|
||||
|
||||
fetch(`/api/reminders?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/reminders?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -52,7 +52,7 @@ function addReminder() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e === 400) {
|
||||
inputs.time.classList.add('error-input');
|
||||
inputs.time.title = 'Time is in the past';
|
||||
@@ -144,7 +144,7 @@ function testReminder() {
|
||||
'notification_service': inputs.notification_service.value,
|
||||
'text': inputs.text.value
|
||||
};
|
||||
fetch(`/api/reminders/test?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/reminders/test?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -158,7 +158,7 @@ function testReminder() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ function editReminder() {
|
||||
data['repeat_interval'] = edit_type_buttons['repeat-edit-interval'].value;
|
||||
};
|
||||
|
||||
fetch(`/api/reminders/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/reminders/${id}?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -51,7 +51,7 @@ function editReminder() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -60,7 +60,7 @@ function editReminder() {
|
||||
|
||||
function showEdit(id) {
|
||||
document.getElementById('edit-form').dataset.id = id;
|
||||
fetch(`/api/reminders/${id}?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/reminders/${id}?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -99,7 +99,7 @@ function showEdit(id) {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e === 404) {
|
||||
fillList();
|
||||
} else {
|
||||
@@ -127,7 +127,7 @@ function toggleEditRepeated() {
|
||||
|
||||
function deleteReminder() {
|
||||
const id = document.getElementById('edit-form').dataset.id;
|
||||
fetch(`/api/reminders/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/reminders/${id}?api_key=${api_key}`, {
|
||||
'method': 'DELETE'
|
||||
})
|
||||
.then(response => {
|
||||
@@ -142,7 +142,7 @@ function deleteReminder() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e === 404) {
|
||||
fillList();
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
function logout() {
|
||||
fetch(`/api/auth/logout?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/auth/logout?api_key=${api_key}`, {
|
||||
'method': 'POST'
|
||||
})
|
||||
.then(response => {
|
||||
sessionStorage.removeItem('api_key');
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix || '/';
|
||||
});
|
||||
};
|
||||
|
||||
@@ -49,9 +49,10 @@ function showTab(tab_id, button_id, load_function=null) {
|
||||
|
||||
// code run on load
|
||||
|
||||
const url_prefix = document.getElementById('url_prefix').dataset.value;
|
||||
const api_key = sessionStorage.getItem('api_key');
|
||||
if (api_key === null) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
};
|
||||
|
||||
document.getElementById('toggle-nav').addEventListener('click', e => toggleNav());
|
||||
|
||||
@@ -10,7 +10,7 @@ function login(data=null) {
|
||||
'password': document.getElementById('password-input').value
|
||||
};
|
||||
};
|
||||
fetch(`/api/auth/login`, {
|
||||
fetch(`${url_prefix}/api/auth/login`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -25,7 +25,7 @@ function login(data=null) {
|
||||
})
|
||||
.then(json => {
|
||||
sessionStorage.setItem('api_key', json.result.api_key);
|
||||
window.location.href = '/reminders';
|
||||
window.location.href = `${url_prefix}/reminders`;
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
@@ -49,7 +49,7 @@ function create() {
|
||||
'username': document.getElementById('new-username-input').value,
|
||||
'password': document.getElementById('new-password-input').value
|
||||
};
|
||||
fetch(`/api/user/add`, {
|
||||
fetch(`${url_prefix}/api/user/add`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -81,6 +81,8 @@ function toggleWindow() {
|
||||
|
||||
// code run on load
|
||||
|
||||
const url_prefix = document.getElementById('url_prefix').dataset.value;
|
||||
|
||||
document.getElementById('login-form').setAttribute('action', 'javascript:login();');
|
||||
document.getElementById('create-form').setAttribute('action', 'javascript:create();');
|
||||
document.querySelectorAll('.switch-button').forEach(e => e.addEventListener('click', e => toggleWindow()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
function fillNotificationSelection() {
|
||||
fetch(`/api/notificationservices?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/notificationservices?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -87,7 +87,7 @@ function fillNotificationSelection() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -96,7 +96,7 @@ function fillNotificationSelection() {
|
||||
|
||||
function deleteService(id) {
|
||||
const row = document.querySelector(`tr[data-id="${id}"]`);
|
||||
fetch(`/api/notificationservices/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/notificationservices/${id}?api_key=${api_key}`, {
|
||||
'method': 'DELETE'
|
||||
})
|
||||
.then(response => response.json())
|
||||
@@ -113,7 +113,7 @@ function deleteService(id) {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e.error === 'ApiKeyExpired' || e.error === 'ApiKeyInvalid') {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e.error === 'NotificationServiceInUse') {
|
||||
const delete_button = row.querySelector('button[title="Delete"]');
|
||||
delete_button.classList.add('error-icon');
|
||||
@@ -136,7 +136,7 @@ function saveService(id) {
|
||||
'title': row.querySelector(`td.title-column > input`).value,
|
||||
'url': row.querySelector(`td.url-column > input`).value
|
||||
};
|
||||
fetch(`/api/notificationservices/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/notificationservices/${id}?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -151,7 +151,7 @@ function saveService(id) {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e === 400) {
|
||||
save_button.classList.add('error-icon');
|
||||
save_button.title = 'Invalid Apprise URL';
|
||||
@@ -176,7 +176,7 @@ function addService() {
|
||||
'title': inputs_buttons.title.value,
|
||||
'url': inputs_buttons.url.value
|
||||
};
|
||||
fetch(`/api/notificationservices?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/notificationservices?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -199,7 +199,7 @@ function addService() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else if (e === 400) {
|
||||
inputs_buttons.save_button.classList.add('error-icon');
|
||||
inputs_buttons.save_button.title = 'Invalid Apprise URL';
|
||||
|
||||
@@ -42,7 +42,7 @@ function fillTable(result) {
|
||||
};
|
||||
|
||||
function fillList() {
|
||||
fetch(`/api/reminders?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/reminders?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -55,7 +55,7 @@ function fillList() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -64,7 +64,7 @@ function fillList() {
|
||||
|
||||
function search() {
|
||||
const query = document.getElementById('search-input').value;
|
||||
fetch(`/api/reminders/search?api_key=${api_key}&query=${query}`)
|
||||
fetch(`${url_prefix}/api/reminders/search?api_key=${api_key}&query=${query}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -77,7 +77,7 @@ function search() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ function changePassword() {
|
||||
const data = {
|
||||
'new_password': document.getElementById('password-input').value
|
||||
};
|
||||
fetch(`/api/user?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/user?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -16,7 +16,7 @@ function changePassword() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -24,11 +24,11 @@ function changePassword() {
|
||||
};
|
||||
|
||||
function deleteAccount() {
|
||||
fetch(`/api/user?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/user?api_key=${api_key}`, {
|
||||
'method': 'DELETE'
|
||||
})
|
||||
.then(response => {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ function loadTemplates(force=true) {
|
||||
return
|
||||
};
|
||||
|
||||
fetch(`/api/templates?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/templates?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -57,7 +57,7 @@ function loadTemplates(force=true) {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -74,7 +74,7 @@ function loadTemplate() {
|
||||
toggleColor(inputs.color);
|
||||
};
|
||||
} else {
|
||||
fetch(`/api/templates/${id}?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -99,7 +99,7 @@ function loadTemplate() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -117,7 +117,7 @@ function addTemplate() {
|
||||
if (!template_inputs.color.classList.contains('hidden')) {
|
||||
data['color'] = template_inputs.color.querySelector('button[data-selected="true"]').dataset.color;
|
||||
};
|
||||
fetch(`/api/templates?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/templates?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -134,7 +134,7 @@ function addTemplate() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -154,7 +154,7 @@ function closeAddTemplate() {
|
||||
};
|
||||
|
||||
function showEditTemplate(id) {
|
||||
fetch(`/api/templates/${id}?api_key=${api_key}`)
|
||||
fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
// catch errors
|
||||
if (!response.ok) {
|
||||
@@ -177,7 +177,7 @@ function showEditTemplate(id) {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -195,7 +195,7 @@ function saveTemplate() {
|
||||
if (!edit_template_inputs.color.classList.contains('hidden')) {
|
||||
data['color'] = edit_template_inputs.color.querySelector('button[data-selected="true"]').dataset.color;
|
||||
};
|
||||
fetch(`/api/templates/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
@@ -210,7 +210,7 @@ function saveTemplate() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -219,7 +219,7 @@ function saveTemplate() {
|
||||
|
||||
function deleteTemplate() {
|
||||
const id = document.getElementById('template-edit-form').dataset.id;
|
||||
fetch(`/api/templates/${id}?api_key=${api_key}`, {
|
||||
fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`, {
|
||||
'method': 'DELETE'
|
||||
})
|
||||
.then(response => {
|
||||
@@ -233,7 +233,7 @@ function deleteTemplate() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
window.location.href = '/';
|
||||
window.location.href = url_prefix;
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta id="url_prefix" data-value="{{url_prefix}}">
|
||||
|
||||
<link rel="stylesheet" href="/static/css/general.css">
|
||||
<link rel="stylesheet" href="/static/css/login.css">
|
||||
<script src="/static/js/login.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/general.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/login.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/login.js') }}" defer></script>
|
||||
|
||||
<title>Login - MIND</title>
|
||||
</head>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<main>
|
||||
<h2>MIND</h1>
|
||||
<p>404 - Page not found :(</p>
|
||||
<a href="/">Go to home page</a>
|
||||
<a href="{{url_prefix}}">Go to home page</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,19 +4,20 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta id="url_prefix" data-value="{{url_prefix}}">
|
||||
|
||||
<link rel="stylesheet" href="/static/css/general.css">
|
||||
<link rel="stylesheet" href="/static/css/reminders_templates.css">
|
||||
<link rel="stylesheet" href="/static/css/add_edit.css">
|
||||
<link rel="stylesheet" href="/static/css/notification.css">
|
||||
<link rel="stylesheet" href="/static/css/settings.css">
|
||||
<script src="/static/js/general.js" defer></script>
|
||||
<script src="/static/js/reminders.js" defer></script>
|
||||
<script src="/static/js/add.js" defer></script>
|
||||
<script src="/static/js/edit.js" defer></script>
|
||||
<script src="/static/js/notification.js" defer></script>
|
||||
<script src="/static/js/settings.js" defer></script>
|
||||
<script src="/static/js/templates.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/general.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/reminders_templates.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/add_edit.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/notification.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/general.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/reminders.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/add.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/edit.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/notification.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/settings.js') }}" defer></script>
|
||||
<script src="{{ url_for('static', filename='js/templates.js') }}" defer></script>
|
||||
|
||||
<title>Reminders - MIND</title>
|
||||
</head>
|
||||
@@ -173,10 +174,10 @@
|
||||
<select id="repeat-quantity">
|
||||
<option value="minutes">Minute(s)</option>
|
||||
<option value="hours">Hour(s)</option>
|
||||
<option value="day" selected>Day(s)</option>
|
||||
<option value="week">Week(s)</option>
|
||||
<option value="month">Month(s)</option>
|
||||
<option value="year">Year(s)</option>
|
||||
<option value="days" selected>Day(s)</option>
|
||||
<option value="weeks">Week(s)</option>
|
||||
<option value="months">Month(s)</option>
|
||||
<option value="years">Year(s)</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea id="text-input" cols="30" rows="10" placeholder="Text (optional)"></textarea>
|
||||
@@ -226,10 +227,10 @@
|
||||
<select id="repeat-edit-quantity">
|
||||
<option value="minutes">Minute(s)</option>
|
||||
<option value="hours">Hour(s)</option>
|
||||
<option value="day" selected>Day(s)</option>
|
||||
<option value="week">Week(s)</option>
|
||||
<option value="month">Month(s)</option>
|
||||
<option value="year">Year(s)</option>
|
||||
<option value="days" selected>Day(s)</option>
|
||||
<option value="weeks">Week(s)</option>
|
||||
<option value="months">Month(s)</option>
|
||||
<option value="years">Year(s)</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea id="text-edit-input" cols="30" rows="10" placeholder="Text (optional)"></textarea>
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
ui = Blueprint('ui', __name__)
|
||||
|
||||
methods = ['GET']
|
||||
|
||||
@ui.errorhandler(404)
|
||||
def not_found(e):
|
||||
return render_template('page_not_found.html', url_prefix=logging.URL_PREFIX)
|
||||
|
||||
@ui.route('/', methods=methods)
|
||||
def ui_login():
|
||||
return render_template('login.html')
|
||||
return render_template('login.html', url_prefix=logging.URL_PREFIX)
|
||||
|
||||
@ui.route('/reminders', methods=methods)
|
||||
def ui_reminders():
|
||||
return render_template('reminders.html')
|
||||
return render_template('reminders.html', url_prefix=logging.URL_PREFIX)
|
||||
|
||||
@@ -15,6 +15,6 @@ class Test_MIND(unittest.TestCase):
|
||||
self.assertEqual(result.blueprints.get('api'), api)
|
||||
|
||||
handlers = result.error_handler_spec[None].keys()
|
||||
required_handlers = 404, 400, 405, 500
|
||||
required_handlers = 400, 405, 500
|
||||
for handler in required_handlers:
|
||||
self.assertIn(handler, handlers)
|
||||
Reference in New Issue
Block a user