- Full-stack: React 18 + Express + SQLite - Drag-and-drop kanban boards with @hello-pangea/dnd - Google App Password email integration (SMTP + IMAP) - Inbound email: create cards by sending emails - Reply-to-card: email replies become comments - Admin/user management with role-based access - Setup wizard: email config → admin creation - Checklists, time tracking, priorities, labels, due dates - Real-time notifications with activity feed - Beautiful HTML email templates
35 lines
1.1 KiB
JavaScript
35 lines
1.1 KiB
JavaScript
import jwt from 'jsonwebtoken';
|
|
import db from '../db.js';
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'teamflow-secret-change-in-production';
|
|
|
|
export function generateToken(user) {
|
|
return jwt.sign(
|
|
{ id: user.id, email: user.email, role: user.role },
|
|
JWT_SECRET,
|
|
{ expiresIn: '7d' }
|
|
);
|
|
}
|
|
|
|
export function authMiddleware(req, res, next) {
|
|
const header = req.headers.authorization;
|
|
if (!header || !header.startsWith('Bearer ')) {
|
|
return res.status(401).json({ error: 'No token provided' });
|
|
}
|
|
try {
|
|
const token = header.split(' ')[1];
|
|
const payload = jwt.verify(token, JWT_SECRET);
|
|
const user = db.prepare('SELECT id, email, name, avatar_color, role, is_active FROM users WHERE id = ?').get(payload.id);
|
|
if (!user || !user.is_active) return res.status(401).json({ error: 'Invalid token' });
|
|
req.user = user;
|
|
next();
|
|
} catch {
|
|
return res.status(401).json({ error: 'Invalid token' });
|
|
}
|
|
}
|
|
|
|
export function adminOnly(req, res, next) {
|
|
if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin access required' });
|
|
next();
|
|
}
|