이미지스튜디오를 ComfyUI 백엔드로 전환, 원본 UI 임베드, LTX 비디오 고화질 옵션 추가
diffusers venv 스크립트 방식 대신 상시구동 ComfyUI(systemd, GPU1)를 통해 SDXL/FLUX/PuLID 스타일변환/LTX 비디오를 생성하도록 전환. ComfyUI 자체 웹 UI를 리버스 프록시로 스튜디오 앱에 같은 도메인으로 임베드(routes-comfyui.ts) 하고, LTX-Video에 non-distilled "dev" 체크포인트 기반 고화질 옵션을 추가. 날씨 앱에는 Windy 제트기류/태풍 마커, 게이트웨이에는 wol-gate 웨이크 로그 조회 API도 함께 반영. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ import {
|
||||
} from './guards/prompt-gates';
|
||||
import { registerArduinoRoutes } from './routes/routes-arduino';
|
||||
import { registerAndroidRoutes, attachAndroidWsProxy } from './routes/routes-android';
|
||||
import { registerComfyUIRoutes, attachComfyUIWsProxy } from './routes/routes-comfyui';
|
||||
import { registerBgTasksRoutes, loadTaskHeartbeatConfig } from './routes/routes-bg-tasks';
|
||||
import { registerKakaoRoutes } from './routes/routes-kakao';
|
||||
import { registerScheduleRoutes } from './routes/routes-schedules';
|
||||
@@ -3596,6 +3597,54 @@ app.get('/api/logs', (req, res) => {
|
||||
res.json({ entries: logRing.slice(), totalCount: logTotalCount });
|
||||
});
|
||||
|
||||
// ── wol-gate wake log (TrueNAS 컨테이너 로그를 SSH로 가져옴) ────────────────────
|
||||
app.get('/api/wol-gate-log', async (req, res) => {
|
||||
const authCfg = getAuthConfig();
|
||||
if (authCfg.enabled) {
|
||||
const sess = getSessionUser(req);
|
||||
if (!sess) return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
const { execFile } = await import('child_process');
|
||||
execFile(
|
||||
'ssh',
|
||||
['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', 'truenas', 'docker', 'logs', '-t', 'ix-wol-gate-wol-gate-1', '--tail', '40'],
|
||||
{ timeout: 8000 },
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
res.status(502).json({ error: (stderr || err.message || 'ssh failed').trim() });
|
||||
return;
|
||||
}
|
||||
const triggerRe = /triggered by (\S+) "(\S+) ([^"]*)" UA="([^"]*)"/;
|
||||
const lines = stdout.split('\n').filter(l => l.trim().length > 0).map(line => {
|
||||
// docker logs -t prefixes each line with an RFC3339 UTC timestamp, e.g.
|
||||
// "2026-08-04T04:12:34.123456789Z [wol-gate] waking - triggered by ..."
|
||||
const tsMatch = line.match(/^(\S+)\s(.*)$/);
|
||||
const rawTs = tsMatch ? tsMatch[1] : null;
|
||||
const text = tsMatch ? tsMatch[2] : line;
|
||||
const time = rawTs
|
||||
? new Date(rawTs).toLocaleString('ko-KR', { timeZone: 'Asia/Seoul', hour12: false })
|
||||
: null;
|
||||
|
||||
let type: 'info' | 'success' | 'warn' = 'info';
|
||||
let event: string | null = null;
|
||||
if (text.includes('waking - triggered by')) { type = 'success'; event = '깨움'; }
|
||||
else if (text.includes('wake suppressed')) { type = 'warn'; event = '억제(쿨다운)'; }
|
||||
|
||||
const m = text.match(triggerRe);
|
||||
if (m) {
|
||||
const [, ip, method, hostPath, ua] = m;
|
||||
const slashIdx = hostPath.indexOf('/');
|
||||
const host = slashIdx >= 0 ? hostPath.slice(0, slashIdx) : hostPath;
|
||||
const path = slashIdx >= 0 ? hostPath.slice(slashIdx) : '';
|
||||
return { type, time, event, ip, method, host, path, ua, text };
|
||||
}
|
||||
return { type, time, event, text };
|
||||
});
|
||||
res.json({ lines });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// ── Network speed tracker ──────────────────────────────────────────────────
|
||||
let _netPrev: { rx: number; tx: number; ts: number } | null = null;
|
||||
let _netSpeed = { rx_bps: 0, tx_bps: 0, iface: '' };
|
||||
@@ -4281,6 +4330,7 @@ app.use((err: any, req: express.Request, res: express.Response, _next: express.N
|
||||
|
||||
registerArduinoRoutes(app, getSessionUser);
|
||||
registerAndroidRoutes(app, getSessionUser);
|
||||
registerComfyUIRoutes(app, getSessionUser);
|
||||
registerBgTasksRoutes(app, {
|
||||
getSessionUser,
|
||||
handleChat,
|
||||
@@ -4403,6 +4453,7 @@ server.on('upgrade', (req, socket, head) => {
|
||||
}
|
||||
});
|
||||
attachAndroidWsProxy(server, getSessionUser);
|
||||
attachComfyUIWsProxy(server, getSessionUserFromUpgradeReq);
|
||||
attachVoiceRealtimeWsProxy(server, getSessionUserFromUpgradeReq);
|
||||
wss.on('error', (err: any) => {
|
||||
if (err?.code === 'EADDRINUSE') {
|
||||
|
||||
Reference in New Issue
Block a user