Build API v2
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const MAX_JSON_BODY_BYTES = 64 * 1024;
|
||||
|
||||
export function jsonOk(data: unknown, status = 200): Response {
|
||||
return new Response(JSON.stringify(data), {
|
||||
status,
|
||||
headers: { 'content-type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
export function jsonError(
|
||||
status: number,
|
||||
message: string,
|
||||
headers?: Record<string, string>
|
||||
): Response {
|
||||
return new Response(JSON.stringify({ message }), {
|
||||
status,
|
||||
headers: { 'content-type': 'application/json', ...headers }
|
||||
});
|
||||
}
|
||||
|
||||
/** Read and parse a JSON request body with a size cap. Throws 413 for
|
||||
* oversized bodies and 400 for invalid JSON / wrong content type. */
|
||||
export async function readJsonBody(request: Request): Promise<unknown> {
|
||||
const contentType = request.headers.get('content-type') ?? '';
|
||||
if (!contentType.includes('application/json')) {
|
||||
throw error(400, 'content-type must be application/json');
|
||||
}
|
||||
|
||||
const contentLength = Number(request.headers.get('content-length') ?? 0);
|
||||
if (contentLength > MAX_JSON_BODY_BYTES) {
|
||||
throw error(413, 'request body too large');
|
||||
}
|
||||
|
||||
const raw = await request.text();
|
||||
if (raw.length === 0) {
|
||||
throw error(400, 'request body is required');
|
||||
}
|
||||
if (raw.length > MAX_JSON_BODY_BYTES) {
|
||||
throw error(413, 'request body too large');
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
throw error(400, 'invalid JSON body');
|
||||
}
|
||||
}
|
||||
|
||||
/** Build a 429 response with Retry-After set in seconds. */
|
||||
export function rateLimited(retryAfterMs: number): Response {
|
||||
const retryAfterSeconds = Math.max(1, Math.ceil(retryAfterMs / 1000));
|
||||
return jsonError(429, 'rate limit exceeded', { 'retry-after': String(retryAfterSeconds) });
|
||||
}
|
||||
|
||||
export function clientIp(request: Request): string {
|
||||
const fwd = request.headers.get('x-forwarded-for');
|
||||
if (fwd) {
|
||||
const first = fwd.split(',')[0]?.trim();
|
||||
if (first) return first;
|
||||
}
|
||||
return request.headers.get('x-real-ip') ?? 'unknown';
|
||||
}
|
||||
Reference in New Issue
Block a user