diff --git a/app/lib/auth.server.ts b/app/lib/auth.server.ts new file mode 100644 index 0000000..418f890 --- /dev/null +++ b/app/lib/auth.server.ts @@ -0,0 +1,6 @@ +export async function requireAdmin(request: Request) { + // Simple fallback: check x-admin-token header vs ADMIN_TOKEN + const token = request.headers.get('x-admin-token'); + if (process.env.ADMIN_TOKEN && token === process.env.ADMIN_TOKEN) return; + throw new Response('Unauthorized', { status: 401 }); +} diff --git a/app/routes/api/admin/__tests__/settings.api.test.ts b/app/routes/api/admin/__tests__/settings.api.test.ts new file mode 100644 index 0000000..3e84ba8 --- /dev/null +++ b/app/routes/api/admin/__tests__/settings.api.test.ts @@ -0,0 +1,8 @@ +import { settingsService } from '../../../lib/settings.server'; + +test('settings API helper behavior', async () => { + const key = 'api_test_' + Date.now(); + await settingsService.set(key, { foo: 'bar' }, 'test'); + const v = await settingsService.get(key); + expect(v).toEqual({ foo: 'bar' }); +}); diff --git a/app/routes/api/admin/settings/[key].ts b/app/routes/api/admin/settings/[key].ts new file mode 100644 index 0000000..7d98d7c --- /dev/null +++ b/app/routes/api/admin/settings/[key].ts @@ -0,0 +1,12 @@ +import type { ActionFunction } from '@remix-run/node'; +import { settingsService } from '~/lib/settings.server'; +import { requireAdmin } from '~/lib/auth.server'; + +export const action: ActionFunction = async ({ request, params }) => { + await requireAdmin(request); + const key = params.key as string; + const body = await request.json(); + if (!key) return new Response('Missing key', { status: 400 }); + await settingsService.set(key, body.value, 'admin'); + return new Response(null, { status: 204 }); +}; diff --git a/app/routes/api/admin/settings/index.ts b/app/routes/api/admin/settings/index.ts new file mode 100644 index 0000000..92cd501 --- /dev/null +++ b/app/routes/api/admin/settings/index.ts @@ -0,0 +1,23 @@ +import type { LoaderFunction, ActionFunction } from '@remix-run/node'; +import { json } from '@remix-run/node'; +import { settingsService } from '~/lib/settings.server'; +import { requireAdmin } from '~/lib/auth.server'; + +export const loader: LoaderFunction = async ({ request }) => { + await requireAdmin(request); + await settingsService.init?.(); + const entries: any[] = []; + // @ts-ignore access cache + for (const key of (settingsService as any).cache.keys()) { + entries.push({ key, value: await settingsService.get(key) }); + } + return json(entries); +}; + +export const action: ActionFunction = async ({ request }) => { + await requireAdmin(request); + const body = await request.json(); + if (!body || !body.key) return new Response('Missing key', { status: 400 }); + const created = await settingsService.set(body.key, body.value, 'admin'); + return new Response(JSON.stringify(created), { status: 201 }); +};