180 lines
6.7 KiB
PHP
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');
|
|
}
|
|
});
|
|
};
|