fix: Docker hardening, security, and deployment readiness for V1 (#82)

Phase 1 — Docker Artifact Optimization:
- Replace broad `COPY . .` with targeted frontend source copies (API/Python
  changes no longer bust the frontend build cache)
- Replace build-essential with gcc/g++ (leaner runtime)
- Fix LOG_LEVEL=debug → info for production
- Harden .dockerignore (exclude worktrees, IDE, CI, test artifacts)

Phase 2 — State & Persistence:
- Add PUID/PGID support in entrypoint.sh for bind mount compatibility
- Guard against PUID=0/PGID=0 to prevent accidental root execution
- Evict conflicting system users (e.g. node:1000) before UID remap

Phase 3 — Security:
- Always register @fastify/rate-limit so login brute-force protection
  works even when global rate limit is disabled (RATE_LIMIT_PER_MIN=0)
- Add trustProxy support (TRUST_PROXY env var, default true) so rate
  limiting and audit logs use real client IPs behind reverse proxies
- Strip stack traces from 500 error responses in production
- Fix FSTDEP022 deprecation: maxParamLength → routerOptions
- Add multi-file guard on single-file tool endpoint with clear error
  message pointing to the /batch endpoint

Phase 4 — Graceful Degradation:
- Add consolidated hardware detection startup banner (GPU, rate limit,
  upload limit, proxy status)
- Add ConnectionMonitor component with health polling and reconnecting
  overlay that auto-dismisses when the server comes back

Phase 5 — Deployment Docs:
- Rewrite deployment.md with copy-paste CPU and GPU compose templates
- Add hardware requirements table (minimum, recommended, heavy workloads)
- Add PUID/PGID bind mount documentation
- Add complete env var reference table
- Add reverse proxy guides for Nginx, Nginx Proxy Manager, Traefik,
  and Cloudflare Tunnels
This commit is contained in:
Ashim
2026-04-21 10:19:08 +08:00
committed by GitHub
parent fa35f57813
commit 4c9dc6e38e
11 changed files with 427 additions and 184 deletions
+15
View File
@@ -110,6 +110,7 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
let filename = "image";
let settingsRaw: string | null = null;
let fileId: string | null = null;
let fileCount = 0;
// Parse multipart parts
try {
@@ -117,6 +118,14 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
for await (const part of parts) {
if (part.type === "file") {
fileCount++;
if (fileCount > 1) {
// Drain remaining parts to avoid hanging the connection
for await (const _ of part.file) {
/* drain */
}
continue;
}
// Consume the file stream into a buffer
const chunks: Buffer[] = [];
for await (const chunk of part.file) {
@@ -141,6 +150,12 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
});
}
if (fileCount > 1) {
return reply.status(400).send({
error: `This endpoint processes one image at a time. Use /api/v1/tools/${config.toolId}/batch for multiple files.`,
});
}
// Require a file
if (!fileBuffer || fileBuffer.length === 0) {
return reply.status(400).send({ error: "No image file provided" });