@seasonkoh/webaz 0.1.24 → 0.1.25

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 (187) hide show
  1. package/README.md +2 -0
  2. package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
  3. package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
  4. package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
  5. package/dist/layer0-foundation/L0-1-database/db.js +65 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
  7. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
  8. package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +165 -64
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
  11. package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
  12. package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
  13. package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
  14. package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
  15. package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +173 -0
  16. package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
  17. package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
  18. package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +10 -2
  19. package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
  20. package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
  21. package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
  22. package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
  23. package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
  24. package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
  25. package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
  26. package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
  27. package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
  28. package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
  29. package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
  30. package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
  31. package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
  32. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
  33. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
  34. package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  38. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  39. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  40. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  41. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  42. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  43. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  44. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  45. package/dist/pwa/acp-feed.js +13 -1
  46. package/dist/pwa/contract-fingerprint.js +2 -0
  47. package/dist/pwa/endpoint-actions.js +5 -1
  48. package/dist/pwa/goal-index.js +8 -8
  49. package/dist/pwa/human-presence.js +62 -0
  50. package/dist/pwa/public/app.js +575 -68
  51. package/dist/pwa/public/i18n.js +29 -20
  52. package/dist/pwa/public/index.html +1 -0
  53. package/dist/pwa/public/openapi.json +2 -2
  54. package/dist/pwa/rate-limit.js +22 -0
  55. package/dist/pwa/routes/account-deletion.js +15 -13
  56. package/dist/pwa/routes/addresses.js +10 -9
  57. package/dist/pwa/routes/admin-admins.js +13 -14
  58. package/dist/pwa/routes/admin-analytics.js +109 -69
  59. package/dist/pwa/routes/admin-catalog.js +13 -11
  60. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  61. package/dist/pwa/routes/admin-events.js +5 -3
  62. package/dist/pwa/routes/admin-health.js +2 -1
  63. package/dist/pwa/routes/admin-moderation.js +26 -29
  64. package/dist/pwa/routes/admin-ops.js +22 -21
  65. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  66. package/dist/pwa/routes/admin-reports.js +23 -21
  67. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  68. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  69. package/dist/pwa/routes/admin-users-query.js +54 -53
  70. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  71. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  72. package/dist/pwa/routes/admin-wallet-ops.js +7 -5
  73. package/dist/pwa/routes/agent-buy.js +46 -22
  74. package/dist/pwa/routes/agent-governance.js +52 -56
  75. package/dist/pwa/routes/ai.js +7 -5
  76. package/dist/pwa/routes/analytics.js +43 -41
  77. package/dist/pwa/routes/anchors.js +19 -20
  78. package/dist/pwa/routes/announcements.js +13 -13
  79. package/dist/pwa/routes/arbitrator.js +97 -31
  80. package/dist/pwa/routes/auction.js +153 -114
  81. package/dist/pwa/routes/auth-login.js +6 -4
  82. package/dist/pwa/routes/auth-read.js +11 -9
  83. package/dist/pwa/routes/auth-register.js +35 -20
  84. package/dist/pwa/routes/auth-sessions.js +12 -11
  85. package/dist/pwa/routes/blocklist.js +16 -15
  86. package/dist/pwa/routes/build-feedback.js +10 -9
  87. package/dist/pwa/routes/build-reputation.js +6 -2
  88. package/dist/pwa/routes/build-tasks.js +45 -13
  89. package/dist/pwa/routes/buyer-feeds.js +27 -25
  90. package/dist/pwa/routes/cart.js +16 -15
  91. package/dist/pwa/routes/charity.js +212 -150
  92. package/dist/pwa/routes/chat.js +42 -43
  93. package/dist/pwa/routes/checkin-tasks.js +10 -9
  94. package/dist/pwa/routes/checkout-helpers.js +12 -10
  95. package/dist/pwa/routes/claim-initiators.js +34 -14
  96. package/dist/pwa/routes/claim-verify.js +86 -53
  97. package/dist/pwa/routes/claim-voting.js +43 -18
  98. package/dist/pwa/routes/contribution-identity.js +147 -0
  99. package/dist/pwa/routes/contribution-score.js +19 -0
  100. package/dist/pwa/routes/coupons.js +19 -16
  101. package/dist/pwa/routes/dashboards.js +18 -16
  102. package/dist/pwa/routes/dispute-cases.js +25 -24
  103. package/dist/pwa/routes/disputes-read.js +45 -51
  104. package/dist/pwa/routes/disputes-write.js +124 -61
  105. package/dist/pwa/routes/evidence.js +9 -9
  106. package/dist/pwa/routes/external-anchors.js +13 -12
  107. package/dist/pwa/routes/feedback.js +29 -33
  108. package/dist/pwa/routes/flash-sales.js +18 -16
  109. package/dist/pwa/routes/follows.js +25 -24
  110. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  111. package/dist/pwa/routes/governance-onboarding.js +70 -59
  112. package/dist/pwa/routes/group-buys.js +22 -22
  113. package/dist/pwa/routes/growth.js +33 -30
  114. package/dist/pwa/routes/import-product.js +12 -10
  115. package/dist/pwa/routes/kyc.js +9 -8
  116. package/dist/pwa/routes/leaderboard.js +20 -18
  117. package/dist/pwa/routes/listings.js +23 -22
  118. package/dist/pwa/routes/logistics.js +10 -8
  119. package/dist/pwa/routes/manifests.js +27 -27
  120. package/dist/pwa/routes/me-data.js +23 -21
  121. package/dist/pwa/routes/notifications.js +7 -6
  122. package/dist/pwa/routes/offers.js +30 -12
  123. package/dist/pwa/routes/orders-action.js +33 -17
  124. package/dist/pwa/routes/orders-create.js +75 -20
  125. package/dist/pwa/routes/orders-read.js +21 -20
  126. package/dist/pwa/routes/p2p-products.js +30 -18
  127. package/dist/pwa/routes/payments-governance.js +61 -56
  128. package/dist/pwa/routes/peers.js +9 -8
  129. package/dist/pwa/routes/pin-receipts.js +13 -13
  130. package/dist/pwa/routes/products-aliases.js +12 -10
  131. package/dist/pwa/routes/products-claims.js +36 -17
  132. package/dist/pwa/routes/products-create.js +53 -38
  133. package/dist/pwa/routes/products-crud.js +17 -16
  134. package/dist/pwa/routes/products-links.js +49 -26
  135. package/dist/pwa/routes/products-list.js +6 -4
  136. package/dist/pwa/routes/products-meta.js +40 -39
  137. package/dist/pwa/routes/products-update.js +19 -5
  138. package/dist/pwa/routes/profile-credentials.js +14 -16
  139. package/dist/pwa/routes/profile-identity.js +14 -13
  140. package/dist/pwa/routes/profile-location.js +7 -6
  141. package/dist/pwa/routes/profile-placement.js +19 -17
  142. package/dist/pwa/routes/profile-prefs.js +11 -11
  143. package/dist/pwa/routes/promoter.js +55 -49
  144. package/dist/pwa/routes/public-build-tasks.js +19 -0
  145. package/dist/pwa/routes/public-utils.js +108 -46
  146. package/dist/pwa/routes/push.js +16 -15
  147. package/dist/pwa/routes/ratings.js +30 -30
  148. package/dist/pwa/routes/recover-key.js +13 -12
  149. package/dist/pwa/routes/referral.js +37 -32
  150. package/dist/pwa/routes/reputation.js +3 -2
  151. package/dist/pwa/routes/returns.js +76 -73
  152. package/dist/pwa/routes/reviews.js +41 -18
  153. package/dist/pwa/routes/rewards-apply.js +16 -15
  154. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  155. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  156. package/dist/pwa/routes/rfqs.js +163 -85
  157. package/dist/pwa/routes/search.js +16 -14
  158. package/dist/pwa/routes/secondhand.js +25 -22
  159. package/dist/pwa/routes/seller-quota.js +24 -26
  160. package/dist/pwa/routes/share-redirects.js +59 -55
  161. package/dist/pwa/routes/shareables-interactions.js +34 -35
  162. package/dist/pwa/routes/shareables.js +55 -51
  163. package/dist/pwa/routes/shop-referral.js +57 -0
  164. package/dist/pwa/routes/shops.js +20 -18
  165. package/dist/pwa/routes/signaling.js +10 -9
  166. package/dist/pwa/routes/skill-market.js +16 -16
  167. package/dist/pwa/routes/skills.js +15 -14
  168. package/dist/pwa/routes/snf.js +14 -13
  169. package/dist/pwa/routes/tags.js +10 -9
  170. package/dist/pwa/routes/task-proposals.js +45 -0
  171. package/dist/pwa/routes/trial.js +69 -51
  172. package/dist/pwa/routes/trusted-kpi.js +20 -18
  173. package/dist/pwa/routes/url-claim.js +67 -28
  174. package/dist/pwa/routes/users-public.js +62 -60
  175. package/dist/pwa/routes/variants.js +12 -13
  176. package/dist/pwa/routes/verifier-user.js +61 -21
  177. package/dist/pwa/routes/verify-tasks.js +49 -25
  178. package/dist/pwa/routes/waitlist.js +16 -15
  179. package/dist/pwa/routes/wallet-read.js +74 -36
  180. package/dist/pwa/routes/wallet-write.js +12 -9
  181. package/dist/pwa/routes/webauthn.js +25 -26
  182. package/dist/pwa/routes/webhooks.js +26 -26
  183. package/dist/pwa/routes/welcome.js +45 -50
  184. package/dist/pwa/routes/wishlist-qa.js +29 -32
  185. package/dist/pwa/server.js +237 -81
  186. package/dist/version.js +1 -1
  187. package/package.json +47 -2
