diff --git a/server/.idea/misc.xml b/server/.idea/misc.xml index 5871b5c..f45d3f0 100644 --- a/server/.idea/misc.xml +++ b/server/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/server/alembic.ini b/server/alembic.ini index bea7a89..d4aed71 100644 --- a/server/alembic.ini +++ b/server/alembic.ini @@ -50,7 +50,7 @@ version_path_separator = os # default: use os.pathsep # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = mysql+pymysql://root:1234567890@192.168.137.129/simple_sam +sqlalchemy.url = mysql+pymysql://root:123456@192.168.137.129/devops [post_write_hooks] diff --git a/server/alembic/env.py b/server/alembic/env.py index aa9436e..b93a82e 100644 --- a/server/alembic/env.py +++ b/server/alembic/env.py @@ -19,8 +19,9 @@ # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -from models import * +from models import * +from models.internal import * target_metadata = SQLModel.metadata diff --git a/server/alembic/script.py.mako b/server/alembic/script.py.mako index 55df286..3124b62 100644 --- a/server/alembic/script.py.mako +++ b/server/alembic/script.py.mako @@ -7,6 +7,7 @@ Create Date: ${create_date} """ from alembic import op import sqlalchemy as sa +import sqlmodel ${imports if imports else ""} # revision identifiers, used by Alembic. diff --git a/server/alembic/versions/334040db4d77_delete_sysapi.py b/server/alembic/versions/334040db4d77_delete_sysapi.py new file mode 100644 index 0000000..0ecc8da --- /dev/null +++ b/server/alembic/versions/334040db4d77_delete_sysapi.py @@ -0,0 +1,53 @@ +"""delete sysapi + +Revision ID: 334040db4d77 +Revises: a21539d7fbb7 +Create Date: 2022-10-28 15:00:57.698249 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '334040db4d77' +down_revision = 'a21539d7fbb7' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('menu_apis') + op.drop_table('sys_api') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('menu_apis', + sa.Column('menu_id', mysql.INTEGER(), autoincrement=False, nullable=False), + sa.Column('api_id', mysql.INTEGER(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['api_id'], ['sys_api.id'], name='menu_apis_ibfk_2'), + sa.ForeignKeyConstraint(['menu_id'], ['menu.id'], name='menu_apis_ibfk_1'), + sa.PrimaryKeyConstraint('menu_id', 'api_id'), + mysql_collate='utf8mb4_general_ci', + mysql_default_charset='utf8mb4', + mysql_engine='InnoDB', + mysql_row_format='DYNAMIC' + ) + op.create_table('sys_api', + sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False), + sa.Column('tags', mysql.VARCHAR(collation='utf8mb4_general_ci', length=10), nullable=False, comment='标签'), + sa.Column('path', mysql.VARCHAR(collation='utf8mb4_general_ci', length=50), nullable=False, comment='API路径'), + sa.Column('method', mysql.VARCHAR(collation='utf8mb4_general_ci', length=10), nullable=False, comment='HTTP方法'), + sa.Column('summary', mysql.VARCHAR(collation='utf8mb4_general_ci', length=20), nullable=False, comment='描述'), + sa.Column('deprecated', mysql.TINYINT(display_width=1), server_default=sa.text("'0'"), autoincrement=False, nullable=True, comment='是否废弃'), + sa.PrimaryKeyConstraint('id'), + mysql_collate='utf8mb4_general_ci', + mysql_default_charset='utf8mb4', + mysql_engine='InnoDB', + mysql_row_format='DYNAMIC' + ) + # ### end Alembic commands ### diff --git a/server/alembic/versions/a21539d7fbb7_init.py b/server/alembic/versions/a21539d7fbb7_init.py new file mode 100644 index 0000000..b9ff371 --- /dev/null +++ b/server/alembic/versions/a21539d7fbb7_init.py @@ -0,0 +1,35 @@ +"""init + +Revision ID: a21539d7fbb7 +Revises: +Create Date: 2022-10-28 10:38:17.100830 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'a21539d7fbb7' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user_roles', + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('role_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('user_id', 'role_id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('user_roles') + # ### end Alembic commands ### diff --git "a/server/alembic/versions/a5e2cea226d4_\346\267\273\345\212\240\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" "b/server/alembic/versions/a5e2cea226d4_\346\267\273\345\212\240\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" new file mode 100644 index 0000000..b4b27c0 --- /dev/null +++ "b/server/alembic/versions/a5e2cea226d4_\346\267\273\345\212\240\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" @@ -0,0 +1,29 @@ +"""添加菜单排序字段 + +Revision ID: a5e2cea226d4 +Revises: 334040db4d77 +Create Date: 2022-10-31 09:44:30.289049 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'a5e2cea226d4' +down_revision = '334040db4d77' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('menu', sa.Column('sort', sa.Integer(), nullable=True, comment='菜单排序')) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('menu', 'sort') + # ### end Alembic commands ### diff --git "a/server/alembic/versions/b703662d9cf9_\346\233\264\346\226\260\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" "b/server/alembic/versions/b703662d9cf9_\346\233\264\346\226\260\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" new file mode 100644 index 0000000..1f582ba --- /dev/null +++ "b/server/alembic/versions/b703662d9cf9_\346\233\264\346\226\260\350\217\234\345\215\225\346\216\222\345\272\217\345\255\227\346\256\265.py" @@ -0,0 +1,37 @@ +"""更新菜单排序字段 + +Revision ID: b703662d9cf9 +Revises: a5e2cea226d4 +Create Date: 2022-10-31 10:22:22.613017 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'b703662d9cf9' +down_revision = 'a5e2cea226d4' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('menu', 'sort', + existing_type=mysql.INTEGER(), + type_=sa.Float(), + existing_comment='菜单排序', + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('menu', 'sort', + existing_type=sa.Float(), + type_=mysql.INTEGER(), + existing_comment='菜单排序', + existing_nullable=True) + # ### end Alembic commands ### diff --git a/server/alembic/versions/cc553c35453a_init.py b/server/alembic/versions/cc553c35453a_init.py deleted file mode 100644 index 969f7dc..0000000 --- a/server/alembic/versions/cc553c35453a_init.py +++ /dev/null @@ -1,222 +0,0 @@ -"""init - -Revision ID: cc553c35453a -Revises: -Create Date: 2022-06-08 14:26:51.364287 - -""" -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - -# revision identifiers, used by Alembic. -revision = 'cc553c35453a' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index('ix_role_category_category_id', table_name='role_category') - op.drop_index('ix_role_category_role_id', table_name='role_category') - op.drop_table('role_category') - op.drop_table('casbin_rule') - op.alter_column('assets', 'id', - existing_type=mysql.BIGINT(), - nullable=True, - autoincrement=True) - op.drop_index('ix_assets_area', table_name='assets') - op.drop_index('ix_assets_category', table_name='assets') - op.drop_index('ix_assets_deleted', table_name='assets') - op.drop_index('ix_assets_id', table_name='assets') - op.drop_index('ix_assets_manager', table_name='assets') - op.drop_index('ix_assets_user', table_name='assets') - op.alter_column('category', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_category_alias', table_name='category') - op.drop_index('ix_category_desc', table_name='category') - op.drop_index('ix_category_id', table_name='category') - op.drop_index('ix_category_name', table_name='category') - op.alter_column('category_field', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_category_field_category_id', table_name='category_field') - op.drop_index('ix_category_field_desc', table_name='category_field') - op.drop_index('ix_category_field_id', table_name='category_field') - op.drop_index('ix_category_field_multi', table_name='category_field') - op.drop_index('ix_category_field_name', table_name='category_field') - op.drop_index('ix_category_field_need', table_name='category_field') - op.drop_index('ix_category_field_show', table_name='category_field') - op.drop_index('ix_category_field_type', table_name='category_field') - op.alter_column('menu', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_menu_component', table_name='menu') - op.drop_index('ix_menu_enable', table_name='menu') - op.drop_index('ix_menu_id', table_name='menu') - op.drop_index('ix_menu_name', table_name='menu') - op.drop_index('ix_menu_parent_id', table_name='menu') - op.drop_index('ix_menu_path', table_name='menu') - op.drop_index('ix_menu_type', table_name='menu') - op.drop_index('ix_role_menu_menu_id', table_name='role_menu') - op.drop_index('ix_role_menu_role_id', table_name='role_menu') - op.alter_column('roles', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_roles_description', table_name='roles') - op.drop_index('ix_roles_enable', table_name='roles') - op.drop_index('ix_roles_id', table_name='roles') - op.drop_index('ix_roles_name', table_name='roles') - op.alter_column('sys_api', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.alter_column('system', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_system_admin', table_name='system') - op.drop_index('ix_system_cpu', table_name='system') - op.drop_index('ix_system_developer', table_name='system') - op.drop_index('ix_system_env', table_name='system') - op.drop_index('ix_system_host', table_name='system') - op.drop_index('ix_system_id', table_name='system') - op.drop_index('ix_system_ip', table_name='system') - op.drop_index('ix_system_memory', table_name='system') - op.drop_index('ix_system_project', table_name='system') - op.drop_index('ix_system_storage', table_name='system') - op.drop_index('ix_system_system', table_name='system') - op.drop_index('ix_system_type', table_name='system') - op.alter_column('user', 'name', - existing_type=mysql.VARCHAR(charset='utf8mb4', collation='utf8mb4_general_ci', length=50), - nullable=False) - op.alter_column('user', 'id', - existing_type=mysql.INTEGER(), - nullable=True, - autoincrement=True) - op.drop_index('ix_user_email', table_name='user') - op.drop_index('ix_user_name', table_name='user') - op.drop_index('name', table_name='user') - op.drop_index('ix_user_roles_role_id', table_name='user_roles') - op.drop_index('ix_user_roles_user_id', table_name='user_roles') - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_index('ix_user_roles_user_id', 'user_roles', ['user_id'], unique=False) - op.create_index('ix_user_roles_role_id', 'user_roles', ['role_id'], unique=False) - op.create_index('name', 'user', ['name', 'email'], unique=False) - op.create_index('ix_user_name', 'user', ['name'], unique=False) - op.create_index('ix_user_email', 'user', ['email'], unique=False) - op.alter_column('user', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.alter_column('user', 'name', - existing_type=mysql.VARCHAR(charset='utf8mb4', collation='utf8mb4_general_ci', length=50), - nullable=True) - op.create_index('ix_system_type', 'system', ['type'], unique=False) - op.create_index('ix_system_system', 'system', ['system'], unique=False) - op.create_index('ix_system_storage', 'system', ['storage'], unique=False) - op.create_index('ix_system_project', 'system', ['project'], unique=False) - op.create_index('ix_system_memory', 'system', ['memory'], unique=False) - op.create_index('ix_system_ip', 'system', ['ip'], unique=False) - op.create_index('ix_system_id', 'system', ['id'], unique=False) - op.create_index('ix_system_host', 'system', ['host'], unique=False) - op.create_index('ix_system_env', 'system', ['env'], unique=False) - op.create_index('ix_system_developer', 'system', ['developer'], unique=False) - op.create_index('ix_system_cpu', 'system', ['cpu'], unique=False) - op.create_index('ix_system_admin', 'system', ['admin'], unique=False) - op.alter_column('system', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.alter_column('sys_api', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.create_index('ix_roles_name', 'roles', ['name'], unique=False) - op.create_index('ix_roles_id', 'roles', ['id'], unique=False) - op.create_index('ix_roles_enable', 'roles', ['enable'], unique=False) - op.create_index('ix_roles_description', 'roles', ['description'], unique=False) - op.alter_column('roles', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.create_index('ix_role_menu_role_id', 'role_menu', ['role_id'], unique=False) - op.create_index('ix_role_menu_menu_id', 'role_menu', ['menu_id'], unique=False) - op.create_index('ix_menu_type', 'menu', ['type'], unique=False) - op.create_index('ix_menu_path', 'menu', ['path'], unique=False) - op.create_index('ix_menu_parent_id', 'menu', ['parent_id'], unique=False) - op.create_index('ix_menu_name', 'menu', ['name'], unique=False) - op.create_index('ix_menu_id', 'menu', ['id'], unique=False) - op.create_index('ix_menu_enable', 'menu', ['enable'], unique=False) - op.create_index('ix_menu_component', 'menu', ['component'], unique=False) - op.alter_column('menu', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.create_index('ix_category_field_type', 'category_field', ['type'], unique=False) - op.create_index('ix_category_field_show', 'category_field', ['show'], unique=False) - op.create_index('ix_category_field_need', 'category_field', ['need'], unique=False) - op.create_index('ix_category_field_name', 'category_field', ['name'], unique=False) - op.create_index('ix_category_field_multi', 'category_field', ['multi'], unique=False) - op.create_index('ix_category_field_id', 'category_field', ['id'], unique=False) - op.create_index('ix_category_field_desc', 'category_field', ['desc'], unique=False) - op.create_index('ix_category_field_category_id', 'category_field', ['category_id'], unique=False) - op.alter_column('category_field', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.create_index('ix_category_name', 'category', ['name'], unique=False) - op.create_index('ix_category_id', 'category', ['id'], unique=False) - op.create_index('ix_category_desc', 'category', ['desc'], unique=False) - op.create_index('ix_category_alias', 'category', ['alias'], unique=False) - op.alter_column('category', 'id', - existing_type=mysql.INTEGER(), - nullable=False, - autoincrement=True) - op.create_index('ix_assets_user', 'assets', ['user'], unique=False) - op.create_index('ix_assets_manager', 'assets', ['manager'], unique=False) - op.create_index('ix_assets_id', 'assets', ['id'], unique=False) - op.create_index('ix_assets_deleted', 'assets', ['deleted'], unique=False) - op.create_index('ix_assets_category', 'assets', ['category'], unique=False) - op.create_index('ix_assets_area', 'assets', ['area'], unique=False) - op.alter_column('assets', 'id', - existing_type=mysql.BIGINT(), - nullable=False, - autoincrement=True) - op.create_table('casbin_rule', - sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False), - sa.Column('ptype', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v0', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v1', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v2', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v3', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v4', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.Column('v5', mysql.VARCHAR(collation='utf8mb4_general_ci', length=255), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_collate='utf8mb4_general_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_table('role_category', - sa.Column('role_id', mysql.INTEGER(), autoincrement=False, nullable=False), - sa.Column('category_id', mysql.INTEGER(), autoincrement=False, nullable=False), - sa.ForeignKeyConstraint(['category_id'], ['category.id'], name='role_category_ibfk_2'), - sa.ForeignKeyConstraint(['role_id'], ['roles.id'], name='role_category_ibfk_1'), - sa.PrimaryKeyConstraint('role_id', 'category_id'), - mysql_collate='utf8mb4_general_ci', - mysql_default_charset='utf8mb4', - mysql_engine='InnoDB' - ) - op.create_index('ix_role_category_role_id', 'role_category', ['role_id'], unique=False) - op.create_index('ix_role_category_category_id', 'role_category', ['category_id'], unique=False) - # ### end Alembic commands ### diff --git a/server/common/auth_casbin.py b/server/common/auth_casbin.py new file mode 100644 index 0000000..d6fcb69 --- /dev/null +++ b/server/common/auth_casbin.py @@ -0,0 +1,16 @@ +from fastapi import Request, HTTPException +from ..settings import casbin_enforcer + + +class Authority: + def __init__(self, policy: str): + self.policy = policy + + def __call__(self, request: Request): + if request.state.uid == 1: + # uid为1的都跳过,为admin用户 + return True + model, act = self.policy.split(':') + if not casbin_enforcer.enforce(f'uid_{request.state.uid}', model, act): + print('没有权限') + raise HTTPException(status_code=403, detail="没有权限") diff --git a/server/db.py b/server/common/database.py similarity index 81% rename from server/db.py rename to server/common/database.py index 67c1264..0aefca4 100644 --- a/server/db.py +++ b/server/common/database.py @@ -1,8 +1,5 @@ from sqlmodel import create_engine, SQLModel, Session, select - -SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:1234567890@192.168.137.129/simple_sam" - -engine = create_engine(SQLALCHEMY_DATABASE_URL, future=False) +from ..settings import engine def init_db(): @@ -34,4 +31,4 @@ def get_or_create(session: Session, model, **kwargs): instance = model(**kwargs) session.add(instance) session.commit() - return instance \ No newline at end of file + return instance diff --git a/server/common/security.py b/server/common/security.py index ee83c94..da41a28 100644 --- a/server/common/security.py +++ b/server/common/security.py @@ -1,22 +1,45 @@ from datetime import datetime, timedelta +from fastapi import Request, HTTPException, status from fastapi.security import OAuth2PasswordBearer +from fastapi.security.utils import get_authorization_scheme_param from jose import JWTError, jwt from passlib.context import CryptContext -from typing import Optional +from ..settings import settings # to get a string like this run: # openssl rand -hex 32 -SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" -ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 30 + pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") -def hash_password(password): - return pwd_context.hash(password) +def auth_check(request: Request): + """ + 检查是否有token信息,并在request.state中添加uid值 + :param request: + :return: + """ + for url in settings.NO_VERIFY_URL: + if url == request.url.path.lower(): + print(f"{request.url.path} 在白名单中,不需要权限验证") + return True + authorization: str = request.headers.get("Authorization") + schema, param = get_authorization_scheme_param(authorization) + if not authorization or schema.lower() != "bearer": + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated") + + try: + playload = jwt.decode(param, settings.SECRET_KEY, settings.ALGORITHM) + except jwt.ExpiredSignatureError as e: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) + except JWTError as e: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e)) + + uid = playload.get('uid') + # 在Request对象中设置用户对象,这样在其他地方就能通过request.state.uid获取当前用户id了 + request.state.uid = uid def create_access_token(data): @@ -25,34 +48,12 @@ def create_access_token(data): :param data: :return: """ - expires_delta = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + expires_delta = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({"exp": expire}) - encoded_jwt = token_encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt - - -def token_encode(to_encode, secret_key, algorithm): - """ - token加密 - :param to_encode: - :param secret_key: - :param algorithm: - :return: - """ - return jwt.encode(to_encode, secret_key, algorithm) - - -def token_decode(token, secret_key=SECRET_KEY, algorithm=ALGORITHM): - """ - token解密 - :param token: - :param secret_key: - :param algorithm: - :return: - """ - return jwt.decode(token, secret_key, algorithm) diff --git a/server/crud/internal/__init__.py b/server/crud/internal/__init__.py index 590d67e..a7c086d 100644 --- a/server/crud/internal/__init__.py +++ b/server/crud/internal/__init__.py @@ -1,4 +1,3 @@ from .user import user from .roles import role from .menu import menu -from .sysapi import api diff --git a/server/crud/internal/menu.py b/server/crud/internal/menu.py index 7ad51e5..1429fbb 100644 --- a/server/crud/internal/menu.py +++ b/server/crud/internal/menu.py @@ -1,6 +1,6 @@ from typing import Optional, List from sqlmodel import Session, select -from server.models.internal.menu import Menu +from ...models.internal.menu import Menu from ..base import CRUDBase @@ -9,6 +9,7 @@ def search(self, session: Session, q: Optional = None) -> List[Menu]: sql = select(self.model) if q is not None: sql = sql.where(self.model.name.like(f'%{q}%')) + sql = sql.order_by(self.model.sort) return session.exec(sql).all() def update(self, session: Session, db_obj, obj_in: Menu): diff --git a/server/crud/internal/roles.py b/server/crud/internal/roles.py index 5b82d6a..db0b49a 100644 --- a/server/crud/internal/roles.py +++ b/server/crud/internal/roles.py @@ -2,7 +2,7 @@ from sqlmodel import select, Session from ...models.internal import Role, Menu, RoleMenu from ..base import CRUDBase -from ...dependencies import casbin_enforcer +from ...settings import casbin_enforcer class CRUDRole(CRUDBase[Role]): @@ -49,11 +49,10 @@ def update_menus(self, session: Session, db_obj: Role, menus: List[int]): casbin_enforcer.delete_permissions_for_user(f'role_{db_obj.id}') print(db_menus) for menu in db_menus: - if len(menu.apis) == 0: - continue - for api in menu.apis: - print(f'增加权限:role_{db_obj.id},{api.path},{api.method}') - casbin_enforcer.add_permission_for_user(f'role_{db_obj.id}', api.path, api.method) + if menu.auth is not None: + model, act = menu.auth.split(':') + print(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() session.refresh(db_obj) diff --git a/server/crud/internal/sysapi.py b/server/crud/internal/sysapi.py deleted file mode 100644 index ea2fa67..0000000 --- a/server/crud/internal/sysapi.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import List, Union -from sqlmodel import select, Session, or_ -from ...models.internal import Api -from ..base import CRUDBase - - -class CRUDAPI(CRUDBase[Api]): - def get_multi(self, db: Session, id: List[int]) -> List[Api]: - return db.exec(select(self.model).where(self.model.id.in_(id))).all() - - def get_tree(self, db: Session): - tags = db.exec(select(self.model.tags).distinct(self.model.tags)).all() - print(tags) - tree_apis = [] - for tag in tags: - child_apis = db.exec(select(self.model.id, self.model.summary).where(self.model.tags == tag)).all() - tree_apis.append({'label': tag, 'options': child_apis}) - return tree_apis - - -api = CRUDAPI(Api) diff --git a/server/db_init.py b/server/db_init.py index 0863780..040290a 100644 --- a/server/db_init.py +++ b/server/db_init.py @@ -1,2 +1,18 @@ -from sqlmodel import create_engine -from .sql.models import * +from sqlmodel import Session +from .db import init_db, engine +from .models.role import Role +from .models.user import User + + +def main(): + init_db() + with Session(engine) as session: + admin_role = Role(name='admin', description='Admin管理员组', enable=1) + admin = User(name='admin', enable=1, password='1111111') + admin.roles.append(admin_role) + session.add(admin) + session.commit() + + +if __name__ == '__main__': + main() diff --git a/server/dependencies.py b/server/dependencies.py deleted file mode 100644 index 4afbf49..0000000 --- a/server/dependencies.py +++ /dev/null @@ -1,54 +0,0 @@ -import json -import base64 -from typing import Optional, Dict, Any -from fastapi import Header, HTTPException, Depends, Request, APIRouter -from .common.security import token_decode -from jose.exceptions import JWTError, ExpiredSignatureError -from .db import engine -import casbin_sqlalchemy_adapter -import casbin - - -async def check_token(token: str = Header(..., alias="Authorization")): - # 传递过来的token信息格式:Bearer token,所以需要匹配 - token = token[7:] - try: - token_info = token_decode(token) - except ExpiredSignatureError as e: - raise HTTPException(status_code=401, detail=str(e)) - except JWTError as e: - raise HTTPException(status_code=401, detail=str(e)) - print(token_info) - return token_info - - -def check_uid(token: dict = Depends(check_token)): - """ - 角色检查 - :param token: - :return: - """ - uid = token['uid'] - return uid - - -class PermissionCheck: - def __init__(self, enforcer): - self.e: casbin.Enforcer = enforcer - - def __call__(self, request: Request, uid: int = Depends(check_uid)): - print('permission check') - request_permission = f"{request.method}:{request.url.path}" - print(request_permission) - if self.e.enforce(f'uid_{uid}', request.url.path, request.method): - print('拥有权限') - return True - else: - print('没有权限') - raise HTTPException(status_code=403, detail="没有权限") - - -adapter = casbin_sqlalchemy_adapter.Adapter(engine) -casbin_enforcer = casbin.Enforcer('server/model.conf', adapter) - -check_permission = PermissionCheck(casbin_enforcer) diff --git a/server/main.py b/server/main.py index d679f1f..a7b9bbf 100644 --- a/server/main.py +++ b/server/main.py @@ -1,18 +1,16 @@ from sqlmodel import Session from fastapi import FastAPI, Depends -from .routers.internal import api, login, user, menu, roles -from .dependencies import check_permission -from .models.internal.api import Api -from .db import engine, get_or_create +from .routers.internal import login, user, menu, roles +from .common.security import auth_check +from .settings import engine -app = FastAPI() +app = FastAPI(dependencies=[Depends(auth_check)]) # 不执行check_permission的,表示不需要权限验证 app.include_router(login.router, tags=['用户登录']) -app.include_router(user.router, tags=['用户管理'], dependencies=[Depends(check_permission)]) -app.include_router(menu.router, tags=['菜单管理'], dependencies=[Depends(check_permission)]) -app.include_router(roles.router, tags=['角色管理'], dependencies=[Depends(check_permission)]) -app.include_router(api.router, tags=['接口管理'], dependencies=[Depends(check_permission)]) +app.include_router(user.router, tags=['用户管理']) +app.include_router(menu.router, tags=['菜单管理']) +app.include_router(roles.router, tags=['角色管理']) @app.on_event("startup") @@ -23,24 +21,6 @@ def startup(): """ print('服务启动后执行服务') print('API接口更新') - with Session(engine) as session: - for url, value in app.openapi()['paths'].items(): - if 'get' in value: - method = 'get' - tags = value[method]['tags'][0] - get_or_create(session, Api, tags=tags, path=url, method='GET', summary=value[method]['summary']) - if 'post' in value: - method = 'post' - tags = value[method]['tags'][0] - get_or_create(session, Api, tags=tags, path=url, method='POST', summary=value[method]['summary']) - if 'put' in value: - method = 'put' - tags = value[method]['tags'][0] - get_or_create(session, Api, tags=tags, path=url, method='PUT', summary=value[method]['summary']) - if 'delete' in value: - method = 'delete' - tags = value[method]['tags'][0] - get_or_create(session, Api, tags=tags, path=url, method='DELETE', summary=value[method]['summary']) @app.on_event("shutdown") diff --git a/server/models/internal/__init__.py b/server/models/internal/__init__.py index 004377f..efb0daf 100644 --- a/server/models/internal/__init__.py +++ b/server/models/internal/__init__.py @@ -1,4 +1,3 @@ from .user import User from .menu import Menu from .role import Role, RoleMenu -from .api import Api diff --git a/server/models/internal/api.py b/server/models/internal/api.py deleted file mode 100644 index 4de6128..0000000 --- a/server/models/internal/api.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import List, TYPE_CHECKING -from sqlmodel import SQLModel, Field, Relationship, Column, Integer -from .relationships import MenuApis - -if TYPE_CHECKING: - from .menu import Menu - - -class ApiBase(SQLModel): - tags: str = Field(max_length=10, sa_column_kwargs={'comment': '标签'}) - path: str = Field(max_length=50, sa_column_kwargs={'comment': 'API路径'}) - method: str = Field(max_length=10, sa_column_kwargs={'comment': 'HTTP方法'}) - summary: str = Field(max_length=20, sa_column_kwargs={'comment': '描述'}) - deprecated: bool = Field(default=False, sa_column_kwargs={'comment': '是否废弃'}) - - -class Api(ApiBase, table=True): - __tablename__ = 'sys_api' - id: int = Field(sa_column=Column('id', Integer, primary_key=True, autoincrement=True)) - menus: List['Menu'] = Relationship(back_populates='apis', link_model=MenuApis) - - -class ApiWithMenus(ApiBase): - id: int - menus: List['Menu'] = [] diff --git a/server/models/internal/menu.py b/server/models/internal/menu.py index 0fc912c..183bfcc 100644 --- a/server/models/internal/menu.py +++ b/server/models/internal/menu.py @@ -1,6 +1,6 @@ -from typing import Optional, List, Any, TYPE_CHECKING +from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship, Column, Boolean, Integer -from .relationships import RoleMenu, MenuApis +from .relationships import RoleMenu class MenuBase(SQLModel): @@ -8,35 +8,23 @@ class MenuBase(SQLModel): name: str = Field(max_length=20, sa_column_kwargs={'comment': '菜单名'}) path: Optional[str] = Field(max_length=100, sa_column_kwargs={'comment': '路径'}) component: Optional[str] = Field(max_length=50, sa_column_kwargs={'comment': '组件'}) + auth: Optional[str] = Field(max_length=50, sa_column_kwargs={'comment': '授权标识'}) type: str = Field(max_length=10, sa_column_kwargs={'comment': '类型'}) parent_id: Optional[int] = Field(sa_column_kwargs={'comment': '父级ID'}) + sort: Optional[float] = Field(sa_column_kwargs={'comment': '菜单排序'}) enable: bool = Field(default=True, sa_column=Column(Boolean, comment='启用')) class Menu(MenuBase, table=True): roles: List["Role"] = Relationship(back_populates="menus", link_model=RoleMenu) - apis: List['Api'] = Relationship(back_populates='menus', link_model=MenuApis) # apis: List['Api'] = Relationship(back_populates="menus", link_model=MenuApi) -class MenuRead(MenuBase): - apis: List['Api'] = [] - - class MenusWithChild(MenuBase): - apis: List['Api'] = [] children: List['MenusWithChild'] = [] -class MenuWithUpdate(MenuBase): - # 更新菜单信息 - # id: Optional[int] - apis: List[int] = [] - - # 底部导入,且延迟注释 from .role import Role -from .api import Api MenusWithChild.update_forward_refs() -MenuRead.update_forward_refs() diff --git a/server/models/internal/relationships.py b/server/models/internal/relationships.py index 8603065..1a4188f 100644 --- a/server/models/internal/relationships.py +++ b/server/models/internal/relationships.py @@ -14,8 +14,3 @@ class UserRole(SQLModel, table=True): user_id: int = Field(foreign_key="user.id", primary_key=True) role_id: int = Field(foreign_key="roles.id", primary_key=True) - -class MenuApis(SQLModel, table=True): - __tablename__ = 'menu_apis' - menu_id: int = Field(foreign_key='menu.id', primary_key=True) - api_id: int = Field(foreign_key='sys_api.id', primary_key=True) diff --git a/server/routers/internal/api.py b/server/routers/internal/api.py deleted file mode 100644 index be499de..0000000 --- a/server/routers/internal/api.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Optional -from sqlmodel import Session -from fastapi import APIRouter, Depends -from ...db import get_session -from ... import crud -from ...schemas.internal.pagination import Pagination -from ...schemas.internal.sysapi import ApiSearch - -router = APIRouter(prefix='/api', ) - - -@router.post('/sysapis/search', summary='获取API列表') -async def get_sysapis(search: Pagination[ApiSearch], - session: Session = Depends(get_session)): - print(search) - total = crud.internal.api.search_total(session, search.search) - print(total) - sys_apis = crud.internal.api.search(session, search) - return { - 'total': total, - 'data': sys_apis - } - - -@router.get('/sysapis/{id}', summary='获取指定API', deprecated=True) -async def get_test(q: Optional[str] = None, direction: str = 'next', id: Optional[int] = 0, - limit: Optional[int] = None, offset_page: Optional[int] = None, - session: Session = Depends(get_session)): - total = crud.internal.api.search_total(session, q) - print(total) - sysapis = crud.internal.api.search(session, q, direction, id, limit, offset_page) - # users_list = [api for api in sysapis] - # print(users_list) - return { - 'total': total, - 'data': sysapis - } diff --git a/server/routers/internal/login.py b/server/routers/internal/login.py index 45c75cb..1727659 100644 --- a/server/routers/internal/login.py +++ b/server/routers/internal/login.py @@ -1,7 +1,6 @@ from typing import List -from fastapi import APIRouter, Depends -from ...dependencies import check_token -from ...db import get_session +from fastapi import APIRouter, Depends, Request +from ...common.database import get_session from sqlmodel import Session, select from sqlalchemy.exc import NoResultFound from ...common.security import create_access_token @@ -43,28 +42,34 @@ async def login(login_form: UserLogin, session: Session = Depends(get_session)): @router.get('/permission', summary='获取权限') -async def get_permission(session: Session = Depends(get_session), token: dict = Depends(check_token)): +async def get_permission(request: Request, session: Session = Depends(get_session)): """ 用户权限请求,返回拥有权限的菜单列表,前端根据返回的菜单列表信息,合成菜单项 :param session: :param token: :return: """ - uid: List[int] = token['uid'] + uid: int = request.state.uid print(f"uid is:{uid}") user: User = crud.internal.user.get(session, uid) print(user.roles) user_menus = [] # admin组用户获取所有菜单列表 - if crud.internal.role.check_admin(session, uid): - menu_list = session.exec(select(Menu)).all() + if uid == 1 or crud.internal.role.check_admin(session, uid): + menu_list = session.exec(select(Menu).where(Menu.type != 'btn').order_by(Menu.sort)).all() + btn_list = session.exec(select(Menu.auth).where(Menu.type == 'btn').where(Menu.auth.is_not(None))).all() else: for role in user.roles: user_menus.extend([menu.id for menu in role.menus]) - menu_list = session.exec(select(Menu).where(Menu.id.in_(set(user_menus)))).all() + 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) - return user_menus + return { + 'menus': user_menus, + 'btns': btn_list + } diff --git a/server/routers/internal/menu.py b/server/routers/internal/menu.py index 43107a0..517c70e 100644 --- a/server/routers/internal/menu.py +++ b/server/routers/internal/menu.py @@ -1,13 +1,12 @@ -from copy import deepcopy from typing import Optional, List from fastapi import APIRouter, Depends, status, HTTPException from sqlmodel import Session -from ...db import get_session -from ...models.internal.menu import Menu, MenusWithChild, MenuWithUpdate, MenuRead -from ...models.internal.api import Api, ApiWithMenus +from ...common.database import get_session +from ...common.auth_casbin import Authority +from ...models.internal.menu import MenuBase, Menu, MenusWithChild from ...common import utils from ... import crud -from ...dependencies import casbin_enforcer +from ...settings import casbin_enforcer router = APIRouter(prefix='/api') @@ -16,41 +15,27 @@ async def get_all_menu(q: Optional[str] = None, session: Session = Depends(get_session)): # 复用crud.get_menu_list,默认role为admin就是返回所有的菜单列表 menu_list: List[Menu] = crud.menu.search(session, q) - menu_list_apis: List[MenuRead] = [] - for menu in menu_list: - new_menu = MenuRead(**menu.dict(), apis=menu.apis) - menu_list_apis.append(new_menu) - user_menus = utils.menu_convert(menu_list_apis) + user_menus = utils.menu_convert(menu_list) return user_menus -@router.get('/menus/tree_apis', summary='获取树形菜单接口') -async def get_menu_apis(session: Session = Depends(get_session)): - apis = crud.internal.api.get_tree(session) - print(apis) - return apis - - -@router.post('/menus', summary="新建菜单", response_model=Menu) -async def add_menu(menu: MenuWithUpdate, session: Session = Depends(get_session)): +@router.post('/menus', summary="新建菜单", response_model=Menu, dependencies=[Depends(Authority("menu:add"))]) +async def add_menu(menu: MenuBase, session: Session = Depends(get_session)): """ # 新建的菜单,还是没有授权给角色的,所以直接新增就行了 :param menu: :param session: :return: """ - apis: List[Api] = crud.internal.api.get_multi(session, menu.apis) - delattr(menu, "apis") db_obj = crud.menu.insert(session, Menu(**menu.dict())) - db_obj.apis = apis session.add(db_obj) session.commit() session.refresh(db_obj) return db_obj -@router.put('/menus', summary="更新菜单") -async def update_menu(menu: MenuWithUpdate, session: Session = Depends(get_session)): +@router.put('/menus', summary="更新菜单", dependencies=[Depends(Authority("menu:update"))]) +async def update_menu(menu: MenuBase, session: Session = Depends(get_session)): """ 更新菜单,涉及到原菜单对应api的更新,则需要更新对应信息 :param menu: @@ -58,32 +43,15 @@ async def update_menu(menu: MenuWithUpdate, session: Session = Depends(get_sessi :return: """ db_obj: Menu = crud.menu.get(session, menu.id) - old_apis: List[ApiWithMenus] = [] - for api in db_obj.apis: - tmp_api = ApiWithMenus(**api.dict()) - tmp_api.menus = [menu.id for menu in api.menus] - old_apis.append(tmp_api) - print('old_apis is:') - print(old_apis) - apis: List[Api] = crud.internal.api.get_multi(session, menu.apis) - delattr(menu, "apis") new_obj: Menu = crud.menu.update(session, db_obj, menu) - print(apis) - new_obj.apis = apis - for role in new_obj.roles: - for api in old_apis: - if len(api.menus) > 1: - continue - casbin_enforcer.delete_permission_for_user(f'role_{role.id}', api.path, api.method) - for api in apis: - casbin_enforcer.add_permission_for_user(f'role_{role.id}', api.path, api.method) session.add(new_obj) session.commit() session.refresh(new_obj) return new_obj -@router.delete('/menus/{id}', summary='删除菜单', status_code=status.HTTP_204_NO_CONTENT) +@router.delete('/menus/{id}', summary='删除菜单', status_code=status.HTTP_204_NO_CONTENT, + dependencies=[Depends(Authority("menu:del"))]) async def del_menu(id: int, session: Session = Depends(get_session)): db_obj = crud.menu.get(session, id) if len(db_obj.roles) > 0: diff --git a/server/routers/internal/roles.py b/server/routers/internal/roles.py index 969f621..580c760 100644 --- a/server/routers/internal/roles.py +++ b/server/routers/internal/roles.py @@ -2,8 +2,8 @@ from fastapi import APIRouter, Depends, status from fastapi.exceptions import HTTPException from sqlmodel import Session -from ...dependencies import check_permission -from ...db import get_session +from ...common.auth_casbin import Authority +from ...common.database import get_session from ... import crud from ...models.internal import Role, Menu from ...models.internal.role import RoleWithMenus, RoleInsert, RoleUpdate @@ -11,7 +11,7 @@ from ...schemas.internal.roles import RoleSearch from ...common.utils import menu_convert -router = APIRouter(prefix='/api', dependencies=[Depends(check_permission), ]) +router = APIRouter(prefix='/api') @router.get('/roles/enable-menus', summary='获取角色菜单') @@ -48,7 +48,7 @@ async def get_roles(search: Pagination[RoleSearch], session: Session = Depends(g } -@router.post('/roles', summary="新建角色") +@router.post('/roles', summary="新建角色", dependencies=[Depends(Authority('role:add'))]) async def add_roles(role_info: RoleInsert, session: Session = Depends(get_session)): print(role_info) enable_menus = role_info.menus @@ -58,7 +58,7 @@ async def add_roles(role_info: RoleInsert, session: Session = Depends(get_sessio return db_obj -@router.put('/roles', summary="更新角色") +@router.put('/roles', summary="更新角色", dependencies=[Depends(Authority('role:update'))]) async def update_roles(role_info: RoleUpdate, session: Session = Depends(get_session)): print(role_info) if role_info.name == 'admin': @@ -71,7 +71,8 @@ async def update_roles(role_info: RoleUpdate, session: Session = Depends(get_ses return db_obj -@router.delete('/roles/{id}', summary='删除角色', status_code=status.HTTP_204_NO_CONTENT) +@router.delete('/roles/{id}', summary='删除角色', dependencies=[Depends(Authority('role:del'))], + status_code=status.HTTP_204_NO_CONTENT) async def del_role(id: int, session: Session = Depends(get_session)): db_obj = crud.internal.role.get(session, id) if db_obj.name == 'admin': diff --git a/server/routers/internal/user.py b/server/routers/internal/user.py index 10fc226..52d980f 100644 --- a/server/routers/internal/user.py +++ b/server/routers/internal/user.py @@ -2,8 +2,9 @@ from sqlmodel import Session, select from sqlalchemy.exc import NoResultFound from fastapi import APIRouter, Depends, status, HTTPException -from ...dependencies import casbin_enforcer -from ...db import get_session +from ...settings import casbin_enforcer +from ...common.auth_casbin import Authority +from ...common.database import get_session from ...models.internal import User, Role from ...models.internal.user import UserCreateWithRoles, UserReadWithRoles, UserUpdateWithRoles, UserUpdatePassword, \ UserWithOutPasswd @@ -14,7 +15,7 @@ @router.get('/users/roles', summary='获取角色') -async def get_roles(id: Optional[int] = None, session: Session = Depends(get_session, )): +async def get_roles(id: Optional[int] = None, session: Session = Depends(get_session)): if id is None: # 添加新用户时无用户id roles = [] @@ -69,7 +70,7 @@ async def get_all_user(search: Pagination[UserWithOutPasswd], } -@router.post('/users', summary="新建用户") +@router.post('/users', summary="新建用户",dependencies=[Depends(Authority("user:add"))]) async def update_user(user_info: UserCreateWithRoles, session: Session = Depends(get_session)): """ 更新用户信息的所有操作,可涉及更新用户名、密码、角色等 @@ -85,13 +86,13 @@ async def update_user(user_info: UserCreateWithRoles, session: Session = Depends return user -@router.put('/users/password', summary='重置密码') +@router.put('/users/password', summary='重置密码', dependencies=[Depends(Authority('user:reset'))]) async def update_password(user: UserUpdatePassword, session: Session = Depends(get_session)): crud.internal.user.update_passwd(session, uid=user.id, passwd=user.password) @router.put('/users/{uid}', - summary='更新用户', status_code=status.HTTP_204_NO_CONTENT) + summary='更新用户', dependencies=[Depends(Authority("user:update"))],status_code=status.HTTP_204_NO_CONTENT) async def update_user(uid: int, user_info: UserUpdateWithRoles, session: Session = Depends(get_session)): """ 更新用户信息的所有操作,可涉及更新用户名、密码、角色等 @@ -110,7 +111,7 @@ async def update_user(uid: int, user_info: UserUpdateWithRoles, session: Session return user -@router.delete('/users/{uid}', summary='删除用户', status_code=status.HTTP_204_NO_CONTENT) +@router.delete('/users/{uid}', summary='删除用户',dependencies=[Depends(Authority("user:del"))], status_code=status.HTTP_204_NO_CONTENT) async def delete_user(uid: int, session: Session = Depends(get_session)): try: user = session.exec(select(User).where(User.id == uid)).one() diff --git a/server/settings.py b/server/settings.py new file mode 100644 index 0000000..ef76e5e --- /dev/null +++ b/server/settings.py @@ -0,0 +1,29 @@ +import casbin_sqlalchemy_adapter +import casbin +from typing import List +from pathlib import Path +from pydantic import BaseSettings +from sqlmodel import create_engine + + +class APISettings(BaseSettings): + # token加密相关参数 + SECRET_KEY: str = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" + ALGORITHM: str = "HS256" + ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 + + CASBIN_MODEL_PATH: str = "server/model.conf" + # sql数据库信息 + DATABASE_URI = "mysql+pymysql://root:123456@192.168.137.129/devops" + # 白名单,不需要进行任何验证即可访问 + NO_VERIFY_URL: List = [ + '/', + '/api/login', + ] + + +settings = APISettings() + +engine = create_engine(settings.DATABASE_URI, future=False) +adapter = casbin_sqlalchemy_adapter.Adapter(engine) +casbin_enforcer = casbin.Enforcer(settings.CASBIN_MODEL_PATH, adapter) diff --git a/www/src/App.vue b/www/src/App.vue index 9489f83..df8366c 100644 --- a/www/src/App.vue +++ b/www/src/App.vue @@ -1,54 +1,59 @@ diff --git a/www/src/api/menus.js b/www/src/api/menus.js index 82289f6..9f34511 100644 --- a/www/src/api/menus.js +++ b/www/src/api/menus.js @@ -5,5 +5,4 @@ export const GetAllMenus = (q) => GET('/api/menus', { q }) export const PostNewMenu = (menu) => POST('/api/menus', menu ) export const PutMenu = (menu) => PUT('/api/menus', menu) export const DeleteMenu = (id) => DELETE('/api/menus/' + id) -export const GetMenuApis = (id) => GET('/api/menus/' + id + '/apis') -export const GetMenuTreeApis = () => GET('/api/menus/tree_apis') \ No newline at end of file +export const GetMenuApis = (id) => GET('/api/menus/' + id + '/apis') \ No newline at end of file diff --git a/www/src/composables/useMenu.js b/www/src/composables/useMenu.js index c170e57..6b3cb65 100644 --- a/www/src/composables/useMenu.js +++ b/www/src/composables/useMenu.js @@ -2,25 +2,12 @@ import {reactive, ref, watch, computed} from 'vue' export default function (form, menuData, emit) { const selectData = reactive(form) - const selectApis = ref([]) - - if (selectData.id !== null) { - for (const api of selectData.apis) { - console.log(api) - selectApis.value.push(api.id) - } - } watch(selectData, (newData) => { console.log('watch selectData change:' + selectData) - selectData.apis = selectApis.value emit('update:form', selectData) }) - watch(selectApis, (newValue) => { - console.log('selectApi watch') - selectData.apis = selectApis.value - }) const menuMap = (arr) => { const menu = arr.filter(item => item.type !== 'btn') @@ -40,7 +27,6 @@ export default function (form, menuData, emit) { return { selectData, - selectApis, cascaderMenu } } \ No newline at end of file diff --git a/www/src/router/index.js b/www/src/router/index.js index dc78c1d..3461ec9 100644 --- a/www/src/router/index.js +++ b/www/src/router/index.js @@ -2,57 +2,58 @@ import {createRouter, createWebHistory} from 'vue-router' export const constantRouterMap = [ - { - path: '/', - name: 'home', - component: () => import('@/views/Layout'), - }, - { - path: '/dashboard', - name: 'dashboard', - component: ()=>import('@/views/Layout'), - children:[ - { - path:'', - component: ()=>import('@/views/DashBoard') - } - ] - }, - { - path: '/login', - component: () => import('@/views/Login/index'), - // hidden: true - }, - { - path: '/404', - component: () => import('@/views/errorPage/404'), - hidden: true - }, - { - path: '/401', - component: () => import('@/views/errorPage/401'), - hidden: true - }, + { + path: '/', + name: 'home', + component: () => import('@/views/Layout'), + }, + { + path: '/dashboard', + name: 'dashboard', + component: () => import('@/views/Layout'), + children: [ + { + path: '', + component: () => import('@/views/DashBoard') + } + ] + }, + { + path: '/login', + component: () => import('@/views/Login/index'), + // hidden: true + }, + // { + // path: '/404', + // name: 'notFound', + // component: () => import('@/views/errorPage/NotFound'), + // hidden: true + // }, + // { + // path:'/:patchMatch(.*)*', + // name:'not-found', + // redirect:'/404' + // } - // { - // path: '/user', - // component: Layout, - // hidden: true, - // redirect: 'noredirect', - // children: [ - // { - // path: 'center', - // component: () => import('@/views/system/user/center'), - // name: '个人中心', - // meta: { title: '个人中心', icon: 'user' } - // } - // ] - // } - // { path: '*', redirect: '/404', hidden: true } + // { + // path: '/user', + // component: Layout, + // hidden: true, + // redirect: 'noredirect', + // children: [ + // { + // path: 'center', + // component: () => import('@/views/system/user/center'), + // name: '个人中心', + // meta: { title: '个人中心', icon: 'user' } + // } + // ] + // } + // { path: '*', redirect: '/404', hidden: true } ] export default createRouter({ - history: createWebHistory(), - // scrollBehavior: () => ({ y: 0 }), - routes: constantRouterMap + history: createWebHistory(), + // scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap }) \ No newline at end of file diff --git a/www/src/stores/index.js b/www/src/stores/index.js index 00f18fe..a7f443d 100644 --- a/www/src/stores/index.js +++ b/www/src/stores/index.js @@ -17,31 +17,10 @@ export const useStore = defineStore('user', { name: '', avatar: '', asyncRoutes: [], + buttons:[], } }, - getters: { - buttons(state) { - let btns = [] - - function findAllBtn(list) { - list.forEach(val => { - if (val.type !== 'btn') { - if (val.children && val.children.length > 0) { - findAllBtn(val.children) - } - } else { - btns.push(val.path) - } - }) - } - - findAllBtn(state.asyncRoutes) - console.log('button:') - console.log(btns) - return btns - }, - }, actions: { //执行登录请求,获取token @@ -88,7 +67,8 @@ export const useStore = defineStore('user', { GetUserPermission().then(response => { console.log('permission response is:') console.log(response) - this.asyncRoutes = response + this.asyncRoutes = response.menus + this.buttons = response.btns resolve(response) }).catch(error => { reject(error) diff --git a/www/src/views/Layout.vue b/www/src/views/Layout.vue index b3f95d0..9869db8 100644 --- a/www/src/views/Layout.vue +++ b/www/src/views/Layout.vue @@ -19,7 +19,7 @@