jwt secret is now autogenerated

This commit is contained in:
2026-01-07 19:07:47 +01:00
parent 82ddc6d5e9
commit c225a9f48d
11 changed files with 101 additions and 25 deletions

View File

@@ -35,13 +35,17 @@ const configRoutes = require("./electron/api/config/config.routes");
const roomRoutes = require("./electron/api/rooms/rooms.routes");
const { setupRoomWebSocket } = require("./electron/api/rooms/rooms.websocket");
fastify.addHook("preHandler", async (request) => {
const { getConfig } = require('./electron/shared/config');
const { values } = getConfig();
const jwtSecret = values.server?.jwt_secret;
fastify.addHook("preHandler", async (request, reply) => {
const auth = request.headers.authorization;
if (!auth) return;
try {
const token = auth.replace("Bearer ", "");
request.user = jwt.verify(token, process.env.JWT_SECRET);
request.user = jwt.verify(token, jwtSecret);
} catch (e) {
return reply.code(401).send({ error: "Invalid token" });
}

View File

@@ -118,7 +118,7 @@ export async function getWatchStream(req: WatchStreamRequest, reply: FastifyRepl
export async function openInMPV(req: any, reply: any) {
try {
const { title, video, subtitles = [], chapters = [], animeId, episode, entrySource, token } = req.body;
const { title, video, subtitles = [], chapters = [], animeId, episode, entrySource } = req.body;
if (!video?.url) return { error: 'Missing video url' };
@@ -250,10 +250,9 @@ export async function openInMPV(req: any, reply: any) {
};
const updateProgress = async () => {
if (!token || progressUpdated) return;
if (!req.user || progressUpdated) return;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any;
const userId = decoded.id;
const userId = req.user.id;
await upsertListEntry({
user_id: userId,
entry_id: animeId,
@@ -263,6 +262,7 @@ export async function openInMPV(req: any, reply: any) {
progress: episode
});
progressUpdated = true;
progressUpdated = true;
} catch (e) { console.error("[MPV] Progress update failed", e); }
};

View File

@@ -1,10 +1,16 @@
import { FastifyReply, FastifyRequest } from 'fastify';
import { getConfig, setConfig } from '../../shared/config';
function hideSecrets(values: any) {
const copy = structuredClone(values);
if (copy.server?.jwt_secret) delete copy.server.jwt_secret;
return copy;
}
export async function getFullConfig(req: FastifyRequest, reply: FastifyReply) {
try {
const { values, schema } = getConfig();
return { values, schema };
return { values: hideSecrets(values), schema };
} catch {
return { error: "Error loading config" };
}
@@ -22,7 +28,7 @@ export async function getConfigSection(
return { error: "Section not found" };
}
return { [section]: values[section] };
return { [section]: hideSecrets(values)[section] };
} catch {
return { error: "Error loading config section" };
}

View File

@@ -2,6 +2,10 @@ import { FastifyReply, FastifyRequest } from 'fastify';
import * as userService from './user.service';
import {queryOne} from '../../shared/database';
import jwt from "jsonwebtoken";
import { getConfig } from '../../shared/config';
const { values } = getConfig();
const jwtSecret = values.server?.jwt_secret;
interface UserIdParams { id: string; }
interface CreateUserBody {
@@ -75,7 +79,7 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
const token = jwt.sign(
{ id: userId },
process.env.JWT_SECRET!,
jwtSecret,
{ expiresIn: "7d" }
);

View File

@@ -1903,15 +1903,17 @@ const AnimePlayer = (function() {
chapters: _skipIntervals,
animeId: _animeId,
episode: _currentEpisode,
entrySource: _entrySource,
token: token
};
entrySource: _entrySource
};
try {
const res = await fetch('/api/watch/mpv', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(body),
});
if (res.ok) {

View File

@@ -2,6 +2,7 @@ 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');
@@ -17,6 +18,9 @@ const DEFAULT_CONFIG = {
ffmpeg: null,
ffprobe: null,
cloudflared: null,
},
server: {
jwt_secret: null
}
};
@@ -39,13 +43,31 @@ function ensureConfigFile() {
fs.mkdirSync(BASE_DIR, { recursive: true });
}
if (!fs.existsSync(CONFIG_PATH)) {
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() {

View File

@@ -29,13 +29,17 @@ const configRoutes = require("./dist/api/config/config.routes");
const roomRoutes = require("./dist/api/rooms/rooms.routes");
const { setupRoomWebSocket } = require("./dist/api/rooms/rooms.websocket");
const { getConfig } = require('./dist/shared/config');
const { values } = getConfig();
const jwtSecret = values.server?.jwt_secret;
fastify.addHook("preHandler", async (request, reply) => {
const auth = request.headers.authorization;
if (!auth) return;
try {
const token = auth.replace("Bearer ", "");
request.user = jwt.verify(token, process.env.JWT_SECRET);
request.user = jwt.verify(token, jwtSecret);
} catch (e) {
return reply.code(401).send({ error: "Invalid token" });
}

View File

@@ -1,10 +1,16 @@
import { FastifyReply, FastifyRequest } from 'fastify';
import { getConfig, setConfig } from '../../shared/config';
function hideSecrets(values: any) {
const copy = structuredClone(values);
if (copy.server?.jwt_secret) delete copy.server.jwt_secret;
return copy;
}
export async function getFullConfig(req: FastifyRequest, reply: FastifyReply) {
try {
const { values, schema } = getConfig();
return { values, schema };
return { values: hideSecrets(values), schema };
} catch {
return { error: "Error loading config" };
}
@@ -22,7 +28,7 @@ export async function getConfigSection(
return { error: "Section not found" };
}
return { [section]: values[section] };
return { [section]: hideSecrets(values)[section] };
} catch {
return { error: "Error loading config section" };
}

View File

@@ -2,6 +2,10 @@ import { FastifyReply, FastifyRequest } from 'fastify';
import * as userService from './user.service';
import {queryOne} from '../../shared/database';
import jwt from "jsonwebtoken";
import { getConfig } from '../../shared/config';
const { values } = getConfig();
const jwtSecret = values.server?.jwt_secret;
interface UserIdParams { id: string; }
interface CreateUserBody {
@@ -75,7 +79,7 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
const token = jwt.sign(
{ id: userId },
process.env.JWT_SECRET!,
jwtSecret,
{ expiresIn: "7d" }
);

View File

@@ -1903,15 +1903,17 @@ const AnimePlayer = (function() {
chapters: _skipIntervals,
animeId: _animeId,
episode: _currentEpisode,
entrySource: _entrySource,
token: token
};
entrySource: _entrySource
};
try {
const res = await fetch('/api/watch/mpv', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(body),
});
if (res.ok) {

View File

@@ -2,6 +2,7 @@ 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');
@@ -17,6 +18,9 @@ const DEFAULT_CONFIG = {
ffmpeg: null,
ffprobe: null,
cloudflared: null,
},
server: {
jwt_secret: null
}
};
@@ -39,13 +43,31 @@ function ensureConfigFile() {
fs.mkdirSync(BASE_DIR, { recursive: true });
}
if (!fs.existsSync(CONFIG_PATH)) {
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() {