|
8 | 8 | /* eslint-disable @typescript-eslint/await-thenable -- DO RPC stubs return Rpc.Promisified which is thenable at runtime */ |
9 | 9 | import { TRPCError } from '@trpc/server'; |
10 | 10 | import { z } from 'zod'; |
11 | | -import { router, gastownProcedure } from './init'; |
| 11 | +import { router, gastownProcedure, adminProcedure } from './init'; |
12 | 12 | import { getTownDOStub } from '../dos/Town.do'; |
13 | 13 | import { getTownContainerStub } from '../dos/TownContainer.do'; |
14 | 14 | import { getGastownUserStub } from '../dos/GastownUser.do'; |
@@ -598,6 +598,104 @@ export const gastownRouter = router({ |
598 | 598 | const status = await townStub.getConvoyStatus(input.convoyId); |
599 | 599 | return status ?? { ...convoy, beads: [] }; |
600 | 600 | }), |
| 601 | + |
| 602 | + // ── Admin-only routes (bypass ownership checks) ────────────────────── |
| 603 | + |
| 604 | + adminListBeads: adminProcedure |
| 605 | + .input( |
| 606 | + z.object({ |
| 607 | + townId: z.string().uuid(), |
| 608 | + status: z.enum(['open', 'in_progress', 'closed', 'failed']).optional(), |
| 609 | + type: z |
| 610 | + .enum(['issue', 'message', 'escalation', 'merge_request', 'convoy', 'molecule', 'agent']) |
| 611 | + .optional(), |
| 612 | + limit: z.number().int().positive().max(500).default(200), |
| 613 | + }) |
| 614 | + ) |
| 615 | + .output(z.array(RpcBeadOutput)) |
| 616 | + .query(async ({ ctx, input }) => { |
| 617 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 618 | + return townStub.listBeads({ |
| 619 | + status: input.status, |
| 620 | + type: input.type, |
| 621 | + limit: input.limit, |
| 622 | + }); |
| 623 | + }), |
| 624 | + |
| 625 | + adminListAgents: adminProcedure |
| 626 | + .input(z.object({ townId: z.string().uuid() })) |
| 627 | + .output(z.array(RpcAgentOutput)) |
| 628 | + .query(async ({ ctx, input }) => { |
| 629 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 630 | + return townStub.listAgents({}); |
| 631 | + }), |
| 632 | + |
| 633 | + adminForceRestartContainer: adminProcedure |
| 634 | + .input(z.object({ townId: z.string().uuid() })) |
| 635 | + .mutation(async ({ ctx, input }) => { |
| 636 | + const containerStub = getTownContainerStub(ctx.env, input.townId); |
| 637 | + await containerStub.destroy(); |
| 638 | + }), |
| 639 | + |
| 640 | + adminForceResetAgent: adminProcedure |
| 641 | + .input(z.object({ townId: z.string().uuid(), agentId: z.string().uuid() })) |
| 642 | + .mutation(async ({ ctx, input }) => { |
| 643 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 644 | + await townStub.unhookBead(input.agentId); |
| 645 | + await townStub.updateAgentStatus(input.agentId, 'idle'); |
| 646 | + }), |
| 647 | + |
| 648 | + adminForceCloseBead: adminProcedure |
| 649 | + .input(z.object({ townId: z.string().uuid(), beadId: z.string().uuid() })) |
| 650 | + .output(RpcBeadOutput) |
| 651 | + .mutation(async ({ ctx, input }) => { |
| 652 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 653 | + return townStub.closeBead(input.beadId, 'admin'); |
| 654 | + }), |
| 655 | + |
| 656 | + adminForceFailBead: adminProcedure |
| 657 | + .input(z.object({ townId: z.string().uuid(), beadId: z.string().uuid() })) |
| 658 | + .output(RpcBeadOutput) |
| 659 | + .mutation(async ({ ctx, input }) => { |
| 660 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 661 | + return townStub.updateBeadStatus(input.beadId, 'failed', 'admin'); |
| 662 | + }), |
| 663 | + |
| 664 | + adminGetAlarmStatus: adminProcedure |
| 665 | + .input(z.object({ townId: z.string().uuid() })) |
| 666 | + .output(RpcAlarmStatusOutput) |
| 667 | + .query(async ({ ctx, input }) => { |
| 668 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 669 | + await townStub.setTownId(input.townId); |
| 670 | + return townStub.getAlarmStatus(); |
| 671 | + }), |
| 672 | + |
| 673 | + adminGetTownEvents: adminProcedure |
| 674 | + .input( |
| 675 | + z.object({ |
| 676 | + townId: z.string().uuid(), |
| 677 | + beadId: z.string().uuid().optional(), |
| 678 | + since: z.string().optional(), |
| 679 | + limit: z.number().int().positive().max(500).default(100), |
| 680 | + }) |
| 681 | + ) |
| 682 | + .output(z.array(RpcBeadEventOutput)) |
| 683 | + .query(async ({ ctx, input }) => { |
| 684 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 685 | + return townStub.listBeadEvents({ |
| 686 | + beadId: input.beadId, |
| 687 | + since: input.since, |
| 688 | + limit: input.limit, |
| 689 | + }); |
| 690 | + }), |
| 691 | + |
| 692 | + adminGetBead: adminProcedure |
| 693 | + .input(z.object({ townId: z.string().uuid(), beadId: z.string().uuid() })) |
| 694 | + .output(RpcBeadOutput.nullable()) |
| 695 | + .query(async ({ ctx, input }) => { |
| 696 | + const townStub = getTownDOStub(ctx.env, input.townId); |
| 697 | + return townStub.getBeadAsync(input.beadId); |
| 698 | + }), |
601 | 699 | }); |
602 | 700 |
|
603 | 701 | export type GastownRouter = typeof gastownRouter; |
|
0 commit comments