Add job runner system with API and database models#125
Merged
runleveldev merged 8 commits intomainfrom Dec 2, 2025
Merged
Conversation
Introduces a job runner service that polls for pending jobs, executes commands, and records output/status. Adds Sequelize models and migrations for Jobs and JobStatuses, a jobs API router for job management, and integrates the router into the server. Also includes a systemd service file and updates package.json scripts.
runleveldev
requested changes
Nov 18, 2025
runleveldev
requested changes
Nov 18, 2025
runleveldev
requested changes
Nov 19, 2025
Introduces an asynchronous job runner for the create-a-container service, including Sequelize models, migrations, a background job-runner process, new API endpoints for job management, and a systemd unit file. This enables long-running tasks to be executed outside HTTP lifecycles, with progress reporting and admin-only job creation for security.
… the user who created each job.
runleveldev
requested changes
Nov 26, 2025
Collaborator
Author
runleveldev
requested changes
Dec 2, 2025
Collaborator
There was a problem hiding this comment.
This should not be in the final merge. These notes can be included in the PR comments but do not need to live forever here. Any relevant information should be migrated to the README
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.





Issue: #119
Jobs / job-runner Feature
This document summarizes the new Jobs feature, how it works, how to deploy it, and how to test it. It's intended to be pasted into the PR description or included in the
create-a-containerdocs.Overview
This change introduces an asynchronous job system for the
create-a-containerservice:Jobstable: stores queued commands and status (pending,running,success,failure,cancelled).JobStatusestable: stores timestamped output logs for each job.job-runner.js: a small service that runs with the same DB/config as the API server. It claimspendingjobs, executes the configuredcommandin a subprocess, streams stdout/stderr intoJobStatuses, and updates job status on exit./api/jobs:POST /api/jobs— enqueue a job (admins only).GET /api/jobs/:id— job metadata (id, command, status, timestamps).GET /api/jobs/:id/status— returns log rows; supportssinceIdandlimitquery params for incremental polling.job-runner.service— systemd unit file (added to repo) to run the runner as a system service.Files changed / added
models/job.js— Sequelize Job modelmodels/jobstatus.js— Sequelize JobStatus modelmigrations/20251117120000-create-jobs.js— migration for Jobsmigrations/20251117120001-create-jobstatuses.js— migration for JobStatusesjob-runner.js— the runner servicejob-runner.service— example systemd unitrouters/jobs.js— new API endpoints; POST restricted to adminsserver.js— mounts/api/jobsSecurity & Access Control
POST /api/jobsis restricted to admin users via the existingrequireAdminmiddleware. Other job endpoints require authentication (requireAuth) but are readable by authenticated non-admin users.commandstring. Do NOT expose this to untrusted users. Enqueue jobs only from trusted server-side code or admin UI.For long-term security, we can change
POST /api/jobsto accepttask+paramsinstead of raw commands, and map tasks to safe server-side scripts.Database migration
Run migrations from the
create-a-containerdirectory:cd create-a-container npm run db:migrateThis will create
JobsandJobStatusestables. Ensure your DB user has privileges to ALTER CREATE tables.Running the job-runner
The runner works with the same environment as
server.js. Example manual startup (fromcreate-a-container):To run as a systemd service on the host (recommended for production):
/opt/container-creatoror adjust paths).If you provide environment variables via
/etc/default/container-creator, addEnvironmentFile=/etc/default/container-creatorto the unit file.Important env variables
job-runner.jsrespects these environment variables:JOB_RUNNER_POLL_MS— poll interval in ms (default 2000)JOB_RUNNER_CWD— working directory for spawned jobs (defaults to service cwd)The runner also uses your DB config from
config/config.js(which in-turn uses.env). Ensure DB env vars (MYSQL_HOST,MYSQL_USER,MYSQL_PASSWORD,MYSQL_DATABASE) are set.How to enqueue a job (admin)
From the UI (recommended): sign in as an admin and use the admin UI that enqueues jobs server-side.
Using
curlwith session cookie (example):cookies.txt.Response:
{ "id": 123, "status": "pending" }Fetching job logs
Poll for job statuses (incremental polling):
The API returns an array of objects:
{ id, output, createdAt }. UsesinceIdto avoid re-downloading old logs.Frontend streaming / status page
The existing frontend
views/status.htmlcurrently talks to an in-memoryjobsobject. With the new persistent job system you should:GET /api/jobs/:id/statuswithsinceIdand append new output lines.JobStatusrows as they are created. The current implementation supports polling and incremental reads.Testing plan
curllogin + POST as described above.Jobsrow created withpendingstatus.job-runnerpicks up the job (watch journalctl or runner stdout) and job status becomesrunning.JobStatusesrows appear and contain stdout/stderr chunks.successorfailureon exit.GET /api/jobs/:id/statusreturns the accumulated log rows.Rollback
To remove the feature, revert this pull request and run migrations to drop
JobStatusesandJobstables (or run the down migration):(Adjust migration undo commands according to your migration tooling.)
Future work / improvements
commandstrings with task identifiers + params to avoid arbitrary shell execution.MAX_WORKERS).