mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: API sync and documentation audit - 100% endpoint coverage (#94)
Code quality: - Add Zod validation to 14 route handlers that used raw JSON.parse (favicon, find-duplicates, barcode-read, upscale, blur-faces, erase-object, colorize, enhance-faces, red-eye-removal, remove-background/effects, auth, api-keys, roles, teams, analytics, settings, user-files) - Standardize error responses to safeParse + formatZodErrors pattern - Replace unsafe `as` type casts with schema validation OpenAPI spec (89 -> 115 operations): - Add 14 missing tool endpoints (adjust-colors, sharpening, optimize-for-web, image-enhancement, noise-removal, red-eye-removal, restore-photo, passport-photo, colorize, enhance-faces, image-to-base64) - Add 12 missing non-tool endpoints (analytics, features, audit-log, roles, admin-health) - Add typed error schemas for 401/403/409 responses - Add descriptions to all path parameters - Bump version from 0.9.0 to 1.15.9 Documentation: - Fix 8 incorrect env var defaults in configuration guide - Add 15 undocumented env vars to configuration guide - Fix tool ID mismatch (color-adjustments -> adjust-colors) - Add 4 new API sections (Roles, Audit Log, Analytics, Features) - Add image-enhancement to AI engine reference - Update AI tool count from 13 to 14 across all docs - Add 6 missing doc links to README
This commit is contained in:
@@ -3,9 +3,14 @@ import { basename, extname } from "node:path";
|
||||
import archiver from "archiver";
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import sharp from "sharp";
|
||||
import { z } from "zod";
|
||||
import { autoOrient } from "../../lib/auto-orient.js";
|
||||
import { formatZodErrors } from "../../lib/errors.js";
|
||||
import { validateImageBuffer } from "../../lib/file-validation.js";
|
||||
import { ensureSharpCompat } from "../../lib/heic-converter.js";
|
||||
|
||||
const settingsSchema = z.object({}).passthrough();
|
||||
|
||||
const FAVICON_SIZES = [
|
||||
{ name: "favicon-16x16.png", size: 16, format: "png" as const },
|
||||
{ name: "favicon-32x32.png", size: 32, format: "png" as const },
|
||||
@@ -23,6 +28,7 @@ interface UploadedFile {
|
||||
export function registerFavicon(app: FastifyInstance) {
|
||||
app.post("/api/v1/tools/favicon", async (request, reply) => {
|
||||
const uploadedFiles: UploadedFile[] = [];
|
||||
let settingsRaw: string | null = null;
|
||||
|
||||
try {
|
||||
const parts = request.parts();
|
||||
@@ -35,6 +41,8 @@ export function registerFavicon(app: FastifyInstance) {
|
||||
const buffer = Buffer.concat(chunks);
|
||||
const filename = basename(part.filename ?? `image-${uploadedFiles.length + 1}`);
|
||||
uploadedFiles.push({ buffer, filename });
|
||||
} else if (part.fieldname === "settings") {
|
||||
settingsRaw = part.value as string;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -48,6 +56,30 @@ export function registerFavicon(app: FastifyInstance) {
|
||||
return reply.status(400).send({ error: "No image file provided" });
|
||||
}
|
||||
|
||||
// Validate all uploaded files
|
||||
for (const file of uploadedFiles) {
|
||||
const validation = await validateImageBuffer(file.buffer, file.filename);
|
||||
if (!validation.valid) {
|
||||
return reply
|
||||
.status(400)
|
||||
.send({ error: `Invalid file "${file.filename}": ${validation.reason}` });
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsRaw) {
|
||||
try {
|
||||
const parsed = JSON.parse(settingsRaw);
|
||||
const result = settingsSchema.safeParse(parsed);
|
||||
if (!result.success) {
|
||||
return reply
|
||||
.status(400)
|
||||
.send({ error: "Invalid settings", details: formatZodErrors(result.error.issues) });
|
||||
}
|
||||
} catch {
|
||||
return reply.status(400).send({ error: "Settings must be valid JSON" });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const jobId = randomUUID();
|
||||
const isSingleFile = uploadedFiles.length === 1;
|
||||
|
||||
Reference in New Issue
Block a user