initialization

This commit is contained in:
Reindl David (IT-PTR-CEN2-SL10) committed 2026-10-07 01:16:57 +02:00
commit 9ff09693ed
92 files changed
+46209

No files matched your search

+133
View File
@@ -0,0 +1,133 @@
{
"app": {
"name": "Password Generator",
"version": "4.1.0",
"environment": "production"
},
"locale": {
"default": "en",
"supported": [
"en",
"de",
"de_CH",
"fr",
"it",
"es",
"pt",
"ja",
"zh"
],
"fallback": "en",
"languages": {
"en": "English",
"de": "Deutsch",
"de_CH": "Schwyzerdütsch",
"fr": "Français",
"it": "Italiano",
"es": "Español",
"pt": "Português",
"ja": "日本語",
"zh": "中文"
}
},
"llm": {
"default_provider": "openai",
"timeout": 30,
"max_tokens": 500,
"temperature": 0.7,
"retry_attempts": 3,
"retry_delay": 1000,
"api_keys": {
"openai": "sk-example-openai-key",
"claude": "sk-ant-example-claude-key",
"gemini": "example-gemini-api-key",
"perplexity": "pplx-example-perplexity-key"
},
"providers": {
"openai": {
"name": "OpenAI",
"models": {
"gpt-3.5-turbo": "GPT-3.5 Turbo",
"gpt-4": "GPT-4",
"gpt-4-turbo": "GPT-4 Turbo",
"gpt-4o": "GPT-4 Omni"
},
"default_model": "gpt-3.5-turbo"
},
"claude": {
"name": "Claude",
"models": {
"claude-3-opus-20240229": "Claude 3 Opus",
"claude-3-sonnet-20240229": "Claude 3 Sonnet",
"claude-3-haiku-20240307": "Claude 3 Haiku"
},
"default_model": "claude-3-sonnet-20240229"
},
"gemini": {
"name": "Gemini",
"models": {
"gemini-pro": "Gemini Pro",
"gemini-1.5-pro": "Gemini 1.5 Pro",
"gemini-1.5-flash": "Gemini 1.5 Flash"
},
"default_model": "gemini-pro"
},
"perplexity": {
"name": "Perplexity",
"models": {
"llama-3-sonar-small-32k-online": "Llama 3 Sonar Small (32k)",
"llama-3-sonar-large-32k-online": "Llama 3 Sonar Large (32k)",
"llama-3-8b-instruct": "Llama 3 8B Instruct",
"llama-3-70b-instruct": "Llama 3 70B Instruct"
},
"default_model": "llama-3-sonar-small-32k-online"
}
}
},
"generator": {
"max_passwords_per_request": 100,
"default_count": 1,
"roulette_interval_seconds": 3,
"xkcd": {
"wordlists_path": "data/wordlists",
"wordlists": {
"en": {
"label": "EFF Large Wordlist (English)",
"file": "example_en_wordlist.txt"
},
"de": {
"label": "German Common Words",
"file": "example_de_wordlist.txt"
},
"fr": {
"label": "French Common Words",
"file": "example_fr_wordlist.txt"
},
"it": {
"label": "Italian Common Words",
"file": "example_it_wordlist.txt"
},
"es": {
"label": "Spanish Common Words",
"file": "example_es_wordlist.txt"
}
}
},
"cache_enabled": true,
"cache_ttl": 3600
},
"security": {
"rate_limit_enabled": true,
"rate_limit_max_requests": 120,
"rate_limit_window": 3600,
"require_https": true,
"cors_enabled": true,
"cors_origins": [
"https://yourdomain.com"
]
},
"logging": {
"enabled": true,
"level": "info"
}
}
+131
View File
@@ -0,0 +1,131 @@
{
"app": {
"name": "Password Generator",
"version": "4.1.0",
"environment": "production"
},
"locale": {
"default": "en",
"supported": [
"en",
"de",
"de_CH",
"fr",
"it",
"es",
"pt",
"ja",
"zh"
],
"fallback": "en",
"languages": {
"en": "English",
"de": "Deutsch",
"de_CH": "Schwyzerdütsch",
"fr": "Français",
"it": "Italiano",
"es": "Español",
"pt": "Português",
"ja": "日本語",
"zh": "中文"
}
},
"llm": {
"default_provider": "openai",
"timeout": 30,
"max_tokens": 500,
"temperature": 0.7,
"retry_attempts": 3,
"retry_delay": 1000,
"providers": {
"openai": {
"name": "OpenAI",
"models": {
"gpt-3.5-turbo": "GPT-3.5 Turbo",
"gpt-4": "GPT-4",
"gpt-4-turbo": "GPT-4 Turbo",
"gpt-4o": "GPT-4 Omni"
},
"default_model": "gpt-3.5-turbo"
},
"claude": {
"name": "Claude",
"models": {
"claude-3-opus-20240229": "Claude 3 Opus",
"claude-3-sonnet-20240229": "Claude 3 Sonnet",
"claude-3-haiku-20240307": "Claude 3 Haiku"
},
"default_model": "claude-3-sonnet-20240229"
},
"gemini": {
"name": "Gemini",
"models": {
"gemini-pro": "Gemini Pro",
"gemini-1.5-pro": "Gemini 1.5 Pro",
"gemini-1.5-flash": "Gemini 1.5 Flash"
},
"default_model": "gemini-pro"
},
"perplexity": {
"name": "Perplexity",
"models": {
"llama-3-sonar-small-32k-online": "Llama 3 Sonar Small (32k)",
"llama-3-sonar-large-32k-online": "Llama 3 Sonar Large (32k)",
"llama-3-8b-instruct": "Llama 3 8B Instruct",
"llama-3-70b-instruct": "Llama 3 70B Instruct"
},
"default_model": "llama-3-sonar-small-32k-online"
}
}
},
"generator": {
"max_passwords_per_request": 100,
"default_count": 1,
"roulette_interval_seconds": 3,
"xkcd": {
"wordlists_path": "data/wordlists",
"wordlists": {
"en": {
"label": "EFF English Wordlist",
"file": "eff_large_wordlist_en.txt"
},
"de": {
"label": "German Common Words",
"file": "german_common_words_de.txt"
},
"ch": {
"label": "Slängikon",
"file": "swiss_german_slaengikon.txt"
},
"fr": {
"label": "French Common Words",
"file": "french_common_words_fr.txt"
},
"it": {
"label": "Italian Common Words",
"file": "italian_common_words_it.txt"
},
"es": {
"label": "Spanish Common Words",
"file": "spanish_common_words_es.txt"
}
}
},
"cache_enabled": true,
"cache_ttl": 3600
},
"security": {
"rate_limit_enabled": true,
"rate_limit_max_requests": 120,
"rate_limit_window": 3600,
"require_https": true,
"cors_enabled": true,
"cors_origins": [
"https://yourdomain.com"
]
},
"logging": {
"enabled": true,
"level": "info"
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
use App\Api\PasswordController;
use App\Managers\PasswordGeneratorManager;
use App\Utils\ResponseHelper;
use App\Utils\SecurityConfig;
use DI\ContainerBuilder;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
LoggerInterface::class => function (ContainerInterface $c) {
$settings = [
'name' => $_ENV['APP_NAME'] ?? 'password-generator',
'path' => __DIR__ . '/../' . ($_ENV['LOG_PATH'] ?? 'logs/app.log'),
'level' => SecurityConfig::resolveLogLevel($_ENV['LOG_LEVEL'] ?? 'warning'),
];
$logger = new Logger($settings['name']);
$processor = new UidProcessor();
$logger->pushProcessor($processor);
$handler = new StreamHandler($settings['path'], $settings['level']);
$logger->pushHandler($handler);
return $logger;
},
ResponseHelper::class => function (ContainerInterface $c) {
return new ResponseHelper();
},
PasswordGeneratorManager::class => function (ContainerInterface $c) {
return new PasswordGeneratorManager();
},
PasswordController::class => function (ContainerInterface $c) {
return new PasswordController(
$c->get(PasswordGeneratorManager::class),
$c->get(ResponseHelper::class),
$c->get(LoggerInterface::class)
);
},
]);
};
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
use App\Middleware\CorsMiddleware;
use App\Middleware\JsonResponseMiddleware;
use App\Middleware\RateLimitMiddleware;
use Slim\App;
use Slim\Middleware\ErrorMiddleware;
return function (App $app) {
// Parse JSON, form data and XML
$app->addBodyParsingMiddleware();
// Add CORS middleware
$app->add(new CorsMiddleware());
// Add JSON response middleware
$app->add(new JsonResponseMiddleware());
// Add rate limiting middleware
$app->add(new RateLimitMiddleware());
// Add routing middleware
$app->addRoutingMiddleware();
// Add error middleware
$displayErrorDetails = filter_var($_ENV['APP_DEBUG'] ?? 'false', FILTER_VALIDATE_BOOLEAN);
$logErrors = true;
$logErrorDetails = $displayErrorDetails;
$errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logErrors, $logErrorDetails);
return $app;
};
+179
View File
@@ -0,0 +1,179 @@
<?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');
}
});
};