chore: remove internal docs from repo, update public documentation

Remove docs/superpowers/, .claude/ config, and PRD.md from version
control (kept locally via .gitignore). Update README, CHANGELOG,
VitePress docs, and .env.example to reflect recent features: Files
page, teams, admin settings, persistent storage, and various API
improvements.
This commit is contained in:
Siddharth Kumar Sah
2026-03-26 01:11:40 +08:00
parent 3b4f522bf4
commit 627ff8a82c
99 changed files with 853 additions and 15277 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import { mkdirSync } from "node:fs";
import { dirname } from "node:path";
import Database from "better-sqlite3";
import Database, { type Database as DatabaseType } from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
import { env } from "../config.js";
import * as schema from "./schema.js";
@@ -8,7 +8,7 @@ import * as schema from "./schema.js";
// Ensure data directory exists
mkdirSync(dirname(env.DB_PATH), { recursive: true });
const sqlite = new Database(env.DB_PATH);
const sqlite: DatabaseType = new Database(env.DB_PATH);
// Critical SQLite pragmas for reliability
sqlite.pragma("journal_mode = WAL");
+1 -1
View File
@@ -18,7 +18,7 @@ export function getMaxAgeMs(): number {
.get();
if (row) {
const hours = parseFloat(row.value);
if (!isNaN(hours) && hours > 0) return hours * 60 * 60 * 1000;
if (!Number.isNaN(hours) && hours > 0) return hours * 60 * 60 * 1000;
}
} catch {
/* DB not ready yet, use env */
+2 -2
View File
@@ -128,7 +128,7 @@ export async function ensureDefaultAdmin(): Promise<void> {
// ── Login attempt limit ──────────────────────────────────────────
const DEFAULT_LOGIN_ATTEMPT_LIMIT = 5;
const DEFAULT_LOGIN_ATTEMPT_LIMIT = 10;
function getLoginAttemptLimit(): number {
const row = db
@@ -378,7 +378,7 @@ export async function authRoutes(app: FastifyInstance): Promise<void> {
const teamExists = db
.select()
.from(schema.teams)
.where(eq(schema.teams.id, (body as { team?: string }).team!))
.where(eq(schema.teams.id, (body as { team?: string }).team ?? ""))
.get();
if (!teamExists)
return reply.status(400).send({ error: "Team not found", code: "VALIDATION_ERROR" });
+6 -1
View File
@@ -150,7 +150,12 @@ export async function registerPipelineRoutes(app: FastifyInstance): Promise<void
try {
for (let i = 0; i < pipeline.steps.length; i++) {
const step = pipeline.steps[i];
const toolConfig = getToolConfig(step.toolId)!;
const toolConfig = getToolConfig(step.toolId);
if (!toolConfig) {
return reply
.status(400)
.send({ error: `Step ${i + 1}: Tool "${step.toolId}" not found` });
}
// Parse settings through the schema to apply defaults
const settings = toolConfig.settingsSchema.parse(step.settings);
+2 -2
View File
@@ -57,7 +57,7 @@ export async function teamsRoutes(app: FastifyInstance): Promise<void> {
return reply.status(400).send({ error: nameError, code: "VALIDATION_ERROR" });
}
const trimmedName = (body!.name as string).trim();
const trimmedName = (body?.name ?? "").trim();
// Check for duplicate name (case-insensitive)
const existing = db
@@ -97,7 +97,7 @@ export async function teamsRoutes(app: FastifyInstance): Promise<void> {
return reply.status(400).send({ error: nameError, code: "VALIDATION_ERROR" });
}
const trimmedName = (body!.name as string).trim();
const trimmedName = (body?.name ?? "").trim();
// Check for duplicate name (case-insensitive), excluding current team
const duplicate = db
+15 -6
View File
@@ -24,18 +24,27 @@ export interface ToolRouteConfig<T> {
) => Promise<{ buffer: Buffer; filename: string; contentType: string }>;
}
/** Type-erased config stored in the registry (settings type is widened to avoid variance issues). */
export interface AnyToolRouteConfig {
toolId: string;
settingsSchema: z.ZodType<unknown, z.ZodTypeDef, unknown>;
process: (
inputBuffer: Buffer,
settings: unknown,
filename: string,
) => Promise<{ buffer: Buffer; filename: string; contentType: string }>;
}
/**
* In-memory registry of all tool configs, keyed by toolId.
* Populated by createToolRoute() calls; used by batch processing.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const toolRegistry = new Map<string, ToolRouteConfig<any>>();
const toolRegistry = new Map<string, AnyToolRouteConfig>();
/**
* Retrieve a registered tool config by its ID.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getToolConfig(toolId: string): ToolRouteConfig<any> | undefined {
export function getToolConfig(toolId: string): AnyToolRouteConfig | undefined {
return toolRegistry.get(toolId);
}
@@ -55,8 +64,8 @@ export function getToolConfig(toolId: string): ToolRouteConfig<any> | undefined
* - Response formatting
*/
export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig<T>): void {
// Register in the tool registry for batch processing
toolRegistry.set(config.toolId, config);
// Register in the tool registry for batch processing (cast to type-erased form)
toolRegistry.set(config.toolId, config as AnyToolRouteConfig);
app.post(
`/api/v1/tools/${config.toolId}`,
+3 -2
View File
@@ -60,10 +60,11 @@ export function registerBlurFaces(app: FastifyInstance) {
await writeFile(inputPath, fileBuffer);
// Process
const onProgress = clientJobId
const jobIdForProgress = clientJobId;
const onProgress = jobIdForProgress
? (percent: number, stage: string) => {
updateSingleFileProgress({
jobId: clientJobId!,
jobId: jobIdForProgress,
phase: "processing",
stage,
percent,
+3 -2
View File
@@ -71,10 +71,11 @@ export function registerEraseObject(app: FastifyInstance) {
await writeFile(inputPath, imageBuffer);
// Process
const onProgress = clientJobId
const jobIdForProgress = clientJobId;
const onProgress = jobIdForProgress
? (percent: number, stage: string) => {
updateSingleFileProgress({
jobId: clientJobId!,
jobId: jobIdForProgress,
phase: "processing",
stage,
percent,
+3 -2
View File
@@ -73,10 +73,11 @@ export function registerOcr(app: FastifyInstance) {
const jobId = randomUUID();
const workspacePath = await createWorkspace(jobId);
const onProgress = clientJobId
const jobIdForProgress = clientJobId;
const onProgress = jobIdForProgress
? (percent: number, stage: string) => {
updateSingleFileProgress({
jobId: clientJobId!,
jobId: jobIdForProgress,
phase: "processing",
stage,
percent,
@@ -62,10 +62,11 @@ export function registerRemoveBackground(app: FastifyInstance) {
await writeFile(inputPath, fileBuffer);
// Process
const onProgress = clientJobId
const jobIdForProgress = clientJobId;
const onProgress = jobIdForProgress
? (percent: number, stage: string) => {
updateSingleFileProgress({
jobId: clientJobId!,
jobId: jobIdForProgress,
phase: "processing",
stage,
percent,
+1 -3
View File
@@ -75,9 +75,7 @@ function parseXmp(xmpBuffer: Buffer): Record<string, string> {
const xml = xmpBuffer.toString("utf-8");
const result: Record<string, string> = {};
const attrRegex = /(\w+:\w+)="([^"]+)"/g;
let match;
while ((match = attrRegex.exec(xml)) !== null) {
for (const match of xml.matchAll(/(\w+:\w+)="([^"]+)"/g)) {
const key = match[1];
if (key.startsWith("xmlns:") || key.startsWith("rdf:")) continue;
result[key] = match[2];
+3 -2
View File
@@ -62,10 +62,11 @@ export function registerUpscale(app: FastifyInstance) {
await writeFile(inputPath, fileBuffer);
// Process
const onProgress = clientJobId
const jobIdForProgress = clientJobId;
const onProgress = jobIdForProgress
? (percent: number, stage: string) => {
updateSingleFileProgress({
jobId: clientJobId!,
jobId: jobIdForProgress,
phase: "processing",
stage,
percent,