@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.
- package/README.md +5 -1
- package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
- package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
- package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
- package/dist/layer0-foundation/L0-1-database/db.js +65 -0
- package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
- package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
- package/dist/layer1-agent/L1-1-mcp-server/server.js +288 -208
- package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
- package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
- package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
- package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
- package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
- package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +182 -0
- package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
- package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
- package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +11 -3
- package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
- package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
- package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
- package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
- package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
- package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
- package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
- package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-discovery.js +55 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
- package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
- package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
- package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
- package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
- package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
- package/dist/pwa/acp-feed.js +13 -1
- package/dist/pwa/admin-bearer-auth.js +21 -0
- package/dist/pwa/contract-fingerprint.js +2 -0
- package/dist/pwa/email-delivery.js +127 -0
- package/dist/pwa/endpoint-actions.js +5 -1
- package/dist/pwa/goal-index.js +8 -8
- package/dist/pwa/human-presence.js +62 -0
- package/dist/pwa/public/app.js +1485 -283
- package/dist/pwa/public/i18n.js +297 -59
- package/dist/pwa/public/index.html +1 -0
- package/dist/pwa/public/openapi.json +5 -5
- package/dist/pwa/public/whitepaper/en/index.html +153 -0
- package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
- package/dist/pwa/rate-limit.js +22 -0
- package/dist/pwa/routes/account-deletion.js +15 -13
- package/dist/pwa/routes/addresses.js +10 -9
- package/dist/pwa/routes/admin-admins.js +13 -14
- package/dist/pwa/routes/admin-analytics.js +109 -69
- package/dist/pwa/routes/admin-atomic.js +10 -4
- package/dist/pwa/routes/admin-catalog.js +13 -11
- package/dist/pwa/routes/admin-editor-picks.js +15 -10
- package/dist/pwa/routes/admin-events.js +5 -3
- package/dist/pwa/routes/admin-health.js +2 -1
- package/dist/pwa/routes/admin-moderation.js +50 -29
- package/dist/pwa/routes/admin-ops.js +35 -23
- package/dist/pwa/routes/admin-protocol-params.js +16 -19
- package/dist/pwa/routes/admin-reports.js +23 -21
- package/dist/pwa/routes/admin-tokenomics.js +26 -25
- package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
- package/dist/pwa/routes/admin-users-query.js +65 -53
- package/dist/pwa/routes/admin-verifier-flow.js +82 -41
- package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
- package/dist/pwa/routes/admin-wallet-ops.js +32 -7
- package/dist/pwa/routes/agent-buy.js +46 -22
- package/dist/pwa/routes/agent-governance.js +52 -56
- package/dist/pwa/routes/ai.js +7 -5
- package/dist/pwa/routes/analytics.js +43 -41
- package/dist/pwa/routes/anchors.js +19 -20
- package/dist/pwa/routes/announcements.js +13 -13
- package/dist/pwa/routes/arbitrator.js +97 -31
- package/dist/pwa/routes/auction.js +157 -116
- package/dist/pwa/routes/auth-login.js +6 -4
- package/dist/pwa/routes/auth-read.js +21 -10
- package/dist/pwa/routes/auth-register.js +111 -26
- package/dist/pwa/routes/auth-sessions.js +12 -11
- package/dist/pwa/routes/blocklist.js +16 -15
- package/dist/pwa/routes/build-feedback.js +10 -9
- package/dist/pwa/routes/build-reputation.js +6 -2
- package/dist/pwa/routes/build-tasks.js +45 -13
- package/dist/pwa/routes/buyer-feeds.js +27 -25
- package/dist/pwa/routes/cart.js +16 -15
- package/dist/pwa/routes/charity.js +212 -150
- package/dist/pwa/routes/chat.js +42 -43
- package/dist/pwa/routes/checkin-tasks.js +10 -9
- package/dist/pwa/routes/checkout-helpers.js +12 -10
- package/dist/pwa/routes/claim-initiators.js +34 -14
- package/dist/pwa/routes/claim-verify.js +86 -53
- package/dist/pwa/routes/claim-voting.js +43 -18
- package/dist/pwa/routes/contribution-identity.js +164 -0
- package/dist/pwa/routes/contribution-score.js +19 -0
- package/dist/pwa/routes/coupons.js +19 -16
- package/dist/pwa/routes/dashboards.js +18 -16
- package/dist/pwa/routes/dispute-cases.js +25 -24
- package/dist/pwa/routes/disputes-read.js +45 -51
- package/dist/pwa/routes/disputes-write.js +124 -61
- package/dist/pwa/routes/evidence.js +9 -9
- package/dist/pwa/routes/external-anchors.js +13 -12
- package/dist/pwa/routes/feedback.js +29 -33
- package/dist/pwa/routes/flash-sales.js +18 -16
- package/dist/pwa/routes/follows.js +25 -24
- package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
- package/dist/pwa/routes/governance-onboarding.js +70 -59
- package/dist/pwa/routes/group-buys.js +22 -22
- package/dist/pwa/routes/growth.js +34 -31
- package/dist/pwa/routes/import-product.js +12 -10
- package/dist/pwa/routes/kyc.js +9 -8
- package/dist/pwa/routes/leaderboard.js +20 -18
- package/dist/pwa/routes/listings.js +23 -22
- package/dist/pwa/routes/logistics.js +10 -8
- package/dist/pwa/routes/manifests.js +27 -27
- package/dist/pwa/routes/me-data.js +23 -21
- package/dist/pwa/routes/notifications.js +7 -6
- package/dist/pwa/routes/offers.js +30 -12
- package/dist/pwa/routes/orders-action.js +51 -29
- package/dist/pwa/routes/orders-create.js +75 -20
- package/dist/pwa/routes/orders-read.js +21 -20
- package/dist/pwa/routes/p2p-products.js +30 -18
- package/dist/pwa/routes/payments-governance.js +61 -56
- package/dist/pwa/routes/peers.js +9 -8
- package/dist/pwa/routes/pin-receipts.js +13 -13
- package/dist/pwa/routes/products-aliases.js +12 -10
- package/dist/pwa/routes/products-claims.js +36 -17
- package/dist/pwa/routes/products-create.js +53 -38
- package/dist/pwa/routes/products-crud.js +17 -16
- package/dist/pwa/routes/products-links.js +49 -26
- package/dist/pwa/routes/products-list.js +6 -4
- package/dist/pwa/routes/products-meta.js +40 -39
- package/dist/pwa/routes/products-update.js +19 -5
- package/dist/pwa/routes/profile-credentials.js +20 -19
- package/dist/pwa/routes/profile-identity.js +14 -13
- package/dist/pwa/routes/profile-location.js +7 -6
- package/dist/pwa/routes/profile-placement.js +20 -19
- package/dist/pwa/routes/profile-prefs.js +11 -11
- package/dist/pwa/routes/promoter.js +58 -66
- package/dist/pwa/routes/public-build-tasks.js +19 -0
- package/dist/pwa/routes/public-utils.js +108 -46
- package/dist/pwa/routes/push.js +16 -15
- package/dist/pwa/routes/ratings.js +92 -32
- package/dist/pwa/routes/recover-key.js +66 -26
- package/dist/pwa/routes/referral.js +37 -52
- package/dist/pwa/routes/reputation.js +3 -2
- package/dist/pwa/routes/returns.js +76 -73
- package/dist/pwa/routes/reviews.js +41 -18
- package/dist/pwa/routes/rewards-apply.js +16 -15
- package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
- package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
- package/dist/pwa/routes/rfqs.js +163 -85
- package/dist/pwa/routes/search.js +16 -14
- package/dist/pwa/routes/secondhand.js +25 -22
- package/dist/pwa/routes/seller-quota.js +24 -26
- package/dist/pwa/routes/share-redirects.js +60 -55
- package/dist/pwa/routes/shareables-interactions.js +34 -35
- package/dist/pwa/routes/shareables.js +55 -51
- package/dist/pwa/routes/shop-referral.js +58 -0
- package/dist/pwa/routes/shops.js +25 -20
- package/dist/pwa/routes/signaling.js +10 -9
- package/dist/pwa/routes/skill-market.js +16 -16
- package/dist/pwa/routes/skills.js +15 -14
- package/dist/pwa/routes/snf.js +14 -13
- package/dist/pwa/routes/tags.js +10 -9
- package/dist/pwa/routes/task-proposals.js +121 -0
- package/dist/pwa/routes/trial.js +72 -52
- package/dist/pwa/routes/trusted-kpi.js +20 -18
- package/dist/pwa/routes/url-claim.js +67 -28
- package/dist/pwa/routes/users-public.js +62 -70
- package/dist/pwa/routes/variants.js +12 -13
- package/dist/pwa/routes/verifier-user.js +61 -21
- package/dist/pwa/routes/verify-tasks.js +49 -25
- package/dist/pwa/routes/waitlist.js +16 -15
- package/dist/pwa/routes/wallet-read.js +75 -37
- package/dist/pwa/routes/wallet-write.js +12 -9
- package/dist/pwa/routes/webauthn.js +25 -26
- package/dist/pwa/routes/webhooks.js +26 -26
- package/dist/pwa/routes/welcome.js +45 -50
- package/dist/pwa/routes/wishlist-qa.js +29 -32
- package/dist/pwa/server.js +304 -90
- package/dist/version.js +1 -1
- package/package.json +76 -3
|
@@ -1,33 +1,35 @@
|
|
|
1
|
+
import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
|
|
1
2
|
export function registerAdminTokenomicsRoutes(app, deps) {
|
|
2
|
-
|
|
3
|
+
// db 已走 RFC-016 异步 seam(dbOne/dbAll/dbRun),不再直接用 deps.db
|
|
4
|
+
const { requireProtocolAdmin, logAdminAction } = deps;
|
|
3
5
|
// Tokenomics 详细数据 + Tier 配置 + 高额榜
|
|
4
|
-
app.get('/api/admin/tokenomics', (req, res) => {
|
|
6
|
+
app.get('/api/admin/tokenomics', async (req, res) => {
|
|
5
7
|
const admin = requireProtocolAdmin(req, res);
|
|
6
8
|
if (!admin)
|
|
7
9
|
return;
|
|
8
|
-
const tiers =
|
|
9
|
-
const topComm =
|
|
10
|
+
const tiers = await dbAll("SELECT * FROM binary_tier_config ORDER BY tier ASC");
|
|
11
|
+
const topComm = await dbAll(`
|
|
10
12
|
SELECT cr.beneficiary_id, u.name, COUNT(*) as records, COALESCE(SUM(cr.amount),0) as earned
|
|
11
13
|
FROM commission_records cr LEFT JOIN users u ON u.id = cr.beneficiary_id
|
|
12
14
|
WHERE cr.beneficiary_id != 'sys_protocol'
|
|
13
15
|
GROUP BY cr.beneficiary_id ORDER BY earned DESC LIMIT 10
|
|
14
|
-
`)
|
|
15
|
-
const topBinary =
|
|
16
|
+
`);
|
|
17
|
+
const topBinary = await dbAll(`
|
|
16
18
|
SELECT bsr.user_id, u.name,
|
|
17
19
|
COUNT(*) as hits,
|
|
18
20
|
COALESCE(SUM(CASE WHEN settled_at IS NOT NULL THEN waz_amount ELSE 0 END),0) as waz_total,
|
|
19
21
|
COALESCE(SUM(score),0) as score_total
|
|
20
22
|
FROM binary_score_records bsr LEFT JOIN users u ON u.id = bsr.user_id
|
|
21
23
|
GROUP BY bsr.user_id ORDER BY waz_total DESC LIMIT 10
|
|
22
|
-
`)
|
|
23
|
-
const gf =
|
|
24
|
-
const mb =
|
|
25
|
-
const pvLedger =
|
|
24
|
+
`);
|
|
25
|
+
const gf = await dbOne("SELECT * FROM global_fund WHERE id=1");
|
|
26
|
+
const mb = await dbOne("SELECT balance FROM management_bonus_pool WHERE id=1");
|
|
27
|
+
const pvLedger = await dbOne(`
|
|
26
28
|
SELECT COUNT(*) as total,
|
|
27
29
|
SUM(CASE WHEN processed=0 THEN 1 ELSE 0 END) as pending,
|
|
28
30
|
COALESCE(SUM(CASE WHEN processed=0 THEN pv ELSE 0 END),0) as pending_pv
|
|
29
31
|
FROM pv_ledger
|
|
30
|
-
`)
|
|
32
|
+
`);
|
|
31
33
|
res.json({
|
|
32
34
|
global_fund: gf,
|
|
33
35
|
management_bonus_pool: mb,
|
|
@@ -38,7 +40,7 @@ export function registerAdminTokenomicsRoutes(app, deps) {
|
|
|
38
40
|
});
|
|
39
41
|
});
|
|
40
42
|
// 调整 Tier 配置
|
|
41
|
-
app.post('/api/admin/tokenomics/tier', (req, res) => {
|
|
43
|
+
app.post('/api/admin/tokenomics/tier', async (req, res) => {
|
|
42
44
|
const admin = requireProtocolAdmin(req, res);
|
|
43
45
|
if (!admin)
|
|
44
46
|
return;
|
|
@@ -47,50 +49,49 @@ export function registerAdminTokenomicsRoutes(app, deps) {
|
|
|
47
49
|
return void res.json({ error: 'tier 无效' });
|
|
48
50
|
if (Number(pv_threshold) <= 0 || Number(score_per_hit) <= 0)
|
|
49
51
|
return void res.json({ error: '阈值/分数必须 > 0' });
|
|
50
|
-
|
|
51
|
-
.run(tier, Number(pv_threshold), Number(score_per_hit), active ? 1 : 0);
|
|
52
|
+
await dbRun(`INSERT OR REPLACE INTO binary_tier_config (tier, pv_threshold, score_per_hit, active) VALUES (?,?,?,?)`, [tier, Number(pv_threshold), Number(score_per_hit), active ? 1 : 0]);
|
|
52
53
|
logAdminAction(admin.id, 'tokenomics_tier_update', 'tier', String(tier), { pv_threshold, score_per_hit, active });
|
|
53
54
|
res.json({ success: true });
|
|
54
55
|
});
|
|
55
56
|
// 管理津贴资格列表 + 开关
|
|
56
|
-
app.get('/api/admin/tokenomics/mgmt-bonus', (req, res) => {
|
|
57
|
+
app.get('/api/admin/tokenomics/mgmt-bonus', async (req, res) => {
|
|
57
58
|
const admin = requireProtocolAdmin(req, res);
|
|
58
59
|
if (!admin)
|
|
59
60
|
return;
|
|
60
|
-
const enabled =
|
|
61
|
-
const eligible =
|
|
61
|
+
const enabled = (await dbOne("SELECT value FROM system_state WHERE key='mgmt_bonus_enabled'"))?.value === '1';
|
|
62
|
+
const eligible = await dbAll(`
|
|
62
63
|
SELECT u.id, u.name, u.created_at,
|
|
63
64
|
(SELECT COUNT(*) FROM users WHERE sponsor_id = u.id) as l1_count,
|
|
64
65
|
COALESCE((SELECT SUM(amount) FROM commission_records WHERE beneficiary_id = u.id),0) as total_commission
|
|
65
66
|
FROM users u WHERE u.mgmt_bonus_eligible = 1
|
|
66
67
|
ORDER BY u.created_at DESC LIMIT 100
|
|
67
|
-
`)
|
|
68
|
+
`);
|
|
68
69
|
res.json({ enabled, eligible_users: eligible, eligible_count: eligible.length });
|
|
69
70
|
});
|
|
70
71
|
// 注册必须 ref 开关
|
|
71
|
-
app.post('/api/admin/tokenomics/require-ref/toggle', (req, res) => {
|
|
72
|
+
app.post('/api/admin/tokenomics/require-ref/toggle', async (req, res) => {
|
|
72
73
|
const admin = requireProtocolAdmin(req, res);
|
|
73
74
|
if (!admin)
|
|
74
75
|
return;
|
|
75
76
|
const { enabled } = req.body;
|
|
76
77
|
const v = enabled ? '1' : '0';
|
|
77
|
-
|
|
78
|
+
await dbRun("INSERT OR REPLACE INTO system_state (key, value) VALUES ('require_ref_to_register', ?)", [v]);
|
|
78
79
|
logAdminAction(admin.id, 'require_ref_toggle', 'system', 'require_ref_to_register', { value: v });
|
|
79
80
|
res.json({ success: true, enabled: !!enabled });
|
|
80
81
|
});
|
|
81
82
|
// 管理津贴池开关
|
|
82
|
-
app.post('/api/admin/tokenomics/mgmt-bonus/toggle', (req, res) => {
|
|
83
|
+
app.post('/api/admin/tokenomics/mgmt-bonus/toggle', async (req, res) => {
|
|
83
84
|
const admin = requireProtocolAdmin(req, res);
|
|
84
85
|
if (!admin)
|
|
85
86
|
return;
|
|
86
87
|
const { enabled } = req.body;
|
|
87
88
|
const v = enabled ? '1' : '0';
|
|
88
|
-
|
|
89
|
+
await dbRun("INSERT OR REPLACE INTO system_state (key, value) VALUES ('mgmt_bonus_enabled', ?)", [v]);
|
|
89
90
|
logAdminAction(admin.id, 'mgmt_bonus_toggle', 'system', 'mgmt_bonus_enabled', { value: v });
|
|
90
91
|
res.json({ success: true, enabled: !!enabled });
|
|
91
92
|
});
|
|
92
93
|
// 池注资
|
|
93
|
-
app.post('/api/admin/tokenomics/inject', (req, res) => {
|
|
94
|
+
app.post('/api/admin/tokenomics/inject', async (req, res) => {
|
|
94
95
|
const admin = requireProtocolAdmin(req, res);
|
|
95
96
|
if (!admin)
|
|
96
97
|
return;
|
|
@@ -99,10 +100,10 @@ export function registerAdminTokenomicsRoutes(app, deps) {
|
|
|
99
100
|
if (!(n > 0))
|
|
100
101
|
return void res.json({ error: '金额必须 > 0' });
|
|
101
102
|
if (pool === 'global_fund') {
|
|
102
|
-
|
|
103
|
+
await dbRun("UPDATE global_fund SET pool_balance = pool_balance + ? WHERE id=1", [n]);
|
|
103
104
|
}
|
|
104
105
|
else if (pool === 'management_bonus') {
|
|
105
|
-
|
|
106
|
+
await dbRun("UPDATE management_bonus_pool SET balance = balance + ? WHERE id=1", [n]);
|
|
106
107
|
}
|
|
107
108
|
else {
|
|
108
109
|
return void res.json({ error: 'pool 名称无效(global_fund / management_bonus)' });
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { dbOne, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
|
|
1
2
|
export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
2
|
-
|
|
3
|
+
// db 已走 RFC-016 异步 seam(dbOne/dbRun),不再直接用 deps.db
|
|
4
|
+
const { requireUsersAdmin, requireProtocolAdmin, requireContentAdmin, requireRootAdmin, adminCanOperateOn, isRootAdmin, safeRoles, logAdminAction, QUOTA_TIERS } = deps;
|
|
3
5
|
// L1 分享权限 override:0 auto / 1 强允 / -1 强禁
|
|
4
|
-
app.post('/api/admin/users/:id/l1-share-override', (req, res) => {
|
|
6
|
+
app.post('/api/admin/users/:id/l1-share-override', async (req, res) => {
|
|
5
7
|
const admin = requireProtocolAdmin(req, res);
|
|
6
8
|
if (!admin)
|
|
7
9
|
return;
|
|
@@ -11,38 +13,38 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
11
13
|
const v = Number(value);
|
|
12
14
|
if (![0, 1, -1].includes(v))
|
|
13
15
|
return void res.json({ error: 'value 必须是 0 / 1 / -1' });
|
|
14
|
-
const target =
|
|
16
|
+
const target = await dbOne("SELECT id FROM users WHERE id = ?", [req.params.id]);
|
|
15
17
|
if (!target)
|
|
16
18
|
return void res.json({ error: '用户不存在' });
|
|
17
|
-
|
|
19
|
+
await dbRun("UPDATE users SET l1_share_override = ?, updated_at = datetime('now') WHERE id = ?", [v, req.params.id]);
|
|
18
20
|
logAdminAction(admin.id, 'l1_share_override', 'user', req.params.id, { value: v, note: note || null });
|
|
19
21
|
res.json({ success: true, value: v });
|
|
20
22
|
});
|
|
21
|
-
app.post('/api/admin/users/:id/mgmt-bonus-eligible', (req, res) => {
|
|
23
|
+
app.post('/api/admin/users/:id/mgmt-bonus-eligible', async (req, res) => {
|
|
22
24
|
const admin = requireProtocolAdmin(req, res);
|
|
23
25
|
if (!admin)
|
|
24
26
|
return;
|
|
25
27
|
if (!adminCanOperateOn(admin, req.params.id, res))
|
|
26
28
|
return;
|
|
27
29
|
const { eligible, note } = req.body;
|
|
28
|
-
const target =
|
|
30
|
+
const target = await dbOne("SELECT id, name FROM users WHERE id = ?", [req.params.id]);
|
|
29
31
|
if (!target)
|
|
30
32
|
return void res.json({ error: '用户不存在' });
|
|
31
|
-
|
|
33
|
+
await dbRun("UPDATE users SET mgmt_bonus_eligible = ?, updated_at = datetime('now') WHERE id = ?", [eligible ? 1 : 0, req.params.id]);
|
|
32
34
|
logAdminAction(admin.id, eligible ? 'mgmt_bonus_grant' : 'mgmt_bonus_revoke', 'user', req.params.id, { note: note || null });
|
|
33
35
|
res.json({ success: true });
|
|
34
36
|
});
|
|
35
|
-
app.post('/api/admin/users/:id/reset-failed-attempts', (req, res) => {
|
|
37
|
+
app.post('/api/admin/users/:id/reset-failed-attempts', async (req, res) => {
|
|
36
38
|
const admin = requireUsersAdmin(req, res);
|
|
37
39
|
if (!admin)
|
|
38
40
|
return;
|
|
39
41
|
if (!adminCanOperateOn(admin, req.params.id, res))
|
|
40
42
|
return;
|
|
41
|
-
|
|
43
|
+
await dbRun("UPDATE users SET failed_attempts = 0, locked_until = NULL WHERE id = ?", [req.params.id]);
|
|
42
44
|
logAdminAction(admin.id, 'reset_failed_attempts', 'user', req.params.id, {});
|
|
43
45
|
res.json({ success: true });
|
|
44
46
|
});
|
|
45
|
-
app.post('/api/admin/users/:id/force-delist-all', (req, res) => {
|
|
47
|
+
app.post('/api/admin/users/:id/force-delist-all', async (req, res) => {
|
|
46
48
|
// P0.5: content 权限 + scope
|
|
47
49
|
const admin = requireContentAdmin(req, res);
|
|
48
50
|
if (!admin)
|
|
@@ -50,15 +52,15 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
50
52
|
if (!adminCanOperateOn(admin, req.params.id, res))
|
|
51
53
|
return;
|
|
52
54
|
const { reason } = req.body;
|
|
53
|
-
const seller =
|
|
55
|
+
const seller = await dbOne("SELECT id, name FROM users WHERE id = ?", [req.params.id]);
|
|
54
56
|
if (!seller)
|
|
55
57
|
return void res.json({ error: '用户不存在' });
|
|
56
|
-
const result =
|
|
58
|
+
const result = await dbRun("UPDATE products SET status = 'paused', updated_at = datetime('now') WHERE seller_id = ? AND status = 'active'", [req.params.id]);
|
|
57
59
|
logAdminAction(admin.id, 'force_delist_all', 'user', req.params.id, { reason: reason || null, count: result.changes });
|
|
58
60
|
res.json({ success: true, count: result.changes });
|
|
59
61
|
});
|
|
60
62
|
// P0.4: users + scope;suspend admin → root only
|
|
61
|
-
app.post('/api/admin/users/:id/suspend', (req, res) => {
|
|
63
|
+
app.post('/api/admin/users/:id/suspend', async (req, res) => {
|
|
62
64
|
const admin = requireUsersAdmin(req, res);
|
|
63
65
|
if (!admin)
|
|
64
66
|
return;
|
|
@@ -66,7 +68,7 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
66
68
|
const { reason } = req.body;
|
|
67
69
|
if (targetId === admin.id)
|
|
68
70
|
return void res.json({ error: '不能暂停自己' });
|
|
69
|
-
const target =
|
|
71
|
+
const target = await dbOne("SELECT id, role, region FROM users WHERE id = ?", [targetId]);
|
|
70
72
|
if (!target)
|
|
71
73
|
return void res.json({ error: '用户不存在' });
|
|
72
74
|
if (target.role === 'admin') {
|
|
@@ -75,35 +77,34 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
75
77
|
}
|
|
76
78
|
if (target.role !== 'admin' && !adminCanOperateOn(admin, targetId, res))
|
|
77
79
|
return;
|
|
78
|
-
|
|
80
|
+
await dbRun(`INSERT INTO user_moderation (user_id, suspended, reason, suspended_by, suspended_at)
|
|
79
81
|
VALUES (?, 1, ?, ?, datetime('now'))
|
|
80
82
|
ON CONFLICT(user_id) DO UPDATE SET
|
|
81
83
|
suspended = 1,
|
|
82
84
|
reason = excluded.reason,
|
|
83
85
|
suspended_by = excluded.suspended_by,
|
|
84
|
-
suspended_at = datetime('now')
|
|
85
|
-
.run(targetId, reason || null, admin.id);
|
|
86
|
+
suspended_at = datetime('now')`, [targetId, reason || null, admin.id]);
|
|
86
87
|
logAdminAction(admin.id, 'suspend_user', 'user', targetId, { reason: reason || null });
|
|
87
88
|
res.json({ success: true });
|
|
88
89
|
});
|
|
89
|
-
app.post('/api/admin/users/:id/unsuspend', (req, res) => {
|
|
90
|
+
app.post('/api/admin/users/:id/unsuspend', async (req, res) => {
|
|
90
91
|
const admin = requireUsersAdmin(req, res);
|
|
91
92
|
if (!admin)
|
|
92
93
|
return;
|
|
93
94
|
const targetId = req.params.id;
|
|
94
|
-
const target =
|
|
95
|
+
const target = await dbOne("SELECT id, role FROM users WHERE id = ?", [targetId]);
|
|
95
96
|
if (!target)
|
|
96
97
|
return void res.json({ error: '用户不存在' });
|
|
97
98
|
if (target.role === 'admin' && !isRootAdmin(admin))
|
|
98
99
|
return void res.status(403).json({ error: '仅 root 可恢复其他 admin' });
|
|
99
100
|
if (target.role !== 'admin' && !adminCanOperateOn(admin, targetId, res))
|
|
100
101
|
return;
|
|
101
|
-
|
|
102
|
+
await dbRun("UPDATE user_moderation SET suspended = 0, suspended_at = NULL WHERE user_id = ?", [targetId]);
|
|
102
103
|
logAdminAction(admin.id, 'unsuspend_user', 'user', targetId, {});
|
|
103
104
|
res.json({ success: true });
|
|
104
105
|
});
|
|
105
106
|
// P0.1: admin 角色提权必须 root;其他角色需 users + scope
|
|
106
|
-
app.post('/api/admin/users/:id/grant-role', (req, res) => {
|
|
107
|
+
app.post('/api/admin/users/:id/grant-role', async (req, res) => {
|
|
107
108
|
const { role } = req.body;
|
|
108
109
|
const allowed = ['admin', 'verifier', 'arbitrator', 'logistics', 'seller', 'buyer'];
|
|
109
110
|
if (!allowed.includes(role))
|
|
@@ -114,20 +115,19 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
114
115
|
const targetId = req.params.id;
|
|
115
116
|
if (!adminCanOperateOn(admin, targetId, res))
|
|
116
117
|
return;
|
|
117
|
-
const target =
|
|
118
|
+
const target = await dbOne("SELECT id, roles FROM users WHERE id = ?", [targetId]);
|
|
118
119
|
if (!target)
|
|
119
120
|
return void res.json({ error: '用户不存在' });
|
|
120
121
|
const roles = safeRoles(target);
|
|
121
122
|
if (roles.includes(role))
|
|
122
123
|
return void res.json({ error: '该用户已拥有此角色' });
|
|
123
124
|
roles.push(role);
|
|
124
|
-
|
|
125
|
-
.run(JSON.stringify(roles), targetId);
|
|
125
|
+
await dbRun("UPDATE users SET roles = ?, updated_at = datetime('now') WHERE id = ?", [JSON.stringify(roles), targetId]);
|
|
126
126
|
logAdminAction(admin.id, 'grant_role', 'user', targetId, { role });
|
|
127
127
|
res.json({ success: true, roles });
|
|
128
128
|
});
|
|
129
129
|
// P0.2: preview diff,含 admin 变更 → root only
|
|
130
|
-
app.post('/api/admin/users/:id/set-roles', (req, res) => {
|
|
130
|
+
app.post('/api/admin/users/:id/set-roles', async (req, res) => {
|
|
131
131
|
const { roles } = req.body;
|
|
132
132
|
const allowed = ['buyer', 'seller', 'logistics', 'arbitrator', 'verifier', 'admin'];
|
|
133
133
|
if (!Array.isArray(roles) || roles.length === 0)
|
|
@@ -138,7 +138,7 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
138
138
|
}
|
|
139
139
|
const dedup = Array.from(new Set(roles));
|
|
140
140
|
const targetId = req.params.id;
|
|
141
|
-
const target =
|
|
141
|
+
const target = await dbOne("SELECT id, role, roles FROM users WHERE id = ?", [targetId]);
|
|
142
142
|
if (!target)
|
|
143
143
|
return void res.json({ error: '用户不存在' });
|
|
144
144
|
const oldRoles = (() => { try {
|
|
@@ -162,8 +162,7 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
162
162
|
return void res.json({ error: '角色无变更' });
|
|
163
163
|
}
|
|
164
164
|
const newActiveRole = dedup.includes(target.role) ? target.role : dedup[0];
|
|
165
|
-
|
|
166
|
-
.run(newActiveRole, JSON.stringify(dedup), targetId);
|
|
165
|
+
await dbRun("UPDATE users SET role = ?, roles = ?, updated_at = datetime('now') WHERE id = ?", [newActiveRole, JSON.stringify(dedup), targetId]);
|
|
167
166
|
if (added.length)
|
|
168
167
|
logAdminAction(admin.id, 'grant_role_batch', 'user', targetId, { roles: added });
|
|
169
168
|
if (removed.length)
|
|
@@ -171,7 +170,7 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
171
170
|
res.json({ success: true, roles: dedup, added, removed });
|
|
172
171
|
});
|
|
173
172
|
// P0.3: revoke admin → root only
|
|
174
|
-
app.post('/api/admin/users/:id/revoke-role', (req, res) => {
|
|
173
|
+
app.post('/api/admin/users/:id/revoke-role', async (req, res) => {
|
|
175
174
|
const { role } = req.body;
|
|
176
175
|
const admin = role === 'admin' ? requireRootAdmin(req, res) : requireUsersAdmin(req, res);
|
|
177
176
|
if (!admin)
|
|
@@ -181,19 +180,18 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
181
180
|
return;
|
|
182
181
|
if (targetId === admin.id && role === 'admin')
|
|
183
182
|
return void res.json({ error: '不能撤销自己的管理员角色(防自杀)' });
|
|
184
|
-
const target =
|
|
183
|
+
const target = await dbOne("SELECT id, role, roles FROM users WHERE id = ?", [targetId]);
|
|
185
184
|
if (!target)
|
|
186
185
|
return void res.json({ error: '用户不存在' });
|
|
187
186
|
const roles = safeRoles(target).filter(r => r !== role);
|
|
188
187
|
if (roles.length === 0)
|
|
189
188
|
return void res.json({ error: '用户至少保留一个角色' });
|
|
190
189
|
const newActiveRole = target.role === role ? roles[0] : target.role;
|
|
191
|
-
|
|
192
|
-
.run(newActiveRole, JSON.stringify(roles), targetId);
|
|
190
|
+
await dbRun("UPDATE users SET role = ?, roles = ?, updated_at = datetime('now') WHERE id = ?", [newActiveRole, JSON.stringify(roles), targetId]);
|
|
193
191
|
logAdminAction(admin.id, 'revoke_role', 'user', targetId, { role });
|
|
194
192
|
res.json({ success: true, roles });
|
|
195
193
|
});
|
|
196
|
-
app.post('/api/admin/users/:id/set-product-quota', (req, res) => {
|
|
194
|
+
app.post('/api/admin/users/:id/set-product-quota', async (req, res) => {
|
|
197
195
|
const admin = requireUsersAdmin(req, res);
|
|
198
196
|
if (!admin)
|
|
199
197
|
return;
|
|
@@ -203,14 +201,14 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
203
201
|
const n = Number(max_products);
|
|
204
202
|
if (!QUOTA_TIERS.includes(n))
|
|
205
203
|
return void res.json({ error: `配额应为 ${QUOTA_TIERS.join(' / ')} 之一` });
|
|
206
|
-
const target =
|
|
204
|
+
const target = await dbOne("SELECT id FROM users WHERE id = ?", [req.params.id]);
|
|
207
205
|
if (!target)
|
|
208
206
|
return void res.json({ error: '用户不存在' });
|
|
209
|
-
|
|
207
|
+
await dbRun("UPDATE users SET max_products = ?, updated_at = datetime('now') WHERE id = ?", [n, req.params.id]);
|
|
210
208
|
logAdminAction(admin.id, 'set_product_quota', 'user', req.params.id, { quota: n });
|
|
211
209
|
res.json({ success: true, max_products: n });
|
|
212
210
|
});
|
|
213
|
-
app.post('/api/admin/users/:id/pause-listing', (req, res) => {
|
|
211
|
+
app.post('/api/admin/users/:id/pause-listing', async (req, res) => {
|
|
214
212
|
const admin = requireUsersAdmin(req, res);
|
|
215
213
|
if (!admin)
|
|
216
214
|
return;
|
|
@@ -219,18 +217,17 @@ export function registerAdminUsersLifecycleRoutes(app, deps) {
|
|
|
219
217
|
const { reason } = req.body;
|
|
220
218
|
if (!reason?.trim())
|
|
221
219
|
return void res.json({ error: '请填写暂停原因' });
|
|
222
|
-
|
|
223
|
-
.run(reason.trim(), admin.id, req.params.id);
|
|
220
|
+
await dbRun(`UPDATE users SET listing_paused = 1, listing_paused_reason = ?, listing_paused_by = ?, listing_paused_at = datetime('now'), updated_at = datetime('now') WHERE id = ?`, [reason.trim(), admin.id, req.params.id]);
|
|
224
221
|
logAdminAction(admin.id, 'pause_listing', 'user', req.params.id, { reason: reason.trim() });
|
|
225
222
|
res.json({ success: true });
|
|
226
223
|
});
|
|
227
|
-
app.post('/api/admin/users/:id/resume-listing', (req, res) => {
|
|
224
|
+
app.post('/api/admin/users/:id/resume-listing', async (req, res) => {
|
|
228
225
|
const admin = requireUsersAdmin(req, res);
|
|
229
226
|
if (!admin)
|
|
230
227
|
return;
|
|
231
228
|
if (!adminCanOperateOn(admin, req.params.id, res))
|
|
232
229
|
return;
|
|
233
|
-
|
|
230
|
+
await dbRun(`UPDATE users SET listing_paused = 0, listing_paused_reason = NULL, updated_at = datetime('now') WHERE id = ?`, [req.params.id]);
|
|
234
231
|
logAdminAction(admin.id, 'resume_listing', 'user', req.params.id, {});
|
|
235
232
|
res.json({ success: true });
|
|
236
233
|
});
|