mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
added backend for user management
This commit is contained in:
255
backend/src/controllers/employeeController.ts
Normal file
255
backend/src/controllers/employeeController.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// backend/src/controllers/employeeController.ts
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
import { db } from '../services/databaseService.js';
|
||||||
|
import { AuthRequest } from '../middleware/auth.js';
|
||||||
|
|
||||||
|
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const employees = await db.all<any>(`
|
||||||
|
SELECT
|
||||||
|
id, email, name, role, is_active as isActive,
|
||||||
|
phone, department, created_at as createdAt,
|
||||||
|
last_login as lastLogin
|
||||||
|
FROM users
|
||||||
|
ORDER BY name
|
||||||
|
`);
|
||||||
|
|
||||||
|
res.json(employees);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching employees:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const employee = await db.get<any>(`
|
||||||
|
SELECT
|
||||||
|
id, email, name, role, is_active as isActive,
|
||||||
|
phone, department, created_at as createdAt,
|
||||||
|
last_login as lastLogin
|
||||||
|
FROM users
|
||||||
|
WHERE id = ?
|
||||||
|
`, [id]);
|
||||||
|
|
||||||
|
if (!employee) {
|
||||||
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(employee);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching employee:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { email, password, name, role, phone, department } = req.body;
|
||||||
|
|
||||||
|
// Validierung
|
||||||
|
if (!email || !password || !name || !role) {
|
||||||
|
res.status(400).json({ error: 'Email, password, name and role are required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 6) {
|
||||||
|
res.status(400).json({ error: 'Password must be at least 6 characters long' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if email already exists
|
||||||
|
const existingUser = await db.get<any>('SELECT id FROM users WHERE email = ?', [email]);
|
||||||
|
if (existingUser) {
|
||||||
|
res.status(409).json({ error: 'Email already exists' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash password
|
||||||
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||||||
|
const employeeId = uuidv4();
|
||||||
|
|
||||||
|
await db.run(
|
||||||
|
`INSERT INTO users (id, email, password, name, role, phone, department, is_active)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[employeeId, email, hashedPassword, name, role, phone, department, 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return employee without password
|
||||||
|
const newEmployee = await db.get<any>(`
|
||||||
|
SELECT
|
||||||
|
id, email, name, role, is_active as isActive,
|
||||||
|
phone, department, created_at as createdAt,
|
||||||
|
last_login as lastLogin
|
||||||
|
FROM users
|
||||||
|
WHERE id = ?
|
||||||
|
`, [employeeId]);
|
||||||
|
|
||||||
|
res.status(201).json(newEmployee);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating employee:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { name, role, isActive, phone, department } = req.body;
|
||||||
|
|
||||||
|
// Check if employee exists
|
||||||
|
const existingEmployee = await db.get('SELECT * FROM users WHERE id = ?', [id]);
|
||||||
|
if (!existingEmployee) {
|
||||||
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update employee
|
||||||
|
await db.run(
|
||||||
|
`UPDATE users
|
||||||
|
SET name = COALESCE(?, name),
|
||||||
|
role = COALESCE(?, role),
|
||||||
|
is_active = COALESCE(?, is_active),
|
||||||
|
phone = COALESCE(?, phone),
|
||||||
|
department = COALESCE(?, department)
|
||||||
|
WHERE id = ?`,
|
||||||
|
[name, role, isActive, phone, department, id]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return updated employee
|
||||||
|
const updatedEmployee = await db.get<any>(`
|
||||||
|
SELECT
|
||||||
|
id, email, name, role, is_active as isActive,
|
||||||
|
phone, department, created_at as createdAt,
|
||||||
|
last_login as lastLogin
|
||||||
|
FROM users
|
||||||
|
WHERE id = ?
|
||||||
|
`, [id]);
|
||||||
|
|
||||||
|
res.json(updatedEmployee);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating employee:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
// Check if employee exists
|
||||||
|
const existingEmployee = await db.get('SELECT * FROM users WHERE id = ?', [id]);
|
||||||
|
if (!existingEmployee) {
|
||||||
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft delete - set is_active to false
|
||||||
|
await db.run('UPDATE users SET is_active = 0 WHERE id = ?', [id]);
|
||||||
|
|
||||||
|
res.status(204).send();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting employee:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { employeeId } = req.params;
|
||||||
|
|
||||||
|
// Check if employee exists
|
||||||
|
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
||||||
|
if (!existingEmployee) {
|
||||||
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const availabilities = await db.all<any>(`
|
||||||
|
SELECT * FROM employee_availabilities
|
||||||
|
WHERE employee_id = ?
|
||||||
|
ORDER BY day_of_week, start_time
|
||||||
|
`, [employeeId]);
|
||||||
|
|
||||||
|
res.json(availabilities.map(avail => ({
|
||||||
|
id: avail.id,
|
||||||
|
employeeId: avail.employee_id,
|
||||||
|
dayOfWeek: avail.day_of_week,
|
||||||
|
startTime: avail.start_time,
|
||||||
|
endTime: avail.end_time,
|
||||||
|
isAvailable: avail.is_available === 1
|
||||||
|
})));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching availabilities:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { employeeId } = req.params;
|
||||||
|
const availabilities = req.body;
|
||||||
|
|
||||||
|
// Check if employee exists
|
||||||
|
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
||||||
|
if (!existingEmployee) {
|
||||||
|
res.status(404).json({ error: 'Employee not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.run('BEGIN TRANSACTION');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Delete existing availabilities
|
||||||
|
await db.run('DELETE FROM employee_availabilities WHERE employee_id = ?', [employeeId]);
|
||||||
|
|
||||||
|
// Insert new availabilities
|
||||||
|
for (const availability of availabilities) {
|
||||||
|
const availabilityId = uuidv4();
|
||||||
|
await db.run(
|
||||||
|
`INSERT INTO employee_availabilities (id, employee_id, day_of_week, start_time, end_time, is_available)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
|
[
|
||||||
|
availabilityId,
|
||||||
|
employeeId,
|
||||||
|
availability.dayOfWeek,
|
||||||
|
availability.startTime,
|
||||||
|
availability.endTime,
|
||||||
|
availability.isAvailable ? 1 : 0
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.run('COMMIT');
|
||||||
|
|
||||||
|
// Return updated availabilities
|
||||||
|
const updatedAvailabilities = await db.all<any>(`
|
||||||
|
SELECT * FROM employee_availabilities
|
||||||
|
WHERE employee_id = ?
|
||||||
|
ORDER BY day_of_week, start_time
|
||||||
|
`, [employeeId]);
|
||||||
|
|
||||||
|
res.json(updatedAvailabilities.map(avail => ({
|
||||||
|
id: avail.id,
|
||||||
|
employeeId: avail.employee_id,
|
||||||
|
dayOfWeek: avail.day_of_week,
|
||||||
|
startTime: avail.start_time,
|
||||||
|
endTime: avail.end_time,
|
||||||
|
isAvailable: avail.is_available === 1
|
||||||
|
})));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await db.run('ROLLBACK');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating availabilities:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -22,3 +22,19 @@ CREATE TABLE IF NOT EXISTS assigned_shifts (
|
|||||||
assigned_employees TEXT DEFAULT '[]', -- JSON array of user IDs
|
assigned_employees TEXT DEFAULT '[]', -- JSON array of user IDs
|
||||||
FOREIGN KEY (shift_plan_id) REFERENCES shift_plans(id) ON DELETE CASCADE
|
FOREIGN KEY (shift_plan_id) REFERENCES shift_plans(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- Zusätzliche Tabelle für Mitarbeiter-Verfügbarkeiten
|
||||||
|
CREATE TABLE IF NOT EXISTS employee_availabilities (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
employee_id TEXT NOT NULL,
|
||||||
|
day_of_week INTEGER NOT NULL CHECK (day_of_week >= 0 AND day_of_week <= 6),
|
||||||
|
start_time TEXT NOT NULL,
|
||||||
|
end_time TEXT NOT NULL,
|
||||||
|
is_available BOOLEAN DEFAULT FALSE,
|
||||||
|
FOREIGN KEY (employee_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Users Tabelle erweitern um zusätzliche Felder
|
||||||
|
ALTER TABLE users ADD COLUMN phone TEXT;
|
||||||
|
ALTER TABLE users ADD COLUMN department TEXT;
|
||||||
|
ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT TRUE;
|
||||||
39
backend/src/models/Employee.ts
Normal file
39
backend/src/models/Employee.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// backend/src/models/Employee.ts
|
||||||
|
export interface Employee {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
role: 'admin' | 'instandhalter' | 'user';
|
||||||
|
isActive: boolean;
|
||||||
|
phone?: string;
|
||||||
|
department?: string;
|
||||||
|
createdAt: string;
|
||||||
|
lastLogin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Availability {
|
||||||
|
id: string;
|
||||||
|
employeeId: string;
|
||||||
|
dayOfWeek: number;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
isAvailable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEmployeeRequest {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
role: 'admin' | 'instandhalter' | 'user';
|
||||||
|
phone?: string;
|
||||||
|
department?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateEmployeeRequest {
|
||||||
|
name?: string;
|
||||||
|
role?: 'admin' | 'instandhalter' | 'user';
|
||||||
|
isActive?: boolean;
|
||||||
|
phone?: string;
|
||||||
|
department?: string;
|
||||||
|
}
|
||||||
30
backend/src/routes/employees.ts
Normal file
30
backend/src/routes/employees.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// backend/src/routes/employees.ts
|
||||||
|
import express from 'express';
|
||||||
|
import { authMiddleware, requireRole } from '../middleware/auth.js';
|
||||||
|
import {
|
||||||
|
getEmployees,
|
||||||
|
getEmployee,
|
||||||
|
createEmployee,
|
||||||
|
updateEmployee,
|
||||||
|
deleteEmployee,
|
||||||
|
getAvailabilities,
|
||||||
|
updateAvailabilities
|
||||||
|
} from '../controllers/employeeController.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Alle Routes benötigen Authentication
|
||||||
|
router.use(authMiddleware);
|
||||||
|
|
||||||
|
// Employee CRUD Routes
|
||||||
|
router.get('/', requireRole(['admin', 'instandhalter']), getEmployees);
|
||||||
|
router.get('/:id', requireRole(['admin', 'instandhalter']), getEmployee);
|
||||||
|
router.post('/', requireRole(['admin']), createEmployee);
|
||||||
|
router.put('/:id', requireRole(['admin']), updateEmployee);
|
||||||
|
router.delete('/:id', requireRole(['admin']), deleteEmployee);
|
||||||
|
|
||||||
|
// Availability Routes
|
||||||
|
router.get('/:employeeId/availabilities', requireRole(['admin', 'instandhalter']), getAvailabilities);
|
||||||
|
router.put('/:employeeId/availabilities', requireRole(['admin', 'instandhalter']), updateAvailabilities);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -1,24 +1,66 @@
|
|||||||
// backend/src/scripts/seedData.ts
|
// backend/src/scripts/seedData.ts - Erweitert
|
||||||
import { db } from '../services/databaseService';
|
import { db } from '../services/databaseService.js';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
export const seedData = async () => {
|
export const seedData = async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log('Starting database seeding...');
|
||||||
|
|
||||||
// Admin User erstellen
|
// Admin User erstellen
|
||||||
const adminId = uuidv4();
|
const adminId = uuidv4();
|
||||||
const hashedPassword = await bcrypt.hash('admin123', 10);
|
const hashedPassword = await bcrypt.hash('admin123', 10);
|
||||||
|
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT OR IGNORE INTO users (id, email, password, name, role) VALUES (?, ?, ?, ?, ?)`,
|
`INSERT OR IGNORE INTO users (id, email, password, name, role, phone, department, is_active)
|
||||||
[adminId, 'admin@schichtplan.de', hashedPassword, 'System Administrator', 'admin']
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[adminId, 'admin@schichtplan.de', hashedPassword, 'System Administrator', 'admin', '+49 123 456789', 'IT', 1]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test-Mitarbeiter erstellen
|
||||||
|
const testUsers = [
|
||||||
|
{
|
||||||
|
id: uuidv4(),
|
||||||
|
email: 'instandhalter@schichtplan.de',
|
||||||
|
password: await bcrypt.hash('instandhalter123', 10),
|
||||||
|
name: 'Max Instandhalter',
|
||||||
|
role: 'instandhalter',
|
||||||
|
phone: '+49 123 456790',
|
||||||
|
department: 'Produktion'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: uuidv4(),
|
||||||
|
email: 'mitarbeiter1@schichtplan.de',
|
||||||
|
password: await bcrypt.hash('user123', 10),
|
||||||
|
name: 'Anna Müller',
|
||||||
|
role: 'user',
|
||||||
|
phone: '+49 123 456791',
|
||||||
|
department: 'Logistik'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: uuidv4(),
|
||||||
|
email: 'mitarbeiter2@schichtplan.de',
|
||||||
|
password: await bcrypt.hash('user123', 10),
|
||||||
|
name: 'Tom Schmidt',
|
||||||
|
role: 'user',
|
||||||
|
phone: '+49 123 456792',
|
||||||
|
department: 'Produktion'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const user of testUsers) {
|
||||||
|
await db.run(
|
||||||
|
`INSERT OR IGNORE INTO users (id, email, password, name, role, phone, department, is_active)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[user.id, user.email, user.password, user.name, user.role, user.phone, user.department, 1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Standard Vorlage erstellen
|
// Standard Vorlage erstellen
|
||||||
const templateId = uuidv4();
|
const templateId = uuidv4();
|
||||||
|
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT OR IGNORE INTO shift_templates (id, name, description, is_default, created_by) VALUES (?, ?, ?, ?, ?)`,
|
`INSERT OR IGNORE INTO shift_templates (id, name, description, is_default, created_by)
|
||||||
|
VALUES (?, ?, ?, ?, ?)`,
|
||||||
[templateId, 'Standard Woche', 'Standard Schichtplan für Montag bis Freitag', 1, adminId]
|
[templateId, 'Standard Woche', 'Standard Schichtplan für Montag bis Freitag', 1, adminId]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -37,16 +79,14 @@ export const seedData = async () => {
|
|||||||
|
|
||||||
for (const shift of shifts) {
|
for (const shift of shifts) {
|
||||||
await db.run(
|
await db.run(
|
||||||
`INSERT OR IGNORE INTO template_shifts (id, template_id, day_of_week, name, start_time, end_time, required_employees) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
`INSERT OR IGNORE INTO template_shifts (id, template_id, day_of_week, name, start_time, end_time, required_employees)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[uuidv4(), templateId, shift.day, shift.name, shift.start, shift.end, shift.employees]
|
[uuidv4(), templateId, shift.day, shift.name, shift.start, shift.end, shift.employees]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Test data seeded successfully');
|
console.log('✅ Test data seeded successfully');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error seeding test data:', error);
|
console.error('❌ Error seeding test data:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Beim Start ausführen
|
|
||||||
seedData();
|
|
||||||
@@ -1,18 +1,31 @@
|
|||||||
const express = require('express');
|
// backend/src/server.ts - Erweitert
|
||||||
const cors = require('cors');
|
import express from 'express';
|
||||||
const sqlite3 = require('sqlite3').verbose();
|
import cors from 'cors';
|
||||||
const path = require('path');
|
import { db } from './services/databaseService.js';
|
||||||
|
import { seedData } from './scripts/seedData.js';
|
||||||
|
import authRoutes from './routes/auth.js';
|
||||||
|
import shiftTemplateRoutes from './routes/shiftTemplates.js';
|
||||||
|
import shiftPlanRoutes from './routes/shiftPlans.js';
|
||||||
|
import employeeRoutes from './routes/employees.js'; // NEU HINZUGEFÜGT
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3002;
|
const PORT = 3002;
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(cors());
|
app.use(cors({
|
||||||
|
origin: 'http://localhost:3000',
|
||||||
|
credentials: true
|
||||||
|
}));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// Health route
|
// Routes
|
||||||
app.get('/api/health', (req: any, res: any) => {
|
app.use('/api/auth', authRoutes);
|
||||||
console.log('✅ Health check called');
|
app.use('/api/shift-templates', shiftTemplateRoutes);
|
||||||
|
app.use('/api/shift-plans', shiftPlanRoutes);
|
||||||
|
app.use('/api/employees', employeeRoutes); // NEU HINZUGEFÜGT
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
app.get('/api/health', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
status: 'OK',
|
status: 'OK',
|
||||||
message: 'Backend läuft!',
|
message: 'Backend läuft!',
|
||||||
@@ -20,55 +33,12 @@ app.get('/api/health', (req: any, res: any) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simple login without bcrypt
|
|
||||||
app.post('/api/auth/login', (req: any, res: any) => {
|
|
||||||
console.log('🔐 Login attempt:', req.body.email);
|
|
||||||
|
|
||||||
// Einfache Hardcoded Auth (OHNE Passwort-Hashing für Test)
|
|
||||||
if (req.body.email === 'admin@schichtplan.de' && req.body.password === 'admin123') {
|
|
||||||
console.log('✅ Login successful');
|
|
||||||
res.json({
|
|
||||||
user: {
|
|
||||||
id: '1',
|
|
||||||
email: 'admin@schichtplan.de',
|
|
||||||
name: 'Admin User',
|
|
||||||
role: 'admin',
|
|
||||||
createdAt: new Date().toISOString()
|
|
||||||
},
|
|
||||||
token: 'simple-jwt-token-' + Date.now(),
|
|
||||||
expiresIn: '7d'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log('❌ Login failed');
|
|
||||||
res.status(401).json({ error: 'Invalid credentials' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get shift templates
|
|
||||||
app.get('/api/shift-templates', (req: any, res: any) => {
|
|
||||||
console.log('📋 Fetching shift templates');
|
|
||||||
res.json([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'Standard Woche',
|
|
||||||
description: 'Standard Schichtplan',
|
|
||||||
isDefault: true,
|
|
||||||
createdBy: '1',
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
shifts: [
|
|
||||||
{ id: '1', dayOfWeek: 1, name: 'Vormittag', startTime: '08:00', endTime: '12:00', requiredEmployees: 2 },
|
|
||||||
{ id: '2', dayOfWeek: 1, name: 'Nachmittag', startTime: '11:30', endTime: '15:30', requiredEmployees: 2 }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, async () => {
|
||||||
console.log('🎉 BACKEND STARTED SUCCESSFULLY!');
|
console.log('🎉 BACKEND STARTED SUCCESSFULLY!');
|
||||||
console.log(`📍 Port: ${PORT}`);
|
console.log(`📍 Port: ${PORT}`);
|
||||||
console.log(`📍 Health: http://localhost:${PORT}/api/health`);
|
console.log(`📍 Health: http://localhost:${PORT}/api/health`);
|
||||||
console.log(`📍 Ready for login!`);
|
console.log('📊 Employee management ready!');
|
||||||
});
|
|
||||||
|
|
||||||
console.log('🚀 Server starting...');
|
await seedData();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user