Some checks failed
Deploy to Production / test (push) Failing after 30s
The web/ folder contains the REST API, WebSocket server, and OAuth routes — not a web frontend. Renaming to api/ clarifies this distinction since the actual web frontend lives in panel/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
275 lines
8.5 KiB
TypeScript
275 lines
8.5 KiB
TypeScript
/**
|
|
* @fileoverview Centralized Zod validation schemas for all Aurora API endpoints.
|
|
* Provides type-safe request/response validation for every entity in the system.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
|
|
// ============================================================================
|
|
// Common Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Standard pagination query parameters.
|
|
*/
|
|
export const PaginationSchema = z.object({
|
|
limit: z.coerce.number().min(1).max(100).optional().default(50),
|
|
offset: z.coerce.number().min(0).optional().default(0),
|
|
});
|
|
|
|
/**
|
|
* Numeric ID parameter validation.
|
|
*/
|
|
export const NumericIdSchema = z.coerce.number().int().positive();
|
|
|
|
/**
|
|
* Discord snowflake ID validation (string of digits).
|
|
*/
|
|
export const SnowflakeIdSchema = z.string().regex(/^\d{17,20}$/, "Invalid Discord ID format");
|
|
|
|
// ============================================================================
|
|
// Items Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Valid item types in the system.
|
|
*/
|
|
export const ItemTypeEnum = z.enum([
|
|
"CONSUMABLE",
|
|
"EQUIPMENT",
|
|
"MATERIAL",
|
|
"LOOTBOX",
|
|
"COLLECTIBLE",
|
|
"KEY",
|
|
"TOOL"
|
|
]);
|
|
|
|
/**
|
|
* Valid item rarities.
|
|
*/
|
|
export const ItemRarityEnum = z.enum(["C", "R", "SR", "SSR"]);
|
|
|
|
/**
|
|
* Query parameters for listing items.
|
|
*/
|
|
export const ItemQuerySchema = PaginationSchema.extend({
|
|
search: z.string().optional(),
|
|
type: z.string().optional(),
|
|
rarity: z.string().optional(),
|
|
});
|
|
|
|
/**
|
|
* Schema for creating a new item.
|
|
*/
|
|
export const CreateItemSchema = z.object({
|
|
name: z.string().min(1, "Name is required").max(100),
|
|
description: z.string().max(500).nullable().optional(),
|
|
rarity: ItemRarityEnum.optional().default("C"),
|
|
type: ItemTypeEnum,
|
|
price: z.union([z.string(), z.number()]).nullable().optional(),
|
|
iconUrl: z.string().optional(),
|
|
imageUrl: z.string().optional(),
|
|
usageData: z.any().nullable().optional(),
|
|
});
|
|
|
|
/**
|
|
* Schema for updating an existing item.
|
|
*/
|
|
export const UpdateItemSchema = z.object({
|
|
name: z.string().min(1).max(100).optional(),
|
|
description: z.string().max(500).nullable().optional(),
|
|
rarity: ItemRarityEnum.optional(),
|
|
type: ItemTypeEnum.optional(),
|
|
price: z.union([z.string(), z.number()]).nullable().optional(),
|
|
iconUrl: z.string().optional(),
|
|
imageUrl: z.string().optional(),
|
|
usageData: z.any().nullable().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Users Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Query parameters for listing users.
|
|
*/
|
|
export const UserQuerySchema = PaginationSchema.extend({
|
|
search: z.string().optional(),
|
|
sortBy: z.enum(["balance", "level", "xp", "username"]).optional().default("balance"),
|
|
sortOrder: z.enum(["asc", "desc"]).optional().default("desc"),
|
|
});
|
|
|
|
/**
|
|
* Schema for updating a user.
|
|
*/
|
|
export const UpdateUserSchema = z.object({
|
|
username: z.string().min(1).max(32).optional(),
|
|
balance: z.union([z.string(), z.number()]).optional(),
|
|
xp: z.union([z.string(), z.number()]).optional(),
|
|
level: z.coerce.number().int().min(0).optional(),
|
|
dailyStreak: z.coerce.number().int().min(0).optional(),
|
|
isActive: z.boolean().optional(),
|
|
settings: z.record(z.string(), z.any()).optional(),
|
|
classId: z.union([z.string(), z.number()]).optional(),
|
|
});
|
|
|
|
/**
|
|
* Schema for adding an item to user inventory.
|
|
*/
|
|
export const InventoryAddSchema = z.object({
|
|
itemId: z.coerce.number().int().positive("Item ID is required"),
|
|
quantity: z.union([z.string(), z.number()]).refine(
|
|
(val) => BigInt(val) > 0n,
|
|
"Quantity must be positive"
|
|
),
|
|
});
|
|
|
|
/**
|
|
* Query params for removing inventory items.
|
|
*/
|
|
export const InventoryRemoveQuerySchema = z.object({
|
|
amount: z.coerce.number().int().min(1).optional().default(1),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Classes Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Schema for creating a new class.
|
|
*/
|
|
export const CreateClassSchema = z.object({
|
|
id: z.union([z.string(), z.number()]),
|
|
name: z.string().min(1, "Name is required").max(50),
|
|
balance: z.union([z.string(), z.number()]).optional().default("0"),
|
|
roleId: z.string().nullable().optional(),
|
|
});
|
|
|
|
/**
|
|
* Schema for updating a class.
|
|
*/
|
|
export const UpdateClassSchema = z.object({
|
|
name: z.string().min(1).max(50).optional(),
|
|
balance: z.union([z.string(), z.number()]).optional(),
|
|
roleId: z.string().nullable().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Moderation Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Valid moderation case types.
|
|
*/
|
|
export const ModerationTypeEnum = z.enum([
|
|
"warn",
|
|
"timeout",
|
|
"kick",
|
|
"ban",
|
|
"note",
|
|
"prune"
|
|
]);
|
|
|
|
/**
|
|
* Query parameters for searching moderation cases.
|
|
*/
|
|
export const CaseQuerySchema = PaginationSchema.extend({
|
|
userId: z.string().optional(),
|
|
moderatorId: z.string().optional(),
|
|
type: ModerationTypeEnum.optional(),
|
|
active: z.preprocess(
|
|
(val) => val === "true" ? true : val === "false" ? false : undefined,
|
|
z.boolean().optional()
|
|
),
|
|
});
|
|
|
|
/**
|
|
* Schema for creating a moderation case.
|
|
*/
|
|
export const CreateCaseSchema = z.object({
|
|
type: ModerationTypeEnum,
|
|
userId: z.string().min(1, "User ID is required"),
|
|
username: z.string().min(1, "Username is required"),
|
|
moderatorId: z.string().min(1, "Moderator ID is required"),
|
|
moderatorName: z.string().min(1, "Moderator name is required"),
|
|
reason: z.string().min(1, "Reason is required").max(1000),
|
|
metadata: z.record(z.string(), z.any()).optional().default({}),
|
|
});
|
|
|
|
/**
|
|
* Schema for clearing/resolving a moderation case.
|
|
*/
|
|
export const ClearCaseSchema = z.object({
|
|
clearedBy: z.string().min(1, "Cleared by ID is required"),
|
|
clearedByName: z.string().min(1, "Cleared by name is required"),
|
|
reason: z.string().max(500).optional().default("Cleared via API"),
|
|
});
|
|
|
|
/**
|
|
* Case ID pattern validation (CASE-XXXX format).
|
|
*/
|
|
export const CaseIdPattern = /^CASE-\d+$/i;
|
|
|
|
// ============================================================================
|
|
// Transactions Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Query parameters for listing transactions.
|
|
*/
|
|
export const TransactionQuerySchema = PaginationSchema.extend({
|
|
userId: z.string().optional(),
|
|
type: z.string().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Lootdrops Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Query parameters for listing lootdrops.
|
|
*/
|
|
export const LootdropQuerySchema = z.object({
|
|
limit: z.coerce.number().min(1).max(100).optional().default(50),
|
|
});
|
|
|
|
/**
|
|
* Schema for spawning a lootdrop.
|
|
*/
|
|
export const CreateLootdropSchema = z.object({
|
|
channelId: z.string().min(1, "Channel ID is required"),
|
|
amount: z.coerce.number().int().positive().optional(),
|
|
currency: z.string().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Admin Actions Schemas
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Schema for toggling maintenance mode.
|
|
*/
|
|
export const MaintenanceModeSchema = z.object({
|
|
enabled: z.boolean(),
|
|
reason: z.string().max(200).optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Type Exports
|
|
// ============================================================================
|
|
|
|
export type ItemQuery = z.infer<typeof ItemQuerySchema>;
|
|
export type CreateItem = z.infer<typeof CreateItemSchema>;
|
|
export type UpdateItem = z.infer<typeof UpdateItemSchema>;
|
|
export type UserQuery = z.infer<typeof UserQuerySchema>;
|
|
export type UpdateUser = z.infer<typeof UpdateUserSchema>;
|
|
export type InventoryAdd = z.infer<typeof InventoryAddSchema>;
|
|
export type CreateClass = z.infer<typeof CreateClassSchema>;
|
|
export type UpdateClass = z.infer<typeof UpdateClassSchema>;
|
|
export type CaseQuery = z.infer<typeof CaseQuerySchema>;
|
|
export type CreateCase = z.infer<typeof CreateCaseSchema>;
|
|
export type ClearCase = z.infer<typeof ClearCaseSchema>;
|
|
export type TransactionQuery = z.infer<typeof TransactionQuerySchema>;
|
|
export type CreateLootdrop = z.infer<typeof CreateLootdropSchema>;
|
|
export type MaintenanceMode = z.infer<typeof MaintenanceModeSchema>;
|