Files
paperclip/packages/shared/src/config-schema.ts
Jason 3860812323 feat: add auth.disableSignUp config option
- Added disableSignUp to authConfigSchema in config-schema.ts
- Added authDisableSignUp to Config interface
- Added parsing from PAPERCLIP_AUTH_DISABLE_SIGN_UP env or config file
- Passed to better-auth emailAndPassword.disableSignUp

When true, blocks new user registrations on public instances.
Defaults to false (backward compatible).

Fixes #241
2026-03-08 09:38:32 +08:00

179 lines
6.1 KiB
TypeScript

import { z } from "zod";
import {
AUTH_BASE_URL_MODES,
DEPLOYMENT_EXPOSURES,
DEPLOYMENT_MODES,
SECRET_PROVIDERS,
STORAGE_PROVIDERS,
} from "./constants.js";
export const configMetaSchema = z.object({
version: z.literal(1),
updatedAt: z.string(),
source: z.enum(["onboard", "configure", "doctor"]),
});
export const llmConfigSchema = z.object({
provider: z.enum(["claude", "openai"]),
apiKey: z.string().optional(),
});
export const databaseBackupConfigSchema = z.object({
enabled: z.boolean().default(true),
intervalMinutes: z.number().int().min(1).max(7 * 24 * 60).default(60),
retentionDays: z.number().int().min(1).max(3650).default(30),
dir: z.string().default("~/.paperclip/instances/default/data/backups"),
});
export const databaseConfigSchema = z.object({
mode: z.enum(["embedded-postgres", "postgres"]).default("embedded-postgres"),
connectionString: z.string().optional(),
embeddedPostgresDataDir: z.string().default("~/.paperclip/instances/default/db"),
embeddedPostgresPort: z.number().int().min(1).max(65535).default(54329),
backup: databaseBackupConfigSchema.default({
enabled: true,
intervalMinutes: 60,
retentionDays: 30,
dir: "~/.paperclip/instances/default/data/backups",
}),
});
export const loggingConfigSchema = z.object({
mode: z.enum(["file", "cloud"]),
logDir: z.string().default("~/.paperclip/instances/default/logs"),
});
export const serverConfigSchema = z.object({
deploymentMode: z.enum(DEPLOYMENT_MODES).default("local_trusted"),
exposure: z.enum(DEPLOYMENT_EXPOSURES).default("private"),
host: z.string().default("127.0.0.1"),
port: z.number().int().min(1).max(65535).default(3100),
allowedHostnames: z.array(z.string().min(1)).default([]),
serveUi: z.boolean().default(true),
});
export const authConfigSchema = z.object({
baseUrlMode: z.enum(AUTH_BASE_URL_MODES).default("auto"),
publicBaseUrl: z.string().url().optional(),
disableSignUp: z.boolean().default(false),
});
export const storageLocalDiskConfigSchema = z.object({
baseDir: z.string().default("~/.paperclip/instances/default/data/storage"),
});
export const storageS3ConfigSchema = z.object({
bucket: z.string().min(1).default("paperclip"),
region: z.string().min(1).default("us-east-1"),
endpoint: z.string().optional(),
prefix: z.string().default(""),
forcePathStyle: z.boolean().default(false),
});
export const storageConfigSchema = z.object({
provider: z.enum(STORAGE_PROVIDERS).default("local_disk"),
localDisk: storageLocalDiskConfigSchema.default({
baseDir: "~/.paperclip/instances/default/data/storage",
}),
s3: storageS3ConfigSchema.default({
bucket: "paperclip",
region: "us-east-1",
prefix: "",
forcePathStyle: false,
}),
});
export const secretsLocalEncryptedConfigSchema = z.object({
keyFilePath: z.string().default("~/.paperclip/instances/default/secrets/master.key"),
});
export const secretsConfigSchema = z.object({
provider: z.enum(SECRET_PROVIDERS).default("local_encrypted"),
strictMode: z.boolean().default(false),
localEncrypted: secretsLocalEncryptedConfigSchema.default({
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
}),
});
export const paperclipConfigSchema = z
.object({
$meta: configMetaSchema,
llm: llmConfigSchema.optional(),
database: databaseConfigSchema,
logging: loggingConfigSchema,
server: serverConfigSchema,
auth: authConfigSchema.default({
baseUrlMode: "auto",
disableSignUp: false,
}),
storage: storageConfigSchema.default({
provider: "local_disk",
localDisk: {
baseDir: "~/.paperclip/instances/default/data/storage",
},
s3: {
bucket: "paperclip",
region: "us-east-1",
prefix: "",
forcePathStyle: false,
},
}),
secrets: secretsConfigSchema.default({
provider: "local_encrypted",
strictMode: false,
localEncrypted: {
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
},
}),
})
.superRefine((value, ctx) => {
if (value.server.deploymentMode === "local_trusted") {
if (value.server.exposure !== "private") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "server.exposure must be private when deploymentMode is local_trusted",
path: ["server", "exposure"],
});
}
return;
}
if (value.auth.baseUrlMode === "explicit" && !value.auth.publicBaseUrl) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "auth.publicBaseUrl is required when auth.baseUrlMode is explicit",
path: ["auth", "publicBaseUrl"],
});
}
if (value.server.exposure === "public" && value.auth.baseUrlMode !== "explicit") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "auth.baseUrlMode must be explicit when deploymentMode=authenticated and exposure=public",
path: ["auth", "baseUrlMode"],
});
}
if (value.server.exposure === "public" && !value.auth.publicBaseUrl) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "auth.publicBaseUrl is required when deploymentMode=authenticated and exposure=public",
path: ["auth", "publicBaseUrl"],
});
}
});
export type PaperclipConfig = z.infer<typeof paperclipConfigSchema>;
export type LlmConfig = z.infer<typeof llmConfigSchema>;
export type DatabaseConfig = z.infer<typeof databaseConfigSchema>;
export type LoggingConfig = z.infer<typeof loggingConfigSchema>;
export type ServerConfig = z.infer<typeof serverConfigSchema>;
export type StorageConfig = z.infer<typeof storageConfigSchema>;
export type StorageLocalDiskConfig = z.infer<typeof storageLocalDiskConfigSchema>;
export type StorageS3Config = z.infer<typeof storageS3ConfigSchema>;
export type SecretsConfig = z.infer<typeof secretsConfigSchema>;
export type SecretsLocalEncryptedConfig = z.infer<typeof secretsLocalEncryptedConfigSchema>;
export type AuthConfig = z.infer<typeof authConfigSchema>;
export type ConfigMeta = z.infer<typeof configMetaSchema>;
export type DatabaseBackupConfig = z.infer<typeof databaseBackupConfigSchema>;