01-manifestManager.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /**
  2. * 云函数:manifestManager(视频清单)
  3. * 替代:GET/POST/PUT/DELETE /api/manifest[/:videoId]
  4. * 支持 action:list | get | create | update | delete
  5. */
  6. const { requireSession, assertRequestedUserMatchesSession } = require('./_session');
  7. async function handler(request, response) {
  8. try {
  9. const action = pickParam(request, 'action') || 'list';
  10. const session = await requireSession(request, Psql);
  11. assertRequestedUserMatchesSession(request, session);
  12. const userId = session.userId;
  13. await Psql.query(`
  14. CREATE TABLE IF NOT EXISTS "Manifest" (
  15. "objectId" VARCHAR(50) PRIMARY KEY,
  16. "bizId" VARCHAR(255),
  17. "data" JSONB NOT NULL DEFAULT '{}',
  18. "userId" VARCHAR(255) DEFAULT '',
  19. "category" VARCHAR(50) DEFAULT '',
  20. "source" VARCHAR(50) DEFAULT '',
  21. "createdAt" TIMESTAMPTZ DEFAULT NOW(),
  22. "updatedAt" TIMESTAMPTZ DEFAULT NOW()
  23. )
  24. `);
  25. await Psql.query(`DROP INDEX IF EXISTS idx_manifest_biz`);
  26. await Psql.query(`CREATE UNIQUE INDEX IF NOT EXISTS idx_manifest_user_biz ON "Manifest" ("userId", "bizId")`);
  27. await Psql.query(`CREATE INDEX IF NOT EXISTS idx_manifest_user ON "Manifest" ("userId")`);
  28. if (action === 'list') {
  29. const limit = Math.min(parseInt(pickParam(request, 'limit') || '200'), 500);
  30. const conds = [], params = [];
  31. if (userId) { params.push(userId); conds.push(`"userId" = $${params.length}`); }
  32. const category = pickParam(request, 'category');
  33. if (category) { params.push(category); conds.push(`"category" = $${params.length}`); }
  34. const where = conds.length ? `WHERE ${conds.join(' AND ')}` : '';
  35. params.push(limit);
  36. const rows = await Psql.query(
  37. `SELECT * FROM "Manifest" ${where} ORDER BY "createdAt" DESC LIMIT $${params.length}`,
  38. params
  39. );
  40. response.json({ code: 200, success: true, data: rows.map(rowToVideo) });
  41. return;
  42. }
  43. if (action === 'get') {
  44. const bizId = pickParam(request, 'videoId', 'id', 'bizId');
  45. if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' });
  46. const rows = await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 LIMIT 1`, [bizId, userId]);
  47. if (!rows.length) return response.json({ code: 404, success: false, error: '未找到视频' });
  48. response.json({ code: 200, success: true, data: rowToVideo(rows[0]) });
  49. return;
  50. }
  51. if (action === 'create') {
  52. const body = pickParam(request, 'video', 'data') || request.body || request.params || {};
  53. const bizId = body.id || body.bizId;
  54. if (!bizId || !body.filename) {
  55. return response.json({ code: 400, success: false, error: '缺少 id 或 filename' });
  56. }
  57. const exists = await Psql.query(`SELECT "objectId" FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2`, [bizId, userId]);
  58. if (exists.length) return response.json({ code: 409, success: false, error: `视频 ${bizId} 已存在` });
  59. const objectId = generateId();
  60. await Psql.query(
  61. `INSERT INTO "Manifest" ("objectId","bizId","data","userId","category","source")
  62. VALUES ($1,$2,$3,$4,$5,$6)`,
  63. [objectId, bizId, JSON.stringify(body), userId, body.category || '', body.source || '']
  64. );
  65. response.json({ code: 200, success: true, data: { ...body, objectId } });
  66. return;
  67. }
  68. if (action === 'update') {
  69. const bizId = pickParam(request, 'videoId', 'id', 'bizId');
  70. if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' });
  71. const patch = pickParam(request, 'video', 'data', 'patch') || request.body || {};
  72. const cur = await Psql.query(`SELECT * FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 LIMIT 1`, [bizId, userId]);
  73. if (!cur.length) return response.json({ code: 404, success: false, error: '未找到视频' });
  74. const merged = { ...rowToVideo(cur[0]), ...patch };
  75. await Psql.query(
  76. `UPDATE "Manifest" SET "data"=$1, "category"=$2, "source"=$3, "updatedAt"=NOW() WHERE "bizId"=$4 AND "userId"=$5`,
  77. [JSON.stringify(merged), merged.category || '', merged.source || '', bizId, userId]
  78. );
  79. response.json({ code: 200, success: true, data: merged });
  80. return;
  81. }
  82. if (action === 'delete') {
  83. const bizId = pickParam(request, 'videoId', 'id', 'bizId');
  84. if (!bizId) return response.json({ code: 400, success: false, error: '缺少 videoId' });
  85. const result = await Psql.query(`DELETE FROM "Manifest" WHERE "bizId" = $1 AND "userId" = $2 RETURNING "bizId"`, [bizId, userId]);
  86. response.json({ code: 200, success: true, data: { videoId: bizId, deleted: result.length > 0 } });
  87. return;
  88. }
  89. response.json({ code: 400, success: false, error: `未知 action: ${action}` });
  90. } catch (error) {
  91. console.error('❌ manifestManager 失败:', error.message);
  92. response.json({ code: error.status || 500, success: false, error: error.message });
  93. }
  94. }
  95. function rowToVideo(row) {
  96. const data = typeof row.data === 'string' ? JSON.parse(row.data) : (row.data || {});
  97. return { ...data, objectId: row.objectId, createdAt: row.createdAt, updatedAt: row.updatedAt };
  98. }
  99. function pickParam(request, ...names) {
  100. const sources = [request.params, request.body, request];
  101. for (const src of sources) {
  102. if (!src || typeof src !== 'object') continue;
  103. for (const n of names) {
  104. const v = src[n];
  105. if (v !== undefined && v !== null && v !== '') return v;
  106. }
  107. }
  108. return null;
  109. }
  110. function generateId() {
  111. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  112. let s = '';
  113. for (let i = 0; i < 10; i++) s += chars.charAt(Math.floor(Math.random() * chars.length));
  114. return s;
  115. }