diff --git a/app/api/endpoints/user.py b/app/api/endpoints/user.py index 85ae3036..580ce16e 100644 --- a/app/api/endpoints/user.py +++ b/app/api/endpoints/user.py @@ -1,6 +1,6 @@ import base64 import re -from typing import Any, List +from typing import Any, List, Union from fastapi import APIRouter, Depends, HTTPException, UploadFile, File from sqlalchemy.orm import Session @@ -10,6 +10,7 @@ from app.core.security import get_password_hash from app.db import get_db from app.db.models.user import User from app.db.userauth import get_current_active_superuser, get_current_active_user +from app.db.userconfig_oper import UserConfigOper from app.utils.otp import OtpUtils router = APIRouter() @@ -182,3 +183,23 @@ def read_user_by_id( detail="用户权限不足" ) return user + +@router.get("/config/{key}", summary="查询用户配置", response_model=schemas.Response) +def get_config(key: str, + current_user: User = Depends(get_current_active_user)): + """ + 查询用户配置 + """ + value = UserConfigOper().get(username=current_user.name, key=key) + return schemas.Response(success=True, data={ + "value": value + }) + +@router.post("/config/{key}", summary="更新用户配置", response_model=schemas.Response) +def set_config(key: str, value: Union[list, dict, bool, int, str] = None, + current_user: User = Depends(get_current_active_user)): + """ + 更新用户配置 + """ + UserConfigOper().set(username=current_user.name, key=key, value=value) + return schemas.Response(success=True) diff --git a/app/db/models/__init__.py b/app/db/models/__init__.py index 57ddee03..661f1107 100644 --- a/app/db/models/__init__.py +++ b/app/db/models/__init__.py @@ -7,3 +7,4 @@ from .subscribe import Subscribe from .systemconfig import SystemConfig from .transferhistory import TransferHistory from .user import User +from .userconfig import UserConfig \ No newline at end of file diff --git a/app/db/models/userconfig.py b/app/db/models/userconfig.py new file mode 100644 index 00000000..fce73578 --- /dev/null +++ b/app/db/models/userconfig.py @@ -0,0 +1,38 @@ +from sqlalchemy import Column, Integer, String, Sequence, UniqueConstraint, Index +from sqlalchemy.orm import Session + +from app.db import db_query, db_update, Base + + +class UserConfig(Base): + """ + 用户配置表 + """ + id = Column(Integer, Sequence('id'), primary_key=True, index=True) + # 用户名 + username = Column(String, index=True) + # 配置键 + key = Column(String) + # 值 + value = Column(String, nullable=True) + + __table_args__ = ( + # 用户名和配置键联合唯一 + UniqueConstraint('username', 'key'), + Index('ix_userconfig_username_key', 'username', 'key'), + ) + + @staticmethod + @db_query + def get_by_key(db: Session, username: str, key: str): + return db.query(UserConfig) \ + .filter(UserConfig.username == username) \ + .filter(UserConfig.key == key) \ + .first() + + @db_update + def delete_by_key(self, db: Session, username: str, key: str): + userconfig = self.get_by_key(db=db, username=username, key=key) + if userconfig: + userconfig.delete(db=db, rid=userconfig.id) + return True diff --git a/app/db/userconfig_oper.py b/app/db/userconfig_oper.py new file mode 100644 index 00000000..d8c614f1 --- /dev/null +++ b/app/db/userconfig_oper.py @@ -0,0 +1,96 @@ +import json +from typing import Any, Union, Dict + +from app.db import DbOper +from app.db.models.userconfig import UserConfig +from app.schemas.types import UserConfigKey +from app.utils.object import ObjectUtils +from app.utils.singleton import Singleton + + +class UserConfigOper(DbOper, metaclass=Singleton): + # 配置缓存 + __USERCONF: Dict[str, Dict[str, Any]] = {} + + def __init__(self): + """ + 加载配置到内存 + """ + super().__init__() + for item in UserConfig.list(self._db): + value = json.loads(item.value) if ObjectUtils.is_obj(item.value) else item.value + self.__set_config_cache(username=item.username, key=item.key, value=value) + + def set(self, username: str, key: Union[str, UserConfigKey], value: Any): + """ + 设置用户配置 + """ + if isinstance(key, UserConfigKey): + key = key.value + # 更新内存 + self.__set_config_cache(username=username, key=key, value=value) + # 写入数据库 + if ObjectUtils.is_obj(value): + value = json.dumps(value) + elif value is None: + value = '' + conf = UserConfig.get_by_key(db=self._db, username=username, key=key) + if conf: + if value: + conf.update(self._db, {"value": value}) + else: + conf.delete(self._db, conf.id) + else: + conf = UserConfig(username=username, key=key, value=value) + conf.create(self._db) + + def get(self, username: str, key: Union[str, UserConfigKey] = None) -> Any: + """ + 获取用户配置 + """ + if not username: + return self.__USERCONF + if isinstance(key, UserConfigKey): + key = key.value + if not key: + return self.__get_config_caches(username=username) + return self.__get_config_cache(username=username, key=key) + + def __del__(self): + if self._db: + self._db.close() + + def __set_config_cache(self, username: str, key: str, value: Any): + """ + 设置配置缓存 + """ + if not username or not key: + return + cache = self.__USERCONF + if not cache: + cache = {} + user_cache = cache.get(username) + if not user_cache: + user_cache = {} + cache[username] = user_cache + user_cache[key] = value + self.__USERCONF = cache + + def __get_config_caches(self, username: str) -> Dict[str, Any]: + """ + 获取配置缓存 + """ + if not username or not self.__USERCONF: + return None + return self.__USERCONF.get(username) + + def __get_config_cache(self, username: str, key: str) -> Any: + """ + 获取配置缓存 + """ + if not username or not key or not self.__USERCONF: + return None + user_cache = self.__get_config_caches(username) + if not user_cache: + return None + return user_cache.get(key) diff --git a/app/schemas/types.py b/app/schemas/types.py index cc28bf67..242bd087 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -124,3 +124,9 @@ class MessageChannel(Enum): SynologyChat = "SynologyChat" VoceChat = "VoceChat" Web = "Web" + + +# 用户配置Key字典 +class UserConfigKey(Enum): + # 监控面板 + Dashboard = "Dashboard"