Optimize api with semantic HTTP status codes (#681)

This commit is contained in:
Dylan
2025-06-23 22:18:17 +08:00
committed by GitHub
parent f9bfe8f510
commit 6d5e741d94
20 changed files with 91 additions and 62 deletions
+2 -2
View File
@@ -93,7 +93,7 @@ class AuthService:
user = await self.user_verify(db, obj.username, obj.password)
captcha_code = await redis_client.get(f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}')
if not captcha_code:
raise errors.ForbiddenError(msg='验证码失效,请重新获取')
raise errors.RequestError(msg='验证码失效,请重新获取')
if captcha_code.lower() != obj.captcha.lower():
raise errors.CustomError(error=CustomErrorCode.CAPTCHA_ERROR)
await redis_client.delete(f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}')
@@ -122,7 +122,7 @@ class AuthService:
except errors.NotFoundError as e:
log.error('登陆错误: 用户名不存在')
raise errors.NotFoundError(msg=e.msg)
except (errors.ForbiddenError, errors.CustomError) as e:
except (errors.RequestError, errors.CustomError) as e:
if not user:
log.error('登陆错误: 用户密码有误')
task = BackgroundTask(
@@ -87,7 +87,7 @@ class DataRuleService:
async with async_db_session.begin() as db:
data_rule = await data_rule_dao.get_by_name(db, obj.name)
if data_rule:
raise errors.ForbiddenError(msg='数据规则已存在')
raise errors.ConflictError(msg='数据规则已存在')
await data_rule_dao.create(db, obj)
@staticmethod
@@ -105,7 +105,7 @@ class DataRuleService:
raise errors.NotFoundError(msg='数据规则不存在')
if data_rule.name != obj.name:
if await data_rule_dao.get_by_name(db, obj.name):
raise errors.ForbiddenError(msg='数据规则已存在')
raise errors.ConflictError(msg='数据规则已存在')
count = await data_rule_dao.update(db, pk, obj)
return count
@@ -78,7 +78,7 @@ class DataScopeService:
async with async_db_session.begin() as db:
data_scope = await data_scope_dao.get_by_name(db, obj.name)
if data_scope:
raise errors.ForbiddenError(msg='数据范围已存在')
raise errors.ConflictError(msg='数据范围已存在')
await data_scope_dao.create(db, obj)
@staticmethod
@@ -96,7 +96,7 @@ class DataScopeService:
raise errors.NotFoundError(msg='数据范围不存在')
if data_scope.name != obj.name:
if await data_scope_dao.get_by_name(db, obj.name):
raise errors.ForbiddenError(msg='数据范围已存在')
raise errors.ConflictError(msg='数据范围已存在')
count = await data_scope_dao.update(db, pk, obj)
for role in await data_scope.awaitable_attrs.roles:
for user in await role.awaitable_attrs.users:
+4 -4
View File
@@ -61,7 +61,7 @@ class DeptService:
async with async_db_session.begin() as db:
dept = await dept_dao.get_by_name(db, obj.name)
if dept:
raise errors.ForbiddenError(msg='部门名称已存在')
raise errors.ConflictError(msg='部门名称已存在')
if obj.parent_id:
parent_dept = await dept_dao.get(db, obj.parent_id)
if not parent_dept:
@@ -83,7 +83,7 @@ class DeptService:
raise errors.NotFoundError(msg='部门不存在')
if dept.name != obj.name:
if await dept_dao.get_by_name(db, obj.name):
raise errors.ForbiddenError(msg='部门名称已存在')
raise errors.ConflictError(msg='部门名称已存在')
if obj.parent_id:
parent_dept = await dept_dao.get(db, obj.parent_id)
if not parent_dept:
@@ -104,10 +104,10 @@ class DeptService:
async with async_db_session.begin() as db:
dept = await dept_dao.get_with_relation(db, pk)
if dept.users:
raise errors.ForbiddenError(msg='部门下存在用户,无法删除')
raise errors.ConflictError(msg='部门下存在用户,无法删除')
children = await dept_dao.get_children(db, pk)
if children:
raise errors.ForbiddenError(msg='部门下存在子部门,无法删除')
raise errors.ConflictError(msg='部门下存在子部门,无法删除')
count = await dept_dao.delete(db, pk)
for user in dept.users:
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
+3 -3
View File
@@ -78,7 +78,7 @@ class MenuService:
async with async_db_session.begin() as db:
title = await menu_dao.get_by_title(db, obj.title)
if title:
raise errors.ForbiddenError(msg='菜单标题已存在')
raise errors.ConflictError(msg='菜单标题已存在')
if obj.parent_id:
parent_menu = await menu_dao.get(db, obj.parent_id)
if not parent_menu:
@@ -100,7 +100,7 @@ class MenuService:
raise errors.NotFoundError(msg='菜单不存在')
if menu.title != obj.title:
if await menu_dao.get_by_title(db, obj.title):
raise errors.ForbiddenError(msg='菜单标题已存在')
raise errors.ConflictError(msg='菜单标题已存在')
if obj.parent_id:
parent_menu = await menu_dao.get(db, obj.parent_id)
if not parent_menu:
@@ -124,7 +124,7 @@ class MenuService:
async with async_db_session.begin() as db:
children = await menu_dao.get_children(db, pk)
if children:
raise errors.ForbiddenError(msg='菜单下存在子菜单,无法删除')
raise errors.ConflictError(msg='菜单下存在子菜单,无法删除')
menu = await menu_dao.get(db, pk)
count = await menu_dao.delete(db, pk)
if menu:
+11 -11
View File
@@ -55,24 +55,24 @@ class PluginService:
contents = await file.read()
file_bytes = io.BytesIO(contents)
if not zipfile.is_zipfile(file_bytes):
raise errors.ForbiddenError(msg='插件压缩包格式非法')
raise errors.RequestError(msg='插件压缩包格式非法')
with zipfile.ZipFile(file_bytes) as zf:
# 校验压缩包
plugin_namelist = zf.namelist()
plugin_name = plugin_namelist[0].split('/')[0]
if not plugin_namelist or plugin_name not in file.filename:
raise errors.ForbiddenError(msg='插件压缩包内容非法')
raise errors.RequestError(msg='插件压缩包内容非法')
if (
len(plugin_namelist) <= 3
or f'{plugin_name}/plugin.toml' not in plugin_namelist
or f'{plugin_name}/README.md' not in plugin_namelist
):
raise errors.ForbiddenError(msg='插件压缩包内缺少必要文件')
raise errors.RequestError(msg='插件压缩包内缺少必要文件')
# 插件是否可安装
full_plugin_path = os.path.join(PLUGIN_DIR, plugin_name)
if os.path.exists(full_plugin_path):
raise errors.ForbiddenError(msg='此插件已安装')
raise errors.ConflictError(msg='此插件已安装')
else:
os.makedirs(full_plugin_path, exist_ok=True)
@@ -99,11 +99,11 @@ class PluginService:
"""
match = is_git_url(repo_url)
if not match:
raise errors.ForbiddenError(msg='Git 仓库地址格式非法')
raise errors.RequestError(msg='Git 仓库地址格式非法')
repo_name = match.group('repo')
plugins = await redis_client.lrange(settings.PLUGIN_REDIS_PREFIX, 0, -1)
if repo_name in plugins:
raise errors.ForbiddenError(msg=f'{repo_name} 插件已安装')
raise errors.ConflictError(msg=f'{repo_name} 插件已安装')
try:
porcelain.clone(repo_url, os.path.join(PLUGIN_DIR, repo_name), checkout=True)
except Exception as e:
@@ -124,11 +124,11 @@ class PluginService:
"""
if type == PluginType.zip:
if not file:
raise errors.ForbiddenError(msg='ZIP 压缩包不能为空')
raise errors.RequestError(msg='ZIP 压缩包不能为空')
await self.install_zip(file=file)
elif type == PluginType.git:
if not repo_url:
raise errors.ForbiddenError(msg='Git 仓库地址不能为空')
raise errors.RequestError(msg='Git 仓库地址不能为空')
await self.install_git(repo_url=repo_url)
@staticmethod
@@ -141,7 +141,7 @@ class PluginService:
"""
plugin_dir = os.path.join(PLUGIN_DIR, plugin)
if not os.path.exists(plugin_dir):
raise errors.ForbiddenError(msg='插件不存在')
raise errors.NotFoundError(msg='插件不存在')
await uninstall_requirements_async(plugin)
bacup_dir = os.path.join(PLUGIN_DIR, f'{plugin}.{timezone.now().strftime("%Y%m%d%H%M%S")}.backup')
shutil.move(plugin_dir, bacup_dir)
@@ -159,7 +159,7 @@ class PluginService:
"""
plugin_info = await redis_client.get(f'{settings.PLUGIN_REDIS_PREFIX}:info:{plugin}')
if not plugin_info:
raise errors.ForbiddenError(msg='插件不存在')
raise errors.NotFoundError(msg='插件不存在')
plugin_info = json.loads(plugin_info)
# 更新持久缓存状态
@@ -184,7 +184,7 @@ class PluginService:
"""
plugin_dir = os.path.join(PLUGIN_DIR, plugin)
if not os.path.exists(plugin_dir):
raise errors.ForbiddenError(msg='插件不存在')
raise errors.NotFoundError(msg='插件不存在')
bio = io.BytesIO()
with zipfile.ZipFile(bio, 'w') as zf:
+2 -2
View File
@@ -98,7 +98,7 @@ class RoleService:
async with async_db_session.begin() as db:
role = await role_dao.get_by_name(db, obj.name)
if role:
raise errors.ForbiddenError(msg='角色已存在')
raise errors.ConflictError(msg='角色已存在')
await role_dao.create(db, obj)
@staticmethod
@@ -116,7 +116,7 @@ class RoleService:
raise errors.NotFoundError(msg='角色不存在')
if role.name != obj.name:
if await role_dao.get_by_name(db, obj.name):
raise errors.ForbiddenError(msg='角色已存在')
raise errors.ConflictError(msg='角色已存在')
count = await role_dao.update(db, pk, obj)
for user in await role.awaitable_attrs.users:
await redis_client.delete_prefix(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
+16 -15
View File
@@ -81,10 +81,10 @@ class UserService:
async with async_db_session.begin() as db:
superuser_verify(request)
if await user_dao.get_by_username(db, obj.username):
raise errors.ForbiddenError(msg='用户名已注册')
raise errors.ConflictError(msg='用户名已注册')
obj.nickname = obj.nickname if obj.nickname else f'#{random.randrange(88888, 99999)}'
if not obj.password:
raise errors.ForbiddenError(msg='密码不允许为空')
raise errors.RequestError(msg='密码不允许为空')
if not await dept_dao.get(db, obj.dept_id):
raise errors.NotFoundError(msg='部门不存在')
for role_id in obj.roles:
@@ -110,7 +110,7 @@ class UserService:
raise errors.ForbiddenError(msg='只能修改自己的信息')
if obj.username != user.username:
if await user_dao.get_by_username(db, obj.username):
raise errors.ForbiddenError(msg='用户名已注册')
raise errors.ConflictError(msg='用户名已注册')
for role_id in obj.roles:
if not await role_dao.get(db, role_id):
raise errors.NotFoundError(msg='角色不存在')
@@ -222,16 +222,17 @@ class UserService:
:param type: 权限类型
:return:
"""
if type == UserPermissionType.superuser:
count = await self.update_superuser(request=request, pk=pk)
elif type == UserPermissionType.staff:
count = await self.update_staff(request=request, pk=pk)
elif type == UserPermissionType.status:
count = await self.update_status(request=request, pk=pk)
elif type == UserPermissionType.multi_login:
count = await self.update_multi_login(request=request, pk=pk)
else:
raise errors.ForbiddenError(msg='权限类型不存在')
match type:
case UserPermissionType.superuser:
count = await self.update_superuser(request=request, pk=pk)
case UserPermissionType.staff:
count = await self.update_staff(request=request, pk=pk)
case UserPermissionType.status:
count = await self.update_status(request=request, pk=pk)
case UserPermissionType.multi_login:
count = await self.update_multi_login(request=request, pk=pk)
case _:
raise errors.RequestError(msg='权限类型不存在')
return count
@staticmethod
@@ -248,9 +249,9 @@ class UserService:
if not user:
raise errors.NotFoundError(msg='用户不存在')
if not password_verify(obj.old_password, user.password):
raise errors.ForbiddenError(msg='原密码错误')
raise errors.RequestError(msg='原密码错误')
if obj.new_password != obj.confirm_password:
raise errors.ForbiddenError(msg='密码输入不一致')
raise errors.RequestError(msg='密码输入不一致')
new_pwd = get_hash_password(obj.new_password, user.salt)
count = await user_dao.reset_password(db, user.id, new_pwd)
key_prefix = [