Files
promote/backend/dist/swagger/index.js
2025-03-07 18:04:27 +08:00

1864 lines
75 KiB
JavaScript
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.
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSwaggerUI = exports.openAPISpec = void 0;
const swagger_ui_1 = require("@hono/swagger-ui");
const hono_1 = require("hono");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const config_1 = __importDefault(require("../config"));
// 创建 OpenAPI 规范
exports.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'
}
}
}
}
},
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 路由
const createSwaggerUI = () => {
const app = new hono_1.Hono();
// 设置 Swagger UI 路由
app.get('/swagger', (0, swagger_ui_1.swaggerUI)({
url: '/api/swagger.json',
}));
// 提供 OpenAPI 规范的 JSON 端点
app.get('/api/swagger.json', (c) => {
return c.json(exports.openAPISpec);
});
// 添加临时的 token 生成端点,仅用于 Swagger 测试
app.get('/api/swagger/token', async (c) => {
try {
// 创建一个临时 token与 authMiddleware 中的验证方式一致
const token = jsonwebtoken_1.default.sign({
sub: 'swagger-test-user',
email: 'swagger@test.com',
}, config_1.default.jwt.secret, {
expiresIn: '1h',
});
return c.json({
message: '此 token 仅用于 Swagger UI 测试',
token,
usage: '在 Authorize 对话框中输入: Bearer [token]'
});
}
catch (error) {
console.error('Error generating swagger token:', error);
return c.json({ error: 'Failed to generate token' }, 500);
}
});
return app;
};
exports.createSwaggerUI = createSwaggerUI;