Skip to content

Postgres fix + English localization #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
282 changes: 126 additions & 156 deletions README.md

Large diffs are not rendered by default.

86 changes: 45 additions & 41 deletions fastapi_user_auth/admin/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from fastapi_amis_admin.amis.constants import LevelEnum
from fastapi_amis_admin.crud.schema import BaseApiOut
from fastapi_amis_admin.models import Field
from fastapi_amis_admin.utils.translation import i18n as _
from pydantic import BaseModel
from pydantic.fields import ModelField
from starlette.requests import Request
Expand Down Expand Up @@ -39,7 +40,7 @@ def get_admin_select_permission_rows(admin: PageSchemaAdmin) -> List[Dict[str, A
for perm in admin.select_permissions:
rows.append(
{
"label": "仅限数据-" + perm.label,
"label": _("Restricted to Data") + '-'+ perm.label,
"rol": f"{admin.unique_id}#page:select:{perm.name}#page:select",
"reverse": perm.reverse,
}
Expand Down Expand Up @@ -69,7 +70,7 @@ def get_admin_field_permission_rows(admin: PageSchemaAdmin, action: str) -> List
return []
rows.append(
{
"label": "全部",
"label": _("All"),
"rol": f"{admin.unique_id}#page:{action}:*#page:{action}",
}
)
Expand All @@ -93,7 +94,7 @@ def __init__(self, admin, **kwargs):
elif self.admin.model.__table__.name == User.__tablename__:
self._subject = "u"
else:
raise Exception("暂不支持的主体模型")
raise Exception(_("Unsupported subject model"))

async def get_subject_by_id(self, item_id: str) -> str:
# 从数据库获取用户选择的数据列表
Expand All @@ -117,23 +118,23 @@ class UpdateSubRolesAction(BaseSubAction):
action = ActionType.Dialog(
name="update_subject_roles",
icon="fa fa-check",
tooltip="设置角色",
tooltip=_("Set Role"),
dialog=amis.Dialog(),
level=LevelEnum.warning,
)

class schema(BaseModel):
role_keys: str = Field(
None,
title="角色列表",
title=_("Role List"),
amis_form_item=amis.Transfer(
selectMode="table",
resultListModeFollowSelect=True,
columns=[
# {"name": "key", "label": "角色标识"},
{"name": "name", "label": "角色名称"},
{"name": "desc", "label": "角色描述"},
{"name": "role_names", "label": "子角色"},
# {"name": "key", "label": _("Role Identifier")},
{"name": "name", "label": _("Role Name")},
{"name": "desc", "label": _("Role Description")},
{"name": "role_names", "label": _("Sub Roles")},
],
source="",
valueField="key",
Expand All @@ -158,18 +159,18 @@ async def get_init_data(self, request: Request, **kwargs) -> BaseApiOut[Any]:
return BaseApiOut(data=self.schema())
subject = await self.get_subject_by_id(item_id)
if not subject:
return BaseApiOut(status=0, msg="暂不支持的模型")
return BaseApiOut(status=0, msg=_("Unsupported model"))
role_keys = await self.site.auth.enforcer.get_roles_for_user(subject)
return BaseApiOut(data=self.schema(role_keys=",".join(role_keys).replace("r:", "")))

async def handle(self, request: Request, item_id: List[str], data: schema, **kwargs):
"""更新角色Casbin权限"""
subject = await self.get_subject_by_id(item_id[0])
if not subject:
return BaseApiOut(status=0, msg="暂不支持的模型")
return BaseApiOut(status=0, msg=_("Unsupported model"))
identity = await self.site.auth.get_current_user_identity(request) or SystemUserEnum.GUEST
if subject == "u:" + identity:
return BaseApiOut(status=0, msg="不能修改自己的权限")
return BaseApiOut(status=0, msg=_("Cannot modify your own permissions"))
enforcer: AsyncEnforcer = self.site.auth.enforcer
role_keys = [f"r:{role}" for role in data.role_keys.split(",") if role]
if role_keys and identity not in [SystemUserEnum.ROOT, SystemUserEnum.ADMIN]:
Expand All @@ -190,7 +191,7 @@ class BaseSubPermAction(BaseSubAction):
action = ActionType.Dialog(
name="view_subject_permissions",
icon="fa fa-check",
tooltip="查看权限",
tooltip=_("View Permissions"),
dialog=amis.Dialog(),
level=LevelEnum.warning,
)
Expand All @@ -199,7 +200,7 @@ class BaseSubPermAction(BaseSubAction):
class schema(BaseModel):
permissions: str = Field(
None,
title="权限列表",
title=_("Permission List"),
amis_form_item=amis.InputTree(
multiple=True,
source="",
Expand Down Expand Up @@ -236,7 +237,7 @@ class ViewSubPagePermAction(BaseSubPermAction):
action = ActionType.Dialog(
name="view_subject_page_permissions",
icon="fa fa-check",
tooltip="查看页面权限",
tooltip=_("View Page Permissions"),
dialog=amis.Dialog(actions=[]),
level=LevelEnum.warning,
)
Expand All @@ -254,13 +255,14 @@ async def get_init_data(self, request: Request, **kwargs) -> BaseApiOut[Any]:
return BaseApiOut(data=self.schema())
subject = await self.get_subject_by_id(item_id)
if not subject:
return BaseApiOut(status=0, msg="暂不支持的模型")
permissions = await get_subject_page_permissions(self.site.auth.enforcer, subject=subject, implicit=self._implicit)
return BaseApiOut(status=0, msg=_("Unsupported model"))
permissions = await get_subject_page_permissions(self.site.auth.enforcer, subject=subject,
implicit=self._implicit)
permissions = [perm.replace("#allow", "") for perm in permissions if perm.endswith("#allow")]
return BaseApiOut(data=self.schema(permissions=",".join(permissions)))

async def handle(self, request: Request, item_id: List[str], data: BaseModel, **kwargs):
return BaseApiOut(status=1, msg="请通过的【设置权限】更新设置!")
return BaseApiOut(status=1, msg=_("Please update settings through 'Set Permissions'!"))


class UpdateSubDataPermAction(BaseSubPermAction):
Expand All @@ -271,18 +273,18 @@ class UpdateSubDataPermAction(BaseSubPermAction):
action = ActionType.Dialog(
name="update_subject_data_permissions",
icon="fa fa-gavel",
tooltip="更新数据权限",
dialog=amis.Dialog(actions=[amis.Action(actionType="submit", label="保存", close=False, primary=True)]),
tooltip=_("Update Data Permissions"),
dialog=amis.Dialog(actions=[amis.Action(actionType="submit", label=_("Save"), close=False, primary=True)]),
level=LevelEnum.warning,
)

# 创建动作表单数据模型
class schema(BaseSubPermAction.schema):
effect_matrix: list = Field(
None,
title="当前权限",
title=_("Current Permissions"),
amis_form_item=amis.MatrixCheckboxes(
rowLabel="权限名称",
rowLabel=_("Permission Name"),
multiple=False,
singleSelectMode="row",
source="",
Expand All @@ -291,9 +293,9 @@ class schema(BaseSubPermAction.schema):
)
policy_matrix: list = Field(
None,
title="权限配置",
title=_("Permission Configuration"),
amis_form_item=amis.MatrixCheckboxes(
rowLabel="名称",
rowLabel=_("Name"),
multiple=False,
singleSelectMode="row",
yCheckAll=True,
Expand Down Expand Up @@ -326,22 +328,23 @@ async def _get_admin_action_options(request: Request, item_id: str):

@self.router.get("/get_admin_action_perm_options", response_model=BaseApiOut)
async def get_admin_action_perm_options(
request: Request,
permission: str = "",
item_id: str = "",
type: str = "policy",
request: Request,
permission: str = "",
item_id: str = "",
type: str = "policy",
):
from fastapi_amis_admin.utils.translation import i18n as _ # TODO: WFT ?
columns = [
{
"label": "默认",
"label": _("Default"),
"col": "default",
},
{
"label": "是",
"label": _("Yes"),
"col": "allow",
},
{
"label": "否",
"label": _("No"),
"col": "deny",
},
]
Expand Down Expand Up @@ -392,7 +395,7 @@ async def handle(self, request: Request, item_id: List[str], data: BaseModel, **
subject = await self.get_subject_by_id(item_id[0])
identity = await self.site.auth.get_current_user_identity(request) or SystemUserEnum.GUEST
if subject == "u:" + identity:
return BaseApiOut(status=0, msg="不能修改自己的权限")
return BaseApiOut(status=0, msg=_("Cannot modify your own permissions"))
msg = await update_subject_data_permissions(
self.site.auth.enforcer,
subject=subject,
Expand All @@ -410,7 +413,7 @@ class UpdateSubPagePermsAction(ViewSubPagePermAction):
action = ActionType.Dialog(
name="update_subject_page_permissions",
icon="fa fa-gavel",
tooltip="更新页面权限",
tooltip=_("Update Page Permissions"),
dialog=amis.Dialog(),
level=LevelEnum.warning,
)
Expand All @@ -419,10 +422,10 @@ async def handle(self, request: Request, item_id: List[str], data: BaseModel, **
"""更新角色Casbin权限"""
subject = await self.get_subject_by_id(item_id[0])
if not subject:
return BaseApiOut(status=0, msg="暂不支持的模型")
return BaseApiOut(status=0, msg=_("Unsupported model"))
identity = await self.site.auth.get_current_user_identity(request) or SystemUserEnum.GUEST
if subject == "u:" + identity:
return BaseApiOut(status=0, msg="不能修改自己的权限")
return BaseApiOut(status=0, msg=_("Cannot modify your own permissions"))
# 权限列表
permissions = [perm for perm in data.permissions.split(",") if perm and perm.endswith("#page")] # 分割权限列表,去除空值
enforcer: AsyncEnforcer = self.site.auth.enforcer
Expand All @@ -439,20 +442,20 @@ class CopyUserAuthLinkAction(ModelAction):
action = amis.ActionType.Dialog(
name="copy_user_auth_link",
icon="fa fa-link",
tooltip="用户免登录链接",
tooltip=_("User Login-Free Link"),
level=amis.LevelEnum.danger,
dialog=amis.Dialog(
size=amis.SizeEnum.md,
title="用户免登录链接",
title=_("User Login-Free Link"),
),
)
form_init = True
form = amis.Form(static=True, disabled=True) # type: ignore # 禁用表单

class schema(UsernameMixin, PkMixin):
auth_url: str = Field(
title="授权链接",
description="复制链接到浏览器打开即可免登录",
title=_("Authorization Link"),
description=_("Copy the link and open it in the browser to log in without credentials"),
amis_form_item=amis.Static(
copyable=True,
),
Expand All @@ -470,8 +473,9 @@ async def get_init_data(self, request: Request, **kwargs) -> BaseApiOut[Any]:
}
token = await auth.backend.token_store.write_token(token_data)
return BaseApiOut(
msg="操作成功",
data={**token_data, "auth_url": f"{str(request.base_url)[:-1]}{self.site.router_path}/login_by_token?token={token}"},
msg=_("Operation successful"),
data={**token_data,
"auth_url": f"{str(request.base_url)[:-1]}{self.site.router_path}/login_by_token?token={token}"},
)

def register_router(self):
Expand Down
33 changes: 18 additions & 15 deletions fastapi_user_auth/admin/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@


def attach_page_head(page: Page) -> Page:
desc = _("Amis is a low-code front-end framework that reduces page development effort and greatly improves efficiency")
desc = _(
"Amis is a low-code front-end framework that reduces page development effort and greatly improves efficiency")
page.body = [
Html(
html=f'<div style="display: flex; justify-content: center; align-items: center; margin: 96px 0px 8px;">'
f'<img src="https://baidu.gitee.io/amis/static/favicon_b3b0647.png" alt="logo" style="margin-right: 8px; '
f'width: 48px;"><span style="font-size: 32px; font-weight: bold;">Amis Admin</span></div>'
f'<div style="width: 100%; text-align: center; color: rgba(0, 0, 0, 0.45); margin-bottom: 40px;">{desc}</div>'
f'<img src="https://baidu.gitee.io/amis/static/favicon_b3b0647.png" alt="logo" style="margin-right: 8px; '
f'width: 48px;"><span style="font-size: 32px; font-weight: bold;">Amis Admin</span></div>'
f'<div style="width: 100%; text-align: center; color: rgba(0, 0, 0, 0.45); margin-bottom: 40px;">{desc}</div>'
),
Grid(columns=[{"body": [page.body], "lg": 2, "md": 4, "valign": "middle"}], align="center", valign="middle"),
]
Expand Down Expand Up @@ -149,7 +150,8 @@ class UserRegFormAdmin(FormAdmin):
page_schema = None
page_route_kwargs = {"name": "reg"}

async def handle(self, request: Request, data: SchemaUpdateT, **kwargs) -> BaseApiOut[BaseModel]: # self.schema_submit_out
async def handle(self, request: Request, data: SchemaUpdateT, **kwargs) -> BaseApiOut[
BaseModel]: # self.schema_submit_out
auth: Auth = request.auth
if data.username.upper() in SystemUserEnum.__members__:
return BaseApiOut(status=-1, msg=_("Username has been registered!"), data=None)
Expand Down Expand Up @@ -271,15 +273,15 @@ class UserAdmin(AuthFieldModelAdmin, AuthSelectModelAdmin, SoftDeleteModelAdmin,
lambda admin: UpdateSubPagePermsAction(
admin=admin,
name="update_subject_page_permissions",
tooltip="更新用户页面权限",
tooltip=_("Update User Page Permissions")
),
lambda admin: UpdateSubDataPermAction(
admin=admin,
name="update_subject_data_permissions",
tooltip="更新用户数据权限",
tooltip=_("Update User Data Permissions")
),
lambda admin: UpdateSubRolesAction(
admin=admin, name="update_subject_roles", tooltip="更新用户角色", icon="fa fa-user", flags="item"
admin=admin, name="update_subject_roles", tooltip=_("Update User Roles"), icon="fa fa-user", flags="item"
),
lambda admin: CopyUserAuthLinkAction(admin),
]
Expand Down Expand Up @@ -334,15 +336,15 @@ class RoleAdmin(AutoTimeModelAdmin, FootableModelAdmin):
lambda admin: UpdateSubPagePermsAction(
admin=admin,
name="update_subject_page_permissions",
tooltip="更新角色页面权限",
tooltip=_("Update role page permissions"),
),
lambda admin: UpdateSubDataPermAction(
admin=admin,
name="update_subject_data_permissions",
tooltip="更新角色数据权限",
tooltip=_("Update role data permissions"),
),
lambda admin: UpdateSubRolesAction(
admin=admin, name="update_subject_roles", tooltip="更新子角色", icon="fa fa-user", flags="item"
admin=admin, name="update_subject_roles", tooltip=_("Update sub roles"), icon="fa fa-user", flags="item"
),
]

Expand All @@ -364,13 +366,14 @@ class CasbinRuleAdmin(ReadOnlyModelAdmin):
unique_id = "Auth>CasbinRuleAdmin"
page_schema = PageSchema(label="CasbinRule", icon="fa fa-lock")
model = CasbinRule
list_filter = [CasbinRule.ptype, CasbinRule.v0, CasbinRule.v1, CasbinRule.v2, CasbinRule.v3, CasbinRule.v4, CasbinRule.v5]
list_filter = [CasbinRule.ptype, CasbinRule.v0, CasbinRule.v1, CasbinRule.v2, CasbinRule.v3, CasbinRule.v4,
CasbinRule.v5]
admin_action_maker = [
lambda admin: AdminAction(
admin=admin,
action=ActionType.Ajax(
id="refresh",
label="刷新权限",
label=_("Refresh Permissions"),
icon="fa fa-refresh",
level=LevelEnum.success,
api=f"GET:{admin.router_path}/load_policy",
Expand All @@ -396,14 +399,14 @@ def register_router(self):
async def _load_policy():
await self.load_policy()
get_admin_action_options.cache_clear() # 清除系统菜单缓存
return BaseApiOut(data="刷新成功")
return BaseApiOut(data=_("Refresh Successful"))

return super().register_router()


class LoginHistoryAdmin(ReadOnlyModelAdmin):
unique_id = "Auth>LoginHistoryAdmin"
page_schema = PageSchema(label="登录历史", icon="fa fa-history")
page_schema = PageSchema(label=_("Login History"), icon="fa fa-history")
model = LoginHistory
search_fields = [LoginHistory.login_name, LoginHistory.ip, LoginHistory.login_status, LoginHistory.user_agent]
list_display = [
Expand Down
7 changes: 4 additions & 3 deletions fastapi_user_auth/admin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from casbin import AsyncEnforcer
from fastapi_amis_admin.admin import FormAdmin, ModelAdmin, PageSchemaAdmin
from fastapi_amis_admin.admin.admin import AdminGroup, BaseActionAdmin, BaseAdminSite
from fastapi_amis_admin.utils.translation import i18n as _

from fastapi_user_auth.auth.schemas import SystemUserEnum
from fastapi_user_auth.utils.casbin import permission_encode, permission_enforce
Expand All @@ -28,10 +29,10 @@ def get_admin_action_options(
if isinstance(admin, BaseActionAdmin):
item["children"] = []
if isinstance(admin, ModelAdmin):
item["children"].append({"label": "查看列表", "value": permission_encode(admin.unique_id, "page:list", "page")})
item["children"].append({"label": "筛选列表", "value": permission_encode(admin.unique_id, "page:filter", "page")})
item["children"].append({"label": _("View list"), "value": permission_encode(admin.unique_id, "page:list", "page")})
item["children"].append({"label": _("Filter list"), "value": permission_encode(admin.unique_id, "page:filter", "page")})
elif isinstance(admin, FormAdmin) and "submit" not in admin.registered_admin_actions:
item["children"].append({"label": "提交", "value": permission_encode(admin.unique_id, "page:submit", "page")})
item["children"].append({"label": _("Submit"), "value": permission_encode(admin.unique_id, "page:submit", "page")})
for admin_action in admin.registered_admin_actions.values():
# todo admin_action 下可能有多个action,需要遍历
item["children"].append(
Expand Down
Loading