Compare commits

...

6 Commits

9 changed files with 249 additions and 270 deletions

View File

@@ -9,10 +9,11 @@ export const validateLogin = [
.normalizeEmail(), .normalizeEmail(),
body('password') body('password')
.isLength({ min: 6 }) .optional()
.withMessage('Password must be at least 6 characters') .isLength({ min: 8 })
.trim() .withMessage('Password must be at least 8 characters')
.escape() .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/)
.withMessage('Password must contain uppercase, lowercase, number and special character'),
]; ];
export const validateRegister = [ export const validateRegister = [
@@ -29,10 +30,11 @@ export const validateRegister = [
.escape(), .escape(),
body('password') body('password')
.optional()
.isLength({ min: 8 }) .isLength({ min: 8 })
.withMessage('Password must be at least 8 characters') .withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/)
.withMessage('Password must contain uppercase, lowercase and number') .withMessage('Password must contain uppercase, lowercase, number and special character'),
]; ];
// ===== EMPLOYEE VALIDATION ===== // ===== EMPLOYEE VALIDATION =====
@@ -53,8 +55,8 @@ export const validateEmployee = [
.optional() .optional()
.isLength({ min: 8 }) .isLength({ min: 8 })
.withMessage('Password must be at least 8 characters') .withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/)
.withMessage('Password must contain uppercase, lowercase and number'), .withMessage('Password must contain uppercase, lowercase, number and special character'),
body('employeeType') body('employeeType')
.isIn(['manager', 'personell', 'apprentice', 'guest']) .isIn(['manager', 'personell', 'apprentice', 'guest'])
@@ -145,14 +147,15 @@ export const validateEmployeeUpdate = [
export const validateChangePassword = [ export const validateChangePassword = [
body('currentPassword') body('currentPassword')
.optional() .optional()
.isLength({ min: 6 })
.withMessage('Current password must be at least 6 characters'),
body('newPassword')
.isLength({ min: 8 }) .isLength({ min: 8 })
.withMessage('New password must be at least 8 characters') .withMessage('Current password must be at least 8 characters'),
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('New password must contain uppercase, lowercase and number') body('password')
.optional()
.isLength({ min: 8 })
.withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/)
.withMessage('Password must contain uppercase, lowercase, number and special character'),
]; ];
// ===== SHIFT PLAN VALIDATION ===== // ===== SHIFT PLAN VALIDATION =====
@@ -284,7 +287,7 @@ export const validateCreateFromPreset = [
body('presetName') body('presetName')
.isLength({ min: 1 }) .isLength({ min: 1 })
.withMessage('Preset name is required') .withMessage('Preset name is required')
.isIn(['standardWeek', 'extendedWeek', 'weekendFocused', 'morningOnly', 'eveningOnly', 'ZEBRA_STANDARD']) .isIn(['GENERAL_STANDARD', 'ZEBRA_STANDARD'])
.withMessage('Invalid preset name'), .withMessage('Invalid preset name'),
body('name') body('name')
@@ -340,10 +343,11 @@ export const validateSetupAdmin = [
.escape(), .escape(),
body('password') body('password')
.optional()
.isLength({ min: 8 }) .isLength({ min: 8 })
.withMessage('Password must be at least 8 characters') .withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/)
.withMessage('Password must contain uppercase, lowercase and number') .withMessage('Password must contain uppercase, lowercase, number and special character'),
]; ];
// ===== SCHEDULING VALIDATION ===== // ===== SCHEDULING VALIDATION =====

View File

@@ -22,6 +22,8 @@ const app = express();
const PORT = 3002; const PORT = 3002;
const isDevelopment = process.env.NODE_ENV === 'development'; const isDevelopment = process.env.NODE_ENV === 'development';
app.set('trust proxy', true);
// Security configuration // Security configuration
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
console.info('Checking for JWT_SECRET'); console.info('Checking for JWT_SECRET');
@@ -34,14 +36,20 @@ if (process.env.NODE_ENV === 'production') {
// Security headers // Security headers
app.use(helmet({ app.use(helmet({
contentSecurityPolicy: isDevelopment ? false : { contentSecurityPolicy: {
directives: { directives: {
defaultSrc: ["'self'"], defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"], styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"], imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
}, },
}, },
hsts: false,
crossOriginEmbedderPolicy: false crossOriginEmbedderPolicy: false
})); }));

View File

