server-api swagger
This commit is contained in:
@@ -3,67 +3,270 @@
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>API Dashboard</title>
|
||||
<title>API Dashboard - Swagger</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>🎛️ Pannello di Controllo API</h1>
|
||||
<h1>Pannello di Controllo API</h1>
|
||||
|
||||
<div class="server-info">
|
||||
<p>Il server è attivo.</p>
|
||||
<p>Indirizzo Base: <strong id="base-url">...</strong></p>
|
||||
<p>✅ Il server è attivo</p>
|
||||
<p style="margin-top: 10px; font-size: 0.9em; opacity: 0.9;">URL Server: <strong id="base-url">...</strong></p>
|
||||
</div>
|
||||
|
||||
<div id="container">Loading resources...</div>
|
||||
<div id="container">Caricamento risorse...</div>
|
||||
|
||||
<script>
|
||||
const container = document.getElementById('container');
|
||||
const baseUrlDisplay = document.getElementById('base-url');
|
||||
const container = document.querySelector('#container');
|
||||
const baseUrlDisplay = document.querySelector('#base-url');
|
||||
|
||||
// Imposta l'URL base dinamico (così funziona sia su localhost che su IP di rete)
|
||||
const BASE_URL = window.location.origin;
|
||||
baseUrlDisplay.innerText = BASE_URL;
|
||||
// Variabile globale per l'URL del server
|
||||
let BASE_URL = '';
|
||||
|
||||
// Calcola l'URL con porta 5000 automaticamente
|
||||
function initializeBaseUrl() {
|
||||
const protocol = window.location.protocol;
|
||||
const hostname = window.location.hostname;
|
||||
BASE_URL = `${protocol}//${hostname}:5000`;
|
||||
baseUrlDisplay.innerText = BASE_URL;
|
||||
}
|
||||
|
||||
// Mappa dei metodi HTTP con colori
|
||||
const methodColors = {
|
||||
'GET': '#61affe',
|
||||
'POST': '#49cc90',
|
||||
'PUT': '#fca130',
|
||||
'DELETE': '#f93e3e'
|
||||
};
|
||||
|
||||
async function scanDatabase() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (!contentType || !contentType.includes('application/json')) {
|
||||
throw new Error('L\'endpoint /api non restituisce JSON. Ricevuto: ' + contentType);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const [key, _] of Object.entries(data)) {
|
||||
createResourceCard(key);
|
||||
for (const [resourceName, fields] of Object.entries(data)) {
|
||||
await createResourceSection(resourceName, fields);
|
||||
}
|
||||
|
||||
if (Object.keys(data).length === 0) {
|
||||
container.innerHTML = '<p style="color:#999; text-align:center;">Nessuna risorsa trovata</p>';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
container.innerHTML = `<h3 style="color:red">Errore di connessione al DB: ${error.message}</h3>`;
|
||||
container.innerHTML = `
|
||||
<div style="background:#ffebee; border-left:4px solid #f44336; padding:20px; border-radius:8px;">
|
||||
<h3 style="color:#c62828; margin:0 0 10px 0;">❌ Errore di Connessione</h3>
|
||||
<p style="margin:0; color:#d32f2f; font-family:monospace;">${error.message}</p>
|
||||
<hr style="border:none; border-top:1px solid #ef5350; margin:15px 0;">
|
||||
<p style="margin:0; color:#666; font-size:0.9em;">
|
||||
<strong>Verificare:</strong><br>
|
||||
1. Che il server sia avviato<br>
|
||||
2. Che l'URL sia corretto: <code>${BASE_URL}/api</code><br>
|
||||
3. Che il browser non abbia problemi di CORS
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
console.error('API Error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function createResourceCard(name) {
|
||||
const url = `${BASE_URL}/api/${name}`;
|
||||
const sample = await fetch(url + "/1").catch(() => null);
|
||||
const sampleJson = sample ? await sample.json() : "Nessun dato!";
|
||||
async function createResourceSection(resourceName, fields) {
|
||||
const resourceUrl = `${BASE_URL}/api/${resourceName}`;
|
||||
|
||||
const html = `
|
||||
<div class="resource-card">
|
||||
<div class="resource-header">
|
||||
<span class="resource-name">/${name}</span>
|
||||
</div>
|
||||
<p>Endpoint per richieste GET, POST, ecc:</p>
|
||||
<div class="url-box">${url}</div>
|
||||
|
||||
<h4>Esempio Risultato:</h4>
|
||||
<div class="code-label">GET ${url}/1</div>
|
||||
<div class="preview">${JSON.stringify(sampleJson, null, 2)}</div>
|
||||
</div>
|
||||
// Ottieni un esempio di dato
|
||||
let sampleData = null;
|
||||
try {
|
||||
const sampleResponse = await fetch(resourceUrl);
|
||||
const allData = await sampleResponse.json();
|
||||
sampleData = Array.isArray(allData) && allData.length > 0 ? allData[0] : null;
|
||||
} catch (e) {
|
||||
sampleData = null;
|
||||
}
|
||||
|
||||
// Crea la sezione della risorsa
|
||||
const section = document.createElement('div');
|
||||
section.className = 'resource-section';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = 'resource-section-header';
|
||||
header.innerHTML = `
|
||||
<span class="resource-title">${resourceName}</span>
|
||||
<span class="resource-url">${resourceUrl}</span>
|
||||
<span class="toggle-icon">▼</span>
|
||||
`;
|
||||
container.innerHTML += html;
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.className = 'resource-content';
|
||||
|
||||
// Crea gli endpoint
|
||||
const endpoints = createEndpoints(resourceName, resourceUrl, fields, sampleData);
|
||||
content.innerHTML = endpoints;
|
||||
|
||||
section.appendChild(header);
|
||||
section.appendChild(content);
|
||||
|
||||
// Aggiungi il toggle
|
||||
header.addEventListener('click', () => {
|
||||
content.classList.toggle('expanded');
|
||||
header.classList.toggle('expanded');
|
||||
});
|
||||
|
||||
container.appendChild(section);
|
||||
}
|
||||
|
||||
// Avvio scansione
|
||||
function createEndpoints(resourceName, resourceUrl, fields, sampleData) {
|
||||
let html = '';
|
||||
|
||||
// GET - Recupera tutti i dati
|
||||
html += createEndpointCard(
|
||||
'GET',
|
||||
`${resourceUrl}`,
|
||||
'Recupera tutti i dati',
|
||||
'Query Parameters:\n• _sort=field (ordinamento)\n• _filter=value (filtro)\n• _page=1 (paginazione)\n• _limit=10 (limite)',
|
||||
null,
|
||||
Array.isArray(sampleData) ? `[${JSON.stringify(sampleData, null, 2)}, ...]` : '[ {...}, {...} ]'
|
||||
);
|
||||
|
||||
// GET by ID
|
||||
html += createEndpointCard(
|
||||
'GET',
|
||||
`${resourceUrl}/{id}`,
|
||||
'Recupera un singolo elemento',
|
||||
'Path Parameters:\n• id (ID dell\'elemento)',
|
||||
null,
|
||||
JSON.stringify(sampleData || {}, null, 2)
|
||||
);
|
||||
|
||||
// POST - Crea nuovo
|
||||
const postBody = {};
|
||||
fields.forEach(field => {
|
||||
postBody[field] = `<${field}>`;
|
||||
});
|
||||
html += createEndpointCard(
|
||||
'POST',
|
||||
`${resourceUrl}`,
|
||||
'Crea un nuovo elemento',
|
||||
null,
|
||||
JSON.stringify(postBody, null, 2),
|
||||
JSON.stringify(sampleData || postBody, null, 2)
|
||||
);
|
||||
|
||||
// PUT - Modifica completa
|
||||
const putBody = {};
|
||||
fields.forEach(field => {
|
||||
putBody[field] = `<${field}>`;
|
||||
});
|
||||
html += createEndpointCard(
|
||||
'PUT',
|
||||
`${resourceUrl}/{id}`,
|
||||
'Modifica un elemento (sostituzione completa)',
|
||||
'Path Parameters:\n• id (ID dell\'elemento)',
|
||||
JSON.stringify(putBody, null, 2),
|
||||
JSON.stringify(sampleData || putBody, null, 2)
|
||||
);
|
||||
|
||||
// DELETE
|
||||
html += createEndpointCard(
|
||||
'DELETE',
|
||||
`${resourceUrl}/{id}`,
|
||||
'Elimina un elemento',
|
||||
'Path Parameters:\n• id (ID dell\'elemento)',
|
||||
null,
|
||||
'{}'
|
||||
);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function createEndpointCard(method, endpoint, description, params, body, response) {
|
||||
const methodColor = methodColors[method];
|
||||
const isExpanded = false;
|
||||
|
||||
// Formatta i parametri come lista leggibile
|
||||
const formattedParams = formatParams(params);
|
||||
|
||||
return `
|
||||
<div class="endpoint-card">
|
||||
<div class="endpoint-header">
|
||||
<span class="method-badge" style="background-color: ${methodColor}">${method}</span>
|
||||
<span class="endpoint-path">${endpoint}</span>
|
||||
<span class="endpoint-desc">${description}</span>
|
||||
<span class="endpoint-toggle">+</span>
|
||||
</div>
|
||||
<div class="endpoint-details" data-expanded="false">
|
||||
${formattedParams ? `<div class="detail-section">
|
||||
<strong>Parametri:</strong>
|
||||
${formattedParams}
|
||||
</div>` : ''}
|
||||
${body ? `<div class="detail-section">
|
||||
<strong>Body:</strong>
|
||||
<pre>${escapeHtml(body)}</pre>
|
||||
</div>` : ''}
|
||||
<div class="detail-section">
|
||||
<strong>Risposta:</strong>
|
||||
<pre>${escapeHtml(response)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function formatParams(paramsText) {
|
||||
if (!paramsText) return '';
|
||||
|
||||
const lines = paramsText.split('\n').filter(line => line.trim());
|
||||
let html = '<div class="params-list">';
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.includes('Path Parameters:') || line.includes('Query Parameters:') || line.includes('Body (JSON):')) {
|
||||
const label = line.replace(':', '').trim();
|
||||
html += `<div class="param-category">${label}</div>`;
|
||||
} else if (line.startsWith('•')) {
|
||||
const cleanLine = line.substring(1).trim();
|
||||
const [param, ...desc] = cleanLine.split(' ');
|
||||
const description = desc.join(' ');
|
||||
html += `<div class="param-item"><span class="param-name">${param}</span><span class="param-desc">${description}</span></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Gestione toggle per gli endpoint
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.closest('.endpoint-header')) {
|
||||
const header = e.target.closest('.endpoint-header');
|
||||
const details = header.nextElementSibling;
|
||||
const isExpanded = details.getAttribute('data-expanded') === 'true';
|
||||
|
||||
details.setAttribute('data-expanded', !isExpanded);
|
||||
header.classList.toggle('expanded');
|
||||
}
|
||||
});
|
||||
|
||||
// Avvio: inizializza l'URL base e carica i dati
|
||||
initializeBaseUrl();
|
||||
scanDatabase();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,85 +1,377 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background: #f4f4f9;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
padding: 20px;
|
||||
max-width: 900px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
font-size: 2.5em;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.server-info {
|
||||
background: #333;
|
||||
background: linear-gradient(135deg, #3d4555 0%, #2a3142 100%);
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.resource-card {
|
||||
.server-info p {
|
||||
margin: 8px 0;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.server-info strong {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Resource Section */
|
||||
.resource-section {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.resource-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.resource-name {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.resource-count {
|
||||
background: #eee;
|
||||
padding: 5px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.url-box {
|
||||
.resource-section:hover {
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.resource-section-header {
|
||||
background: linear-gradient(135deg, #7577e0 0%, #8f65ac 100%);
|
||||
color: white;
|
||||
padding: 18px 24px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
user-select: none;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.resource-section-header:hover {
|
||||
background: linear-gradient(135deg, #6668d5 0%, #7f559c 100%);
|
||||
}
|
||||
|
||||
.resource-section-header.expanded {
|
||||
border-bottom: 3px solid #fca130;
|
||||
}
|
||||
|
||||
.resource-title {
|
||||
font-size: 1.4em;
|
||||
font-weight: 600;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.resource-url {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.95em;
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
flex: 1;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
font-size: 1.2em;
|
||||
transition: transform 0.15s ease;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.resource-section-header.expanded .toggle-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.resource-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.15s ease;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.resource-content.expanded {
|
||||
max-height: 10000px;
|
||||
overflow-y: auto;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* Endpoint Card */
|
||||
.endpoint-card {
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: #fafafa;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.endpoint-card:hover {
|
||||
border-color: #d0d0d0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.endpoint-header {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
grid-template-rows: auto auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
cursor: pointer;
|
||||
background: white;
|
||||
border-bottom: 1px solid #eee;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.endpoint-header:hover {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.endpoint-header.expanded {
|
||||
background: #f3f3f9;
|
||||
border-bottom-color: #7577e0;
|
||||
}
|
||||
|
||||
.method-badge {
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.85em;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.endpoint-path {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.95em;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.endpoint-desc {
|
||||
font-size: 0.85em;
|
||||
color: #666;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.endpoint-toggle {
|
||||
color: #999;
|
||||
font-size: 1.2em;
|
||||
transition: transform 0.15s ease;
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.endpoint-header.expanded .endpoint-toggle {
|
||||
transform: rotate(45deg);
|
||||
color: #7577e0;
|
||||
}
|
||||
|
||||
/* Endpoint Details */
|
||||
.endpoint-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.15s ease;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.endpoint-details[data-expanded="true"] {
|
||||
max-height: 5000px;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 14px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-section:last-child {
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.detail-section strong {
|
||||
display: block;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #ccc;
|
||||
color: #333;
|
||||
font-size: 1.05em;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.detail-section pre {
|
||||
background: #2d2d2d;
|
||||
color: #76ff03;
|
||||
padding: 10px;
|
||||
font-family: monospace;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.preview {
|
||||
background: #f9f9f9;
|
||||
padding: 10px;
|
||||
border-left: 4px solid #007bff;
|
||||
margin-top: 15px;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.code-label {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.85em;
|
||||
overflow-x: auto;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Parametri List */
|
||||
.params-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.param-category {
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
color: #555;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 5px;
|
||||
font-family: monospace;
|
||||
margin-top: 14px;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px 10px;
|
||||
background: #f0f0f5;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.2px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.param-category:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.param-item {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.param-name {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 700;
|
||||
font-size: 1em;
|
||||
color: #333;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.param-desc {
|
||||
color: #7577e0;
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.resource-section-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.resource-title {
|
||||
min-width: auto;
|
||||
flex: 1 0 100%;
|
||||
}
|
||||
|
||||
.resource-url {
|
||||
flex: 1 0 calc(100% - 40px);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.endpoint-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.method-badge {
|
||||
flex: 0 0 auto;
|
||||
min-width: 50px;
|
||||
font-size: 0.8em;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.endpoint-path {
|
||||
flex: 1 0 100%;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.endpoint-desc {
|
||||
flex: 1 0 100%;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.endpoint-toggle {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.detail-section pre {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,15 @@ const middlewares = jsonServer.defaults({ static: 'public' });
|
||||
|
||||
// Configurazione del server
|
||||
server.use(middlewares);
|
||||
server.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
next();
|
||||
});
|
||||
server.get('/api', (req, res) => {
|
||||
const keys = Object.keys(router.db.getState());
|
||||
const schema = {};
|
||||
|
||||
Reference in New Issue
Block a user