diff --git a/server/common/auth_casbin.py b/server/common/auth_casbin.py index d6fcb69..d88114c 100644 --- a/server/common/auth_casbin.py +++ b/server/common/auth_casbin.py @@ -1,3 +1,4 @@ +from loguru import logger from fastapi import Request, HTTPException from ..settings import casbin_enforcer @@ -12,5 +13,5 @@ def __call__(self, request: Request): return True model, act = self.policy.split(':') if not casbin_enforcer.enforce(f'uid_{request.state.uid}', model, act): - print('没有权限') + logger.warning(f'uid_{request.state.uid} {model} {act} 没有权限') raise HTTPException(status_code=403, detail="没有权限") diff --git a/server/common/log.py b/server/common/log.py new file mode 100644 index 0000000..f308de5 --- /dev/null +++ b/server/common/log.py @@ -0,0 +1,37 @@ +import logging +from loguru import logger + + +# 参考自:https://gist.github.com/nkhitrov/a3e31cfcc1b19cba8e1b626276148c49 + + +class InterceptHandler(logging.Handler): + def emit(self, record): + # Get corresponding Loguru level if it exists + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + # Find caller from where originated the logged message + frame, depth = logging.currentframe(), 2 + while frame.f_code.co_filename == logging.__file__: + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) + + +def init_logging(): + # 获取所有uvicorn的日志设定,并重置 + print(logging.root.manager.loggerDict) + loggers = ( + logging.getLogger(name) + for name in logging.root.manager.loggerDict + if name.startswith("uvicorn.") + ) + + for uvicorn_logger in loggers: + # 为了防止日志重复输出 + uvicorn_logger.propagate = False + uvicorn_logger.handlers = [InterceptHandler()] diff --git a/server/common/security.py b/server/common/security.py index da41a28..6b3fd9c 100644 --- a/server/common/security.py +++ b/server/common/security.py @@ -1,3 +1,6 @@ +import logging + +from loguru import logger from datetime import datetime, timedelta from fastapi import Request, HTTPException, status from fastapi.security import OAuth2PasswordBearer @@ -21,9 +24,10 @@ def auth_check(request: Request): :param request: :return: """ + logger.info(f'request url:{request.url} method:{request.method}') for url in settings.NO_VERIFY_URL: if url == request.url.path.lower(): - print(f"{request.url.path} 在白名单中,不需要权限验证") + logger.debug(f"{request.url.path} 在白名单中,不需要权限验证") return True authorization: str = request.headers.get("Authorization") schema, param = get_authorization_scheme_param(authorization) diff --git a/server/common/utils.py b/server/common/utils.py index b0733e5..5c2e890 100644 --- a/server/common/utils.py +++ b/server/common/utils.py @@ -1,5 +1,6 @@ import os from typing import List, Generic, Type, TypeVar +from loguru import logger from sqlmodel import SQLModel from ..models.internal.menu import MenusWithChild @@ -61,5 +62,5 @@ def update_model(old_model, new_model): def remove_tmp_file(file): - print(f'删除临时文件{file}') + logger.debug(f'删除临时文件{file}') os.remove(file) diff --git a/server/crud/base.py b/server/crud/base.py index 5efcdfa..5a2472e 100644 --- a/server/crud/base.py +++ b/server/crud/base.py @@ -1,5 +1,6 @@ # 参考自:tiangolo/full-stack-fastapi-postgresql项目,部分代码为直接摘抄 from copy import deepcopy +from loguru import logger from typing import TypeVar, Generic, List, Type, Any, Dict, Optional from sqlmodel import Session, select, SQLModel, func, desc from ..schemas.internal.pagination import Pagination @@ -28,7 +29,7 @@ def insert(self, db: Session, obj_in): def update(self, db: Session, db_obj: ModelType, new_obj: ModelType): # SQLModel直接使用的pydantic的dict方法,没有轮询SQLModel封装的__sqlmodel_relationships__,对于外键的更新,只能手动指定 update_date = new_obj.dict() - print(update_date) + logger.debug(update_date) for field in update_date: setattr(db_obj, field, update_date[field]) db.add(db_obj) @@ -76,13 +77,14 @@ def _make_search(self, sql, search: Optional[Dict[str, Any]] = None, filter_type return sql def search(self, session: Session, search: Pagination, filter_type: Optional[Dict[str, str]] = None, - columns: Optional[List] = None): + columns: Optional[List] = None, order_col: Optional[str] = 'id'): """ 分页查询方法 :param session: :param search: Pagination实例对象,包含各搜索参数 :param filter_type: 指定的各属性值判断形式 :param columns: 查询返回指定columns + :param order_col: order排序列名,默认id,此col需要为自增id :return: """ if columns is None: @@ -90,19 +92,20 @@ def search(self, session: Session, search: Pagination, filter_type: Optional[Dic else: sql = select(*columns) sql = self._make_search(sql, search.search, filter_type) - subquery = select(self.model.id) + subquery = select(getattr(self.model, order_col)) subquery = self._make_search(subquery, search.search, filter_type) if search.model == 'desc': - subquery = subquery.order_by(desc(self.model.id)) + subquery = subquery.order_by(desc(getattr(self.model, order_col))) else: - subquery = subquery.order_by(self.model.id) + subquery = subquery.order_by(getattr(self.model, order_col)) subquery = subquery.offset( (search.page - 1) * search.page_size).limit(1).subquery() if search.model == 'desc': - sql = sql.where(self.model.id <= subquery).order_by(desc(self.model.id)).limit(search.page_size) + sql = sql.where(getattr(self.model, order_col) <= subquery).order_by(desc(getattr(self.model, order_col))).limit( + search.page_size) else: - sql = sql.where(self.model.id >= subquery).limit(search.page_size) - print(sql) + sql = sql.where(getattr(self.model, order_col) >= subquery).limit(search.page_size) + logger.debug(sql) results = session.exec(sql).all() return results @@ -116,5 +119,5 @@ def search_total(self, session: Session, q: Dict[str, Any], filter_type: Optiona """ sql = select(func.count(self.model.id)) sql = self._make_search(sql, q, filter_type) - print(str(sql)) + logger.debug(str(sql)) return session.execute(sql).scalar() diff --git a/server/crud/internal/dictonary.py b/server/crud/internal/dictonary.py index 21b1e9c..24f741a 100644 --- a/server/crud/internal/dictonary.py +++ b/server/crud/internal/dictonary.py @@ -1,9 +1,9 @@ -from typing import Union -from sqlmodel import select, Session -from ...models.internal.dictonary import DataDict, DictItem +from typing import Optional, List, Dict +from loguru import logger +from sqlmodel import select, Session, desc +from ...models.internal.dictonary import DataDict, DictItem, DictItemSearch from ..base import CRUDBase -from ...schemas.internal.user import UserInfo, UserLogin -from .roles import role +from ...schemas.internal.pagination import Pagination class CRUDDict(CRUDBase[DataDict]): @@ -14,9 +14,24 @@ class CRUDItem(CRUDBase[DictItem]): def get_items_by_code(self, db: Session, code: str): dict_id = select(DataDict.id).where(DataDict.code == code).subquery() sql = select(self.model).where(self.model.dict_id == dict_id).where(self.model.enable == 1).order_by( - self.model.id) + self.model.sort) return db.exec(sql).all() + def search(self, session: Session, search: Pagination[DictItemSearch], filter_type: Optional[Dict[str, str]] = None, + columns: Optional[List] = None, order_col: Optional[str] = 'id'): + """ + 重写search函数,数据字典通过sort进行排序 + """ + sql = select(self.model).where(self.model.dict_id == search.search['dict_id']) + sql = self._make_search(sql, search.search, filter_type) + if search.model == 'desc': + sql = sql.order_by(desc(self.model.sort)) + else: + sql = sql.order_by(self.model.sort) + sql = sql.limit(search.page_size).offset((search.page - 1) * search.page_size) + logger.debug(sql) + return session.exec(sql).all() + data_dict = CRUDDict(DataDict) dict_item = CRUDItem(DictItem) diff --git a/server/crud/internal/menu.py b/server/crud/internal/menu.py index 10d1ddd..bd8c514 100644 --- a/server/crud/internal/menu.py +++ b/server/crud/internal/menu.py @@ -1,4 +1,5 @@ from typing import Optional, List +from loguru import logger from sqlmodel import Session, select from ...models.internal.menu import Menu from ..base import CRUDBase @@ -19,7 +20,7 @@ def update(self, session: Session, db_obj, obj_in: Menu): :param obj_in: :return: """ - print(db_obj) + logger.debug(db_obj) return super(CRUDMenu, self).update(session, db_obj, obj_in) def delete(self, session: Session, id: int): diff --git a/server/crud/internal/roles.py b/server/crud/internal/roles.py index db0b49a..397ea95 100644 --- a/server/crud/internal/roles.py +++ b/server/crud/internal/roles.py @@ -1,4 +1,5 @@ from typing import List, Optional +from loguru import logger from sqlmodel import select, Session from ...models.internal import Role, Menu, RoleMenu from ..base import CRUDBase @@ -43,15 +44,15 @@ def update_menus(self, session: Session, db_obj: Role, menus: List[int]): :param menus: :return: """ - print(db_obj.menus) + logger.debug(db_obj.menus) db_menus = session.exec(select(Menu).where(Menu.id.in_(menus))).all() db_obj.menus = db_menus casbin_enforcer.delete_permissions_for_user(f'role_{db_obj.id}') - print(db_menus) + logger.debug(db_menus) for menu in db_menus: - if menu.auth is not None: + if (menu.auth is not None) and menu.auth: model, act = menu.auth.split(':') - print(f'增加权限:role_{db_obj.id},{model},{act}') + logger.debug(f'增加权限:role_{db_obj.id},{model},{act}') casbin_enforcer.add_permission_for_user(f'role_{db_obj.id}', model, act) session.add(db_obj) session.commit() diff --git a/server/crud/internal/user.py b/server/crud/internal/user.py index b16d154..102d7b0 100644 --- a/server/crud/internal/user.py +++ b/server/crud/internal/user.py @@ -1,4 +1,5 @@ from typing import Union +from loguru import logger from sqlmodel import select, Session from ...models.internal.user import User from ..base import CRUDBase @@ -29,8 +30,8 @@ def update(self, session: Session, uid: int, user_info: UserInfo): db_obj = super(CRUDUser, self).update(session, db_obj, updated_user) user_roles = role.get_roles_by_id(session, user_info.roles) db_obj.roles = user_roles - print('update:') - print(db_obj) + logger.debug('update:') + logger.debug(db_obj) session.add(db_obj) session.commit() session.refresh(db_obj) diff --git a/server/main.py b/server/main.py index 7fca15c..6c46ca1 100644 --- a/server/main.py +++ b/server/main.py @@ -1,12 +1,13 @@ -from sqlmodel import Session from fastapi import FastAPI, Depends +from loguru import logger +from .common.log import init_logging from .routers.internal import login, user, menu, roles, dictonary from .common.security import auth_check -from .settings import engine + +init_logging() app = FastAPI(dependencies=[Depends(auth_check)]) -# 不执行check_permission的,表示不需要权限验证 app.include_router(login.router, tags=['用户登录']) app.include_router(user.router, tags=['用户管理']) app.include_router(menu.router, tags=['菜单管理']) @@ -15,15 +16,14 @@ @app.on_event("startup") -def startup(): +async def startup(): """ 在此时添加openapi数据的获取,导入api表,判断有没有新接口信息需要添加进去 :return: """ - print('服务启动后执行服务') - print('API接口更新') + logger.debug('服务启动后执行服务') @app.on_event("shutdown") def shutdown(): - print('关闭服务') + logger.debug('关闭服务') diff --git a/server/routers/internal/login.py b/server/routers/internal/login.py index e5c9df8..84dccd0 100644 --- a/server/routers/internal/login.py +++ b/server/routers/internal/login.py @@ -1,4 +1,5 @@ from typing import List +from loguru import logger from fastapi import APIRouter, Depends, Request, status from ...common.database import get_session from sqlmodel import Session, select @@ -52,9 +53,9 @@ async def get_permission(request: Request, session: Session = Depends(get_sessio :return: """ uid: int = request.state.uid - print(f"uid is:{uid}") + logger.debug(f"uid is:{uid}") user: User = crud.internal.user.get(session, uid) - print(user.roles) + logger.debug(user.roles) user_menus = [] # admin组用户获取所有菜单列表 if uid == 1 or crud.internal.role.check_admin(session, uid): @@ -66,11 +67,9 @@ async def get_permission(request: Request, session: Session = Depends(get_sessio menu_list = session.exec( select(Menu).where(Menu.id.in_(set(user_menus))).where(Menu.type != 'btn').order_by(Menu.sort)).all() btn_list = session.exec(select(Menu.auth).where(Menu.id.in_(set(user_menus))).where(Menu.type == 'btn')).all() - print('menulist') - print(menu_list) user_menus = menu_convert(menu_list) - print(user_menus) + logger.debug(f"user menus:{user_menus}") return ApiResponse( data={ 'menus': user_menus, diff --git a/server/routers/internal/roles.py b/server/routers/internal/roles.py index 5b90e4c..71ffc7f 100644 --- a/server/routers/internal/roles.py +++ b/server/routers/internal/roles.py @@ -1,4 +1,5 @@ from typing import Optional, List +from loguru import logger from fastapi import APIRouter, Depends, status from fastapi.exceptions import HTTPException from sqlmodel import Session @@ -38,7 +39,7 @@ async def get_role_menus(id: Optional[int] = None, session: Session = Depends(ge summary="查询角色") async def get_roles(search: Pagination[RoleBase], session: Session = Depends(get_session)): total = crud.internal.role.search_total(session, search.search, {'name': 'like', 'enable': 'eq'}) - print(total) + logger.debug(total) roles: List[Role] = crud.internal.role.search(session, search, {'name': 'like', 'enable': 'eq'}) role_with_menus: List[RoleWithMenus] = [] for role in roles: @@ -55,7 +56,7 @@ async def get_roles(search: Pagination[RoleBase], session: Session = Depends(get @router.post('/roles', summary="新建角色", response_model=ApiResponse[Role], dependencies=[Depends(Authority('role:add'))]) async def add_roles(role_info: RoleInsert, session: Session = Depends(get_session)): - print(role_info) + logger.debug(role_info) enable_menus = role_info.menus delattr(role_info, 'menus') db_obj = crud.internal.role.insert(session, Role(**role_info.dict())) @@ -68,7 +69,7 @@ async def add_roles(role_info: RoleInsert, session: Session = Depends(get_sessio @router.put('/roles', summary="更新角色", response_model=ApiResponse[Role], dependencies=[Depends(Authority('role:update'))]) async def update_roles(role_info: RoleUpdate, session: Session = Depends(get_session)): - print(role_info) + logger.debug(role_info) if role_info.name == 'admin': ApiResponse(code=status.HTTP_400_BAD_REQUEST, message='admin权限组无法更新信息') db_obj = crud.internal.role.get(session, role_info.id) diff --git a/server/routers/internal/user.py b/server/routers/internal/user.py index fed7061..eb54a1c 100644 --- a/server/routers/internal/user.py +++ b/server/routers/internal/user.py @@ -1,4 +1,5 @@ from typing import Optional, List +from loguru import logger from sqlmodel import Session, select from sqlalchemy.exc import NoResultFound from fastapi import APIRouter, Depends, status, HTTPException @@ -66,10 +67,10 @@ async def get_all_user(search: Pagination[UserWithOutPasswd], :return: """ total = crud.internal.user.search_total(session, search.search, {'name': 'like', 'enable': 'eq'}) - print(total) + logger.debug(total) users = crud.internal.user.search(session, search, {'name': 'like', 'enable': 'eq'}) users_list = [user.dict(exclude={"password"}) for user in users] - print(users_list) + logger.debug(users_list) return ApiResponse( data={ 'total': total, @@ -87,7 +88,7 @@ async def update_user(user_info: UserCreateWithRoles, session: Session = Depends :param session: :return: """ - print(user_info) + logger.debug(user_info) user: User = crud.internal.user.insert(session, user_info) new_roles = [role.id for role in user.roles] for role in new_roles: @@ -113,7 +114,7 @@ async def update_user(uid: int, user_info: UserUpdateWithRoles, session: Session :param session: :return: """ - print(user_info.dict(exclude_unset=True, exclude_none=True)) + logger.debug(user_info.dict(exclude_unset=True, exclude_none=True)) user = crud.internal.user.update(session, uid, user_info) new_roles = [role.id for role in user.roles] casbin_enforcer.delete_roles_for_user(f'uid_{user.id}') diff --git a/www/src/components/AutoDict.vue b/www/src/components/AutoDict.vue index 5c2177c..a77a31c 100644 --- a/www/src/components/AutoDict.vue +++ b/www/src/components/AutoDict.vue @@ -12,29 +12,84 @@ + diff --git a/www/src/views/system/roles/index.vue b/www/src/views/system/roles/index.vue index 509a9f6..2991e79 100644 --- a/www/src/views/system/roles/index.vue +++ b/www/src/views/system/roles/index.vue @@ -126,7 +126,7 @@ id: null, name: '', description: '', - enable: '' + enable: true }) console.log(selectRole) dialogVisible.value = true