Files
promote/backend/src/swagger/index.ts
2025-03-11 00:36:22 +08:00

2891 lines
84 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { swaggerUI } from '@hono/swagger-ui'
import { Hono } from 'hono'
import supabase from '../utils/supabase'
// 创建 OpenAPI 规范
export const openAPISpec = {
openapi: '3.0.0',
info: {
title: 'Promote API',
version: '1.0.0',
description: 'API documentation for the Promote platform',
},
servers: [
{
url: 'http://localhost:4000',
description: 'Local development server',
},
],
paths: {
'/': {
get: {
summary: 'Health check',
description: 'Returns the API status',
responses: {
'200': {
description: 'API is running',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
status: { type: 'string', example: 'ok' },
message: { type: 'string', example: 'Promote API is running' },
version: { type: 'string', example: '1.0.0' },
},
},
},
},
},
},
},
},
'/api/auth/register': {
post: {
summary: 'Register a new user',
description: 'Creates a new user account',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['email', 'password', 'name'],
properties: {
email: { type: 'string', format: 'email', example: 'user@example.com' },
password: { type: 'string', format: 'password', example: 'securepassword' },
name: { type: 'string', example: 'John Doe' },
},
},
},
},
},
responses: {
'201': {
description: 'User registered successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'User registered successfully' },
user: {
type: 'object',
properties: {
id: { type: 'string', example: '123e4567-e89b-12d3-a456-426614174000' },
email: { type: 'string', example: 'user@example.com' },
name: { type: 'string', example: 'John Doe' },
},
},
token: { type: 'string', example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' },
},
},
},
},
},
'400': {
description: 'Bad request',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Email, password, and name are required' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/auth/login': {
post: {
summary: 'Login user',
description: 'Authenticates a user and returns a JWT token',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['email', 'password'],
properties: {
email: { type: 'string', format: 'email', example: 'vitalitymailg@gmail.com' },
password: { type: 'string', format: 'password', example: 'password123' },
},
},
examples: {
demoUser: {
summary: '示例用户',
value: {
email: 'vitalitymailg@gmail.com',
password: 'password123'
}
}
}
},
},
},
responses: {
'200': {
description: 'Login successful',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'Login successful' },
user: {
type: 'object',
properties: {
id: { type: 'string', example: '123e4567-e89b-12d3-a456-426614174000' },
email: { type: 'string', example: 'vitalitymailg@gmail.com' },
name: { type: 'string', example: 'Vitality User' },
},
},
token: { type: 'string', example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' },
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Invalid credentials' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/auth/verify': {
get: {
summary: 'Verify token',
description: 'Verifies a JWT token',
security: [
{
bearerAuth: [],
},
],
responses: {
'200': {
description: 'Token is valid',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'Token is valid' },
user: {
type: 'object',
properties: {
id: { type: 'string', example: '123e4567-e89b-12d3-a456-426614174000' },
email: { type: 'string', example: 'vitalitymailg@gmail.com' },
},
},
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Invalid token' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/analytics/view': {
post: {
summary: 'Track view event',
description: 'Records a view event for content',
security: [
{
bearerAuth: [],
},
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['content_id'],
properties: {
content_id: { type: 'string', example: 'content-123' },
},
},
},
},
},
responses: {
'200': {
description: 'View tracked successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'View tracked successfully' },
},
},
},
},
},
'400': {
description: 'Bad request',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Content ID is required' },
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Unauthorized: No token provided' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/analytics/like': {
post: {
summary: 'Track like event',
description: 'Records a like or unlike event for content',
security: [
{
bearerAuth: [],
},
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['content_id', 'action'],
properties: {
content_id: { type: 'string', example: 'content-123' },
action: { type: 'string', enum: ['like', 'unlike'], example: 'like' },
},
},
},
},
},
responses: {
'200': {
description: 'Like/unlike tracked successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'like tracked successfully' },
},
},
},
},
},
'400': {
description: 'Bad request',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Content ID and action are required' },
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Unauthorized: No token provided' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/analytics/follow': {
post: {
summary: 'Track follow event',
description: 'Records a follow or unfollow event for a user',
security: [
{
bearerAuth: [],
},
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['followed_id', 'action'],
properties: {
followed_id: { type: 'string', example: 'user-123' },
action: { type: 'string', enum: ['follow', 'unfollow'], example: 'follow' },
},
},
},
},
},
responses: {
'200': {
description: 'Follow/unfollow tracked successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'follow tracked successfully' },
},
},
},
},
},
'400': {
description: 'Bad request',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Followed ID and action are required' },
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Unauthorized: No token provided' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/analytics/content/{id}': {
get: {
summary: 'Get content analytics',
description: 'Returns analytics data for a specific content',
security: [
{
bearerAuth: [],
},
],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'string',
},
description: 'Content ID',
example: 'content-123',
},
],
responses: {
'200': {
description: 'Content analytics data',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
content_id: { type: 'string', example: 'content-123' },
views: { type: 'integer', example: 1250 },
likes: { type: 'integer', example: 87 },
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Unauthorized: No token provided' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/analytics/user/{id}': {
get: {
summary: 'Get user analytics',
description: 'Returns analytics data for a specific user',
security: [
{
bearerAuth: [],
},
],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: {
type: 'string',
},
description: 'User ID',
example: 'user-123',
},
],
responses: {
'200': {
description: 'User analytics data',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
user_id: { type: 'string', example: 'user-123' },
followers: { type: 'integer', example: 542 },
content_analytics: {
type: 'object',
properties: {
views: {
type: 'array',
items: {
type: 'object',
properties: {
content_id: { type: 'string', example: 'content-123' },
view_count: { type: 'integer', example: 1250 },
},
},
},
likes: {
type: 'array',
items: {
type: 'object',
properties: {
content_id: { type: 'string', example: 'content-123' },
like_count: { type: 'integer', example: 87 },
},
},
},
},
},
},
},
},
},
},
'401': {
description: 'Unauthorized',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Unauthorized: No token provided' },
},
},
},
},
},
'500': {
description: 'Internal server error',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string', example: 'Internal server error' },
},
},
},
},
},
},
},
},
'/api/posts': {
get: {
summary: '获取帖子列表',
description: '返回分页的帖子列表,支持过滤和排序',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'influencer_id',
in: 'query',
description: '按影响者ID过滤',
schema: { type: 'string', format: 'uuid' },
required: false
},
{
name: 'platform',
in: 'query',
description: '按平台过滤',
schema: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
},
required: false
},
{
name: 'limit',
in: 'query',
description: '每页返回的记录数',
schema: { type: 'integer', default: 20 },
required: false
},
{
name: 'offset',
in: 'query',
description: '分页偏移量',
schema: { type: 'integer', default: 0 },
required: false
},
{
name: 'sort',
in: 'query',
description: '排序字段',
schema: { type: 'string', default: 'published_at' },
required: false
},
{
name: 'order',
in: 'query',
description: '排序方向',
schema: {
type: 'string',
enum: ['asc', 'desc'],
default: 'desc'
},
required: false
}
],
responses: {
'200': {
description: '成功获取帖子列表',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
posts: {
type: 'array',
items: {
$ref: '#/components/schemas/Post'
}
},
total: { type: 'integer' },
limit: { type: 'integer' },
offset: { type: 'integer' }
}
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
},
post: {
summary: '创建新帖子',
description: '创建一个新的帖子',
security: [{ bearerAuth: [] }],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['influencer_id', 'platform', 'post_url'],
properties: {
influencer_id: { type: 'string', format: 'uuid' },
platform: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
},
post_url: { type: 'string', format: 'uri' },
title: { type: 'string' },
description: { type: 'string' },
published_at: { type: 'string', format: 'date-time' }
}
}
}
}
},
responses: {
'201': {
description: '帖子创建成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
post: {
$ref: '#/components/schemas/Post'
}
}
}
}
}
},
'400': {
description: '请求参数错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'409': {
description: '帖子URL已存在',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: { type: 'string' },
post: {
$ref: '#/components/schemas/Post'
}
}
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/posts/{id}': {
get: {
summary: '获取单个帖子详情',
description: '返回指定ID的帖子详情包括统计数据和时间线',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '帖子ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
responses: {
'200': {
description: '成功获取帖子详情',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/PostDetail'
}
}
}
},
'404': {
description: '帖子不存在',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
},
put: {
summary: '更新帖子',
description: '更新指定ID的帖子信息',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '帖子ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
properties: {
title: { type: 'string' },
description: { type: 'string' }
}
}
}
}
},
responses: {
'200': {
description: '帖子更新成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
post: {
$ref: '#/components/schemas/Post'
}
}
}
}
}
},
'404': {
description: '帖子不存在',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
},
delete: {
summary: '删除帖子',
description: '删除指定ID的帖子',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '帖子ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
responses: {
'200': {
description: '帖子删除成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' }
}
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/posts/{id}/comments': {
get: {
summary: '获取帖子评论',
description: '返回指定帖子的评论列表',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '帖子ID',
required: true,
schema: { type: 'string', format: 'uuid' }
},
{
name: 'limit',
in: 'query',
description: '每页返回的记录数',
schema: { type: 'integer', default: 20 },
required: false
},
{
name: 'offset',
in: 'query',
description: '分页偏移量',
schema: { type: 'integer', default: 0 },
required: false
}
],
responses: {
'200': {
description: '成功获取评论列表',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
comments: {
type: 'array',
items: {
$ref: '#/components/schemas/Comment'
}
},
total: { type: 'integer' },
limit: { type: 'integer' },
offset: { type: 'integer' }
}
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
},
post: {
summary: '添加评论',
description: '为指定帖子添加新评论',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '帖子ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['content'],
properties: {
content: { type: 'string' },
sentiment_score: { type: 'number' }
}
}
}
}
},
responses: {
'201': {
description: '评论添加成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
comment: {
$ref: '#/components/schemas/Comment'
}
}
}
}
}
},
'400': {
description: '请求参数错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/posts/comments/{id}': {
put: {
summary: '更新评论',
description: '更新指定ID的评论',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '评论ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
properties: {
content: { type: 'string' },
sentiment_score: { type: 'number' }
}
}
}
}
},
responses: {
'200': {
description: '评论更新成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
comment: {
$ref: '#/components/schemas/Comment'
}
}
}
}
}
},
'404': {
description: '评论不存在或无权限',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
},
delete: {
summary: '删除评论',
description: '删除指定ID的评论',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
description: '评论ID',
required: true,
schema: { type: 'string', format: 'uuid' }
}
],
responses: {
'200': {
description: '评论删除成功',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' }
}
}
}
}
},
'404': {
description: '评论不存在或无权限',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/comments': {
get: {
tags: ['Comments'],
summary: 'Get comments',
description: 'Retrieve a list of comments with optional filtering by post_id',
parameters: [
{
name: 'post_id',
in: 'query',
description: 'Filter comments by post ID',
required: false,
schema: {
type: 'string',
format: 'uuid'
}
},
{
name: 'limit',
in: 'query',
description: 'Number of comments to return',
required: false,
schema: {
type: 'integer',
default: 10
}
},
{
name: 'offset',
in: 'query',
description: 'Number of comments to skip',
required: false,
schema: {
type: 'integer',
default: 0
}
}
],
responses: {
'200': {
description: 'List of comments',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
comments: {
type: 'array',
items: {
$ref: '#/components/schemas/Comment'
}
},
count: {
type: 'integer'
},
limit: {
type: 'integer'
},
offset: {
type: 'integer'
}
}
}
}
}
}
}
},
post: {
tags: ['Comments'],
summary: 'Create a comment',
description: 'Create a new comment on a post',
security: [
{
bearerAuth: []
}
],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['post_id', 'content'],
properties: {
post_id: {
type: 'string',
format: 'uuid'
},
content: {
type: 'string'
}
}
}
}
}
},
responses: {
'201': {
description: 'Comment created successfully',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Comment'
}
}
}
},
'401': {
description: 'Unauthorized'
}
}
}
},
'/api/comments/{comment_id}': {
delete: {
tags: ['Comments'],
summary: 'Delete a comment',
description: 'Delete a comment by ID (only for comment owner)',
security: [
{
bearerAuth: []
}
],
parameters: [
{
name: 'comment_id',
in: 'path',
description: 'Comment ID to delete',
required: true,
schema: {
type: 'string',
format: 'uuid'
}
}
],
responses: {
'204': {
description: 'Comment deleted successfully'
},
'401': {
description: 'Unauthorized'
},
'404': {
description: 'Comment not found or unauthorized'
}
}
}
},
'/api/influencers': {
get: {
tags: ['Influencers'],
summary: 'Get influencers',
description: 'Retrieve a list of influencers with optional filtering and sorting',
parameters: [
{
name: 'platform',
in: 'query',
description: 'Filter by platform',
required: false,
schema: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
}
},
{
name: 'min_followers',
in: 'query',
description: 'Minimum number of followers',
required: false,
schema: {
type: 'integer'
}
},
{
name: 'max_followers',
in: 'query',
description: 'Maximum number of followers',
required: false,
schema: {
type: 'integer'
}
},
{
name: 'sort_by',
in: 'query',
description: 'Field to sort by',
required: false,
schema: {
type: 'string',
enum: ['followers_count', 'video_count', 'created_at'],
default: 'followers_count'
}
},
{
name: 'sort_order',
in: 'query',
description: 'Sort order',
required: false,
schema: {
type: 'string',
enum: ['asc', 'desc'],
default: 'desc'
}
},
{
name: 'limit',
in: 'query',
description: 'Number of influencers to return',
required: false,
schema: {
type: 'integer',
default: 10
}
},
{
name: 'offset',
in: 'query',
description: 'Number of influencers to skip',
required: false,
schema: {
type: 'integer',
default: 0
}
}
],
responses: {
'200': {
description: 'List of influencers',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
influencers: {
type: 'array',
items: {
$ref: '#/components/schemas/Influencer'
}
},
count: {
type: 'integer'
},
limit: {
type: 'integer'
},
offset: {
type: 'integer'
}
}
}
}
}
}
}
}
},
'/api/influencers/stats': {
get: {
tags: ['Influencers'],
summary: 'Get influencer statistics',
description: 'Retrieve aggregated statistics about influencers',
parameters: [
{
name: 'platform',
in: 'query',
description: 'Filter statistics by platform',
required: false,
schema: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
}
}
],
responses: {
'200': {
description: 'Influencer statistics',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
total_influencers: {
type: 'integer'
},
total_followers: {
type: 'integer'
},
total_videos: {
type: 'integer'
},
average_followers: {
type: 'integer'
},
average_videos: {
type: 'integer'
}
}
}
}
}
}
}
}
},
'/api/influencers/{influencer_id}': {
get: {
tags: ['Influencers'],
summary: 'Get influencer by ID',
description: 'Retrieve detailed information about a specific influencer',
parameters: [
{
name: 'influencer_id',
in: 'path',
description: 'Influencer ID',
required: true,
schema: {
type: 'string',
format: 'uuid'
}
}
],
responses: {
'200': {
description: 'Influencer details',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/InfluencerWithPosts'
}
}
}
},
'404': {
description: 'Influencer not found'
}
}
}
},
'/api/analytics/influencer/track': {
post: {
tags: ['Analytics'],
summary: '追踪网红指标变化',
description: '记录网红账号的关键指标(如粉丝数、视频数等)变化',
security: [{ bearerAuth: [] }],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['influencer_id', 'metrics'],
properties: {
influencer_id: { type: 'string', format: 'uuid', description: '网红ID' },
metrics: {
type: 'object',
properties: {
followers_count: { type: 'number', description: '粉丝数量' },
video_count: { type: 'number', description: '视频数量' },
views_count: { type: 'number', description: '总观看数' },
likes_count: { type: 'number', description: '总点赞数' }
}
}
}
}
}
}
},
responses: {
'200': {
description: '成功追踪网红指标',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'Influencer metrics tracked successfully' },
influencer_id: { type: 'string', format: 'uuid' },
tracked_metrics: {
type: 'object',
properties: {
followers_count: { type: 'number' },
video_count: { type: 'number' }
}
}
}
}
}
}
},
'400': {
description: '请求参数错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'401': {
description: '未授权',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器内部错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/analytics/influencer/{id}/growth': {
get: {
tags: ['Analytics'],
summary: '获取网红粉丝增长趋势',
description: '按不同时间粒度(天/周/月)获取网红的粉丝或其他指标变化趋势',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '网红ID'
},
{
name: 'metric',
in: 'query',
schema: {
type: 'string',
enum: ['followers_count', 'video_count', 'views_count', 'likes_count'],
default: 'followers_count'
},
description: '要分析的指标'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['30days', '90days', '6months', '1year'],
default: '6months'
},
description: '分析的时间范围'
},
{
name: 'interval',
in: 'query',
schema: {
type: 'string',
enum: ['day', 'week', 'month'],
default: 'month'
},
description: '数据聚合的时间间隔'
}
],
responses: {
'200': {
description: '网红增长趋势数据',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
influencer_id: { type: 'string', format: 'uuid' },
influencer_info: {
type: 'object',
properties: {
name: { type: 'string' },
platform: { type: 'string' },
followers_count: { type: 'number' }
}
},
metric: { type: 'string' },
timeframe: { type: 'string' },
interval: { type: 'string' },
data: {
type: 'array',
items: {
type: 'object',
properties: {
time_period: { type: 'string', format: 'date' },
change: { type: 'number' },
total_value: { type: 'number' }
}
}
}
}
}
}
}
},
'401': {
description: '未授权',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
},
'500': {
description: '服务器内部错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/analytics/content/track': {
post: {
tags: ['Analytics'],
summary: '追踪内容互动数据',
description: '记录文章/内容的互动数据变化(如观看数、点赞数等)',
security: [{ bearerAuth: [] }],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['post_id', 'metrics'],
properties: {
post_id: { type: 'string', format: 'uuid', description: '文章ID' },
metrics: {
type: 'object',
properties: {
views_count: { type: 'number', description: '观看数量' },
likes_count: { type: 'number', description: '点赞数量' },
comments_count: { type: 'number', description: '评论数量' },
shares_count: { type: 'number', description: '分享数量' }
}
}
}
}
}
}
},
responses: {
'200': {
description: '成功追踪内容指标',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string', example: 'Post metrics tracked successfully' },
post_id: { type: 'string', format: 'uuid' },
tracked_metrics: {
type: 'object',
properties: {
views_count: { type: 'number' },
likes_count: { type: 'number' },
comments_count: { type: 'number' },
shares_count: { type: 'number' }
}
}
}
}
}
}
},
'400': {
description: '请求参数错误',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
}
}
}
}
}
}
},
'/api/analytics/content/{id}/trends': {
get: {
tags: ['Analytics'],
summary: '获取内容互动趋势',
description: '按不同时间粒度查看内容的互动数据变化趋势',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '文章ID'
},
{
name: 'metric',
in: 'query',
schema: {
type: 'string',
enum: ['views_count', 'likes_count', 'comments_count', 'shares_count'],
default: 'views_count'
},
description: '要分析的指标'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['7days', '30days', '90days'],
default: '30days'
},
description: '分析的时间范围'
},
{
name: 'interval',
in: 'query',
schema: {
type: 'string',
enum: ['hour', 'day', 'week'],
default: 'day'
},
description: '数据聚合的时间间隔'
}
],
responses: {
'200': {
description: '内容互动趋势数据',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
post_id: { type: 'string', format: 'uuid' },
post_info: {
type: 'object',
properties: {
title: { type: 'string' },
platform: { type: 'string' },
published_at: { type: 'string', format: 'date-time' }
}
},
metric: { type: 'string' },
timeframe: { type: 'string' },
interval: { type: 'string' },
data: {
type: 'array',
items: {
type: 'object',
properties: {
time_period: { type: 'string', format: 'date' },
change: { type: 'number' },
total_value: { type: 'number' }
}
}
}
}
}
}
}
}
}
}
},
'/api/analytics/project/{id}/overview': {
get: {
tags: ['Analytics'],
summary: '获取项目整体分析',
description: '获取项目的整体表现数据,包括关键指标、平台分布和时间线',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '项目ID'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['7days', '30days', '90days', '6months'],
default: '30days'
},
description: '分析的时间范围'
}
],
responses: {
'200': {
description: '项目概览数据',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
project: {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
description: { type: 'string' },
created_at: { type: 'string', format: 'date-time' }
}
},
timeframe: { type: 'string' },
metrics: {
type: 'object',
properties: {
total_influencers: { type: 'number' },
total_posts: { type: 'number' },
total_views: { type: 'number' },
total_likes: { type: 'number' },
total_comments: { type: 'number' },
total_shares: { type: 'number' },
total_followers: { type: 'number' }
}
},
platforms: {
type: 'array',
items: {
type: 'object',
properties: {
platform: { type: 'string' },
count: { type: 'number' },
percentage: { type: 'number' }
}
}
},
timeline: {
type: 'array',
items: {
type: 'object',
properties: {
date: { type: 'string', format: 'date' },
views_change: { type: 'number' },
likes_change: { type: 'number' },
comments_change: { type: 'number' },
shares_change: { type: 'number' },
followers_change: { type: 'number' }
}
}
}
}
}
},
'text/csv': {
schema: {
type: 'string'
}
}
}
}
}
}
},
'/api/analytics/project/{id}/conversion-funnel': {
get: {
summary: '获取KOL合作转换漏斗数据',
description: '获取项目中KOL合作的转换漏斗数据包括各个阶段的数量和比率',
tags: ['Analytics'],
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
description: '项目ID',
schema: {
type: 'string'
}
},
{
name: 'timeframe',
in: 'query',
required: false,
description: '时间范围 (7days, 30days, 90days, 6months)',
schema: {
type: 'string',
enum: ['7days', '30days', '90days', '6months'],
default: '30days'
}
}
],
responses: {
'200': {
description: '成功获取KOL合作转换漏斗数据',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
project: {
type: 'object',
properties: {
id: {
type: 'string',
description: '项目ID'
},
name: {
type: 'string',
description: '项目名称'
}
}
},
timeframe: {
type: 'string',
description: '时间范围'
},
funnel_data: {
type: 'array',
description: '漏斗数据',
items: {
type: 'object',
properties: {
stage: {
type: 'string',
description: '阶段名称'
},
count: {
type: 'integer',
description: 'KOL数量'
},
rate: {
type: 'integer',
description: '占总数的百分比'
}
}
}
},
metrics: {
type: 'object',
properties: {
total_influencers: {
type: 'integer',
description: 'KOL总数'
},
conversion_rate: {
type: 'integer',
description: '总体转化率'
},
avg_stage_dropoff: {
type: 'integer',
description: '平均阶段流失率'
}
}
}
}
}
}
}
},
'404': {
description: '项目未找到',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: {
type: 'string',
example: 'Project not found'
}
}
}
}
}
},
'500': {
description: '服务器错误',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
error: {
type: 'string',
example: 'Internal server error'
}
}
}
}
}
}
}
}
},
'/api/analytics/project/{id}/top-performers': {
get: {
tags: ['Analytics'],
summary: '获取项目中表现最佳的网红',
description: '获取项目中表现最佳的网红列表,可按不同指标排序',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '项目ID'
},
{
name: 'metric',
in: 'query',
schema: {
type: 'string',
enum: ['views_count', 'likes_count', 'followers_count', 'engagement_rate'],
default: 'views_count'
},
description: '排序指标'
},
{
name: 'limit',
in: 'query',
schema: {
type: 'string',
default: '10'
},
description: '返回结果数量'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['7days', '30days', '90days', '6months'],
default: '30days'
},
description: '分析的时间范围'
}
],
responses: {
'200': {
description: '表现最佳的网红列表',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
project_id: { type: 'string', format: 'uuid' },
metric: { type: 'string' },
timeframe: { type: 'string' },
top_performers: {
type: 'array',
items: {
type: 'object',
properties: {
influencer_id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
platform: { type: 'string' },
profile_url: { type: 'string' },
followers_count: { type: 'number' },
video_count: { type: 'number' },
views_count: { type: 'number' },
engagement_rate: { type: 'number' }
}
}
}
}
}
}
}
}
}
}
},
'/api/analytics/schedule/influencer': {
post: {
tags: ['Analytics', 'Scheduled Collection'],
summary: '调度网红数据采集',
description: '设置定时采集网红指标数据的任务',
security: [{ bearerAuth: [] }],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['influencer_id'],
properties: {
influencer_id: { type: 'string', format: 'uuid', description: '网红ID' },
cron_expression: {
type: 'string',
description: 'Cron表达式默认为每天午夜执行 (0 0 * * *)',
example: '0 0 * * *'
}
}
}
}
}
},
responses: {
'200': {
description: '成功调度数据采集',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
influencer_id: { type: 'string', format: 'uuid' },
cron_expression: { type: 'string' }
}
}
}
}
}
}
}
},
'/api/analytics/schedule/post': {
post: {
tags: ['Analytics', 'Scheduled Collection'],
summary: '调度内容数据采集',
description: '设置定时采集内容指标数据的任务',
security: [{ bearerAuth: [] }],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['post_id'],
properties: {
post_id: { type: 'string', format: 'uuid', description: '文章ID' },
cron_expression: {
type: 'string',
description: 'Cron表达式默认为每天午夜执行 (0 0 * * *)',
example: '0 0 * * *'
}
}
}
}
}
},
responses: {
'200': {
description: '成功调度数据采集',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
post_id: { type: 'string', format: 'uuid' },
cron_expression: { type: 'string' }
}
}
}
}
}
}
}
},
'/api/analytics/schedule': {
get: {
tags: ['Analytics', 'Scheduled Collection'],
summary: '获取所有调度任务',
description: '获取所有已设置的定时数据采集任务',
security: [{ bearerAuth: [] }],
responses: {
'200': {
description: '调度任务列表',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
scheduled_jobs: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
pattern: { type: 'string' },
next: { type: 'string', format: 'date-time' }
}
}
}
}
}
}
}
}
}
}
},
'/api/analytics/schedule/{job_id}': {
delete: {
tags: ['Analytics', 'Scheduled Collection'],
summary: '删除调度任务',
description: '删除一个已创建的定时数据采集任务',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'job_id',
in: 'path',
required: true,
schema: { type: 'string' },
description: '任务ID'
}
],
responses: {
'200': {
description: '成功删除任务',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: { type: 'string' },
job_id: { type: 'string' }
}
}
}
}
}
}
}
},
'/api/analytics/export/influencer/{id}/growth': {
get: {
tags: ['Analytics', 'Data Export'],
summary: '导出网红增长数据',
description: '导出网红指标增长数据为CSV格式',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '网红ID'
},
{
name: 'metric',
in: 'query',
schema: {
type: 'string',
enum: ['followers_count', 'video_count', 'views_count', 'likes_count'],
default: 'followers_count'
},
description: '要导出的指标'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['30days', '90days', '6months', '1year'],
default: '6months'
},
description: '导出的时间范围'
},
{
name: 'interval',
in: 'query',
schema: {
type: 'string',
enum: ['day', 'week', 'month'],
default: 'month'
},
description: '数据聚合的时间间隔'
}
],
responses: {
'200': {
description: 'CSV格式的网红增长数据',
content: {
'text/csv': {
schema: {
type: 'string'
}
}
}
}
}
}
},
'/api/analytics/export/project/{id}/performance': {
get: {
tags: ['Analytics', 'Data Export'],
summary: '导出项目表现数据',
description: '导出项目表现数据为CSV格式',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '项目ID'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['7days', '30days', '90days', '6months'],
default: '30days'
},
description: '导出的时间范围'
}
],
responses: {
'200': {
description: 'CSV格式的项目表现数据',
content: {
'text/csv': {
schema: {
type: 'string'
}
}
}
}
}
}
},
'/api/analytics/reports/project/{id}': {
get: {
tags: ['Analytics', 'Reports'],
summary: '生成项目报告',
description: '生成项目表现的详细报告',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
description: '项目ID'
},
{
name: 'timeframe',
in: 'query',
schema: {
type: 'string',
enum: ['7days', '30days', '90days', '6months'],
default: '30days'
},
description: '报告的时间范围'
},
{
name: 'format',
in: 'query',
schema: {
type: 'string',
enum: ['json', 'csv'],
default: 'json'
},
description: '报告格式'
}
],
responses: {
'200': {
description: '项目报告',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
report_type: { type: 'string' },
generated_at: { type: 'string', format: 'date-time' },
timeframe: { type: 'string' },
project: {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
description: { type: 'string' }
}
},
summary: {
type: 'object',
properties: {
total_influencers: { type: 'number' },
total_posts: { type: 'number' },
total_views_gain: { type: 'number' },
total_likes_gain: { type: 'number' },
total_followers_gain: { type: 'number' }
}
},
top_influencers: {
type: 'array',
items: {
type: 'object',
properties: {
influencer_id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
platform: { type: 'string' },
followers_count: { type: 'number' },
total_views_gain: { type: 'number' }
}
}
},
top_posts: {
type: 'array',
items: {
type: 'object',
properties: {
post_id: { type: 'string', format: 'uuid' },
title: { type: 'string' },
platform: { type: 'string' },
published_at: { type: 'string', format: 'date-time' },
influencer_name: { type: 'string' },
views_count: { type: 'number' },
likes_count: { type: 'number' },
engagement_rate: { type: 'number' }
}
}
}
}
}
},
'text/csv': {
schema: {
type: 'string'
}
}
}
}
}
}
},
},
components: {
schemas: {
Post: {
type: 'object',
properties: {
post_id: { type: 'string', format: 'uuid' },
influencer_id: { type: 'string', format: 'uuid' },
platform: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
},
post_url: { type: 'string', format: 'uri' },
title: { type: 'string' },
description: { type: 'string' },
published_at: { type: 'string', format: 'date-time' },
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' },
influencer: {
type: 'object',
properties: {
name: { type: 'string' },
platform: { type: 'string' },
profile_url: { type: 'string' },
followers_count: { type: 'integer' }
}
},
stats: {
type: 'object',
properties: {
views: { type: 'integer' },
likes: { type: 'integer' }
}
}
}
},
PostDetail: {
type: 'object',
properties: {
post_id: { type: 'string', format: 'uuid' },
influencer_id: { type: 'string', format: 'uuid' },
platform: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
},
post_url: { type: 'string', format: 'uri' },
title: { type: 'string' },
description: { type: 'string' },
published_at: { type: 'string', format: 'date-time' },
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' },
influencer: {
type: 'object',
properties: {
name: { type: 'string' },
platform: { type: 'string' },
profile_url: { type: 'string' },
followers_count: { type: 'integer' }
}
},
stats: {
type: 'object',
properties: {
views: { type: 'integer' },
likes: { type: 'integer' }
}
},
timeline: {
type: 'array',
items: {
type: 'object',
properties: {
date: { type: 'string', format: 'date' },
event_type: { type: 'string' },
value: { type: 'integer' }
}
}
},
comment_count: { type: 'integer' }
}
},
Comment: {
type: 'object',
properties: {
comment_id: {
type: 'string',
format: 'uuid'
},
content: {
type: 'string'
},
sentiment_score: {
type: 'number',
description: '情感分析分数,范围从 -1 到 1'
},
created_at: {
type: 'string',
format: 'date-time'
},
updated_at: {
type: 'string',
format: 'date-time'
},
post_id: {
type: 'string',
format: 'uuid'
},
user_id: {
type: 'string',
format: 'uuid'
},
user_profile: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
full_name: {
type: 'string'
},
avatar_url: {
type: 'string',
format: 'uri'
}
}
},
post: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
title: {
type: 'string'
},
platform: {
type: 'string',
enum: ['facebook', 'threads', 'instagram', 'linkedin', 'xiaohongshu', 'youtube']
},
content_type: {
type: 'string',
enum: ['post', 'reel', 'video', 'short']
},
influencer: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
},
type: {
type: 'string',
enum: ['user', 'kol', 'official']
}
}
}
}
},
status: {
type: 'string',
enum: ['pending', 'approved', 'rejected']
},
reply_status: {
type: 'string',
enum: ['none', 'draft', 'sent']
},
language: {
type: 'string',
enum: ['zh-TW', 'zh-CN', 'en']
}
}
},
Influencer: {
type: 'object',
properties: {
influencer_id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
},
platform: {
type: 'string',
enum: ['youtube', 'instagram', 'tiktok', 'twitter', 'facebook']
},
profile_url: {
type: 'string'
},
followers_count: {
type: 'integer'
},
video_count: {
type: 'integer'
},
platform_count: {
type: 'integer'
},
created_at: {
type: 'string',
format: 'date-time'
},
updated_at: {
type: 'string',
format: 'date-time'
}
}
},
InfluencerWithPosts: {
allOf: [
{
$ref: '#/components/schemas/Influencer'
},
{
type: 'object',
properties: {
posts: {
type: 'array',
items: {
type: 'object',
properties: {
post_id: {
type: 'string',
format: 'uuid'
},
title: {
type: 'string'
},
description: {
type: 'string'
},
published_at: {
type: 'string',
format: 'date-time'
}
}
}
}
}
}
]
},
Error: {
type: 'object',
properties: {
error: { type: 'string' }
}
}
},
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
description: '登录后获取的 token 可直接在此处使用。在 Authorize 按钮中输入 "Bearer your-token" 或直接输入 token不带 Bearer 前缀)。'
},
},
},
}
// 创建 Swagger UI 路由
export const createSwaggerUI = () => {
const app = new Hono()
// 设置 Swagger UI 路由
app.get('/swagger', swaggerUI({
url: '/api/swagger.json',
}))
// 提供 OpenAPI 规范的 JSON 端点
app.get('/api/swagger.json', (c) => {
return c.json(openAPISpec)
})
// 添加临时的 token 生成端点,仅用于 Swagger 测试
app.get('/api/swagger/token', async (c) => {
try {
// Swagger 测试用户的凭据
const email = 'swagger@test.com';
const password = 'swagger-test-password';
// 尝试使用 Supabase 认证
const { data, error } = await supabase.auth.signInWithPassword({
email,
password
});
if (error || !data.session) {
// 如果登录失败,可能需要先创建测试用户
console.log('尝试创建 Swagger 测试用户...');
// 尝试创建测试用户
await supabase.auth.admin.createUser({
email,
password,
email_confirm: true,
});
// 再次尝试登录
const loginResult = await supabase.auth.signInWithPassword({
email,
password
});
if (loginResult.error || !loginResult.data.session) {
return c.json({ error: '无法创建测试用户凭据', details: loginResult.error?.message }, 500);
}
return c.json({
message: '已创建 Swagger 测试用户并生成 token',
token: loginResult.data.session.access_token,
usage: '在 Authorize 对话框中输入: Bearer [token]'
});
}
return c.json({
message: '此 token 仅用于 Swagger UI 测试',
token: data.session.access_token,
usage: '在 Authorize 对话框中输入: Bearer [token]'
});
} catch (error) {
console.error('Error generating swagger token:', error);
return c.json({ error: '生成 token 失败', details: error instanceof Error ? error.message : String(error) }, 500);
}
});
return app
}