diff --git a/server/actions.mjs b/server/actions.mjs index 5c18f58..576e34a 100644 --- a/server/actions.mjs +++ b/server/actions.mjs @@ -2,7 +2,7 @@ // policy: "auto-accept" | "auto-deny" | "queue" -import { resolve_path, exec } from './helpers.mjs'; +import { resolve_path } from './helpers.mjs'; import { check_can_approve } from './auth.mjs'; export const actions = { @@ -24,7 +24,7 @@ export const actions = { description: "Open a file in the editor", params: [{ name: "filename", required: true, type: "path" }], policy: "auto-accept", - handler: ({ filename }) => { + handler: ({ filename }, { exec }) => { const resolved = resolve_path(filename); exec('subl3', [resolved]); return { opened: resolved }; @@ -48,7 +48,7 @@ export const actions = { description: "Open a URL in the web browser", params: [{ name: "url", required: true, type: "string" }], policy: "queue", - handler: ({ url }) => { + handler: ({ url }, { exec }) => { const parsed = new URL(url); if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { throw new Error(`Disallowed protocol: ${parsed.protocol}`); @@ -62,7 +62,7 @@ export const actions = { description: "Open a terminal in a given directory", params: [{ name: "path", required: false, type: "path" }], policy: 'queue', - handler: ({ path }) => { + handler: ({ path }, { exec }) => { const resolved = resolve_path(path ?? 'workspace'); exec('konsole', ['--workdir', resolved, '-e', 'bash']); return { opened: resolved }; diff --git a/server/index.mjs b/server/index.mjs index 35c5082..822024e 100644 --- a/server/index.mjs +++ b/server/index.mjs @@ -4,6 +4,7 @@ import { enqueue, get_entry, list_pending, resolve } from './queue.mjs'; import { load_secrets } from './secrets.mjs'; import { create_auth_middleware, check_can_approve } from './auth.mjs'; import { create_mailer } from './mailer.mjs'; +import { exec as real_exec } from './helpers.mjs'; import { load_mail_perms } from './mail_perms.mjs'; function get_arg(argv, flag) { @@ -44,7 +45,7 @@ if (!mail_perms_path) { console.warn('Warning: --mail-perms not set; mail permissions will not persist across restarts'); } -const mailer_send = create_mailer(smtp); +const real_mailer_send = create_mailer(smtp); const app = express(); app.use(express.json({ @@ -62,13 +63,17 @@ app.use((req, _res, next) => { next(); }); -async function run_action(def, action, params, ctx) { - if (DRY_RUN) { - console.log(`[${ts()}] [DRY-RUN] action: ${action}`); - console.log(`[${ts()}] [DRY-RUN] caller: ${ctx.caller}`); - console.log(`[${ts()}] [DRY-RUN] params: ${JSON.stringify(params, null, 2)}`); - return { dry_run: true, action, params }; - } +function make_ctx(caller) { + const exec = DRY_RUN + ? (bin, args) => console.log(`[${ts()}] [DRY-RUN] exec: ${bin} ${JSON.stringify(args)}`) + : real_exec; + const mailer_send = DRY_RUN + ? async (to, subject) => console.log(`[${ts()}] [DRY-RUN] send-mail: to=${to} subject=${JSON.stringify(subject)}`) + : real_mailer_send; + return { caller, users, mail_perm_store, exec, mailer_send }; +} + +async function run_action(def, params, ctx) { return def.handler(params, ctx); } @@ -104,11 +109,11 @@ app.post('/action', async (req, res) => { return res.status(403).json({ status: 'denied', reason: 'Policy: auto-deny' }); } - const ctx = { caller: req.conduit_user, users, mail_perm_store, mailer_send }; + const ctx = make_ctx(req.conduit_user); if (def.policy === 'auto-accept') { try { - const result = await run_action(def, action, params, ctx); + const result = await run_action(def, params, ctx); return res.json({ status: 'accepted', result }); } catch (err) { return res.status(500).json({ status: 'error', error: err.message }); @@ -142,10 +147,10 @@ app.post('/queue/:id/approve', async (req, res) => { entry.resolved_by = req.conduit_user; resolve(req.params.id, 'approved'); - const ctx = { caller: entry.submitted_by, users, mail_perm_store, mailer_send }; + const ctx = make_ctx(entry.submitted_by); const def = actions[entry.action]; try { - const result = await run_action(def, entry.action, entry.params, ctx); + const result = await run_action(def, entry.params, ctx); res.json({ status: 'approved', result }); } catch (err) { res.status(500).json({ status: 'error', error: err.message });