Initial
This commit is contained in:
76
server/models/jobOutputModel.js
Normal file
76
server/models/jobOutputModel.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { config } from '../config.js';
|
||||
import { db, parseJson } from '../db.js';
|
||||
import { camelJobOutput } from './mappers.js';
|
||||
import { isPathInside, path } from '../utils/pathUtils.js';
|
||||
|
||||
// Keep output artifact visibility identical to job visibility so logs and
|
||||
// generated files do not leak across users.
|
||||
function visibleJobClause() {
|
||||
return `(
|
||||
j.triggered_by = ?
|
||||
OR r.visibility = 'shared'
|
||||
OR r.owner_user_id = ?
|
||||
OR r.group_id IN (SELECT group_id FROM user_groups WHERE user_id = ?)
|
||||
)`;
|
||||
}
|
||||
|
||||
function outputSelect() {
|
||||
return `
|
||||
SELECT jo.*, j.status AS job_status, j.runplan_id, j.script_id, j.triggered_by,
|
||||
r.name AS runplan_name, s.name AS script_name
|
||||
FROM job_outputs jo
|
||||
JOIN jobs j ON j.id = jo.job_id
|
||||
LEFT JOIN runplans r ON r.id = j.runplan_id
|
||||
LEFT JOIN scripts s ON s.id = j.script_id
|
||||
`;
|
||||
}
|
||||
|
||||
export function listJobOutputs(userId, isAdmin = false) {
|
||||
const rows = isAdmin
|
||||
? db.prepare(`
|
||||
${outputSelect()}
|
||||
ORDER BY jo.updated_at DESC
|
||||
LIMIT 250
|
||||
`).all()
|
||||
: db.prepare(`
|
||||
${outputSelect()}
|
||||
WHERE ${visibleJobClause()}
|
||||
ORDER BY jo.updated_at DESC
|
||||
LIMIT 250
|
||||
`).all(userId, userId, userId);
|
||||
return rows.map(camelJobOutput);
|
||||
}
|
||||
|
||||
export function listJobOutputsForJob(jobId, userId, isAdmin = false) {
|
||||
const rows = isAdmin
|
||||
? db.prepare(`
|
||||
${outputSelect()}
|
||||
WHERE jo.job_id = ?
|
||||
ORDER BY jo.kind
|
||||
`).all(jobId)
|
||||
: db.prepare(`
|
||||
${outputSelect()}
|
||||
WHERE jo.job_id = ? AND ${visibleJobClause()}
|
||||
ORDER BY jo.kind
|
||||
`).all(jobId, userId, userId, userId);
|
||||
return rows.map(camelJobOutput);
|
||||
}
|
||||
|
||||
export function getJobOutput(outputId, userId, isAdmin = false) {
|
||||
const row = isAdmin
|
||||
? db.prepare(`${outputSelect()} WHERE jo.id = ?`).get(outputId)
|
||||
: db.prepare(`${outputSelect()} WHERE jo.id = ? AND ${visibleJobClause()}`).get(outputId, userId, userId, userId);
|
||||
return row ? camelJobOutput(row) : null;
|
||||
}
|
||||
|
||||
export function getJobOutputFile(outputId, userId, isAdmin = false) {
|
||||
const row = isAdmin
|
||||
? db.prepare(`${outputSelect()} WHERE jo.id = ?`).get(outputId)
|
||||
: db.prepare(`${outputSelect()} WHERE jo.id = ? AND ${visibleJobClause()}`).get(outputId, userId, userId, userId);
|
||||
if (!row) return null;
|
||||
const filePath = path.resolve(row.storage_path);
|
||||
if (!isPathInside(config.fileLockerDir, filePath)) {
|
||||
throw new Error('Job output path is outside the configured FileLocker directory');
|
||||
}
|
||||
return { output: camelJobOutput(row), filePath, metadata: parseJson(row.metadata_json, {}) };
|
||||
}
|
||||
Reference in New Issue
Block a user