- "src/db/schema/platform-settings.ts" - "drizzle/0018_platform_settings_nfo_view.sql" - "src/types/index.ts" - "src/db/repositories/platform-settings-repository.ts" - "src/server/routes/platform-settings.ts" - "src/frontend/src/components/PlatformSettingsForm.tsx" - "src/frontend/src/pages/Settings.tsx" - "src/__tests__/platform-settings-api.test.ts" GSD-Task: S04/T01
132 lines
4.3 KiB
TypeScript
132 lines
4.3 KiB
TypeScript
import { type FastifyInstance } from 'fastify';
|
|
import {
|
|
getAllPlatformSettings,
|
|
getPlatformSettings,
|
|
upsertPlatformSettings,
|
|
deletePlatformSettings,
|
|
} from '../../db/repositories/platform-settings-repository';
|
|
import { Platform } from '../../types/index';
|
|
|
|
// ── JSON Schemas for Fastify Validation ──
|
|
|
|
const VALID_PLATFORMS = [Platform.YouTube, Platform.SoundCloud, Platform.Generic] as const;
|
|
|
|
const upsertPlatformSettingsBodySchema = {
|
|
type: 'object' as const,
|
|
properties: {
|
|
defaultFormatProfileId: { type: 'integer' as const, nullable: true },
|
|
checkInterval: { type: 'integer' as const, minimum: 1 },
|
|
concurrencyLimit: { type: 'integer' as const, minimum: 1, maximum: 10 },
|
|
subtitleLanguages: { type: 'string' as const, nullable: true },
|
|
grabAllEnabled: { type: 'boolean' as const },
|
|
grabAllOrder: { type: 'string' as const, enum: ['newest', 'oldest'] },
|
|
scanLimit: { type: 'integer' as const, minimum: 10, maximum: 1000 },
|
|
rateLimitDelay: { type: 'integer' as const, minimum: 0, maximum: 10000 },
|
|
defaultMonitoringMode: { type: 'string' as const, enum: ['all', 'future', 'existing', 'none'] },
|
|
nfoEnabled: { type: 'boolean' as const },
|
|
defaultView: { type: 'string' as const, enum: ['list', 'poster', 'table'] },
|
|
},
|
|
additionalProperties: false,
|
|
};
|
|
|
|
// ── Route Plugin ──
|
|
|
|
/**
|
|
* Platform settings CRUD route plugin.
|
|
*
|
|
* Registers:
|
|
* GET /api/v1/platform-settings — list all platform settings
|
|
* GET /api/v1/platform-settings/:platform — get settings for a specific platform
|
|
* PUT /api/v1/platform-settings/:platform — upsert settings for a platform
|
|
* DELETE /api/v1/platform-settings/:platform — delete settings for a platform
|
|
*/
|
|
export async function platformSettingsRoutes(fastify: FastifyInstance): Promise<void> {
|
|
// ── GET /api/v1/platform-settings ──
|
|
|
|
fastify.get('/api/v1/platform-settings', async (_request, _reply) => {
|
|
return getAllPlatformSettings(fastify.db);
|
|
});
|
|
|
|
// ── GET /api/v1/platform-settings/:platform ──
|
|
|
|
fastify.get<{ Params: { platform: string } }>(
|
|
'/api/v1/platform-settings/:platform',
|
|
async (request, reply) => {
|
|
const { platform } = request.params;
|
|
|
|
const settings = await getPlatformSettings(fastify.db, platform);
|
|
if (!settings) {
|
|
return reply.status(404).send({
|
|
statusCode: 404,
|
|
error: 'Not Found',
|
|
message: `Platform settings for '${platform}' not found`,
|
|
});
|
|
}
|
|
|
|
return settings;
|
|
}
|
|
);
|
|
|
|
// ── PUT /api/v1/platform-settings/:platform ──
|
|
|
|
fastify.put<{
|
|
Params: { platform: string };
|
|
Body: {
|
|
defaultFormatProfileId?: number | null;
|
|
checkInterval?: number;
|
|
concurrencyLimit?: number;
|
|
subtitleLanguages?: string | null;
|
|
grabAllEnabled?: boolean;
|
|
grabAllOrder?: 'newest' | 'oldest';
|
|
scanLimit?: number;
|
|
rateLimitDelay?: number;
|
|
defaultMonitoringMode?: 'all' | 'future' | 'existing' | 'none';
|
|
nfoEnabled?: boolean;
|
|
defaultView?: 'list' | 'poster' | 'table';
|
|
};
|
|
}>(
|
|
'/api/v1/platform-settings/:platform',
|
|
{
|
|
schema: { body: upsertPlatformSettingsBodySchema },
|
|
},
|
|
async (request, reply) => {
|
|
const { platform } = request.params;
|
|
|
|
// Validate platform enum
|
|
if (!VALID_PLATFORMS.includes(platform as typeof VALID_PLATFORMS[number])) {
|
|
return reply.status(400).send({
|
|
statusCode: 400,
|
|
error: 'Bad Request',
|
|
message: `Invalid platform '${platform}'. Must be one of: ${VALID_PLATFORMS.join(', ')}`,
|
|
});
|
|
}
|
|
|
|
const settings = await upsertPlatformSettings(fastify.db, {
|
|
platform: platform as typeof VALID_PLATFORMS[number],
|
|
...request.body,
|
|
});
|
|
|
|
return settings;
|
|
}
|
|
);
|
|
|
|
// ── DELETE /api/v1/platform-settings/:platform ──
|
|
|
|
fastify.delete<{ Params: { platform: string } }>(
|
|
'/api/v1/platform-settings/:platform',
|
|
async (request, reply) => {
|
|
const { platform } = request.params;
|
|
|
|
const deleted = await deletePlatformSettings(fastify.db, platform);
|
|
if (!deleted) {
|
|
return reply.status(404).send({
|
|
statusCode: 404,
|
|
error: 'Not Found',
|
|
message: `Platform settings for '${platform}' not found`,
|
|
});
|
|
}
|
|
|
|
return reply.status(204).send();
|
|
}
|
|
);
|
|
}
|