Compare commits
14 Commits
a0381b12cc
...
v1.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 5db2e5fb0b | |||
| 5b3edbdfe5 | |||
| 8edbeba2ec | |||
| ca914b915b | |||
| 53fbcbe22c | |||
| e330119753 | |||
| 194cd3c175 | |||
| 72b8d60985 | |||
| 917fb26e05 | |||
| a28867b398 | |||
| ac71ea2c49 | |||
| efa1750cac | |||
| aa6e28d818 | |||
| 2cf797545c |
@@ -539,6 +539,7 @@ function _renderJobConfigFields(key, cfg) {
|
||||
</div>`;
|
||||
if (key === 'patchmon_sync' || key === 'semaphore_sync') {
|
||||
const label = key === 'semaphore_sync' ? 'API Token (Bearer)' : 'API Token (Basic)';
|
||||
const tokenPlaceholder = key === 'patchmon_sync' ? 'token_key:token_secret' : '';
|
||||
return `
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="job-cfg-api-url">API URL</label>
|
||||
@@ -548,6 +549,7 @@ function _renderJobConfigFields(key, cfg) {
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="job-cfg-api-token">${label}</label>
|
||||
<input class="form-input" id="job-cfg-api-token" type="password"
|
||||
placeholder="${tokenPlaceholder}"
|
||||
value="${esc(cfg.api_token ?? '')}">
|
||||
</div>`;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
const VERSION = "1.5.0";
|
||||
const VERSION = "1.7.2";
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "catalyst",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node server/server.js",
|
||||
|
||||
+23
-2
@@ -6,6 +6,8 @@ import { fileURLToPath } from 'url';
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DEFAULT_PATH = join(__dirname, '../data/catalyst.db');
|
||||
|
||||
const JOB_RUN_LIMIT = 10;
|
||||
|
||||
let db;
|
||||
|
||||
function init(path) {
|
||||
@@ -17,7 +19,7 @@ function init(path) {
|
||||
db.exec('PRAGMA foreign_keys = ON');
|
||||
db.exec('PRAGMA synchronous = NORMAL');
|
||||
createSchema();
|
||||
if (path !== ':memory:') { seed(); seedJobs(); }
|
||||
if (path !== ':memory:') { seed(); seedJobs(); pruneAllJobRuns(); }
|
||||
}
|
||||
|
||||
function createSchema() {
|
||||
@@ -267,6 +269,7 @@ export function importJobs(jobRows, jobRunRows = []) {
|
||||
`);
|
||||
for (const r of jobRunRows) insertRun.run(r);
|
||||
}
|
||||
pruneAllJobRuns();
|
||||
db.exec('COMMIT');
|
||||
}
|
||||
|
||||
@@ -326,10 +329,28 @@ export function completeJobRun(runId, status, result) {
|
||||
db.prepare(`
|
||||
UPDATE job_runs SET ended_at=strftime('%Y-%m-%dT%H:%M:%f', 'now'), status=@status, result=@result WHERE id=@id
|
||||
`).run({ id: runId, status, result });
|
||||
const row = db.prepare('SELECT job_id FROM job_runs WHERE id = ?').get(runId);
|
||||
if (row) pruneJobRuns(row.job_id);
|
||||
}
|
||||
|
||||
export function getJobRuns(jobId) {
|
||||
return db.prepare('SELECT * FROM job_runs WHERE job_id = ? ORDER BY id DESC').all(jobId);
|
||||
return db.prepare(`SELECT * FROM job_runs WHERE job_id = ? ORDER BY id DESC LIMIT ${JOB_RUN_LIMIT}`).all(jobId);
|
||||
}
|
||||
|
||||
function pruneJobRuns(jobId) {
|
||||
db.prepare(`
|
||||
DELETE FROM job_runs
|
||||
WHERE job_id = ?
|
||||
AND id NOT IN (
|
||||
SELECT id FROM job_runs WHERE job_id = ? ORDER BY id DESC LIMIT ?
|
||||
)
|
||||
`).run(jobId, jobId, JOB_RUN_LIMIT);
|
||||
}
|
||||
|
||||
function pruneAllJobRuns() {
|
||||
for (const j of db.prepare('SELECT id FROM jobs').all()) {
|
||||
pruneJobRuns(j.id);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Test helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
+7
-1
@@ -41,8 +41,14 @@ async function patchmonSyncHandler(cfg) {
|
||||
const { api_url, api_token } = cfg;
|
||||
if (!api_url || !api_token) throw new Error('Patchmon not configured — set API URL and token');
|
||||
|
||||
// Accept raw "key:secret" (recommended) or a pre-encoded base64 string.
|
||||
// ":" cannot appear in a valid base64 string, so it's a reliable discriminator.
|
||||
const credential = api_token.includes(':')
|
||||
? Buffer.from(api_token).toString('base64')
|
||||
: api_token;
|
||||
|
||||
const res = await fetch(api_url, {
|
||||
headers: { Authorization: `Basic ${api_token}` },
|
||||
headers: { Authorization: `Basic ${credential}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Patchmon API ${res.status}`);
|
||||
|
||||
|
||||
@@ -450,4 +450,17 @@ describe('job_runs', () => {
|
||||
expect(runs[0].id).toBe(r2);
|
||||
expect(runs[1].id).toBe(r1);
|
||||
});
|
||||
|
||||
it('caps history at the last 10 runs per job', () => {
|
||||
createJob(baseJob);
|
||||
const id = getJobs()[0].id;
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const runId = createJobRun(id);
|
||||
completeJobRun(runId, 'success', `run ${i}`);
|
||||
}
|
||||
const runs = getJobRuns(id);
|
||||
expect(runs).toHaveLength(10);
|
||||
expect(runs[0].result).toBe('run 14');
|
||||
expect(runs[9].result).toBe('run 5');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user