82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import { readFile, writeFile, mkdir, rename, unlink } from 'node:fs/promises';
|
|
import { existsSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { logger } from '@/lib/logger';
|
|
import { getConfigDir, assertWritable } from './paths';
|
|
|
|
function getPluginConfigDir(): string {
|
|
return path.join(getConfigDir(), 'plugin-config');
|
|
}
|
|
|
|
function configPath(pluginId: string): string {
|
|
return path.join(getPluginConfigDir(), `${pluginId}.json`);
|
|
}
|
|
|
|
async function ensureDir(dir: string): Promise<void> {
|
|
if (!existsSync(dir)) {
|
|
await mkdir(dir, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all config for a plugin.
|
|
*/
|
|
export async function getPluginConfig(pluginId: string): Promise<Record<string, unknown>> {
|
|
try {
|
|
const raw = await readFile(configPath(pluginId), 'utf-8');
|
|
return JSON.parse(raw);
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return {};
|
|
logger.warn(`Failed to read plugin config for ${pluginId}`, {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set a single config key for a plugin.
|
|
*/
|
|
export async function setPluginConfig(pluginId: string, key: string, value: unknown): Promise<void> {
|
|
assertWritable('update plugin config');
|
|
const dir = getPluginConfigDir();
|
|
await ensureDir(dir);
|
|
|
|
const config = await getPluginConfig(pluginId);
|
|
config[key] = value;
|
|
|
|
const filePath = configPath(pluginId);
|
|
const tmpPath = filePath + '.tmp';
|
|
await writeFile(tmpPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
await rename(tmpPath, filePath);
|
|
}
|
|
|
|
/**
|
|
* Delete a single config key for a plugin.
|
|
*/
|
|
export async function deletePluginConfigKey(pluginId: string, key: string): Promise<void> {
|
|
assertWritable('delete plugin config key');
|
|
const config = await getPluginConfig(pluginId);
|
|
delete config[key];
|
|
|
|
if (Object.keys(config).length === 0) {
|
|
try { await unlink(configPath(pluginId)); } catch { /* ok if missing */ }
|
|
return;
|
|
}
|
|
|
|
const dir = getPluginConfigDir();
|
|
await ensureDir(dir);
|
|
const filePath = configPath(pluginId);
|
|
const tmpPath = filePath + '.tmp';
|
|
await writeFile(tmpPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
await rename(tmpPath, filePath);
|
|
}
|
|
|
|
/**
|
|
* Delete all config for a plugin (used when uninstalling).
|
|
*/
|
|
export async function deleteAllPluginConfig(pluginId: string): Promise<void> {
|
|
assertWritable('delete plugin config');
|
|
try { await unlink(configPath(pluginId)); } catch { /* ok if missing */ }
|
|
}
|