@seasonkoh/webaz 0.1.24 → 0.1.26

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 (195) hide show
  1. package/README.md +5 -1
  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 +288 -208
  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 +182 -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 +11 -3
  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-discovery.js +55 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  38. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  39. package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
  40. package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
  41. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  42. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  43. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  44. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  45. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  46. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  47. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  48. package/dist/pwa/acp-feed.js +13 -1
  49. package/dist/pwa/admin-bearer-auth.js +21 -0
  50. package/dist/pwa/contract-fingerprint.js +2 -0
  51. package/dist/pwa/email-delivery.js +127 -0
  52. package/dist/pwa/endpoint-actions.js +5 -1
  53. package/dist/pwa/goal-index.js +8 -8
  54. package/dist/pwa/human-presence.js +62 -0
  55. package/dist/pwa/public/app.js +1485 -283
  56. package/dist/pwa/public/i18n.js +297 -59
  57. package/dist/pwa/public/index.html +1 -0
  58. package/dist/pwa/public/openapi.json +5 -5
  59. package/dist/pwa/public/whitepaper/en/index.html +153 -0
  60. package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
  61. package/dist/pwa/rate-limit.js +22 -0
  62. package/dist/pwa/routes/account-deletion.js +15 -13
  63. package/dist/pwa/routes/addresses.js +10 -9
  64. package/dist/pwa/routes/admin-admins.js +13 -14
  65. package/dist/pwa/routes/admin-analytics.js +109 -69
  66. package/dist/pwa/routes/admin-atomic.js +10 -4
  67. package/dist/pwa/routes/admin-catalog.js +13 -11
  68. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  69. package/dist/pwa/routes/admin-events.js +5 -3
  70. package/dist/pwa/routes/admin-health.js +2 -1
  71. package/dist/pwa/routes/admin-moderation.js +50 -29
  72. package/dist/pwa/routes/admin-ops.js +35 -23
  73. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  74. package/dist/pwa/routes/admin-reports.js +23 -21
  75. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  76. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  77. package/dist/pwa/routes/admin-users-query.js +65 -53
  78. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  79. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  80. package/dist/pwa/routes/admin-wallet-ops.js +32 -7
  81. package/dist/pwa/routes/agent-buy.js +46 -22
  82. package/dist/pwa/routes/agent-governance.js +52 -56
  83. package/dist/pwa/routes/ai.js +7 -5
  84. package/dist/pwa/routes/analytics.js +43 -41
  85. package/dist/pwa/routes/anchors.js +19 -20
  86. package/dist/pwa/routes/announcements.js +13 -13
  87. package/dist/pwa/routes/arbitrator.js +97 -31
  88. package/dist/pwa/routes/auction.js +157 -116
  89. package/dist/pwa/routes/auth-login.js +6 -4
  90. package/dist/pwa/routes/auth-read.js +21 -10
  91. package/dist/pwa/routes/auth-register.js +111 -26
  92. package/dist/pwa/routes/auth-sessions.js +12 -11
  93. package/dist/pwa/routes/blocklist.js +16 -15
  94. package/dist/pwa/routes/build-feedback.js +10 -9
  95. package/dist/pwa/routes/build-reputation.js +6 -2
  96. package/dist/pwa/routes/build-tasks.js +45 -13
  97. package/dist/pwa/routes/buyer-feeds.js +27 -25
  98. package/dist/pwa/routes/cart.js +16 -15
  99. package/dist/pwa/routes/charity.js +212 -150
  100. package/dist/pwa/routes/chat.js +42 -43
  101. package/dist/pwa/routes/checkin-tasks.js +10 -9
  102. package/dist/pwa/routes/checkout-helpers.js +12 -10
  103. package/dist/pwa/routes/claim-initiators.js +34 -14
  104. package/dist/pwa/routes/claim-verify.js +86 -53
  105. package/dist/pwa/routes/claim-voting.js +43 -18
  106. package/dist/pwa/routes/contribution-identity.js +164 -0
  107. package/dist/pwa/routes/contribution-score.js +19 -0
  108. package/dist/pwa/routes/coupons.js +19 -16
  109. package/dist/pwa/routes/dashboards.js +18 -16
  110. package/dist/pwa/routes/dispute-cases.js +25 -24
  111. package/dist/pwa/routes/disputes-read.js +45 -51
  112. package/dist/pwa/routes/disputes-write.js +124 -61
  113. package/dist/pwa/routes/evidence.js +9 -9
  114. package/dist/pwa/routes/external-anchors.js +13 -12
  115. package/dist/pwa/routes/feedback.js +29 -33
  116. package/dist/pwa/routes/flash-sales.js +18 -16
  117. package/dist/pwa/routes/follows.js +25 -24
  118. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  119. package/dist/pwa/routes/governance-onboarding.js +70 -59
  120. package/dist/pwa/routes/group-buys.js +22 -22
  121. package/dist/pwa/routes/growth.js +34 -31
  122. package/dist/pwa/routes/import-product.js +12 -10
  123. package/dist/pwa/routes/kyc.js +9 -8
  124. package/dist/pwa/routes/leaderboard.js +20 -18
  125. package/dist/pwa/routes/listings.js +23 -22
  126. package/dist/pwa/routes/logistics.js +10 -8
  127. package/dist/pwa/routes/manifests.js +27 -27
  128. package/dist/pwa/routes/me-data.js +23 -21
  129. package/dist/pwa/routes/notifications.js +7 -6
  130. package/dist/pwa/routes/offers.js +30 -12
  131. package/dist/pwa/routes/orders-action.js +51 -29
  132. package/dist/pwa/routes/orders-create.js +75 -20
  133. package/dist/pwa/routes/orders-read.js +21 -20
  134. package/dist/pwa/routes/p2p-products.js +30 -18
  135. package/dist/pwa/routes/payments-governance.js +61 -56
  136. package/dist/pwa/routes/peers.js +9 -8
  137. package/dist/pwa/routes/pin-receipts.js +13 -13
  138. package/dist/pwa/routes/products-aliases.js +12 -10
  139. package/dist/pwa/routes/products-claims.js +36 -17
  140. package/dist/pwa/routes/products-create.js +53 -38
  141. package/dist/pwa/routes/products-crud.js +17 -16
  142. package/dist/pwa/routes/products-links.js +49 -26
  143. package/dist/pwa/routes/products-list.js +6 -4
  144. package/dist/pwa/routes/products-meta.js +40 -39
  145. package/dist/pwa/routes/products-update.js +19 -5
  146. package/dist/pwa/routes/profile-credentials.js +20 -19
  147. package/dist/pwa/routes/profile-identity.js +14 -13
  148. package/dist/pwa/routes/profile-location.js +7 -6
  149. package/dist/pwa/routes/profile-placement.js +20 -19
  150. package/dist/pwa/routes/profile-prefs.js +11 -11
  151. package/dist/pwa/routes/promoter.js +58 -66
  152. package/dist/pwa/routes/public-build-tasks.js +19 -0
  153. package/dist/pwa/routes/public-utils.js +108 -46
  154. package/dist/pwa/routes/push.js +16 -15
  155. package/dist/pwa/routes/ratings.js +92 -32
  156. package/dist/pwa/routes/recover-key.js +66 -26
  157. package/dist/pwa/routes/referral.js +37 -52
  158. package/dist/pwa/routes/reputation.js +3 -2
  159. package/dist/pwa/routes/returns.js +76 -73
  160. package/dist/pwa/routes/reviews.js +41 -18
  161. package/dist/pwa/routes/rewards-apply.js +16 -15
  162. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  163. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  164. package/dist/pwa/routes/rfqs.js +163 -85
  165. package/dist/pwa/routes/search.js +16 -14
  166. package/dist/pwa/routes/secondhand.js +25 -22
  167. package/dist/pwa/routes/seller-quota.js +24 -26
  168. package/dist/pwa/routes/share-redirects.js +60 -55
  169. package/dist/pwa/routes/shareables-interactions.js +34 -35
  170. package/dist/pwa/routes/shareables.js +55 -51
  171. package/dist/pwa/routes/shop-referral.js +58 -0
  172. package/dist/pwa/routes/shops.js +25 -20
  173. package/dist/pwa/routes/signaling.js +10 -9
  174. package/dist/pwa/routes/skill-market.js +16 -16
  175. package/dist/pwa/routes/skills.js +15 -14
  176. package/dist/pwa/routes/snf.js +14 -13
  177. package/dist/pwa/routes/tags.js +10 -9
  178. package/dist/pwa/routes/task-proposals.js +121 -0
  179. package/dist/pwa/routes/trial.js +72 -52
  180. package/dist/pwa/routes/trusted-kpi.js +20 -18
  181. package/dist/pwa/routes/url-claim.js +67 -28
  182. package/dist/pwa/routes/users-public.js +62 -70
  183. package/dist/pwa/routes/variants.js +12 -13
  184. package/dist/pwa/routes/verifier-user.js +61 -21
  185. package/dist/pwa/routes/verify-tasks.js +49 -25
  186. package/dist/pwa/routes/waitlist.js +16 -15
  187. package/dist/pwa/routes/wallet-read.js +75 -37
  188. package/dist/pwa/routes/wallet-write.js +12 -9
  189. package/dist/pwa/routes/webauthn.js +25 -26
  190. package/dist/pwa/routes/webhooks.js +26 -26
  191. package/dist/pwa/routes/welcome.js +45 -50
  192. package/dist/pwa/routes/wishlist-qa.js +29 -32
  193. package/dist/pwa/server.js +304 -90
  194. package/dist/version.js +1 -1
  195. package/package.json +76 -3
