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:
boos
2026-05-17 13:04:46 +08:00
parent 2fe8d8528a
commit ad0bb86e31
11 changed files with 308 additions and 7 deletions
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Install ComfyUI locally for development
* Clones repo + sets up Python venv + installs deps
*/
import { execSync } from 'child_process';
import { existsSync, mkdirSync, copyFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = resolve(__dirname, '../..');
const COMFYUI_DIR = resolve(PROJECT_ROOT, '.comfyui');
async function run() {
console.log('[comfyui] Installing ComfyUI for local development...');
console.log(`[comfyui] Target: ${COMFYUI_DIR}`);
if (existsSync(COMFYUI_DIR)) {
console.log('[comfyui] ComfyUI already installed. Pulling latest...');
execSync('git pull', { cwd: COMFYUI_DIR, stdio: 'inherit' });
} else {
console.log('[comfyui] Cloning ComfyUI...');
execSync(
'git clone https://github.com/comfyanonymous/ComfyUI.git .comfyui',
{ cwd: PROJECT_ROOT, stdio: 'inherit' },
);
}
// Check Python
try {
execSync('python3 --version', { stdio: 'pipe' });
} catch {
console.error('[comfyui] ❌ python3 not found. Please install Python 3.10+.');
process.exit(1);
}
// Setup venv
const venvDir = resolve(COMFYUI_DIR, 'venv');
if (!existsSync(venvDir)) {
console.log('[comfyui] Creating Python venv...');
execSync('python3 -m venv venv', { cwd: COMFYUI_DIR, stdio: 'inherit' });
}
// Install deps (CPU-only — all generation goes through BoosAPI)
const pip = process.platform === 'win32'
? resolve(venvDir, 'Scripts', 'pip')
: resolve(venvDir, 'bin', 'pip');
const python = process.platform === 'win32'
? resolve(venvDir, 'Scripts', 'python')
: resolve(venvDir, 'bin', 'python');
console.log('[comfyui] Upgrading pip...');
execSync(`"${pip}" install --upgrade pip`, { cwd: COMFYUI_DIR, stdio: 'inherit' });
console.log('[comfyui] Installing PyTorch (CPU)...');
execSync(
`"${pip}" install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu`,
{ cwd: COMFYUI_DIR, stdio: 'inherit' },
);
console.log('[comfyui] Installing ComfyUI dependencies...');
execSync(
`grep -v kornia_rs requirements.txt | "${pip}" install -r /dev/stdin`,
{ cwd: COMFYUI_DIR, stdio: 'inherit' },
);
// Install BoosAPI custom nodes
const customNodesDir = resolve(COMFYUI_DIR, 'custom_nodes');
mkdirSync(customNodesDir, { recursive: true });
const customNodeSrc = resolve(PROJECT_ROOT, 'boosapi_comfy_nodes.py');
const customNodeDst = resolve(customNodesDir, 'boosapi_comfy_nodes.py');
if (existsSync(customNodeSrc)) {
copyFileSync(customNodeSrc, customNodeDst);
console.log('[comfyui] ✅ BoosAPI custom nodes installed');
}
console.log('');
console.log('[comfyui] ✅ ComfyUI installed!');
console.log(`[comfyui] Directory: ${COMFYUI_DIR}`);
console.log('[comfyui] Start with: npm run comfyui:start');
console.log('[comfyui] Or set COMFYUI_BASE env to point elsewhere');
}
run().catch((err) => {
console.error('[comfyui] ❌ Install failed:', err.message);
process.exit(1);
});
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env node
/**
* Start local ComfyUI (managed subprocess)
*/
import { spawn } from 'child_process';
import { existsSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = resolve(__dirname, '../..');
const COMFYUI_DIR = resolve(PROJECT_ROOT, '.comfyui');
function getPythonPath() {
const venvBin = resolve(COMFYUI_DIR, 'venv', 'bin');
const venvScripts = resolve(COMFYUI_DIR, 'venv', 'Scripts');
if (existsSync(resolve(venvBin, 'python3'))) return resolve(venvBin, 'python3');
if (existsSync(resolve(venvBin, 'python'))) return resolve(venvBin, 'python');
if (existsSync(resolve(venvScripts, 'python.exe'))) return resolve(venvScripts, 'python.exe');
return 'python3';
}
async function run() {
if (!existsSync(COMFYUI_DIR)) {
console.error('[comfyui] ❌ ComfyUI not installed. Run: npm run comfyui:install');
process.exit(1);
}
const python = getPythonPath();
const port = process.env.COMFYUI_PORT || '8188';
// Check if already running
try {
const resp = await fetch(`http://127.0.0.1:${port}/`);
if (resp.ok) {
console.log(`[comfyui] ✅ ComfyUI already running on port ${port}`);
process.exit(0);
}
} catch {
// Not running, start it
}
console.log(`[comfyui] Starting ComfyUI on port ${port}...`);
console.log(`[comfyui] Python: ${python}`);
console.log(`[comfyui] Dir: ${COMFYUI_DIR}`);
const proc = spawn(python, ['main.py', '--listen', '127.0.0.1', '--port', port, '--cpu'], {
cwd: COMFYUI_DIR,
stdio: 'inherit',
env: {
...process.env,
BOOSAPI_BASE: process.env.BOOSAPI_BASE || 'http://127.0.0.1:4000/v1',
BOOSAPI_KEY: process.env.BOOSAPI_KEY || '',
},
});
proc.on('exit', (code) => {
if (code !== 0 && code !== null) {
console.error(`[comfyui] ❌ Exited with code ${code}`);
}
});
// Graceful shutdown
process.on('SIGINT', () => { proc.kill(); process.exit(); });
process.on('SIGTERM', () => { proc.kill(); process.exit(); });
}
run().catch((err) => {
console.error('[comfyui] ❌ Start failed:', err.message);
process.exit(1);
});
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env node
/**
* Stop local ComfyUI by PID
*/
import { readFileSync, existsSync, unlinkSync } from 'fs';
import { execSync } from 'child_process';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const COMFYUI_DIR = resolve(__dirname, '../..', '.comfyui');
const PID_FILE = resolve(COMFYUI_DIR, 'comfyui.pid');
// Try PID file first
if (existsSync(PID_FILE)) {
try {
const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim(), 10);
process.kill(pid, 'SIGTERM');
console.log(`[comfyui] Stopped PID ${pid}`);
unlinkSync(PID_FILE);
process.exit(0);
} catch (e) {
if (e.code === 'ESRCH') {
console.log('[comfyui] Process already stopped');
unlinkSync(PID_FILE);
process.exit(0);
}
// Fall through to pkill
}
}
// Fallback: kill by process name
try {
execSync('pkill -f "python.*main.py.*--listen.*8188"', { stdio: 'pipe' });
console.log('[comfyui] Stopped ComfyUI process');
} catch {
console.log('[comfyui] No running ComfyUI process found');
}