import { Router } from 'express'; import fs from 'fs/promises'; import path from 'path'; import multer from 'multer'; const ACCOUNTS_DIR = path.resolve(__dirname, '..', '..', '..', 'accounts'); export const accountsRouter = Router(); async function readAccountJson(id: string): Promise | null> { const p = path.join(ACCOUNTS_DIR, id, 'account.json'); try { const data = await fs.readFile(p, 'utf-8'); return JSON.parse(data); } catch { return null; } } async function writeAccountJson(id: string, data: Record): Promise { const dir = path.join(ACCOUNTS_DIR, id); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(path.join(dir, 'account.json'), JSON.stringify(data, null, 2), 'utf-8'); } // List accountsRouter.get('/', async (_req, res) => { const dirs = await fs.readdir(ACCOUNTS_DIR, { withFileTypes: true }); const accountDirs = dirs.filter((d) => d.isDirectory() && !d.name.startsWith('_') && !d.name.startsWith('.')); const accounts: Record[] = []; for (const d of accountDirs) { const data = await readAccountJson(d.name); if (data) accounts.push(data); } res.json(accounts); }); // Get accountsRouter.get('/:id', async (req, res) => { const data = await readAccountJson(req.params.id); if (!data) return res.status(404).json({ error: 'Account not found' }); res.json(data); }); // Create accountsRouter.post('/', async (req, res) => { const { id, ...rest } = req.body; if (!id) return res.status(400).json({ error: 'id is required' }); const dir = path.join(ACCOUNTS_DIR, id); try { await fs.access(dir); return res.status(409).json({ error: 'Account already exists' }); } catch { // Directory doesn't exist, proceed } const data = { id, name: rest.name || id, description: rest.description || '', defaultFormat: rest.defaultFormat || '9:16', imageModel: rest.imageModel || 'gemini', videoModel: rest.videoModel || 'veo3-fast', batchSize: rest.batchSize || 30, ttsVoice: rest.ttsVoice || '', ttsInstruction: rest.ttsInstruction || '', storyboardPrompt: rest.storyboardPrompt || 'prompts/分镜.md', imageStylePrompt: rest.imageStylePrompt || 'prompts/图片提示词.md', videoStylePrompt: rest.videoStylePrompt || 'prompts/视频提示词.md', references: rest.references || [], capcut: rest.capcut || {}, }; await writeAccountJson(id, data); res.status(201).json(data); }); // Update accountsRouter.put('/:id', async (req, res) => { const existing = await readAccountJson(req.params.id); if (!existing) return res.status(404).json({ error: 'Account not found' }); const merged = { ...existing, ...req.body, id: req.params.id }; await writeAccountJson(req.params.id, merged); res.json(merged); }); // Delete accountsRouter.delete('/:id', async (req, res) => { const dir = path.join(ACCOUNTS_DIR, req.params.id); try { await fs.access(dir); } catch { return res.status(404).json({ error: 'Account not found' }); } await fs.rm(dir, { recursive: true }); res.status(204).send(); }); // Reference image upload const upload = multer({ dest: path.join(ACCOUNTS_DIR, '_uploads') }); accountsRouter.post('/:id/references/upload', upload.single('file'), async (req, res) => { const id = req.params.id as string; const file = req.file as Express.Multer.File | undefined; if (!file) return res.status(400).json({ error: 'No file uploaded' }); const account = await readAccountJson(id); if (!account) return res.status(404).json({ error: 'Account not found' }); const refsDir = path.join(ACCOUNTS_DIR, id, 'references'); await fs.mkdir(refsDir, { recursive: true }); const destPath = path.join(refsDir, file.originalname); await fs.rename(file.path, destPath); const refEntry = { file: `references/${file.originalname}` }; const refs = (account.references as unknown[]) || []; refs.push(refEntry); account.references = refs; await writeAccountJson(id, account); res.json({ ok: true, reference: refEntry }); }); // Reference image delete accountsRouter.delete('/:id/references/:index', async (req, res) => { const id = req.params.id as string; const idx = parseInt(req.params.index as string); const account = await readAccountJson(id); if (!account) return res.status(404).json({ error: 'Account not found' }); const refs = (account.references as unknown[]) || []; if (idx < 0 || idx >= refs.length) return res.status(400).json({ error: 'Invalid index' }); const removed = refs.splice(idx, 1)[0] as { file?: string }; if (removed?.file) { const filePath = path.join(ACCOUNTS_DIR, id, removed.file); try { await fs.unlink(filePath); } catch { /* ignore */ } } account.references = refs; await writeAccountJson(id, account); res.json({ ok: true }); });