From 95a3dee29ffcff411d4b57a92bbb9fca8380892a Mon Sep 17 00:00:00 2001 From: kongkx Date: Tue, 7 Jul 2026 22:07:05 +0800 Subject: [PATCH] feat: admin OpenAPI type generation (gen:api) + envelope/entity types --- package.json | 5 +- src/api/types.gen.ts | 4101 ++++++++++++++++++++++++++++++++++++++++++ src/api/types.ts | 257 +++ 3 files changed, 4361 insertions(+), 2 deletions(-) create mode 100644 src/api/types.gen.ts create mode 100644 src/api/types.ts diff --git a/package.json b/package.json index eff2623..be314a7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "generate-routes": "tsr generate", "build": "vite build", "preview": "vite preview", - "test": "vitest run" + "test": "vitest run", + "gen:api": "openapi-typescript ../gig-platform/public/api-docs/admin/openapi.yaml -o src/api/types.gen.ts" }, "dependencies": { "@hookform/resolvers": "^5.4.0", @@ -64,4 +65,4 @@ "lightningcss" ] } -} \ No newline at end of file +} diff --git a/src/api/types.gen.ts b/src/api/types.gen.ts new file mode 100644 index 0000000..65676e5 --- /dev/null +++ b/src/api/types.gen.ts @@ -0,0 +1,4101 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/api/admin/v1/auth/login/otp": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * [管理后台] 请求OTP验证码 + * @description Request OTP code for admin login via email or SMS + */ + post: operations["OTP"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/auth/login/verify-otp": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * [管理后台] 验证OTP验证码 + * @description Verify OTP code for admin login and return access token + */ + post: operations["OTP"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/auth/login/password": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * [管理后台] 密码登录 + * @description Login admin user with identity and password + */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Admin identity (email). + * @example admin@example.com + */ + identity: string; + /** + * @description Admin password. + * @example admin123 + */ + password: string; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6... */ + access_token?: string; + /** @example Bearer */ + token_type?: string; + /** @example 900 */ + expires_in?: number; + /** @example refresh-token-string */ + refresh_token?: string; + }; + /** @example Admin login successful */ + message?: string; + }; + }; + }; + /** @description invalid_credentials */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example INVALID_CREDENTIALS */ + code?: string; + /** @example Invalid admin credentials */ + message?: string; + }; + }; + }; + /** @description validation_error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "Identity is required" + * ] + */ + identity?: string[]; + /** + * @example [ + * "Password is required" + * ] + */ + password?: string[]; + }; + }; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/auth/robots/{uid}/token": { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + uid: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * [管理后台] 为机器人账号签发会话令牌 + * @description 为指定机器人账号签发一组 OAuth 访问令牌和刷新令牌。 + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + uid: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/auth/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get the current admin user with roles and permissions + * @description Returns the authenticated user plus their roles and effective permissions + * (role-derived + direct) so the admin frontend can render permission-aware UI. + */ + get: operations["getTheCurrentAdminUserWithRolesAndPermissions"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/comments/reports": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** [评论举报] 获取举报列表 */ + get: { + parameters: { + query?: { + /** + * @description optional Filter by status: open, resolved, dismissed. + * @example open + */ + status?: string; + /** + * @description optional Page number. + * @example 1 + */ + page?: number; + /** + * @description optional Page size (max 100). + * @example 20 + */ + page_size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @example open + * @enum {string|null} + */ + status?: "open" | "resolved" | "dismissed" | null; + /** + * @description Must be at least 1. + * @example 16 + */ + page?: number | null; + /** + * @description Must be at least 1. Must not be greater than 100. + * @example 22 + */ + page_size?: number | null; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": 1, + * "comment_id": 10, + * "reason": "invalid", + * "message": "Contains spam links", + * "status": "open", + * "created_at": "2025-12-01T10:10:00Z", + * "handled_at": null, + * "handled_by": null, + * "reporter": { + * "uid": "01HXXXX", + * "display_name": "Alice" + * }, + * "comment": { + * "id": 10, + * "body": "Root comment" + * } + * } + * ] + */ + data?: { + /** @example 1 */ + id?: number; + /** @example 10 */ + comment_id?: number; + /** @example invalid */ + reason?: string; + /** @example Contains spam links */ + message?: string; + /** @example open */ + status?: string; + /** @example 2025-12-01T10:10:00Z */ + created_at?: string; + /** @example null */ + handled_at?: string; + /** @example null */ + handled_by?: string; + reporter?: { + /** @example 01HXXXX */ + uid?: string; + /** @example Alice */ + display_name?: string; + }; + comment?: { + /** @example 10 */ + id?: number; + /** @example Root comment */ + body?: string; + }; + }[]; + pagination?: { + /** @example 1 */ + current_page?: number; + /** @example null */ + next_page?: string; + /** @example 20 */ + page_size?: number; + /** @example null */ + prev_page?: string; + /** @example 1 */ + total_count?: number; + /** @example 1 */ + total_pages?: number; + }; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example UNAUTHENTICATED */ + code?: string; + /** @example 未认证 */ + message?: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example FORBIDDEN */ + code?: string; + /** @example 无权限访问 */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example The given data was invalid. */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "The selected status is invalid." + * ] + */ + status?: string[]; + }; + }; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/comments/reports/{report_id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the report. + * @example 16 + */ + report_id: number; + /** + * @description Report ID. + * @example 1 + */ + report: number; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** [评论举报] 处理举报 */ + patch: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the report. + * @example 16 + */ + report_id: number; + /** + * @description Report ID. + * @example 1 + */ + report: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Action to take: hide, delete, dismiss. + * @example hide + */ + action: string; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 1 */ + id?: number; + /** @example resolved */ + status?: string; + /** @example 2025-12-01T10:20:00Z */ + handled_at?: string; + /** @example 01HXXXX */ + handled_by_uid?: string; + }; + /** @example Report handled */ + message?: string; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example UNAUTHENTICATED */ + code?: string; + /** @example 未认证 */ + message?: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example FORBIDDEN */ + code?: string; + /** @example 无权限访问 */ + message?: string; + }; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example NOT_FOUND */ + code?: string; + /** @example 举报不存在 */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example The given data was invalid. */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "The selected action is invalid." + * ] + */ + action?: string[]; + }; + }; + }; + }; + }; + }; + }; + trace?: never; + }; + "/api/admin/v1/dimzou/translations/{translation_id}/retranslate": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the translation. + * @example 1 + */ + translation_id: number; + /** + * @description Translation ID. + * @example 1 + */ + translation: number; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * [运营] 重新机器翻译 + * @description 将译文档最新草稿版本中"待译/失败"的块重新入队翻译。仅平台管理员可用。 + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the translation. + * @example 1 + */ + translation_id: number; + /** + * @description Translation ID. + * @example 1 + */ + translation: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 5 */ + translation_version_id?: number; + /** @example 12 */ + queued_blocks?: number; + }; + }; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example No translation draft to re-translate. */ + message?: string; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/notification/devices": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * [管理] 获取设备列表 + * @description 获取所有设备的分页列表,支持按平台、在线状态等筛选。 + */ + get: { + parameters: { + query?: { + /** + * @description 可选。按平台筛选 (ios, android, web, desktop) + * @example architecto + */ + platform?: string; + /** + * @description 可选。按在线状态筛选 + * @example false + */ + is_online?: boolean; + /** + * @description 可选。页码 (默认: 1) + * @example 16 + */ + page?: number; + /** + * @description 可选。每页条数 (默认: 20, 最大: 100) + * @example 16 + */ + page_size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @example desktop + * @enum {string|null} + */ + platform?: "ios" | "android" | "web" | "desktop" | null; + /** @example false */ + is_online?: boolean | null; + /** + * @description Must be at least 1. + * @example 16 + */ + page?: number | null; + /** + * @description Must be at least 1. Must not be greater than 100. + * @example 22 + */ + page_size?: number | null; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": "01HN1234567890ABCDEF", + * "device_client_id": "device_123", + * "platform": "ios", + * "app_key": "app_v1", + * "device_model": "iPhone 12", + * "push_token": "push_token_here", + * "websocket_session_id": "ws_session_123", + * "last_seen_at": "2023-11-15T07:30:00.000000Z", + * "is_online": true, + * "kind": "device", + * "active_sessions_count": 2, + * "created_at": "2023-11-01T00:00:00.000000Z", + * "updated_at": "2023-11-15T07:30:00.000000Z" + * } + * ] + */ + data?: { + /** @example 01HN1234567890ABCDEF */ + id?: string; + /** @example device_123 */ + device_client_id?: string; + /** @example ios */ + platform?: string; + /** @example app_v1 */ + app_key?: string; + /** @example iPhone 12 */ + device_model?: string; + /** @example push_token_here */ + push_token?: string; + /** @example ws_session_123 */ + websocket_session_id?: string; + /** @example 2023-11-15T07:30:00.000000Z */ + last_seen_at?: string; + /** @example true */ + is_online?: boolean; + /** @example device */ + kind?: string; + /** @example 2 */ + active_sessions_count?: number; + /** @example 2023-11-01T00:00:00.000000Z */ + created_at?: string; + /** @example 2023-11-15T07:30:00.000000Z */ + updated_at?: string; + }[]; + pagination?: { + /** @example 1 */ + current_page?: number; + /** @example 2 */ + next_page?: number; + /** @example 20 */ + page_size?: number; + /** @example null */ + prev_page?: string; + /** @example 150 */ + total_count?: number; + /** @example 8 */ + total_pages?: number; + }; + /** @example Devices retrieved successfully */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + data?: { + /** + * @example [ + * "The selected platform is invalid." + * ] + */ + platform?: string[]; + /** + * @example [ + * "The is_online field must be true or false." + * ] + */ + is_online?: string[]; + }; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/agent/tokens": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** 查看当前有效的 Agent token 列表 */ + get: operations["AgentToken"]; + put?: never; + /** + * 签发新的 Agent token + * @description 返回的 access_token 仅此一次完整展示;配置到 Agent 的 MCP client + * 后即可访问 POST /mcp/studio。 + */ + post: operations["AgentToken"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/agent/tokens/{tokenId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + tokenId: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** 吊销一个 Agent token */ + delete: operations["AgentToken"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/category-briefs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioCategoryBriefs"]; + put?: never; + post: operations["postApiAdminV1StudioCategoryBriefs"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/category-briefs/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category brief. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioCategoryBriefsId"]; + put: operations["putApiAdminV1StudioCategoryBriefsId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioCategoryBriefsId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/subtopics": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioSubtopics"]; + put?: never; + post: operations["postApiAdminV1StudioSubtopics"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/subtopics/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the subtopic. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioSubtopicsId"]; + put: operations["putApiAdminV1StudioSubtopicsId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioSubtopicsId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/articles": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioArticles"]; + put?: never; + post: operations["postApiAdminV1StudioArticles"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/articles/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the article. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioArticlesId"]; + put: operations["putApiAdminV1StudioArticlesId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioArticlesId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/series": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioSeries"]; + put?: never; + post: operations["postApiAdminV1StudioSeries"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/series/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the series. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioSeriesId"]; + put: operations["putApiAdminV1StudioSeriesId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioSeriesId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/robot-authors": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioRobotAuthors"]; + put?: never; + post: operations["postApiAdminV1StudioRobotAuthors"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/robot-authors/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the robot author. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioRobotAuthorsId"]; + put: operations["putApiAdminV1StudioRobotAuthorsId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioRobotAuthorsId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/style-presets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getApiAdminV1StudioStylePresets"]; + put?: never; + post: operations["postApiAdminV1StudioStylePresets"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/studio/style-presets/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the style preset. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get: operations["getApiAdminV1StudioStylePresetsId"]; + put: operations["putApiAdminV1StudioStylePresetsId"]; + post?: never; + delete: operations["deleteApiAdminV1StudioStylePresetsId"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/file-x/event-reports": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** [事件举报] 获取举报列表 */ + get: { + parameters: { + query?: { + /** + * @description 可选。按状态过滤: open, resolved, dismissed。 示例: open + * @example architecto + */ + status?: string; + /** + * @description 可选。页码。 示例: 1 + * @example 16 + */ + page?: number; + /** + * @description 可选。每页数量,最大100。 示例: 20 + * @example 16 + */ + page_size?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @example dismissed + * @enum {string|null} + */ + status?: "open" | "resolved" | "dismissed" | null; + /** + * @description Must be at least 1. + * @example 16 + */ + page?: number | null; + /** + * @description Must be at least 1. Must not be greater than 100. + * @example 22 + */ + page_size?: number | null; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": 1, + * "kind": "filex_event_report", + * "event_id": 25, + * "reason": "spam", + * "message": "含有大量垃圾推广链接", + * "status": "open", + * "created_at": "2026-06-06T10:00:00Z", + * "handled_at": null, + * "reporter": { + * "uid": "01HXXXX", + * "display_name": "Alice" + * }, + * "handled_by": null + * } + * ] + */ + data?: { + /** @example 1 */ + id?: number; + /** @example filex_event_report */ + kind?: string; + /** @example 25 */ + event_id?: number; + /** @example spam */ + reason?: string; + /** @example 含有大量垃圾推广链接 */ + message?: string; + /** @example open */ + status?: string; + /** @example 2026-06-06T10:00:00Z */ + created_at?: string; + /** @example null */ + handled_at?: string; + reporter?: { + /** @example 01HXXXX */ + uid?: string; + /** @example Alice */ + display_name?: string; + }; + /** @example null */ + handled_by?: string; + }[]; + pagination?: { + /** @example 1 */ + current_page?: number; + /** @example null */ + next_page?: string; + /** @example 20 */ + page_size?: number; + /** @example null */ + prev_page?: string; + /** @example 1 */ + total_count?: number; + /** @example 1 */ + total_pages?: number; + }; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example UNAUTHENTICATED */ + code?: string; + /** @example 未认证 */ + message?: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example FORBIDDEN */ + code?: string; + /** @example 无权限访问 */ + message?: string; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/file-x/event-reports/{report_id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the report. + * @example 16 + */ + report_id: number; + /** @description Optional parameter. 必需。举报ID。 示例: 1 */ + report: number; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** [事件举报] 处理举报 */ + patch: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the report. + * @example 16 + */ + report_id: number; + /** @description Optional parameter. 必需。举报ID。 示例: 1 */ + report: number; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description 必需。处理动作: hide, delete, resolve, dismiss。 示例: hide + * @example architecto + */ + action?: string; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 1 */ + id?: number; + /** @example resolved */ + status?: string; + /** @example 2026-06-06T10:20:00Z */ + handled_at?: string; + /** @example 01HXXXX */ + handled_by_uid?: string; + }; + /** @example 举报已处理 */ + message?: string; + }; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example UNAUTHENTICATED */ + code?: string; + /** @example 未认证 */ + message?: string; + }; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example FORBIDDEN */ + code?: string; + /** @example 无权限访问 */ + message?: string; + }; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example NOT_FOUND */ + code?: string; + /** @example 请求的资源未找到。 */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example The given data was invalid. */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "The selected action is invalid." + * ] + */ + action?: string[]; + }; + }; + }; + }; + }; + }; + }; + trace?: never; + }; + "/api/admin/v1/categories/tree": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get category tree (admin) + * @description Returns categories in tree structure with option to include inactive categories. + */ + get: operations["getCategoryTreeadmin"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/pending": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List pending user-created categories + * @description User-created categories awaiting review (approval_status = pending). + */ + get: operations["listPendingUserCreatedCategories"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{category_id}/approve": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Approve a pending category (make it public) */ + post: operations["approveAPendingCategorymakeItPublic"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{category_id}/reject": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Reject a public request (keep the category private) */ + post: operations["rejectAPublicRequestkeepTheCategoryPrivate"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{category_id}/merge": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** Merge a category into another (preserves both IDs) */ + post: operations["mergeACategoryIntoAnotherpreservesBothIDs"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create category + * @description Create a new category. Translations can be added immediately or later via separate endpoints. + */ + post: operations["createCategory"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + /** + * Get category details + * @description Retrieve detailed information about a specific category. + */ + get: operations["getCategoryDetails"]; + /** + * Update category + * @description Update an existing category with new data and translations. + */ + put: operations["updateCategory"]; + post?: never; + /** + * Delete category + * @description Soft delete a category. Categories with children cannot be deleted. + */ + delete: operations["deleteCategory"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{category_id}/translations": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + /** + * List all translations for category + * @description Get all translations associated with a specific category. + */ + get: operations["listAllTranslationsForCategory"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/categories/{category_id}/translations/{locale}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description Locale code (e.g., 'en', 'zh-hans'). + * @example sr_BA + */ + locale: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + /** + * Get translation for specific locale + * @description Retrieve the translation for a specific category and locale. + */ + get: operations["getTranslationForSpecificLocale"]; + put?: never; + /** + * Create or update translation for specific locale + * @description Add or update a translation for a specific category and locale. + */ + post: operations["createOrUpdateTranslationForSpecificLocale"]; + /** + * Delete translation for specific locale + * @description Remove the translation for a specific category and locale. + */ + delete: operations["deleteTranslationForSpecificLocale"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/locales": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** 获取语言列表 */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "kind": "locale", + * "code": "en", + * "name": "English", + * "native_name": "English", + * "is_active": true, + * "sort_order": 1, + * "created_at": "2024-01-01T00:00:00Z", + * "updated_at": "2024-01-01T00:00:00Z" + * } + * ] + */ + data?: { + /** @example locale */ + kind?: string; + /** @example en */ + code?: string; + /** @example English */ + name?: string; + /** @example English */ + native_name?: string; + /** @example true */ + is_active?: boolean; + /** @example 1 */ + sort_order?: number; + /** @example 2024-01-01T00:00:00Z */ + created_at?: string; + /** @example 2024-01-01T00:00:00Z */ + updated_at?: string; + }[]; + }; + }; + }; + /** @description unauthorized */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example UNAUTHORIZED */ + code?: string; + /** @example 未授权访问 */ + message?: string; + }; + }; + }; + }; + }; + put?: never; + /** 创建语言 */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description 语言代码(BCP-47)。 + * @example fr + */ + code: string; + /** + * @description 语言名称。 + * @example French + */ + name: string; + /** + * @description 语言本地名称。 + * @example Français + */ + native_name: string; + /** + * @description 是否启用。 + * @example true + */ + is_active?: boolean; + /** + * @description 排序值。 + * @example 4 + */ + sort_order?: number; + }; + }; + }; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example locale */ + kind?: string; + /** @example fr */ + code?: string; + /** @example French */ + name?: string; + /** @example Français */ + native_name?: string; + /** @example true */ + is_active?: boolean; + /** @example 4 */ + sort_order?: number; + /** @example 2024-01-01T00:00:00Z */ + created_at?: string; + /** @example 2024-01-01T00:00:00Z */ + updated_at?: string; + }; + /** @example 语言已创建。 */ + message?: string; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/locales/{code}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description 语言代码。 + * @example en + */ + code: string; + }; + cookie?: never; + }; + /** 获取语言详情 */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description 语言代码。 + * @example en + */ + code: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example locale */ + kind?: string; + /** @example en */ + code?: string; + /** @example English */ + name?: string; + /** @example English */ + native_name?: string; + /** @example true */ + is_active?: boolean; + /** @example 1 */ + sort_order?: number; + /** @example 2024-01-01T00:00:00Z */ + created_at?: string; + /** @example 2024-01-01T00:00:00Z */ + updated_at?: string; + }; + }; + }; + }; + }; + }; + /** 更新语言 */ + put: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description 语言代码。 + * @example en + */ + code: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description 语言名称。 + * @example French Updated + */ + name?: string; + /** + * @description 本地名称。 + * @example Français + */ + native_name?: string; + /** + * @description 是否启用。 + * @example true + */ + is_active?: boolean; + /** + * @description 排序值。 + * @example 5 + */ + sort_order?: number; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example locale */ + kind?: string; + /** @example fr */ + code?: string; + /** @example French Updated */ + name?: string; + /** @example Français */ + native_name?: string; + /** @example true */ + is_active?: boolean; + /** @example 5 */ + sort_order?: number; + /** @example 2024-01-01T00:00:00Z */ + created_at?: string; + /** @example 2024-01-01T00:00:00Z */ + updated_at?: string; + }; + /** @example 语言已更新。 */ + message?: string; + }; + }; + }; + }; + }; + post?: never; + /** 删除语言 */ + delete: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description 语言代码。 + * @example en + */ + code: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** @example 语言已删除。 */ + message?: string; + }; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/assign": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 分配用户 SID + * @description 管理员为用户分配或覆盖 SID。 + */ + post: operations["SID"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/rules": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 规则列表 + * @description 获取当前 SID 规则开关列表。 + */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": 1, + * "key": "all_same_letters", + * "description": "全相同字母", + * "enabled": true + * } + * ] + */ + data?: { + /** @example 1 */ + id?: number; + /** @example all_same_letters */ + key?: string; + /** @example 全相同字母 */ + description?: string; + /** @example true */ + enabled?: boolean; + }[]; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/rules/{key}": { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + key: string; + }; + cookie?: never; + }; + get?: never; + /** + * 更新规则开关 + * @description 更新指定 SID 规则开关状态。 + */ + put: { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + key: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description 是否启用。 + * @example false + */ + enabled: boolean; + /** + * @description 规则描述。 + * @example Eius et animi quos velit et. + */ + description?: string | null; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 1 */ + id?: number; + /** @example all_same_letters */ + key?: string; + /** @example 全相同字母 */ + description?: string; + /** @example false */ + enabled?: boolean; + }; + }; + }; + }; + }; + }; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/lucky-numbers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 吉利号列表 + * @description 获取已配置的吉利号列表。 + */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": 1, + * "number": "888", + * "category": "顶级超级吉利", + * "enabled": true + * } + * ] + */ + data?: { + /** @example 1 */ + id?: number; + /** @example 888 */ + number?: string; + /** @example 顶级超级吉利 */ + category?: string; + /** @example true */ + enabled?: boolean; + }[]; + }; + }; + }; + }; + }; + put?: never; + /** + * 新增吉利号 + * @description 新增一条吉利号配置。 + */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description 3位数字吉利号。 + * @example 888 + */ + number: string; + /** + * @description 分类标签。 + * @example 顶级超级吉利 + */ + category?: string | null; + /** + * @description 是否启用。 + * @example false + */ + enabled?: boolean; + }; + }; + }; + responses: { + /** @description success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 10 */ + id?: number; + /** @example 888 */ + number?: string; + /** @example 顶级超级吉利 */ + category?: string; + /** @example true */ + enabled?: boolean; + }; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/lucky-numbers/{id}": { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the lucky number. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + get?: never; + /** + * 更新吉利号 + * @description 更新吉利号的状态或分类。 + */ + put: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the lucky number. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description 分类标签。 + * @example architecto + */ + category?: string | null; + /** + * @description 是否启用。 + * @example false + */ + enabled?: boolean; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 10 */ + id?: number; + /** @example 888 */ + number?: string; + /** @example 顶级超级吉利 */ + category?: string; + /** @example false */ + enabled?: boolean; + }; + }; + }; + }; + }; + }; + post?: never; + /** + * 删除吉利号 + * @description 删除指定吉利号。 + */ + delete: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the lucky number. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** @example null */ + data?: string; + }; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/v1/sid/check": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 校验 SID 是否保留 + * @description 判断 SID 是否命中保留规则。 + */ + post: operations["SID"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: never; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + OTP: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Admin email or phone. + * @example admin@example.com + */ + identity: string; + /** + * @description Identity type (email/phone). + * @example email + */ + type: string; + /** + * @description required_if:type,phone ISO 3166-1 alpha-3 country code. + * @example CHN + */ + country_code?: string; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** @example null */ + data?: string; + meta?: { + /** @example 4 */ + code_length?: number; + }; + /** @example OTP sent successfully */ + message?: string; + }; + }; + }; + /** @description validation_error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "Identity field is required" + * ] + */ + identity?: string[]; + /** + * @example [ + * "Type must be email or phone" + * ] + */ + type?: string[]; + /** + * @example [ + * "Country code is required for phone type" + * ] + */ + country_code?: string[]; + }; + }; + }; + }; + }; + /** @description rate_limit */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example TOO_MANY_REQUESTS */ + code?: string; + /** @example Too many requests */ + message?: string; + data?: { + /** @example 60 */ + retry_after?: number; + }; + }; + }; + }; + }; + }; + OTP: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Admin email or phone. + * @example admin@example.com + */ + identity: string; + /** + * @description Identity type (email/phone). + * @example email + */ + type: string; + /** + * @description OTP code. + * @example 1234 + */ + code: string; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6... */ + access_token?: string; + /** @example Bearer */ + token_type?: string; + /** @example 900 */ + expires_in?: number; + /** @example refresh-token-string */ + refresh_token?: string; + }; + /** @example Admin login successful */ + message?: string; + }; + }; + }; + /** @description invalid_otp */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example INVALID_OTP */ + code?: string; + /** @example Invalid or expired OTP code */ + message?: string; + }; + }; + }; + /** @description validation_error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + data?: { + errors?: { + /** + * @example [ + * "OTP code is required" + * ] + */ + code?: string[]; + /** + * @example [ + * "Type must be email or phone" + * ] + */ + type?: string[]; + }; + }; + }; + }; + }; + }; + }; + getTheCurrentAdminUserWithRolesAndPermissions: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example user */ + kind?: string; + /** @example 01J... */ + uid?: string; + /** @example Admin */ + display_name?: string; + /** @example active */ + status?: string; + }; + meta?: { + /** + * @example [ + * "admin" + * ] + */ + roles?: string[]; + /** + * @example [ + * "admin:category:read", + * "admin:category:approve" + * ] + */ + permissions?: string[]; + }; + }; + }; + }; + }; + }; + AgentToken: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + AgentToken: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example studio_agent_token_grant */ + kind?: string; + /** @example Bearer */ + token_type?: string; + /** @example 1296000 */ + expires_in?: number; + /** @example eyJ0… */ + access_token?: string; + /** @example def5… */ + refresh_token?: string; + connect?: { + /** @example https://host/mcp/studio */ + endpoint?: string; + /** @example Authorization: Bearer */ + header?: string; + }; + }; + /** @example Agent token 签发成功 */ + message?: string; + }; + }; + }; + }; + }; + AgentToken: { + parameters: { + query?: never; + header?: never; + path: { + /** @example architecto */ + tokenId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioCategoryBriefs: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioCategoryBriefs: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioCategoryBriefsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category brief. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioCategoryBriefsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category brief. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioCategoryBriefsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category brief. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioSubtopics: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioSubtopics: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioSubtopicsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the subtopic. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioSubtopicsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the subtopic. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioSubtopicsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the subtopic. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioArticles: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioArticles: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioArticlesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the article. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioArticlesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the article. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioArticlesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the article. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioSeries: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioSeries: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioSeriesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the series. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioSeriesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the series. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioSeriesId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the series. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioRobotAuthors: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioRobotAuthors: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioRobotAuthorsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the robot author. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioRobotAuthorsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the robot author. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioRobotAuthorsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the robot author. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioStylePresets: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + postApiAdminV1StudioStylePresets: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getApiAdminV1StudioStylePresetsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the style preset. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Authentication required */ + message?: string; + /** @example UNAUTHENTICATED */ + code?: string; + }; + }; + }; + }; + }; + putApiAdminV1StudioStylePresetsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the style preset. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + deleteApiAdminV1StudioStylePresetsId: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the style preset. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + getCategoryTreeadmin: { + parameters: { + query?: { + /** + * @description Optional locale code (e.g., 'zh-hans', 'en'). Defaults to current application locale. + * @example sr_BA + */ + locale?: string; + /** + * @description Optional flag to include inactive categories. Defaults to false. + * @example false + */ + include_inactive?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + }; + }; + listPendingUserCreatedCategories: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example You are not authorized to access this resource. (missing permission: admin:category:approve) */ + message?: string; + /** @example HTTP_ERROR */ + code?: string; + }; + }; + }; + }; + }; + approveAPendingCategorymakeItPublic: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + rejectAPublicRequestkeepTheCategoryPrivate: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: never; + }; + mergeACategoryIntoAnotherpreservesBothIDs: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description The surviving category UUID. + * @example architecto + */ + target_id: string; + }; + }; + }; + responses: never; + }; + createCategory: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Optional UUID for the category. Auto-generated if not provided. + * @example architecto + */ + id?: string | null; + /** + * @description Optional UUID of parent category. + * @example architecto + */ + parent_id?: string | null; + /** + * @description Unique slug identifier for the category. + * @example architecto + */ + slug: string; + /** + * @description Optional sort order (default: 0). + * @example 16 + */ + sort_order?: number; + /** + * @description Optional active status (default: true). + * @example false + */ + is_active?: boolean; + /** + * @description Optional Array of translations. + * @example [ + * "architecto" + * ] + */ + translations?: (string | null)[]; + }; + }; + }; + responses: { + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example category */ + kind?: string; + /** @example uuid */ + id?: string; + /** @example null */ + parent_id?: string; + /** @example 1 */ + level?: number; + /** @example new-category */ + slug?: string; + /** @example 0 */ + sort_order?: number; + /** @example true */ + is_active?: boolean; + /** @example new-category */ + name?: string; + /** @example null */ + description?: string; + /** @example [] */ + translations?: unknown[]; + /** @example 2026-01-27T12:00:00.000000Z */ + created_at?: string; + /** @example 2026-01-27T12:00:00.000000Z */ + updated_at?: string; + }; + /** @example Category created successfully */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + /** + * @example [ + * "Category slug must be unique" + * ] + */ + data?: string[]; + }; + }; + }; + }; + }; + getCategoryDetails: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example Category not found */ + message?: string; + }; + }; + }; + }; + }; + updateCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description Optional UUID of parent category. + * @example architecto + */ + parent_id?: string | null; + /** + * @description Optional unique slug identifier. + * @example architecto + */ + slug?: string; + /** + * @description Optional sort order. + * @example 16 + */ + sort_order?: number; + /** + * @description Optional active status. + * @example false + */ + is_active?: boolean; + /** + * @description Optional array of translations. + * @example [ + * "architecto" + * ] + */ + translations?: string[]; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example category */ + kind?: string; + /** @example uuid */ + id?: string; + /** @example null */ + parent_id?: string; + /** @example 1 */ + level?: number; + /** @example updated-category */ + slug?: string; + /** @example 更新的分类 */ + name?: string; + /** @example 更新的描述 */ + description?: string; + }; + /** @example Category updated successfully */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example VALIDATION_ERROR */ + code?: string; + /** @example Validation failed */ + message?: string; + /** + * @example [ + * "Cannot set parent as it would create a circular reference" + * ] + */ + data?: string[]; + }; + }; + }; + }; + }; + deleteCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** @example Category deleted successfully */ + message?: string; + }; + }; + }; + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example DELETE_FAILED */ + code?: string; + /** @example Failed to delete category */ + message?: string; + /** @example Cannot delete category with children */ + data?: string; + }; + }; + }; + }; + }; + listAllTranslationsForCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** + * @example [ + * { + * "id": "translation-uuid-1", + * "category_id": "category-uuid", + * "locale": "en", + * "name": "Home Services", + * "description": "Various home services" + * }, + * { + * "id": "translation-uuid-2", + * "category_id": "category-uuid", + * "locale": "zh-hans", + * "name": "家庭服务", + * "description": "提供各种家庭服务" + * } + * ] + */ + data?: { + /** @example translation-uuid-1 */ + id?: string; + /** @example category-uuid */ + category_id?: string; + /** @example en */ + locale?: string; + /** @example Home Services */ + name?: string; + /** @example Various home services */ + description?: string; + }[]; + }; + }; + }; + }; + }; + getTranslationForSpecificLocale: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description Locale code (e.g., 'en', 'zh-hans'). + * @example sr_BA + */ + locale: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example translation-uuid */ + id?: string; + /** @example category-uuid */ + category_id?: string; + /** @example zh-hans */ + locale?: string; + /** @example 家庭服务 */ + name?: string; + /** @example 提供各种家庭服务 */ + description?: string; + /** @example 2026-01-27T12:00:00.000000Z */ + created_at?: string; + /** @example 2026-01-27T12:00:00.000000Z */ + updated_at?: string; + }; + }; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example TRANSLATION_NOT_FOUND */ + code?: string; + /** @example Translation not found */ + message?: string; + }; + }; + }; + }; + }; + createOrUpdateTranslationForSpecificLocale: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description Locale code (e.g., 'en', 'zh-hans'). + * @example sr_BA + */ + locale: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Translation name. + * @example architecto + */ + name: string; + /** + * @description Optional translation description. + * @example Eius et animi quos velit et. + */ + description?: string | null; + }; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example translation-uuid */ + id?: string; + /** @example category-uuid */ + category_id?: string; + /** @example zh-hans */ + locale?: string; + /** @example 家庭服务 */ + name?: string; + /** @example 提供各种家庭服务 */ + description?: string; + /** @example 2026-01-27T12:00:00.000000Z */ + created_at?: string; + /** @example 2026-01-27T12:00:00.000000Z */ + updated_at?: string; + }; + /** @example Translation updated successfully */ + message?: string; + }; + }; + }; + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example translation-uuid */ + id?: string; + /** @example category-uuid */ + category_id?: string; + /** @example zh-hans */ + locale?: string; + /** @example 家庭服务 */ + name?: string; + /** @example 提供各种家庭服务 */ + description?: string; + }; + /** @example Translation created successfully */ + message?: string; + }; + }; + }; + }; + }; + deleteTranslationForSpecificLocale: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description The ID of the category. + * @example 2912b591-09f0-4c14-9190-866abd51cbee + */ + category_id: string; + /** + * @description Locale code (e.g., 'en', 'zh-hans'). + * @example sr_BA + */ + locale: string; + /** + * @description UUID of the category. + * @example architecto + */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + /** @example Translation deleted successfully */ + message?: string; + }; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example false */ + success?: boolean; + /** @example TRANSLATION_NOT_FOUND */ + code?: string; + /** @example Translation not found */ + message?: string; + }; + }; + }; + }; + }; + SID: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description 用户 UID。 + * @example 01HQ8M4Z2A3B5C6D7E8F9G0H1I2J3K4L + */ + user_uid: string; + /** + * @description SID 值,支持带/不带横杠。 + * @example ABC-168888 + */ + sid: string; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example 01HQ8M4Z2A3B5C6D7E8F9G0H1I2J3K4L */ + uid?: string; + /** @example ABC168888 */ + sid?: string; + }; + }; + }; + }; + }; + }; + SID: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description SID 值,支持带/不带横杠。 + * @example ABC-888888 + */ + sid: string; + }; + }; + }; + responses: { + /** @description success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example true */ + success?: boolean; + data?: { + /** @example ABC-888888 */ + sid?: string; + /** @example true */ + reserved?: boolean; + }; + }; + }; + }; + }; + }; +} diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 0000000..a526f53 --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,257 @@ +/** + * Hand-written types for the gig-platform admin API. + * + * The scribe-generated `types.gen.ts` covers paths/params, but its response + * schemas are example-based and incomplete — the envelope and entities below + * are the source of truth for data shapes (mirrors CLAUDE.md API design). + */ + +// ---------- envelope ---------- + +export interface ApiResponse { + success: boolean + data: T + message?: string + meta?: Record +} + +export interface Pagination { + current_page: number + next_page: number | null + page_size: number + prev_page: number | null + total_count: number + total_pages: number +} + +export interface PaginatedResponse extends ApiResponse { + pagination: Pagination +} + +export interface ApiErrorPayload { + success: false + code: string + message: string + data?: { errors?: Record } & Record +} + +export interface ListParams { + page?: number + page_size?: number + [key: string]: string | number | boolean | undefined +} + +// ---------- auth ---------- + +export interface TokenGrant { + token_type: string + expires_in: number + access_token: string + refresh_token?: string +} + +export interface AdminUser { + kind: 'user' + uid: string + sid?: string | null + username: string | null + email: string | null + phone: string | null + display_name: string | null + avatar: string | null + status: boolean | string + is_robot?: boolean +} + +export interface MeResponse extends ApiResponse { + meta: { + roles: string[] + permissions: string[] + } +} + +// ---------- category ---------- + +export interface Category { + kind: 'category' + id: string + parent_id: string | null + level: number + slug: string + name: string + description: string | null + status: string + sort_order: number + is_leaf?: boolean + created_by_uid?: string | null + children?: Category[] + created_at?: string + updated_at?: string +} + +export interface CategoryTranslation { + locale: string + name: string + description: string | null +} + +// ---------- studio ---------- + +export type ArticleStatus = + | 'planned' + | 'drafting' + | 'drafted' + | 'published' + | 'scheduled' + +export interface StudioArticle { + kind: 'studio_article' + id: string + series_id: string + robot_author_id: string + category_id: string | null + subtopic_id: string | null + article_number: number + plan: Record | null + outline: Record | null + title: string | null + summary: string | null + key_takeaways: string[] | null + cta: string | null + tags: string[] | null + seo_keywords: string[] | null + cover_image_prompt: string | null + cover_asset_id: string | null + cover_image: { url: string; alt?: string | null } | null + inline_images: Array<{ url: string; alt?: string | null }> | null + inline_image_prompts: unknown[] | null + status: ArticleStatus + publish_schedule: Record | null + dimzou_document_id: number | null + dimzou_publication_id: number | null + created_at: string + updated_at: string +} + +export type RobotAuthorStatus = 'pending' | 'profiling' | 'ready' | 'paused' + +export interface StudioRobotAuthor { + kind: 'studio_robot_author' + id: string + user_uid: string + category_id: string + subtopic_id: string | null + persona: string | null + positioning: string | null + target_readers: string | null + article_style: Record | null + image_style: Record | null + publishing_rules: Record | null + status: RobotAuthorStatus + source: 'user' | 'category' + created_at: string + updated_at: string +} + +export type SeriesStatus = 'planned' | 'in_progress' | 'complete' + +export interface StudioSeries { + kind: 'studio_series' + id: string + category_id: string + subtopic_id: string | null + robot_author_id: string + goal: string | null + reader_journey: string | null + target_article_count: number | null + planned_articles: unknown[] | null + status: SeriesStatus + created_at: string + updated_at: string +} + +export interface StudioSubtopic { + kind: 'studio_subtopic' + id: string + category_id: string + title: string + target_audience: string | null + pain_points: string | null + content_value: string | null + expandability_score: number | null + status: 'active' | 'archived' + created_at: string + updated_at: string +} + +export interface StudioCategoryBrief { + kind: 'studio_category_brief' + id: string + category_id: string + description: string | null + target_audience: string | null + content_angles: string[] | null + risk_notes: string | null + subtopic_count: number + author_count: number + article_count: number + created_at: string + updated_at: string +} + +export interface StudioStylePreset { + kind: 'studio_style_preset' + id: string + type: 'article' | 'image' | 'bundle' + name: string + payload: Record + created_at: string + updated_at: string +} + +export interface StudioAgentToken { + kind: 'studio_agent_token' + id: string + scopes: string[] | null + created_at: string | null + expires_at: string | null +} + +export interface StudioAgentTokenGrant extends TokenGrant { + kind: 'studio_agent_token_grant' + connect: { endpoint: string; header: string } +} + +// ---------- sid / locales / devices ---------- + +export interface SidRule { + key: string + value: unknown + description?: string | null +} + +export interface LuckyNumber { + id: number | string + number: string + price?: number | null + status?: string + reserved_for_uid?: string | null + created_at?: string +} + +export interface LocaleRow { + code: string + name: string + native_name?: string | null + enabled?: boolean + is_default?: boolean +} + +export interface NotificationDevice { + id: number | string + user_uid: string + platform?: string + token?: string + last_seen_at?: string | null + created_at?: string +}