jwt secret is now autogenerated
This commit is contained in:
@@ -35,13 +35,17 @@ const configRoutes = require("./electron/api/config/config.routes");
|
|||||||
const roomRoutes = require("./electron/api/rooms/rooms.routes");
|
const roomRoutes = require("./electron/api/rooms/rooms.routes");
|
||||||
const { setupRoomWebSocket } = require("./electron/api/rooms/rooms.websocket");
|
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;
|
const auth = request.headers.authorization;
|
||||||
if (!auth) return;
|
if (!auth) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = auth.replace("Bearer ", "");
|
const token = auth.replace("Bearer ", "");
|
||||||
request.user = jwt.verify(token, process.env.JWT_SECRET);
|
request.user = jwt.verify(token, jwtSecret);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reply.code(401).send({ error: "Invalid token" });
|
return reply.code(401).send({ error: "Invalid token" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export async function getWatchStream(req: WatchStreamRequest, reply: FastifyRepl
|
|||||||
|
|
||||||
export async function openInMPV(req: any, reply: any) {
|
export async function openInMPV(req: any, reply: any) {
|
||||||
try {
|
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' };
|
if (!video?.url) return { error: 'Missing video url' };
|
||||||
|
|
||||||
@@ -250,10 +250,9 @@ export async function openInMPV(req: any, reply: any) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateProgress = async () => {
|
const updateProgress = async () => {
|
||||||
if (!token || progressUpdated) return;
|
if (!req.user || progressUpdated) return;
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as any;
|
const userId = req.user.id;
|
||||||
const userId = decoded.id;
|
|
||||||
await upsertListEntry({
|
await upsertListEntry({
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
entry_id: animeId,
|
entry_id: animeId,
|
||||||
@@ -263,6 +262,7 @@ export async function openInMPV(req: any, reply: any) {
|
|||||||
progress: episode
|
progress: episode
|
||||||
});
|
});
|
||||||
progressUpdated = true;
|
progressUpdated = true;
|
||||||
|
progressUpdated = true;
|
||||||
} catch (e) { console.error("[MPV] Progress update failed", e); }
|
} catch (e) { console.error("[MPV] Progress update failed", e); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { getConfig, setConfig } from '../../shared/config';
|
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) {
|
export async function getFullConfig(req: FastifyRequest, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { values, schema } = getConfig();
|
const { values, schema } = getConfig();
|
||||||
return { values, schema };
|
return { values: hideSecrets(values), schema };
|
||||||
} catch {
|
} catch {
|
||||||
return { error: "Error loading config" };
|
return { error: "Error loading config" };
|
||||||
}
|
}
|
||||||
@@ -22,7 +28,7 @@ export async function getConfigSection(
|
|||||||
return { error: "Section not found" };
|
return { error: "Section not found" };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { [section]: values[section] };
|
return { [section]: hideSecrets(values)[section] };
|
||||||
} catch {
|
} catch {
|
||||||
return { error: "Error loading config section" };
|
return { error: "Error loading config section" };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { FastifyReply, FastifyRequest } from 'fastify';
|
|||||||
import * as userService from './user.service';
|
import * as userService from './user.service';
|
||||||
import {queryOne} from '../../shared/database';
|
import {queryOne} from '../../shared/database';
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
import { getConfig } from '../../shared/config';
|
||||||
|
const { values } = getConfig();
|
||||||
|
const jwtSecret = values.server?.jwt_secret;
|
||||||
|
|
||||||
|
|
||||||
interface UserIdParams { id: string; }
|
interface UserIdParams { id: string; }
|
||||||
interface CreateUserBody {
|
interface CreateUserBody {
|
||||||
@@ -75,7 +79,7 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{ id: userId },
|
{ id: userId },
|
||||||
process.env.JWT_SECRET!,
|
jwtSecret,
|
||||||
{ expiresIn: "7d" }
|
{ expiresIn: "7d" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1903,15 +1903,17 @@ const AnimePlayer = (function() {
|
|||||||
chapters: _skipIntervals,
|
chapters: _skipIntervals,
|
||||||
animeId: _animeId,
|
animeId: _animeId,
|
||||||
episode: _currentEpisode,
|
episode: _currentEpisode,
|
||||||
entrySource: _entrySource,
|
entrySource: _entrySource
|
||||||
token: token
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/watch/mpv', {
|
const res = await fetch('/api/watch/mpv', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {
|
||||||
body: JSON.stringify(body)
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const BASE_DIR = path.join(os.homedir(), 'WaifuBoards');
|
const BASE_DIR = path.join(os.homedir(), 'WaifuBoards');
|
||||||
const CONFIG_PATH = path.join(BASE_DIR, 'config.yaml');
|
const CONFIG_PATH = path.join(BASE_DIR, 'config.yaml');
|
||||||
@@ -17,6 +18,9 @@ const DEFAULT_CONFIG = {
|
|||||||
ffmpeg: null,
|
ffmpeg: null,
|
||||||
ffprobe: null,
|
ffprobe: null,
|
||||||
cloudflared: null,
|
cloudflared: null,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
jwt_secret: null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,13 +43,31 @@ function ensureConfigFile() {
|
|||||||
fs.mkdirSync(BASE_DIR, { recursive: true });
|
fs.mkdirSync(BASE_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(CONFIG_PATH)) {
|
let configExists = fs.existsSync(CONFIG_PATH);
|
||||||
|
|
||||||
|
if (!configExists) {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
CONFIG_PATH,
|
CONFIG_PATH,
|
||||||
yaml.dump(DEFAULT_CONFIG),
|
yaml.dump(DEFAULT_CONFIG),
|
||||||
'utf8'
|
'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() {
|
export function getConfig() {
|
||||||
|
|||||||
@@ -29,13 +29,17 @@ const configRoutes = require("./dist/api/config/config.routes");
|
|||||||
const roomRoutes = require("./dist/api/rooms/rooms.routes");
|
const roomRoutes = require("./dist/api/rooms/rooms.routes");
|
||||||
const { setupRoomWebSocket } = require("./dist/api/rooms/rooms.websocket");
|
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) => {
|
fastify.addHook("preHandler", async (request, reply) => {
|
||||||
const auth = request.headers.authorization;
|
const auth = request.headers.authorization;
|
||||||
if (!auth) return;
|
if (!auth) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = auth.replace("Bearer ", "");
|
const token = auth.replace("Bearer ", "");
|
||||||
request.user = jwt.verify(token, process.env.JWT_SECRET);
|
request.user = jwt.verify(token, jwtSecret);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reply.code(401).send({ error: "Invalid token" });
|
return reply.code(401).send({ error: "Invalid token" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
import { getConfig, setConfig } from '../../shared/config';
|
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) {
|
export async function getFullConfig(req: FastifyRequest, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const { values, schema } = getConfig();
|
const { values, schema } = getConfig();
|
||||||
return { values, schema };
|
return { values: hideSecrets(values), schema };
|
||||||
} catch {
|
} catch {
|
||||||
return { error: "Error loading config" };
|
return { error: "Error loading config" };
|
||||||
}
|
}
|
||||||
@@ -22,7 +28,7 @@ export async function getConfigSection(
|
|||||||
return { error: "Section not found" };
|
return { error: "Section not found" };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { [section]: values[section] };
|
return { [section]: hideSecrets(values)[section] };
|
||||||
} catch {
|
} catch {
|
||||||
return { error: "Error loading config section" };
|
return { error: "Error loading config section" };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { FastifyReply, FastifyRequest } from 'fastify';
|
|||||||
import * as userService from './user.service';
|
import * as userService from './user.service';
|
||||||
import {queryOne} from '../../shared/database';
|
import {queryOne} from '../../shared/database';
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
import { getConfig } from '../../shared/config';
|
||||||
|
const { values } = getConfig();
|
||||||
|
const jwtSecret = values.server?.jwt_secret;
|
||||||
|
|
||||||
|
|
||||||
interface UserIdParams { id: string; }
|
interface UserIdParams { id: string; }
|
||||||
interface CreateUserBody {
|
interface CreateUserBody {
|
||||||
@@ -75,7 +79,7 @@ export async function login(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
const token = jwt.sign(
|
const token = jwt.sign(
|
||||||
{ id: userId },
|
{ id: userId },
|
||||||
process.env.JWT_SECRET!,
|
jwtSecret,
|
||||||
{ expiresIn: "7d" }
|
{ expiresIn: "7d" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1903,15 +1903,17 @@ const AnimePlayer = (function() {
|
|||||||
chapters: _skipIntervals,
|
chapters: _skipIntervals,
|
||||||
animeId: _animeId,
|
animeId: _animeId,
|
||||||
episode: _currentEpisode,
|
episode: _currentEpisode,
|
||||||
entrySource: _entrySource,
|
entrySource: _entrySource
|
||||||
token: token
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/watch/mpv', {
|
const res = await fetch('/api/watch/mpv', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {
|
||||||
body: JSON.stringify(body)
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const BASE_DIR = path.join(os.homedir(), 'WaifuBoards');
|
const BASE_DIR = path.join(os.homedir(), 'WaifuBoards');
|
||||||
const CONFIG_PATH = path.join(BASE_DIR, 'config.yaml');
|
const CONFIG_PATH = path.join(BASE_DIR, 'config.yaml');
|
||||||
@@ -17,6 +18,9 @@ const DEFAULT_CONFIG = {
|
|||||||
ffmpeg: null,
|
ffmpeg: null,
|
||||||
ffprobe: null,
|
ffprobe: null,
|
||||||
cloudflared: null,
|
cloudflared: null,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
jwt_secret: null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,13 +43,31 @@ function ensureConfigFile() {
|
|||||||
fs.mkdirSync(BASE_DIR, { recursive: true });
|
fs.mkdirSync(BASE_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(CONFIG_PATH)) {
|
let configExists = fs.existsSync(CONFIG_PATH);
|
||||||
|
|
||||||
|
if (!configExists) {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
CONFIG_PATH,
|
CONFIG_PATH,
|
||||||
yaml.dump(DEFAULT_CONFIG),
|
yaml.dump(DEFAULT_CONFIG),
|
||||||
'utf8'
|
'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() {
|
export function getConfig() {
|
||||||
|
|||||||
Reference in New Issue
Block a user