refactor: cleans up authentication duplication between page and api routes
This commit is contained in:
parent
384ca71f89
commit
5cc122bf39
5 changed files with 33 additions and 54 deletions
|
|
@ -1,5 +1,3 @@
|
|||
import { isAdmin } from './auth';
|
||||
|
||||
export function jsonResponse(data: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(data), {
|
||||
status,
|
||||
|
|
@ -10,10 +8,3 @@ export function jsonResponse(data: unknown, status = 200): Response {
|
|||
export function errorResponse(message: string, status: number): Response {
|
||||
return jsonResponse({ error: message }, status);
|
||||
}
|
||||
|
||||
export function requireAdmin(session: { user?: { id?: string } } | null): Response | null {
|
||||
if (!session?.user?.id || !isAdmin(session.user.id)) {
|
||||
return errorResponse('Unauthorized', 403);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,26 @@
|
|||
import { getSession } from 'auth-astro/server';
|
||||
|
||||
type Session = { user?: { id?: string; name?: string | null } };
|
||||
export type Session = { user?: { id?: string; name?: string | null } };
|
||||
|
||||
export function isAdmin(userId: string | undefined): boolean {
|
||||
return userId === import.meta.env.ADMIN_GITHUB_ID;
|
||||
}
|
||||
export type AuthResult =
|
||||
| { status: 'admin'; session: Session }
|
||||
| { status: 'unauthenticated' }
|
||||
| { status: 'forbidden' }
|
||||
| { status: 'error' };
|
||||
|
||||
export async function requireAdminSession(request: Request): Promise<
|
||||
| { session: Session; error: null }
|
||||
| { session: null; error: Response | null }
|
||||
> {
|
||||
export async function getAdminSession(request: Request): Promise<AuthResult> {
|
||||
let session: Session | null;
|
||||
try {
|
||||
session = await getSession(request);
|
||||
} catch {
|
||||
return { session: null, error: new Response('Auth not configured', { status: 500 }) };
|
||||
return { status: 'error' };
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return { session: null, error: null };
|
||||
if (!session) return { status: 'unauthenticated' };
|
||||
|
||||
if (session.user?.id !== import.meta.env.ADMIN_GITHUB_ID) {
|
||||
return { status: 'forbidden' };
|
||||
}
|
||||
|
||||
if (!isAdmin(session.user?.id)) {
|
||||
return { session: null, error: new Response('Forbidden', { status: 403 }) };
|
||||
}
|
||||
|
||||
return { session, error: null };
|
||||
return { status: 'admin', session };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
export const prerender = false;
|
||||
|
||||
import { getPendingEntries, type GuestbookEntry } from '../lib/db';
|
||||
import { requireAdminSession } from '../lib/auth';
|
||||
import { getAdminSession } from '../lib/auth';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { formatDate } from '../lib/format';
|
||||
|
||||
const { session, error } = await requireAdminSession(Astro.request);
|
||||
if (error) return error;
|
||||
if (!session) return Astro.redirect('/api/auth/signin');
|
||||
const auth = await getAdminSession(Astro.request);
|
||||
if (auth.status === 'error') return new Response('Auth not configured', { status: 500 });
|
||||
if (auth.status === 'unauthenticated') return Astro.redirect('/api/auth/signin');
|
||||
if (auth.status !== 'admin') return new Response('Forbidden', { status: 403 });
|
||||
const { session } = auth;
|
||||
|
||||
let entries: GuestbookEntry[] = [];
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
import { getSession } from 'auth-astro/server';
|
||||
import { jsonResponse, errorResponse, requireAdmin } from '../../lib/api';
|
||||
import { jsonResponse, errorResponse } from '../../lib/api';
|
||||
import { getAdminSession } from '../../lib/auth';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
const session = await getSession(request);
|
||||
const authError = requireAdmin(session);
|
||||
if (authError) return authError;
|
||||
const auth = await getAdminSession(request);
|
||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
||||
|
||||
const hookUrl = import.meta.env.VERCEL_DEPLOY_HOOK;
|
||||
if (!hookUrl) {
|
||||
return errorResponse('Deploy hook not configured', 500);
|
||||
}
|
||||
if (!hookUrl) return errorResponse('Deploy hook not configured', 500);
|
||||
|
||||
const res = await fetch(hookUrl, { method: 'POST' });
|
||||
if (!res.ok) {
|
||||
return errorResponse('Failed to trigger deploy', 502);
|
||||
}
|
||||
if (!res.ok) return errorResponse('Failed to trigger deploy', 502);
|
||||
|
||||
return jsonResponse({ success: true });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
import { getSession } from 'auth-astro/server';
|
||||
import { approveEntry, deleteEntry } from '../../../lib/db';
|
||||
import { jsonResponse, errorResponse, requireAdmin } from '../../../lib/api';
|
||||
import { jsonResponse, errorResponse } from '../../../lib/api';
|
||||
import { getAdminSession } from '../../../lib/auth';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export const PATCH: APIRoute = async ({ params, request }) => {
|
||||
const session = await getSession(request);
|
||||
const authError = requireAdmin(session);
|
||||
if (authError) return authError;
|
||||
const auth = await getAdminSession(request);
|
||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
||||
|
||||
const id = parseInt(params.id!, 10);
|
||||
if (isNaN(id)) {
|
||||
return errorResponse('Invalid ID', 400);
|
||||
}
|
||||
if (isNaN(id)) return errorResponse('Invalid ID', 400);
|
||||
|
||||
await approveEntry(id);
|
||||
return jsonResponse({ success: true });
|
||||
};
|
||||
|
||||
export const DELETE: APIRoute = async ({ params, request }) => {
|
||||
const session = await getSession(request);
|
||||
const authError = requireAdmin(session);
|
||||
if (authError) return authError;
|
||||
const auth = await getAdminSession(request);
|
||||
if (auth.status !== 'admin') return errorResponse('Unauthorized', 403);
|
||||
|
||||
const id = parseInt(params.id!, 10);
|
||||
if (isNaN(id)) {
|
||||
return errorResponse('Invalid ID', 400);
|
||||
}
|
||||
if (isNaN(id)) return errorResponse('Invalid ID', 400);
|
||||
|
||||
await deleteEntry(id);
|
||||
return jsonResponse({ success: true });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue