mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
removed phone and departement as user attribute
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// backend/src/controllers/authController.ts
|
||||
import { Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import bcrypt from 'bcrypt';
|
||||
@@ -9,8 +10,6 @@ export interface User {
|
||||
email: string;
|
||||
name: string;
|
||||
role: string;
|
||||
phone?: string;
|
||||
department?: string;
|
||||
}
|
||||
|
||||
export interface UserWithPassword extends User {
|
||||
@@ -34,8 +33,8 @@ export interface RegisterRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
name: string;
|
||||
phone?: string;
|
||||
department?: string;
|
||||
//employee_type?: string;
|
||||
//is_sufficiently_independent?: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
@@ -52,7 +51,7 @@ export const login = async (req: Request, res: Response) => {
|
||||
|
||||
// Get user from database
|
||||
const user = await db.get<UserWithPassword>(
|
||||
'SELECT id, email, password, name, role, phone, department FROM users WHERE email = ? AND is_active = 1',
|
||||
'SELECT id, email, password, name, role FROM users WHERE email = ? AND is_active = 1',
|
||||
[email]
|
||||
);
|
||||
|
||||
@@ -116,7 +115,7 @@ export const getCurrentUser = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
const user = await db.get<User>(
|
||||
'SELECT id, email, name, role, phone, department FROM users WHERE id = ? AND is_active = 1',
|
||||
'SELECT id, email, name, role FROM users WHERE id = ? AND is_active = 1',
|
||||
[jwtUser.userId] // ← HIER: userId verwenden
|
||||
);
|
||||
|
||||
@@ -162,7 +161,7 @@ export const validateToken = async (req: Request, res: Response) => {
|
||||
|
||||
export const register = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { email, password, name, phone, department, role = 'user' } = req.body as RegisterRequest;
|
||||
const { email, password, name, role = 'user' } = req.body as RegisterRequest;
|
||||
|
||||
// Validate required fields
|
||||
if (!email || !password || !name) {
|
||||
@@ -188,9 +187,9 @@ export const register = async (req: Request, res: Response) => {
|
||||
|
||||
// Insert user
|
||||
const result = await db.run(
|
||||
`INSERT INTO users (email, password, name, role, phone, department)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[email, hashedPassword, name, role, phone, department]
|
||||
`INSERT INTO users (email, password, name, role)
|
||||
VALUES (?, ?, ?, ?)`,
|
||||
[email, hashedPassword, name, role]
|
||||
);
|
||||
|
||||
if (!result.lastID) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { db } from '../services/databaseService.js';
|
||||
import { AuthRequest } from '../middleware/auth.js';
|
||||
import { CreateEmployeeRequest } from '../models/Employee.js';
|
||||
|
||||
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||
try {
|
||||
@@ -12,7 +13,9 @@ export const getEmployees = async (req: AuthRequest, res: Response): Promise<voi
|
||||
const employees = await db.all<any>(`
|
||||
SELECT
|
||||
id, email, name, role, is_active as isActive,
|
||||
phone, department, created_at as createdAt,
|
||||
employee_type as employeeType,
|
||||
is_sufficiently_independent as isSufficientlyIndependent,
|
||||
created_at as createdAt,
|
||||
last_login as lastLogin
|
||||
FROM users
|
||||
WHERE is_active = 1
|
||||
@@ -36,7 +39,9 @@ export const getEmployee = async (req: AuthRequest, res: Response): Promise<void
|
||||
const employee = await db.get<any>(`
|
||||
SELECT
|
||||
id, email, name, role, is_active as isActive,
|
||||
phone, department, created_at as createdAt,
|
||||
employee_type as employeeType,
|
||||
is_sufficiently_independent as isSufficientlyIndependent,
|
||||
created_at as createdAt,
|
||||
last_login as lastLogin
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
@@ -61,19 +66,22 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
password: '***hidden***'
|
||||
});
|
||||
|
||||
const { email, password, name, role, phone, department } = req.body as {
|
||||
email: string;
|
||||
password: string;
|
||||
name: string;
|
||||
role: string;
|
||||
phone?: string;
|
||||
department?: string;
|
||||
};
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
role,
|
||||
employeeType,
|
||||
isSufficientlyIndependent,
|
||||
notes
|
||||
} = req.body as CreateEmployeeRequest;
|
||||
|
||||
// Validierung
|
||||
if (!email || !password || !name || !role) {
|
||||
if (!email || !password || !name || !role || !employeeType) {
|
||||
console.log('❌ Validation failed: Missing required fields');
|
||||
res.status(400).json({ error: 'Email, password, name and role are required' });
|
||||
res.status(400).json({
|
||||
error: 'Email, password, name, role und employeeType sind erforderlich'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,13 +91,8 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
return;
|
||||
}
|
||||
|
||||
// First check for ANY user with this email to debug
|
||||
const allUsersWithEmail = await db.all<any>('SELECT id, email, is_active FROM users WHERE email = ?', [email]);
|
||||
console.log('🔍 Found existing users with this email:', allUsersWithEmail);
|
||||
|
||||
// Check if email already exists among active users only
|
||||
const existingActiveUser = await db.get<any>('SELECT id, is_active FROM users WHERE email = ? AND is_active = 1', [email]);
|
||||
console.log('🔍 Checking active users with this email:', existingActiveUser);
|
||||
// Check if email already exists
|
||||
const existingActiveUser = await db.get<any>('SELECT id FROM users WHERE email = ? AND is_active = 1', [email]);
|
||||
|
||||
if (existingActiveUser) {
|
||||
console.log('❌ Email exists for active user:', existingActiveUser);
|
||||
@@ -102,16 +105,30 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
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]
|
||||
`INSERT INTO users (
|
||||
id, email, password, name, role, employee_type, is_sufficiently_independent,
|
||||
notes, is_active
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
employeeId,
|
||||
email,
|
||||
hashedPassword,
|
||||
name,
|
||||
role,
|
||||
employeeType,
|
||||
isSufficientlyIndependent ? 1 : 0,
|
||||
notes || null,
|
||||
1
|
||||
]
|
||||
);
|
||||
|
||||
// Return employee without password
|
||||
// Return created employee
|
||||
const newEmployee = await db.get<any>(`
|
||||
SELECT
|
||||
id, email, name, role, is_active as isActive,
|
||||
phone, department, created_at as createdAt,
|
||||
id, email, name, role, is_active as isActive,
|
||||
employee_type as employeeType,
|
||||
is_sufficiently_independent as isSufficientlyIndependent,
|
||||
notes, created_at as createdAt,
|
||||
last_login as lastLogin
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
@@ -127,7 +144,7 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
export const updateEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { name, role, isActive, phone, department } = req.body;
|
||||
const { name, role, isActive, employeeType, isSufficientlyIndependent } = req.body;
|
||||
|
||||
// Check if employee exists
|
||||
const existingEmployee = await db.get('SELECT * FROM users WHERE id = ?', [id]);
|
||||
@@ -142,17 +159,19 @@ export const updateEmployee = async (req: AuthRequest, res: Response): Promise<v
|
||||
SET name = COALESCE(?, name),
|
||||
role = COALESCE(?, role),
|
||||
is_active = COALESCE(?, is_active),
|
||||
phone = COALESCE(?, phone),
|
||||
department = COALESCE(?, department)
|
||||
employee_type = COALESCE(?, employee_type),
|
||||
is_sufficiently_independent = COALESCE(?, is_sufficiently_independent)
|
||||
WHERE id = ?`,
|
||||
[name, role, isActive, phone, department, id]
|
||||
[name, role, isActive, employeeType, isSufficientlyIndependent, 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,
|
||||
employee_type as employeeType,
|
||||
is_sufficiently_independent as isSufficientlyIndependent,
|
||||
created_at as createdAt,
|
||||
last_login as lastLogin
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
|
||||
@@ -44,10 +44,10 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
return;
|
||||
}
|
||||
|
||||
const { password, name, phone, department } = req.body;
|
||||
const { password, name } = req.body;
|
||||
const email = 'admin@instandhaltung.de'; // Fixed admin email
|
||||
|
||||
console.log('👤 Creating admin with data:', { name, email, phone, department });
|
||||
console.log('👤 Creating admin with data:', { name, email });
|
||||
|
||||
// Validation
|
||||
if (!password || !name) {
|
||||
@@ -63,16 +63,16 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const adminId = uuidv4();
|
||||
const adminId = randomUUID();
|
||||
|
||||
console.log('📝 Inserting admin user with ID:', adminId);
|
||||
|
||||
try {
|
||||
// Create admin user
|
||||
await db.run(
|
||||
`INSERT INTO users (id, email, password, name, role, phone, department, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[adminId, email, hashedPassword, name, 'admin', phone || null, department || null, 1]
|
||||
`INSERT INTO users (id, email, password, name, role, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[adminId, email, hashedPassword, name, 'admin', 1]
|
||||
);
|
||||
|
||||
console.log('✅ Admin user created successfully');
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add employee fields
|
||||
ALTER TABLE users ADD COLUMN employee_type TEXT CHECK(employee_type IN ('chef', 'neuling', 'erfahren'));
|
||||
ALTER TABLE users ADD COLUMN is_sufficiently_independent BOOLEAN DEFAULT FALSE;
|
||||
@@ -5,8 +5,8 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
password TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
role TEXT CHECK(role IN ('admin', 'user', 'instandhalter')) NOT NULL,
|
||||
phone TEXT,
|
||||
department TEXT,
|
||||
employee_type TEXT CHECK(employee_type IN ('chef', 'neuling', 'erfahren')),
|
||||
is_sufficiently_independent BOOLEAN DEFAULT FALSE,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
@@ -2,38 +2,35 @@
|
||||
export interface Employee {
|
||||
id: string;
|
||||
email: string;
|
||||
password: string;
|
||||
name: string;
|
||||
role: 'admin' | 'instandhalter' | 'user';
|
||||
employeeType: 'chef' | 'neuling' | 'erfahren';
|
||||
isSufficientlyIndependent: boolean;
|
||||
isActive: boolean;
|
||||
phone?: string;
|
||||
department?: string;
|
||||
notes?: string;
|
||||
createdAt: string;
|
||||
lastLogin?: string | null;
|
||||
}
|
||||
|
||||
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;
|
||||
employeeType: 'chef' | 'neuling' | 'erfahren';
|
||||
isSufficientlyIndependent: boolean;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface UpdateEmployeeRequest {
|
||||
name?: string;
|
||||
role?: 'admin' | 'instandhalter' | 'user';
|
||||
employeeType?: 'chef' | 'neuling' | 'erfahren';
|
||||
isSufficientlyIndependent?: boolean;
|
||||
isActive?: boolean;
|
||||
phone?: string;
|
||||
department?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface EmployeeWithPassword extends Employee {
|
||||
password: string;
|
||||
}
|
||||
43
backend/src/scripts/applyMigration.ts
Normal file
43
backend/src/scripts/applyMigration.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { db } from '../services/databaseService.js';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export async function applyMigration() {
|
||||
try {
|
||||
console.log('📦 Starting database migration...');
|
||||
|
||||
// Read the migration file
|
||||
const migrationPath = join(__dirname, '../database/migrations/002_add_employee_fields.sql');
|
||||
const migrationSQL = await readFile(migrationPath, 'utf-8');
|
||||
|
||||
// Split into individual statements
|
||||
const statements = migrationSQL
|
||||
.split(';')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0);
|
||||
|
||||
// Execute each statement
|
||||
for (const statement of statements) {
|
||||
try {
|
||||
await db.exec(statement);
|
||||
console.log('✅ Executed:', statement.slice(0, 50) + '...');
|
||||
} catch (error) {
|
||||
const err = error as { code: string; message: string };
|
||||
if (err.code === 'SQLITE_ERROR' && err.message.includes('duplicate column name')) {
|
||||
console.log('ℹ️ Column already exists, skipping...');
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Migration completed successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// backend/src/scripts/seedData.ts - Erweitert
|
||||
import { db } from '../services/databaseService.js';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
export const seedData = async () => {
|
||||
try {
|
||||
console.log('Starting database seeding...');
|
||||
|
||||
// Admin User erstellen
|
||||
const adminId = uuidv4();
|
||||
const hashedPassword = await bcrypt.hash('admin123', 10);
|
||||
|
||||
await db.run(
|
||||
`INSERT OR IGNORE INTO users (id, email, password, name, role, phone, department, is_active)
|
||||
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
|
||||
const templateId = uuidv4();
|
||||
await db.run(
|
||||
`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]
|
||||
);
|
||||
|
||||
// Standard Schichten
|
||||
const shifts = [
|
||||
{ day: 1, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 },
|
||||
{ day: 1, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 },
|
||||
{ day: 2, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 },
|
||||
{ day: 2, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 },
|
||||
{ day: 3, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 },
|
||||
{ day: 3, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 },
|
||||
{ day: 4, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 },
|
||||
{ day: 4, name: 'Nachmittag', start: '11:30', end: '15:30', employees: 2 },
|
||||
{ day: 5, name: 'Vormittag', start: '08:00', end: '12:00', employees: 2 }
|
||||
];
|
||||
|
||||
for (const shift of shifts) {
|
||||
await db.run(
|
||||
`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]
|
||||
);
|
||||
}
|
||||
|
||||
console.log('✅ Test data seeded successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Error seeding test data:', error);
|
||||
}
|
||||
};
|
||||
@@ -59,9 +59,16 @@ app.get('/api/initial-setup', async (req: any, res: any) => {
|
||||
// Initialize the application
|
||||
const initializeApp = async () => {
|
||||
try {
|
||||
// Initialize database with base schema
|
||||
await initializeDatabase();
|
||||
console.log('✅ Database initialized successfully');
|
||||
|
||||
// Apply any pending migrations
|
||||
const { applyMigration } = await import('./scripts/applyMigration.js');
|
||||
await applyMigration();
|
||||
console.log('✅ Database migrations applied');
|
||||
|
||||
// Setup default template
|
||||
await setupDefaultTemplate();
|
||||
console.log('✅ Default template checked/created');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user