02a7abeb3f
Project CI / AI game creator shell Rust crates (push) Successful in 2m53s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m42s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
- 后台新增「模板管理」页:按权限查看并编辑模板名称/简介/标签/封面,支持上架与下架;路由、侧栏、tab 权限、API 客户端与 DTO 同步接入 - api-server 新增 /admin/api/agc-templates 读取与更新路由,复用现有后台鉴权与 tab 权限校验,权限不足按既有失败关闭口径拒绝 - module-assets 收敛模板库纯规则(active/inactive 投影、索引 schema 与重复项校验、ZIP 大小与摘要核对),platform-oss 承担对象存储读写、不可变对象复用与发布互斥锁 - shared-contracts 补后台模板 DTO;spacetime-module 账号存储适配保持既有 schema 不变 - AGC 壳内置模板索引与建项读取改为内容寻址对象,补模板安装、越界归档、索引不匹配等用例 - 模板库发布脚本支持定向发布与上架/下架合并、内容地址复用与锁语义,CLI 守卫用例同步;jenkins 两条客户端打包管线统一触发邮件通知 Job 并传递首装包链接 - 内置 5 个 Cocos 官方模板载荷与索引 fixture(内容地址对象,共 90 个文件) - 补「后台模板管理」里程碑与实施计划,更新模板库技术方案、开发运维文档与共享记忆 - 验证:admin-web 39 项用例与 typecheck、发布脚本 25 项用例、api-server 与 AGC 壳编译、模板库定向 Rust 用例(AGC 壳 18 项、module-assets/platform-oss 16 项、后台权限 1 项)、check:encoding / check:doc-index / check:production-ops / rustfmt 全部通过
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
/** 后台单页应用可导航的路由标识。 */
|
|
export type AdminRouteId =
|
|
| 'dashboard'
|
|
| 'overview'
|
|
| 'tables'
|
|
| 'debug'
|
|
| 'tracking'
|
|
| 'error-reports'
|
|
| 'gray-release'
|
|
| 'redeem'
|
|
| 'invite'
|
|
| 'profile-wallet'
|
|
| 'tasks'
|
|
| 'recharge-products'
|
|
| 'recharge-orders'
|
|
| 'editor-generation-pricing'
|
|
| 'editor-showcase'
|
|
| 'editor-assets'
|
|
| 'project-snapshots'
|
|
| 'agc-models'
|
|
| 'agc-templates'
|
|
| 'accounts';
|
|
|
|
export type AdminTabPermission = Exclude<
|
|
AdminRouteId,
|
|
'accounts' | 'agc-models'
|
|
>;
|
|
|
|
/** 后台导航项定义,hash 是浏览器地址栏和移动底栏共用入口。 */
|
|
export interface AdminRouteDefinition {
|
|
id: AdminRouteId;
|
|
label: string;
|
|
hash: string;
|
|
ownerOnly?: boolean;
|
|
}
|
|
|
|
export const adminRoutes: AdminRouteDefinition[] = [
|
|
{ id: 'dashboard', label: 'Dashboard', hash: '#dashboard' },
|
|
{ id: 'overview', label: '服务总览', hash: '#overview' },
|
|
{ id: 'tables', label: '表查询', hash: '#tables' },
|
|
{ id: 'debug', label: 'API 调试', hash: '#debug' },
|
|
{ id: 'tracking', label: '埋点数据', hash: '#tracking' },
|
|
{ id: 'error-reports', label: '错误报告', hash: '#error-reports' },
|
|
{ id: 'gray-release', label: '灰度发布', hash: '#gray-release' },
|
|
{ id: 'redeem', label: '兑换码', hash: '#redeem' },
|
|
{ id: 'invite', label: '邀请码', hash: '#invite' },
|
|
{ id: 'profile-wallet', label: '账号配置', hash: '#profile-wallet' },
|
|
{ id: 'tasks', label: '任务配置', hash: '#tasks' },
|
|
{ id: 'recharge-products', label: '充值商品', hash: '#recharge-products' },
|
|
{ id: 'recharge-orders', label: '充值管理', hash: '#recharge-orders' },
|
|
{
|
|
id: 'editor-generation-pricing',
|
|
label: '模型定价',
|
|
hash: '#editor-generation-pricing',
|
|
},
|
|
{ id: 'agc-models', label: 'AGC 模型', hash: '#agc-models', ownerOnly: true },
|
|
{ id: 'agc-templates', label: '模板管理', hash: '#agc-templates' },
|
|
{ id: 'editor-showcase', label: '精选审核', hash: '#editor-showcase' },
|
|
{ id: 'editor-assets', label: '素材查询', hash: '#editor-assets' },
|
|
{ id: 'project-snapshots', label: '项目工程', hash: '#project-snapshots' },
|
|
{ id: 'accounts', label: '账号管理', hash: '#accounts', ownerOnly: true },
|
|
];
|
|
|
|
export interface AdminRouteAccess {
|
|
accountRole: 'owner' | 'member';
|
|
tabPermissions: string[];
|
|
}
|
|
|
|
export function getAccessibleAdminRoutes(
|
|
admin: AdminRouteAccess,
|
|
): AdminRouteDefinition[] {
|
|
if (admin.accountRole === 'owner') {
|
|
return adminRoutes;
|
|
}
|
|
|
|
const permissions = new Set(admin.tabPermissions);
|
|
return adminRoutes.filter(
|
|
(route) => !route.ownerOnly && permissions.has(route.id),
|
|
);
|
|
}
|
|
|
|
export function resolveAccessibleAdminRoute(
|
|
hash: string,
|
|
routes: AdminRouteDefinition[],
|
|
): AdminRouteId | null {
|
|
const normalizedHash = hash.trim().toLowerCase().split('?')[0] ?? '';
|
|
return (
|
|
routes.find((route) => route.hash === normalizedHash)?.id ??
|
|
routes[0]?.id ??
|
|
null
|
|
);
|
|
}
|
|
|
|
/** 根据地址栏 hash 解析后台路由,未知 hash 回落到 Dashboard。 */
|
|
export function resolveAdminRoute(hash: string): AdminRouteId {
|
|
const normalizedHash = hash.trim().toLowerCase().split('?')[0] ?? '';
|
|
return (
|
|
adminRoutes.find((route) => route.hash === normalizedHash)?.id ??
|
|
'dashboard'
|
|
);
|
|
}
|
|
|
|
/** 根据后台路由标识反查 hash,供导航点击时同步地址栏。 */
|
|
export function routeHash(routeId: AdminRouteId) {
|
|
return (
|
|
adminRoutes.find((route) => route.id === routeId)?.hash ??
|
|
adminRoutes[0]?.hash ??
|
|
'#dashboard'
|
|
);
|
|
}
|