@waiaas/daemon 2.6.0-rc.1 → 2.6.0-rc.3
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/dist/api/routes/admin.d.ts +5 -1
- package/dist/api/routes/admin.d.ts.map +1 -1
- package/dist/api/routes/admin.js +298 -4
- package/dist/api/routes/admin.js.map +1 -1
- package/dist/api/routes/openapi-schemas.d.ts +21 -0
- package/dist/api/routes/openapi-schemas.d.ts.map +1 -1
- package/dist/api/routes/openapi-schemas.js +3 -0
- package/dist/api/routes/openapi-schemas.js.map +1 -1
- package/dist/api/routes/policies.d.ts.map +1 -1
- package/dist/api/routes/policies.js +18 -4
- package/dist/api/routes/policies.js.map +1 -1
- package/dist/api/routes/tokens.d.ts +5 -1
- package/dist/api/routes/tokens.d.ts.map +1 -1
- package/dist/api/routes/tokens.js +70 -1
- package/dist/api/routes/tokens.js.map +1 -1
- package/dist/api/routes/wallets.d.ts.map +1 -1
- package/dist/api/routes/wallets.js +3 -0
- package/dist/api/routes/wallets.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +6 -1
- package/dist/api/server.js.map +1 -1
- package/dist/pipeline/database-policy-engine.d.ts +29 -4
- package/dist/pipeline/database-policy-engine.d.ts.map +1 -1
- package/dist/pipeline/database-policy-engine.js +147 -27
- package/dist/pipeline/database-policy-engine.js.map +1 -1
- package/dist/pipeline/sign-only.d.ts +4 -0
- package/dist/pipeline/sign-only.d.ts.map +1 -1
- package/dist/pipeline/sign-only.js +1 -0
- package/dist/pipeline/sign-only.js.map +1 -1
- package/dist/pipeline/stages.d.ts.map +1 -1
- package/dist/pipeline/stages.js +2 -0
- package/dist/pipeline/stages.js.map +1 -1
- package/package.json +4 -4
- package/public/admin/assets/index-C608OrkU.js +1 -0
- package/public/admin/index.html +1 -1
- package/public/admin/assets/index-D06O_cSo.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin route handlers:
|
|
2
|
+
* Admin route handlers: 24 daemon administration endpoints.
|
|
3
3
|
*
|
|
4
4
|
* GET /admin/status - Daemon health/uptime/version (masterAuth)
|
|
5
5
|
* POST /admin/kill-switch - Activate kill switch (masterAuth)
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* GET /admin/telegram-users - List Telegram bot users (masterAuth)
|
|
22
22
|
* PUT /admin/telegram-users/:chatId - Update Telegram user role (masterAuth)
|
|
23
23
|
* DELETE /admin/telegram-users/:chatId - Delete Telegram user (masterAuth)
|
|
24
|
+
* GET /admin/transactions - Cross-wallet transaction list with filters (masterAuth)
|
|
25
|
+
* GET /admin/incoming - Cross-wallet incoming transaction list with filters (masterAuth)
|
|
24
26
|
*
|
|
25
27
|
* @see docs/37-rest-api-complete-spec.md
|
|
26
28
|
* @see docs/36-killswitch-evm-freeze.md
|
|
@@ -97,6 +99,8 @@ export interface AdminRouteDeps {
|
|
|
97
99
|
* GET /admin/telegram-users - List Telegram bot users (masterAuth)
|
|
98
100
|
* PUT /admin/telegram-users/:chatId - Update Telegram user role (masterAuth)
|
|
99
101
|
* DELETE /admin/telegram-users/:chatId - Delete Telegram user (masterAuth)
|
|
102
|
+
* GET /admin/transactions - Cross-wallet transaction list (masterAuth)
|
|
103
|
+
* GET /admin/incoming - Cross-wallet incoming tx list (masterAuth)
|
|
100
104
|
*/
|
|
101
105
|
export declare function adminRoutes(deps: AdminRouteDeps): OpenAPIHono;
|
|
102
106
|
//# sourceMappingURL=admin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../../src/api/routes/admin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../../src/api/routes/admin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAEhE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,KAAK,EAAyE,YAAY,EAAE,iBAAiB,EAAgB,MAAM,cAAc,CAAC;AAEzJ,OAAO,KAAK,EAAE,gBAAgB,EAAc,MAAM,gDAAgD,CAAC;AAInG,OAAO,KAAK,KAAK,MAAM,MAAM,yCAAyC,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yDAAyD,CAAC;AACtG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AA+BjG,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,qBAAqB,CAAC,OAAO,MAAM,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,kBAAkB,EAAE,MAAM,eAAe,CAAC;IAC1C,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClE,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,kBAAkB,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC;IACnD,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpD,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE;QAAE,yBAAyB,EAAE,OAAO,CAAC;QAAC,wBAAwB,EAAE,MAAM,CAAA;KAAE,CAAC;IACxF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAClD;AAmoBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CAq8C7D"}
|
package/dist/api/routes/admin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin route handlers:
|
|
2
|
+
* Admin route handlers: 24 daemon administration endpoints.
|
|
3
3
|
*
|
|
4
4
|
* GET /admin/status - Daemon health/uptime/version (masterAuth)
|
|
5
5
|
* POST /admin/kill-switch - Activate kill switch (masterAuth)
|
|
@@ -21,16 +21,18 @@
|
|
|
21
21
|
* GET /admin/telegram-users - List Telegram bot users (masterAuth)
|
|
22
22
|
* PUT /admin/telegram-users/:chatId - Update Telegram user role (masterAuth)
|
|
23
23
|
* DELETE /admin/telegram-users/:chatId - Delete Telegram user (masterAuth)
|
|
24
|
+
* GET /admin/transactions - Cross-wallet transaction list with filters (masterAuth)
|
|
25
|
+
* GET /admin/incoming - Cross-wallet incoming transaction list with filters (masterAuth)
|
|
24
26
|
*
|
|
25
27
|
* @see docs/37-rest-api-complete-spec.md
|
|
26
28
|
* @see docs/36-killswitch-evm-freeze.md
|
|
27
29
|
*/
|
|
28
30
|
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
|
|
29
|
-
import { sql, desc, eq, and, isNull, gt, count as drizzleCount } from 'drizzle-orm';
|
|
31
|
+
import { sql, desc, eq, and, isNull, gt, gte, lte, count as drizzleCount } from 'drizzle-orm';
|
|
30
32
|
import { createHash } from 'node:crypto';
|
|
31
33
|
import { WAIaaSError, getDefaultNetwork, getNetworksForEnvironment } from '@waiaas/core';
|
|
32
34
|
import { CurrencyCodeSchema, formatRatePreview } from '@waiaas/core';
|
|
33
|
-
import { wallets, sessions, sessionWallets, notificationLogs, policies, transactions } from '../../infrastructure/database/schema.js';
|
|
35
|
+
import { wallets, sessions, sessionWallets, notificationLogs, policies, transactions, incomingTransactions } from '../../infrastructure/database/schema.js';
|
|
34
36
|
import { generateId } from '../../infrastructure/database/id.js';
|
|
35
37
|
import { buildConnectInfoPrompt } from './connect-info.js';
|
|
36
38
|
import { getSettingDefinition } from '../../infrastructure/settings/index.js';
|
|
@@ -168,6 +170,9 @@ const notificationLogQuerySchema = z.object({
|
|
|
168
170
|
pageSize: z.string().optional().default('20'),
|
|
169
171
|
channel: z.string().optional(),
|
|
170
172
|
status: z.string().optional(),
|
|
173
|
+
eventType: z.string().optional(),
|
|
174
|
+
since: z.string().optional(),
|
|
175
|
+
until: z.string().optional(),
|
|
171
176
|
});
|
|
172
177
|
const notificationsLogRoute = createRoute({
|
|
173
178
|
method: 'get',
|
|
@@ -369,6 +374,7 @@ const adminWalletTransactionsRoute = createRoute({
|
|
|
369
374
|
params: z.object({ id: z.string().uuid() }),
|
|
370
375
|
query: z.object({
|
|
371
376
|
limit: z.coerce.number().int().min(1).max(100).default(20).optional(),
|
|
377
|
+
offset: z.coerce.number().int().min(0).default(0).optional(),
|
|
372
378
|
}),
|
|
373
379
|
},
|
|
374
380
|
responses: {
|
|
@@ -416,6 +422,7 @@ const adminWalletBalanceRoute = createRoute({
|
|
|
416
422
|
.object({
|
|
417
423
|
balance: z.string(),
|
|
418
424
|
symbol: z.string(),
|
|
425
|
+
usd: z.number().nullable().optional(),
|
|
419
426
|
})
|
|
420
427
|
.nullable(),
|
|
421
428
|
tokens: z.array(z.object({
|
|
@@ -516,6 +523,105 @@ const telegramUserDeleteRoute = createRoute({
|
|
|
516
523
|
},
|
|
517
524
|
});
|
|
518
525
|
// ---------------------------------------------------------------------------
|
|
526
|
+
// Cross-wallet admin transaction route definitions
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
const adminTransactionsQuerySchema = z.object({
|
|
529
|
+
offset: z.coerce.number().int().min(0).default(0).optional(),
|
|
530
|
+
limit: z.coerce.number().int().min(1).max(100).default(20).optional(),
|
|
531
|
+
wallet_id: z.string().uuid().optional(),
|
|
532
|
+
type: z.string().optional(),
|
|
533
|
+
status: z.string().optional(),
|
|
534
|
+
network: z.string().optional(),
|
|
535
|
+
since: z.coerce.number().optional(),
|
|
536
|
+
until: z.coerce.number().optional(),
|
|
537
|
+
search: z.string().optional(),
|
|
538
|
+
});
|
|
539
|
+
const adminTransactionsRoute = createRoute({
|
|
540
|
+
method: 'get',
|
|
541
|
+
path: '/admin/transactions',
|
|
542
|
+
tags: ['Admin'],
|
|
543
|
+
summary: 'List cross-wallet transactions with filters and pagination',
|
|
544
|
+
request: {
|
|
545
|
+
query: adminTransactionsQuerySchema,
|
|
546
|
+
},
|
|
547
|
+
responses: {
|
|
548
|
+
200: {
|
|
549
|
+
description: 'Paginated cross-wallet transaction list',
|
|
550
|
+
content: {
|
|
551
|
+
'application/json': {
|
|
552
|
+
schema: z.object({
|
|
553
|
+
items: z.array(z.object({
|
|
554
|
+
id: z.string(),
|
|
555
|
+
walletId: z.string(),
|
|
556
|
+
walletName: z.string().nullable(),
|
|
557
|
+
type: z.string(),
|
|
558
|
+
status: z.string(),
|
|
559
|
+
tier: z.string().nullable(),
|
|
560
|
+
toAddress: z.string().nullable(),
|
|
561
|
+
amount: z.string().nullable(),
|
|
562
|
+
amountUsd: z.number().nullable(),
|
|
563
|
+
network: z.string().nullable(),
|
|
564
|
+
txHash: z.string().nullable(),
|
|
565
|
+
chain: z.string(),
|
|
566
|
+
createdAt: z.number().nullable(),
|
|
567
|
+
})),
|
|
568
|
+
total: z.number().int(),
|
|
569
|
+
offset: z.number().int(),
|
|
570
|
+
limit: z.number().int(),
|
|
571
|
+
}),
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
const adminIncomingQuerySchema = z.object({
|
|
578
|
+
offset: z.coerce.number().int().min(0).default(0).optional(),
|
|
579
|
+
limit: z.coerce.number().int().min(1).max(100).default(20).optional(),
|
|
580
|
+
wallet_id: z.string().uuid().optional(),
|
|
581
|
+
chain: z.string().optional(),
|
|
582
|
+
status: z.string().optional(),
|
|
583
|
+
suspicious: z.enum(['true', 'false']).optional(),
|
|
584
|
+
});
|
|
585
|
+
const adminIncomingRoute = createRoute({
|
|
586
|
+
method: 'get',
|
|
587
|
+
path: '/admin/incoming',
|
|
588
|
+
tags: ['Admin'],
|
|
589
|
+
summary: 'List cross-wallet incoming transactions with filters and pagination',
|
|
590
|
+
request: {
|
|
591
|
+
query: adminIncomingQuerySchema,
|
|
592
|
+
},
|
|
593
|
+
responses: {
|
|
594
|
+
200: {
|
|
595
|
+
description: 'Paginated cross-wallet incoming transaction list',
|
|
596
|
+
content: {
|
|
597
|
+
'application/json': {
|
|
598
|
+
schema: z.object({
|
|
599
|
+
items: z.array(z.object({
|
|
600
|
+
id: z.string(),
|
|
601
|
+
txHash: z.string(),
|
|
602
|
+
walletId: z.string(),
|
|
603
|
+
walletName: z.string().nullable(),
|
|
604
|
+
fromAddress: z.string(),
|
|
605
|
+
amount: z.string(),
|
|
606
|
+
tokenAddress: z.string().nullable(),
|
|
607
|
+
chain: z.string(),
|
|
608
|
+
network: z.string(),
|
|
609
|
+
status: z.string(),
|
|
610
|
+
blockNumber: z.number().nullable(),
|
|
611
|
+
detectedAt: z.number().nullable(),
|
|
612
|
+
confirmedAt: z.number().nullable(),
|
|
613
|
+
suspicious: z.boolean(),
|
|
614
|
+
})),
|
|
615
|
+
total: z.number().int(),
|
|
616
|
+
offset: z.number().int(),
|
|
617
|
+
limit: z.number().int(),
|
|
618
|
+
}),
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
// ---------------------------------------------------------------------------
|
|
519
625
|
// Route factory
|
|
520
626
|
// ---------------------------------------------------------------------------
|
|
521
627
|
/**
|
|
@@ -543,6 +649,8 @@ const telegramUserDeleteRoute = createRoute({
|
|
|
543
649
|
* GET /admin/telegram-users - List Telegram bot users (masterAuth)
|
|
544
650
|
* PUT /admin/telegram-users/:chatId - Update Telegram user role (masterAuth)
|
|
545
651
|
* DELETE /admin/telegram-users/:chatId - Delete Telegram user (masterAuth)
|
|
652
|
+
* GET /admin/transactions - Cross-wallet transaction list (masterAuth)
|
|
653
|
+
* GET /admin/incoming - Cross-wallet incoming tx list (masterAuth)
|
|
546
654
|
*/
|
|
547
655
|
export function adminRoutes(deps) {
|
|
548
656
|
const router = new OpenAPIHono({ defaultHook: openApiValidationHook });
|
|
@@ -599,6 +707,7 @@ export function adminRoutes(deps) {
|
|
|
599
707
|
amount: transactions.amount,
|
|
600
708
|
amountUsd: transactions.amountUsd,
|
|
601
709
|
network: transactions.network,
|
|
710
|
+
txHash: transactions.txHash,
|
|
602
711
|
createdAt: transactions.createdAt,
|
|
603
712
|
})
|
|
604
713
|
.from(transactions)
|
|
@@ -616,6 +725,7 @@ export function adminRoutes(deps) {
|
|
|
616
725
|
amount: tx.amount ?? null,
|
|
617
726
|
amountUsd: tx.amountUsd ?? null,
|
|
618
727
|
network: tx.network ?? null,
|
|
728
|
+
txHash: tx.txHash ?? null,
|
|
619
729
|
createdAt: tx.createdAt instanceof Date
|
|
620
730
|
? Math.floor(tx.createdAt.getTime() / 1000)
|
|
621
731
|
: (typeof tx.createdAt === 'number' ? tx.createdAt : null),
|
|
@@ -890,6 +1000,21 @@ export function adminRoutes(deps) {
|
|
|
890
1000
|
if (query.status) {
|
|
891
1001
|
conditions.push(eq(notificationLogs.status, query.status));
|
|
892
1002
|
}
|
|
1003
|
+
if (query.eventType) {
|
|
1004
|
+
conditions.push(eq(notificationLogs.eventType, query.eventType));
|
|
1005
|
+
}
|
|
1006
|
+
if (query.since) {
|
|
1007
|
+
const sinceTs = parseInt(query.since, 10);
|
|
1008
|
+
if (!isNaN(sinceTs)) {
|
|
1009
|
+
conditions.push(gte(notificationLogs.createdAt, new Date(sinceTs * 1000)));
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (query.until) {
|
|
1013
|
+
const untilTs = parseInt(query.until, 10);
|
|
1014
|
+
if (!isNaN(untilTs)) {
|
|
1015
|
+
conditions.push(lte(notificationLogs.createdAt, new Date(untilTs * 1000)));
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
893
1018
|
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
894
1019
|
// Query total count
|
|
895
1020
|
const totalResult = deps.db
|
|
@@ -1149,6 +1274,7 @@ export function adminRoutes(deps) {
|
|
|
1149
1274
|
const { id } = c.req.valid('param');
|
|
1150
1275
|
const query = c.req.valid('query');
|
|
1151
1276
|
const limit = query.limit ?? 20;
|
|
1277
|
+
const offset = query.offset ?? 0;
|
|
1152
1278
|
// Verify wallet exists
|
|
1153
1279
|
const wallet = deps.db.select().from(wallets).where(eq(wallets.id, id)).get();
|
|
1154
1280
|
if (!wallet) {
|
|
@@ -1161,6 +1287,7 @@ export function adminRoutes(deps) {
|
|
|
1161
1287
|
.where(eq(transactions.walletId, id))
|
|
1162
1288
|
.orderBy(desc(transactions.createdAt))
|
|
1163
1289
|
.limit(limit)
|
|
1290
|
+
.offset(offset)
|
|
1164
1291
|
.all();
|
|
1165
1292
|
// Total count
|
|
1166
1293
|
const totalResult = deps.db
|
|
@@ -1211,6 +1338,15 @@ export function adminRoutes(deps) {
|
|
|
1211
1338
|
const adapter = await deps.adapterPool.resolve(chain, network, rpcUrl);
|
|
1212
1339
|
const balanceInfo = await adapter.getBalance(wallet.publicKey);
|
|
1213
1340
|
const nativeBalance = (Number(balanceInfo.balance) / 10 ** balanceInfo.decimals).toString();
|
|
1341
|
+
// Resolve USD price for native token if price oracle is available
|
|
1342
|
+
let nativeUsd = null;
|
|
1343
|
+
if (deps.priceOracle) {
|
|
1344
|
+
try {
|
|
1345
|
+
const priceInfo = await deps.priceOracle.getNativePrice(chain);
|
|
1346
|
+
nativeUsd = Number(nativeBalance) * priceInfo.usdPrice;
|
|
1347
|
+
}
|
|
1348
|
+
catch { /* non-critical: USD price unavailable */ }
|
|
1349
|
+
}
|
|
1214
1350
|
const assets = await adapter.getAssets(wallet.publicKey);
|
|
1215
1351
|
const tokens = assets
|
|
1216
1352
|
.filter((a) => !a.isNative)
|
|
@@ -1222,7 +1358,7 @@ export function adminRoutes(deps) {
|
|
|
1222
1358
|
return {
|
|
1223
1359
|
network,
|
|
1224
1360
|
isDefault: network === defaultNetwork,
|
|
1225
|
-
native: { balance: nativeBalance, symbol: balanceInfo.symbol },
|
|
1361
|
+
native: { balance: nativeBalance, symbol: balanceInfo.symbol, usd: nativeUsd },
|
|
1226
1362
|
tokens,
|
|
1227
1363
|
};
|
|
1228
1364
|
}));
|
|
@@ -1291,6 +1427,164 @@ export function adminRoutes(deps) {
|
|
|
1291
1427
|
return c.json({ success: true }, 200);
|
|
1292
1428
|
});
|
|
1293
1429
|
// ---------------------------------------------------------------------------
|
|
1430
|
+
// GET /admin/transactions (cross-wallet transaction list)
|
|
1431
|
+
// ---------------------------------------------------------------------------
|
|
1432
|
+
router.openapi(adminTransactionsRoute, async (c) => {
|
|
1433
|
+
const query = c.req.valid('query');
|
|
1434
|
+
const offset = query.offset ?? 0;
|
|
1435
|
+
const limit = query.limit ?? 20;
|
|
1436
|
+
// Build WHERE conditions
|
|
1437
|
+
const conditions = [];
|
|
1438
|
+
if (query.wallet_id) {
|
|
1439
|
+
conditions.push(eq(transactions.walletId, query.wallet_id));
|
|
1440
|
+
}
|
|
1441
|
+
if (query.type) {
|
|
1442
|
+
conditions.push(eq(transactions.type, query.type));
|
|
1443
|
+
}
|
|
1444
|
+
if (query.status) {
|
|
1445
|
+
conditions.push(eq(transactions.status, query.status));
|
|
1446
|
+
}
|
|
1447
|
+
if (query.network) {
|
|
1448
|
+
conditions.push(eq(transactions.network, query.network));
|
|
1449
|
+
}
|
|
1450
|
+
if (query.since !== undefined) {
|
|
1451
|
+
conditions.push(sql `${transactions.createdAt} >= ${query.since}`);
|
|
1452
|
+
}
|
|
1453
|
+
if (query.until !== undefined) {
|
|
1454
|
+
conditions.push(sql `${transactions.createdAt} <= ${query.until}`);
|
|
1455
|
+
}
|
|
1456
|
+
if (query.search) {
|
|
1457
|
+
const pattern = `%${query.search}%`;
|
|
1458
|
+
conditions.push(sql `(${transactions.txHash} LIKE ${pattern} OR ${transactions.toAddress} LIKE ${pattern})`);
|
|
1459
|
+
}
|
|
1460
|
+
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
1461
|
+
// Count total
|
|
1462
|
+
const totalResult = deps.db
|
|
1463
|
+
.select({ count: drizzleCount() })
|
|
1464
|
+
.from(transactions)
|
|
1465
|
+
.where(whereClause)
|
|
1466
|
+
.get();
|
|
1467
|
+
const total = totalResult?.count ?? 0;
|
|
1468
|
+
// Query with JOIN for walletName
|
|
1469
|
+
const rows = deps.db
|
|
1470
|
+
.select({
|
|
1471
|
+
id: transactions.id,
|
|
1472
|
+
walletId: transactions.walletId,
|
|
1473
|
+
walletName: wallets.name,
|
|
1474
|
+
type: transactions.type,
|
|
1475
|
+
status: transactions.status,
|
|
1476
|
+
tier: transactions.tier,
|
|
1477
|
+
toAddress: transactions.toAddress,
|
|
1478
|
+
amount: transactions.amount,
|
|
1479
|
+
amountUsd: transactions.amountUsd,
|
|
1480
|
+
network: transactions.network,
|
|
1481
|
+
txHash: transactions.txHash,
|
|
1482
|
+
chain: transactions.chain,
|
|
1483
|
+
createdAt: transactions.createdAt,
|
|
1484
|
+
})
|
|
1485
|
+
.from(transactions)
|
|
1486
|
+
.leftJoin(wallets, eq(transactions.walletId, wallets.id))
|
|
1487
|
+
.where(whereClause)
|
|
1488
|
+
.orderBy(desc(transactions.createdAt))
|
|
1489
|
+
.offset(offset)
|
|
1490
|
+
.limit(limit)
|
|
1491
|
+
.all();
|
|
1492
|
+
const items = rows.map((row) => ({
|
|
1493
|
+
id: row.id,
|
|
1494
|
+
walletId: row.walletId,
|
|
1495
|
+
walletName: row.walletName ?? null,
|
|
1496
|
+
type: row.type,
|
|
1497
|
+
status: row.status,
|
|
1498
|
+
tier: row.tier ?? null,
|
|
1499
|
+
toAddress: row.toAddress ?? null,
|
|
1500
|
+
amount: row.amount ?? null,
|
|
1501
|
+
amountUsd: row.amountUsd ?? null,
|
|
1502
|
+
network: row.network ?? null,
|
|
1503
|
+
txHash: row.txHash ?? null,
|
|
1504
|
+
chain: row.chain,
|
|
1505
|
+
createdAt: row.createdAt instanceof Date
|
|
1506
|
+
? Math.floor(row.createdAt.getTime() / 1000)
|
|
1507
|
+
: (typeof row.createdAt === 'number' ? row.createdAt : null),
|
|
1508
|
+
}));
|
|
1509
|
+
return c.json({ items, total, offset, limit }, 200);
|
|
1510
|
+
});
|
|
1511
|
+
// ---------------------------------------------------------------------------
|
|
1512
|
+
// GET /admin/incoming (cross-wallet incoming transaction list)
|
|
1513
|
+
// ---------------------------------------------------------------------------
|
|
1514
|
+
router.openapi(adminIncomingRoute, async (c) => {
|
|
1515
|
+
const query = c.req.valid('query');
|
|
1516
|
+
const offset = query.offset ?? 0;
|
|
1517
|
+
const limit = query.limit ?? 20;
|
|
1518
|
+
// Build WHERE conditions (no default status filter -- admin sees all)
|
|
1519
|
+
const conditions = [];
|
|
1520
|
+
if (query.wallet_id) {
|
|
1521
|
+
conditions.push(eq(incomingTransactions.walletId, query.wallet_id));
|
|
1522
|
+
}
|
|
1523
|
+
if (query.chain) {
|
|
1524
|
+
conditions.push(eq(incomingTransactions.chain, query.chain));
|
|
1525
|
+
}
|
|
1526
|
+
if (query.status) {
|
|
1527
|
+
conditions.push(eq(incomingTransactions.status, query.status));
|
|
1528
|
+
}
|
|
1529
|
+
if (query.suspicious !== undefined) {
|
|
1530
|
+
conditions.push(eq(incomingTransactions.isSuspicious, query.suspicious === 'true'));
|
|
1531
|
+
}
|
|
1532
|
+
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
1533
|
+
// Count total
|
|
1534
|
+
const totalResult = deps.db
|
|
1535
|
+
.select({ count: drizzleCount() })
|
|
1536
|
+
.from(incomingTransactions)
|
|
1537
|
+
.where(whereClause)
|
|
1538
|
+
.get();
|
|
1539
|
+
const total = totalResult?.count ?? 0;
|
|
1540
|
+
// Query with JOIN for walletName
|
|
1541
|
+
const rows = deps.db
|
|
1542
|
+
.select({
|
|
1543
|
+
id: incomingTransactions.id,
|
|
1544
|
+
txHash: incomingTransactions.txHash,
|
|
1545
|
+
walletId: incomingTransactions.walletId,
|
|
1546
|
+
walletName: wallets.name,
|
|
1547
|
+
fromAddress: incomingTransactions.fromAddress,
|
|
1548
|
+
amount: incomingTransactions.amount,
|
|
1549
|
+
tokenAddress: incomingTransactions.tokenAddress,
|
|
1550
|
+
chain: incomingTransactions.chain,
|
|
1551
|
+
network: incomingTransactions.network,
|
|
1552
|
+
status: incomingTransactions.status,
|
|
1553
|
+
blockNumber: incomingTransactions.blockNumber,
|
|
1554
|
+
detectedAt: incomingTransactions.detectedAt,
|
|
1555
|
+
confirmedAt: incomingTransactions.confirmedAt,
|
|
1556
|
+
isSuspicious: incomingTransactions.isSuspicious,
|
|
1557
|
+
})
|
|
1558
|
+
.from(incomingTransactions)
|
|
1559
|
+
.leftJoin(wallets, eq(incomingTransactions.walletId, wallets.id))
|
|
1560
|
+
.where(whereClause)
|
|
1561
|
+
.orderBy(desc(incomingTransactions.detectedAt))
|
|
1562
|
+
.offset(offset)
|
|
1563
|
+
.limit(limit)
|
|
1564
|
+
.all();
|
|
1565
|
+
const items = rows.map((row) => ({
|
|
1566
|
+
id: row.id,
|
|
1567
|
+
txHash: row.txHash,
|
|
1568
|
+
walletId: row.walletId,
|
|
1569
|
+
walletName: row.walletName ?? null,
|
|
1570
|
+
fromAddress: row.fromAddress,
|
|
1571
|
+
amount: row.amount,
|
|
1572
|
+
tokenAddress: row.tokenAddress ?? null,
|
|
1573
|
+
chain: row.chain,
|
|
1574
|
+
network: row.network,
|
|
1575
|
+
status: row.status,
|
|
1576
|
+
blockNumber: row.blockNumber ?? null,
|
|
1577
|
+
detectedAt: row.detectedAt instanceof Date
|
|
1578
|
+
? Math.floor(row.detectedAt.getTime() / 1000)
|
|
1579
|
+
: (typeof row.detectedAt === 'number' ? row.detectedAt : null),
|
|
1580
|
+
confirmedAt: row.confirmedAt instanceof Date
|
|
1581
|
+
? Math.floor(row.confirmedAt.getTime() / 1000)
|
|
1582
|
+
: (typeof row.confirmedAt === 'number' ? row.confirmedAt : null),
|
|
1583
|
+
suspicious: row.isSuspicious ?? false,
|
|
1584
|
+
}));
|
|
1585
|
+
return c.json({ items, total, offset, limit }, 200);
|
|
1586
|
+
});
|
|
1587
|
+
// ---------------------------------------------------------------------------
|
|
1294
1588
|
// POST /admin/agent-prompt — Generate agent connection prompt
|
|
1295
1589
|
// ---------------------------------------------------------------------------
|
|
1296
1590
|
const agentPromptRoute = createRoute({
|