This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
TaskFlow is a lightweight, self-hosted task scheduler with:
- Backend: Go with SQLite, single binary deployment
- Frontend: Vue.js SPA with Vite
- Core Features: Job scheduling (cron-like), script execution, real-time logs via WebSocket, CPU/memory metrics, JWT auth with admin/user roles
# Build both frontend and backend
make build
# Run the built binary (generates random JWT_SECRET)
make run
# Development mode with hot-reload
make dev # Terminal 1: Backend server on :8080
make dev-frontend # Terminal 2: Frontend dev server (optional)
# Frontend only
make build-frontend
make dev-frontend
# Backend only
make build-backend
make dev
# Clean all artifacts
make clean# Run all tests (uses in-memory SQLite for isolation)
make test
# Run specific test package
go test -v ./internal/auth
# Run single test function
go test -v ./internal/auth -run TestHashPassword
# Run tests with coverage
go test -cover ./...- User Request → API handlers (internal/api) → Store operations (internal/store)
- Job Scheduling → Scheduler checks every minute (internal/scheduler/scheduler.go:71-82) → Matcher validates cron rules → JobQueue enqueues
- Job Execution → Executor spawns bash subprocess → Streams logs to database → Updates run status with exit code
- Real-time Logs → WebSocket hub (internal/api/websocket.go) → Client subscriptions
HTTP Request
↓
CORSMiddleware (internal/api/middleware.go)
↓
AuthMiddleware (validates JWT, loads user context)
↓
Route Handler (internal/api/handlers.go)
↓
Store Operations (internal/store/*.go)
↓
Database (SQLite)
Job Created → Scheduled by Scheduler → Enqueued → Executed (one at a time)
↓
Logs streamed to DB
↓
Metrics captured
↓
Run record updated
- Central database abstraction
- Methods for CRUD operations on jobs, runs, users, logs, metrics
- Automatic migrations on startup
- Uses
sql.DBfor connection pooling
- Runs every 60 seconds (time.Minute ticker)
- Calls Matcher to check if job should run based on schedule
- Prevents duplicate runs in same minute
- Uses JobQueue for sequential execution
- Single-threaded: only one job runs at a time
- Spawns bash subprocess for each job
- Respects timeout context (ctx.WithTimeout)
- Streams stdout/stderr to database
- Captures exit code and determines success/failure/timeout
- Called directly by job handler in main.go:63-76
- Standard Go http.ServeMux
- Handlers grouped: auth (login, setup), jobs (CRUD), runs (history, logs)
- WebSocket endpoint for live log streaming
- CORS and Auth middleware
- JWT signing with HS256
- Bootstrap mode: first user creation without auth (setup endpoint disabled after)
- Role-based access control: admin can create jobs, users can only run their own
users - User accounts with roles (admin/user)
jobs - Job definitions (script, timeout, retry settings)
schedules - Cron-like scheduling rules (flexible year/month/day/hour/minute)
runs - Execution history (status, exit code, timestamps)
logs - Streaming job output (stdout, stderr, system messages)
metrics - CPU/memory samples during execution (2s interval)
metrics_aggregate - Hourly/daily aggregated statistics
settings - Key-value configuration store
Jobs run one at a time in FIFO order (see CanExecute in executor.go:154-164). This is by design for Phase 1 to avoid resource contention on single-server deployments.
On first run with no users, the /setup/admin endpoint allows creating the first admin without JWT auth. After first user is created, this endpoint becomes disabled. See handlers.go for bootstrap logic.
The scheduler checks if a job already ran in the same minute (scheduler.go:108-120) to prevent accidental duplicate executions when the clock rolls over.
Runs older than 30 days (configurable via LOG_RETENTION_DAYS) are automatically deleted daily at cleanup time. See main.go:83-93 for cleanup goroutine.
Jobs have a timezone field. The Matcher respects timezones when evaluating cron schedules. See internal/scheduler/matcher.go for logic.
All config via environment variables (loaded in internal/config/config.go):
PORT=8080 # HTTP listen port
DB_PATH=taskflow.db # SQLite file path
JWT_SECRET=<required> # HMAC secret for JWT signing
LOG_LEVEL=info # Logging verbosity
LOG_RETENTION_DAYS=30 # Delete runs older than this
ALLOWED_ORIGINS=* # CORS allowed origins
API_BASE_PATH=/taskflow/api # Base path for all API endpoints (default: /taskflow/api)
SMTP_SERVER/PORT/USERNAME/PASSWORD # Optional email notifications
For frontend, set VITE_API_BASE_PATH at build time to match the backend's API_BASE_PATH.
- Tests use in-memory SQLite (":memory:" connection string) for speed and isolation
- Helper: testutil.go provides setup/teardown utilities
- Test packages: auth_test.go, middleware_test.go, handlers_test.go, executor_test.go, matcher_test.go
- Run with
make testorgo test -v ./...
- Define handler in internal/api/handlers.go
- Register route in internal/api/router.go with appropriate middleware
- Add store method in internal/store/*.go if needed
- Write tests in internal/api/handlers_test.go
- Add SQL query in relevant file (internal/store/{jobs,runs,logs,users,metrics}.go)
- Add test in that same file
- Call from handlers
- Check logs: query logs table for run_id
- Check metrics: query metrics table for run_id
- Check run status: query runs table for status/error_message
- Enable LOG_LEVEL=debug for verbose logging
- Scheduler checks jobs every minute (time.Minute ticker)
- Matcher determines if cron schedule matches current time (see matcher_test.go for examples)
- Avoid blocking operations in scheduler main loop
Vue.js SPA: web/frontend/
- Vite build tool
- npm install && npm run build (integrated in make build)
- Development: npm run dev on separate port
- API client: web/frontend/src/services/api.js
- State management: Pinia stores in web/frontend/src/stores/
- Single-threaded job execution prevents resource contention
- In-memory SQLite caching improves query performance
- WebSocket streaming reduces log polling overhead
- Metrics sampled at 2-second intervals during job execution (can be adjusted in executor.go)
- Enable debug logging:
LOG_LEVEL=debug - Inspect database:
sqlite3 taskflow.db - Check scheduler state: See IsRunning() in scheduler.go
- Monitor job queue: See JobQueue in scheduler/queue.go
- Watch WebSocket connections: api/websocket.go maintains active client list