- Replaced static hosts.json and staticConfig.ts with SQLite database (Prisma) - Implemented JWT authentication and Login UI - Added dynamic API routes for hosts, topology, and settings - Updated UI components to fetch and manage state dynamically - Added Settings interface for managing hosts and topology nodes
79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
import { Router } from 'express';
|
|
import bcrypt from 'bcrypt';
|
|
import jwt from 'jsonwebtoken';
|
|
import { PrismaClient } from '@prisma/client';
|
|
import rateLimit from 'express-rate-limit';
|
|
|
|
const router = Router();
|
|
const prisma = new PrismaClient();
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret-for-development-only';
|
|
|
|
const loginLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 5, // Limit each IP to 5 failed login attempts per window
|
|
message: { status: 'error', message: 'Too many login attempts, please try again later' },
|
|
skipSuccessfulRequests: true,
|
|
});
|
|
|
|
// Check if setup is required (no users exist)
|
|
router.get('/auth/status', async (req, res) => {
|
|
try {
|
|
const userCount = await prisma.user.count();
|
|
res.json({ setupRequired: userCount === 0 });
|
|
} catch (error) {
|
|
res.status(500).json({ status: 'error', message: 'Database error' });
|
|
}
|
|
});
|
|
|
|
// Initial Setup (Only works if no users exist)
|
|
router.post('/auth/setup', async (req, res) => {
|
|
try {
|
|
const userCount = await prisma.user.count();
|
|
if (userCount > 0) {
|
|
return res.status(403).json({ status: 'error', message: 'Setup has already been completed' });
|
|
}
|
|
|
|
const { username, password } = req.body;
|
|
if (!username || !password || password.length < 8) {
|
|
return res.status(400).json({ status: 'error', message: 'Invalid username or password must be at least 8 characters' });
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
const user = await prisma.user.create({
|
|
data: { username, password: hashedPassword },
|
|
});
|
|
|
|
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '7d' });
|
|
res.json({ status: 'success', token, user: { id: user.id, username: user.username } });
|
|
} catch (error) {
|
|
res.status(500).json({ status: 'error', message: 'Failed to create user' });
|
|
}
|
|
});
|
|
|
|
// Login
|
|
router.post('/auth/login', loginLimiter, async (req, res) => {
|
|
try {
|
|
const { username, password } = req.body;
|
|
if (!username || !password) {
|
|
return res.status(400).json({ status: 'error', message: 'Username and password required' });
|
|
}
|
|
|
|
const user = await prisma.user.findUnique({ where: { username } });
|
|
if (!user) {
|
|
return res.status(401).json({ status: 'error', message: 'Invalid credentials' });
|
|
}
|
|
|
|
const valid = await bcrypt.compare(password, user.password);
|
|
if (!valid) {
|
|
return res.status(401).json({ status: 'error', message: 'Invalid credentials' });
|
|
}
|
|
|
|
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '7d' });
|
|
res.json({ status: 'success', token, user: { id: user.id, username: user.username } });
|
|
} catch (error) {
|
|
res.status(500).json({ status: 'error', message: 'Login failed' });
|
|
}
|
|
});
|
|
|
|
export default router;
|