82 lines
3.7 KiB
Python
82 lines
3.7 KiB
Python
import secrets
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from app.core.security import require_permission, token_digest
|
|
from app.repositories import license_repository
|
|
from app.schemas.license import LicenseCreateRequest, LicenseDeleteRequest, LicenseStatusRequest
|
|
from app.services.common_service import validate_channel_code
|
|
from app.services.license_service import decrypt_license_key, encrypt_license_key
|
|
|
|
|
|
router = APIRouter(tags=["admin-license"])
|
|
|
|
|
|
@router.post("/admin/license/create")
|
|
def admin_license_create(body: LicenseCreateRequest, auth=Depends(require_permission("license:manage"))):
|
|
app_id = body.app_id.strip()
|
|
channel = body.channel.strip()
|
|
customer = body.customer_name.strip()
|
|
max_devices = int(body.max_devices or 1)
|
|
valid_until = body.valid_until.strip()
|
|
try:
|
|
expiry = datetime.fromisoformat(valid_until.replace("Z", "+00:00"))
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="valid_until 必须是 ISO-8601 时间")
|
|
if not app_id or not customer or not validate_channel_code(channel) or max_devices < 1 or expiry <= datetime.now(timezone.utc):
|
|
raise HTTPException(status_code=400, detail="授权参数无效")
|
|
license_id = "lic_" + secrets.token_hex(12)
|
|
license_key = "MARSCO-" + secrets.token_urlsafe(24)
|
|
result = license_repository.insert_license(
|
|
license_id,
|
|
token_digest(license_key),
|
|
encrypt_license_key(license_key),
|
|
customer,
|
|
app_id,
|
|
channel,
|
|
max_devices,
|
|
valid_until,
|
|
)
|
|
if result == "channel_not_found":
|
|
raise HTTPException(status_code=400, detail={"error": "channel_not_found", "msg": f"渠道 {channel} 不存在"})
|
|
if result == "channel_disabled":
|
|
raise HTTPException(status_code=403, detail={"error": "channel_disabled", "msg": f"渠道 {channel} 已停用"})
|
|
return {"license_id": license_id, "license_key": license_key, "msg": "授权已创建;密钥已加密保存,可在授权列表中再次查看"}
|
|
|
|
|
|
@router.get("/admin/license/list")
|
|
def admin_license_list(app_id: str = "", include_deleted: bool = False, auth=Depends(require_permission("license:view"))):
|
|
rows = license_repository.list_licenses(app_id, include_deleted)
|
|
result = []
|
|
for r in rows:
|
|
license_key = decrypt_license_key(r["license_key_cipher"] if "license_key_cipher" in r.keys() else "")
|
|
result.append(
|
|
{k: r[k] for k in ("license_id", "customer_name", "app_id", "channel_code", "max_devices", "valid_until", "status", "created_at")}
|
|
| {"used_devices": r["used_devices"], "license_key": license_key, "license_key_available": bool(license_key)}
|
|
)
|
|
return {"list": result}
|
|
|
|
|
|
@router.post("/admin/license/set-status")
|
|
def admin_license_set_status(body: LicenseStatusRequest, auth=Depends(require_permission("license:manage"))):
|
|
status = body.status
|
|
license_id = body.license_id
|
|
if status not in ("active", "disabled"):
|
|
raise HTTPException(status_code=400, detail="授权状态无效")
|
|
updated = license_repository.set_license_status(license_id, status)
|
|
if updated != 1:
|
|
raise HTTPException(status_code=404, detail="授权不存在")
|
|
return {"msg": "授权状态已更新"}
|
|
|
|
|
|
@router.post("/admin/license/delete")
|
|
def admin_license_delete(body: LicenseDeleteRequest, auth=Depends(require_permission("license:manage"))):
|
|
license_id = body.license_id.strip()
|
|
if not license_id:
|
|
raise HTTPException(status_code=400, detail="license_id 不能为空")
|
|
updated = license_repository.soft_delete_license(license_id)
|
|
if updated != 1:
|
|
raise HTTPException(status_code=404, detail="授权不存在")
|
|
return {"msg": "授权已删除,历史记录仍会保留"}
|