1
0

refactor: modify backend for better understand

This commit is contained in:
2026-05-15 11:08:57 +08:00
parent e484ded5be
commit 35fee0f473
3 changed files with 617 additions and 408 deletions

View File

@@ -3,33 +3,50 @@ import threading
from typing import cast
from pathlib import Path
from dataclasses import dataclass
from typing import Any
from typing import Callable, ParamSpec, TypeVar, Generic
import dt
import utils
import config
from logger import LOGGER
T = TypeVar('T')
P = ParamSpec('P')
R = TypeVar('R')
@dataclass(frozen=True)
class ResponseBody:
class ResponseBody(Generic[T]):
"""The generic response body for API return."""
success: bool
"""True if this operation is successful, otherwise false."""
error: str
"""The error message provided when operation failed."""
data: Any
data: T | None
"""The payload provided when operation successed."""
def SafeDatabaseOperation(func):
def wrapper(self: 'CalendarDatabase', *args, **kwargs):
class DbException(Exception):
"""Error occurs when manipulating with database."""
pass
def SafeDatabaseOperation(inner: Callable[P, R]) -> Callable[P, ResponseBody[R]]:
def wrapper(*args, **kwargs) -> ResponseBody[R]:
# extract self from args
self: 'CalendarDatabase' = args[0]
# get config
cfg = config.get_config()
with self.mutex:
# check database and acquire cursor
# try to fetching database and allocate database cursor
try:
self.check_database()
self.cursor = self.db.cursor()
db = self._get_db()
self._allocate_cursor()
except Exception as e:
self.cursor = None
self._free_cursor()
if cfg.others.debug:
LOGGER.exception(e)
return ResponseBody(False, str(e), None)
@@ -42,15 +59,13 @@ def SafeDatabaseOperation(func):
LOGGER.info('Cleaning outdated token...')
self.tokenOper_clean()
result = ResponseBody(True, '', func(self, *args, **kwargs))
self.cursor.close()
self.cursor = None
self.db.commit()
result = ResponseBody(True, '', inner(*args, **kwargs))
self._free_cursor()
db.commit()
return result
except Exception as e:
self.cursor.close()
self.cursor = None
self.db.rollback()
self._free_cursor()
db.rollback()
if cfg.others.debug:
LOGGER.exception(e)
return ResponseBody(False, str(e), None)
@@ -59,8 +74,8 @@ def SafeDatabaseOperation(func):
class CalendarDatabase:
db: sqlite3.Connection
cursor: sqlite3.Cursor
db: sqlite3.Connection | None
cursor: sqlite3.Cursor | None
mutex: threading.Lock
latestClean: int
@@ -71,8 +86,8 @@ class CalendarDatabase:
self.latestClean = 0
def open(self):
if (self.is_database_valid()):
raise Exception('Databade is opened')
if (self.db is not None):
raise DbException('Database is already opened')
cfg = config.get_config()
match cfg.database.driver:
@@ -81,13 +96,13 @@ class CalendarDatabase:
self.db.execute('PRAGMA encoding = "UTF-8";')
self.db.execute('PRAGMA foreign_keys = ON;')
case config.DatabaseDriver.MYSQL:
raise Exception('Not implemented database')
raise DbException('Not implemented database')
case _:
raise Exception('Unknow database type')
raise DbException('Unknow database type')
def init(self, username, password):
if (self.is_database_valid()):
raise Exception('Database is opened')
def init(self, username: str, password: str):
if (self.db is not None):
raise DbException('Database is already opened')
# establish tables
cfg = config.get_config()
@@ -97,44 +112,74 @@ class CalendarDatabase:
case config.DatabaseDriver.SQLITE:
sql_file = backend_sql_path / 'sqlite.sql'
case config.DatabaseDriver.MYSQL:
raise Exception('Not implemented database')
raise DbException('Not implemented database')
case _:
raise Exception('Unknow database type')
raise DbException('Unknow database type')
self.open()
cursor = self.db.cursor()
db = self._get_db()
self._allocate_cursor()
cursor = self._get_cursor()
# execute script for creating tables
with open(sql_file, 'r', encoding='utf-8') as fsql:
cursor.executescript(fsql.read())
# finish init
# add default user in user table
cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', (
username,
utils.ComputePasswordHash(password),
1,
utils.GenerateSalt()
))
cursor.close()
self.db.commit()
self._free_cursor()
# commit to database
db.commit()
def close(self):
self.check_database()
self.db.close()
self.db = None
if (self.db is None):
LOGGER.warning('Try to close null database.')
else:
self._free_cursor()
self.db.close()
self.db = None
def check_database(self):
if (not self.is_database_valid()):
raise Exception('Databade is None')
def _get_db(self) -> sqlite3.Connection:
if (self.db is None):
raise DbException('There is no opened database')
else:
return self.db
def is_database_valid(self):
return not (self.db == None)
def _allocate_cursor(self) -> None:
if (self.cursor is not None):
raise DbException('There is already opened database cursor')
else:
self.cursor = self._get_db().cursor()
def _get_cursor(self) -> sqlite3.Cursor:
if (self.cursor is None):
raise DbException('There is no opened database cursor')
else:
return self.cursor
def _free_cursor(self) -> None:
if (self.cursor is None):
LOGGER.warning('Try to free null databse cursor.')
else:
self.cursor.close()
self.cursor = None
# ======================= token related internal operation
def tokenOper_clean(self):
# remove outdated token
self.cursor.execute('DELETE FROM token WHERE [token_expire_on] <= ?',(utils.GetCurrentTimestamp(), ))
cursor = self._get_cursor()
cursor.execute('DELETE FROM token WHERE [token_expire_on] <= ?',(utils.GetCurrentTimestamp(), ))
def tokenOper_postpone_expireOn(self, token):
self.cursor.execute('UPDATE token SET [token_expire_on] = ? WHERE [token] = ?;', (
cursor = self._get_cursor()
cursor.execute('UPDATE token SET [token_expire_on] = ? WHERE [token] = ?;', (
utils.GetTokenExpireOn(),
token
))
@@ -143,16 +188,18 @@ class CalendarDatabase:
self.tokenOper_get_username(token)
def tokenOper_is_admin(self, username):
self.cursor.execute('SELECT [is_admin] FROM user WHERE [name] = ?;',(username, ))
cache = self.cursor.fetchone()[0]
cursor = self._get_cursor()
cursor.execute('SELECT [is_admin] FROM user WHERE [name] = ?;',(username, ))
cache = cursor.fetchone()[0]
return cache == 1
def tokenOper_get_username(self, token):
self.cursor.execute('SELECT [user] FROM token WHERE [token] = ? AND [token_expire_on] > ?;',(
cursor = self._get_cursor()
cursor.execute('SELECT [user] FROM token WHERE [token] = ? AND [token_expire_on] > ?;',(
token,
utils.GetCurrentTimestamp()
))
result = self.cursor.fetchone()[0]
result = cursor.fetchone()[0]
# need postpone expire on time
self.tokenOper_postpone_expireOn(token)
return result
@@ -162,8 +209,9 @@ class CalendarDatabase:
@SafeDatabaseOperation
def common_salt(self, username):
cursor = self._get_cursor()
salt = utils.GenerateSalt()
self.cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
salt,
username
))
@@ -171,16 +219,17 @@ class CalendarDatabase:
@SafeDatabaseOperation
def common_login(self, username, password, clientUa, clientIp):
self.cursor.execute('SELECT [password], [salt] FROM user WHERE [name] = ?;', (username, ))
(gotten_salt, gotten_password) = self.cursor.fetchone()
cursor = self._get_cursor()
cursor.execute('SELECT [password], [salt] FROM user WHERE [name] = ?;', (username, ))
(gotten_salt, gotten_password) = cursor.fetchone()
if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt):
token = utils.GenerateToken(username)
self.cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
utils.GenerateSalt(), # regenerate a new slat to prevent re-login try
username
))
self.cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
username,
token,
utils.GetTokenExpireOn(), # add 2 day from now
@@ -190,20 +239,21 @@ class CalendarDatabase:
return token
else:
# throw a exception to indicate fail to login
raise Exception('Login authentication failed')
raise DbException('Login authentication failed')
@SafeDatabaseOperation
def common_webLogin(self, username, password, clientUa, clientIp):
cursor = self._get_cursor()
LOGGER.debug(f'WebLogin Username: {username}')
LOGGER.debug(f'WebLogin Password: {password}')
passwordHash = utils.ComputePasswordHash(password)
LOGGER.debug(f'WebLogin Password Hash: {passwordHash}')
self.cursor.execute('SELECT [name] FROM user WHERE [name] = ? AND [password] = ?;', (username, passwordHash))
cursor.execute('SELECT [name] FROM user WHERE [name] = ? AND [password] = ?;', (username, passwordHash))
if len(self.cursor.fetchall()) != 0:
if len(cursor.fetchall()) != 0:
token = utils.GenerateToken(username)
self.cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
username,
token,
utils.GetTokenExpireOn(), # add 2 day from now
@@ -213,12 +263,13 @@ class CalendarDatabase:
return token
else:
# throw a exception to indicate fail to login
raise Exception('Login authentication failed')
raise DbException('Login authentication failed')
@SafeDatabaseOperation
def common_logout(self, token):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
self.cursor.execute('DELETE FROM token WHERE [token] = ?;', (token, ))
cursor.execute('DELETE FROM token WHERE [token] = ?;', (token, ))
return True
@SafeDatabaseOperation
@@ -229,35 +280,39 @@ class CalendarDatabase:
# =============================== calendar
@SafeDatabaseOperation
def calendar_getFull(self, token, startDateTime, endDateTime):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT calendar.* FROM calendar INNER JOIN collection \
cursor.execute('SELECT calendar.* FROM calendar INNER JOIN collection \
ON collection.uuid = calendar.belong_to \
WHERE (collection.user = ? AND calendar.loop_date_time_end >= ? AND calendar.loop_date_time_start - (calendar.event_date_time_end - calendar.event_date_time_start) <= ?);',
(username, startDateTime, endDateTime))
return self.cursor.fetchall()
return cursor.fetchall()
@SafeDatabaseOperation
def calendar_getList(self, token, startDateTime, endDateTime):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT calendar.uuid FROM calendar INNER JOIN collection \
cursor.execute('SELECT calendar.uuid FROM calendar INNER JOIN collection \
ON collection.uuid = calendar.belong_to \
WHERE (collection.user = ? AND calendar.loop_date_time_end >= ? AND calendar.loop_date_time_start - (calendar.event_date_time_end - calendar.event_date_time_start) <= ?);',
(username, startDateTime, endDateTime))
return tuple(map(lambda x: x[0], self.cursor.fetchall()))
return tuple(map(lambda x: x[0], cursor.fetchall()))
@SafeDatabaseOperation
def calendar_getDetail(self, token, uuid):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
self.cursor.execute('SELECT * FROM calendar WHERE [uuid] = ?;', (uuid, ))
return self.cursor.fetchone()
cursor.execute('SELECT * FROM calendar WHERE [uuid] = ?;', (uuid, ))
return cursor.fetchone()
@SafeDatabaseOperation
def calendar_update(self, token, uuid, lastChange, **optArgs):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
# get prev data
self.cursor.execute('SELECT * FROM calendar WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
analyseData = list(self.cursor.fetchone())
cursor.execute('SELECT * FROM calendar WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
analyseData = list(cursor.fetchone())
# construct update data
lastupdate = utils.GenerateUUID()
@@ -319,14 +374,15 @@ class CalendarDatabase:
# execute
argumentsList.append(uuid)
self.cursor.execute('UPDATE calendar SET {} WHERE [uuid] = ?;'.format(', '.join(sqlList)),
cursor.execute('UPDATE calendar SET {} WHERE [uuid] = ?;'.format(', '.join(sqlList)),
tuple(argumentsList))
if self.cursor.rowcount != 1:
raise Exception('Fail to update due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to update due to no matched rows or too much rows.')
return lastupdate
@SafeDatabaseOperation
def calendar_add(self, token, belongTo, title, description, eventDateTimeStart, eventDateTimeEnd, loopRules, timezoneOffset):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
newuuid = utils.GenerateUUID()
@@ -336,7 +392,7 @@ class CalendarDatabase:
loopDateTimeStart = eventDateTimeStart
loopDateTimeEnd = dt.ResolveLoopStr(loopRules, eventDateTimeStart, timezoneOffset)
self.cursor.execute('INSERT INTO calendar VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
cursor.execute('INSERT INTO calendar VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
(newuuid,
belongTo,
title,
@@ -352,134 +408,149 @@ class CalendarDatabase:
@SafeDatabaseOperation
def calendar_delete(self, token, uuid, lastChange):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
self.cursor.execute('DELETE FROM calendar WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('DELETE FROM calendar WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return True
# =============================== collection
@SafeDatabaseOperation
def collection_getFullOwn(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ?;', (username, ))
return self.cursor.fetchall()
cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ?;', (username, ))
return cursor.fetchall()
@SafeDatabaseOperation
def collection_getListOwn(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT [uuid] FROM collection WHERE [user] = ?;', (username, ))
return tuple(map(lambda x: x[0], self.cursor.fetchall()))
cursor.execute('SELECT [uuid] FROM collection WHERE [user] = ?;', (username, ))
return tuple(map(lambda x: x[0], cursor.fetchall()))
@SafeDatabaseOperation
def collection_getDetailOwn(self, token, uuid):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ? AND [uuid] = ?;', (username, uuid))
return self.cursor.fetchone()
cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ? AND [uuid] = ?;', (username, uuid))
return cursor.fetchone()
@SafeDatabaseOperation
def collection_addOwn(self, token, newname):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
newuuid = utils.GenerateUUID()
lastupdate = utils.GenerateUUID()
self.cursor.execute('INSERT INTO collection VALUES (?, ?, ?, ?);',
cursor.execute('INSERT INTO collection VALUES (?, ?, ?, ?);',
(newuuid, newname, username, lastupdate))
return newuuid
@SafeDatabaseOperation
def collection_updateOwn(self, token, uuid, newname, lastChange):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
lastupdate = utils.GenerateUUID()
self.cursor.execute('UPDATE collection SET [name] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
cursor.execute('UPDATE collection SET [name] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
newname,
lastupdate,
uuid,
lastChange
))
if self.cursor.rowcount != 1:
raise Exception('Fail to update due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to update due to no matched rows or too much rows.')
return lastupdate
@SafeDatabaseOperation
def collection_deleteOwn(self, token, uuid, lastChange):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
self.cursor.execute('DELETE FROM collection WHERE [uuid] = ? AND [last_change] = ?;', (
cursor.execute('DELETE FROM collection WHERE [uuid] = ? AND [last_change] = ?;', (
uuid,
lastChange
))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return True
@SafeDatabaseOperation
def collection_getSharing(self, token, uuid):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
self.cursor.execute('SELECT [target] FROM share WHERE [uuid] = ?;', (uuid, ))
return tuple(map(lambda x: x[0], self.cursor.fetchall()))
cursor.execute('SELECT [target] FROM share WHERE [uuid] = ?;', (uuid, ))
return tuple(map(lambda x: x[0], cursor.fetchall()))
@SafeDatabaseOperation
def collection_deleteSharing(self, token, uuid, target, lastChange):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
lastupdate = utils.GenerateUUID()
self.cursor.execute('UPDATE collection SET [last_change] = ?, WHERE [uuid] = ? AND [last_change] = ?;', (lastupdate, uuid, lastChange))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('UPDATE collection SET [last_change] = ?, WHERE [uuid] = ? AND [last_change] = ?;', (lastupdate, uuid, lastChange))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
self.cursor.execute('DELETE FROM share WHERE [uuid] = ? AND [target] = ?;', (uuid, target))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('DELETE FROM share WHERE [uuid] = ? AND [target] = ?;', (uuid, target))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return lastupdate
@SafeDatabaseOperation
def collection_addSharing(self, token, uuid, target, lastChange):
cursor = self._get_cursor()
self.tokenOper_check_valid(token)
lastupdate = utils.GenerateUUID()
self.cursor.execute('UPDATE collection SET [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (lastupdate, uuid, lastChange))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('UPDATE collection SET [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (lastupdate, uuid, lastChange))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
self.cursor.execute('SELECT * FROM share WHERE [uuid] = ? AND [target] = ?;', (uuid, target))
if len(self.cursor.fetchall()) != 0:
raise Exception('Fail to insert duplicated item.')
self.cursor.execute('INSERT INTO share VALUES (?, ?);', (uuid, target))
cursor.execute('SELECT * FROM share WHERE [uuid] = ? AND [target] = ?;', (uuid, target))
if len(cursor.fetchall()) != 0:
raise DbException('Fail to insert duplicated item.')
cursor.execute('INSERT INTO share VALUES (?, ?);', (uuid, target))
return lastupdate
@SafeDatabaseOperation
def collection_getShared(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT collection.uuid, collection.name, collection.user \
cursor.execute('SELECT collection.uuid, collection.name, collection.user \
FROM share INNER JOIN collection \
ON share.uuid = collection.uuid \
WHERE share.target = ?;', (username, ))
return self.cursor.fetchall()
return cursor.fetchall()
# =============================== todo
@SafeDatabaseOperation
def todo_getFull(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT * FROM todo WHERE [belong_to] = ?;', (username, ))
return self.cursor.fetchall()
cursor.execute('SELECT * FROM todo WHERE [belong_to] = ?;', (username, ))
return cursor.fetchall()
@SafeDatabaseOperation
def todo_getList(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT [uuid] FROM todo WHERE [belong_to] = ?;', (username, ))
return tuple(map(lambda x: x[0], self.cursor.fetchall()))
cursor.execute('SELECT [uuid] FROM todo WHERE [belong_to] = ?;', (username, ))
return tuple(map(lambda x: x[0], cursor.fetchall()))
@SafeDatabaseOperation
def todo_getDetail(self, token, uuid):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT * FROM todo WHERE [belong_to] = ? AND [uuid] = ?;', (username, uuid))
return self.cursor.fetchone()
cursor.execute('SELECT * FROM todo WHERE [belong_to] = ? AND [uuid] = ?;', (username, uuid))
return cursor.fetchone()
@SafeDatabaseOperation
def todo_add(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
newuuid = utils.GenerateUUID()
lastupdate = utils.GenerateUUID()
@@ -489,56 +560,60 @@ class CalendarDatabase:
'',
lastupdate,
)
self.cursor.execute('INSERT INTO todo VALUES (?, ?, ?, ?);', returnedData)
cursor.execute('INSERT INTO todo VALUES (?, ?, ?, ?);', returnedData)
return returnedData
@SafeDatabaseOperation
def todo_update(self, token, uuid, data, lastChange):
cursor = self._get_cursor()
# check valid token
self.tokenOper_check_valid(token)
# update
newLastChange = utils.GenerateUUID()
self.cursor.execute('UPDATE todo SET [data] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
cursor.execute('UPDATE todo SET [data] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
data,
newLastChange,
uuid,
lastChange
))
if self.cursor.rowcount != 1:
raise Exception('Fail to update due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to update due to no matched rows or too much rows.')
return newLastChange
@SafeDatabaseOperation
def todo_delete(self, token, uuid, lastChange):
cursor = self._get_cursor()
# check valid token
self.tokenOper_check_valid(token)
# delete
self.cursor.execute('DELETE FROM todo WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('DELETE FROM todo WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return True
# =============================== admin
@SafeDatabaseOperation
def admin_get(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.')
raise DbException('Permission denied.')
self.cursor.execute('SELECT [name], [is_admin] FROM user;')
return tuple(map(lambda x: (x[0], x[1] == 1), self.cursor.fetchall()))
cursor.execute('SELECT [name], [is_admin] FROM user;')
return tuple(map(lambda x: (x[0], x[1] == 1), cursor.fetchall()))
@SafeDatabaseOperation
def admin_add(self, token, newname):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.')
raise DbException('Permission denied.')
newpassword = utils.ComputePasswordHash(utils.GenerateUUID())
self.cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', (
cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', (
newname,
newpassword,
0,
@@ -548,9 +623,10 @@ class CalendarDatabase:
@SafeDatabaseOperation
def admin_update(self, token, _username, **optArgs):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
if not self.tokenOper_is_admin(username):
raise Exception('Permission denied.')
raise DbException('Permission denied.')
# construct data
sqlList = []
@@ -568,36 +644,39 @@ class CalendarDatabase:
# execute
argumentsList.append(_username)
self.cursor.execute('UPDATE user SET {} WHERE [name] = ?;'.format(', '.join(sqlList)),
cursor.execute('UPDATE user SET {} WHERE [name] = ?;'.format(', '.join(sqlList)),
tuple(argumentsList))
LOGGER.debug(cache)
LOGGER.debug(tuple(argumentsList))
if self.cursor.rowcount != 1:
raise Exception('Fail to update due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to update due to no matched rows or too much rows.')
return True
@SafeDatabaseOperation
def admin_delete(self, token, username):
cursor = self._get_cursor()
_username = self.tokenOper_get_username(token)
if not self.tokenOper_is_admin(_username):
raise Exception('Permission denied.')
raise DbException('Permission denied.')
# delete
self.cursor.execute('DELETE FROM user WHERE [name] = ?;', (username, ))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
cursor.execute('DELETE FROM user WHERE [name] = ?;', (username, ))
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return True
# =============================== profile
@SafeDatabaseOperation
def profile_isAdmin(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
return self.tokenOper_is_admin(username)
@SafeDatabaseOperation
def profile_changePassword(self, token, newpassword):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('UPDATE user SET [password] = ? WHERE [name] = ?;', (
cursor.execute('UPDATE user SET [password] = ? WHERE [name] = ?;', (
utils.ComputePasswordHash(newpassword),
username
))
@@ -605,23 +684,25 @@ class CalendarDatabase:
@SafeDatabaseOperation
def profile_getToken(self, token):
cursor = self._get_cursor()
username = self.tokenOper_get_username(token)
self.cursor.execute('SELECT * FROM token WHERE [user] = ?;', (
cursor.execute('SELECT * FROM token WHERE [user] = ?;', (
username,
))
return self.cursor.fetchall()
return cursor.fetchall()
@SafeDatabaseOperation
def profile_deleteToken(self, token, deleteToken):
cursor = self._get_cursor()
_username = self.tokenOper_get_username(token)
# delete
self.cursor.execute('DELETE FROM token WHERE [user] = ? AND [token] = ?;', (
cursor.execute('DELETE FROM token WHERE [user] = ? AND [token] = ?;', (
_username,
deleteToken
))
if self.cursor.rowcount != 1:
raise Exception('Fail to delete due to no matched rows or too much rows.')
if cursor.rowcount != 1:
raise DbException('Fail to delete due to no matched rows or too much rows.')
return True