From 542768c50126626b9703014c52b2c179602acc33 Mon Sep 17 00:00:00 2001 From: 4linuxfun Date: Mon, 21 Nov 2022 15:06:54 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9AAutoDict?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=B7=BB=E5=8A=A0checkbox=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=86=E5=88=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- www/src/components/AutoDict.vue | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/www/src/components/AutoDict.vue b/www/src/components/AutoDict.vue index 5c2177c..19b10ff 100644 --- a/www/src/components/AutoDict.vue +++ b/www/src/components/AutoDict.vue @@ -13,19 +13,47 @@ + From f50c1eb0c03f14d5c8ef60680e689675be6c31b2 Mon Sep 17 00:00:00 2001 From: 4linuxfun Date: Thu, 1 Dec 2022 10:55:15 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E4=BD=BF=E7=94=A8loguru=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=8E=A5=E7=AE=A1uvicorn=E9=BB=98=E8=AE=A4=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=BE=93=E5=87=BA=20fix=20#40?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/common/auth_casbin.py | 3 ++- server/common/log.py | 37 ++++++++++++++++++++++++++++++++ server/common/security.py | 6 +++++- server/common/utils.py | 3 ++- server/crud/base.py | 7 +++--- server/crud/internal/menu.py | 3 ++- server/crud/internal/roles.py | 7 +++--- server/crud/internal/user.py | 5 +++-- server/main.py | 14 ++++++------ server/routers/internal/login.py | 9 ++++---- server/routers/internal/roles.py | 7 +++--- server/routers/internal/user.py | 9 ++++---- 12 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 server/common/log.py 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..d5435ef 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) @@ -102,7 +103,7 @@ def search(self, session: Session, search: Pagination, filter_type: Optional[Dic sql = sql.where(self.model.id <= subquery).order_by(desc(self.model.id)).limit(search.page_size) else: sql = sql.where(self.model.id >= subquery).limit(search.page_size) - print(sql) + logger.debug(sql) results = session.exec(sql).all() return results @@ -116,5 +117,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/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..d3e3a3d 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: 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}') From ba94e3366bd75b2cc489385d758559d9d6e20c43 Mon Sep 17 00:00:00 2001 From: 4linuxfun Date: Thu, 8 Dec 2022 16:49:14 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E2=80=9C=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E7=AE=A1=E7=90=86=EF=BC=9A=E6=B7=BB=E5=8A=A0=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E2=80=9D=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/crud/internal/roles.py | 2 +- www/src/views/system/roles/index.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/crud/internal/roles.py b/server/crud/internal/roles.py index d3e3a3d..397ea95 100644 --- a/server/crud/internal/roles.py +++ b/server/crud/internal/roles.py @@ -50,7 +50,7 @@ def update_menus(self, session: Session, db_obj: Role, menus: List[int]): casbin_enforcer.delete_permissions_for_user(f'role_{db_obj.id}') 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(':') logger.debug(f'增加权限:role_{db_obj.id},{model},{act}') casbin_enforcer.add_permission_for_user(f'role_{db_obj.id}', model, act) 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 From dfb43cda599d546e942fd7853e23dec9b2bfb87d Mon Sep 17 00:00:00 2001 From: 4linuxfun Date: Fri, 9 Dec 2022 10:57:13 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E4=BC=98=E5=8C=96=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?order=5Fcol=E5=8F=82=E6=95=B0=EF=BC=8C=E6=96=B9=E4=BE=BF?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=8E=92=E5=BA=8F=E7=9A=84=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=90=8D=EF=BC=8C=E4=BD=86=E6=98=AF=E6=AD=A4=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E4=B8=80=E5=AE=9A=E8=A6=81=E6=98=AF=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E8=87=AA=E5=A2=9E=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/crud/base.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/server/crud/base.py b/server/crud/base.py index d5435ef..5a2472e 100644 --- a/server/crud/base.py +++ b/server/crud/base.py @@ -77,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: @@ -91,18 +92,19 @@ 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) + sql = sql.where(getattr(self.model, order_col) >= subquery).limit(search.page_size) logger.debug(sql) results = session.exec(sql).all() return results From 73951fa8b3694d02afee7b2b155c86d9535b9490 Mon Sep 17 00:00:00 2001 From: 4linuxfun Date: Fri, 9 Dec 2022 10:57:45 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=95=B0=E6=8D=AE=E5=AD=97=E5=85=B8=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E6=8E=92=E5=BA=8F=E5=8A=9F=E8=83=BD=20fix=20?= =?UTF-8?q?#90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/crud/internal/dictonary.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) 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)