tubearr/src/server/routes/platform-settings.ts
jlightner 0f42a4b269 feat: Add Generic platform, per-platform NFO toggle, and default view s…
- "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
2026-04-04 09:56:53 +00:00

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();
}
);
}