From 1d5ce8a1d231022b1ed989153010c137502b9723 Mon Sep 17 00:00:00 2001 From: Anthony Stirling Date: Mon, 8 Jun 2026 18:38:00 +0100 Subject: [PATCH] chore: shorten verbose block comments across SaaS branch --- build.gradle | 7 +-- docker/backend/Dockerfile | 15 +------ docker/frontend/Dockerfile | 44 +++---------------- engine/Dockerfile | 21 +-------- frontend/editor/.env.saas | 4 +- frontend/editor/src/core/constants/app.ts | 5 +-- .../src/core/services/apiClientSetup.ts | 6 +-- .../src/core/utils/settingsNavigation.ts | 40 ++--------------- .../src/core/workers/pixelCompareWorker.ts | 7 +-- .../proprietary/services/apiClientSetup.ts | 5 +-- .../src/saas/services/apiClientSetup.ts | 26 ++--------- frontend/editor/src/saas/utils/pathUtils.ts | 3 +- 12 files changed, 24 insertions(+), 159 deletions(-) diff --git a/build.gradle b/build.gradle index 40714ed9b..d67485dff 100644 --- a/build.gradle +++ b/build.gradle @@ -185,11 +185,7 @@ subprojects { allowInsecureProtocol = true } } - // Maven Central first: it hosts the vast majority of artifacts, so Gradle - // never has to consult the slower/less-reliable mirrors below for them. - // shibboleth + jboss remain as fallbacks for the few artifacts only they - // serve (Gradle falls through on a 404). Keeping them ahead of Central - // meant a jboss outage (503) aborted resolution of common deps. + // Maven Central first; mirrors below are fallbacks for niche artifacts. mavenCentral() maven { url = "https://build.shibboleth.net/maven/releases" } maven { url = "https://repository.jboss.org/" } @@ -588,7 +584,6 @@ repositories { allowInsecureProtocol = true } } - // Maven Central first (see note in the subprojects repositories block above). mavenCentral() maven { url = "https://build.shibboleth.net/maven/releases" } } diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index d5bd45fb7..4cc63ddda 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -1,14 +1,4 @@ -# Stirling-PDF - Backend-only version (no embedded frontend) -# -# Identical to docker/embedded/Dockerfile except the JAR is built with -# -PbuildWithFrontend=false, so the React UI is NOT baked in. The gradle build -# then swaps in the lightweight API landing page (see app/core/build.gradle -# copyApiLandingPage). Use this for the split frontend/backend deployments -# (e.g. SaaS) where the UI ships as its own image (docker/frontend/Dockerfile). -# -# Because no frontend is built, the Node.js + go-task tooling that the embedded -# image installs is intentionally omitted here — the only frontend gradle tasks -# (npmInstall / npmBuild) are gated on buildWithFrontend and stay disabled. +# Stirling-PDF backend-only image — JAR built with -PbuildWithFrontend=false, UI ships separately. ARG BASE_VERSION=1.0.2 ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION} @@ -89,8 +79,7 @@ RUN set -eux; \ chmod 750 /tmp/stirling-pdf/heap_dumps; \ fc-cache -f -# Write version to a file so it is readable by scripts without env-var inheritance. -# init-without-ocr.sh reads /etc/stirling_version for the AOT cache fingerprint. +# Version file for scripts (init-without-ocr.sh reads /etc/stirling_version). RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version # Environment variables diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index 99d449321..67e35f82c 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,74 +1,44 @@ # check=skip=SecretsUsedInArgOrEnv -# The only ARG the SecretsUsedInArgOrEnv rule flags here is the Supabase -# PUBLISHABLE key — a client-safe value embedded in the bundle by design, not a -# secret. No real secrets are passed via ARG/ENV in this image. +# Supabase publishable ARG is client-safe by design, not a real secret. -# Frontend Dockerfile - React/Vite application +# Stage 1: build FROM node:25-alpine@sha256:e80397b81fa93888b5f855e8bef37d9b18d3c5eb38b8731fc23d6d878647340f AS build WORKDIR /app -# Copy package files COPY frontend/package.json frontend/package-lock.json ./ - -# Install dependencies RUN npm ci -# Copy source code COPY frontend . -# Generate the material-symbols icon subset that LocalIcon.tsx imports at build -# time. Normally produced by `task frontend:build:` via its prepare:icons -# dependency; since this Dockerfile invokes vite directly, run it explicitly. -# Fully offline — reads the @iconify-json/material-symbols dev dependency. +# Generate material-symbols icon subset (normally done by task prepare:icons). RUN node editor/scripts/generate-icons.js -# ---- Build configuration (backward compatible) ---- -# STIRLING_FLAVOR selects the editor tsconfig + source set (see -# frontend/editor/vite.config.ts). VITE_BUILD_MODE selects which .env. -# files vite loads and is passed to `vite build --mode`. The defaults below -# reproduce the previous behaviour exactly: proprietary flavor, production mode -# (vite's implicit default for `vite build`). -# -# VITE_SUPABASE_URL + VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY are CLIENT-SAFE -# publishable values, NOT secrets. They are intentionally empty here so the OSS -# repo ships no injected config; a downstream pipeline (e.g. SaaS) passes the -# target project's publishable values as build args. Note: .env* is excluded by -# .dockerignore, so these args are the only way Supabase config reaches the -# bundle in a Docker build. vite's loadEnv merges (and prioritises) VITE_- -# prefixed process env vars, so exporting them before `vite build` is enough. +# Defaults match prior behaviour. Supabase values are client-publishable build args. ARG STIRLING_FLAVOR=proprietary ARG VITE_BUILD_MODE=production ARG VITE_SUPABASE_URL="" -# pragma: allowlist secret — Supabase publishable key (safe for client) +# pragma: allowlist secret ARG VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY="" -# Build the application (vite root is editor/, output lands in editor/dist/). -# Only export the Supabase overrides when provided so unset stays unset. +# Build vite from editor/, output lands in editor/dist/. RUN set -eu; \ export STIRLING_FLAVOR="${STIRLING_FLAVOR}"; \ if [ -n "${VITE_SUPABASE_URL}" ]; then export VITE_SUPABASE_URL="${VITE_SUPABASE_URL}"; fi; \ if [ -n "${VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY}" ]; then export VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY="${VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY}"; fi; \ npx vite build editor --mode "${VITE_BUILD_MODE}" -# Production stage +# Stage 2: nginx FROM nginx:alpine@sha256:b0f7830b6bfaa1258f45d94c240ab668ced1b3651c8a222aefe6683447c7bf55 -# Copy built files from build stage COPY --from=build /app/editor/dist /usr/share/nginx/html - -# Copy nginx configuration and entrypoint COPY docker/frontend/nginx.conf /etc/nginx/nginx.conf COPY docker/frontend/entrypoint.sh /entrypoint.sh -# Make entrypoint executable RUN chmod +x /entrypoint.sh -# Expose port 80 (standard HTTP port) EXPOSE 80 -# Environment variables for flexibility ENV VITE_API_BASE_URL=http://backend:8080 -# Use custom entrypoint ENTRYPOINT ["/entrypoint.sh"] diff --git a/engine/Dockerfile b/engine/Dockerfile index 2f826120b..5a0a835fa 100644 --- a/engine/Dockerfile +++ b/engine/Dockerfile @@ -10,23 +10,7 @@ RUN apt-get update \ && rm /tmp/task.deb \ && rm -rf /var/lib/apt/lists/* -# The OSS repo's root Taskfile.yml expects engine tasks to run from ./engine/ -# (configured via `includes.engine.dir: engine`). The OSS-clone-build workflow -# copies the root Taskfile.yml + .taskfiles/ into the engine build context, then -# this Dockerfile copies them into the image. To match the Taskfile's expectations -# at runtime, engine source must live at /app/engine/ — not flattened to /app/ — -# so `task engine:run` can chdir there successfully and uv resolves -# scripts/setup_env.py to /app/engine/scripts/setup_env.py. -# -# Layout: -# /app/Taskfile.yml ← workflow-copied root Taskfile -# /app/.taskfiles/ ← workflow-copied .taskfiles -# /app/engine/pyproject.toml -# /app/engine/uv.lock -# /app/engine/.env -# /app/engine/scripts/ ← scripts/setup_env.py here -# /app/engine/src/ ← uvicorn entrypoint dir -# /app/engine/.venv/ ← uv-managed venv +# Source under /app/engine/ to match root Taskfile's `includes.engine.dir: engine`. WORKDIR /app/engine COPY pyproject.toml uv.lock .env ./ @@ -36,9 +20,6 @@ RUN --mount=type=cache,target=/root/.cache/uv \ COPY src/ ./src/ -# Task definitions live at /app/ — root Taskfile + included .taskfiles — because -# the root Taskfile's `includes.engine.dir: engine` is resolved relative to the -# Taskfile's own location, so it must sit one level above engine/. WORKDIR /app COPY Taskfile.yml ./ COPY .taskfiles/ ./.taskfiles/ diff --git a/frontend/editor/.env.saas b/frontend/editor/.env.saas index ca6c4b0e1..ee182fe99 100644 --- a/frontend/editor/.env.saas +++ b/frontend/editor/.env.saas @@ -8,7 +8,5 @@ # Userback feedback widget — leave blank to disable VITE_USERBACK_TOKEN= -# Development-only auth bypass — allows unauthenticated access on localhost in dev mode -# The URL subpath prefix is read from BASE_PATH at runtime, sourced from the -# top-level RUN_SUBPATH env var which Vite consumes at build time. +# Dev-only auth bypass for localhost. Subpath comes from RUN_SUBPATH (build-time). VITE_DEV_BYPASS_AUTH=false diff --git a/frontend/editor/src/core/constants/app.ts b/frontend/editor/src/core/constants/app.ts index 97346c9eb..abb9b3f94 100644 --- a/frontend/editor/src/core/constants/app.ts +++ b/frontend/editor/src/core/constants/app.ts @@ -39,10 +39,7 @@ export const absoluteWithBasePath = (path: string): string => { return `${window.location.origin}${BASE_PATH}${clean}`; }; -/** - * Remove the BASE_PATH prefix from a pathname so comparisons against - * bare app routes ("/login", "/files", …) work under any subpath deploy. - */ +/** Strip BASE_PATH prefix so route comparisons work under any subpath deploy. */ export const stripBasePath = (pathname: string): string => { if (!BASE_PATH) return pathname; if (pathname === BASE_PATH) return "/"; diff --git a/frontend/editor/src/core/services/apiClientSetup.ts b/frontend/editor/src/core/services/apiClientSetup.ts index d30d6be0a..95f5971a1 100644 --- a/frontend/editor/src/core/services/apiClientSetup.ts +++ b/frontend/editor/src/core/services/apiClientSetup.ts @@ -13,11 +13,7 @@ export function setupApiInterceptors(client: AxiosInstance): void { ); } -/** - * Auth headers for raw fetch() calls (SSE streams, etc.). - * Proprietary overrides with stored JWT + XSRF; SaaS overrides with the live - * Supabase access token. Async because SaaS has to consult the auth client. - */ +/** Auth headers for raw fetch() calls — empty in core; proprietary/SaaS override. */ export async function getAuthHeaders(): Promise> { return {}; } diff --git a/frontend/editor/src/core/utils/settingsNavigation.ts b/frontend/editor/src/core/utils/settingsNavigation.ts index 443389e37..fddd9ab5b 100644 --- a/frontend/editor/src/core/utils/settingsNavigation.ts +++ b/frontend/editor/src/core/utils/settingsNavigation.ts @@ -1,53 +1,21 @@ import { NavKey } from "@app/components/shared/config/types"; import { stripBasePath, withBasePath } from "@app/constants/app"; -/** - * Navigate to a specific settings section - * - * @param section - The settings section key to navigate to - * - * @example - * // Navigate to People section - * navigateToSettings('people'); - * - * // Navigate to Admin Premium section - * navigateToSettings('adminPremium'); - */ +/** Push the URL for a settings section and notify listeners. */ export function navigateToSettings(section: NavKey) { const newPath = withBasePath(`/settings/${section}`); window.history.pushState({}, "", newPath); - - // Trigger a popstate event to notify components window.dispatchEvent(new PopStateEvent("popstate")); } -/** - * Get the URL path for a settings section - * Useful for creating links - * - * @param section - The settings section key - * @returns The URL path for the settings section - * - * @example - * Go to People Settings - * // Returns: "/settings/people" (or "/bpp/settings/people" under subpath) - */ +/** URL for a settings section (subpath-aware). */ export function getSettingsUrl(section: NavKey): string { return withBasePath(`/settings/${section}`); } -/** - * Check if currently viewing a settings section - * - * @param section - Optional section key to check for specific section - * @returns True if in settings (and matching specific section if provided) - */ +/** Whether the current URL is in /settings (optionally a specific section). */ export function isInSettings(section?: NavKey): boolean { const pathname = stripBasePath(window.location.pathname); - - if (!section) { - return pathname.startsWith("/settings"); - } - + if (!section) return pathname.startsWith("/settings"); return pathname === `/settings/${section}`; } diff --git a/frontend/editor/src/core/workers/pixelCompareWorker.ts b/frontend/editor/src/core/workers/pixelCompareWorker.ts index 987857821..c1390391b 100644 --- a/frontend/editor/src/core/workers/pixelCompareWorker.ts +++ b/frontend/editor/src/core/workers/pixelCompareWorker.ts @@ -390,12 +390,7 @@ self.addEventListener( let compDoc: PDFDocumentProxy | null = null; try { - // `CanvasFactory`/`FilterFactory` are supported at runtime but not declared on legacy types. - // pdfjs-dist 5.x renamed the option to `CanvasFactory` (capital C); without a FilterFactory - // the default DOMFilterFactory crashes in a worker calling document.createElementNS. - // Resolve CMap/standard-font URLs against the worker's own origin. Vite copies these - // directories from pdfjs-dist at build time via viteStaticCopy (see vite.config.ts). - // import.meta.env.BASE_URL ends with a slash and includes any subpath prefix. + // Resolve pdfjs CMap/standard-font URLs against the worker's origin + subpath. const assetsBase = new URL( `${import.meta.env.BASE_URL}pdfjs/`, self.location.origin, diff --git a/frontend/editor/src/proprietary/services/apiClientSetup.ts b/frontend/editor/src/proprietary/services/apiClientSetup.ts index 02708a4a5..18b13861d 100644 --- a/frontend/editor/src/proprietary/services/apiClientSetup.ts +++ b/frontend/editor/src/proprietary/services/apiClientSetup.ts @@ -100,10 +100,7 @@ async function refreshAuthToken(client: AxiosInstance): Promise { } } -/** - * Auth headers for raw fetch() calls (SSE streams, etc.). - * Async to match the SaaS override, which has to await the Supabase session. - */ +/** Auth headers for raw fetch() calls (SSE streams). Async to match SaaS override. */ export async function getAuthHeaders(): Promise> { const headers: Record = {}; const jwt = getJwtTokenFromStorage(); diff --git a/frontend/editor/src/saas/services/apiClientSetup.ts b/frontend/editor/src/saas/services/apiClientSetup.ts index e84fb6bb9..eaed3ec19 100644 --- a/frontend/editor/src/saas/services/apiClientSetup.ts +++ b/frontend/editor/src/saas/services/apiClientSetup.ts @@ -1,20 +1,7 @@ import type { AxiosInstance } from "axios"; import { supabase } from "@app/auth/supabase"; -/** - * SaaS auth headers for raw fetch() calls (e.g. AI chat streaming). - * - * Pulls the live Supabase access token. Required because the SaaS apiClient's - * axios interceptor attaches this header to every axios call, but raw fetch() - * calls bypass that path and end up with no Authorization header → backend - * returns 401. The chat streaming endpoint uses fetch() (not axios) because - * axios doesn't stream SSE responses well, so this override exists to give - * it the same bearer token the axios calls already get. - * - * supabase.auth.getSession() reads from in-memory cache when possible; only - * issues a network request if the session needs refreshing. Adds an Accept - * header so the backend negotiates JSON correctly. - */ +/** SaaS auth headers for raw fetch() calls — Supabase access token from current session. */ export async function getAuthHeaders(): Promise> { const headers: Record = {}; try { @@ -30,12 +17,5 @@ export async function getAuthHeaders(): Promise> { return headers; } -/** - * SaaS apiClient wires up its own interceptors inline (see saas/services/apiClient.ts). - * This re-export exists so the cascade through @app/services/apiClientSetup - * remains consistent for callers that import setupApiInterceptors — currently - * none in SaaS mode, but keeps the shape uniform. - */ -export function setupApiInterceptors(_client: AxiosInstance): void { - // No-op: SaaS apiClient handles its own interceptors with the Supabase session. -} +/** No-op: SaaS apiClient wires its own interceptors (see saas/services/apiClient.ts). */ +export function setupApiInterceptors(_client: AxiosInstance): void {} diff --git a/frontend/editor/src/saas/utils/pathUtils.ts b/frontend/editor/src/saas/utils/pathUtils.ts index d746fff5b..1055df746 100644 --- a/frontend/editor/src/saas/utils/pathUtils.ts +++ b/frontend/editor/src/saas/utils/pathUtils.ts @@ -1,8 +1,7 @@ import { URL_TO_TOOL_MAP } from "@app/utils/urlMapping"; import { BASE_PATH } from "@app/constants/app"; -// BASE_PATH is "/bpp" or "" (no trailing slash). Strip the leading slash -// to match the legacy SUBPATH shape used below ("bpp" or ""). +// "bpp" or "" — BASE_PATH without leading slash. const SUBPATH = BASE_PATH.replace(/^\//, ""); /**