87 lines
5.9 KiB
JavaScript
87 lines
5.9 KiB
JavaScript
'use strict';
|
|
const TaobaoAuth = require('./taobao-auth');
|
|
module.exports = function ({ accounts, wrinkleJobs, readBody, sendJson, handleAnalyze }) {
|
|
const taobaoSessions = require('./taobao-session')({ session: accounts.taobaoSession });
|
|
return async function (req, res, pathname) {
|
|
res.setHeader('Cache-Control', 'no-store');
|
|
// The cloud gateway requires its own envelope. H5 responses stay unchanged.
|
|
const reply = (status, data) => sendJson(res, 200, { success: true, data: { ...data, http_status: status } });
|
|
if (req.method !== 'POST') return reply(405, { ok: false, message: '请求方法不支持' });
|
|
if (!accounts.sameOrigin(req)) return reply(403, { ok: false, message: '请求来源不匹配' });
|
|
try {
|
|
const raw = (await readBody(req, pathname === '/api/taobao/photo' ? 70 * 1024 : 4096)).toString('utf8');
|
|
const body = JSON.parse(raw);
|
|
if (!body || typeof body !== 'object' || Array.isArray(body)) return reply(400, { ok: false, message: '请求数据异常' });
|
|
if (pathname === '/api/taobao/challenge') {
|
|
const challenge = taobaoSessions.challenge(req.socket.remoteAddress);
|
|
return reply(challenge ? 200 : 429, challenge ? { ok: true, challenge } : { ok: false, message: '操作频繁,请稍后重试' });
|
|
}
|
|
if (pathname === '/api/taobao/session') {
|
|
if (body.consent !== true) return reply(400, { ok: false, message: '请先阅读并同意隐私协议' });
|
|
const identity = TaobaoAuth.verify(req.url, raw);
|
|
if (!identity || !taobaoSessions.consume(body.challenge)) return reply(401, { ok: false, message: '淘宝身份校验失败,请重新授权登录' });
|
|
return reply(200, { ok: true, ...accounts.loginTaobao(identity.providerKey) });
|
|
}
|
|
if (pathname === '/api/taobao/guest-session') {
|
|
if (body.consent !== true) return reply(400, { ok: false, message: '请先阅读并同意隐私协议' });
|
|
if (!TaobaoAuth.verifyGateway(req.url, raw) || !taobaoSessions.consume(body.challenge)) {
|
|
return reply(401, { ok: false, message: '游客会话校验失败,请重新开始' });
|
|
}
|
|
const guest = accounts.loginTaobaoGuest();
|
|
return reply(guest ? 200 : 429, guest ? { ok: true, ...guest } : { ok: false, message: '操作频繁,请稍后再试' });
|
|
}
|
|
const owner = accounts.taobaoSession(req);
|
|
if (!owner) return reply(401, { ok: false, message: '检测会话已失效,请重新开始' });
|
|
if (pathname === '/api/taobao/me') {
|
|
// A cached Bearer must not restore the previous Taobao account after
|
|
// the host app switches accounts. Guests require a signed app gateway
|
|
// but intentionally have no dependency on userInfo/open_id authorization.
|
|
const identity = owner.kind === 'taobao_guest' ? null : TaobaoAuth.verify(req.url, raw);
|
|
const matches = owner.kind === 'taobao_guest' ? !!TaobaoAuth.verifyGateway(req.url, raw)
|
|
: !!identity && identity.providerKey === owner.provider_key;
|
|
return reply(matches ? 200 : 401, matches ? { ok: true, user: { id: owner.id, kind: owner.kind } }
|
|
: { ok: false, message: '淘宝账号状态已变化,请重新进入' });
|
|
}
|
|
if (pathname === '/api/taobao/photo') {
|
|
const { status, ...data } = taobaoSessions.upload(req, body);
|
|
return reply(status, { ok: status === 200, ...data });
|
|
}
|
|
if (pathname === '/api/taobao/skin-analyze') {
|
|
// Start once and poll: cloud gateway requests have a maximum 10s timeout.
|
|
const attempt = taobaoSessions.beginAnalysis(req, body, photos => new Promise(resolve => {
|
|
const response = { setHeader() {}, writeHead() {}, end(text, cb) { resolve(JSON.parse(text)); if (cb) cb(); } };
|
|
handleAnalyze(req, response, true, { ...body, ...photos }, owner).catch(() => resolve({ ok: false, message: '检测服务暂不可用,请重试' }));
|
|
}));
|
|
return reply(attempt.status, { ok: attempt.status === 202,
|
|
...(attempt.job ? { job: attempt.job } : { message: attempt.message }) });
|
|
}
|
|
if (pathname === '/api/taobao/analysis') {
|
|
const result = taobaoSessions.result(req, body.job);
|
|
return reply(result ? 200 : 404, result ? { ok: true, ...result } : { ok: false, message: owner.kind === 'taobao_guest' ? '本次检测任务已失效,请重新开始' : '检测任务已失效,请查看历史报告或重新检测' });
|
|
}
|
|
if (pathname === '/api/taobao/reports') {
|
|
if (owner.kind === 'taobao_guest') return reply(body.id ? 404 : 200, body.id ? { ok: false, message: '游客模式不保存历史报告' } : { ok: true, total: 0, items: [] });
|
|
if (!body.id) return reply(200, { ok: true, total: accounts.reports(owner.id).length, items: accounts.reports(owner.id) });
|
|
const report = accounts.ownedReport(owner.id, body.id);
|
|
return reply(report ? 200 : 404, report ? { ok: true, report } : { ok: false, message: '报告不存在' });
|
|
}
|
|
if (pathname === '/api/taobao/logout') {
|
|
taobaoSessions.revoke(req);
|
|
accounts.taobaoLogout(req);
|
|
return reply(200, { ok: true });
|
|
}
|
|
if (pathname === '/api/taobao/wrinkle-views') {
|
|
const job = taobaoSessions.start(req, async () => {
|
|
const views = await wrinkleJobs.run(owner, body.token);
|
|
return views ? { ok: true, wrinkle_views: views } : { ok: false, message: '标注任务已失效' };
|
|
});
|
|
return reply(job ? 202 : 429, job ? { ok: true, job } : { ok: false, message: '服务繁忙,请稍后再试' });
|
|
}
|
|
return reply(404, { ok: false, message: '接口不存在' });
|
|
} catch (e) {
|
|
const status = e.message === 'TOO_LARGE' ? 413 : e instanceof SyntaxError ? 400 : 500;
|
|
return reply(status, { ok: false, message: status === 413 ? '请求数据过大' : status === 400 ? '请求数据异常' : '服务暂不可用,请稍后再试' });
|
|
}
|
|
};
|
|
};
|