cherry-studio/src/main/apiServer/middleware/openapi.ts
beyondkmp 4f701d3e45
fix(apiServer): use 127.0.0.1 instead of localhost for better compatibility (#11673)
* fix(apiServer): use 127.0.0.1 instead of localhost for better compatibility

- Change default host from localhost to 127.0.0.1 in config and settings
- Add buildApiServerUrl helper to properly construct API server URLs
- Update OpenAPI documentation server URL
- Update test files to use 127.0.0.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(migration): migrate existing localhost config to 127.0.0.1

- Add migration 180 to automatically update localhost to 127.0.0.1
- Handle both plain host and hosts with http/https protocol
- Increment store version to 180

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(apiServer): simplify buildApiServerUrl implementation

- Remove complex URL parsing and protocol handling
- Use simple string concatenation for URL building
- Assume http protocol since API server is local

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: remove buildApiServerUrl helper and simplify migration

- Remove buildApiServerUrl helper function
- Use 127.0.0.1 directly in URL construction
- Simplify migration 180 to unconditionally set host to 127.0.0.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(apiServer): fix critical bugs and improve code structure

🔴 Critical Fixes:
- Fix config.ts to use stored host value instead of ignoring it
- Fix hardcoded 127.0.0.1 URLs to use apiServerConfig.host

🟡 Improvements:
- Extract API_SERVER_DEFAULTS to shared constants in packages/shared/config/constant.ts
- Apply consistent fallback pattern using API_SERVER_DEFAULTS.HOST and API_SERVER_DEFAULTS.PORT
- Update all imports to use shared constants across main and renderer processes

Files changed:
- packages/shared/config/constant.ts: Add API_SERVER_DEFAULTS constants
- src/main/apiServer/config.ts: Use stored host with fallback
- src/main/apiServer/middleware/openapi.ts: Use constants
- src/renderer/src/pages/settings/ToolSettings/ApiServerSettings/ApiServerSettings.tsx: Use config host and constants
- src/renderer/src/store/settings.ts: Use constants in initial state
- src/renderer/src/store/migrate.ts: Use constants in migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* update

* fix(apiServer): use relative URL in OpenAPI spec for better compatibility

- Change server URL from hardcoded defaults to relative path '/'
- This ensures Swagger UI "Try it out" works correctly regardless of configured host/port
- Remove unused API_SERVER_DEFAULTS import

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 10:57:42 +08:00

208 lines
5.5 KiB
TypeScript

import type { Express } from 'express'
import swaggerJSDoc from 'swagger-jsdoc'
import swaggerUi from 'swagger-ui-express'
import { loggerService } from '../../services/LoggerService'
const logger = loggerService.withContext('OpenAPIMiddleware')
const swaggerOptions: swaggerJSDoc.Options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Cherry Studio API',
version: '1.0.0',
description: 'OpenAI-compatible API for Cherry Studio with additional Cherry-specific endpoints',
contact: {
name: 'Cherry Studio',
url: 'https://github.com/CherryHQ/cherry-studio'
}
},
servers: [
{
url: '/',
description: 'Current server'
}
],
components: {
securitySchemes: {
BearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
description: 'Use the API key from Cherry Studio settings'
}
},
schemas: {
Error: {
type: 'object',
properties: {
error: {
type: 'object',
properties: {
message: { type: 'string' },
type: { type: 'string' },
code: { type: 'string' }
}
}
}
},
ChatMessage: {
type: 'object',
properties: {
role: {
type: 'string',
enum: ['system', 'user', 'assistant', 'tool']
},
content: {
oneOf: [
{ type: 'string' },
{
type: 'array',
items: {
type: 'object',
properties: {
type: { type: 'string' },
text: { type: 'string' },
image_url: {
type: 'object',
properties: {
url: { type: 'string' }
}
}
}
}
}
]
},
name: { type: 'string' },
tool_calls: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
type: { type: 'string' },
function: {
type: 'object',
properties: {
name: { type: 'string' },
arguments: { type: 'string' }
}
}
}
}
}
}
},
ChatCompletionRequest: {
type: 'object',
required: ['model', 'messages'],
properties: {
model: {
type: 'string',
description: 'The model to use for completion, in format provider:model-id'
},
messages: {
type: 'array',
items: { $ref: '#/components/schemas/ChatMessage' }
},
temperature: {
type: 'number',
minimum: 0,
maximum: 2,
default: 1
},
max_tokens: {
type: 'integer',
minimum: 1
},
stream: {
type: 'boolean',
default: false
},
tools: {
type: 'array',
items: {
type: 'object',
properties: {
type: { type: 'string' },
function: {
type: 'object',
properties: {
name: { type: 'string' },
description: { type: 'string' },
parameters: { type: 'object' }
}
}
}
}
}
}
},
Model: {
type: 'object',
properties: {
id: { type: 'string' },
object: { type: 'string', enum: ['model'] },
created: { type: 'integer' },
owned_by: { type: 'string' }
}
},
MCPServer: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
command: { type: 'string' },
args: {
type: 'array',
items: { type: 'string' }
},
env: { type: 'object' },
disabled: { type: 'boolean' }
}
}
}
},
security: [
{
BearerAuth: []
}
]
},
apis: ['./src/main/apiServer/routes/**/*.ts', './src/main/apiServer/app.ts']
}
export function setupOpenAPIDocumentation(app: Express) {
try {
const specs = swaggerJSDoc(swaggerOptions)
// Serve OpenAPI JSON
app.get('/api-docs.json', (_req, res) => {
res.setHeader('Content-Type', 'application/json')
res.send(specs)
})
// Serve Swagger UI
app.use(
'/api-docs',
swaggerUi.serve,
swaggerUi.setup(specs, {
customCss: `
.swagger-ui .topbar { display: none; }
.swagger-ui .info .title { color: #1890ff; }
`,
customSiteTitle: 'Cherry Studio API Documentation'
})
)
logger.info('OpenAPI documentation ready', {
docsPath: '/api-docs',
specPath: '/api-docs.json'
})
} catch (error) {
logger.error('Failed to setup OpenAPI documentation', { error })
}
}