Files
SkillMate/backend/full-backend-3005.js
Claude Project Manager 6b9b6d4f20 Initial commit
2025-09-20 21:31:04 +02:00

332 Zeilen
8.7 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const Database = require('better-sqlite3');
const bcryptjs = require('bcryptjs');
const CryptoJS = require('crypto-js');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3005;
// Environment variables
const FIELD_ENCRYPTION_KEY = process.env.FIELD_ENCRYPTION_KEY || 'dev_field_key_change_in_production_32chars_min!';
// Database setup
const dbPath = path.join(__dirname, 'skillmate.dev.db');
const db = new Database(dbPath);
console.log('Database path:', dbPath);
// Middleware
app.use(cors({
origin: ['http://localhost:5173', 'http://127.0.0.1:5173', 'http://localhost:3006', 'http://127.0.0.1:3006'],
credentials: true
}));
app.use(express.json());
// Encryption/Decryption functions
function encrypt(text) {
if (!text) return null;
return CryptoJS.AES.encrypt(text, FIELD_ENCRYPTION_KEY).toString();
}
function decrypt(encryptedText) {
if (!encryptedText) return null;
try {
const bytes = CryptoJS.AES.decrypt(encryptedText, FIELD_ENCRYPTION_KEY);
return bytes.toString(CryptoJS.enc.Utf8);
} catch (error) {
console.error('Decryption error:', error);
return null;
}
}
// Login endpoint
app.post('/api/auth/login', async (req, res) => {
console.log('Login attempt:', req.body);
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({
success: false,
error: { message: 'Email and password are required' }
});
}
try {
// Find user by email (encrypted)
const users = db.prepare('SELECT * FROM users WHERE is_active = 1').all();
let user = null;
// Check encrypted emails
for (const u of users) {
const decryptedEmail = decrypt(u.email);
if (decryptedEmail === username || u.email === username) {
user = u;
break;
}
}
if (!user) {
console.log('User not found:', username);
return res.status(401).json({
success: false,
error: { message: 'Invalid credentials' }
});
}
console.log('User found:', { id: user.id, username: user.username, role: user.role });
// Verify password
const isValidPassword = bcryptjs.compareSync(password, user.password);
console.log('Password valid:', isValidPassword);
if (!isValidPassword) {
return res.status(401).json({
success: false,
error: { message: 'Invalid credentials' }
});
}
// Decrypt sensitive fields
const decryptedUser = {
id: user.id,
username: user.username,
email: decrypt(user.email) || user.email,
role: user.role,
employee_id: user.employee_id,
is_active: user.is_active,
last_login: user.last_login,
created_at: user.created_at,
updated_at: user.updated_at
};
res.json({
success: true,
message: 'Login successful!',
user: decryptedUser
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({
success: false,
error: { message: 'Internal server error' }
});
}
});
// Get all users endpoint (for admin panel)
app.get('/api/users', (req, res) => {
console.log('Get users request');
try {
const users = db.prepare('SELECT id, username, email, role, employee_id, is_active, last_login, created_at, updated_at FROM users').all();
// Decrypt sensitive fields for each user
const decryptedUsers = users.map(user => ({
id: user.id,
username: user.username,
email: decrypt(user.email) || user.email,
role: user.role,
employee_id: user.employee_id,
is_active: user.is_active,
last_login: user.last_login,
created_at: user.created_at,
updated_at: user.updated_at
}));
console.log(`Returning ${decryptedUsers.length} users`);
res.json({
success: true,
users: decryptedUsers
});
} catch (error) {
console.error('Get users error:', error);
res.status(500).json({
success: false,
error: { message: 'Internal server error' }
});
}
});
// Get all employees endpoint
app.get('/api/employees', (req, res) => {
console.log('Get employees request');
try {
const employees = db.prepare('SELECT * FROM employees ORDER BY last_name, first_name').all();
// Decrypt sensitive fields for each employee
const decryptedEmployees = employees.map(emp => ({
...emp,
first_name: decrypt(emp.first_name) || emp.first_name,
last_name: decrypt(emp.last_name) || emp.last_name,
email: decrypt(emp.email) || emp.email,
phone: decrypt(emp.phone) || emp.phone,
mobile: decrypt(emp.mobile) || emp.mobile
}));
console.log(`Returning ${decryptedEmployees.length} employees`);
res.json({
success: true,
employees: decryptedEmployees
});
} catch (error) {
console.error('Get employees error:', error);
res.status(500).json({
success: false,
error: { message: 'Internal server error' }
});
}
});
// Create employee endpoint
app.post('/api/employees', (req, res) => {
console.log('Create employee request:', req.body);
const {
first_name,
last_name,
employee_number,
position,
department,
email,
phone,
mobile,
office,
availability = 'available',
clearance_level,
clearance_valid_until,
clearance_issued_date
} = req.body;
if (!first_name || !last_name || !employee_number || !position || !department || !email || !phone) {
return res.status(400).json({
success: false,
error: { message: 'Required fields missing' }
});
}
try {
const crypto = require('crypto');
const employeeId = crypto.randomUUID();
const now = new Date().toISOString();
// Encrypt sensitive fields
const encryptedEmployee = {
id: employeeId,
first_name: encrypt(first_name),
last_name: encrypt(last_name),
employee_number,
photo: null,
position,
department,
email: encrypt(email),
phone: encrypt(phone),
mobile: mobile ? encrypt(mobile) : null,
office,
availability,
clearance_level,
clearance_valid_until,
clearance_issued_date,
created_at: now,
updated_at: now,
created_by: 'system', // TODO: use actual user ID
updated_by: null
};
const insertStmt = db.prepare(`
INSERT INTO employees (
id, first_name, last_name, employee_number, photo, position, department,
email, phone, mobile, office, availability, clearance_level,
clearance_valid_until, clearance_issued_date, created_at, updated_at,
created_by, updated_by
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
insertStmt.run(
encryptedEmployee.id,
encryptedEmployee.first_name,
encryptedEmployee.last_name,
encryptedEmployee.employee_number,
encryptedEmployee.photo,
encryptedEmployee.position,
encryptedEmployee.department,
encryptedEmployee.email,
encryptedEmployee.phone,
encryptedEmployee.mobile,
encryptedEmployee.office,
encryptedEmployee.availability,
encryptedEmployee.clearance_level,
encryptedEmployee.clearance_valid_until,
encryptedEmployee.clearance_issued_date,
encryptedEmployee.created_at,
encryptedEmployee.updated_at,
encryptedEmployee.created_by,
encryptedEmployee.updated_by
);
console.log('Employee created successfully:', employeeId);
res.status(201).json({
success: true,
message: 'Employee created successfully',
employee: {
id: employeeId,
first_name,
last_name,
employee_number,
position,
department,
email,
phone,
mobile,
office,
availability,
clearance_level,
clearance_valid_until,
clearance_issued_date
}
});
} catch (error) {
console.error('Create employee error:', error);
res.status(500).json({
success: false,
error: { message: 'Internal server error' }
});
}
});
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({ status: 'OK', message: 'Backend server is running' });
});
// Error handling middleware
app.use((error, req, res, next) => {
console.error('Unhandled error:', error);
res.status(500).json({
success: false,
error: { message: 'Internal server error' }
});
});
// Start server
app.listen(PORT, () => {
console.log(`Backend server running on http://localhost:${PORT}`);
console.log('Database ready');
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('Received SIGTERM, closing database...');
db.close();
process.exit(0);
});