From 7a37c79ebcd3ad314aa82ec045ba9c25f9befada Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 30 Mar 2026 17:54:44 -0300 Subject: [PATCH] ci: fix pipeline errors and enforce route lint validatation --- .github/workflows/npm-publish.yml | 17 +++++----- src/app/api/settings/cache-config/route.ts | 39 +++++++++++++++++----- src/app/api/tunnels/cloudflared/route.ts | 19 ++++++----- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 46ae68bb..b3d0abda 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -57,17 +57,18 @@ jobs: - name: Resolve version and dist-tag id: resolve run: | - case "${{ github.event_name }}" in - workflow_dispatch|workflow_call) - VERSION="${{ inputs.version }}" - TAG="${{ inputs.tag }}" - ;; - release) + VERSION="${{ inputs.version }}" + TAG="${{ inputs.tag }}" + + if [ -z "$VERSION" ]; then + if [ "${{ github.event_name }}" = "release" ]; then VERSION="${GITHUB_REF_NAME}" - ;; - esac + fi + fi + # Strip v prefix if present VERSION="${VERSION#v}" + # Default dist-tag logic if [ -z "$TAG" ]; then if [[ "$VERSION" == *-* ]]; then diff --git a/src/app/api/settings/cache-config/route.ts b/src/app/api/settings/cache-config/route.ts index a3fecff4..e7704454 100644 --- a/src/app/api/settings/cache-config/route.ts +++ b/src/app/api/settings/cache-config/route.ts @@ -1,6 +1,17 @@ import { NextRequest, NextResponse } from "next/server"; import { getSettings, updateSettings } from "@/lib/localDb"; import { isAuthenticated } from "@/shared/utils/apiAuth"; +import { z } from "zod"; +import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; + +const cacheConfigUpdateSchema = z.object({ + semanticCacheEnabled: z.boolean().optional(), + semanticCacheMaxSize: z.number().positive().optional(), + semanticCacheTTL: z.number().positive().optional(), + promptCacheEnabled: z.boolean().optional(), + promptCacheStrategy: z.enum(["auto", "system-only", "manual"]).optional(), + alwaysPreserveClientCache: z.enum(["auto", "always", "never"]).optional(), +}); const CACHE_CONFIG_KEYS = [ "semanticCacheEnabled", @@ -43,25 +54,37 @@ export async function PUT(request: NextRequest) { } try { - const body = await request.json(); - const updates: Record = {}; + let rawBody: unknown; + try { + rawBody = await request.json(); + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } - if (typeof body.semanticCacheEnabled === "boolean") { + const validation = validateBody(cacheConfigUpdateSchema, rawBody); + if (isValidationFailure(validation)) { + return validation.response; + } + + const updates: Record = {}; + const body = validation.data; + + if (body.semanticCacheEnabled !== undefined) { updates.semanticCacheEnabled = body.semanticCacheEnabled; } - if (typeof body.semanticCacheMaxSize === "number" && body.semanticCacheMaxSize > 0) { + if (body.semanticCacheMaxSize !== undefined) { updates.semanticCacheMaxSize = body.semanticCacheMaxSize; } - if (typeof body.semanticCacheTTL === "number" && body.semanticCacheTTL > 0) { + if (body.semanticCacheTTL !== undefined) { updates.semanticCacheTTL = body.semanticCacheTTL; } - if (typeof body.promptCacheEnabled === "boolean") { + if (body.promptCacheEnabled !== undefined) { updates.promptCacheEnabled = body.promptCacheEnabled; } - if (["auto", "system-only", "manual"].includes(body.promptCacheStrategy)) { + if (body.promptCacheStrategy !== undefined) { updates.promptCacheStrategy = body.promptCacheStrategy; } - if (["auto", "always", "never"].includes(body.alwaysPreserveClientCache)) { + if (body.alwaysPreserveClientCache !== undefined) { updates.alwaysPreserveClientCache = body.alwaysPreserveClientCache; } diff --git a/src/app/api/tunnels/cloudflared/route.ts b/src/app/api/tunnels/cloudflared/route.ts index 3b654505..14f78fbd 100644 --- a/src/app/api/tunnels/cloudflared/route.ts +++ b/src/app/api/tunnels/cloudflared/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { isAuthenticated } from "@/shared/utils/apiAuth"; +import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; import { getCloudflaredTunnelStatus, startCloudflaredTunnel, @@ -40,27 +41,27 @@ export async function POST(request: NextRequest) { return unauthorized(); } - let payload: unknown; + let rawBody: unknown; try { - payload = await request.json(); + rawBody = await request.json(); } catch { return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); } - const parsed = actionSchema.safeParse(payload); - if (!parsed.success) { - return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }); + const validation = validateBody(actionSchema, rawBody); + if (isValidationFailure(validation)) { + return validation.response; } + const parsed = validation.data; + try { const status = - parsed.data.action === "enable" - ? await startCloudflaredTunnel() - : await stopCloudflaredTunnel(); + parsed.action === "enable" ? await startCloudflaredTunnel() : await stopCloudflaredTunnel(); return NextResponse.json({ success: true, - action: parsed.data.action, + action: parsed.action, status, }); } catch (error) {