/** * 云函数:manifestManager(视频清单) * 替代:GET/POST/PUT/DELETE /api/manifest[/:videoId] * actions: list | get | create | update | delete */ async function handler(request, response) { try { await Psql.query(` CREATE TABLE IF NOT EXISTS "Manifest" ( "objectId" VARCHAR(50) PRIMARY KEY, "bizId" VARCHAR(255), "data" JSONB NOT NULL DEFAULT '{}', "userId" VARCHAR(255) DEFAULT '', "category" VARCHAR(50) DEFAULT '', "source" VARCHAR(50) DEFAULT '', "createdAt" TIMESTAMPTZ DEFAULT NOW(), "updatedAt" TIMESTAMPTZ DEFAULT NOW() ) `); await Psql.query(`CREATE UNIQUE INDEX IF NOT EXISTS idx_manifest_biz ON "Manifest" ("bizId")`); await Psql.query(`CREATE INDEX IF NOT EXISTS idx_manifest_user ON "Manifest" ("userId")`); const action = pickParam(request, 'action') || 'list'; const userId = pickParam(request, 'userId') || ''; if (action === 'list') { const limit = Math.min(parseInt(pickParam(request, 'limit') || '200'), 500); const conds = [], params = []; if (userId) { params.push(userId); conds.push(`"userId" = $${params.length}`); } const category = pickParam(request, 'category'); if (category) { params.push(category); conds.push(`"category" = $${params.length}`); } const where = conds.length ? `WHERE ${conds.join(' AND ')}` : ''; params.push(limit); const rows = await Psql.query( `SELECT * FROM "Manifest" ${where} ORDER BY "createdAt" DESC LIMIT $${params.length}`, params ); response.json({ code: 200, success: true, data: rows.map(rowToVideo) }); return; } if (action === 'get') { const bizId = pickParam(request, 'videoId', 'id', 'bizId'); if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' }); const rows = userId ? await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 LIMIT 1`, [bizId, userId]) : await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 LIMIT 1`, [bizId]); if (!rows.length) return response.json({ code: 404, success: false, error: '未找到视频' }); response.json({ code: 200, success: true, data: rowToVideo(rows[0]) }); return; } if (action === 'create') { const body = pickParam(request, 'video', 'data') || request.body || request.params || {}; const bizId = body.id || body.bizId; if (!bizId || !body.filename) { return response.json({ code: 400, success: false, error: '缺少 id 或 filename' }); } const exists = await Psql.query(`SELECT "objectId" FROM "Manifest" WHERE "bizId" = $1`, [bizId]); if (exists.length) return response.json({ code: 409, success: false, error: `视频 ${bizId} 已存在` }); const objectId = generateId(); await Psql.query( `INSERT INTO "Manifest" ("objectId","bizId","data","userId","category","source") VALUES ($1,$2,$3,$4,$5,$6)`, [objectId, bizId, JSON.stringify(body), userId, body.category || '', body.source || ''] ); response.json({ code: 200, success: true, data: { ...body, objectId } }); return; } if (action === 'update') { const bizId = pickParam(request, 'videoId', 'id', 'bizId'); if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' }); const patch = pickParam(request, 'video', 'data', 'patch') || request.body || {}; const cur = userId ? await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 LIMIT 1`, [bizId, userId]) : await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 LIMIT 1`, [bizId]); if (!cur.length) return response.json({ code: 404, success: false, error: '未找到视频' }); const merged = { ...rowToVideo(cur[0]), ...patch }; await Psql.query( userId ? `UPDATE "Manifest" SET "data"=$1, "category"=$2, "source"=$3, "updatedAt"=NOW() WHERE "bizId"=$4 AND "userId"=$5` : `UPDATE "Manifest" SET "data"=$1, "category"=$2, "source"=$3, "updatedAt"=NOW() WHERE "bizId"=$4`, userId ? [JSON.stringify(merged), merged.category || '', merged.source || '', bizId, userId] : [JSON.stringify(merged), merged.category || '', merged.source || '', bizId] ); response.json({ code: 200, success: true, data: merged }); return; } if (action === 'delete') { const bizId = pickParam(request, 'videoId', 'id', 'bizId'); if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' }); const result = userId ? await Psql.query(`DELETE FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 RETURNING "bizId"`, [bizId, userId]) : await Psql.query(`DELETE FROM "Manifest" WHERE "bizId" = $1 RETURNING "bizId"`, [bizId]); response.json({ code: 200, success: true, data: { videoId: bizId, deleted: result.length > 0 } }); return; } response.json({ code: 400, success: false, error: `未知 action: ${action}` }); } catch (error) { console.error('❌ manifestManager 失败:', error.message); response.json({ code: 500, success: false, error: error.message }); } } function rowToVideo(row) { const data = typeof row.data === 'string' ? JSON.parse(row.data) : (row.data || {}); return { ...data, objectId: row.objectId, createdAt: row.createdAt, updatedAt: row.updatedAt }; } function pickParam(request, ...names) { const sources = [request.params, request.body, request]; for (const src of sources) { if (!src || typeof src !== 'object') continue; for (const n of names) { const v = src[n]; if (v !== undefined && v !== null && v !== '') return v; } } return null; } function generateId() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let s = ''; for (let i = 0; i < 10; i++) s += chars.charAt(Math.floor(Math.random() * chars.length)); return s; }