diff --git a/backend/app/api/admin_catalog.py b/backend/app/api/admin_catalog.py index a7b54a7..8ebef7e 100644 --- a/backend/app/api/admin_catalog.py +++ b/backend/app/api/admin_catalog.py @@ -1,6 +1,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.orm import Session +from app.core.keygen import generate_key from app.core.config import get_settings from app.db.session import get_db from app.models.api_client import ApiClient @@ -81,6 +82,14 @@ def _split_module_key(payload_module: str | None) -> str: return payload_module +def _generate_unique_key(prefix: str, exists_fn) -> str: + for salt in range(1000): + key = generate_key(prefix=prefix, salt=salt) + if not exists_fn(key): + return key + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"failed_to_generate_{prefix.lower()}_key") + + def _sync_member_to_authentik( *, authentik_sub: str, @@ -124,9 +133,8 @@ def create_system( db: Session = Depends(get_db), ) -> SystemItem: repo = SystemsRepository(db) - if repo.get_by_key(payload.system_key): - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="system_key_already_exists") - row = repo.create(system_key=payload.system_key, name=payload.name, status=payload.status) + system_key = _generate_unique_key("ST", repo.get_by_key) + row = repo.create(system_key=system_key, name=payload.name, status=payload.status) return SystemItem(id=row.id, system_key=row.system_key, name=row.name, status=row.status) @@ -180,9 +188,8 @@ def create_module( system = systems_repo.get_by_key(payload.system_key) if not system: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="system_not_found") - full_module_key = f"{payload.system_key}.{payload.module_key}" - if modules_repo.get_by_key(full_module_key): - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="module_key_already_exists") + leaf_module_key = _generate_unique_key("MD", lambda k: modules_repo.get_by_key(f"{payload.system_key}.{k}")) + full_module_key = f"{payload.system_key}.{leaf_module_key}" row = modules_repo.create( module_key=full_module_key, name=payload.name, @@ -317,9 +324,8 @@ def create_company( db: Session = Depends(get_db), ) -> CompanyItem: repo = CompaniesRepository(db) - if repo.get_by_key(payload.company_key): - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="company_key_already_exists") - row = repo.create(company_key=payload.company_key, name=payload.name, status=payload.status) + company_key = _generate_unique_key("CP", repo.get_by_key) + row = repo.create(company_key=company_key, name=payload.name, status=payload.status) return CompanyItem(id=row.id, company_key=row.company_key, name=row.name, status=row.status) @@ -414,9 +420,8 @@ def create_site( company = companies_repo.get_by_key(payload.company_key) if not company: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="company_not_found") - if sites_repo.get_by_key(payload.site_key): - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="site_key_already_exists") - row = sites_repo.create(site_key=payload.site_key, company_id=company.id, name=payload.name, status=payload.status) + site_key = _generate_unique_key("ST", sites_repo.get_by_key) + row = sites_repo.create(site_key=site_key, company_id=company.id, name=payload.name, status=payload.status) return SiteItem(id=row.id, site_key=row.site_key, company_key=payload.company_key, name=row.name, status=row.status) @@ -688,9 +693,8 @@ def create_permission_group( db: Session = Depends(get_db), ) -> PermissionGroupItem: repo = PermissionGroupsRepository(db) - if repo.get_by_key(payload.group_key): - raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="group_key_already_exists") - row = repo.create(group_key=payload.group_key, name=payload.name, status=payload.status) + group_key = _generate_unique_key("GP", repo.get_by_key) + row = repo.create(group_key=group_key, name=payload.name, status=payload.status) return PermissionGroupItem(id=row.id, group_key=row.group_key, name=row.name, status=row.status) diff --git a/backend/app/core/keygen.py b/backend/app/core/keygen.py new file mode 100644 index 0000000..44deb6b --- /dev/null +++ b/backend/app/core/keygen.py @@ -0,0 +1,9 @@ +from datetime import datetime +import time + + +def generate_key(prefix: str, salt: int = 0) -> str: + date_str = datetime.now().strftime("%Y%m%d") + tail = (int(time.time() * 1000) + salt) % 10000 + return f"{prefix}{date_str}X{tail:04d}" + diff --git a/backend/app/schemas/catalog.py b/backend/app/schemas/catalog.py index 90af683..09b2730 100644 --- a/backend/app/schemas/catalog.py +++ b/backend/app/schemas/catalog.py @@ -3,7 +3,6 @@ from typing import Literal class SystemCreateRequest(BaseModel): - system_key: str name: str status: str = "active" @@ -22,7 +21,6 @@ class SystemItem(BaseModel): class ModuleCreateRequest(BaseModel): system_key: str - module_key: str name: str status: str = "active" @@ -41,7 +39,6 @@ class ModuleItem(BaseModel): class CompanyCreateRequest(BaseModel): - company_key: str name: str status: str = "active" @@ -59,7 +56,6 @@ class CompanyItem(BaseModel): class SiteCreateRequest(BaseModel): - site_key: str company_key: str name: str status: str = "active" @@ -114,7 +110,6 @@ class ListResponse(BaseModel): class PermissionGroupCreateRequest(BaseModel): - group_key: str name: str status: str = "active" diff --git a/frontend/src/pages/admin/CompaniesPage.vue b/frontend/src/pages/admin/CompaniesPage.vue index 05f5023..a473450 100644 --- a/frontend/src/pages/admin/CompaniesPage.vue +++ b/frontend/src/pages/admin/CompaniesPage.vue @@ -23,7 +23,6 @@ -