mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
added setup files
This commit is contained in:
@@ -6,17 +6,31 @@ import { db } from '../services/databaseService.js';
|
|||||||
|
|
||||||
export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => {
|
export const checkSetupStatus = async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const adminExists = await db.get<{ count: number }>(
|
// First, ensure database is properly initialized
|
||||||
'SELECT COUNT(*) as count FROM users WHERE role = ?',
|
try {
|
||||||
['admin']
|
const adminExists = await db.get<{ count: number }>(
|
||||||
);
|
'SELECT COUNT(*) as count FROM users WHERE role = ?',
|
||||||
|
['admin']
|
||||||
|
);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
needsSetup: !adminExists || adminExists.count === 0
|
needsSetup: !adminExists || adminExists.count === 0,
|
||||||
});
|
message: adminExists && adminExists.count > 0 ? 'Admin user exists' : 'No admin user found'
|
||||||
|
});
|
||||||
|
} catch (dbError) {
|
||||||
|
console.error('Database error in checkSetupStatus:', dbError);
|
||||||
|
// If there's a database error, assume setup is needed
|
||||||
|
res.json({
|
||||||
|
needsSetup: true,
|
||||||
|
message: 'Database not ready, setup required'
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking setup status:', error);
|
console.error('Error checking setup status:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({
|
||||||
|
error: 'Internal server error',
|
||||||
|
needsSetup: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,7 +92,8 @@ export const setupAdmin = async (req: Request, res: Response): Promise<void> =>
|
|||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
message: 'Admin user created successfully',
|
message: 'Admin user created successfully',
|
||||||
userId: adminId
|
userId: adminId,
|
||||||
|
email: email
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in setup:', error);
|
console.error('Error in setup:', error);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// backend/src/routes/setup.ts
|
// backend/src/routes/setup.ts
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
import { checkSetupStatus, setupAdmin } from '../controllers/setupController.js';
|
import { checkSetupStatus, setupAdmin } from '../controllers/setupController.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// backend/src/scripts/initializeDatabase.ts
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
@@ -14,6 +15,20 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
console.log('Starting database initialization...');
|
console.log('Starting database initialization...');
|
||||||
|
|
||||||
|
// Check if users table exists and has data
|
||||||
|
try {
|
||||||
|
const existingAdmin = await db.get<{ count: number }>(
|
||||||
|
"SELECT COUNT(*) as count FROM users WHERE role = 'admin'"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingAdmin && existingAdmin.count > 0) {
|
||||||
|
console.log('✅ Database already initialized with admin user');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('ℹ️ Database tables might not exist yet, creating schema...');
|
||||||
|
}
|
||||||
|
|
||||||
// Get list of existing tables
|
// Get list of existing tables
|
||||||
interface TableInfo {
|
interface TableInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -26,7 +41,7 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
|
|
||||||
console.log('Existing tables found:', existingTables.map(t => t.name).join(', ') || 'none');
|
console.log('Existing tables found:', existingTables.map(t => t.name).join(', ') || 'none');
|
||||||
|
|
||||||
// Drop existing tables in reverse order of dependencies
|
// Drop existing tables in reverse order of dependencies if they exist
|
||||||
const tablesToDrop = [
|
const tablesToDrop = [
|
||||||
'employee_availabilities',
|
'employee_availabilities',
|
||||||
'assigned_shifts',
|
'assigned_shifts',
|
||||||
@@ -77,54 +92,13 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await db.run('COMMIT');
|
await db.run('COMMIT');
|
||||||
console.log('✅ Datenbankschema erfolgreich initialisiert');
|
console.log('✅ Database schema successfully initialized');
|
||||||
|
|
||||||
// Give a small delay to ensure all transactions are properly closed
|
// Give a small delay to ensure all transactions are properly closed
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
// Create default template
|
|
||||||
await setupDefaultTemplate();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler bei der Datenbankinitialisierung:', error);
|
console.error('Error during database initialization:', error);
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createAdminUser(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await db.run('BEGIN TRANSACTION');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Erstelle Admin-Benutzer, wenn noch keiner existiert
|
|
||||||
const admin = await db.get('SELECT id FROM users WHERE role = ?', ['admin']);
|
|
||||||
|
|
||||||
if (!admin) {
|
|
||||||
await db.run(
|
|
||||||
`INSERT INTO users (id, email, password, name, role, phone, department, is_active)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
||||||
[
|
|
||||||
'admin-' + Math.random().toString(36).substring(2),
|
|
||||||
'admin@schichtplan.de',
|
|
||||||
'admin123',
|
|
||||||
'Administrator',
|
|
||||||
'admin',
|
|
||||||
'+49 123 456789',
|
|
||||||
'IT',
|
|
||||||
true
|
|
||||||
]
|
|
||||||
);
|
|
||||||
console.log('✅ Admin-Benutzer erstellt');
|
|
||||||
} else {
|
|
||||||
console.log('ℹ️ Admin-Benutzer existiert bereits');
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.run('COMMIT');
|
|
||||||
} catch (error) {
|
|
||||||
await db.run('ROLLBACK');
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fehler beim Erstellen des Admin-Benutzers:', error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// backend/src/server.ts - Login für alle Benutzer
|
// backend/src/server.ts
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { setupDefaultTemplate } from './scripts/setupDefaultTemplate.js';
|
import { setupDefaultTemplate } from './scripts/setupDefaultTemplate.js';
|
||||||
import { initializeDatabase } from './scripts/initializeDatabase.js';
|
import { initializeDatabase } from './scripts/initializeDatabase.js';
|
||||||
|
|
||||||
@@ -38,6 +37,29 @@ app.get('/api/health', (req: any, res: any) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Setup status route (additional endpoint for clarity)
|
||||||
|
app.get('/api/initial-setup', async (req: any, res: any) => {
|
||||||
|
try {
|
||||||
|
const { db } = await import('./services/databaseService.js');
|
||||||
|
|
||||||
|
// Define proper interface for the result
|
||||||
|
interface AdminCountResult {
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminExists = await db.get<AdminCountResult>(
|
||||||
|
'SELECT COUNT(*) as count FROM users WHERE role = ?',
|
||||||
|
['admin']
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
needsInitialSetup: !adminExists || adminExists.count === 0
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking initial setup:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
app.listen(PORT, async () => {
|
app.listen(PORT, async () => {
|
||||||
@@ -47,13 +69,15 @@ app.listen(PORT, async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await initializeDatabase();
|
await initializeDatabase();
|
||||||
|
console.log('✅ Database initialized successfully');
|
||||||
|
|
||||||
await setupDefaultTemplate();
|
await setupDefaultTemplate();
|
||||||
console.log('✅ Standard-Vorlage überprüft/erstellt');
|
console.log('✅ Default template checked/created');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Fehler bei der Initialisierung:', error);
|
console.error('❌ Error during initialization:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('🔧 Setup ready at: http://localhost:${PORT}/api/setup/status');
|
console.log(`🔧 Setup ready at: http://localhost:${PORT}/api/setup/status`);
|
||||||
console.log('📝 Create your admin account on first launch');
|
console.log('📝 Create your admin account on first launch');
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// frontend/src/contexts/AuthContext.tsx - KORRIGIERT
|
// frontend/src/contexts/AuthContext.tsx
|
||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
import { authService, User, LoginRequest } from '../services/authService';
|
import { authService, User, LoginRequest } from '../services/authService';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ interface AuthContextType {
|
|||||||
logout: () => void;
|
logout: () => void;
|
||||||
hasRole: (roles: string[]) => boolean;
|
hasRole: (roles: string[]) => boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
refreshUser: () => void; // NEU: Force refresh
|
refreshUser: () => void;
|
||||||
needsSetup: boolean;
|
needsSetup: boolean;
|
||||||
checkSetupStatus: () => Promise<void>;
|
checkSetupStatus: () => Promise<void>;
|
||||||
}
|
}
|
||||||
@@ -19,15 +19,20 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
const [user, setUser] = useState<User | null>(null);
|
const [user, setUser] = useState<User | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [needsSetup, setNeedsSetup] = useState(false);
|
const [needsSetup, setNeedsSetup] = useState(false);
|
||||||
const [refreshTrigger, setRefreshTrigger] = useState(0); // NEU: Refresh trigger
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||||
|
|
||||||
const checkSetupStatus = async () => {
|
const checkSetupStatus = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/setup/status');
|
const response = await fetch('/api/setup/status');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to check setup status');
|
||||||
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setNeedsSetup(data.needsSetup);
|
setNeedsSetup(data.needsSetup);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking setup status:', error);
|
console.error('Error checking setup status:', error);
|
||||||
|
// If we can't reach the server, assume setup is needed
|
||||||
|
setNeedsSetup(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,19 +40,35 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initializeApp = async () => {
|
const initializeApp = async () => {
|
||||||
await checkSetupStatus();
|
await checkSetupStatus();
|
||||||
const savedUser = authService.getCurrentUser();
|
|
||||||
if (savedUser) {
|
// Only try to load user if setup is not needed
|
||||||
setUser(savedUser);
|
if (!needsSetup) {
|
||||||
console.log('✅ User from localStorage:', savedUser.email);
|
const savedUser = authService.getCurrentUser();
|
||||||
|
if (savedUser) {
|
||||||
|
setUser(savedUser);
|
||||||
|
console.log('✅ User from localStorage:', savedUser.email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
initializeApp();
|
initializeApp();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// NEU: User vom Server laden wenn nötig
|
// Update needsSetup when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (refreshTrigger > 0) {
|
if (!needsSetup && !user) {
|
||||||
|
// If setup is complete but no user is loaded, try to load from localStorage
|
||||||
|
const savedUser = authService.getCurrentUser();
|
||||||
|
if (savedUser) {
|
||||||
|
setUser(savedUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [needsSetup, user]);
|
||||||
|
|
||||||
|
// User vom Server laden wenn nötig
|
||||||
|
useEffect(() => {
|
||||||
|
if (refreshTrigger > 0 && !needsSetup) {
|
||||||
const loadUserFromServer = async () => {
|
const loadUserFromServer = async () => {
|
||||||
const serverUser = await authService.fetchCurrentUser();
|
const serverUser = await authService.fetchCurrentUser();
|
||||||
if (serverUser) {
|
if (serverUser) {
|
||||||
@@ -57,7 +78,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||||||
};
|
};
|
||||||
loadUserFromServer();
|
loadUserFromServer();
|
||||||
}
|
}
|
||||||
}, [refreshTrigger]);
|
}, [refreshTrigger, needsSetup]);
|
||||||
|
|
||||||
const login = async (credentials: LoginRequest) => {
|
const login = async (credentials: LoginRequest) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// frontend/src/pages/Setup/Setup.tsx
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
@@ -14,7 +15,7 @@ const Setup: React.FC = () => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { login } = useAuth();
|
const { login, checkSetupStatus } = useAuth();
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
@@ -81,6 +82,9 @@ const Setup: React.FC = () => {
|
|||||||
throw new Error(data.error || 'Setup fehlgeschlagen');
|
throw new Error(data.error || 'Setup fehlgeschlagen');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-check setup status after successful setup
|
||||||
|
await checkSetupStatus();
|
||||||
|
|
||||||
// Automatically log in after setup
|
// Automatically log in after setup
|
||||||
await login({ email: adminEmail, password: formData.password });
|
await login({ email: adminEmail, password: formData.password });
|
||||||
navigate('/');
|
navigate('/');
|
||||||
|
|||||||
Reference in New Issue
Block a user