migration to postgres
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
import { settingsPath } from './server-settings';
|
||||
import { readServerSettings, writeServerSettings } from 'officerdb';
|
||||
import { readResourceConfig } from './resources';
|
||||
|
||||
type OcrConfig = {
|
||||
@@ -10,8 +10,8 @@ type OcrConfig = {
|
||||
export async function readOcrConfig(): Promise<OcrConfig | undefined> {
|
||||
const config = await readResourceConfig('optical-character-recognition');
|
||||
if (config.url) return { url: config.url, model: config.model ?? '' };
|
||||
// Fallback to legacy settings.json
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
// Fallback to legacy DB settings
|
||||
const settings = await readServerSettings();
|
||||
return settings.ocr as OcrConfig | undefined;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ ocrRouter.get('/', async (ctx) => {
|
||||
|
||||
ocrRouter.put('/', async (ctx) => {
|
||||
const body = await ctx.req.json<OcrConfig>();
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const settings = await readServerSettings();
|
||||
settings.ocr = body;
|
||||
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
||||
await writeServerSettings(settings);
|
||||
return ctx.json({ success: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
import { mkdir } from 'node:fs/promises';
|
||||
import { SERVER_CONFIG_DIR } from '@@/data-path';
|
||||
import { readdirSync, existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { readServerSettings, writeServerSettings } from 'officerdb';
|
||||
import { claudeCodeRouter } from './claude-code';
|
||||
import { opencodeRouter } from './opencode';
|
||||
import { piMonoRouter } from './pi-mono';
|
||||
@@ -14,14 +13,6 @@ import { sttRouter } from './stt';
|
||||
import { ocrRouter } from './ocr';
|
||||
import { searxngRouter } from './searxng';
|
||||
|
||||
export const settingsPath = `${SERVER_CONFIG_DIR}/server-settings.json`;
|
||||
|
||||
const settingsFile = Bun.file(settingsPath);
|
||||
if (!(await settingsFile.exists())) {
|
||||
await mkdir(SERVER_CONFIG_DIR, { recursive: true });
|
||||
await Bun.write(settingsPath, '{}');
|
||||
}
|
||||
|
||||
export const serverSettingsRouter = createRouter();
|
||||
|
||||
serverSettingsRouter.route('/claude-code', claudeCodeRouter);
|
||||
@@ -35,33 +26,29 @@ serverSettingsRouter.route('/stt', sttRouter);
|
||||
serverSettingsRouter.route('/ocr', ocrRouter);
|
||||
serverSettingsRouter.route('/searxng', searxngRouter);
|
||||
|
||||
export const readSettings = async () => {
|
||||
try { return await Bun.file(settingsPath).json(); } catch { return {}; }
|
||||
};
|
||||
export { readServerSettings as readSettings };
|
||||
|
||||
serverSettingsRouter.get('/settings', async (ctx) => {
|
||||
return ctx.json(await readSettings());
|
||||
return ctx.json(await readServerSettings());
|
||||
});
|
||||
|
||||
serverSettingsRouter.get('/onboarding-complete', async (ctx) => {
|
||||
const settings = await readSettings();
|
||||
const settings = await readServerSettings();
|
||||
return ctx.json({ onboardingComplete: !!settings.onboardingComplete });
|
||||
});
|
||||
|
||||
serverSettingsRouter.put('/', async (ctx) => {
|
||||
const body = await ctx.req.json();
|
||||
const settings = await readSettings();
|
||||
const settings = await readServerSettings();
|
||||
const updated = { ...settings, ...body };
|
||||
await Bun.write(settingsPath, JSON.stringify(updated, null, 2));
|
||||
await writeServerSettings(updated);
|
||||
return ctx.json(updated);
|
||||
});
|
||||
|
||||
serverSettingsRouter.get('/plugins', async (ctx) => {
|
||||
const pluginsDir = join(import.meta.dir, '../../../workspaces/plugins');
|
||||
const settings = await Bun.file(settingsPath)
|
||||
.json()
|
||||
.catch(() => ({}));
|
||||
const pluginSettings: Record<string, boolean> = settings.plugins ?? {};
|
||||
const settings = await readServerSettings();
|
||||
const pluginSettings: Record<string, boolean> = (settings.plugins as Record<string, boolean>) ?? {};
|
||||
|
||||
const plugins: { id: string; name: string; description: string; enabled: boolean }[] = [];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { createRouter } from '../../create-router';
|
||||
import { settingsPath } from './server-settings';
|
||||
import { readServerSettings, writeServerSettings } from 'officerdb';
|
||||
import { getTransport } from 'emailer';
|
||||
|
||||
type SmtpConfig = {
|
||||
@@ -46,24 +46,24 @@ function buildTransportUrl(config: SmtpConfig): string {
|
||||
export const smtpRouter = createRouter();
|
||||
|
||||
smtpRouter.get('/', async (ctx) => {
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const smtp: SmtpConfig | undefined = settings.smtp;
|
||||
const settings = await readServerSettings();
|
||||
const smtp: SmtpConfig | undefined = settings.smtp as SmtpConfig | undefined;
|
||||
if (smtp) return ctx.json(serializeConfig(smtp));
|
||||
return ctx.json(null);
|
||||
});
|
||||
|
||||
smtpRouter.put('/', async (ctx) => {
|
||||
const body = await ctx.req.json<SmtpConfig>();
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const settings = await readServerSettings();
|
||||
|
||||
const existing: SmtpConfig | undefined = settings.smtp;
|
||||
const existing: SmtpConfig | undefined = settings.smtp as SmtpConfig | undefined;
|
||||
if (existing) {
|
||||
if (body.apiKey && body.apiKey.includes('****')) body.apiKey = existing.apiKey;
|
||||
if (body.password && body.password.includes('****')) body.password = existing.password;
|
||||
}
|
||||
|
||||
settings.smtp = body;
|
||||
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
||||
await writeServerSettings(settings);
|
||||
return ctx.json({ success: true });
|
||||
});
|
||||
|
||||
@@ -95,8 +95,8 @@ smtpRouter.post('/test', async (ctx) => {
|
||||
if (!body.to) return ctx.json({ error: 'Recipient address required' }, 400);
|
||||
|
||||
// Resolve masked secrets from saved config
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const saved: SmtpConfig | undefined = settings.smtp;
|
||||
const settings = await readServerSettings();
|
||||
const saved: SmtpConfig | undefined = settings.smtp as SmtpConfig | undefined;
|
||||
if (saved) {
|
||||
if (body.apiKey?.includes('****')) body.apiKey = saved.apiKey;
|
||||
if (body.password?.includes('****')) body.password = saved.password;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
import { settingsPath } from './server-settings';
|
||||
import { readServerSettings, writeServerSettings } from 'officerdb';
|
||||
import { readResourceConfig } from './resources';
|
||||
|
||||
type SttConfig = {
|
||||
@@ -9,8 +9,8 @@ type SttConfig = {
|
||||
export async function readSttConfig(): Promise<SttConfig | undefined> {
|
||||
const config = await readResourceConfig('speech-to-text');
|
||||
if (config.url) return { url: config.url };
|
||||
// Fallback to legacy settings.json
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
// Fallback to legacy DB settings
|
||||
const settings = await readServerSettings();
|
||||
return settings.stt as SttConfig | undefined;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ sttRouter.get('/', async (ctx) => {
|
||||
|
||||
sttRouter.put('/', async (ctx) => {
|
||||
const body = await ctx.req.json<SttConfig>();
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const settings = await readServerSettings();
|
||||
settings.stt = body;
|
||||
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
||||
await writeServerSettings(settings);
|
||||
return ctx.json({ success: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ export async function syncUserPiConfig(email: string): Promise<void> {
|
||||
}
|
||||
|
||||
export async function syncAllUserPiConfigs(): Promise<void> {
|
||||
const users = getUsers();
|
||||
const users = await getUsers();
|
||||
if (users.length === 0) return;
|
||||
|
||||
const [appConfig, policy, apiKeys] = await Promise.all([
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
import { settingsPath } from './server-settings';
|
||||
import { readServerSettings, writeServerSettings } from 'officerdb';
|
||||
import { readResourceConfig } from './resources';
|
||||
|
||||
type TtsConfig = {
|
||||
@@ -18,8 +18,8 @@ function maskSecret(value: string | undefined): string | undefined {
|
||||
export async function readTtsConfig(): Promise<TtsConfig | undefined> {
|
||||
const config = await readResourceConfig('text-to-speech');
|
||||
if (!config.url && !config.provider) {
|
||||
// Fallback to legacy settings.json
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
// Fallback to legacy DB settings
|
||||
const settings = await readServerSettings();
|
||||
return settings.tts as TtsConfig | undefined;
|
||||
}
|
||||
return {
|
||||
@@ -41,15 +41,15 @@ ttsRouter.get('/', async (ctx) => {
|
||||
|
||||
ttsRouter.put('/', async (ctx) => {
|
||||
const body = await ctx.req.json<TtsConfig>();
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const settings = await readServerSettings();
|
||||
|
||||
const existing: TtsConfig | undefined = settings.tts;
|
||||
const existing: TtsConfig | undefined = settings.tts as TtsConfig | undefined;
|
||||
if (existing && body.apiKey?.includes('****')) {
|
||||
body.apiKey = existing.apiKey;
|
||||
}
|
||||
|
||||
settings.tts = body;
|
||||
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
||||
await writeServerSettings(settings);
|
||||
return ctx.json({ success: true });
|
||||
});
|
||||
|
||||
@@ -152,8 +152,8 @@ async function fetchHuggingFaceVoices(repoId: string): Promise<{ flat: string[];
|
||||
ttsRouter.post('/test', async (ctx) => {
|
||||
const body = await ctx.req.json<TtsConfig>();
|
||||
|
||||
const settings = await Bun.file(settingsPath).json().catch(() => ({}));
|
||||
const saved: TtsConfig | undefined = settings.tts;
|
||||
const settings = await readServerSettings();
|
||||
const saved: TtsConfig | undefined = settings.tts as TtsConfig | undefined;
|
||||
if (saved && body.apiKey?.includes('****')) {
|
||||
body.apiKey = saved.apiKey;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user