init: AIGC-Hub/AVCC 方案文档 + TCS-IPTV 内容可信锁定系统 MVP

- 方案文档: AVCC 体系建设、IPTV TCS 需求(0-req)/PRD(1-prd)/任务(2-task)/二三四期任务
- tcs-iptv: Go 后端(哈希SDK/MA码生成/可信数据空间mock/业务编排/HTTP API+HMAC鉴权)
- web-console: React+AntD 监管大屏(角色工作台/全流程演示/监管片库)
- 一剧一码+集级哈希, 集级下架/恢复, 全栈测试通过
This commit is contained in:
selfrelease
2026-06-14 16:50:31 +08:00
commit a329d4906b
103 changed files with 20052 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
// API 客户端:Web Crypto 实现 HMAC-SHA256 签名,与后端 httpx.Sign 一致。
//
// ⚠️ 安全提示:四角色密钥放前端仅用于 MVP/演示。
// 生产必须改为「控制台 BFF + 会话令牌」,密钥不下发浏览器。
const enc = new TextEncoder()
async function hmacSha256Base64(secret, message) {
const key = await crypto.subtle.importKey(
'raw', enc.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
)
const sig = await crypto.subtle.sign('HMAC', key, enc.encode(message))
let bin = ''
for (const b of new Uint8Array(sig)) bin += String.fromCharCode(b)
return btoa(bin)
}
// 四角色演示密钥(与 api-svc 预置一致)
export const ROLE_KEYS = {
regulator: { apiKey: 'ak-regulator', apiSecret: 'sk-regulator', label: '监管主体' },
reviewer: { apiKey: 'ak-reviewer', apiSecret: 'sk-reviewer', label: '审核/媒资' },
cp: { apiKey: 'ak-cp', apiSecret: 'sk-cp', label: '内容提供商' },
operator: { apiKey: 'ak-operator', apiSecret: 'sk-operator', label: '运营商' },
}
async function request(role, method, path, body) {
const cred = ROLE_KEYS[role]
const signPath = '/api/v1' + path.split('?')[0]
const sig = await hmacSha256Base64(cred.apiSecret, method + '\n' + signPath)
const headers = { Authorization: `TCS ${cred.apiKey}:${sig}` }
const opts = { method, headers }
if (body !== undefined) {
headers['Content-Type'] = 'application/json'
opts.body = JSON.stringify(body)
}
const resp = await fetch('/api/v1' + path, opts)
const data = await resp.json().catch(() => ({}))
return { status: resp.status, ok: resp.status >= 200 && resp.status < 300, data }
}
// 通用:按角色发起请求(供多角色工作台使用)
export function call(role, method, path, body) {
return request(role, method, path, body)
}
export const api = {
// 全流程各步骤(标注发起角色)
register: (body) => request('cp', 'POST', '/content/register', body),
issue: (body) => request('regulator', 'POST', '/content/issue', body),
csps: (body) => request('reviewer', 'POST', '/content/csps-result', body),
ingest: (body) => request('reviewer', 'POST', '/content/ingest', body),
publish: (body) => request('reviewer', 'POST', '/content/publish', body),
inject: (body) => request('operator', 'POST', '/content/inject', body),
// 监管功能
verify: (maCode, fileHash) => request('regulator', 'POST', '/content/verify', { ma_code: maCode, file_sha256: fileHash }),
mappings: (maCode) => request('regulator', 'GET', '/content/mappings?ma_code=' + encodeURIComponent(maCode)),
takedown: (maCode, reason) => request('regulator', 'POST', '/content/takedown', { ma_code: maCode, reason }),
takedownEpisode: (maCode, episode, reason) => request('regulator', 'POST', '/content/takedown-episode', { ma_code: maCode, episode, reason }),
restore: (maCode) => request('regulator', 'POST', '/content/restore', { ma_code: maCode }),
restoreEpisode: (maCode, episode) => request('regulator', 'POST', '/content/restore-episode', { ma_code: maCode, episode }),
// 集级粒度(一剧一码 + 集级哈希)
episodes: (maCode) => request('regulator', 'GET', '/content/episodes?ma_code=' + encodeURIComponent(maCode)),
verifyEpisode: (maCode, episode, fileHash) => request('regulator', 'POST', '/content/verify-episode', { ma_code: maCode, episode, file_sha256: fileHash }),
// 工作队列(多角色工作台)
reviews: (role, status) => request(role, 'GET', '/content/reviews?status=' + status),
list: (role, status) => request(role, 'GET', '/content/list?status=' + status),
}