initialization
This commit is contained in:
commit
9ff09693ed
92 files changed
+46209
No files matched your search
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
},
|
||||
]);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in new issue
Block a user