import { db, id, now, visibleClause } from '../db.js'; import { normalizeRunPlanPayload } from '../forms/schemas.js'; import { camelRunPlan } from './mappers.js'; import { filterVisibleHostIds } from './hostModel.js'; export function listRunPlans(userId) { return db.prepare(` SELECT r.*, s.name AS script_name, g.name AS group_name, (SELECT COUNT(*) FROM runplan_hosts rh WHERE rh.runplan_id = r.id) AS host_count FROM runplans r JOIN scripts s ON s.id = r.script_id LEFT JOIN groups g ON g.id = r.group_id WHERE ${visibleClause('r')} ORDER BY r.updated_at DESC `).all(userId, userId).map(camelRunPlan); } export function createRunPlan(body, userId) { const payload = normalizeRunPlanPayload(body); const runplanId = id('run'); db.prepare(` INSERT INTO runplans (id, name, description, script_id, visibility, owner_user_id, group_id, parallel, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run(runplanId, payload.name, payload.description, payload.scriptId, payload.visibility, userId, payload.groupId, payload.parallel ? 1 : 0, now(), now()); replaceRunPlanHosts(runplanId, filterVisibleHostIds(payload.hostIds, userId)); return loadRunPlan(runplanId); } export function loadRunPlan(runplanId) { const runplan = db.prepare(` SELECT r.*, s.name AS script_name, g.name AS group_name, (SELECT COUNT(*) FROM runplan_hosts rh WHERE rh.runplan_id = r.id) AS host_count FROM runplans r JOIN scripts s ON s.id = r.script_id LEFT JOIN groups g ON g.id = r.group_id WHERE r.id = ? `).get(runplanId); if (!runplan) return null; const hostIds = db.prepare('SELECT host_id FROM runplan_hosts WHERE runplan_id = ? ORDER BY host_id').all(runplanId).map((row) => row.host_id); return { ...camelRunPlan(runplan), hostIds }; } export function loadVisibleRunPlan(runplanId, userId) { const visible = db.prepare(`SELECT id FROM runplans WHERE id = ? AND ${visibleClause()}`).get(runplanId, userId, userId); return visible ? loadRunPlan(runplanId) : null; } export function updateRunPlan(runplanId, body, userId) { const existing = db.prepare(`SELECT * FROM runplans WHERE id = ? AND ${visibleClause()}`).get(runplanId, userId, userId); if (!existing) return null; const payload = normalizeRunPlanPayload({ ...existing, ...body, hostIds: body.hostIds || undefined }); db.prepare(` UPDATE runplans SET name = ?, description = ?, script_id = ?, visibility = ?, group_id = ?, parallel = ?, updated_at = ? WHERE id = ? `).run(payload.name, payload.description, payload.scriptId, payload.visibility, payload.groupId, payload.parallel ? 1 : 0, now(), runplanId); if (body.hostIds) replaceRunPlanHosts(runplanId, filterVisibleHostIds(payload.hostIds, userId)); return loadRunPlan(runplanId); } export function deleteRunPlan(runplanId, userId) { db.prepare(`DELETE FROM runplans WHERE id = ? AND ${visibleClause()}`).run(runplanId, userId, userId); } function replaceRunPlanHosts(runplanId, hostIds) { db.prepare('DELETE FROM runplan_hosts WHERE runplan_id = ?').run(runplanId); const stmt = db.prepare('INSERT OR IGNORE INTO runplan_hosts (runplan_id, host_id) VALUES (?, ?)'); for (const hostId of hostIds) stmt.run(runplanId, hostId); }