@@ -9,41 +9,42 @@ generate_secret() {
tr -dc 'A-Za-z0-9!@#$%^&*()_+-=' < /dev/urandom | head -c $length tr -dc 'A-Za-z0-9!@#$%^&*()_+-=' < /dev/urandom | head -c $length
} }
# Prüfe ob .env existiert, falls nicht erstelle sie # Prüfe ob .env existiert
if [ ! -f /app/.env ]; then if [ ! -f /app/.env ]; then
echo "📝 Erstelle .env Datei..." echo "📝 Erstelle .env Datei..."
# Generiere automatisch ein sicheres JWT Secret falls nicht gesetzt # Verwende vorhandenes JWT_SECRET oder generiere ein neues
if [ -z "$JWT_SECRET" ] || [ "$JWT_SECRET" = "your-secret-key-please-change" ]; then if [ -z "$JWT_SECRET" ] || [ "$JWT_SECRET" = "your-secret-key-please-change" ]; then
export JWT_SECRET=$(generate_secret 64) export JWT_SECRET=$(generate_secret 64)
echo "🔑 Automatisch generiertes JWT Secret wurde erstellt" echo "🔑 Automatisch sicheres JWT Secret generiert"
else
echo "🔑 Verwende vorhandenes JWT Secret aus Umgebungsvariable"
fi fi
# Erstelle .env aus Template # Erstelle .env aus Template mit envsubst
envsubst < /app/.env.template > /app/.env envsubst < /app/.env.template > /app/.env
echo "✅ .env Datei erstellt"
# Logge die ersten Zeilen (ohne Secrets)
echo "✅ .env Datei erstellt mit folgenden Einstellungen:"
head -n 5 /app/.env
else else
echo " .env Datei existiert bereits" echo " .env Datei existiert bereits"
# Validiere bestehende .env Datei # Wenn .env existiert, aber JWT_SECRET Umgebungsvariable gesetzt ist, aktualisiere sie
if ! grep -q "JWT_SECRET=" /app/.env; then if [ -n "$JWT_SECRET" ] && [ "$JWT_SECRET" != "your-secret-key-please-change" ]; then
echo "❌ Fehler: JWT_SECRET nicht in .env gefunden" echo "🔑 Aktualisiere JWT Secret in .env Datei"
exit 1 # Aktualisiere nur das JWT_SECRET in der .env Datei
sed -i "s/^JWT_SECRET=.*/JWT_SECRET=$JWT_SECRET/" /app/.env
fi fi
fi fi
# Sicherheitsüberprüfungen # Validiere dass JWT_SECERT nicht der Standardwert ist
if grep -q "your-secret-key" /app/.env; then if grep -q "JWT_SECRET=your-secret-key-please-change" /app/.env; then
echo "❌ FEHLER: Standard JWT Secret in .env gefunden - bitte ändern!" echo "❌ FEHLER: Standard JWT Secret in .env gefunden!"
echo "❌ Bitte setzen Sie JWT_SECRET Umgebungsvariable"
exit 1 exit 1
fi fi
# Setze sichere Berechtigungen # Setze sichere Berechtigungen
chmod 600 /app/.env chmod 600 /app/.env
chown -R schichtplaner:nodejs /app
echo "🔧 Starte Anwendung..." echo "🔧 Starte Anwendung..."
exec "$@" exec "$@"

View File

