Skip to content

Repository files navigation

Device Lending Portal

Self-hosted web app for cataloging lendable devices (drills, saws, etc.) and running a request → handover → return lending workflow with email notifications. Built on PocketBase embedded in a single Go binary; server-rendered UI with htmx, no Node.js/JS build step.

Prerequisites

  • An OIDC provider (tested against Authentik; any standard OIDC provider, e.g. Zitadel, should work).
  • Docker and Docker Compose, for self-hosted deployment.

OIDC provider setup

  1. Create an OAuth2/OIDC application/provider for this app.
  2. Set its redirect URI to ${BASE_URL}/oidc/callback (must match BASE_URL exactly, including scheme and no trailing slash).
  3. Add a groups scope mapping to the client so the groups claim is included in the userinfo/id_token response — required for admin sync to work. Without this, OIDC_ADMIN_GROUP has nothing to match against and no user will ever become an admin via login.
  4. Note the issuer URL, client ID, and client secret for the next step.

Configuration

Copy .env.example to .env and fill in:

Variable Required Description
OIDC_ISSUER yes Your provider's issuer URL (its /.well-known/openid-configuration must be reachable at <issuer>/.well-known/openid-configuration)
OIDC_CLIENT_ID / OIDC_CLIENT_SECRET yes From your OIDC provider
OIDC_ADMIN_GROUP no Group name granting admin/cleanup rights; leave unset to disable admin sync
BASE_URL yes Public URL this app is reachable at, no trailing slash — must be https:// in any real deployment, see below
SESSION_SECRET yes Random string, 32+ characters, used to sign session cookies
PUBLIC_READ no (default false) true to let anyone browse/view devices without logging in; requesting a device always requires login regardless
DEV_AUTH no (default false) true to skip OIDC entirely for local development — see "Local development without OIDC" below

HTTPS is required

BASE_URL must be an https:// URL (a real hostname, not localhost) in any deployment you actually log into. The session cookie this app sets after an OIDC login is marked Secure, so browsers only store and return it over a secure context — over plain http:// on a non-localhost host the cookie is silently dropped and login appears to do nothing: you are redirected back to the home page still logged out, with no error anywhere.

Terminate TLS in front of the container (reverse proxy, ingress, tunnel) and point BASE_URL at that public HTTPS address. http://localhost:8090 is fine for local development only, because browsers treat localhost as a secure context.

Local development without OIDC

For running the app on your own machine with no OIDC provider at all, set DEV_AUTH=true. This swaps the entire OIDC login flow for two fixed, pre-seeded PocketBase accounts you log into with one click:

DEV_AUTH=true
BASE_URL=http://localhost:8090
SESSION_SECRET=<any 32+ character string>

OIDC_ISSUER/OIDC_CLIENT_ID/OIDC_CLIENT_SECRET are not required in this mode. Boot the app, then visit ${BASE_URL}/dev/login and click "Log in as regular user" or "Log in as admin" — no credentials to type, no provider to register with.

This is enforced to be local-only. DEV_AUTH=true requires BASE_URL to be http://localhost... or http://127.0.0.1... — the app refuses to boot under DEV_AUTH=true with any other BASE_URL, so this cannot accidentally end up active in a real deployment. Never set DEV_AUTH=true anywhere other than your own machine.

The two seeded accounts let you exercise both is_admin code paths (e.g. the /admin cleanup view, edit/delete permissions) without needing a real OIDC group claim.

Running with Docker Compose

cp .env.example .env
# edit .env with real values
docker compose up -d --build

The app listens on port 8090. All state (SQLite database + uploaded photos) lives in the pb_data named volume.

First-run setup

On first boot, PocketBase has no superuser account yet. Create one to access the PocketBase admin panel (used only for initial setup — category management and configuring automated backups — not for day-to-day app use):

docker compose exec device-lending ./device-lending superuser upsert admin@example.com <a-strong-password>

Then visit ${BASE_URL}/_/ to log into the PocketBase admin panel, where you can add device categories (Collections → categories) and configure S3-compatible automated backups (Settings → Backups).

Email / SMTP settings

The app sends notification emails for the whole lending workflow (new request, request declined, handover, device now unavailable, device available again, request withdrawn, device removed). There are no SMTP environment variables: mail is configured in the PocketBase admin panel you just logged into, under Settings → Mail settings — enable the SMTP server and fill in host, port, credentials and the sender address/name. Use its "Send test email" button to confirm the settings before relying on them.

Until SMTP is configured, notification emails simply do not go out: PocketBase falls back to a local sendmail binary, which is not present in this app's Alpine-based image, so every send fails. This is logged (visit Logs in the admin panel) but is deliberately not treated as an error by the app — the underlying action still succeeds and is saved, so a user who submits a request or hands a device over gets the normal confirmation and the state change sticks. Only the notification is lost, so people will not be told about requests they need to act on until you configure SMTP.

Becoming an admin

Log into the app once via ${BASE_URL}/oidc/login so your user record exists, then make sure your account is a member of the OIDC_ADMIN_GROUP group in your OIDC provider. Admin status syncs on every login.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages