Files
cfw-autumn/shared/api/platform/platformModels.ts
John Yeo 98ba9f9841 feat: Add CTE utilities and enhance platform users endpoint
## CTE Utils System
- Created comprehensive CTE (Common Table Expression) builder utilities
- Supports declarative query building with automatic relation handling
- Smart strategy selection between JOIN+GROUP BY and subquery patterns
- Handles nested relations with automatic optimization
- Type-safe query construction with Drizzle ORM

## Platform Users Enhancements
- Refactored to use new CTE utils for cleaner query building
- Added analytics middleware to Hono pipeline
- Improved query param handling with queryStringArray helper
- Changed timestamps from ISO strings to milliseconds for consistency
- Extracted cleanOrgSlug utility function

## Other Improvements
- Added analytics middleware for Hono routes
- Enhanced base middleware with better context handling
- Fixed template literal linting in initHono.ts
- Updated logger for better structured logging
- Added test files for CTE development

## File Changes
- New: server/src/db/cteUtils/ - Complete CTE builder system
- New: server/src/honoMiddlewares/analyticsMiddleware.ts
- Modified: handleListPlatformUsers.ts - Refactored with CTE utils
- Modified: platformModels.ts - Updated types and validation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 17:48:10 +01:00

75 lines
2.1 KiB
TypeScript

import { queryStringArray } from "@api/common/queryHelpers.js";
import { z } from "zod/v4";
/**
* Query params for GET /platform/users endpoint
*/
export const ListPlatformUsersQuerySchema = z.object({
limit: z
.number()
.int({ error: "limit must be an integer" })
.min(1, { error: "limit must be at least 1" })
.max(100, { error: "limit must be at most 100" })
.default(10),
offset: z
.number({ error: "offset must be a number" })
.int({ error: "offset must be an integer" })
.min(0, { error: "offset must be at least 0" })
.default(0),
expand: queryStringArray(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
.number()
.describe("Timestamp of when org was created in milliseconds since epoch"),
});
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
.number()
.describe("Timestamp of when user was created in milliseconds since epoch"),
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
>;