@seasonkoh/webaz 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +156 -20
  3. package/dist/layer0-foundation/L0-1-database/schema.js +5 -4
  4. package/dist/layer0-foundation/L0-2-state-machine/engine.js +228 -7
  5. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +156 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +53 -12
  7. package/dist/layer0-foundation/L0-5-manifest/manifest.js +14 -1
  8. package/dist/layer1-agent/L1-1-mcp-server/auth.js +1 -1
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +3691 -714
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +324 -0
  11. package/dist/layer1-agent/L1-2-identity/agent-passport.js +100 -0
  12. package/dist/layer2-business/L2-6-notifications/notification-engine.js +72 -5
  13. package/dist/layer2-business/L2-7-snf/snf-engine.js +287 -0
  14. package/dist/layer2-business/L2-anchor-registry/anchor-registry.js +396 -0
  15. package/dist/layer2-business/L2-notes/note-photo-storage.js +133 -0
  16. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +6 -6
  17. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +246 -0
  18. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +95 -1
  19. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +31 -2
  20. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +358 -0
  21. package/dist/pwa/public/app.js +31947 -0
  22. package/dist/pwa/public/i18n.js +5751 -0
  23. package/dist/pwa/public/icon.svg +11 -0
  24. package/dist/pwa/public/index.html +21 -0
  25. package/dist/pwa/public/manifest.json +48 -0
  26. package/dist/pwa/public/openapi.json +5946 -0
  27. package/dist/pwa/public/style.css +535 -0
  28. package/dist/pwa/public/sw.js +63 -0
  29. package/dist/pwa/public/vendor/jsQR.js +10102 -0
  30. package/dist/pwa/public/webaz-logo.png +0 -0
  31. package/dist/pwa/routes/account-deletion.js +53 -0
  32. package/dist/pwa/routes/addresses.js +105 -0
  33. package/dist/pwa/routes/admin-admins.js +151 -0
  34. package/dist/pwa/routes/admin-analytics.js +253 -0
  35. package/dist/pwa/routes/admin-atomic.js +21 -0
  36. package/dist/pwa/routes/admin-catalog.js +64 -0
  37. package/dist/pwa/routes/admin-editor-picks.js +45 -0
  38. package/dist/pwa/routes/admin-events.js +60 -0
  39. package/dist/pwa/routes/admin-health.js +66 -0
  40. package/dist/pwa/routes/admin-moderation.js +120 -0
  41. package/dist/pwa/routes/admin-ops.js +179 -0
  42. package/dist/pwa/routes/admin-protocol-params.js +79 -0
  43. package/dist/pwa/routes/admin-reports.js +154 -0
  44. package/dist/pwa/routes/admin-tokenomics.js +113 -0
  45. package/dist/pwa/routes/admin-users-lifecycle.js +237 -0
  46. package/dist/pwa/routes/admin-users-query.js +390 -0
  47. package/dist/pwa/routes/admin-verifier-flow.js +126 -0
  48. package/dist/pwa/routes/admin-verifier-whitelist.js +111 -0
  49. package/dist/pwa/routes/admin-wallet-ops.js +66 -0
  50. package/dist/pwa/routes/agent-buy.js +215 -0
  51. package/dist/pwa/routes/agent-governance.js +341 -0
  52. package/dist/pwa/routes/agent-reputation.js +34 -0
  53. package/dist/pwa/routes/ai.js +101 -0
  54. package/dist/pwa/routes/analytics.js +272 -0
  55. package/dist/pwa/routes/anchors.js +169 -0
  56. package/dist/pwa/routes/announcements.js +110 -0
  57. package/dist/pwa/routes/arbitrator.js +117 -0
  58. package/dist/pwa/routes/auction.js +436 -0
  59. package/dist/pwa/routes/auth-login.js +40 -0
  60. package/dist/pwa/routes/auth-read.js +66 -0
  61. package/dist/pwa/routes/auth-register.js +138 -0
  62. package/dist/pwa/routes/auth-sessions.js +62 -0
  63. package/dist/pwa/routes/blocklist.js +60 -0
  64. package/dist/pwa/routes/buyer-feeds.js +224 -0
  65. package/dist/pwa/routes/cart.js +155 -0
  66. package/dist/pwa/routes/charity.js +816 -0
  67. package/dist/pwa/routes/chat.js +318 -0
  68. package/dist/pwa/routes/checkin-tasks.js +122 -0
  69. package/dist/pwa/routes/checkout-helpers.js +85 -0
  70. package/dist/pwa/routes/claim-initiators.js +88 -0
  71. package/dist/pwa/routes/claim-verify.js +615 -0
  72. package/dist/pwa/routes/claim-voting.js +114 -0
  73. package/dist/pwa/routes/claim-withdrawals.js +20 -0
  74. package/dist/pwa/routes/coupons.js +165 -0
  75. package/dist/pwa/routes/dashboards.js +99 -0
  76. package/dist/pwa/routes/dispute-cases.js +267 -0
  77. package/dist/pwa/routes/disputes-read.js +358 -0
  78. package/dist/pwa/routes/disputes-write.js +475 -0
  79. package/dist/pwa/routes/evidence.js +86 -0
  80. package/dist/pwa/routes/external-anchors.js +107 -0
  81. package/dist/pwa/routes/feedback.js +270 -0
  82. package/dist/pwa/routes/flash-sales.js +130 -0
  83. package/dist/pwa/routes/follows.js +103 -0
  84. package/dist/pwa/routes/group-buys.js +208 -0
  85. package/dist/pwa/routes/growth.js +199 -0
  86. package/dist/pwa/routes/import-product.js +153 -0
  87. package/dist/pwa/routes/kyc.js +40 -0
  88. package/dist/pwa/routes/leaderboard.js +149 -0
  89. package/dist/pwa/routes/listings.js +281 -0
  90. package/dist/pwa/routes/logistics.js +35 -0
  91. package/dist/pwa/routes/manifests.js +126 -0
  92. package/dist/pwa/routes/me-data.js +101 -0
  93. package/dist/pwa/routes/notifications.js +48 -0
  94. package/dist/pwa/routes/offers.js +96 -0
  95. package/dist/pwa/routes/orders-action.js +285 -0
  96. package/dist/pwa/routes/orders-create.js +339 -0
  97. package/dist/pwa/routes/orders-read.js +180 -0
  98. package/dist/pwa/routes/p2p-products.js +178 -0
  99. package/dist/pwa/routes/payments-governance.js +311 -0
  100. package/dist/pwa/routes/peers.js +34 -0
  101. package/dist/pwa/routes/pin-receipts.js +39 -0
  102. package/dist/pwa/routes/products-aliases.js +119 -0
  103. package/dist/pwa/routes/products-claims.js +60 -0
  104. package/dist/pwa/routes/products-create.js +206 -0
  105. package/dist/pwa/routes/products-crud.js +73 -0
  106. package/dist/pwa/routes/products-links.js +129 -0
  107. package/dist/pwa/routes/products-list.js +424 -0
  108. package/dist/pwa/routes/products-meta.js +155 -0
  109. package/dist/pwa/routes/products-update.js +125 -0
  110. package/dist/pwa/routes/profile-credentials.js +105 -0
  111. package/dist/pwa/routes/profile-identity.js +174 -0
  112. package/dist/pwa/routes/profile-location.js +35 -0
  113. package/dist/pwa/routes/profile-placement.js +70 -0
  114. package/dist/pwa/routes/profile-prefs.js +93 -0
  115. package/dist/pwa/routes/promoter.js +208 -0
  116. package/dist/pwa/routes/public-utils.js +170 -0
  117. package/dist/pwa/routes/push.js +54 -0
  118. package/dist/pwa/routes/ratings.js +220 -0
  119. package/dist/pwa/routes/recover-key.js +100 -0
  120. package/dist/pwa/routes/referral.js +58 -0
  121. package/dist/pwa/routes/reputation.js +34 -0
  122. package/dist/pwa/routes/returns.js +493 -0
  123. package/dist/pwa/routes/reviews.js +81 -0
  124. package/dist/pwa/routes/rfqs.js +443 -0
  125. package/dist/pwa/routes/search.js +172 -0
  126. package/dist/pwa/routes/secondhand.js +278 -0
  127. package/dist/pwa/routes/seller-quota.js +225 -0
  128. package/dist/pwa/routes/share-redirects.js +164 -0
  129. package/dist/pwa/routes/shareables-interactions.js +212 -0
  130. package/dist/pwa/routes/shareables.js +470 -0
  131. package/dist/pwa/routes/shops.js +98 -0
  132. package/dist/pwa/routes/signaling.js +43 -0
  133. package/dist/pwa/routes/skill-market.js +173 -0
  134. package/dist/pwa/routes/skills.js +174 -0
  135. package/dist/pwa/routes/snf.js +126 -0
  136. package/dist/pwa/routes/tags.js +47 -0
  137. package/dist/pwa/routes/trial.js +333 -0
  138. package/dist/pwa/routes/trusted-kpi.js +87 -0
  139. package/dist/pwa/routes/url-claim.js +113 -0
  140. package/dist/pwa/routes/users-public.js +317 -0
  141. package/dist/pwa/routes/variants.js +156 -0
  142. package/dist/pwa/routes/verifier-user.js +107 -0
  143. package/dist/pwa/routes/verify-tasks.js +120 -0
  144. package/dist/pwa/routes/waitlist.js +65 -0
  145. package/dist/pwa/routes/wallet-read.js +218 -0
  146. package/dist/pwa/routes/wallet-write.js +273 -0
  147. package/dist/pwa/routes/webauthn.js +188 -0
  148. package/dist/pwa/routes/webhooks.js +162 -0
  149. package/dist/pwa/routes/welcome.js +226 -0
  150. package/dist/pwa/routes/wishlist-qa.js +135 -0
  151. package/dist/pwa/security/ssrf.js +110 -0
  152. package/dist/pwa/server.js +9679 -698
  153. package/package.json +11 -4