@@ -1,35 +1,36 @@
1
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerWishlistQaRoutes(app, deps) {
2
- const { db, generateId, auth, isTrustedRole, errorRes } = deps;
3
+ // db 已全量走 RFC-016 异步 seam(dbOne/dbAll/dbRun),不再直接用 deps.db
4
+ const { generateId, auth, isTrustedRole, errorRes } = deps;
3
5
  // ─── Wave A-1: 心愿单 ────────────────────────────────────
4
- app.post('/api/wishlist/:product_id', (req, res) => {
6
+ app.post('/api/wishlist/:product_id', async (req, res) => {
5
7
  const user = auth(req, res);
6
8
  if (!user)
7
9
  return;
8
10
  if (isTrustedRole(user))
9
11
  return void errorRes(res, 403, 'TRUSTED_ROLE_NO_TRADE', '受信角色无购物功能');
10
- const p = db.prepare('SELECT id, price, status, seller_id FROM products WHERE id = ?').get(req.params.product_id);
12
+ const p = await dbOne('SELECT id, price, status, seller_id FROM products WHERE id = ?', [req.params.product_id]);
11
13
  if (!p)
12
14
  return void res.status(404).json({ error: '商品不存在' });
13
15
  if (p.seller_id === user.id)
14
16
  return void res.status(400).json({ error: '不可收藏自己的商品' });
15
17
  const note = req.body?.note ? String(req.body.note).slice(0, 200) : null;
16
- db.prepare(`INSERT OR REPLACE INTO user_wishlist (user_id, product_id, note, price_at_add) VALUES (?,?,?,?)`)
17
- .run(user.id, req.params.product_id, note, p.price);
18
+ await dbRun(`INSERT OR REPLACE INTO user_wishlist (user_id, product_id, note, price_at_add) VALUES (?,?,?,?)`, [user.id, req.params.product_id, note, p.price]);
18
19
  res.json({ success: true });
19
20
  });
20
- app.delete('/api/wishlist/:product_id', (req, res) => {
21
+ app.delete('/api/wishlist/:product_id', async (req, res) => {
21
22
  const user = auth(req, res);
22
23
  if (!user)
23
24
  return;
24
- db.prepare('DELETE FROM user_wishlist WHERE user_id = ? AND product_id = ?').run(user.id, req.params.product_id);
25
+ await dbRun('DELETE FROM user_wishlist WHERE user_id = ? AND product_id = ?', [user.id, req.params.product_id]);
25
26
  res.json({ success: true });
26
27
  });
27
- app.get('/api/wishlist', (req, res) => {
28
+ app.get('/api/wishlist', async (req, res) => {
28
29
  const user = auth(req, res);
29
30
  if (!user)
30
31
  return;
31
32
  // 过滤已删除商品(已下架但 status=warehouse 仍显示让买家可见)
32
- const rows = db.prepare(`
33
+ const rows = await dbAll(`
33
34
  SELECT w.product_id, w.note, w.price_at_add, w.notify_price_drop, w.notify_back_in_stock, w.created_at,
34
35
  p.title, p.price as current_price, p.stock, p.status as product_status,
35
36
  p.category, p.claim_loss_count,
@@ -39,7 +40,7 @@ export function registerWishlistQaRoutes(app, deps) {
39
40
  JOIN users u ON u.id = p.seller_id
40
41
  WHERE w.user_id = ? AND p.status != 'deleted'
41
42
  ORDER BY w.created_at DESC LIMIT 200
42
- `).all(user.id);
43
+ `, [user.id]);
43
44
  for (const r of rows) {
44
45
  const cur = Number(r.current_price);
45
46
  const old = Number(r.price_at_add || cur);
@@ -48,21 +49,21 @@ export function registerWishlistQaRoutes(app, deps) {
48
49
  }
49
50
  res.json({ items: rows });
50
51
  });
51
- app.get('/api/wishlist/:product_id/check', (req, res) => {
52
+ app.get('/api/wishlist/:product_id/check', async (req, res) => {
52
53
  const user = auth(req, res);
53
54
  if (!user)
54
55
  return;
55
- const exists = db.prepare('SELECT 1 FROM user_wishlist WHERE user_id = ? AND product_id = ?').get(user.id, req.params.product_id);
56
+ const exists = await dbOne('SELECT 1 FROM user_wishlist WHERE user_id = ? AND product_id = ?', [user.id, req.params.product_id]);
56
57
  res.json({ in_wishlist: !!exists });
57
58
  });
58
59
  // ─── Wave A-2: 商品 Q&A ─────────────────────────────────
59
- app.post('/api/products/:product_id/qa', (req, res) => {
60
+ app.post('/api/products/:product_id/qa', async (req, res) => {
60
61
  const user = auth(req, res);
61
62
  if (!user)
62
63
  return;
63
64
  if (isTrustedRole(user))
64
65
  return void errorRes(res, 403, 'TRUSTED_ROLE_NO_QA', '受信角色不参与商品 Q&A');
65
- const p = db.prepare('SELECT id, seller_id, status FROM products WHERE id = ?').get(req.params.product_id);
66
+ const p = await dbOne('SELECT id, seller_id, status FROM products WHERE id = ?', [req.params.product_id]);
66
67
  if (!p)
67
68
  return void res.status(404).json({ error: '商品不存在' });
68
69
  if (p.seller_id === user.id)
@@ -73,20 +74,18 @@ export function registerWishlistQaRoutes(app, deps) {
73
74
  if (question.length < 6 || question.length > 500)
74
75
  return void res.status(400).json({ error: 'question 长度需 6-500 字' });
75
76
  const id = generateId('qa');
76
- db.prepare(`INSERT INTO product_qa (id, product_id, asker_id, seller_id, question) VALUES (?,?,?,?,?)`)
77
- .run(id, req.params.product_id, user.id, p.seller_id, question);
77
+ await dbRun(`INSERT INTO product_qa (id, product_id, asker_id, seller_id, question) VALUES (?,?,?,?,?)`, [id, req.params.product_id, user.id, p.seller_id, question]);
78
78
  try {
79
- db.prepare(`INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)`)
80
- .run(generateId('ntf'), p.seller_id, '收到商品提问', question.slice(0, 80) + (question.length > 80 ? '...' : ''), null);
79
+ await dbRun(`INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)`, [generateId('ntf'), p.seller_id, '收到商品提问', question.slice(0, 80) + (question.length > 80 ? '...' : ''), null]);
81
80
  }
82
81
  catch { }
83
82
  res.json({ success: true, id });
84
83
  });
85
- app.post('/api/products/:product_id/qa/:qa_id/answer', (req, res) => {
84
+ app.post('/api/products/:product_id/qa/:qa_id/answer', async (req, res) => {
86
85
  const user = auth(req, res);
87
86
  if (!user)
88
87
  return;
89
- const qa = db.prepare('SELECT id, seller_id, answer FROM product_qa WHERE id = ? AND product_id = ?').get(req.params.qa_id, req.params.product_id);
88
+ const qa = await dbOne('SELECT id, seller_id, answer FROM product_qa WHERE id = ? AND product_id = ?', [req.params.qa_id, req.params.product_id]);
90
89
  if (!qa)
91
90
  return void res.status(404).json({ error: '提问不存在' });
92
91
  if (qa.seller_id !== user.id)
@@ -96,40 +95,38 @@ export function registerWishlistQaRoutes(app, deps) {
96
95
  const answer = String(req.body?.answer || '').trim();
97
96
  if (answer.length < 2 || answer.length > 1000)
98
97
  return void res.status(400).json({ error: 'answer 长度需 2-1000 字' });
99
- db.prepare(`UPDATE product_qa SET answer = ?, answered_at = datetime('now') WHERE id = ?`).run(answer, req.params.qa_id);
98
+ await dbRun(`UPDATE product_qa SET answer = ?, answered_at = datetime('now') WHERE id = ?`, [answer, req.params.qa_id]);
100
99
  try {
101
- const asker = db.prepare('SELECT asker_id, question FROM product_qa WHERE id = ?').get(req.params.qa_id);
102
- db.prepare(`INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)`)
103
- .run(generateId('ntf'), asker.asker_id, '卖家回答了你的提问', answer.slice(0, 80) + (answer.length > 80 ? '...' : ''), null);
100
+ const asker = (await dbOne('SELECT asker_id, question FROM product_qa WHERE id = ?', [req.params.qa_id]));
101
+ await dbRun(`INSERT INTO notifications (id, user_id, title, body, order_id) VALUES (?,?,?,?,?)`, [generateId('ntf'), asker.asker_id, '卖家回答了你的提问', answer.slice(0, 80) + (answer.length > 80 ? '...' : ''), null]);
104
102
  }
105
103
  catch { }
106
104
  res.json({ success: true });
107
105
  });
108
- app.get('/api/products/:product_id/qa', (req, res) => {
106
+ app.get('/api/products/:product_id/qa', async (req, res) => {
109
107
  // 公开列表(不需要登录,但只返回 is_public=1)
110
- const rows = db.prepare(`
108
+ const rows = await dbAll(`
111
109
  SELECT qa.id, qa.question, qa.answer, qa.answered_at, qa.helpful_count, qa.created_at,
112
110
  ua.name as asker_name, ua.handle as asker_handle
113
111
  FROM product_qa qa JOIN users ua ON ua.id = qa.asker_id
114
112
  WHERE qa.product_id = ? AND qa.is_public = 1
115
113
  ORDER BY qa.answered_at IS NULL ASC, qa.helpful_count DESC, qa.created_at DESC LIMIT 50
116
- `).all(req.params.product_id);
114
+ `, [req.params.product_id]);
117
115
  res.json({ items: rows });
118
116
  });
119
- app.post('/api/products/:product_id/qa/:qa_id/helpful', (req, res) => {
117
+ app.post('/api/products/:product_id/qa/:qa_id/helpful', async (req, res) => {
120
118
  const user = auth(req, res);
121
119
  if (!user)
122
120
  return;
123
121
  // 防重复 — 每用户每条 QA 仅可 +1
124
122
  try {
125
- db.prepare(`INSERT INTO product_qa_helpful_voters (qa_id, user_id) VALUES (?, ?)`).run(req.params.qa_id, user.id);
123
+ await dbRun(`INSERT INTO product_qa_helpful_voters (qa_id, user_id) VALUES (?, ?)`, [req.params.qa_id, user.id]);
126
124
  }
127
125
  catch {
128
- const qa = db.prepare(`SELECT helpful_count FROM product_qa WHERE id = ?`).get(req.params.qa_id);
126
+ const qa = await dbOne(`SELECT helpful_count FROM product_qa WHERE id = ?`, [req.params.qa_id]);
129
127
  return void res.json({ success: false, already_voted: true, helpful_count: qa?.helpful_count || 0 });
130
128
  }
131
- db.prepare(`UPDATE product_qa SET helpful_count = COALESCE(helpful_count, 0) + 1 WHERE id = ? AND product_id = ?`)
132
- .run(req.params.qa_id, req.params.product_id);
129
+ await dbRun(`UPDATE product_qa SET helpful_count = COALESCE(helpful_count, 0) + 1 WHERE id = ? AND product_id = ?`, [req.params.qa_id, req.params.product_id]);
133
130
  res.json({ success: true });
134
131
  });
135
132
  }