routes/setupRoutes.ts
Rama main · réplica de lectura del repositorio original.
import express from 'express';
import User from '../models/User';
import Collection from '../models/Collection';
import { generateUniqueSlug } from '../utils/collectionHelpers';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { normalizeLanguage } from '../config/constants';
const router = express.Router();
const maxAge = 3 * 24 * 60 * 60;
const createToken = (id: any) => {
const passjwt = process.env.PASSJWT;
if (!passjwt) {
throw new Error('Missing PASSJWT environment variable');
}
return jwt.sign({ id }, passjwt, {
expiresIn: maxAge
});
}
// GET /setup - render initial setup page if no users exist
router.get('/', async (req, res) => {
const count = await User.countDocuments();
if (count > 0) return res.redirect('/login'); // safety: only allow setup when DB empty
res.render('setup', { error: null });
});
// POST /setup - create initial admin user and issue a JWT
router.post('/', async (req, res) => {
const count = await User.countDocuments();
if (count > 0) return res.redirect('/login'); // safety: only allow setup when DB empty
try {
const { username, email, password, language } = req.body;
if (!username || !email || !password) {
return res.render('setup', { error: req.t('errors.setup_error') });
}
// Backdate lastChange so the freshly issued JWT's second-granularity iat
// is not rejected by authMiddleware's staleness check
const lastChange = new Date(Date.now() - 60_000);
// Create the initial admin user (isAdmin: true)
const hashedPassword = await bcrypt.hash(password, 10);
const newAdmin = await User.create({
username,
email,
password: password,
isAdmin: true,
// The form posts whatever the page was rendered in, and the field is client
// side: normalized rather than trusted, or the enum rejects the whole document
// and the instance cannot be installed at all.
language: normalizeLanguage(language || req.language),
lastChange
});
// Force-update the stored password hash
await User.updateOne({ _id: newAdmin._id }, { $set: { password: hashedPassword } });
// Create the default collection for the first admin (migrate.ts can't have done this
// at boot: there were zero users then). The admin becomes its first member/owner.
const collection = await Collection.create({
name: 'Vinyl',
slug: await generateUniqueSlug('Vinyl'),
createdBy: newAdmin._id,
isDefault: true,
shelvesSeeded: true,
members: [{ user: newAdmin._id, role: 'admin' }]
});
await User.updateOne(
{ _id: newAdmin._id },
{ $set: { lastActiveCollectionId: collection._id } }
);
// Issue JWT and set cookie
const token = createToken(newAdmin._id);
res.cookie('jwt', token, {
httpOnly: true,
maxAge: 3 * 24 * 60 * 60 * 1000,
secure: process.env.PROD === 'true', // Only send cookie over HTTPS in production
sameSite: 'lax' // Mitigate CSRF
});
res.redirect('/');
} catch (err) {
console.error(err);
res.render('setup', { error: req.t('errors.setup_error') });
}
});
export = router;