@@ -0,0 +1,100 @@
1
+ export function registerRecoverKeyRoutes(app, deps) {
2
+ const { db, internalAuditorId, issueCode, findActiveCode, CODE_TTL_MIN, MAX_CODE_ATTEMPTS } = deps;
3
+ // IP 级速率(5/min)— 防爆破列举账户
4
+ const recoverKeyHits = new Map();
5
+ app.post('/api/recover-key', (req, res) => {
6
+ const ip = req.ip || '';
7
+ if (ip) {
8
+ const now = Date.now();
9
+ const rec = recoverKeyHits.get(ip);
10
+ if (rec && now - rec.firstAt < 60_000) {
11
+ rec.count++;
12
+ if (rec.count > 5)
13
+ return void res.status(429).json({ error: '查询过于频繁,请 1 分钟后再试' });
14
+ }
15
+ else {
16
+ recoverKeyHits.set(ip, { count: 1, firstAt: now });
17
+ }
18
+ if (recoverKeyHits.size > 1000) {
19
+ for (const [k, v] of recoverKeyHits)
20
+ if (now - v.firstAt > 60_000)
21
+ recoverKeyHits.delete(k);
22
+ }
23
+ }
24
+ const { name } = req.body;
25
+ if (!name?.trim())
26
+ return void res.json({ error: '请填写注册时使用的名称' });
27
+ const rows = db.prepare("SELECT name, role, api_key, email, phone, created_at FROM users WHERE name = ? AND id NOT IN ('sys_protocol', ?)").all(name.trim(), internalAuditorId);
28
+ if (rows.length === 0)
29
+ return void res.json({ error: '未找到该名称的账号' });
30
+ const mask = (s) => s && s.length > 8 ? `${s.slice(0, 4)}…${s.slice(-4)}` : s;
31
+ const maskEmail = (e) => {
32
+ if (!e)
33
+ return null;
34
+ const [u, d] = e.split('@');
35
+ if (!d)
36
+ return mask(e);
37
+ return `${u.slice(0, 1)}***@${d}`;
38
+ };
39
+ const maskPhone = (p) => p ? `${p.slice(0, 3)}****${p.slice(-4)}` : null;
40
+ const accounts = rows.map(r => ({
41
+ name: r.name,
42
+ role: r.role,
43
+ key_hint: mask(r.api_key), // 模糊辨认,不可登录
44
+ email_hint: maskEmail(r.email || null),
45
+ phone_hint: maskPhone(r.phone || null),
46
+ has_email: !!r.email,
47
+ has_phone: !!r.phone,
48
+ created_at: r.created_at,
49
+ }));
50
+ res.json({
51
+ found: accounts.length,
52
+ accounts,
53
+ notice: '完整密钥找回需通过已绑定的邮箱/手机验证(P1 即将上线)。若你此前未绑定任何渠道,请使用本机已保存的密钥登录或联系管理员。',
54
+ });
55
+ });
56
+ // 步骤 1:发送验证码到已绑定邮箱(防泄露:找没找到都同响应)
57
+ app.post('/api/recover-key/start', (req, res) => {
58
+ const { name, email } = req.body;
59
+ if (!name?.trim() || !email?.trim())
60
+ return void res.json({ error: '请填写名称和邮箱' });
61
+ const target = email.trim().toLowerCase();
62
+ const user = db.prepare(`
63
+ SELECT id, name, email FROM users
64
+ WHERE name = ? AND email = ? AND email_verified = 1
65
+ AND id NOT IN ('sys_protocol', ?) LIMIT 1
66
+ `).get(name.trim(), target, internalAuditorId);
67
+ if (user)
68
+ issueCode(user.id, 'email', target, 'recover_key');
69
+ res.json({
70
+ success: true,
71
+ notice: '若该名称与邮箱组合存在,验证码已发送至该邮箱',
72
+ expires_in_min: CODE_TTL_MIN,
73
+ });
74
+ });
75
+ // 步骤 2:提交验证码 → 返回完整 api_key
76
+ app.post('/api/recover-key/confirm', (req, res) => {
77
+ const { name, email, code } = req.body;
78
+ if (!name?.trim() || !email?.trim() || !code?.trim())
79
+ return void res.json({ error: '请填写完整信息' });
80
+ const target = email.trim().toLowerCase();
81
+ const row = findActiveCode('email', target, 'recover_key');
82
+ if (!row)
83
+ return void res.json({ error: '验证码已过期或未发送,请重新开始' });
84
+ const user = db.prepare(`SELECT id, name, api_key FROM users WHERE id = ?`).get(row.user_id);
85
+ if (!user || user.name !== name.trim())
86
+ return void res.json({ error: '名称与验证码不匹配' });
87
+ if (String(row.code) !== code.trim()) {
88
+ const attempts = row.attempts + 1;
89
+ if (attempts >= MAX_CODE_ATTEMPTS) {
90
+ db.prepare("UPDATE verification_codes SET attempts = ?, used_at = datetime('now') WHERE id = ?")
91
+ .run(attempts, row.id);
92
+ return void res.json({ error: '错误次数过多,验证码已作废,请重新开始' });
93
+ }
94
+ db.prepare("UPDATE verification_codes SET attempts = ? WHERE id = ?").run(attempts, row.id);
95
+ return void res.json({ error: `验证码错误(剩余 ${MAX_CODE_ATTEMPTS - attempts} 次)` });
96
+ }
97
+ db.prepare("UPDATE verification_codes SET used_at = datetime('now') WHERE id = ?").run(row.id);
98
+ res.json({ success: true, api_key: user.api_key, name: user.name });
99
+ });
100
+ }
@@ -0,0 +1,58 @@
1
+ export function registerReferralRoutes(app, deps) {
2
+ const { db, auth, requireProtocolAdmin, logAdminAction, issueInviteSlot, inviteRotationLookup } = deps;
3
+ // B-1: 个人邀请 dashboard
4
+ app.get('/api/referral/me', (req, res) => {
5
+ const user = auth(req, res);
6
+ if (!user)
7
+ return;
8
+ const code = user.permanent_code || null;
9
+ // 我直接邀请的人
10
+ const directInvitees = db.prepare(`
11
+ SELECT u.id, u.handle, u.name, u.role, u.created_at,
12
+ (SELECT COUNT(*) FROM orders WHERE buyer_id = u.id AND status = 'completed') as completed_orders,
13
+ (SELECT COALESCE(SUM(total_amount), 0) FROM orders WHERE buyer_id = u.id AND status = 'completed') as gmv
14
+ FROM users u WHERE u.sponsor_id = ?
15
+ ORDER BY u.created_at DESC LIMIT 50
16
+ `).all(user.id);
17
+ // 推土机奖励 / 商品分享佣金(commission_records 按订单粒度)
18
+ const earnings = db.prepare(`
19
+ SELECT COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total FROM commission_records WHERE beneficiary_id = ?
20
+ `).get(user.id);
21
+ const todayEarnings = db.prepare(`SELECT COALESCE(SUM(amount), 0) as t FROM commission_records WHERE beneficiary_id = ? AND created_at > datetime('now', '-1 day')`).get(user.id).t;
22
+ const monthEarnings = db.prepare(`SELECT COALESCE(SUM(amount), 0) as t FROM commission_records WHERE beneficiary_id = ? AND created_at > datetime('now', '-30 days')`).get(user.id).t;
23
+ res.json({
24
+ invite_code: code,
25
+ invite_link: code ? `${req.protocol}://${req.get('host')}/?ref=${code}` : null,
26
+ direct_invitees_count: directInvitees.length,
27
+ direct_invitees: directInvitees,
28
+ earnings: {
29
+ total_records: earnings.cnt,
30
+ total_waz: earnings.total,
31
+ today_waz: todayEarnings,
32
+ month_waz: monthEarnings,
33
+ },
34
+ });
35
+ });
36
+ // 公开邀请码轮询(开关 ON 时)
37
+ app.post('/api/invite/rotate', (_req, res) => {
38
+ const enabled = db.prepare("SELECT value FROM system_state WHERE key='invite_rotation_enabled'").get()?.value === '1';
39
+ if (!enabled)
40
+ return void res.status(403).json({ error: '邀请码获取暂未开放', enabled: false });
41
+ const slot = issueInviteSlot();
42
+ const u = inviteRotationLookup(slot);
43
+ if (!u)
44
+ return void res.status(503).json({ error: `轮询用户未就绪,请联系管理员`, enabled: true });
45
+ res.json({ enabled: true, code: u.code });
46
+ });
47
+ // protocol 开关
48
+ app.post('/api/admin/invite-rotation/toggle', (req, res) => {
49
+ const admin = requireProtocolAdmin(req, res);
50
+ if (!admin)
51
+ return;
52
+ const { enabled } = req.body;
53
+ const v = enabled ? '1' : '0';
54
+ db.prepare("INSERT OR REPLACE INTO system_state (key, value) VALUES ('invite_rotation_enabled', ?)").run(v);
55
+ logAdminAction(admin.id, 'invite_rotation_toggle', 'system', 'invite_rotation_enabled', { value: v });
56
+ res.json({ success: true, enabled: !!enabled });
57
+ });
58
+ }
@@ -0,0 +1,34 @@
1
+ export function registerReputationRoutes(app, deps) {
2
+ const { db, auth, getReputation, getSellerMetrics } = deps;
3
+ app.get('/api/reputation', (req, res) => {
4
+ const user = auth(req, res);
5
+ if (!user)
6
+ return;
7
+ const rep = getReputation(db, user.id);
8
+ res.json({
9
+ level: rep.level,
10
+ total_points: rep.total_points,
11
+ transactions_done: rep.transactions_done,
12
+ disputes_won: rep.disputes_won,
13
+ disputes_lost: rep.disputes_lost,
14
+ violations: rep.violations,
15
+ recent_events: rep.recent_events,
16
+ metrics: getSellerMetrics(user.id),
17
+ });
18
+ });
19
+ app.get('/api/reputation/:userId', (req, res) => {
20
+ const rep = getReputation(db, req.params.userId);
21
+ const decayRow = db.prepare(`SELECT last_decay_at FROM reputation_scores WHERE user_id = ?`).get(req.params.userId);
22
+ res.json({
23
+ level: rep.level,
24
+ total_points: rep.total_points,
25
+ transactions_done: rep.transactions_done,
26
+ disputes_won: rep.disputes_won,
27
+ disputes_lost: rep.disputes_lost,
28
+ violations: rep.violations,
29
+ metrics: getSellerMetrics(req.params.userId),
30
+ last_decay_at: decayRow?.last_decay_at || null,
31
+ decay_rate: 0.02,
32
+ });
33
+ });
34
+ }