158 lines
4.8 KiB
TypeScript
158 lines
4.8 KiB
TypeScript
import * as p from "@clack/prompts";
|
|
import type { DatabaseConfig } from "../config/schema.js";
|
|
import {
|
|
resolveDefaultBackupDir,
|
|
resolveDefaultEmbeddedPostgresDir,
|
|
resolvePaperclipInstanceId,
|
|
} from "../config/home.js";
|
|
|
|
export async function promptDatabase(current?: DatabaseConfig): Promise<DatabaseConfig> {
|
|
const instanceId = resolvePaperclipInstanceId();
|
|
const defaultEmbeddedDir = resolveDefaultEmbeddedPostgresDir(instanceId);
|
|
const defaultBackupDir = resolveDefaultBackupDir(instanceId);
|
|
const base: DatabaseConfig = current ?? {
|
|
mode: "embedded-postgres",
|
|
embeddedPostgresDataDir: defaultEmbeddedDir,
|
|
embeddedPostgresPort: 54329,
|
|
backup: {
|
|
enabled: true,
|
|
intervalMinutes: 60,
|
|
retentionDays: 30,
|
|
dir: defaultBackupDir,
|
|
},
|
|
};
|
|
|
|
const mode = await p.select({
|
|
message: "Database mode",
|
|
options: [
|
|
{ value: "embedded-postgres" as const, label: "Embedded PostgreSQL (managed locally)", hint: "recommended" },
|
|
{ value: "postgres" as const, label: "PostgreSQL (external server)" },
|
|
],
|
|
initialValue: base.mode,
|
|
});
|
|
|
|
if (p.isCancel(mode)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
let connectionString: string | undefined = base.connectionString;
|
|
let embeddedPostgresDataDir = base.embeddedPostgresDataDir || defaultEmbeddedDir;
|
|
let embeddedPostgresPort = base.embeddedPostgresPort || 54329;
|
|
|
|
if (mode === "postgres") {
|
|
const value = await p.text({
|
|
message: "PostgreSQL connection string",
|
|
defaultValue: base.connectionString ?? "",
|
|
placeholder: "postgres://user:pass@localhost:5432/paperclip",
|
|
validate: (val) => {
|
|
if (!val) return "Connection string is required for PostgreSQL mode";
|
|
if (!val.startsWith("postgres")) return "Must be a postgres:// or postgresql:// URL";
|
|
},
|
|
});
|
|
|
|
if (p.isCancel(value)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
connectionString = value;
|
|
} else {
|
|
const dataDir = await p.text({
|
|
message: "Embedded PostgreSQL data directory",
|
|
defaultValue: base.embeddedPostgresDataDir || defaultEmbeddedDir,
|
|
placeholder: defaultEmbeddedDir,
|
|
});
|
|
|
|
if (p.isCancel(dataDir)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
embeddedPostgresDataDir = dataDir || defaultEmbeddedDir;
|
|
|
|
const portValue = await p.text({
|
|
message: "Embedded PostgreSQL port",
|
|
defaultValue: String(base.embeddedPostgresPort || 54329),
|
|
placeholder: "54329",
|
|
validate: (val) => {
|
|
const n = Number(val);
|
|
if (!Number.isInteger(n) || n < 1 || n > 65535) return "Port must be an integer between 1 and 65535";
|
|
},
|
|
});
|
|
|
|
if (p.isCancel(portValue)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
embeddedPostgresPort = Number(portValue || "54329");
|
|
connectionString = undefined;
|
|
}
|
|
|
|
const backupEnabled = await p.confirm({
|
|
message: "Enable automatic database backups?",
|
|
initialValue: base.backup.enabled,
|
|
});
|
|
if (p.isCancel(backupEnabled)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const backupDirInput = await p.text({
|
|
message: "Backup directory",
|
|
defaultValue: base.backup.dir || defaultBackupDir,
|
|
placeholder: defaultBackupDir,
|
|
validate: (val) => (!val || val.trim().length === 0 ? "Backup directory is required" : undefined),
|
|
});
|
|
if (p.isCancel(backupDirInput)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const backupIntervalInput = await p.text({
|
|
message: "Backup interval (minutes)",
|
|
defaultValue: String(base.backup.intervalMinutes || 60),
|
|
placeholder: "60",
|
|
validate: (val) => {
|
|
const n = Number(val);
|
|
if (!Number.isInteger(n) || n < 1) return "Interval must be a positive integer";
|
|
if (n > 10080) return "Interval must be 10080 minutes (7 days) or less";
|
|
return undefined;
|
|
},
|
|
});
|
|
if (p.isCancel(backupIntervalInput)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const backupRetentionInput = await p.text({
|
|
message: "Backup retention (days)",
|
|
defaultValue: String(base.backup.retentionDays || 30),
|
|
placeholder: "30",
|
|
validate: (val) => {
|
|
const n = Number(val);
|
|
if (!Number.isInteger(n) || n < 1) return "Retention must be a positive integer";
|
|
if (n > 3650) return "Retention must be 3650 days or less";
|
|
return undefined;
|
|
},
|
|
});
|
|
if (p.isCancel(backupRetentionInput)) {
|
|
p.cancel("Setup cancelled.");
|
|
process.exit(0);
|
|
}
|
|
|
|
return {
|
|
mode,
|
|
connectionString,
|
|
embeddedPostgresDataDir,
|
|
embeddedPostgresPort,
|
|
backup: {
|
|
enabled: backupEnabled,
|
|
intervalMinutes: Number(backupIntervalInput || "60"),
|
|
retentionDays: Number(backupRetentionInput || "30"),
|
|
dir: backupDirInput || defaultBackupDir,
|
|
},
|
|
};
|
|
}
|