Files
pw-generator/config/routes.php
T

180 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Api\PasswordController;
use Slim\App;
use Slim\Interfaces\RouteCollectorProxyInterface as Group;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
return function (App $app) {
// Serve static frontend
$app->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');
}
});
};