Includes homelab deploy tooling, image position/zoom manifest persistence, and full trading/casino stack. Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.2 KiB
JavaScript
71 lines
2.2 KiB
JavaScript
import bcrypt from 'bcryptjs';
|
|
import jwt from 'jsonwebtoken';
|
|
import { config } from './config.js';
|
|
import { Users } from './db.js';
|
|
|
|
const COOKIE = 'pb_token';
|
|
|
|
export function signToken(user) {
|
|
return jwt.sign({ id: user.id, username: user.username }, config.JWT_SECRET, { expiresIn: '30d' });
|
|
}
|
|
|
|
export function setAuthCookie(res, token) {
|
|
res.cookie(COOKIE, token, {
|
|
httpOnly: true,
|
|
sameSite: 'lax',
|
|
maxAge: 30 * 24 * 60 * 60 * 1000,
|
|
});
|
|
}
|
|
|
|
export function clearAuthCookie(res) {
|
|
res.clearCookie(COOKIE);
|
|
}
|
|
|
|
function readToken(req) {
|
|
const c = req.cookies?.[COOKIE];
|
|
if (c) return c;
|
|
const h = req.headers.authorization;
|
|
if (h && h.startsWith('Bearer ')) return h.slice(7);
|
|
return null;
|
|
}
|
|
|
|
// Haengt req.user an, wenn eingeloggt (sonst null). Blockiert nicht.
|
|
export function optionalAuth(req, _res, next) {
|
|
req.user = null;
|
|
const token = readToken(req);
|
|
if (token) {
|
|
try {
|
|
const decoded = jwt.verify(token, config.JWT_SECRET);
|
|
const u = Users.byId(decoded.id);
|
|
if (u) req.user = u;
|
|
} catch { /* ungueltig -> Gast */ }
|
|
}
|
|
next();
|
|
}
|
|
|
|
// Erzwingt Login (fuer Gamble & Trades).
|
|
export function requireAuth(req, res, next) {
|
|
if (!req.user) return res.status(401).json({ error: 'Login erforderlich' });
|
|
next();
|
|
}
|
|
|
|
export async function registerUser(username, password) {
|
|
username = String(username || '').trim();
|
|
if (username.length < 3 || username.length > 20) throw new Error('Username: 3-20 Zeichen');
|
|
if (!/^[a-zA-Z0-9_]+$/.test(username)) throw new Error('Nur Buchstaben, Zahlen, _');
|
|
if (String(password || '').length < 4) throw new Error('Passwort: min. 4 Zeichen');
|
|
if (Users.byName(username)) throw new Error('Username bereits vergeben');
|
|
const hash = await bcrypt.hash(password, 10);
|
|
return Users.create(username, hash, config.START_BALANCE);
|
|
}
|
|
|
|
export async function loginUser(username, password) {
|
|
const u = Users.byName(String(username || '').trim());
|
|
if (!u) throw new Error('Falscher Username oder Passwort');
|
|
const ok = await bcrypt.compare(String(password || ''), u.password_hash);
|
|
if (!ok) throw new Error('Falscher Username oder Passwort');
|
|
return u;
|
|
}
|
|
|
|
export { COOKIE };
|