131 lines
3.3 KiB
JavaScript
131 lines
3.3 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import os from 'os';
|
|
import yaml from 'js-yaml';
|
|
import crypto from 'crypto';
|
|
|
|
const BASE_DIR = path.join(os.homedir(), 'WaifuBoards');
|
|
const CONFIG_PATH = path.join(BASE_DIR, 'config.yaml');
|
|
|
|
const DEFAULT_CONFIG = {
|
|
library: {
|
|
anime: null,
|
|
manga: null,
|
|
novels: null
|
|
},
|
|
paths: {
|
|
mpv: null,
|
|
ffmpeg: null,
|
|
ffprobe: null,
|
|
cloudflared: null,
|
|
},
|
|
server: {
|
|
jwt_secret: null
|
|
}
|
|
};
|
|
|
|
export const CONFIG_SCHEMA = {
|
|
library: {
|
|
anime: { description: "Path where anime is stored" },
|
|
manga: { description: "Path where manga is stored" },
|
|
novels: { description: "Path where novels are stored" }
|
|
},
|
|
paths: {
|
|
mpv: { description: "Required to open anime episodes in mpv on desktop version." },
|
|
ffmpeg: { description: "Required for downloading anime episodes." },
|
|
ffprobe: { description: "Required for watching local anime episodes." },
|
|
cloudflared: { description: "Required for creating pubic rooms." }
|
|
}
|
|
};
|
|
|
|
function ensureConfigFile() {
|
|
if (!fs.existsSync(BASE_DIR)) {
|
|
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
}
|
|
|
|
let configExists = fs.existsSync(CONFIG_PATH);
|
|
|
|
if (!configExists) {
|
|
fs.writeFileSync(
|
|
CONFIG_PATH,
|
|
yaml.dump(DEFAULT_CONFIG),
|
|
'utf8'
|
|
);
|
|
}
|
|
|
|
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
const loaded = yaml.load(raw) || {};
|
|
|
|
if (!loaded.server) loaded.server = {};
|
|
if (!loaded.server.jwt_secret) {
|
|
loaded.server.jwt_secret = crypto.randomBytes(32).toString('hex');
|
|
fs.writeFileSync(CONFIG_PATH, yaml.dump(deepMerge(structuredClone(DEFAULT_CONFIG), loaded)), 'utf8');
|
|
}
|
|
}
|
|
|
|
export function getPublicConfig() {
|
|
const { values } = getConfig();
|
|
const publicConfig = structuredClone(values);
|
|
if (publicConfig.server) delete publicConfig.server.jwt_secret;
|
|
return publicConfig;
|
|
}
|
|
|
|
export function getConfig() {
|
|
ensureConfigFile();
|
|
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
const loaded = yaml.load(raw) || {};
|
|
|
|
return {
|
|
values: deepMerge(structuredClone(DEFAULT_CONFIG), loaded),
|
|
schema: CONFIG_SCHEMA
|
|
};
|
|
}
|
|
|
|
function sanitizeLoadedConfig(loaded) {
|
|
return {
|
|
library: loaded.library,
|
|
paths: loaded.paths
|
|
};
|
|
}
|
|
|
|
export function setConfig(partialConfig) {
|
|
ensureConfigFile();
|
|
|
|
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
const loadedRaw = yaml.load(raw) || {};
|
|
const loaded = sanitizeLoadedConfig(loadedRaw);
|
|
|
|
const next = deepMerge(
|
|
structuredClone(DEFAULT_CONFIG),
|
|
deepMerge(loaded, partialConfig)
|
|
);
|
|
|
|
const toSave = {
|
|
library: next.library,
|
|
paths: next.paths
|
|
};
|
|
|
|
fs.writeFileSync(CONFIG_PATH, yaml.dump(toSave), 'utf8');
|
|
return next;
|
|
}
|
|
|
|
function deepMerge(target, source) {
|
|
for (const key in source) {
|
|
if (
|
|
source[key] &&
|
|
typeof source[key] === 'object' &&
|
|
!Array.isArray(source[key])
|
|
) {
|
|
target[key] = deepMerge(target[key] || {}, source[key]);
|
|
} else {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
|
|
module.exports = {
|
|
ensureConfigFile,
|
|
getConfig,
|
|
setConfig,
|
|
}; |