feat: ComfyUI embedded dev setup + proxy auth with downstream keys
- COMFYUI_BASE configurable via env var (default 127.0.0.1:8188) - All /api/comfyui/ routes protected by downstream API Key or admin token - docker/Dockerfile.comfyui + docker-compose.comfyui.yml for dev - npm scripts: comfyui:install, comfyui:start, comfyui:stop - .gitignore / .env.example updated
This commit is contained in:
@@ -5,7 +5,7 @@ const DESKTOP_HEALTH_ROUTE = '/api/desktop/health';
|
||||
export function isPublicApiRoute(url: string): boolean {
|
||||
return url === DESKTOP_HEALTH_ROUTE
|
||||
|| url.startsWith('/api/oauth/callback/')
|
||||
|| url.startsWith('/api/comfyui/proxy/')
|
||||
|| url.startsWith('/api/comfyui/')
|
||||
// User SaaS routes (JWT auth, not admin token)
|
||||
|| url === '/api/users/register'
|
||||
|| url === '/api/users/login'
|
||||
|
||||
@@ -1,7 +1,34 @@
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import { fetch } from 'undici';
|
||||
import { authorizeDownstreamToken } from '../../services/downstreamApiKeyService.js';
|
||||
import { config } from '../../config.js';
|
||||
|
||||
const COMFYUI_BASE = 'http://127.0.0.1:8188';
|
||||
const COMFYUI_BASE = process.env.COMFYUI_BASE || 'http://127.0.0.1:8188';
|
||||
|
||||
/** 同时支持管理员 token 和下游 API Key 认证 */
|
||||
async function comfyuiAuth(request: { headers: { authorization?: string } }): Promise<{ ok: boolean; error?: string; code?: number }> {
|
||||
const auth = (request.headers.authorization || '').trim();
|
||||
if (!auth) {
|
||||
return { ok: false, error: 'Missing Authorization header', code: 401 };
|
||||
}
|
||||
const token = auth.startsWith('Bearer ') ? auth.slice(7).trim() : auth;
|
||||
|
||||
// 1. 管理员 token
|
||||
if (token === config.authToken) {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
// 2. 下游 API Key (sk-xxx)
|
||||
if (token.startsWith('sk-')) {
|
||||
const result = await authorizeDownstreamToken(token);
|
||||
if (result.ok) {
|
||||
return { ok: true };
|
||||
}
|
||||
return { ok: false, error: result.error, code: result.statusCode };
|
||||
}
|
||||
|
||||
return { ok: false, error: 'Invalid token', code: 403 };
|
||||
}
|
||||
|
||||
/** Rebrand ComfyUI HTML to show BoosAPI instead */
|
||||
function rebrandHtml(html: string): string {
|
||||
@@ -17,8 +44,16 @@ function rewriteProxyUrls(html: string): string {
|
||||
}
|
||||
|
||||
export async function comfyuiRoutes(app: FastifyInstance) {
|
||||
// Auth preHandler: prompt, history, queue, view 需要认证
|
||||
const requireAuth = async (request: { headers: { authorization?: string } }, reply: any) => {
|
||||
const result = await comfyuiAuth(request);
|
||||
if (!result.ok) {
|
||||
reply.code(result.code || 403).send({ error: result.error });
|
||||
}
|
||||
};
|
||||
|
||||
// Proxy: POST /api/comfyui/prompt -> ComfyUI /prompt
|
||||
app.post('/api/comfyui/prompt', async (request, reply) => {
|
||||
app.post('/api/comfyui/prompt', { preHandler: requireAuth }, async (request, reply) => {
|
||||
try {
|
||||
const body = typeof request.body === 'object' ? JSON.stringify(request.body) : '{}';
|
||||
const resp = await fetch(`${COMFYUI_BASE}/prompt`, {
|
||||
@@ -34,7 +69,7 @@ export async function comfyuiRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Proxy: GET /api/comfyui/history -> ComfyUI /history
|
||||
app.get('/api/comfyui/history', async (_request, reply) => {
|
||||
app.get('/api/comfyui/history', { preHandler: requireAuth }, async (_request, reply) => {
|
||||
try {
|
||||
const resp = await fetch(`${COMFYUI_BASE}/history`);
|
||||
const text = await resp.text();
|
||||
@@ -45,7 +80,7 @@ export async function comfyuiRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Proxy: GET /api/comfyui/history/:id -> ComfyUI /history/{id}
|
||||
app.get<{ Params: { id: string } }>('/api/comfyui/history/:id', async (request, reply) => {
|
||||
app.get<{ Params: { id: string } }>('/api/comfyui/history/:id', { preHandler: requireAuth }, async (request, reply) => {
|
||||
try {
|
||||
const resp = await fetch(`${COMFYUI_BASE}/history/${encodeURIComponent(request.params.id)}`);
|
||||
const text = await resp.text();
|
||||
@@ -56,7 +91,7 @@ export async function comfyuiRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Proxy: GET /api/comfyui/queue -> ComfyUI /queue
|
||||
app.get('/api/comfyui/queue', async (_request, reply) => {
|
||||
app.get('/api/comfyui/queue', { preHandler: requireAuth }, async (_request, reply) => {
|
||||
try {
|
||||
const resp = await fetch(`${COMFYUI_BASE}/queue`);
|
||||
const text = await resp.text();
|
||||
@@ -67,7 +102,7 @@ export async function comfyuiRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Proxy: GET /api/comfyui/view -> ComfyUI /view (image output, preserve query params)
|
||||
app.get('/api/comfyui/view', async (request, reply) => {
|
||||
app.get('/api/comfyui/view', { preHandler: requireAuth }, async (request, reply) => {
|
||||
try {
|
||||
const qs = request.url.includes('?') ? request.url.substring(request.url.indexOf('?')) : '';
|
||||
const resp = await fetch(`${COMFYUI_BASE}/view${qs}`);
|
||||
|
||||
Reference in New Issue
Block a user