feat: Implement a new API routing system by adding dedicated route files for users, transactions, assets, items, quests, and other game entities, and integrating them into the server.
All checks were successful
Deploy to Production / test (push) Successful in 44s
All checks were successful
Deploy to Production / test (push) Successful in 44s
This commit is contained in:
158
web/src/routes/settings.routes.ts
Normal file
158
web/src/routes/settings.routes.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @fileoverview Bot settings endpoints for Aurora API.
|
||||
* Provides endpoints for reading and updating bot configuration,
|
||||
* as well as fetching Discord metadata.
|
||||
*/
|
||||
|
||||
import type { RouteContext, RouteModule } from "./types";
|
||||
import { jsonResponse, errorResponse, withErrorHandling } from "./utils";
|
||||
|
||||
/**
|
||||
* JSON replacer for BigInt serialization.
|
||||
*/
|
||||
function jsonReplacer(_key: string, value: unknown): unknown {
|
||||
return typeof value === "bigint" ? value.toString() : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings routes handler.
|
||||
*
|
||||
* Endpoints:
|
||||
* - GET /api/settings - Get current bot configuration
|
||||
* - POST /api/settings - Update bot configuration (partial merge)
|
||||
* - GET /api/settings/meta - Get Discord metadata (roles, channels, commands)
|
||||
*/
|
||||
async function handler(ctx: RouteContext): Promise<Response | null> {
|
||||
const { pathname, method, req } = ctx;
|
||||
|
||||
// Only handle requests to /api/settings*
|
||||
if (!pathname.startsWith("/api/settings")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @route GET /api/settings
|
||||
* @description Returns the current bot configuration.
|
||||
* Configuration includes economy settings, leveling settings,
|
||||
* command toggles, and other system settings.
|
||||
* @response 200 - Full configuration object
|
||||
* @response 500 - Error fetching settings
|
||||
*
|
||||
* @example
|
||||
* // Response
|
||||
* {
|
||||
* "economy": { "dailyReward": 100, "streakBonus": 10 },
|
||||
* "leveling": { "xpPerMessage": 15, "levelUpChannel": "123456789" },
|
||||
* "commands": { "disabled": [], "channelLocks": {} }
|
||||
* }
|
||||
*/
|
||||
if (pathname === "/api/settings" && method === "GET") {
|
||||
return withErrorHandling(async () => {
|
||||
const { config } = await import("@shared/lib/config");
|
||||
return new Response(JSON.stringify(config, jsonReplacer), {
|
||||
headers: { "Content-Type": "application/json" }
|
||||
});
|
||||
}, "fetch settings");
|
||||
}
|
||||
|
||||
/**
|
||||
* @route POST /api/settings
|
||||
* @description Updates bot configuration with partial merge.
|
||||
* Only the provided fields will be updated; other settings remain unchanged.
|
||||
* After updating, commands are automatically reloaded.
|
||||
*
|
||||
* @body Partial configuration object
|
||||
* @response 200 - `{ success: true }`
|
||||
* @response 400 - Validation error
|
||||
* @response 500 - Error saving settings
|
||||
*
|
||||
* @example
|
||||
* // Request - Only update economy daily reward
|
||||
* POST /api/settings
|
||||
* { "economy": { "dailyReward": 150 } }
|
||||
*/
|
||||
if (pathname === "/api/settings" && method === "POST") {
|
||||
try {
|
||||
const partialConfig = await req.json();
|
||||
const { saveConfig, config: currentConfig } = await import("@shared/lib/config");
|
||||
const { deepMerge } = await import("@shared/lib/utils");
|
||||
|
||||
// Merge partial update into current config
|
||||
const mergedConfig = deepMerge(currentConfig, partialConfig);
|
||||
|
||||
// saveConfig throws if validation fails
|
||||
saveConfig(mergedConfig);
|
||||
|
||||
const { systemEvents, EVENTS } = await import("@shared/lib/events");
|
||||
systemEvents.emit(EVENTS.ACTIONS.RELOAD_COMMANDS);
|
||||
|
||||
return jsonResponse({ success: true });
|
||||
} catch (error) {
|
||||
// Return 400 for validation errors
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return errorResponse("Failed to save settings", 400, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @route GET /api/settings/meta
|
||||
* @description Returns Discord server metadata for settings UI.
|
||||
* Provides lists of roles, channels, and registered commands.
|
||||
*
|
||||
* @response 200 - `{ roles: Role[], channels: Channel[], commands: Command[] }`
|
||||
* @response 500 - Error fetching metadata
|
||||
*
|
||||
* @example
|
||||
* // Response
|
||||
* {
|
||||
* "roles": [
|
||||
* { "id": "123456789", "name": "Admin", "color": "#FF0000" }
|
||||
* ],
|
||||
* "channels": [
|
||||
* { "id": "987654321", "name": "general", "type": 0 }
|
||||
* ],
|
||||
* "commands": [
|
||||
* { "name": "daily", "category": "economy" }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
if (pathname === "/api/settings/meta" && method === "GET") {
|
||||
return withErrorHandling(async () => {
|
||||
const { AuroraClient } = await import("../../../bot/lib/BotClient");
|
||||
const { env } = await import("@shared/lib/env");
|
||||
|
||||
if (!env.DISCORD_GUILD_ID) {
|
||||
return jsonResponse({ roles: [], channels: [], commands: [] });
|
||||
}
|
||||
|
||||
const guild = AuroraClient.guilds.cache.get(env.DISCORD_GUILD_ID);
|
||||
if (!guild) {
|
||||
return jsonResponse({ roles: [], channels: [], commands: [] });
|
||||
}
|
||||
|
||||
// Map roles and channels to a simplified format
|
||||
const roles = guild.roles.cache
|
||||
.sort((a, b) => b.position - a.position)
|
||||
.map(r => ({ id: r.id, name: r.name, color: r.hexColor }));
|
||||
|
||||
const channels = guild.channels.cache
|
||||
.map(c => ({ id: c.id, name: c.name, type: c.type }));
|
||||
|
||||
const commands = Array.from(AuroraClient.knownCommands.entries())
|
||||
.map(([name, category]) => ({ name, category }))
|
||||
.sort((a, b) => {
|
||||
if (a.category !== b.category) return a.category.localeCompare(b.category);
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
return jsonResponse({ roles, channels, commands });
|
||||
}, "fetch settings meta");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export const settingsRoutes: RouteModule = {
|
||||
name: "settings",
|
||||
handler
|
||||
};
|
||||
Reference in New Issue
Block a user