2021-01-16 22:15:10 +08:00
|
|
|
import sqlite3
|
2021-01-19 22:20:11 +08:00
|
|
|
import threading
|
2026-04-28 13:17:54 +08:00
|
|
|
from typing import cast
|
|
|
|
|
from pathlib import Path
|
2026-05-14 21:13:13 +08:00
|
|
|
from dataclasses import dataclass
|
2026-05-15 11:08:57 +08:00
|
|
|
from typing import Callable, ParamSpec, TypeVar, Generic
|
2026-05-14 21:13:13 +08:00
|
|
|
|
|
|
|
|
import dt
|
|
|
|
|
import utils
|
|
|
|
|
import config
|
|
|
|
|
from logger import LOGGER
|
|
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
|
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
P = ParamSpec('P')
|
|
|
|
|
R = TypeVar('R')
|
|
|
|
|
|
|
|
|
|
|
2026-05-14 21:13:13 +08:00
|
|
|
@dataclass(frozen=True)
|
2026-05-15 11:08:57 +08:00
|
|
|
class ResponseBody(Generic[T]):
|
|
|
|
|
"""The generic response body for API return."""
|
|
|
|
|
|
2026-05-14 21:13:13 +08:00
|
|
|
success: bool
|
|
|
|
|
"""True if this operation is successful, otherwise false."""
|
|
|
|
|
error: str
|
|
|
|
|
"""The error message provided when operation failed."""
|
2026-05-15 11:08:57 +08:00
|
|
|
data: T | None
|
2026-05-14 21:13:13 +08:00
|
|
|
"""The payload provided when operation successed."""
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
|
|
|
|
|
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
|
2026-04-28 13:17:54 +08:00
|
|
|
cfg = config.get_config()
|
|
|
|
|
|
2021-01-19 22:20:11 +08:00
|
|
|
with self.mutex:
|
2026-05-15 11:08:57 +08:00
|
|
|
# try to fetching database and allocate database cursor
|
2021-01-19 22:20:11 +08:00
|
|
|
try:
|
2026-05-15 11:08:57 +08:00
|
|
|
db = self._get_db()
|
|
|
|
|
self._allocate_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
except Exception as e:
|
2026-05-15 11:08:57 +08:00
|
|
|
self._free_cursor()
|
2026-04-28 13:17:54 +08:00
|
|
|
if cfg.others.debug:
|
2026-05-14 21:13:13 +08:00
|
|
|
LOGGER.exception(e)
|
|
|
|
|
return ResponseBody(False, str(e), None)
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
# do real data work
|
|
|
|
|
try:
|
2021-01-25 20:42:06 +08:00
|
|
|
currentTime = utils.GetCurrentTimestamp()
|
2026-04-28 13:17:54 +08:00
|
|
|
if currentTime - self.latestClean > cfg.others.auto_token_clean_duration:
|
2021-01-25 20:42:06 +08:00
|
|
|
self.latestClean = currentTime
|
2026-05-14 21:13:13 +08:00
|
|
|
LOGGER.info('Cleaning outdated token...')
|
2021-01-24 14:38:08 +08:00
|
|
|
self.tokenOper_clean()
|
|
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
result = ResponseBody(True, '', inner(*args, **kwargs))
|
|
|
|
|
self._free_cursor()
|
|
|
|
|
db.commit()
|
2021-01-19 22:20:11 +08:00
|
|
|
return result
|
2021-01-24 14:38:08 +08:00
|
|
|
except Exception as e:
|
2026-05-15 11:08:57 +08:00
|
|
|
self._free_cursor()
|
|
|
|
|
db.rollback()
|
2026-04-28 13:17:54 +08:00
|
|
|
if cfg.others.debug:
|
2026-05-14 21:13:13 +08:00
|
|
|
LOGGER.exception(e)
|
|
|
|
|
return ResponseBody(False, str(e), None)
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
return wrapper
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2026-04-28 13:17:54 +08:00
|
|
|
class CalendarDatabase:
|
|
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
db: sqlite3.Connection | None
|
|
|
|
|
cursor: sqlite3.Cursor | None
|
2026-04-28 13:17:54 +08:00
|
|
|
mutex: threading.Lock
|
|
|
|
|
latestClean: int
|
|
|
|
|
|
2021-01-16 22:15:10 +08:00
|
|
|
def __init__(self):
|
|
|
|
|
self.db = None
|
2021-01-19 22:20:11 +08:00
|
|
|
self.cursor = None
|
|
|
|
|
self.mutex = threading.Lock()
|
2021-01-25 20:42:06 +08:00
|
|
|
self.latestClean = 0
|
2021-01-16 22:15:10 +08:00
|
|
|
|
|
|
|
|
def open(self):
|
2026-05-15 11:08:57 +08:00
|
|
|
if (self.db is not None):
|
|
|
|
|
raise DbException('Database is already opened')
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2026-04-28 13:17:54 +08:00
|
|
|
cfg = config.get_config()
|
|
|
|
|
match cfg.database.driver:
|
|
|
|
|
case config.DatabaseDriver.SQLITE:
|
|
|
|
|
self.db = sqlite3.connect(cast(config.SqliteDatabaseConfig, cfg.database.config).path, check_same_thread = False)
|
|
|
|
|
self.db.execute('PRAGMA encoding = "UTF-8";')
|
|
|
|
|
self.db.execute('PRAGMA foreign_keys = ON;')
|
|
|
|
|
case config.DatabaseDriver.MYSQL:
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Not implemented database')
|
2026-04-28 13:17:54 +08:00
|
|
|
case _:
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Unknow database type')
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
def init(self, username: str, password: str):
|
|
|
|
|
if (self.db is not None):
|
|
|
|
|
raise DbException('Database is already opened')
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2021-01-19 22:20:11 +08:00
|
|
|
# establish tables
|
2026-04-28 13:17:54 +08:00
|
|
|
cfg = config.get_config()
|
|
|
|
|
backend_path = Path(__file__).resolve().parent
|
|
|
|
|
backend_sql_path = backend_path / 'sql'
|
|
|
|
|
match cfg.database.driver:
|
|
|
|
|
case config.DatabaseDriver.SQLITE:
|
|
|
|
|
sql_file = backend_sql_path / 'sqlite.sql'
|
|
|
|
|
case config.DatabaseDriver.MYSQL:
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Not implemented database')
|
2026-04-28 13:17:54 +08:00
|
|
|
case _:
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Unknow database type')
|
2026-04-28 13:17:54 +08:00
|
|
|
|
2021-01-16 22:15:10 +08:00
|
|
|
self.open()
|
2026-05-15 11:08:57 +08:00
|
|
|
db = self._get_db()
|
|
|
|
|
|
|
|
|
|
self._allocate_cursor()
|
|
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
|
|
|
|
|
# execute script for creating tables
|
2026-04-28 13:17:54 +08:00
|
|
|
with open(sql_file, 'r', encoding='utf-8') as fsql:
|
2021-01-16 22:15:10 +08:00
|
|
|
cursor.executescript(fsql.read())
|
2026-05-15 11:08:57 +08:00
|
|
|
# add default user in user table
|
2021-01-24 14:38:08 +08:00
|
|
|
cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', (
|
2021-01-19 22:20:11 +08:00
|
|
|
username,
|
|
|
|
|
utils.ComputePasswordHash(password),
|
|
|
|
|
1,
|
2021-01-24 14:38:08 +08:00
|
|
|
utils.GenerateSalt()
|
2021-01-19 22:20:11 +08:00
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
|
|
|
|
|
self._free_cursor()
|
|
|
|
|
|
|
|
|
|
# commit to database
|
|
|
|
|
db.commit()
|
2021-01-16 22:15:10 +08:00
|
|
|
|
|
|
|
|
def close(self):
|
2026-05-15 11:08:57 +08:00
|
|
|
if (self.db is None):
|
|
|
|
|
LOGGER.warning('Try to close null database.')
|
|
|
|
|
else:
|
|
|
|
|
self._free_cursor()
|
|
|
|
|
self.db.close()
|
|
|
|
|
self.db = None
|
|
|
|
|
|
|
|
|
|
def _get_db(self) -> sqlite3.Connection:
|
|
|
|
|
if (self.db is None):
|
|
|
|
|
raise DbException('There is no opened database')
|
|
|
|
|
else:
|
|
|
|
|
return self.db
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
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()
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
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
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2021-01-24 14:38:08 +08:00
|
|
|
# ======================= token related internal operation
|
|
|
|
|
def tokenOper_clean(self):
|
|
|
|
|
# remove outdated token
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
cursor.execute('DELETE FROM token WHERE [token_expire_on] <= ?',(utils.GetCurrentTimestamp(), ))
|
2021-01-24 14:38:08 +08:00
|
|
|
|
|
|
|
|
def tokenOper_postpone_expireOn(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
cursor.execute('UPDATE token SET [token_expire_on] = ? WHERE [token] = ?;', (
|
2021-01-24 14:38:08 +08:00
|
|
|
utils.GetTokenExpireOn(),
|
|
|
|
|
token
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def tokenOper_check_valid(self, token):
|
|
|
|
|
self.tokenOper_get_username(token)
|
|
|
|
|
|
|
|
|
|
def tokenOper_is_admin(self, username):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
cursor.execute('SELECT [is_admin] FROM user WHERE [name] = ?;',(username, ))
|
|
|
|
|
cache = cursor.fetchone()[0]
|
2021-02-03 16:08:40 +08:00
|
|
|
return cache == 1
|
2021-01-24 14:38:08 +08:00
|
|
|
|
|
|
|
|
def tokenOper_get_username(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
cursor.execute('SELECT [user] FROM token WHERE [token] = ? AND [token_expire_on] > ?;',(
|
2021-01-19 22:20:11 +08:00
|
|
|
token,
|
|
|
|
|
utils.GetCurrentTimestamp()
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
result = cursor.fetchone()[0]
|
2021-01-25 20:42:06 +08:00
|
|
|
# need postpone expire on time
|
|
|
|
|
self.tokenOper_postpone_expireOn(token)
|
|
|
|
|
return result
|
2021-01-24 14:38:08 +08:00
|
|
|
|
2021-01-19 22:20:11 +08:00
|
|
|
# =============================== # =============================== operation function
|
|
|
|
|
# =============================== common
|
2021-01-29 11:10:25 +08:00
|
|
|
|
2021-01-19 22:20:11 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def common_salt(self, username):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-19 22:20:11 +08:00
|
|
|
salt = utils.GenerateSalt()
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
|
2021-01-19 22:20:11 +08:00
|
|
|
salt,
|
|
|
|
|
username
|
|
|
|
|
))
|
|
|
|
|
return salt
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
2021-03-08 21:56:03 +08:00
|
|
|
def common_login(self, username, password, clientUa, clientIp):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
|
|
|
|
cursor.execute('SELECT [password], [salt] FROM user WHERE [name] = ?;', (username, ))
|
|
|
|
|
(gotten_salt, gotten_password) = cursor.fetchone()
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt):
|
2021-01-20 22:57:41 +08:00
|
|
|
token = utils.GenerateToken(username)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE user SET [salt] = ? WHERE [name] = ?;', (
|
2021-01-20 22:57:41 +08:00
|
|
|
utils.GenerateSalt(), # regenerate a new slat to prevent re-login try
|
|
|
|
|
username
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
|
2021-01-24 14:38:08 +08:00
|
|
|
username,
|
|
|
|
|
token,
|
|
|
|
|
utils.GetTokenExpireOn(), # add 2 day from now
|
2021-03-08 21:56:03 +08:00
|
|
|
clientUa,
|
|
|
|
|
clientIp,
|
2021-01-24 14:38:08 +08:00
|
|
|
))
|
2021-01-20 22:57:41 +08:00
|
|
|
return token
|
|
|
|
|
else:
|
2021-01-23 18:37:12 +08:00
|
|
|
# throw a exception to indicate fail to login
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Login authentication failed')
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
2021-03-08 21:56:03 +08:00
|
|
|
def common_webLogin(self, username, password, clientUa, clientIp):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2026-05-14 21:13:13 +08:00
|
|
|
LOGGER.debug(f'WebLogin Username: {username}')
|
|
|
|
|
LOGGER.debug(f'WebLogin Password: {password}')
|
|
|
|
|
passwordHash = utils.ComputePasswordHash(password)
|
|
|
|
|
LOGGER.debug(f'WebLogin Password Hash: {passwordHash}')
|
|
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [name] FROM user WHERE [name] = ? AND [password] = ?;', (username, passwordHash))
|
2021-01-20 22:57:41 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
if len(cursor.fetchall()) != 0:
|
2021-01-19 22:20:11 +08:00
|
|
|
token = utils.GenerateToken(username)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO token VALUES (?, ?, ?, ?, ?);', (
|
2021-01-24 14:38:08 +08:00
|
|
|
username,
|
2021-01-19 22:20:11 +08:00
|
|
|
token,
|
2021-01-24 14:38:08 +08:00
|
|
|
utils.GetTokenExpireOn(), # add 2 day from now
|
2021-03-08 21:56:03 +08:00
|
|
|
clientUa,
|
|
|
|
|
clientIp,
|
2021-01-19 22:20:11 +08:00
|
|
|
))
|
|
|
|
|
return token
|
|
|
|
|
else:
|
2021-01-23 18:37:12 +08:00
|
|
|
# throw a exception to indicate fail to login
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Login authentication failed')
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def common_logout(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('DELETE FROM token WHERE [token] = ?;', (token, ))
|
2021-01-31 13:50:20 +08:00
|
|
|
return True
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def common_tokenValid(self, token):
|
2021-01-24 14:38:08 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2021-01-31 13:50:20 +08:00
|
|
|
return True
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
# =============================== calendar
|
2021-02-02 12:11:13 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_getFull(self, token, startDateTime, endDateTime):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT calendar.* FROM calendar INNER JOIN collection \
|
2026-05-12 19:25:31 +08:00
|
|
|
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) <= ?);',
|
2021-02-02 12:11:13 +08:00
|
|
|
(username, startDateTime, endDateTime))
|
2026-05-15 11:08:57 +08:00
|
|
|
return cursor.fetchall()
|
2021-02-02 12:11:13 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_getList(self, token, startDateTime, endDateTime):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT calendar.uuid FROM calendar INNER JOIN collection \
|
2026-05-12 19:25:31 +08:00
|
|
|
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) <= ?);',
|
2021-02-02 12:11:13 +08:00
|
|
|
(username, startDateTime, endDateTime))
|
2026-05-15 11:08:57 +08:00
|
|
|
return tuple(map(lambda x: x[0], cursor.fetchall()))
|
2021-02-02 12:11:13 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_getDetail(self, token, uuid):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT * FROM calendar WHERE [uuid] = ?;', (uuid, ))
|
|
|
|
|
return cursor.fetchone()
|
2021-02-02 12:11:13 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_update(self, token, uuid, lastChange, **optArgs):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2021-02-02 12:11:13 +08:00
|
|
|
# get prev data
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT * FROM calendar WHERE [uuid] = ? AND [last_change] = ?;', (uuid, lastChange))
|
|
|
|
|
analyseData = list(cursor.fetchone())
|
2021-02-02 12:11:13 +08:00
|
|
|
|
|
|
|
|
# construct update data
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
|
|
|
|
sqlList = [
|
2026-05-12 19:25:31 +08:00
|
|
|
'[last_change] = ?',
|
2021-02-02 12:11:13 +08:00
|
|
|
]
|
|
|
|
|
argumentsList = [
|
|
|
|
|
lastupdate,
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# analyse opt arg
|
|
|
|
|
reAnalyseLoop = False
|
|
|
|
|
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('belongTo', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[belong_to] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('title', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[title] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('description', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[description] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('eventDateTimeStart', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[event_date_time_start] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
|
|
|
|
reAnalyseLoop = True
|
|
|
|
|
analyseData[5] = cache
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('eventDateTimeEnd', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[event_date_time_end] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('loopRules', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[loop_rules] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
|
|
|
|
reAnalyseLoop = True
|
|
|
|
|
analyseData[8] = cache
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('timezoneOffset', None)
|
2021-02-02 12:11:13 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[timezone_offset] = ?')
|
2021-02-02 12:11:13 +08:00
|
|
|
argumentsList.append(cache)
|
|
|
|
|
reAnalyseLoop = True
|
|
|
|
|
analyseData[7] = cache
|
|
|
|
|
|
|
|
|
|
if reAnalyseLoop:
|
2021-02-07 13:44:16 +08:00
|
|
|
# re-compute loop data and upload it into list
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[loop_date_time_start] = ?')
|
2021-02-07 13:44:16 +08:00
|
|
|
argumentsList.append(analyseData[5])
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[loop_date_time_end] = ?')
|
2026-04-28 13:17:54 +08:00
|
|
|
argumentsList.append(str(dt.ResolveLoopStr(
|
2021-02-07 13:44:16 +08:00
|
|
|
analyseData[8],
|
|
|
|
|
analyseData[5],
|
|
|
|
|
analyseData[7]
|
2026-04-28 13:17:54 +08:00
|
|
|
)))
|
2021-02-02 12:11:13 +08:00
|
|
|
|
|
|
|
|
# execute
|
|
|
|
|
argumentsList.append(uuid)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE calendar SET {} WHERE [uuid] = ?;'.format(', '.join(sqlList)),
|
2021-02-02 12:11:13 +08:00
|
|
|
tuple(argumentsList))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to update due to no matched rows or too much rows.')
|
2021-02-02 12:11:13 +08:00
|
|
|
return lastupdate
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_add(self, token, belongTo, title, description, eventDateTimeStart, eventDateTimeEnd, loopRules, timezoneOffset):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
|
|
|
|
|
|
|
|
|
newuuid = utils.GenerateUUID()
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
|
|
|
|
|
2021-02-07 13:44:16 +08:00
|
|
|
# analyse loopRules and output following 2 fileds.
|
2021-02-02 12:11:13 +08:00
|
|
|
loopDateTimeStart = eventDateTimeStart
|
2021-02-07 13:44:16 +08:00
|
|
|
loopDateTimeEnd = dt.ResolveLoopStr(loopRules, eventDateTimeStart, timezoneOffset)
|
2021-02-02 12:11:13 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO calendar VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
|
2021-02-02 12:11:13 +08:00
|
|
|
(newuuid,
|
|
|
|
|
belongTo,
|
|
|
|
|
title,
|
|
|
|
|
description,
|
|
|
|
|
lastupdate,
|
|
|
|
|
eventDateTimeStart,
|
|
|
|
|
eventDateTimeEnd,
|
|
|
|
|
timezoneOffset,
|
|
|
|
|
loopRules,
|
|
|
|
|
loopDateTimeStart,
|
|
|
|
|
loopDateTimeEnd))
|
|
|
|
|
return newuuid
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def calendar_delete(self, token, uuid, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 12:11:13 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-02-02 12:11:13 +08:00
|
|
|
return True
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
# =============================== collection
|
2021-02-02 20:19:53 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_getFullOwn(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ?;', (username, ))
|
|
|
|
|
return cursor.fetchall()
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_getListOwn(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [uuid] FROM collection WHERE [user] = ?;', (username, ))
|
|
|
|
|
return tuple(map(lambda x: x[0], cursor.fetchall()))
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_getDetailOwn(self, token, uuid):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [uuid], [name], [last_change] FROM collection WHERE [user] = ? AND [uuid] = ?;', (username, uuid))
|
|
|
|
|
return cursor.fetchone()
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_addOwn(self, token, newname):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
|
|
|
|
newuuid = utils.GenerateUUID()
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO collection VALUES (?, ?, ?, ?);',
|
2021-02-02 20:19:53 +08:00
|
|
|
(newuuid, newname, username, lastupdate))
|
|
|
|
|
return newuuid
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_updateOwn(self, token, uuid, newname, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
|
|
|
|
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE collection SET [name] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
|
2021-02-02 20:19:53 +08:00
|
|
|
newname,
|
|
|
|
|
lastupdate,
|
|
|
|
|
uuid,
|
|
|
|
|
lastChange
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to update due to no matched rows or too much rows.')
|
2021-02-02 20:19:53 +08:00
|
|
|
return lastupdate
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_deleteOwn(self, token, uuid, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('DELETE FROM collection WHERE [uuid] = ? AND [last_change] = ?;', (
|
2021-02-02 20:19:53 +08:00
|
|
|
uuid,
|
|
|
|
|
lastChange
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to delete due to no matched rows or too much rows.')
|
2021-02-02 20:19:53 +08:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_getSharing(self, token, uuid):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [target] FROM share WHERE [uuid] = ?;', (uuid, ))
|
|
|
|
|
return tuple(map(lambda x: x[0], cursor.fetchall()))
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_deleteSharing(self, token, uuid, target, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
|
|
|
|
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
return lastupdate
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_addSharing(self, token, uuid, target, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
|
|
|
|
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
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))
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
return lastupdate
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def collection_getShared(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT collection.uuid, collection.name, collection.user \
|
2021-02-02 20:19:53 +08:00
|
|
|
FROM share INNER JOIN collection \
|
2026-05-12 19:25:31 +08:00
|
|
|
ON share.uuid = collection.uuid \
|
|
|
|
|
WHERE share.target = ?;', (username, ))
|
2026-05-15 11:08:57 +08:00
|
|
|
return cursor.fetchall()
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
# =============================== todo
|
2021-01-20 22:57:41 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_getFull(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT * FROM todo WHERE [belong_to] = ?;', (username, ))
|
|
|
|
|
return cursor.fetchall()
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2021-01-20 22:57:41 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_getList(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [uuid] FROM todo WHERE [belong_to] = ?;', (username, ))
|
|
|
|
|
return tuple(map(lambda x: x[0], cursor.fetchall()))
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_getDetail(self, token, uuid):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT * FROM todo WHERE [belong_to] = ? AND [uuid] = ?;', (username, uuid))
|
|
|
|
|
return cursor.fetchone()
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_add(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-24 14:38:08 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2021-01-20 22:57:41 +08:00
|
|
|
newuuid = utils.GenerateUUID()
|
|
|
|
|
lastupdate = utils.GenerateUUID()
|
2021-01-23 18:37:12 +08:00
|
|
|
returnedData = (
|
2021-01-20 22:57:41 +08:00
|
|
|
newuuid,
|
|
|
|
|
username,
|
|
|
|
|
'',
|
|
|
|
|
lastupdate,
|
2021-01-23 18:37:12 +08:00
|
|
|
)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO todo VALUES (?, ?, ?, ?);', returnedData)
|
2021-01-23 18:37:12 +08:00
|
|
|
return returnedData
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_update(self, token, uuid, data, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-20 22:57:41 +08:00
|
|
|
# check valid token
|
2021-01-24 14:38:08 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
# update
|
2021-01-23 18:37:12 +08:00
|
|
|
newLastChange = utils.GenerateUUID()
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE todo SET [data] = ?, [last_change] = ? WHERE [uuid] = ? AND [last_change] = ?;', (
|
2021-01-20 22:57:41 +08:00
|
|
|
data,
|
2021-01-23 18:37:12 +08:00
|
|
|
newLastChange,
|
2021-02-02 20:19:53 +08:00
|
|
|
uuid,
|
|
|
|
|
lastChange
|
2021-01-20 22:57:41 +08:00
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to update due to no matched rows or too much rows.')
|
2021-01-23 18:37:12 +08:00
|
|
|
return newLastChange
|
2021-01-20 22:57:41 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def todo_delete(self, token, uuid, lastChange):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-01-20 22:57:41 +08:00
|
|
|
# check valid token
|
2021-01-24 14:38:08 +08:00
|
|
|
self.tokenOper_check_valid(token)
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2021-01-20 22:57:41 +08:00
|
|
|
# delete
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-01-31 13:50:20 +08:00
|
|
|
return True
|
2021-01-19 22:20:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# =============================== admin
|
2021-02-02 20:19:53 +08:00
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def admin_get(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2021-02-03 16:08:40 +08:00
|
|
|
if not self.tokenOper_is_admin(username):
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Permission denied.')
|
2021-01-19 22:20:11 +08:00
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT [name], [is_admin] FROM user;')
|
|
|
|
|
return tuple(map(lambda x: (x[0], x[1] == 1), cursor.fetchall()))
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def admin_add(self, token, newname):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2021-02-03 16:08:40 +08:00
|
|
|
if not self.tokenOper_is_admin(username):
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Permission denied.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
newpassword = utils.ComputePasswordHash(utils.GenerateUUID())
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', (
|
2021-02-02 20:19:53 +08:00
|
|
|
newname,
|
|
|
|
|
newpassword,
|
|
|
|
|
0,
|
|
|
|
|
utils.GenerateSalt()
|
|
|
|
|
))
|
|
|
|
|
return (newname, False)
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
2021-02-03 16:08:40 +08:00
|
|
|
def admin_update(self, token, _username, **optArgs):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2021-02-03 16:08:40 +08:00
|
|
|
if not self.tokenOper_is_admin(username):
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Permission denied.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
# construct data
|
|
|
|
|
sqlList = []
|
|
|
|
|
argumentsList = []
|
|
|
|
|
|
|
|
|
|
# analyse opt arg
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('password', None)
|
2021-02-02 20:19:53 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[password] = ?')
|
2021-02-02 20:19:53 +08:00
|
|
|
argumentsList.append(utils.ComputePasswordHash(cache))
|
2021-02-03 16:08:40 +08:00
|
|
|
cache = optArgs.get('isAdmin', None)
|
2021-02-02 20:19:53 +08:00
|
|
|
if cache is not None:
|
2026-05-12 19:25:31 +08:00
|
|
|
sqlList.append('[is_admin] = ?')
|
2021-02-02 20:19:53 +08:00
|
|
|
argumentsList.append(1 if cache else 0)
|
|
|
|
|
|
|
|
|
|
# execute
|
2021-02-03 16:08:40 +08:00
|
|
|
argumentsList.append(_username)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE user SET {} WHERE [name] = ?;'.format(', '.join(sqlList)),
|
2021-02-02 20:19:53 +08:00
|
|
|
tuple(argumentsList))
|
2026-05-14 21:13:13 +08:00
|
|
|
LOGGER.debug(cache)
|
|
|
|
|
LOGGER.debug(tuple(argumentsList))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to update due to no matched rows or too much rows.')
|
2021-02-02 20:19:53 +08:00
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def admin_delete(self, token, username):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-02-02 20:19:53 +08:00
|
|
|
_username = self.tokenOper_get_username(token)
|
2021-02-03 16:08:40 +08:00
|
|
|
if not self.tokenOper_is_admin(_username):
|
2026-05-15 11:08:57 +08:00
|
|
|
raise DbException('Permission denied.')
|
2021-02-02 20:19:53 +08:00
|
|
|
|
|
|
|
|
# delete
|
2026-05-15 11:08:57 +08:00
|
|
|
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.')
|
2021-02-02 20:19:53 +08:00
|
|
|
return True
|
2021-01-16 22:15:10 +08:00
|
|
|
|
2021-03-08 21:56:03 +08:00
|
|
|
# =============================== profile
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def profile_isAdmin(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-03-08 21:56:03 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
|
|
|
|
return self.tokenOper_is_admin(username)
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def profile_changePassword(self, token, newpassword):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-03-08 21:56:03 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('UPDATE user SET [password] = ? WHERE [name] = ?;', (
|
2021-03-08 21:56:03 +08:00
|
|
|
utils.ComputePasswordHash(newpassword),
|
|
|
|
|
username
|
|
|
|
|
))
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def profile_getToken(self, token):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-03-08 21:56:03 +08:00
|
|
|
username = self.tokenOper_get_username(token)
|
|
|
|
|
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('SELECT * FROM token WHERE [user] = ?;', (
|
2021-03-08 21:56:03 +08:00
|
|
|
username,
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
return cursor.fetchall()
|
2021-03-08 21:56:03 +08:00
|
|
|
|
|
|
|
|
@SafeDatabaseOperation
|
|
|
|
|
def profile_deleteToken(self, token, deleteToken):
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor = self._get_cursor()
|
2021-03-08 21:56:03 +08:00
|
|
|
_username = self.tokenOper_get_username(token)
|
|
|
|
|
|
|
|
|
|
# delete
|
2026-05-15 11:08:57 +08:00
|
|
|
cursor.execute('DELETE FROM token WHERE [user] = ? AND [token] = ?;', (
|
2021-03-08 21:56:03 +08:00
|
|
|
_username,
|
|
|
|
|
deleteToken
|
|
|
|
|
))
|
2026-05-15 11:08:57 +08:00
|
|
|
if cursor.rowcount != 1:
|
|
|
|
|
raise DbException('Fail to delete due to no matched rows or too much rows.')
|
2021-03-08 21:56:03 +08:00
|
|
|
return True
|
|
|
|
|
|