@@ -1,4 +1,4 @@
// frontend/src/pages/Features/Features.tsx // frontend/src/components/Layou/FooterLinks/Features/Features.tsx
import React from 'react'; import React from 'react';
const Features: React.FC = () => { const Features: React.FC = () => {

View File

@@ -20,7 +20,7 @@ interface AuthContextType {
} }
const AuthContext = createContext<AuthContextType | undefined>(undefined); const AuthContext = createContext<AuthContextType | undefined>(undefined);
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || '/api'; const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';
interface AuthProviderProps { interface AuthProviderProps {
children: ReactNode; children: ReactNode;

View File

@@ -107,7 +107,7 @@
.createButton { .createButton {
padding: 10px 20px; padding: 10px 20px;
background-color: #2ecc71; background-color: #51258f;
color: white; color: white;
border: none; border: none;
border-radius: 4px; border-radius: 4px;
@@ -116,7 +116,7 @@
} }
.createButton:hover { .createButton:hover {
background-color: #27ae60; background-color: #51258f;
} }
.createButton:disabled { .createButton:disabled {

View File

@@ -1,6 +1,6 @@
// frontend/src/services/authService.ts // frontend/src/services/authService.ts
import { Employee } from '../models/Employee'; import { Employee } from '../models/Employee';
const API_BASE = process.env.REACT_APP_API_BASE_URL || '/api'; const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';
export interface LoginRequest { export interface LoginRequest {
email: string; email: string;
@@ -24,7 +24,7 @@ class AuthService {
private token: string | null = null; private token: string | null = null;
async login(credentials: LoginRequest): Promise<AuthResponse> { async login(credentials: LoginRequest): Promise<AuthResponse> {
const response = await fetch(`${API_BASE}/auth/login`, { const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials) body: JSON.stringify(credentials)
@@ -39,12 +39,11 @@ class AuthService {
this.token = data.token; this.token = data.token;
localStorage.setItem('token', data.token); localStorage.setItem('token', data.token);
localStorage.setItem('employee', JSON.stringify(data.employee)); localStorage.setItem('employee', JSON.stringify(data.employee));
return data; return data;
} }
async register(userData: RegisterRequest): Promise<AuthResponse> { async register(userData: RegisterRequest): Promise<AuthResponse> {
const response = await fetch(`${API_BASE}/employees`, { const response = await fetch(`${API_BASE_URL}/employees`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData) body: JSON.stringify(userData)
@@ -73,7 +72,7 @@ class AuthService {
} }
try { try {
const response = await fetch(`${API_BASE}/auth/me`, { const response = await fetch(`${API_BASE_URL}/auth/me`, {
headers: { headers: {
'Authorization': `Bearer ${token}` 'Authorization': `Bearer ${token}`
} }

View File

@@ -1,164 +1,57 @@
// vite.config.ts
import { defineConfig, loadEnv } from 'vite' import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import { resolve } from 'path' import { resolve } from 'path'
// Security-focused Vite configuration
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
const isProduction = mode === 'production' const isProduction = mode === 'production'
const isDevelopment = mode === 'development' const isDevelopment = mode === 'development'
// Load environment variables securely
const env = loadEnv(mode, process.cwd(), '') const env = loadEnv(mode, process.cwd(), '')
// Strictly defined client-safe environment variables // 🆕 WICHTIG: Relative Pfade für Production
const clientEnv = { const clientEnv = {
NODE_ENV: mode, NODE_ENV: mode,
ENABLE_PRO: env.ENABLE_PRO || 'false', ENABLE_PRO: env.ENABLE_PRO || 'false',
VITE_APP_TITLE: env.APP_TITLE || 'Shift Planning App', VITE_APP_TITLE: env.APP_TITLE || 'Shift Planning App',
VITE_API_URL: isProduction ? '/api' : 'http://localhost:3002/api', VITE_API_URL: isProduction ? '/api' : '/api',
} }
return { return {
plugins: [ plugins: [react()],
react({
// React specific security settings
jsxRuntime: 'automatic',
babel: {
plugins: [
// Remove console in production
isProduction && ['babel-plugin-transform-remove-console', { exclude: ['error', 'warn'] }]
].filter(Boolean)
}
})
],
server: { server: {
port: 3003, port: 3003,
host: true, host: true,
open: isDevelopment, open: isDevelopment,
// Security headers for dev server
headers: {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), location=()'
},
proxy: { proxy: {
'/api': { '/api': {
target: 'http://localhost:3002', target: 'http://localhost:3002',
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
} }
}, }
// Security: disable HMR in non-dev environments
hmr: isDevelopment
}, },
build: { build: {
outDir: 'dist', outDir: 'dist',
// Security: No source maps in production sourcemap: isDevelopment,
sourcemap: isDevelopment ? 'inline' : false, base: isProduction ? '/' : '/',
// Generate deterministic hashes for better caching and security
assetsDir: 'assets',
rollupOptions: { rollupOptions: {
output: { output: {
// Security: Use content hashes for cache busting and integrity
chunkFileNames: 'assets/[name]-[hash].js', chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js', entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]', assetFileNames: 'assets/[name]-[hash].[ext]',
// Security: Manual chunks to separate vendor code
manualChunks: (id) => {
if (id.includes('node_modules')) {
if (id.includes('react') || id.includes('react-dom')) {
return 'vendor-react'
}
if (id.includes('react-router-dom')) {
return 'vendor-router'
}
return 'vendor'
}
}
} }
}, },
// Minification with security-focused settings
minify: isProduction ? 'terser' : false, minify: isProduction ? 'terser' : false,
terserOptions: isProduction ? { terserOptions: isProduction ? {
compress: { compress: {
drop_console: true, drop_console: true,
drop_debugger: true, drop_debugger: true,
// Security: Remove potentially sensitive code pure_funcs: ['console.log', 'console.debug', 'console.info']
pure_funcs: [
'console.log',
'console.info',
'console.debug',
'console.warn',
'console.trace',
'console.table',
'debugger'
],
dead_code: true,
if_return: true,
comparisons: true,
loops: true,
hoist_funs: true,
hoist_vars: true,
reduce_vars: true,
booleans: true,
conditionals: true,
evaluate: true,
sequences: true,
unused: true
},
mangle: {
// Security: Obfuscate code
toplevel: true,
keep_classnames: false,
keep_fnames: false,
reserved: [
'React',
'ReactDOM',
'useState',
'useEffect',
'useContext',
'createElement'
]
},
format: {
comments: false,
beautify: false,
// Security: ASCII only to prevent encoding attacks
ascii_only: true
} }
} : undefined, } : undefined,
// Security: Report bundle size issues
reportCompressedSize: true,
chunkSizeWarningLimit: 1000,
// Security: Don't expose source paths
assetsInlineLimit: 4096
},
preview: {
port: 3004,
headers: {
// Security headers for preview server
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Content-Security-Policy': `
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
`.replace(/\s+/g, ' ').trim()
}
}, },
resolve: { resolve: {
@@ -174,30 +67,9 @@ export default defineConfig(({ mode }) => {
} }
}, },
// ✅ SICHER: Strict environment variable control
define: Object.keys(clientEnv).reduce((acc, key) => { define: Object.keys(clientEnv).reduce((acc, key) => {
acc[`import.meta.env.${key}`] = JSON.stringify(clientEnv[key]) acc[`import.meta.env.${key}`] = JSON.stringify(clientEnv[key])
return acc return acc
}, {} as Record<string, string>), }, {} as Record<string, string>)
// Security: Clear build directory
emptyOutDir: true,
// Security: Optimize dependencies
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom'],
exclude: ['@vitejs/plugin-react']
},
// Security: CSS configuration
css: {
devSourcemap: isDevelopment,
modules: {
localsConvention: 'camelCase',
generateScopedName: isProduction
? '[hash:base64:8]'
: '[name]__[local]--[hash:base64:5]'
}
}
} }
}) })

125
package-lock.json generated
View File

@@ -280,6 +280,7 @@
"backend/node_modules/@types/node": { "backend/node_modules/@types/node": {
"version": "24.7.0", "version": "24.7.0",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.14.0" "undici-types": "~7.14.0"
} }
@@ -349,17 +350,6 @@
"license": "ISC", "license": "ISC",
"optional": true "optional": true
}, },
"backend/node_modules/acorn": {
"version": "8.15.0",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"backend/node_modules/acorn-walk": { "backend/node_modules/acorn-walk": {
"version": "8.3.4", "version": "8.3.4",
"dev": true, "dev": true,
@@ -2038,6 +2028,7 @@
"frontend": { "frontend": {
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"date-fns": "4.1.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router-dom": "^6.28.0" "react-router-dom": "^6.28.0"
@@ -2048,7 +2039,9 @@
"@types/react-dom": "^19.0.0", "@types/react-dom": "^19.0.0",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"babel-plugin-transform-remove-console": "6.9.4",
"esbuild": "^0.21.0", "esbuild": "^0.21.0",
"terser": "5.44.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"vite": "^6.0.7" "vite": "^6.0.7"
} }
@@ -2078,6 +2071,7 @@
"version": "7.28.5", "version": "7.28.5",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -2338,6 +2332,17 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5", "version": "1.5.5",
"dev": true, "dev": true,
@@ -2388,10 +2393,6 @@
"win32" "win32"
] ]
}, },
"node_modules/@schichtenplaner/premium": {
"resolved": "premium",
"link": true
},
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.5", "version": "7.20.5",
"dev": true, "dev": true,
@@ -2443,6 +2444,7 @@
"version": "20.19.23", "version": "20.19.23",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
@@ -2451,6 +2453,7 @@
"version": "19.2.2", "version": "19.2.2",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
@@ -2514,12 +2517,32 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/array-flatten": { "node_modules/array-flatten": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/babel-plugin-transform-remove-console": {
"version": "6.9.4",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz",
"integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==",
"dev": true,
"license": "MIT"
},
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.8.20", "version": "2.8.20",
"dev": true, "dev": true,
@@ -2585,6 +2608,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.8.19", "baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751", "caniuse-lite": "^1.0.30001751",
@@ -2599,6 +2623,13 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -2656,6 +2687,13 @@
], ],
"license": "CC-BY-4.0" "license": "CC-BY-4.0"
}, },
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -2702,6 +2740,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"devOptional": true, "devOptional": true,
@@ -3368,6 +3416,7 @@
"version": "4.0.3", "version": "4.0.3",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -3457,6 +3506,7 @@
"node_modules/react": { "node_modules/react": {
"version": "19.2.0", "version": "19.2.0",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -3464,6 +3514,7 @@
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "19.2.0", "version": "19.2.0",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@@ -3730,6 +3781,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"dev": true, "dev": true,
@@ -3738,6 +3799,17 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -3747,6 +3819,26 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/terser": {
"version": "5.44.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.15", "version": "0.2.15",
"dev": true, "dev": true,
@@ -3788,6 +3880,7 @@
"version": "5.9.3", "version": "5.9.3",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -3870,6 +3963,7 @@
"version": "6.4.1", "version": "6.4.1",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.4.4", "fdir": "^6.4.4",
@@ -4002,6 +4096,7 @@
"premium": { "premium": {
"name": "@schichtenplaner/premium", "name": "@schichtenplaner/premium",
"version": "1.0.0", "version": "1.0.0",
"extraneous": true,
"workspaces": [ "workspaces": [
"backendPRO", "backendPRO",
"frontendPRO" "frontendPRO"