Release v2.5.6

This commit is contained in:
lzstealth committed 2026-08-05 19:38:16 +01:00
1 parent a8ef35b287
commit 9f831f04bc
161 files changed
+8487 -1295

No files matched your search

@@ -0,0 +1,31 @@
// API source duplication helpers.
function buildDuplicateApiSourceName(sourceName) {
const baseName = 'Copy of ' + String(sourceName || '').trim();
return baseName;
}
function buildDuplicateApiSource(apiSource, duplicateName) {
return Object.assign({}, apiSource, {
id: null,
name: duplicateName,
apiUrl: apiSource.api_url || '',
lastPulledAt: null,
lastPullError: '',
lastResponseStatus: null,
lastResponseContentType: '',
lastResponseJson: '',
authMethod: apiSource.auth_method || 'none',
authUsername: apiSource.auth_username || '',
authPassword: apiSource.auth_password || '',
authBearerToken: apiSource.auth_bearer_token || '',
authHeaderName: apiSource.auth_header_name || 'X-API-Key',
authHeaderValue: apiSource.auth_header_value || '',
itemsPath: apiSource.items_path || ''
});
}
module.exports = {
buildDuplicateApiSourceName,
buildDuplicateApiSource
};
@@ -35,8 +35,9 @@ function buildApiSourceFormViewModel(apiSource, message, currentUser, options, i
inUse: Boolean(viewApiSource.inUse),
lastResponseJson: viewOptions.lastResponseJson || viewApiSource.lastResponseJson || '',
lastPullError: viewOptions.lastPullError || viewApiSource.lastPullError || '',
messageVariant: viewOptions.messageVariant || '',
isEdit: Boolean(isEdit),
showSaveSecondaryActions: Boolean(isEdit),
showSaveSecondaryActions: true,
deleteDisabled: !isEdit || Boolean(viewApiSource.inUse),
formAction: isEdit && viewApiSource.id ? '/data-sources/api-sources/' + viewApiSource.id : '/data-sources/api-sources',
formAttrs: isEdit && viewApiSource.id ? 'data-async-save data-async-save-refresh-target="#api-source-response-panel" data-async-save-refresh-state-url="/data-sources/api-sources/' + viewApiSource.id + '/state" data-async-save-close-url="/data-sources/api-sources" data-async-save-new-url="/data-sources/api-sources/new"' : 'data-async-save data-async-save-close-url="/data-sources/api-sources" data-async-save-new-redirect="response-url" data-async-save-new-url="/data-sources/api-sources/new"',
@@ -4,6 +4,7 @@ const { buildPagination } = require('../../../lib/pagination');
const renderApiSourcesPage = require('./list');
const renderApiSourceAddPage = require('./add');
const renderApiSourceEditPage = require('./edit');
const { buildDuplicateApiSourceName, buildDuplicateApiSource } = require('./duplicate');
function toIsoTimestamp(value) {
if (!value) {
@@ -185,6 +186,29 @@ module.exports = function registerApiSourceRoutes(app, deps) {
}
});
app.get('/data-sources/api-sources/:id/duplicate', requirePermission('api-sources.read'), requirePermission('api-sources.create'), async function (req, res, next) {
try {
const apiSource = await common.fetchApiSourceById(pool, Number(req.params.id));
if (!apiSource) {
return res.status(404).send('API source not found');
}
let duplicateName = buildDuplicateApiSourceName(apiSource.name);
let duplicateIndex = 2;
while (await common.fetchDuplicateName(pool, 'i_api_sources', duplicateName)) {
duplicateName = buildDuplicateApiSourceName(apiSource.name) + ' (' + duplicateIndex + ')';
duplicateIndex += 1;
}
res.send(renderApiSourceAddPage(buildDuplicateApiSource(apiSource, duplicateName), req.query.message ? String(req.query.message) : 'Review the copied values and save when ready.', req.currentUser, {
messageVariant: 'info',
showSaveSecondaryActions: true
}));
} catch (error) {
next(error);
}
});
app.get('/data-sources/api-sources/:id/state', requirePermission('api-sources.update'), function (req, res, next) {
sendRefreshTaskState(req, res, dataSourceTasks, Number(req.params.id)).catch(next);
});