@@ -1,46 +1,48 @@
1
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerShopsRoutes(app, deps) {
2
- const { db, auth } = deps;
3
- app.get('/api/shops/:identifier', (req, res) => {
3
+ // db 已走 RFC-016 异步 seam(dbOne/dbAll/dbRun),不再直接用 deps.db
4
+ const { auth } = deps;
5
+ app.get('/api/shops/:identifier', async (req, res) => {
4
6
  const id = String(req.params.identifier || '').replace(/^@/, '');
5
7
  // 先按 handle 查,找不到再按 id
6
- let seller = db.prepare(`
8
+ let seller = await dbOne(`
7
9
  SELECT id, name, handle, role, bio, shop_banner_url, shop_intro, created_at, region
8
10
  FROM users WHERE handle = ? AND role = 'seller'
9
- `).get(id);
11
+ `, [id]);
10
12
  if (!seller) {
11
- seller = db.prepare(`
13
+ seller = await dbOne(`
12
14
  SELECT id, name, handle, role, bio, shop_banner_url, shop_intro, created_at, region
13
15
  FROM users WHERE id = ? AND role = 'seller'
14
- `).get(id);
16
+ `, [id]);
15
17
  }
16
18
  if (!seller)
17
19
  return void res.status(404).json({ error: '店铺不存在' });
18
20
  const sellerId = String(seller.id);
19
- const products = db.prepare(`
21
+ const products = await dbAll(`
20
22
  SELECT p.id, p.title, p.price, p.stock, p.category, p.images, p.has_variants, p.commission_rate,
21
23
  (SELECT COUNT(1) FROM orders o WHERE o.product_id = p.id AND o.status = 'completed') as sales_count
22
24
  FROM products p
23
25
  WHERE p.seller_id = ? AND p.status = 'active'
24
26
  ORDER BY sales_count DESC, p.created_at DESC
25
27
  LIMIT 50
26
- `).all(sellerId);
27
- const ratingsAgg = db.prepare(`
28
+ `, [sellerId]);
29
+ const ratingsAgg = (await dbOne(`
28
30
  SELECT COUNT(*) as cnt, COALESCE(AVG(stars), 0) as avg_stars FROM order_ratings WHERE seller_id = ?
29
- `).get(sellerId);
30
- const followers = db.prepare(`SELECT COUNT(*) as n FROM follows WHERE followee_id = ?`).get(sellerId).n;
31
- const completedOrders = db.prepare(`SELECT COUNT(*) as n FROM orders WHERE seller_id = ? AND status = 'completed'`).get(sellerId).n;
31
+ `, [sellerId]));
32
+ const followers = (await dbOne(`SELECT COUNT(*) as n FROM follows WHERE followee_id = ?`, [sellerId])).n;
33
+ const completedOrders = (await dbOne(`SELECT COUNT(*) as n FROM orders WHERE seller_id = ? AND status = 'completed'`, [sellerId])).n;
32
34
  // 当前 viewer 是否关注
33
35
  let is_following = false;
34
36
  try {
35
37
  const token = (req.headers.authorization || '').replace(/^Bearer\s+/i, '');
36
38
  if (token) {
37
- const u = db.prepare('SELECT id FROM users WHERE api_key = ?').get(token);
39
+ const u = await dbOne('SELECT id FROM users WHERE api_key = ?', [token]);
38
40
  if (u)
39
- is_following = !!db.prepare('SELECT 1 FROM follows WHERE follower_id = ? AND followee_id = ?').get(u.id, sellerId);
41
+ is_following = !!(await dbOne('SELECT 1 FROM follows WHERE follower_id = ? AND followee_id = ?', [u.id, sellerId]));
40
42
  }
41
43
  }
42
44
  catch { }
43
- const recentRatings = db.prepare(`
45
+ const recentRatings = await dbAll(`
44
46
  SELECT r.stars, r.comment, r.reply, r.created_at,
45
47
  u.handle as buyer_handle, p.title as product_title
46
48
  FROM order_ratings r
@@ -48,7 +50,7 @@ export function registerShopsRoutes(app, deps) {
48
50
  JOIN products p ON p.id = r.product_id
49
51
  WHERE r.seller_id = ?
50
52
  ORDER BY r.created_at DESC LIMIT 5
51
- `).all(sellerId);
53
+ `, [sellerId]);
52
54
  res.json({
53
55
  seller,
54
56
  stats: {
@@ -64,7 +66,7 @@ export function registerShopsRoutes(app, deps) {
64
66
  });
65
67
  });
66
68
  // 卖家更新自己店铺装饰
67
- app.patch('/api/shops/me', (req, res) => {
69
+ app.patch('/api/shops/me', async (req, res) => {
68
70
  const user = auth(req, res);
69
71
  if (!user)
70
72
  return;
@@ -92,7 +94,7 @@ export function registerShopsRoutes(app, deps) {
92
94
  return void res.status(400).json({ error: '无可更新字段' });
93
95
  sets.push(`updated_at = datetime('now')`);
94
96
  args.push(user.id);
95
- db.prepare(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`).run(...args);
97
+ await dbRun(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`, args);
96
98
  res.json({ success: true });
97
99
  });
98
100
  }
@@ -1,6 +1,8 @@
1
+ import { dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerSignalingRoutes(app, deps) {
2
- const { db, auth, generateId } = deps;
3
- app.post('/api/signaling/send', (req, res) => {
3
+ // db 已走 RFC-016 异步 seam(dbAll/dbRun),不再直接用 deps.db
4
+ const { auth, generateId } = deps;
5
+ app.post('/api/signaling/send', async (req, res) => {
4
6
  const me = auth(req, res);
5
7
  if (!me)
6
8
  return;
@@ -11,23 +13,22 @@ export function registerSignalingRoutes(app, deps) {
11
13
  return void res.json({ error: 'type 不合法' });
12
14
  if (JSON.stringify(data).length > 50000)
13
15
  return void res.json({ error: 'data 过大' });
14
- db.prepare(`INSERT INTO signaling_queue (id, to_peer_id, from_peer_id, signal_type, signal_data, created_at)
15
- VALUES (?,?,?,?,?,datetime('now'))`)
16
- .run(generateId('sig'), to, me.id, type, JSON.stringify(data));
16
+ await dbRun(`INSERT INTO signaling_queue (id, to_peer_id, from_peer_id, signal_type, signal_data, created_at)
17
+ VALUES (?,?,?,?,?,datetime('now'))`, [generateId('sig'), to, me.id, type, JSON.stringify(data)]);
17
18
  res.json({ ok: true });
18
19
  });
19
- app.get('/api/signaling/poll', (req, res) => {
20
+ app.get('/api/signaling/poll', async (req, res) => {
20
21
  const me = auth(req, res);
21
22
  if (!me)
22
23
  return;
23
- const rows = db.prepare(`
24
+ const rows = await dbAll(`
24
25
  SELECT id, from_peer_id, signal_type, signal_data, created_at FROM signaling_queue
25
26
  WHERE to_peer_id = ? AND delivered_at IS NULL AND created_at > datetime('now', '-2 minutes')
26
27
  ORDER BY created_at ASC LIMIT 50
27
- `).all(me.id);
28
+ `, [me.id]);
28
29
  if (rows.length > 0) {
29
30
  const ids = rows.map(r => r.id);
30
- db.prepare(`UPDATE signaling_queue SET delivered_at = datetime('now') WHERE id IN (${ids.map(() => '?').join(',')})`).run(...ids);
31
+ await dbRun(`UPDATE signaling_queue SET delivered_at = datetime('now') WHERE id IN (${ids.map(() => '?').join(',')})`, ids);
31
32
  }
32
33
  res.json({ signals: rows.map(r => {
33
34
  let signal_data = null;
@@ -1,18 +1,18 @@
1
+ import { dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  import { publishListing, updateListing, delistListing, resubmitListing, listMarket, getMarketDetail, getMyListings, purchaseListing, readContent, getMyLibrary, listPendingAudit, auditListing, } from '../../layer4-economics/L4-4-skill-market/skill-listing-engine.js';
2
3
  export function registerSkillMarketRoutes(app, deps) {
3
4
  const { db, generateId, auth, getUser, requireContentAdmin, getProtocolParam } = deps;
4
5
  const feeRate = () => getProtocolParam('skill_fee_rate', 0.05);
5
- const notify = (userId, title, body) => {
6
+ const notify = async (userId, title, body) => {
6
7
  try {
7
- db.prepare('INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)')
8
- .run(generateId('ntf'), userId, title, body, null);
8
+ await dbRun('INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)', [generateId('ntf'), userId, title, body, null]);
9
9
  }
10
10
  catch { /* notifications best-effort */ }
11
11
  };
12
12
  // ─── 公开列表 ───────────────────────────────────────────────
13
- app.get('/api/skill-market', (req, res) => {
13
+ app.get('/api/skill-market', async (req, res) => {
14
14
  const user = getUser(req);
15
- res.json(listMarket(db, {
15
+ res.json(await listMarket(db, {
16
16
  category: req.query.category,
17
17
  skillKind: req.query.kind,
18
18
  billingMode: req.query.billing,
@@ -22,23 +22,23 @@ export function registerSkillMarketRoutes(app, deps) {
22
22
  }));
23
23
  });
24
24
  // ─── 我发布的(须在 /:id 之前注册)───────────────────────────
25
- app.get('/api/skill-market/mine', (req, res) => {
25
+ app.get('/api/skill-market/mine', async (req, res) => {
26
26
  const user = auth(req, res);
27
27
  if (!user)
28
28
  return;
29
- res.json(getMyListings(db, user.id));
29
+ res.json(await getMyListings(db, user.id));
30
30
  });
31
31
  // ─── 我的技能库 ─────────────────────────────────────────────
32
- app.get('/api/skill-market/library', (req, res) => {
32
+ app.get('/api/skill-market/library', async (req, res) => {
33
33
  const user = auth(req, res);
34
34
  if (!user)
35
35
  return;
36
- res.json(getMyLibrary(db, user.id));
36
+ res.json(await getMyLibrary(db, user.id));
37
37
  });
38
38
  // ─── 公开详情 ───────────────────────────────────────────────
39
- app.get('/api/skill-market/:id', (req, res) => {
39
+ app.get('/api/skill-market/:id', async (req, res) => {
40
40
  const user = getUser(req);
41
- const detail = getMarketDetail(db, req.params.id, user?.id);
41
+ const detail = await getMarketDetail(db, req.params.id, user?.id);
42
42
  if (!detail)
43
43
  return void res.status(404).json({ error: '技能不存在或未上架' });
44
44
  res.json(detail);
@@ -141,14 +141,14 @@ export function registerSkillMarketRoutes(app, deps) {
141
141
  }
142
142
  });
143
143
  // ─── Admin:待审列表 ────────────────────────────────────────
144
- app.get('/api/admin/skill-market/pending', (req, res) => {
144
+ app.get('/api/admin/skill-market/pending', async (req, res) => {
145
145
  const admin = requireContentAdmin(req, res);
146
146
  if (!admin)
147
147
  return;
148
- res.json({ items: listPendingAudit(db) });
148
+ res.json({ items: await listPendingAudit(db) });
149
149
  });
150
150
  // ─── Admin:审核 ────────────────────────────────────────────
151
- app.post('/api/admin/skill-market/:id/audit', (req, res) => {
151
+ app.post('/api/admin/skill-market/:id/audit', async (req, res) => {
152
152
  const admin = requireContentAdmin(req, res);
153
153
  if (!admin)
154
154
  return;
@@ -159,10 +159,10 @@ export function registerSkillMarketRoutes(app, deps) {
159
159
  try {
160
160
  const listing = auditListing(db, req.params.id, admin.id, decision, note);
161
161
  if (decision === 'approve') {
162
- notify(listing.author_id, '✓ 技能审核通过', `「${listing.title}」已上架技能市场`);
162
+ await notify(listing.author_id, '✓ 技能审核通过', `「${listing.title}」已上架技能市场`);
163
163
  }
164
164
  else {
165
- notify(listing.author_id, '✗ 技能审核未通过', `「${listing.title}」被退回:${note ?? ''}`);
165
+ await notify(listing.author_id, '✗ 技能审核未通过', `「${listing.title}」被退回:${note ?? ''}`);
166
166
  }
167
167
  res.json({ success: true, listing });
168
168
  }
@@ -1,4 +1,5 @@
1
1
  import { publishSkill, listSkills, getMySkills, subscribeSkill, unsubscribeSkill, getMySubscriptions, } from '../../layer4-economics/L4-4-skill-market/skill-engine.js';
2
+ import { dbOne, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
2
3
  const SKILL_TRUST_REQ = {
3
4
  price_negotiation: 'quality',
4
5
  quality_guarantee: 'quality',
@@ -10,9 +11,9 @@ const LEVEL_ORDER = ['new', 'trusted', 'quality', 'legend'];
10
11
  export function registerSkillsRoutes(app, deps) {
11
12
  const { db, auth, getUser } = deps;
12
13
  // 公开浏览
13
- app.get('/api/skills', (req, res) => {
14
+ app.get('/api/skills', async (req, res) => {
14
15
  const user = getUser(req);
15
- const skills = listSkills(db, {
16
+ const skills = await listSkills(db, {
16
17
  skillType: req.query.type,
17
18
  query: req.query.q,
18
19
  subscriberId: user?.id,
@@ -20,20 +21,20 @@ export function registerSkillsRoutes(app, deps) {
20
21
  });
21
22
  res.json(skills);
22
23
  });
23
- app.get('/api/skills/mine', (req, res) => {
24
+ app.get('/api/skills/mine', async (req, res) => {
24
25
  const user = auth(req, res);
25
26
  if (!user)
26
27
  return;
27
- res.json(getMySkills(db, user.id));
28
+ res.json(await getMySkills(db, user.id));
28
29
  });
29
- app.get('/api/skills/subscriptions', (req, res) => {
30
+ app.get('/api/skills/subscriptions', async (req, res) => {
30
31
  const user = auth(req, res);
31
32
  if (!user)
32
33
  return;
33
- res.json(getMySubscriptions(db, user.id));
34
+ res.json(await getMySubscriptions(db, user.id));
34
35
  });
35
36
  // 发布
36
- app.post('/api/skills', (req, res) => {
37
+ app.post('/api/skills', async (req, res) => {
37
38
  const user = auth(req, res);
38
39
  if (!user)
39
40
  return;
@@ -45,7 +46,7 @@ export function registerSkillsRoutes(app, deps) {
45
46
  // trust level 门槛
46
47
  const required = SKILL_TRUST_REQ[skill_type] || 'new';
47
48
  if (required !== 'new') {
48
- const rep = db.prepare(`SELECT level FROM agent_reputation WHERE api_key = ?`).get(user.api_key);
49
+ const rep = await dbOne(`SELECT level FROM agent_reputation WHERE api_key = ?`, [user.api_key]);
49
50
  const myLevel = rep?.level || 'new';
50
51
  if (LEVEL_ORDER.indexOf(myLevel) < LEVEL_ORDER.indexOf(required)) {
51
52
  return void res.status(403).json({
@@ -103,11 +104,11 @@ export function registerSkillsRoutes(app, deps) {
103
104
  }
104
105
  });
105
106
  // 卖家:修改 Skill
106
- app.patch('/api/skills/:id', (req, res) => {
107
+ app.patch('/api/skills/:id', async (req, res) => {
107
108
  const user = auth(req, res);
108
109
  if (!user)
109
110
  return;
110
- const skill = db.prepare('SELECT seller_id FROM skills WHERE id = ?').get(req.params.id);
111
+ const skill = await dbOne('SELECT seller_id FROM skills WHERE id = ?', [req.params.id]);
111
112
  if (!skill)
112
113
  return void res.status(404).json({ error: 'Skill 不存在' });
113
114
  if (skill.seller_id !== user.id)
@@ -134,20 +135,20 @@ export function registerSkillsRoutes(app, deps) {
134
135
  if (!updates.length)
135
136
  return void res.json({ error: '无任何修改' });
136
137
  args.push(req.params.id);
137
- db.prepare(`UPDATE skills SET ${updates.join(', ')} WHERE id = ?`).run(...args);
138
+ await dbRun(`UPDATE skills SET ${updates.join(', ')} WHERE id = ?`, args);
138
139
  res.json({ success: true });
139
140
  });
140
141
  // 卖家:停用
141
- app.post('/api/skills/:id/disable', (req, res) => {
142
+ app.post('/api/skills/:id/disable', async (req, res) => {
142
143
  const user = auth(req, res);
143
144
  if (!user)
144
145
  return;
145
- const skill = db.prepare('SELECT seller_id FROM skills WHERE id = ?').get(req.params.id);
146
+ const skill = await dbOne('SELECT seller_id FROM skills WHERE id = ?', [req.params.id]);
146
147
  if (!skill)
147
148
  return void res.status(404).json({ error: 'Skill 不存在' });
148
149
  if (skill.seller_id !== user.id)
149
150
  return void res.status(403).json({ error: '仅 Skill owner 可停用' });
150
- db.prepare("UPDATE skills SET active = 0 WHERE id = ?").run(req.params.id);
151
+ await dbRun("UPDATE skills SET active = 0 WHERE id = ?", [req.params.id]);
151
152
  res.json({ success: true });
152
153
  });
153
154
  // 订阅
@@ -1,3 +1,4 @@
1
+ import { dbOne } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  import { snfSend, snfPullInbox, snfListInbox, snfAck, snfPendingCount, snfVerify, snfDesignate, snfGetDesignation, snfNack, snfListDeadLetter, snfRevive, } from '../../layer2-business/L2-7-snf/snf-engine.js';
2
3
  export function registerSnfRoutes(app, deps) {
3
4
  const { db, auth } = deps;
@@ -26,13 +27,13 @@ export function registerSnfRoutes(app, deps) {
26
27
  }
27
28
  });
28
29
  // 只读列表(不消费)
29
- app.get('/api/snf/inbox', (req, res) => {
30
+ app.get('/api/snf/inbox', async (req, res) => {
30
31
  const user = auth(req, res);
31
32
  if (!user)
32
33
  return;
33
34
  const limit = Math.min(200, Math.max(1, Number(req.query.limit) || 80));
34
35
  const sinceDays = Math.min(180, Math.max(1, Number(req.query.since_days) || 30));
35
- const msgs = snfListInbox(db, user.id, limit, sinceDays);
36
+ const msgs = await snfListInbox(db, user.id, limit, sinceDays);
36
37
  res.json({ items: msgs, count: msgs.length });
37
38
  });
38
39
  // 协议级 pull — 一次性消费,agent / 内部组件用
@@ -56,12 +57,12 @@ export function registerSnfRoutes(app, deps) {
56
57
  const r = snfNack(db, user.id, ids, error);
57
58
  res.json({ ok: true, reopened: r.reopened, dead_lettered: r.deadLettered });
58
59
  });
59
- app.get('/api/snf/dead-letter', (req, res) => {
60
+ app.get('/api/snf/dead-letter', async (req, res) => {
60
61
  const user = auth(req, res);
61
62
  if (!user)
62
63
  return;
63
64
  const limit = Math.min(200, Math.max(1, Number(req.query.limit) || 50));
64
- const items = snfListDeadLetter(db, user.id, limit);
65
+ const items = await snfListDeadLetter(db, user.id, limit);
65
66
  res.json({ items, count: items.length });
66
67
  });
67
68
  app.post('/api/snf/revive/:id', (req, res) => {
@@ -76,7 +77,7 @@ export function registerSnfRoutes(app, deps) {
76
77
  res.json({ ok: true });
77
78
  });
78
79
  // 显式 ack(无 ids → ack 全部未读)
79
- app.post('/api/snf/ack', (req, res) => {
80
+ app.post('/api/snf/ack', async (req, res) => {
80
81
  const user = auth(req, res);
81
82
  if (!user)
82
83
  return;
@@ -85,29 +86,29 @@ export function registerSnfRoutes(app, deps) {
85
86
  const r = snfAck(db, user.id, ids);
86
87
  return void res.json({ ok: true, acked: r.acked });
87
88
  }
88
- const all = snfListInbox(db, user.id, 200, 365).filter(m => !m.delivered_at).map(m => m.id);
89
+ const all = (await snfListInbox(db, user.id, 200, 365)).filter(m => !m.delivered_at).map(m => m.id);
89
90
  const r = snfAck(db, user.id, all);
90
91
  res.json({ ok: true, acked: r.acked });
91
92
  });
92
- app.get('/api/snf/pending', (req, res) => {
93
+ app.get('/api/snf/pending', async (req, res) => {
93
94
  const user = auth(req, res);
94
95
  if (!user)
95
96
  return;
96
- res.json({ pending: snfPendingCount(db, user.id) });
97
+ res.json({ pending: await snfPendingCount(db, user.id) });
97
98
  });
98
99
  // 验签(仅当事人或 arbitrator/admin)
99
- app.get('/api/snf/:id/verify', (req, res) => {
100
+ app.get('/api/snf/:id/verify', async (req, res) => {
100
101
  const user = auth(req, res);
101
102
  if (!user)
102
103
  return;
103
- const r = db.prepare(`SELECT sender_id, recipient_id FROM snf_messages WHERE id = ?`).get(req.params.id);
104
+ const r = await dbOne(`SELECT sender_id, recipient_id FROM snf_messages WHERE id = ?`, [req.params.id]);
104
105
  if (!r)
105
106
  return void res.status(404).json({ error: '消息不存在' });
106
107
  const uid = user.id;
107
108
  if (uid !== r.sender_id && uid !== r.recipient_id && user.role !== 'arbitrator' && user.role !== 'admin') {
108
109
  return void res.status(403).json({ error: '无权验证' });
109
110
  }
110
- res.json(snfVerify(db, req.params.id));
111
+ res.json(await snfVerify(db, req.params.id));
111
112
  });
112
113
  app.post('/api/snf/designate', (req, res) => {
113
114
  const user = auth(req, res);
@@ -117,10 +118,10 @@ export function registerSnfRoutes(app, deps) {
117
118
  snfDesignate(db, user.id, peers);
118
119
  res.json({ ok: true, peers });
119
120
  });
120
- app.get('/api/snf/designate', (req, res) => {
121
+ app.get('/api/snf/designate', async (req, res) => {
121
122
  const user = auth(req, res);
122
123
  if (!user)
123
124
  return;
124
- res.json({ peers: snfGetDesignation(db, user.id), server_implicit: true });
125
+ res.json({ peers: await snfGetDesignation(db, user.id), server_implicit: true });
125
126
  });
126
127
  }
@@ -1,11 +1,12 @@
1
- export function registerTagsRoutes(app, deps) {
2
- const { db } = deps;
3
- app.get('/api/tags/:tag/notes', (req, res) => {
1
+ import { dbOne, dbAll } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
2
+ export function registerTagsRoutes(app, _deps) {
3
+ // db 已全量走 RFC-016 异步 seam(dbOne/dbAll),不再用 deps.db
4
+ app.get('/api/tags/:tag/notes', async (req, res) => {
4
5
  const tag = String(req.params.tag || '').trim().toLowerCase();
5
6
  if (!tag || tag.length > 30)
6
7
  return void res.status(400).json({ error: 'tag invalid' });
7
8
  const limit = Math.min(50, Math.max(1, Number(req.query.limit) || 30));
8
- const rows = db.prepare(`
9
+ const rows = await dbAll(`
9
10
  SELECT s.id, s.owner_id, s.owner_code, s.type, s.title, s.native_text,
10
11
  s.related_product_id, s.related_anchor, s.photo_hashes,
11
12
  s.click_count, s.like_count, s.created_at,
@@ -18,7 +19,7 @@ export function registerTagsRoutes(app, deps) {
18
19
  LEFT JOIN users u ON u.id = s.owner_id
19
20
  WHERE t.tag = ? AND s.status = 'active'
20
21
  ORDER BY s.created_at DESC LIMIT ?
21
- `).all(tag, limit);
22
+ `, [tag, limit]);
22
23
  for (const r of rows) {
23
24
  if (typeof r.photo_hashes === 'string') {
24
25
  try {
@@ -29,19 +30,19 @@ export function registerTagsRoutes(app, deps) {
29
30
  }
30
31
  }
31
32
  }
32
- const stat = db.prepare(`SELECT COUNT(*) as count FROM shareable_tags WHERE tag = ?`).get(tag);
33
+ const stat = (await dbOne(`SELECT COUNT(*) as count FROM shareable_tags WHERE tag = ?`, [tag]));
33
34
  res.json({ tag, count: stat.count, items: rows });
34
35
  });
35
36
  // 热门标签:24h + 总数综合排序
36
- app.get('/api/tags/trending', (_req, res) => {
37
- const rows = db.prepare(`
37
+ app.get('/api/tags/trending', async (_req, res) => {
38
+ const rows = await dbAll(`
38
39
  SELECT tag, COUNT(*) as total,
39
40
  SUM(CASE WHEN created_at > datetime('now', '-1 day') THEN 1 ELSE 0 END) as recent_24h
40
41
  FROM shareable_tags
41
42
  GROUP BY tag
42
43
  HAVING total >= 1
43
44
  ORDER BY recent_24h DESC, total DESC LIMIT 20
44
- `).all();
45
+ `);
45
46
  res.json({ items: rows });
46
47
  });
47
48
  }
@@ -0,0 +1,45 @@
1
+ import { validateProposalInput, insertTaskProposal, listTaskProposals, reviewTaskProposal } from '../../layer2-business/L2-9-contribution/task-proposal-store.js';
2
+ import { withUncommittedValueBoundary } from '../../layer2-business/L2-9-contribution/contribution-display-envelope.js';
3
+ import { getCanonicalContributionTarget } from '../../layer2-business/L2-9-contribution/canonical-contribution-target.js';
4
+ const PROPOSAL_NOTICE = 'A task proposal is a SUGGESTION in the maintainer review inbox. It is NOT a contribution fact, formal participation, or any reward / payout / score, and it never appears on the public task board until a maintainer reviews and (manually) converts it. source_ref is a reference only; the canonical contribution target is fixed by trusted config.';
5
+ function withProposalEnvelope(payload) {
6
+ return withUncommittedValueBoundary({ ...payload, proposal_notice: PROPOSAL_NOTICE, canonical_contribution_target: getCanonicalContributionTarget() });
7
+ }
8
+ export function registerTaskProposalsRoutes(app, deps) {
9
+ const { db, errorRes, requireSupportAdmin, rateLimitOk } = deps;
10
+ // public submit — anonymous; proposer_account_id is never taken from the body (anti-spoof).
11
+ app.post('/api/public/task-proposals', (req, res) => {
12
+ // anti-flood: per-IP rate limit (counts every attempt, before validation) then a recent-window dedup.
13
+ const ip = (typeof req.headers['x-forwarded-for'] === 'string' ? req.headers['x-forwarded-for'].split(',')[0].trim() : '') || req.ip || 'unknown';
14
+ if (!rateLimitOk(`proposal:${ip}`))
15
+ return void errorRes(res, 429, 'RATE_LIMITED', '提交过于频繁,请稍后再试');
16
+ const v = validateProposalInput(req.body);
17
+ if (!v.ok)
18
+ return void errorRes(res, 400, v.code, v.message);
19
+ const result = insertTaskProposal(db, v.input, null);
20
+ if ('duplicate' in result)
21
+ return void errorRes(res, 409, 'DUPLICATE_PROPOSAL', '相同建议已在收件箱中,请勿重复提交', { existing_id: result.existing_id });
22
+ res.json(withProposalEnvelope({ proposal: { id: result.id, status: result.status } }));
23
+ });
24
+ // admin list (maintainer only)
25
+ app.get('/api/admin/task-proposals', (req, res) => {
26
+ const admin = requireSupportAdmin(req, res);
27
+ if (!admin)
28
+ return;
29
+ const status = typeof req.query.status === 'string' ? req.query.status : undefined;
30
+ res.json(withProposalEnvelope({ proposals: listTaskProposals(db, { status }) }));
31
+ });
32
+ // admin review (maintainer only): needs_info | rejected | converted — no build_task is created here.
33
+ app.post('/api/admin/task-proposals/:id/review', (req, res) => {
34
+ const admin = requireSupportAdmin(req, res);
35
+ if (!admin)
36
+ return;
37
+ const { status, note, converted_ref } = req.body ?? {};
38
+ const result = reviewTaskProposal(db, String(req.params.id), admin.id, String(status), note, converted_ref);
39
+ if ('error' in result) {
40
+ const code = result.code === 'NOT_FOUND' ? 404 : result.code === 'ALREADY_TERMINAL' ? 409 : 400;
41
+ return void errorRes(res, code, result.code, result.error);
42
+ }
43
+ res.json(withProposalEnvelope({ proposal: result }));
44
+ });
45
+ }