atualiza versão para 0.1.5; adiciona botão de log na aba Backup Runs, novo endpoint para detalhes de backup e melhora a exibição do changelog
This commit is contained in:
parent
83e7490654
commit
9f7eacecb9
16
README.md
16
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./public/docker_backup_icon_for_appstore.png" width="200" alt="DockerBackup" />
|
<img src="./public/docker_backup_icon_for_appstore.png" width="200" alt="DockerBackup" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/VERSION-0.1.4-blue?style=flat-square" />
|
<img src="https://img.shields.io/badge/VERSION-0.1.5-blue?style=flat-square" />
|
||||||
<img src="https://img.shields.io/badge/NODE.JS-%3E%3D20-339933?style=flat-square&logo=node.js&logoColor=white" />
|
<img src="https://img.shields.io/badge/NODE.JS-%3E%3D20-339933?style=flat-square&logo=node.js&logoColor=white" />
|
||||||
<img src="https://img.shields.io/badge/DOCKER-ready-2496ED?style=flat-square&logo=docker&logoColor=white" />
|
<img src="https://img.shields.io/badge/DOCKER-ready-2496ED?style=flat-square&logo=docker&logoColor=white" />
|
||||||
<img src="https://img.shields.io/badge/READY-yes-brightgreen?style=flat-square" />
|
<img src="https://img.shields.io/badge/READY-yes-brightgreen?style=flat-square" />
|
||||||
|
|
@ -23,6 +23,18 @@ Versão atual: **0.1.4**
|
||||||
---
|
---
|
||||||
|
|
||||||
## <20> Changelog
|
## <20> Changelog
|
||||||
|
### [0.1.5] — 2026-05-10
|
||||||
|
|
||||||
|
#### Adicionado
|
||||||
|
- **Botão `Log` na aba Backup Runs:** cada run exibe agora um botão que abre um modal com o log completo do backup por container, incluindo saída do tar, avisos e erros ocorridos durante a execução.
|
||||||
|
- **Endpoint `GET /api/backups/:backupId`:** novo endpoint que retorna os dados completos de um backup (incluindo logs) a partir do seu ID.
|
||||||
|
- **Logs persistidos por container no backup:** o array de logs gerado durante o backup (`pushLog`) é agora salvo em `containerBackup.logs` e persistido no `store.json`, disponível para consulta posterior.
|
||||||
|
|
||||||
|
#### Melhorado
|
||||||
|
- **Dropdowns estilizados como campos de texto:** todos os elementos `<select>` dentro de `.form-field` e o `.settings-select` passaram a usar o mesmo estilo visual dos inputs de texto (borda, padding, tipografia), com ícone de seta customizado via SVG.
|
||||||
|
- **Tela Sobre — posição do autor:** o texto `Desenvolvido por Alexander Sabino em 2026` foi movido para logo abaixo da descrição da aplicação.
|
||||||
|
- **Tela Sobre — changelog limitado:** a seção de changelog exibe agora apenas as últimas 4 entradas de versão.
|
||||||
|
|
||||||
### [0.1.4] — 2026-05-09
|
### [0.1.4] — 2026-05-09
|
||||||
|
|
||||||
#### Corrigido
|
#### Corrigido
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dockerbackup-app",
|
"name": "dockerbackup-app",
|
||||||
"version": "0.1.4",
|
"version": "0.1.5",
|
||||||
"description": "Aplicacao web para backup e restauracao de volumes Docker",
|
"description": "Aplicacao web para backup e restauracao de volumes Docker",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,7 @@ async function loadAllRuns() {
|
||||||
<td>${fileCount || '—'}</td>
|
<td>${fileCount || '—'}</td>
|
||||||
<td>${sizeStr}</td>
|
<td>${sizeStr}</td>
|
||||||
<td>${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))}</td>
|
<td>${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))}</td>
|
||||||
|
<td><button class="btn btn--secondary btn--sm run-log-btn" data-backup-id="${escapeHtml(b.id)}">Log</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
@ -1818,7 +1819,7 @@ function markdownInline(raw) {
|
||||||
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseChangelogSection(markdown) {
|
function parseChangelogSection(markdown, maxEntries = 4) {
|
||||||
// Extract from ## Changelog (or ## 🗂 Changelog etc.) to next ##
|
// Extract from ## Changelog (or ## 🗂 Changelog etc.) to next ##
|
||||||
const start = markdown.search(/^##\s+[^\n]*[Cc]hangelog/m);
|
const start = markdown.search(/^##\s+[^\n]*[Cc]hangelog/m);
|
||||||
if (start === -1) return null;
|
if (start === -1) return null;
|
||||||
|
|
@ -1829,6 +1830,7 @@ function parseChangelogSection(markdown) {
|
||||||
const lines = section.split('\n');
|
const lines = section.split('\n');
|
||||||
let html = '';
|
let html = '';
|
||||||
let inList = false;
|
let inList = false;
|
||||||
|
let entryCount = 0;
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const h3 = line.match(/^###\s+(.+)/);
|
const h3 = line.match(/^###\s+(.+)/);
|
||||||
|
|
@ -1836,12 +1838,16 @@ function parseChangelogSection(markdown) {
|
||||||
const li = line.match(/^-\s+(.+)/);
|
const li = line.match(/^-\s+(.+)/);
|
||||||
|
|
||||||
if (h3) {
|
if (h3) {
|
||||||
|
if (entryCount >= maxEntries) break;
|
||||||
if (inList) { html += '</ul>'; inList = false; }
|
if (inList) { html += '</ul>'; inList = false; }
|
||||||
html += `<h4>${markdownInline(h3[1].replace(/\[([^\]]+)\]/, '$1'))}</h4>`;
|
html += `<h4>${markdownInline(h3[1].replace(/\[([^\]]+)\]/, '$1'))}</h4>`;
|
||||||
|
entryCount += 1;
|
||||||
} else if (h4) {
|
} else if (h4) {
|
||||||
|
if (entryCount > maxEntries) break;
|
||||||
if (inList) { html += '</ul>'; inList = false; }
|
if (inList) { html += '</ul>'; inList = false; }
|
||||||
html += `<h5 class="changelog-section">${markdownInline(h4[1])}</h5>`;
|
html += `<h5 class="changelog-section">${markdownInline(h4[1])}</h5>`;
|
||||||
} else if (li) {
|
} else if (li) {
|
||||||
|
if (entryCount > maxEntries) break;
|
||||||
if (!inList) { html += '<ul>'; inList = true; }
|
if (!inList) { html += '<ul>'; inList = true; }
|
||||||
html += `<li>${markdownInline(li[1])}</li>`;
|
html += `<li>${markdownInline(li[1])}</li>`;
|
||||||
} else if (line.startsWith('---') || line.startsWith('## ')) {
|
} else if (line.startsWith('---') || line.startsWith('## ')) {
|
||||||
|
|
@ -1939,6 +1945,60 @@ elements.profilesList.addEventListener('click', handleProfileAction);
|
||||||
document.querySelector('#backupsViewList').addEventListener('click', handleProfileAction);
|
document.querySelector('#backupsViewList').addEventListener('click', handleProfileAction);
|
||||||
document.querySelector('#refreshRuns')?.addEventListener('click', () => loadAllRuns());
|
document.querySelector('#refreshRuns')?.addEventListener('click', () => loadAllRuns());
|
||||||
|
|
||||||
|
// Run log modal
|
||||||
|
document.querySelector('#allRunsBody')?.addEventListener('click', async (e) => {
|
||||||
|
const btn = e.target.closest('.run-log-btn');
|
||||||
|
if (!btn) return;
|
||||||
|
const backupId = btn.dataset.backupId;
|
||||||
|
if (!backupId) return;
|
||||||
|
openRunLogModal(backupId);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#runLogModalClose')?.addEventListener('click', () => {
|
||||||
|
document.querySelector('#runLogModal')?.classList.add('hidden');
|
||||||
|
});
|
||||||
|
document.querySelector('#runLogModal')?.addEventListener('click', (e) => {
|
||||||
|
if (e.target.dataset.action === 'close-run-log-modal') {
|
||||||
|
document.querySelector('#runLogModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function openRunLogModal(backupId) {
|
||||||
|
const modal = document.querySelector('#runLogModal');
|
||||||
|
const content = document.querySelector('#runLogContent');
|
||||||
|
if (!modal || !content) return;
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
content.innerHTML = '<p class="changelog-loading">Carregando log...</p>';
|
||||||
|
try {
|
||||||
|
const backup = await api(`/api/backups/${encodeURIComponent(backupId)}`);
|
||||||
|
const containers = backup.containers || [];
|
||||||
|
if (!containers.length) {
|
||||||
|
content.innerHTML = '<p class="changelog-loading">Nenhum log disponível para este backup.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let html = '';
|
||||||
|
for (const c of containers) {
|
||||||
|
const name = escapeHtml(c.containerName || c.containerId || '?');
|
||||||
|
const status = escapeHtml(c.status || '—');
|
||||||
|
html += `<div class="run-log-section">`;
|
||||||
|
html += `<h4 class="run-log-container-name">${name} <span class="status-badge status-badge--${escapeHtml(c.status || '')}">${status}</span></h4>`;
|
||||||
|
if (c.error) {
|
||||||
|
html += `<div class="run-log-error"><strong>Erro:</strong> ${escapeHtml(c.error)}</div>`;
|
||||||
|
}
|
||||||
|
const logs = c.logs || [];
|
||||||
|
if (logs.length) {
|
||||||
|
html += `<pre class="run-log-pre">${logs.map((l) => escapeHtml(l)).join('\n')}</pre>`;
|
||||||
|
} else {
|
||||||
|
html += `<p class="run-log-empty">Sem log detalhado (backup pode ter sido criado antes desta versão).</p>`;
|
||||||
|
}
|
||||||
|
html += `</div>`;
|
||||||
|
}
|
||||||
|
content.innerHTML = html;
|
||||||
|
} catch (err) {
|
||||||
|
content.innerHTML = `<p class="changelog-loading">Erro ao carregar log: ${escapeHtml(err.message)}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply translations early (before auth check so login page is translated)
|
// Apply translations early (before auth check so login page is translated)
|
||||||
applyTranslations();
|
applyTranslations();
|
||||||
checkAuthAndInit();
|
checkAuthAndInit();
|
||||||
|
|
@ -193,10 +193,11 @@
|
||||||
<th>Files</th>
|
<th>Files</th>
|
||||||
<th>Size</th>
|
<th>Size</th>
|
||||||
<th>Started</th>
|
<th>Started</th>
|
||||||
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="allRunsBody">
|
<tbody id="allRunsBody">
|
||||||
<tr><td colspan="8" class="empty-row">Nenhum run encontrado.</td></tr>
|
<tr><td colspan="9" class="empty-row">Nenhum run encontrado.</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -296,6 +297,7 @@
|
||||||
<h2>DockerBackup</h2>
|
<h2>DockerBackup</h2>
|
||||||
</div>
|
</div>
|
||||||
<p class="about-description" data-i18n="about.description">Aplicação web para backup e restauração de volumes Docker com suporte a snapshots incrementais e restore seletivo.</p>
|
<p class="about-description" data-i18n="about.description">Aplicação web para backup e restauração de volumes Docker com suporte a snapshots incrementais e restore seletivo.</p>
|
||||||
|
<p class="about-author">Desenvolvido por Alexander Sabino em 2026</p>
|
||||||
|
|
||||||
<div class="about-version-grid">
|
<div class="about-version-grid">
|
||||||
<div class="about-version-item">
|
<div class="about-version-item">
|
||||||
|
|
@ -319,7 +321,6 @@
|
||||||
<p class="changelog-loading">Carregando changelog...</p>
|
<p class="changelog-loading">Carregando changelog...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="about-author">Desenvolvido por Alexander Sabino em 2026</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -579,6 +580,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal: Log do Backup Run -->
|
||||||
|
<div id="runLogModal" class="modal hidden" aria-hidden="true">
|
||||||
|
<div class="modal-backdrop" data-action="close-run-log-modal"></div>
|
||||||
|
<div class="modal-card modal-card--wide" role="dialog" aria-modal="true" aria-labelledby="runLogTitle">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 id="runLogTitle">Log do Backup</h3>
|
||||||
|
<button id="runLogModalClose" class="btn btn--ghost btn--sm" type="button">Fechar</button>
|
||||||
|
</div>
|
||||||
|
<div id="runLogContent" class="run-log-content">
|
||||||
|
<p class="changelog-loading">Carregando log...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="/translations.js"></script>
|
<script src="/translations.js"></script>
|
||||||
<script src="/app.js"></script>
|
<script src="/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -532,6 +532,9 @@ button, input, select {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field input[type='text'],
|
.form-field input[type='text'],
|
||||||
|
.form-field input[type='password'],
|
||||||
|
.form-field input[type='datetime-local'],
|
||||||
|
.form-field select,
|
||||||
.form-fieldset input[type='text'] {
|
.form-fieldset input[type='text'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 9px 12px;
|
padding: 9px 12px;
|
||||||
|
|
@ -540,9 +543,25 @@ button, input, select {
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
transition: border-color 150ms;
|
transition: border-color 150ms;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23718096' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
padding-right: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field input:focus {
|
.form-field input[type='text'],
|
||||||
|
.form-field input[type='password'],
|
||||||
|
.form-field input[type='datetime-local'] {
|
||||||
|
background-image: none;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field input:focus,
|
||||||
|
.form-field select:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
@ -1064,15 +1083,23 @@ button, input, select {
|
||||||
}
|
}
|
||||||
.settings-select {
|
.settings-select {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
|
padding-right: 32px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
font-family: inherit;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23718096' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
transition: border-color 150ms;
|
||||||
}
|
}
|
||||||
.settings-select:focus { outline: 2px solid var(--accent); }
|
.settings-select:focus { outline: none; border-color: var(--accent); }
|
||||||
.toggle-label {
|
.toggle-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -1210,6 +1237,59 @@ button, input, select {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Run Log Modal --------------------------------------- */
|
||||||
|
.run-log-content {
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 4px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.run-log-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.run-log-container-name {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.run-log-error {
|
||||||
|
background: #fff5f5;
|
||||||
|
border: 1px solid #feb2b2;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .run-log-error {
|
||||||
|
background: rgba(229, 62, 62, 0.12);
|
||||||
|
border-color: rgba(229, 62, 62, 0.3);
|
||||||
|
}
|
||||||
|
.run-log-pre {
|
||||||
|
background: #1a1a2e;
|
||||||
|
color: #c3d3e8;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-family: 'IBM Plex Mono', monospace;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.run-log-empty {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Modal ----------------------------------------------- */
|
/* --- Modal ----------------------------------------------- */
|
||||||
.modal {
|
.modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
|
|
@ -516,6 +516,7 @@ class BackupService {
|
||||||
|
|
||||||
containerBackup.fileCount = Math.max(fileCurrent, fileTotal);
|
containerBackup.fileCount = Math.max(fileCurrent, fileTotal);
|
||||||
try { containerBackup.archiveSize = (await fs.stat(absoluteArchivePath)).size; } catch {}
|
try { containerBackup.archiveSize = (await fs.stat(absoluteArchivePath)).size; } catch {}
|
||||||
|
containerBackup.logs = [...logs];
|
||||||
|
|
||||||
onProgress({
|
onProgress({
|
||||||
containerName,
|
containerName,
|
||||||
|
|
@ -594,6 +595,7 @@ class BackupService {
|
||||||
|
|
||||||
containerBackup.fileCount = Math.max(fileCurrent, fileTotal);
|
containerBackup.fileCount = Math.max(fileCurrent, fileTotal);
|
||||||
try { containerBackup.archiveSize = (await fs.stat(absoluteArchivePath)).size; } catch {}
|
try { containerBackup.archiveSize = (await fs.stat(absoluteArchivePath)).size; } catch {}
|
||||||
|
containerBackup.logs = [...logs];
|
||||||
|
|
||||||
onProgress({
|
onProgress({
|
||||||
containerName,
|
containerName,
|
||||||
|
|
@ -670,6 +672,7 @@ class BackupService {
|
||||||
const hostArchivePath = path.join(profile.backupDir, ...archiveRelativePath.split('/'));
|
const hostArchivePath = path.join(profile.backupDir, ...archiveRelativePath.split('/'));
|
||||||
containerBackup.archiveSize = (await fs.stat(hostArchivePath)).size;
|
containerBackup.archiveSize = (await fs.stat(hostArchivePath)).size;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
containerBackup.logs = [...logs];
|
||||||
|
|
||||||
onProgress({
|
onProgress({
|
||||||
containerName,
|
containerName,
|
||||||
|
|
@ -698,6 +701,7 @@ class BackupService {
|
||||||
...containerBackup,
|
...containerBackup,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
error: error.message,
|
error: error.message,
|
||||||
|
logs: [...logs],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,19 @@ async function main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/api/backups/:backupId', authMiddleware, async (request, response) => {
|
||||||
|
try {
|
||||||
|
const backup = await store.getBackup(request.params.backupId);
|
||||||
|
if (!backup) {
|
||||||
|
response.status(404).json({ error: 'Backup nao encontrado.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.json(backup);
|
||||||
|
} catch (error) {
|
||||||
|
response.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/profiles/:profileId/run', authMiddleware, async (request, response) => {
|
app.post('/api/profiles/:profileId/run', authMiddleware, async (request, response) => {
|
||||||
try {
|
try {
|
||||||
const profileId = request.params.profileId;
|
const profileId = request.params.profileId;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue