Files
cfw-autumn/shared/api/platform/platformModels.ts
John Yeo cff3d5b6e9 feat: Add GET /platform/users endpoint for multi-tenancy
Implement paginated endpoint to list users and their organizations created by master org through the platform exchange flow.

## Changes
- Create shared/api/platform/platformModels.ts with Zod schemas for query params and response types
- Implement handleListPlatformUsers handler with pagination support (limit/offset)
- Add expand=organizations query param to fetch up to 100 orgs per user
- Clean org slugs by removing master org prefix in responses
- Create honoPlatformRouter and register it in Hono app at /v1/platform/users
- Export platform types from shared package index
- Apply biome linting fixes to platformRouter.ts (let -> const conversions)

## Features
 Paginated results (limit 1-100, default 10)
 Offset-based pagination matching handleBatchCustomers pattern
 Optional organizations expansion
 Filters by organizations.created_by = ctx.org_id
 Removes master org slug prefix from org responses

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 09:08:59 +01:00

77 lines
2.0 KiB
TypeScript

import { z } from "zod/v4";
/**
* Query params for GET /platform/users endpoint
*/
export const ListPlatformUsersQuerySchema = z.object({
limit: z
.number({
invalid_type_error: "limit must be a number",
})
.int({ message: "limit must be an integer" })
.min(1, { message: "limit must be at least 1" })
.max(100, { message: "limit must be at most 100" })
.default(10),
offset: z
.number({
invalid_type_error: "offset must be a number",
})
.int({ message: "offset must be an integer" })
.min(0, { message: "offset must be at least 0" })
.default(0),
expand: z
.enum(["organizations"])
.optional()
.describe(
"Comma-separated list of fields to expand. Currently supports: organizations",
),
});
export type ListPlatformUsersQuery = z.infer<
typeof ListPlatformUsersQuerySchema
>;
/**
* Platform organization schema
*/
export const ApiPlatformOrgSchema = z.object({
slug: z.string().describe("Organization slug without the master org prefix"),
name: z.string().describe("Organization name"),
created_at: z.string().describe("ISO 8601 timestamp of when org was created"),
});
export type ApiPlatformOrg = z.infer<typeof ApiPlatformOrgSchema>;
/**
* Platform user schema
*/
export const ApiPlatformUserSchema = z.object({
name: z.string().describe("User name"),
email: z.string().describe("User email"),
created_at: z
.string()
.describe("ISO 8601 timestamp of when user was created"),
organizations: z
.array(ApiPlatformOrgSchema)
.optional()
.describe("List of organizations created by the master org for this user"),
});
export type ApiPlatformUser = z.infer<typeof ApiPlatformUserSchema>;
/**
* Response schema for GET /platform/users
*/
export const ListPlatformUsersResponseSchema = z.object({
list: z.array(ApiPlatformUserSchema),
total: z.number().describe("Total number of users returned"),
limit: z.number().describe("Limit used in the query"),
offset: z.number().describe("Offset used in the query"),
});
export type ListPlatformUsersResponse = z.infer<
typeof ListPlatformUsersResponseSchema
>;