| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- /**
- * 云函数:douyinManager
- * 代理抖音/TikHub 相关接口,避免前端暴露 TikHub 地址和 token。
- *
- * 部署后把函数 objectId 填入 src/app/services/cloud-functions.ts 的 douyin。
- */
- const TIKHUB_BASE_URL = 'https://api.tikhub.io';
- const TIKHUB_TOKEN = 'gqsZHfMWgAiMwV+ITbmZy0qALADWBZVS7QnV7kKJe9CwzgWgJG+7bwK+GQ==';
- const ROUTES = {
- searchVideos: { method: 'POST', path: '/api/v1/douyin/search/fetch_general_search_v2' },
- challengeSearch: { method: 'POST', path: '/api/v1/douyin/search/fetch_challenge_search_v2' },
- videoDetail: { method: 'GET', path: '/api/v1/douyin/app/v3/fetch_one_video_v3' },
- userProfileWeb: { method: 'GET', path: '/api/v1/douyin/web/handler_user_profile_v2' },
- userProfileApp: { method: 'GET', path: '/api/v1/douyin/app/v3/handler_user_profile' },
- userPosts: { method: 'GET', path: '/api/v1/douyin/web/fetch_user_post_videos' },
- comments: { method: 'GET', path: '/api/v1/douyin/app/v3/fetch_video_comments' },
- replies: { method: 'POST', path: '/api/v1/douyin/comment/reply/list' },
- };
- async function handler(request, response) {
- try {
- const action = pickParam(request, 'action') || 'call';
- if (action !== 'call') {
- return response.json({ code: 400, success: false, error: `未知 action: ${action}` });
- }
- const routeName = String(pickParam(request, 'route') || '').trim();
- const route = ROUTES[routeName];
- if (!route) {
- return response.json({ code: 400, success: false, error: '不支持的抖音接口' });
- }
- const params = pickParam(request, 'params') || {};
- const body = pickParam(request, 'payload', 'data') || {};
- const data = route.method === 'GET'
- ? await requestJson('GET', route.path, params)
- : await requestJson('POST', route.path, body);
- response.json(data);
- } catch (error) {
- console.error('douyinManager failed:', error && error.message ? error.message : error);
- response.json({ code: 500, success: false, error: error && error.message ? error.message : '抖音服务调用失败' });
- }
- }
- async function requestJson(method, path, payload) {
- const url = new URL(`${TIKHUB_BASE_URL}${path}`);
- const init = {
- method,
- headers: {
- 'Content-Type': 'application/json',
- 'Accept': 'application/json',
- 'Authorization': `Bearer ${TIKHUB_TOKEN}`,
- },
- };
- if (method === 'GET') {
- for (const [key, value] of Object.entries(payload || {})) {
- if (value !== undefined && value !== null && value !== '') {
- url.searchParams.set(key, String(value));
- }
- }
- } else {
- init.body = JSON.stringify(stripEmpty(payload || {}));
- }
- const r = await fetch(url.toString(), init);
- const text = await r.text();
- let data = null;
- try { data = text ? JSON.parse(text) : null; } catch {}
- if (!r.ok) {
- return { code: r.status, success: false, error: readError(data, text) || `HTTP ${r.status}` };
- }
- return data || { code: 500, success: false, error: '服务返回异常' };
- }
- 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 stripEmpty(value) {
- if (!value || typeof value !== 'object') return value;
- const out = Array.isArray(value) ? [] : {};
- for (const [key, val] of Object.entries(value)) {
- if (val === undefined || val === null || val === '') continue;
- if (val && typeof val === 'object' && !Array.isArray(val)) {
- const nested = stripEmpty(val);
- if (Object.keys(nested).length) out[key] = nested;
- } else {
- out[key] = val;
- }
- }
- return out;
- }
- function readError(data, fallback) {
- return data?.error?.message || data?.error || data?.message || data?.msg || fallback || '';
- }
|