initialization
This commit is contained in:
commit
9ff09693ed
92 files changed
+46209
No files matched your search
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passwort-Generator API - Vollständige Dokumentation</title>
|
||||
<!-- Same styling -->
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1>🔐 Passwort-Generator API - Vollständiger Leitfaden</h1>
|
||||
<nav class="nav-links">
|
||||
<a href="/">← Zurück zum Generator</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- High Entropy Generator in German -->
|
||||
<div class="generator-section">
|
||||
<div class="generator-header">
|
||||
<h3>🔐 Hochentropie-Generator</h3>
|
||||
<p>Kryptographisch sichere Zufallspasswörter mit vollständiger Zeichensatzkontrolle.</p>
|
||||
</div>
|
||||
<div class="generator-content">
|
||||
<h4>Zweck & Anwendungsfall</h4>
|
||||
<p>Generiert traditionelle Zufallspasswörter mit vollständiger Kontrolle über Zeichentypen. Am besten für Systeme, die spezifische Zeichensatzkonformität erfordern oder wenn Sie maximale Zufälligkeit benötigen.</p>
|
||||
|
||||
<h4>Verfügbare Optionen</h4>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">Zahl</span><span class="option-default">16</span>length (Länge)</h4>
|
||||
<p><strong>Bereich:</strong> 8-128 Zeichen</p>
|
||||
<p><strong>Beschreibung:</strong> Gesamte Passwortlänge. Längere Passwörter bieten exponentiell mehr Sicherheit.</p>
|
||||
<p><strong>Sicherheitsauswirkung:</strong> Jedes zusätzliche Zeichen verdoppelt ungefähr die Zeit, die zum Knacken des Passworts erforderlich ist.</p>
|
||||
</div>
|
||||
|
||||
<!-- Continue with other options in German -->
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
+974
@@ -0,0 +1,974 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>API Documentation - Password Generator</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: white;
|
||||
padding: 20px 0;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header .container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.header .nav-links {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header .nav-links a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
padding: 8px 16px;
|
||||
border-radius: 5px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.header .nav-links a:hover {
|
||||
background: #ecf0f1;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.intro-section h2 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.intro-section p {
|
||||
color: #7f8c8d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.base-url {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.downloads-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.downloads-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.download-card {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.download-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.download-card h4 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.download-card .icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.download-card p {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.download-btn:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
|
||||
.swagger-container {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.swagger-header {
|
||||
background: #34495e;
|
||||
color: white;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
.swagger-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.swagger-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#swagger-ui {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.method-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.method-get { background: #27ae60; color: white; }
|
||||
.method-post { background: #e74c3c; color: white; }
|
||||
.method-put { background: #f39c12; color: white; }
|
||||
.method-delete { background: #e67e22; color: white; }
|
||||
|
||||
.features-section {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
padding: 20px;
|
||||
border-left: 4px solid #3498db;
|
||||
background: #f8f9fa;
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.feature-item h4 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.feature-item p {
|
||||
color: #7f8c8d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.code-example {
|
||||
background: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
overflow-x: auto;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #34495e;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #2c3e50;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 60px;
|
||||
padding: 40px 0;
|
||||
background: white;
|
||||
border-top: 1px solid #ecf0f1;
|
||||
text-align: center;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 15px 20px;
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
font-weight: 500;
|
||||
z-index: 1000;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.notification.show {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
/* Swagger UI Customizations */
|
||||
.swagger-ui .info .title {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.swagger-ui .scheme-container {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.swagger-ui .info .description {
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock.opblock-get .opblock-summary-method {
|
||||
background: #27ae60;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock.opblock-post .opblock-summary-method {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock-tag {
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header .container {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.downloads-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1>🔐 Password Generator API</h1>
|
||||
<nav class="nav-links">
|
||||
<a href="/">← Back to Generator</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-container">
|
||||
<div class="intro-section">
|
||||
<h2>API Documentation</h2>
|
||||
<p>Complete OpenAPI 3.0 specification for the Password Generator API. Generate secure passwords with multiple algorithms, manage wordlists dynamically, and support international languages.</p>
|
||||
|
||||
<div class="base-url">
|
||||
Base URL: <code>http://localhost:8080/api/v1</code>
|
||||
</div>
|
||||
|
||||
<div class="features-grid">
|
||||
<div class="feature-item">
|
||||
<h4>🔐 Multiple Algorithms</h4>
|
||||
<p>High-entropy, XKCD, Diceware, PWGen, and Passkey generation methods</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<h4>🌍 Internationalization</h4>
|
||||
<p>Dynamic wordlist discovery and multi-language UI support</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<h4>📊 Entropy Analysis</h4>
|
||||
<p>Real-time password strength calculation and security metrics</p>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<h4>🔧 Configurable</h4>
|
||||
<p>Extensive customization options for all password types</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="downloads-section">
|
||||
<h2>Download Configuration Files</h2>
|
||||
<p>Get the complete API specification in various formats for integration with your tools and workflows.</p>
|
||||
|
||||
<div class="downloads-grid">
|
||||
<div class="download-card">
|
||||
<h4><span class="icon">📄</span> OpenAPI JSON</h4>
|
||||
<p>Complete OpenAPI 3.0 specification in JSON format. Perfect for code generation, API testing, and integration with development tools.</p>
|
||||
<a href="#" class="download-btn" id="download-json">Download JSON</a>
|
||||
</div>
|
||||
|
||||
<div class="download-card">
|
||||
<h4><span class="icon">📋</span> OpenAPI YAML</h4>
|
||||
<p>Human-readable YAML format specification. Ideal for documentation, version control, and collaborative API development.</p>
|
||||
<a href="#" class="download-btn" id="download-yaml">Download YAML</a>
|
||||
</div>
|
||||
|
||||
<div class="download-card">
|
||||
<h4><span class="icon">🚀</span> Postman Collection</h4>
|
||||
<p>Ready-to-use Postman collection with all endpoints, examples, and environment variables for immediate API testing.</p>
|
||||
<a href="#" class="download-btn" id="download-postman">Download Collection</a>
|
||||
</div>
|
||||
|
||||
<div class="download-card">
|
||||
<h4><span class="icon">⚡</span> cURL Examples</h4>
|
||||
<p>Complete set of cURL commands with examples for all endpoints. Perfect for command-line testing and scripting.</p>
|
||||
<a href="#" class="download-btn" id="download-curl">Download Examples</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="swagger-container">
|
||||
<div class="swagger-header">
|
||||
<h3>Interactive API Explorer</h3>
|
||||
</div>
|
||||
<div class="swagger-content">
|
||||
<div id="swagger-ui"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features-section">
|
||||
<h2>Quick Start</h2>
|
||||
<p>Get started with the Password Generator API in minutes. Here are some common examples:</p>
|
||||
|
||||
<h3>Generate XKCD Passphrase</h3>
|
||||
<button class="copy-btn" onclick="copyToClipboard('curl-xkcd')">Copy</button>
|
||||
<div class="code-example" id="curl-xkcd">curl -X POST "http://localhost:8080/api/v1/passwords/generate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "xkcdpass",
|
||||
"count": 3,
|
||||
"options": {
|
||||
"words": 4,
|
||||
"wordlist": "en",
|
||||
"separator": "-",
|
||||
"add_number": true,
|
||||
"number_position": "random"
|
||||
}
|
||||
}'</div>
|
||||
|
||||
<h3>Check Available Wordlists</h3>
|
||||
<button class="copy-btn" onclick="copyToClipboard('curl-wordlists')">Copy</button>
|
||||
<div class="code-example" id="curl-wordlists">curl -X GET "http://localhost:8080/api/v1/passwords/wordlists"</div>
|
||||
|
||||
<h3>Generate High-Entropy Password</h3>
|
||||
<button class="copy-btn" onclick="copyToClipboard('curl-entropy')">Copy</button>
|
||||
<div class="code-example" id="curl-entropy">curl -X POST "http://localhost:8080/api/v1/passwords/generate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "high-entropy",
|
||||
"count": 1,
|
||||
"options": {
|
||||
"length": 20,
|
||||
"include_symbols": true
|
||||
}
|
||||
}'</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>
|
||||
Password Generator API v2.0.0 •
|
||||
<a href="/">Main Application</a> •
|
||||
<a href="#swagger-ui">Interactive Docs</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-standalone-preset.js"></script>
|
||||
<script>
|
||||
// OpenAPI Specification (embedded)
|
||||
const spec = {
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Password Generator API",
|
||||
"description": "Secure password generation with multiple algorithms and customizable options. Generate high-entropy passwords, XKCD-style passphrases, Diceware passwords, and more with comprehensive internationalization support.",
|
||||
"version": "2.0.0",
|
||||
"contact": {
|
||||
"name": "Password Generator API",
|
||||
"url": "http://localhost:8080"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://opensource.org/licenses/MIT"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:8080/api/v1",
|
||||
"description": "Development server"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/passwords": {
|
||||
"get": {
|
||||
"summary": "Get available password types",
|
||||
"description": "Returns all available password generation types with their descriptions and configuration options.",
|
||||
"operationId": "getPasswordTypes",
|
||||
"tags": ["Password Generation"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of available password generation types",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PasswordTypesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/passwords/generate": {
|
||||
"post": {
|
||||
"summary": "Generate passwords",
|
||||
"description": "Generate one or more passwords using the specified type and custom options.",
|
||||
"operationId": "generatePasswords",
|
||||
"tags": ["Password Generation"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GeneratePasswordRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successfully generated passwords",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GeneratePasswordResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request parameters",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/passwords/wordlists": {
|
||||
"get": {
|
||||
"summary": "Get available wordlists",
|
||||
"description": "Returns all available wordlists for XKCD password generation with availability status. Used by frontend to dynamically populate language options.",
|
||||
"operationId": "getAvailableWordlists",
|
||||
"tags": ["Wordlists"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of available wordlists",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/WordlistsResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/locales/{lang}": {
|
||||
"get": {
|
||||
"summary": "Get localization strings",
|
||||
"description": "Returns translation strings for the specified language code.",
|
||||
"operationId": "getLocalization",
|
||||
"tags": ["Internationalization"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "lang",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "Language code (2-3 letters)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]{2,3}(_[A-Z]{2})?$",
|
||||
"enum": ["en", "de", "de_CH", "fr", "it", "es", "pt", "ja", "zh"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Localization strings for the specified language",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LocalizationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"summary": "Health check",
|
||||
"description": "Returns the current health status of the API service.",
|
||||
"operationId": "healthCheck",
|
||||
"tags": ["System"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "API is healthy",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HealthResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"PasswordTypesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean" },
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"types": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/PasswordType"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"PasswordType": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"options": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"GeneratePasswordRequest": {
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["high-entropy", "xkcdpass", "diceware", "pwgen", "passkey"]
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 50,
|
||||
"default": 1
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"GeneratePasswordResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean" },
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"passwords": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"entropy_bits": { "type": "number" },
|
||||
"generator_info": { "type": "string" },
|
||||
"options_used": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"message": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"WordlistsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean" },
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"wordlists": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/Wordlist"
|
||||
}
|
||||
},
|
||||
"count": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"message": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"Wordlist": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"file": { "type": "string" },
|
||||
"available": { "type": "boolean" },
|
||||
"fallback": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"LocalizationResponse": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"HealthResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["healthy", "degraded", "unhealthy"]
|
||||
},
|
||||
"version": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": { "type": "boolean", "example": false },
|
||||
"error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": { "type": "string" },
|
||||
"code": { "type": "string" },
|
||||
"status": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{ "name": "Password Generation", "description": "Core password generation endpoints" },
|
||||
{ "name": "Wordlists", "description": "Wordlist management for XKCD passwords" },
|
||||
{ "name": "Internationalization", "description": "Localization and language support" },
|
||||
{ "name": "System", "description": "System health and monitoring" }
|
||||
]
|
||||
};
|
||||
|
||||
// Initialize Swagger UI
|
||||
window.onload = function() {
|
||||
SwaggerUIBundle({
|
||||
url: '',
|
||||
spec: spec,
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout",
|
||||
tryItOutEnabled: true,
|
||||
requestInterceptor: (request) => {
|
||||
// Add any custom headers or modifications
|
||||
return request;
|
||||
},
|
||||
responseInterceptor: (response) => {
|
||||
// Handle responses
|
||||
return response;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Download functions
|
||||
function downloadFile(content, filename, contentType) {
|
||||
const blob = new Blob([content], { type: contentType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
showNotification(`Downloaded ${filename}`);
|
||||
}
|
||||
|
||||
function showNotification(message) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'notification';
|
||||
notification.textContent = message;
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => notification.classList.add('show'), 10);
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('show');
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Download event handlers
|
||||
document.getElementById('download-json').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
downloadFile(JSON.stringify(spec, null, 2), 'password-generator-api.json', 'application/json');
|
||||
});
|
||||
|
||||
document.getElementById('download-yaml').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
// Convert JSON to YAML (simplified)
|
||||
const yaml = jsonToYaml(spec);
|
||||
downloadFile(yaml, 'password-generator-api.yaml', 'text/yaml');
|
||||
});
|
||||
|
||||
document.getElementById('download-postman').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const postmanCollection = generatePostmanCollection(spec);
|
||||
downloadFile(JSON.stringify(postmanCollection, null, 2), 'password-generator-api.postman_collection.json', 'application/json');
|
||||
});
|
||||
|
||||
document.getElementById('download-curl').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const curlExamples = generateCurlExamples(spec);
|
||||
downloadFile(curlExamples, 'password-generator-api-examples.sh', 'text/plain');
|
||||
});
|
||||
|
||||
// Copy to clipboard function
|
||||
function copyToClipboard(elementId) {
|
||||
const element = document.getElementById(elementId);
|
||||
const text = element.textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showNotification('Copied to clipboard!');
|
||||
});
|
||||
}
|
||||
|
||||
// Utility functions for file generation
|
||||
function jsonToYaml(obj, indent = 0) {
|
||||
let yaml = '';
|
||||
const spaces = ' '.repeat(indent);
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null) {
|
||||
yaml += `${spaces}${key}: null\n`;
|
||||
} else if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
yaml += `${spaces}${key}:\n`;
|
||||
yaml += jsonToYaml(value, indent + 1);
|
||||
} else if (Array.isArray(value)) {
|
||||
yaml += `${spaces}${key}:\n`;
|
||||
value.forEach(item => {
|
||||
if (typeof item === 'object') {
|
||||
yaml += `${spaces} -\n`;
|
||||
yaml += jsonToYaml(item, indent + 2);
|
||||
} else {
|
||||
yaml += `${spaces} - ${item}\n`;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
yaml += `${spaces}${key}: ${typeof value === 'string' ? `"${value}"` : value}\n`;
|
||||
}
|
||||
}
|
||||
return yaml;
|
||||
}
|
||||
|
||||
function generatePostmanCollection(spec) {
|
||||
return {
|
||||
info: {
|
||||
name: spec.info.title,
|
||||
description: spec.info.description,
|
||||
version: spec.info.version,
|
||||
schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
variable: [
|
||||
{
|
||||
key: "baseUrl",
|
||||
value: spec.servers[0].url,
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
item: Object.entries(spec.paths).map(([path, methods]) => {
|
||||
return Object.entries(methods).map(([method, operation]) => ({
|
||||
name: operation.summary,
|
||||
request: {
|
||||
method: method.toUpperCase(),
|
||||
header: method === 'post' ? [
|
||||
{
|
||||
key: "Content-Type",
|
||||
value: "application/json"
|
||||
}
|
||||
] : [],
|
||||
url: {
|
||||
raw: `{{baseUrl}}${path}`,
|
||||
host: ["{{baseUrl}}"],
|
||||
path: path.split('/').filter(p => p)
|
||||
},
|
||||
body: method === 'post' ? {
|
||||
mode: "raw",
|
||||
raw: JSON.stringify({
|
||||
type: "xkcdpass",
|
||||
count: 1,
|
||||
options: {
|
||||
words: 4,
|
||||
language: "en"
|
||||
}
|
||||
}, null, 2)
|
||||
} : undefined
|
||||
}
|
||||
}));
|
||||
}).flat()
|
||||
};
|
||||
}
|
||||
|
||||
function generateCurlExamples(spec) {
|
||||
const baseUrl = spec.servers[0].url;
|
||||
let examples = `#!/bin/bash
|
||||
# Password Generator API - cURL Examples
|
||||
# Base URL: ${baseUrl}
|
||||
|
||||
echo "Password Generator API Examples"
|
||||
echo "=============================="
|
||||
|
||||
`;
|
||||
|
||||
Object.entries(spec.paths).forEach(([path, methods]) => {
|
||||
Object.entries(methods).forEach(([method, operation]) => {
|
||||
examples += `# ${operation.summary}\n`;
|
||||
examples += `echo "\\n${operation.summary}:"\n`;
|
||||
|
||||
if (method === 'get') {
|
||||
examples += `curl -X GET "${baseUrl}${path}"\n\n`;
|
||||
} else if (method === 'post') {
|
||||
examples += `curl -X POST "${baseUrl}${path}" \\\n`;
|
||||
examples += ` -H "Content-Type: application/json" \\\n`;
|
||||
examples += ` -d '{\n`;
|
||||
examples += ` "type": "xkcdpass",\n`;
|
||||
examples += ` "count": 1,\n`;
|
||||
examples += ` "options": {\n`;
|
||||
examples += ` "words": 4,\n`;
|
||||
examples += ` "wordlist": "en",\n`;
|
||||
examples += ` "add_number": true\n`;
|
||||
examples += ` }\n`;
|
||||
examples += ` }'\n\n`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return examples;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Password Generator API - Complete Documentation</title>
|
||||
<link rel="stylesheet" href="../assets/css/style.css" />
|
||||
<link rel="stylesheet" href="../assets/css/style-docs.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="language-selector">
|
||||
<select id="language-select" class="language-select form-control form-control--language" onchange="switchLanguage(this.value)">
|
||||
<option value="en">🇺🇸 English</option>
|
||||
<option value="de">🇩🇪 Deutsch</option>
|
||||
<option value="de_CH">🇨🇭 Schwyzerdütsch</option>
|
||||
<option value="fr">🇫🇷 Français</option>
|
||||
<option value="es">🇪🇸 Español</option>
|
||||
<option value="pt">🇵🇹 Portugues</option>
|
||||
<option value="it">🇮🇹 Italiano</option>
|
||||
<option value="ja">🇯🇵 日本語</option>
|
||||
<option value="zh">🇨🇳 中文</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1>🔐 Password Generator API - Complete Guide</h1>
|
||||
<nav class="nav-links">
|
||||
<a href="/">← Back to Generator</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-container">
|
||||
<!-- Base URL and Quick Start -->
|
||||
<div class="intro-section">
|
||||
<h2>API Overview</h2>
|
||||
<div class="base-url">
|
||||
Base URL: <code>http://localhost:8080/api/v1</code>
|
||||
</div>
|
||||
|
||||
<div class="security-note">
|
||||
All password generation uses cryptographically secure random number generation for maximum security.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- High Entropy Generator -->
|
||||
<div class="generator-section">
|
||||
<div class="generator-header">
|
||||
<h3>🔐 High Entropy Generator</h3>
|
||||
<p>Cryptographically secure random passwords with full character set control.</p>
|
||||
</div>
|
||||
<div class="generator-content">
|
||||
<h4>Purpose & Use Case</h4>
|
||||
<p>Generate traditional random passwords with complete control over character types. Best for systems requiring specific character set compliance or when you need maximum randomness.</p>
|
||||
|
||||
<h4>Available Options</h4>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">number</span><span class="option-default">16</span>length</h4>
|
||||
<p><strong>Range:</strong> 8-128 characters</p>
|
||||
<p><strong>Description:</strong> Total password length. Longer passwords provide exponentially more security.</p>
|
||||
<p><strong>Security Impact:</strong> Each additional character roughly doubles the time required to crack the password.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">boolean</span><span class="option-default">true</span>include_uppercase</h4>
|
||||
<p><strong>Description:</strong> Include uppercase letters (A-Z) in the character set.</p>
|
||||
<p><strong>Character Set:</strong> ABCDEFGHIJKLMNOPQRSTUVWXYZ (26 characters)</p>
|
||||
<p><strong>Security Impact:</strong> Adds 26 possible characters, significantly increasing entropy.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">boolean</span><span class="option-default">true</span>include_lowercase</h4>
|
||||
<p><strong>Description:</strong> Include lowercase letters (a-z) in the character set.</p>
|
||||
<p><strong>Character Set:</strong> abcdefghijklmnopqrstuvwxyz (26 characters)</p>
|
||||
<p><strong>Note:</strong> At least one character type must be selected.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">boolean</span><span class="option-default">true</span>include_numbers</h4>
|
||||
<p><strong>Description:</strong> Include numeric digits (0-9) in the character set.</p>
|
||||
<p><strong>Character Set:</strong> 0123456789 (10 characters)</p>
|
||||
<p><strong>Compliance:</strong> Required by many corporate password policies.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">boolean</span><span class="option-default">true</span>include_symbols</h4>
|
||||
<p><strong>Description:</strong> Include special symbols in the character set.</p>
|
||||
<p><strong>Character Set:</strong> !@#$%^&*()_+-=[]{}|;:,.<>? (25 characters)</p>
|
||||
<p><strong>Note:</strong> Some systems have restrictions on certain symbols. Validate accepted characters in your target environment.</p>
|
||||
</div>
|
||||
|
||||
<h4>Example Request</h4>
|
||||
<div class="example-request">
|
||||
POST /api/v1/passwords/generate
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"type": "high-entropy",
|
||||
"count": 2,
|
||||
"options": {
|
||||
"length": 20,
|
||||
"include_uppercase": true,
|
||||
"include_lowercase": true,
|
||||
"include_numbers": true,
|
||||
"include_symbols": false
|
||||
}
|
||||
}</div>
|
||||
|
||||
<h4>Example Response</h4>
|
||||
<div class="example-response">
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"passwords": [
|
||||
"Km9xB3nQ2pL8vR5wE6sA",
|
||||
"T7gY4hJ9kM1nP6qU8zC3"
|
||||
],
|
||||
"entropy_bits": 119.5,
|
||||
"generator_info": "High entropy generator with alphanumeric characters",
|
||||
"options_used": {
|
||||
"length": 20,
|
||||
"include_uppercase": true,
|
||||
"include_lowercase": true,
|
||||
"include_numbers": true,
|
||||
"include_symbols": false
|
||||
}
|
||||
},
|
||||
"message": "Generated 2 password(s) of type 'high-entropy'",
|
||||
"timestamp": "2025-08-23T21:24:00+02:00"
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- XKCD Generator -->
|
||||
<div class="generator-section">
|
||||
<div class="generator-header">
|
||||
<h3>🗣️ XKCD Pass Generator</h3>
|
||||
<p>Memorable passphrases using word combinations with multilingual support.</p>
|
||||
</div>
|
||||
<div class="generator-content">
|
||||
<h4>Purpose & Use Case</h4>
|
||||
<p>Generate memorable passphrases based on the famous XKCD comic "Password Strength". Ideal when users need to remember passwords without writing them down. Supports multiple languages for international users.</p>
|
||||
|
||||
<h4>Available Options</h4>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">number</span><span class="option-default">4</span>words</h4>
|
||||
<p><strong>Range:</strong> 3-8 words</p>
|
||||
<p><strong>Description:</strong> Number of words in the passphrase.</p>
|
||||
<p><strong>Security Impact:</strong> Each additional word exponentially increases security. 4 words ≈ 52 bits, 6 words ≈ 77 bits entropy.</p>
|
||||
<p><strong>Memorability:</strong> 4-5 words strike the best balance between security and memorability.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">select</span><span class="option-default">en</span>wordlist</h4>
|
||||
<p><strong>Options:</strong> en, de, de_CH, fr, it, es, pt, ja, zh</p>
|
||||
<p><strong>Description:</strong> Wordlist to use for generation.</p>
|
||||
<p><strong>Wordlist Quality:</strong> Built-in curated collections are selected per language and can be checked dynamically from the wordlist endpoint.</p>
|
||||
<p><strong>Availability:</strong> Check <code>/api/v1/passwords/wordlists</code> for current language availability.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">text</span><span class="option-default">-</span>separator</h4>
|
||||
<p><strong>Description:</strong> Character(s) used to separate words in the passphrase.</p>
|
||||
<p><strong>Single Character:</strong> All separations use the same character (e.g., "word-word-word").</p>
|
||||
<p><strong>Multiple Characters:</strong> Each separation randomly selects from the character set (e.g., "word-word_word.word").</p>
|
||||
<p><strong>Space Handling:</strong> Spaces are excluded from multi-character separator sets for security.</p>
|
||||
<p><strong>Examples:</strong> "-", "_", ".", "-_.", "!@#"</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">boolean</span><span class="option-default">false</span>add_number</h4>
|
||||
<p><strong>Description:</strong> Add a 2-digit number to the passphrase.</p>
|
||||
<p><strong>Security Benefit:</strong> Increases entropy by ~6.6 bits (100 possible values).</p>
|
||||
<p><strong>Position Control:</strong> Use <code>number_position</code> to control placement.</p>
|
||||
</div>
|
||||
|
||||
<div class="option-detail">
|
||||
<h4><span class="option-type">select</span><span class="option-default">end</span>number_position</h4>
|
||||
<p><strong>Requires:</strong> <code>add_number: true</code></p>
|
||||
<p><strong>Options:</strong></p>
|
||||
<ul>
|
||||
<li><code>beginning</code>: "42-word-word-word"</li>
|
||||
<li><code>end</code>: "word-word-word-42"</li>
|
||||
<li><code>random</code>: "word-42-word-word" (random separator position)</li>
|
||||
</ul>
|
||||
<p><strong>Security Note:</strong> Random position provides additional unpredictability.</p>
|
||||
</div>
|
||||
|
||||
<h4>Example Request</h4>
|
||||
<div class="example-request">
|
||||
POST /api/v1/passwords/generate
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"type": "xkcdpass",
|
||||
"count": 1,
|
||||
"options": {
|
||||
"words": 5,
|
||||
"wordlist": "en",
|
||||
"separator": "-_.",
|
||||
"add_number": true,
|
||||
"number_position": "random"
|
||||
}
|
||||
}</div>
|
||||
|
||||
<h4>Example Response</h4>
|
||||
<div class="example-response">
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"passwords": ["horse-battery_42.correct_staple"],
|
||||
"explanations": ["large four-legged animal – device that stores electrical energy – free from error; accurate – basic or principal item"],
|
||||
"entropy_bits": 58.7,
|
||||
"generator_info": "XKCD-style passphrase using 5 words from en wordlist",
|
||||
"options_used": {
|
||||
"words": 5,
|
||||
"wordlist": "en",
|
||||
"separator": "-_.",
|
||||
"add_number": true,
|
||||
"number_position": "random"
|
||||
}
|
||||
},
|
||||
"message": "Generated 1 password(s) of type 'xkcdpass'",
|
||||
"timestamp": "2025-08-23T21:24:00+02:00"
|
||||
}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add similar detailed sections for Diceware, PWGen, and Passkey generators -->
|
||||
|
||||
<!-- Security Recommendations -->
|
||||
<div class="generator-section">
|
||||
<div class="generator-header">
|
||||
<h3>🔒 Security Recommendations</h3>
|
||||
</div>
|
||||
<div class="generator-content">
|
||||
<h4>Choosing the Right Generator</h4>
|
||||
<ul>
|
||||
<li><strong>High Entropy:</strong> Maximum security with strict policy control</li>
|
||||
<li><strong>XKCD Pass:</strong> User memorability, multi-language support needed</li>
|
||||
<li><strong>Diceware:</strong> Maximum security with educational value</li>
|
||||
<li><strong>PWGen:</strong> Pronounceable passwords with balanced strength and usability</li>
|
||||
<li><strong>Passkey:</strong> Pronounceable passwords for verbal communication</li>
|
||||
</ul>
|
||||
|
||||
<h4>Entropy Guidelines</h4>
|
||||
<ul>
|
||||
<li><strong>50+ bits:</strong> Basic security (personal accounts)</li>
|
||||
<li><strong>65+ bits:</strong> Strong security (business accounts)</li>
|
||||
<li><strong>80+ bits:</strong> Excellent security (high-value accounts)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
function switchLanguage(lang) {
|
||||
// Redirect to language-specific documentation
|
||||
const baseUrl = window.location.origin + window.location.pathname;
|
||||
const newUrl = baseUrl.replace('/docs', `/docs-${lang}`);
|
||||
window.location.href = newUrl;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user