get('/', function (Request $request, Response $response, $args) { $html = file_get_contents(__DIR__ . '/../templates/index.html'); $response->getBody()->write($html); return $response->withHeader('Content-Type', 'text/html'); }); // API routes $app->group('/api/v1', function (Group $group) { // Password generation endpoints $group->get('/passwords', PasswordController::class . ':getPasswordTypes'); $group->get('/passwords/generators', PasswordController::class . ':getGenerators'); $group->post('/passwords/generate', PasswordController::class . ':generatePasswords'); // NEW: Available wordlists endpoint for dynamic language dropdown $group->get('/passwords/wordlists', PasswordController::class . ':getAvailableWordlists'); // Health check $group->get('/health', function (Request $request, Response $response) { $response->getBody()->write(json_encode([ 'status' => 'healthy', 'version' => $_ENV['APP_VERSION'] ?? '1.0.0', 'timestamp' => date('c') ])); return $response->withHeader('Content-Type', 'application/json'); }); // Updated locales endpoint with fixed pattern validation $group->get('/locales/{lang:[A-Za-z_]+}', function (Request $request, Response $response, array $args) { $lang = $args['lang']; // Updated supported languages list to match frontend and wordlists $supportedLangs = ['en', 'de', 'de_CH', 'fr', 'it', 'es', 'pt', 'ja', 'zh']; if (!in_array($lang, $supportedLangs, true)) { $lang = 'en'; // fallback } // Preserve Swiss German locale when available, otherwise fall back to German if ($lang === 'ch') { $lang = 'de'; } if ($lang === 'de_CH' && !file_exists(__DIR__ . '/../locales/de_CH.json')) { $lang = 'de'; } $path = __DIR__ . "/../locales/{$lang}.json"; if (!file_exists($path)) { return $response->withStatus(404); } $content = file_get_contents($path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'application/json'); }); }); /* // API documentation $app->get('/docs', function (Request $request, Response $response, $args) { $html = file_get_contents(__DIR__ . '/../templates/docs.html'); $response->getBody()->write($html); return $response->withHeader('Content-Type', 'text/html'); }); */ // Serve static assets $app->get('/assets/{type}/{file}', function (Request $request, Response $response, array $args) { $type = $args['type']; $file = $args['file']; $path = __DIR__ . "/../assets/{$type}/{$file}"; if (!file_exists($path)) { return $response->withStatus(404); } $content = file_get_contents($path); $contentType = $type === 'css' ? 'text/css' : 'application/javascript'; $response->getBody()->write($content); return $response->withHeader('Content-Type', $contentType); }); // Serve locale files directly with improved pattern matching $app->get('/locales/{file:[A-Za-z_]+\.json}', function (Request $request, Response $response, array $args) { $file = $args['file']; $path = __DIR__ . "/../locales/{$file}"; if (!file_exists($path)) { return $response->withStatus(404); } $content = file_get_contents($path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'application/json'); }); // Main documentation route (English - default) $app->get('/docs', function ($request, $response) { $docFile = __DIR__ . '/../docs/index.html'; if (file_exists($docFile)) { $response->getBody()->write(file_get_contents($docFile)); return $response->withHeader('Content-Type', 'text/html; charset=utf-8'); } else { return $response->withStatus(404)->write('Documentation not found'); } }); // Language-specific documentation routes $app->get('/docs-{lang}', function ($request, $response, array $args) { $lang = $args['lang']; // Allowed languages $allowedLanguages = ['de', 'fr', 'es', 'it', 'ja', 'zh']; if (!in_array($lang, $allowedLanguages)) { return $response->withStatus(302)->withHeader('Location', '/docs'); } $docFile = __DIR__ . "/../docs/docs-{$lang}.html"; if (file_exists($docFile)) { $response->getBody()->write(file_get_contents($docFile)); return $response->withHeader('Content-Type', 'text/html; charset=utf-8'); } else { // Fallback to English $fallbackFile = __DIR__ . '/../docs/index.html'; if (file_exists($fallbackFile)) { $response->getBody()->write(file_get_contents($fallbackFile)); return $response->withHeader('Content-Type', 'text/html; charset=utf-8'); } else { return $response->withStatus(404)->write('Documentation not found'); } } }); // Static assets for documentation $app->get('/docs/assets/{type}/{file}', function ($request, $response, array $args) { $type = $args['type']; $file = $args['file']; // Security: prevent directory traversal if (strpos($file, '..') !== false || strpos($type, '..') !== false) { return $response->withStatus(403)->write('Access denied'); } $assetFile = __DIR__ . "/../docs/assets/{$type}/{$file}"; if (file_exists($assetFile)) { $mimeTypes = [ 'css' => 'text/css', 'js' => 'application/javascript', 'png' => 'image/png', 'jpg' => 'image/jpeg', 'svg' => 'image/svg+xml' ]; $extension = pathinfo($file, PATHINFO_EXTENSION); $mimeType = $mimeTypes[$extension] ?? 'text/plain'; $response->getBody()->write(file_get_contents($assetFile)); return $response->withHeader('Content-Type', $mimeType); } else { return $response->withStatus(404)->write('Asset not found'); } }); };