System for creating test plans and executing them
  • JavaScript 49.9%
  • Python 31.4%
  • CSS 8.9%
  • Nix 6.4%
  • HTML 3.2%
  • Other 0.2%
Find a file
Eli Ribble cb6365df54
Some checks failed
Bundle / build (push) Failing after 8s
Rename package to mechaturk
2026-06-16 03:13:25 +00:00
.forgejo/workflows Add ref to build 2026-06-12 03:37:08 +00:00
backend Get userinfo from the token 2026-06-16 03:02:43 +00:00
frontend Add initial OIDC implementation 2026-06-16 00:52:12 +00:00
.gitignore Add easy script for starting mechaturk in dev mode 2026-06-16 02:53:32 +00:00
flake.lock Initial flake.nix for building the backend of the project. 2026-06-11 21:30:33 +00:00
flake.nix Rename package to mechaturk 2026-06-16 03:13:25 +00:00
README.md Rename package to mechaturk 2026-06-16 03:13:25 +00:00
start-mechaturk.sh Add easy script for starting mechaturk in dev mode 2026-06-16 02:53:32 +00:00

Todoiam

A simple task-tracking web app with a Python/Flask backend and a vanilla HTML/CSS/JS frontend, backed by PostgreSQL. Supports OpenID Connect for authentication (optional — the app works without it in dev mode).

Project structure

├── backend/
│   └── app.py             Flask API + OIDC + DB migrations
├── frontend/
│   ├── index.html         Main page (login/logout UI + task list)
│   ├── script.js          Client-side logic (auth + API calls)
│   └── style.css          Dark green theme (Gleipnir)
├── flake.nix              Nix flake build (python + frontend assets)
├── flake.lock
├── oidc-roadmap.md        OIDC integration plan
└── README.md

Dependencies — all resolved by the Nix flake:

  • Python: flask, flask-cors, psycopg (v3), authlib
  • No npm / node required at runtime (the frontend is static HTML/CSS/JS)

Quick start (development, no auth)

You'll need a running PostgreSQL instance with a database and user both named liam:

createdb liam
createuser liam

Start the backend (OIDC is disabled by default when no provider is configured):

nix build
./result/bin/mechaturk

Open http://127.0.0.1:5000 — Flask serves the frontend, the JS, the CSS, and the API all from the same process. No separate web server needed. The task UI loads immediately when auth is disabled.

Building

nix build

Produces ./result containing:

result/
├── bin/mechaturk                 ← runs the Flask API
├── lib/app.py                          ← Python backend source
└── share/mechaturk/frontend/
    ├── index.html
    ├── script.js
    └── style.css

The short git commit hash is stamped into index.html at build time (bottom-right corner).

OIDC configuration

Set these environment variables to enable authentication:

Variable Required Example
FLASK_SECRET_KEY yes openssl rand -hex 32
OIDC_ISSUER yes https://auth.example.com/realms/myrealm
OIDC_CLIENT_ID yes mechaturk
OIDC_CLIENT_SECRET yes
DB_HOST no localhost
DB_PORT no 5432
DB_NAME no liam
DB_USER no liam
DB_PASSWORD no

If OIDC_ISSUER and OIDC_CLIENT_ID are both absent, authentication is disabled and all routes are public — useful for local development.

The OIDC redirect URI that must be registered with your provider is https://<your-domain>/oidc/callback.

NixOS deployment with Caddy

Since Flask now serves the frontend directly, the simplest Caddy config just reverse-proxies everything to Flask:

services.caddy.virtualHosts."tasks.example.com".extraConfig = ''
  reverse_proxy localhost:5000
'';

To offload static files to Caddy (better for production throughput), use the config below. Caddy serves the frontend directly from the Nix store and only proxies API/OIDC routes to Flask:

{ config, pkgs, ... }:

let
  mechaturk = (builtins.getFlake "/path/to/mechaturk").packages.${pkgs.system}.default;
in
{
  # --- PostgreSQL (if not already configured) ---
  services.postgresql = {
    enable = true;
    ensureDatabases = [ "liam" ];
    ensureUsers = [{
      name = "liam";
      ensureDBOwnership = true;
    }];
  };

  # --- Caddy: static files + reverse proxy ---
  services.caddy = {
    enable = true;

    virtualHosts."tasks.example.com" = {
      extraConfig = ''
        root * ${mechaturk}/share/mechaturk/frontend

        # Proxy OIDC auth flows to Flask
        handle /oidc/* {
          reverse_proxy localhost:5000
        }

        # Proxy task API routes to Flask
        handle_path /gettasks   { reverse_proxy localhost:5000 }
        handle_path /addelement { reverse_proxy localhost:5000 }
        handle_path /deletetask { reverse_proxy localhost:5000 }
        handle_path /checkoff   { reverse_proxy localhost:5000 }
        handle_path /message    { reverse_proxy localhost:5000 }

        # Everything else → static frontend
        file_server
      '';
    };
  };

  # --- Flask backend as a systemd service ---
  systemd.services.mechaturk = {
    description = "Todoiam Flask API";
    after = [ "network.target" "postgresql.service" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      ExecStart = "${mechaturk}/bin/mechaturk";
      Restart = "always";
      User = "mechaturk";
      EnvironmentFile = "/etc/mechaturk/secrets";   # OIDC + DB credentials
    };
  };

  users.users.mechaturk = {
    isSystemUser = true;
    group = "mechaturk";
  };
  users.groups.mechaturk = {};
}

Create /etc/mechaturk/secrets (mode 0400, owned by mechaturk):

FLASK_SECRET_KEY=<output of: openssl rand -hex 32>
OIDC_ISSUER=https://auth.example.com/realms/myrealm
OIDC_CLIENT_ID=mechaturk
OIDC_CLIENT_SECRET=<your-client-secret>
DB_PASSWORD=<your-db-password>

Replace tasks.example.com with your domain, and /path/to/mechaturk with the actual repo path on disk (or reference it via a flake input).

API endpoints

Method Path Auth Description
GET /oidc/user public Returns current user info (or 401)
GET /oidc/login public Redirects to OIDC provider
GET /oidc/callback public OIDC authorization-code callback
POST /oidc/logout public Clears local session
GET /gettasks required List tasks for current user
POST /addelement required Create a task ({"task_name": "…"})
POST /deletetask required Delete a task
POST /checkoff required Toggle completed state
POST /message public Echo endpoint (debugging)

All protected routes return 401 when unauthenticated. The frontend automatically redirects to /oidc/login on 401.