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>
208 lines
6.7 KiB
TypeScript
208 lines
6.7 KiB
TypeScript
/**
|
|
* @fileoverview Quest management endpoints for Aurora API.
|
|
* Provides CRUD operations for game quests.
|
|
*/
|
|
|
|
import type { RouteContext, RouteModule } from "./types";
|
|
import { jsonResponse, errorResponse, parseIdFromPath, withErrorHandling } from "./utils";
|
|
import { CreateQuestSchema, UpdateQuestSchema } from "@shared/modules/quest/quest.types";
|
|
|
|
/**
|
|
* Quest routes handler.
|
|
*
|
|
* Endpoints:
|
|
* - GET /api/quests - List all quests
|
|
* - POST /api/quests - Create a new quest
|
|
* - PUT /api/quests/:id - Update an existing quest
|
|
* - DELETE /api/quests/:id - Delete a quest
|
|
*/
|
|
async function handler(ctx: RouteContext): Promise<Response | null> {
|
|
const { pathname, method, req } = ctx;
|
|
|
|
// Only handle requests to /api/quests*
|
|
if (!pathname.startsWith("/api/quests")) {
|
|
return null;
|
|
}
|
|
|
|
const { questService } = await import("@shared/modules/quest/quest.service");
|
|
|
|
/**
|
|
* @route GET /api/quests
|
|
* @description Returns all quests in the system.
|
|
* @response 200 - `{ success: true, data: Quest[] }`
|
|
* @response 500 - Error fetching quests
|
|
*
|
|
* @example
|
|
* // Response
|
|
* {
|
|
* "success": true,
|
|
* "data": [
|
|
* {
|
|
* "id": 1,
|
|
* "name": "Daily Login",
|
|
* "description": "Login once to claim",
|
|
* "triggerEvent": "login",
|
|
* "requirements": { "target": 1 },
|
|
* "rewards": { "xp": 50, "balance": 100 }
|
|
* }
|
|
* ]
|
|
* }
|
|
*/
|
|
if (pathname === "/api/quests" && method === "GET") {
|
|
return withErrorHandling(async () => {
|
|
const quests = await questService.getAllQuests();
|
|
return jsonResponse({
|
|
success: true,
|
|
data: quests.map(q => ({
|
|
id: q.id,
|
|
name: q.name,
|
|
description: q.description,
|
|
triggerEvent: q.triggerEvent,
|
|
requirements: q.requirements,
|
|
rewards: q.rewards,
|
|
})),
|
|
});
|
|
}, "fetch quests");
|
|
}
|
|
|
|
/**
|
|
* @route POST /api/quests
|
|
* @description Creates a new quest.
|
|
*
|
|
* @body {
|
|
* name: string,
|
|
* description?: string,
|
|
* triggerEvent: string,
|
|
* target: number,
|
|
* xpReward: number,
|
|
* balanceReward: number
|
|
* }
|
|
* @response 200 - `{ success: true, quest: Quest }`
|
|
* @response 400 - Validation error
|
|
* @response 500 - Error creating quest
|
|
*
|
|
* @example
|
|
* // Request
|
|
* POST /api/quests
|
|
* {
|
|
* "name": "Win 5 Battles",
|
|
* "description": "Defeat 5 enemies in combat",
|
|
* "triggerEvent": "battle_win",
|
|
* "target": 5,
|
|
* "xpReward": 200,
|
|
* "balanceReward": 500
|
|
* }
|
|
*/
|
|
if (pathname === "/api/quests" && method === "POST") {
|
|
return withErrorHandling(async () => {
|
|
const rawData = await req.json();
|
|
const parseResult = CreateQuestSchema.safeParse(rawData);
|
|
|
|
if (!parseResult.success) {
|
|
return Response.json({
|
|
error: "Invalid payload",
|
|
issues: parseResult.error.issues.map(i => ({ path: i.path, message: i.message }))
|
|
}, { status: 400 });
|
|
}
|
|
|
|
const data = parseResult.data;
|
|
const result = await questService.createQuest({
|
|
name: data.name,
|
|
description: data.description || "",
|
|
triggerEvent: data.triggerEvent,
|
|
requirements: { target: data.target },
|
|
rewards: {
|
|
xp: data.xpReward,
|
|
balance: data.balanceReward
|
|
}
|
|
});
|
|
|
|
return jsonResponse({ success: true, quest: result[0] });
|
|
}, "create quest");
|
|
}
|
|
|
|
/**
|
|
* @route PUT /api/quests/:id
|
|
* @description Updates an existing quest by ID.
|
|
*
|
|
* @param id - Quest ID (numeric)
|
|
* @body Partial quest fields to update
|
|
* @response 200 - `{ success: true, quest: Quest }`
|
|
* @response 400 - Invalid quest ID or validation error
|
|
* @response 404 - Quest not found
|
|
* @response 500 - Error updating quest
|
|
*/
|
|
if (pathname.match(/^\/api\/quests\/\d+$/) && method === "PUT") {
|
|
const id = parseIdFromPath(pathname);
|
|
if (!id) {
|
|
return errorResponse("Invalid quest ID", 400);
|
|
}
|
|
|
|
return withErrorHandling(async () => {
|
|
const rawData = await req.json();
|
|
const parseResult = UpdateQuestSchema.safeParse(rawData);
|
|
|
|
if (!parseResult.success) {
|
|
return Response.json({
|
|
error: "Invalid payload",
|
|
issues: parseResult.error.issues.map(i => ({ path: i.path, message: i.message }))
|
|
}, { status: 400 });
|
|
}
|
|
|
|
const data = parseResult.data;
|
|
const result = await questService.updateQuest(id, {
|
|
...(data.name !== undefined && { name: data.name }),
|
|
...(data.description !== undefined && { description: data.description }),
|
|
...(data.triggerEvent !== undefined && { triggerEvent: data.triggerEvent }),
|
|
...(data.target !== undefined && { requirements: { target: data.target } }),
|
|
...((data.xpReward !== undefined || data.balanceReward !== undefined) && {
|
|
rewards: {
|
|
xp: data.xpReward ?? 0,
|
|
balance: data.balanceReward ?? 0
|
|
}
|
|
})
|
|
});
|
|
|
|
if (!result || result.length === 0) {
|
|
return errorResponse("Quest not found", 404);
|
|
}
|
|
|
|
return jsonResponse({ success: true, quest: result[0] });
|
|
}, "update quest");
|
|
}
|
|
|
|
/**
|
|
* @route DELETE /api/quests/:id
|
|
* @description Deletes a quest by ID.
|
|
*
|
|
* @param id - Quest ID (numeric)
|
|
* @response 200 - `{ success: true, deleted: number }`
|
|
* @response 400 - Invalid quest ID
|
|
* @response 404 - Quest not found
|
|
* @response 500 - Error deleting quest
|
|
*/
|
|
if (pathname.match(/^\/api\/quests\/\d+$/) && method === "DELETE") {
|
|
const id = parseIdFromPath(pathname);
|
|
if (!id) {
|
|
return errorResponse("Invalid quest ID", 400);
|
|
}
|
|
|
|
return withErrorHandling(async () => {
|
|
const result = await questService.deleteQuest(id);
|
|
|
|
if (!result || result.length === 0) {
|
|
return errorResponse("Quest not found", 404);
|
|
}
|
|
|
|
return jsonResponse({ success: true, deleted: (result[0] as { id: number }).id });
|
|
}, "delete quest");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export const questsRoutes: RouteModule = {
|
|
name: "quests",
|
|
handler
|
|
};
|