Skip to content

Repository files navigation

MakerSpaceAPI

Unified REST API and web frontend for makerspace NFC devices — machines, checkout terminals, ATM (Bankomat) and rental stations.

Features

  • Machine sessions — NFC-authenticated machine access with per-minute billing and balance checks
  • Product checkout — EAN barcode scanning, alias EANs, stock tracking, full audit trail
  • Bankomat (ATM) — top-up, balance transfer between members, PIN-protected payout to booking targets; PDF account statements per booking target
  • Treasurer management — users with a PIN set are treasurers; PIN can be set/cleared by admins; has_pin exposed in all user API responses
  • Rental system — UHF RFID item tracking with per-user permissions
  • User self-service — logged-in users can view their own balance, transaction history, current rentals and authorised machines at /me
  • OIDC self-service card linking — devices generate a short-lived QR link; the user scans it and logs in via OIDC to link their NFC card to their account without admin intervention
  • Admin web UI — dashboard, machine registration, product/inventory management, user list, booking targets, rentals; i18n (English / German, detected from browser)
  • OIDC login — admin group and optional product-manager group via configurable OIDC claims
  • Device API tokens — machines authenticate with bearer tokens; separate checkout-only restriction

Requirements

  • Python 3.12+
  • MariaDB / MySQL 10.x+
  • An OIDC provider (e.g. Authentik, Keycloak) for admin login
  • Node.js (optional, only needed to rebuild Tailwind CSS after template changes)

Quick start (Docker)

cp .env.example .env
# Edit .env — set OIDC credentials, SECRET_KEY, database passwords
docker compose up -d

The app runs on port 8000. Alembic migrations are applied automatically on startup.

Local development

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env
alembic upgrade head
uvicorn app.main:app --reload

Interactive API docs: http://localhost:8000/api/docs

Configuration

All settings are loaded from environment variables or a .env file.

VariableDefaultDescription
DATABASE_URLmysql+pymysql://makerspace:makerspace@localhost:3306/makerspaceapiSQLAlchemy connection string
SECRET_KEYchange-me-in-productionJWT signing key — change this
DEBUGfalseEnables HTTP-only session cookies and relaxed cookie security for local dev
BASE_URLhttp://localhost:8000Public base URL
OIDC_CLIENT_IDOIDC client ID
OIDC_CLIENT_SECRETOIDC client secret
OIDC_DISCOVERY_URLOIDC discovery URL (.well-known/openid-configuration)
OIDC_REDIRECT_URIhttp://localhost:8000/auth/callbackOAuth2 callback URL
OIDC_GROUP_CLAIMgroupsJWT claim that contains group memberships
OIDC_ADMIN_GROUPmakerspace-adminsGroup name that grants full admin access
OIDC_PRODUCT_MANAGER_GROUP(empty)Group name for product-manager role (optional, see below)
OIDC_LINK_UPDATE_NAMEfalseSet true to overwrite a user's display name from OIDC claims on self-service card linking
CHECKOUT_BOX_SLUGS(empty)Comma-separated machine slugs restricted to checkout operations

For Docker Compose, also set DB_ROOT_PASSWORD, DB_NAME, DB_USER, DB_PASSWORD.

OIDC redirect URIs: Register both {BASE_URL}/auth/callback and {BASE_URL}/auth/connect/callback as allowed redirect URIs in your OIDC provider.

Product-manager role

Set OIDC_PRODUCT_MANAGER_GROUP to a group name to allow members of that group to manage products, stock, categories and EAN aliases without granting full admin access. Leaving this variable empty or unset means only admins can access product management.

Authentication

Web (OIDC + JWT cookie)

Admins log in via OIDC. After a successful OIDC callback, the server issues a signed HS256 JWT (8-hour expiry) stored in an auth_token httpOnly cookie. The JWT encodes the OIDC claims including group membership, which determines admin/product-manager access. The cookie is sent automatically by the browser on all same-origin requests.

The frontend can query GET /auth/me to retrieve the current user's identity and role flags (is_admin, is_product_manager) without exposing the token.

Any user whose OIDC account is linked to an NFC card can log in and view their own account at /me.

Self-service card linking

A device (e.g. the checkout terminal) calls POST /api/v1/users/{nfc_id}/connect-link to generate a 15-minute signed URL. The URL is typically shown as a QR code. The user scans it, is redirected to the OIDC provider, and after a successful login their NFC card is linked to their OIDC account. No admin action is required.

Devices (API tokens)

Each machine gets a bearer token on registration (POST /api/v1/machines). The plaintext token is shown once in the web UI and must be saved immediately. Tokens are stored as SHA-256 hashes.

Machines that need to create users (checkout terminals) must have machine_type = checkout or their slug listed in CHECKOUT_BOX_SLUGS.

API overview

All device-facing endpoints live under /api/v1. Full interactive docs at /api/docs.

Users

MethodPathAuthDescription
GET/api/v1/users/nfc/{nfc_id}DeviceLook up user by NFC card UID; includes has_pin
POST/api/v1/usersCheckout deviceRegister new NFC card
GET/api/v1/usersAdminList all users
GET/api/v1/users/{nfc_id}AdminGet user details; includes has_pin
PUT/api/v1/users/{nfc_id}/oidcAdminLink OIDC subject to card
POST/api/v1/users/{nfc_id}/connect-linkDeviceGenerate 15-min self-service card-linking URL
GET/api/v1/users/meOIDC sessionLogged-in user's profile and balance
GET/api/v1/users/me/transactionsOIDC sessionPaginated transaction history (?limit=&offset=)
GET/api/v1/users/me/rentalsOIDC sessionCurrently rented items
GET/api/v1/users/me/machinesOIDC sessionAuthorised machines with pricing

Machines

MethodPathAuthDescription
GET/POST/api/v1/machinesAdminList / register machines
GET/PUT/DELETE/api/v1/machines/{slug}AdminGet / update / deactivate
POST/api/v1/machines/{slug}/tokenAdminRotate API token
GET/POST/DELETE/api/v1/machines/{slug}/adminsAdminManage per-machine sub-admins (by OIDC sub)
GET/POST/PUT/DELETE/api/v1/machines/{slug}/authorizationsMachine managerGrant / update / revoke user access
GET/api/v1/machines/{slug}/authorize/{nfc_id}DeviceCheck authorisation & pricing
GET/api/v1/machines/myOIDC sessionMachines the current user manages

Sessions

MethodPathAuthDescription
POST/api/v1/sessionsDeviceStart session (charges login fee + first interval)
PUT/api/v1/sessions/{id}DeviceExtend session (charges next interval or returns 402)
DELETE/api/v1/sessions/{id}DeviceEnd session

Products

MethodPathAuthDescription
GET/api/v1/productsPublicList active products
GET/api/v1/products/{ean}PublicGet product by EAN or alias
POST/api/v1/productsProduct managerCreate product
PUT/api/v1/products/{ean}Product managerUpdate product
POST/api/v1/products/{ean}/stockProduct managerAdjust stock
POST/api/v1/products/{ean}/stocktakingProduct managerSet absolute stock
GET/api/v1/products/{ean}/auditProduct managerChange history
GET/api/v1/products/{ean}/popularityProduct managerPurchase count (last N days)
GET/POST/DELETE/api/v1/products/{ean}/aliasesProduct managerManage alias EANs
POST/api/v1/products/{ean}/purchaseCheckout devicePurchase product
GET/api/v1/categoriesPublicList product categories
POST/api/v1/categoriesProduct managerCreate category
DELETE/api/v1/categories/{name}Product managerDelete category (rejected if in use)

Bankomat

MethodPathAuthDescription
GET/api/v1/bankomat/targetsDevice or AdminList booking targets and balances
POST/api/v1/bankomat/targetsAdminCreate booking target
POST/api/v1/bankomat/topupDeviceTop up user balance + booking target
POST/api/v1/bankomat/target-topupDeviceIncrease target only (e.g. donation)
POST/api/v1/bankomat/transferDeviceTransfer balance between users
POST/api/v1/bankomat/payoutDeviceWithdraw from target (PIN required)
GET/api/v1/bankomat/transactions/{nfc_id}DeviceRecent transactions for a user
POST/api/v1/bankomat/pinAdminSet or update user PIN (bcrypt stored)
DELETE/api/v1/bankomat/pin/{nfc_id}AdminClear user PIN (removes treasurer status)

Users with a PIN set are considered treasurers and are shown in a dedicated section of the Bankomat admin page. The has_pin boolean field is included in all user API responses.

Rentals

MethodPathAuthDescription
GET/POST/api/v1/rentals/itemsAdminList / create rental items
PUT/api/v1/rentals/items/{id}AdminEdit rental item
GET/api/v1/rentals/items/{uhf_tid}/statusDeviceCheck if item is rented
GET/api/v1/rentals/authorize/{nfc_id}DeviceCheck rental permission
POST/api/v1/rentalsDeviceRent an item
DELETE/api/v1/rentals/{id}DeviceReturn item
GET/api/v1/rentals/activeAdminList all currently rented items
GET/POST/DELETE/api/v1/rentals/permissions/{nfc_id}AdminGrant / revoke rental permission

Database migrations

Migrations live in alembic/versions/ and are committed to the repository.

# Apply all pending migrations
alembic upgrade head
# Create a new migration after changing a model
alembic revision --autogenerate -m "describe the change"

Web UI

The frontend is served at / and uses Tailwind CSS with Alpine.js. Jinja2 renders the HTML shell and navigation; Alpine.js components fetch data from the /api/v1/ REST endpoints directly. Language is auto-detected from the browser's Accept-Language header with English as the fallback. German and English are supported.

PathAccessDescription
/PublicLanding page
/productsPublicProduct catalogue
/meOIDC sessionSelf-service: balance, transactions, rentals, authorised machines
/dashboardAdminStats overview
/machinesAdmin / machine sub-adminMachine list, registration, authorisations
/usersAdminMember list
/bankomatAdminBooking targets and treasurer management
/rentalsAdminRental items and permissions
/products/manageAdmin / Product managerProduct, category and alias management

Rebuilding Tailwind CSS

The compiled CSS lives at app/web/static/css/tailwind.css and is committed to the repository, so a Node.js install is not needed to run the app. To regenerate it after changing templates:

npm install
npm run build:css # one-shot build
npm run watch:css # rebuild on template changes

Migrating from legacy systems

A migration script is provided for moving data from the three legacy projects (NFCKasse, MachineUserManager, Bankomat) into MakerSpaceAPI.

python scripts/migrate_legacy.py \
--target-url mysql+pymysql://user:pass@host/makerspaceapi \
--mum-url mysql+pymysql://user:pass@host/machines \
--nfc-url mysql+pymysql://user:pass@host/nfckasse \
--bankomat-url mysql+pymysql://user:pass@host/bankomat

Use --dry-run to preview changes without writing. Use --only machines products targets to run specific sections. See python scripts/migrate_legacy.py --help for all options.

Note: NFCKasse card UIDs were stored as MD5 hashes and cannot be reversed. Cards registered in the legacy NFCKasse system must be re-registered in MakerSpaceAPI.

Legacy hardware projects

The projects/ directory contains updated versions of the three original hardware projects, modified to call the MakerSpaceAPI REST endpoints instead of connecting directly to MySQL.

ProjectConfigKey change
MachineUserManager/Set DB_TYPE = 'api', API_URL, API_TOKEN in user_config.pyNew dbconnectors/db_api.py connector
Bankomat/Set API_URL, API_TOKEN in user_config.pyunified_kasse.py, nfckasse.py, machines.py use REST API
NFCKasse/Set api_url, api_token, uid_hash = False in settings.pydatabase.py uses REST API; raw integer UIDs

See the *.example.py config files in each project directory for the required settings.

Project structure

app/
├── api/v1/ # REST API endpoints
├── auth/ # OIDC client, JWT helpers, FastAPI dependencies
│ ├── deps.py # get_session_user, get_current_device, etc.
│ ├── jwt.py # create_admin_jwt / verify_admin_jwt (HS256 cookie auth)
│ ├── oidc.py # authlib OIDC client, is_admin / is_product_manager
│ └── tokens.py # Machine API token generation / SHA-256 verification
├── models/ # SQLAlchemy models
├── schemas/ # Pydantic request/response schemas
└── web/
├── locales/ # i18n translation files (en.json, de.json)
├── static/
│ ├── css/ # tailwind.css (CLI-built, committed)
│ └── js/ # alpine.min.js, htmx.min.js (local copies)
├── templates/ # Jinja2 HTML templates (shell + Alpine.js components)
├── auth.py # OIDC login / callback / logout + /auth/me
├── i18n.py # Language detection and translation helpers
└── router.py # Web page routes (thin — no DB queries)
alembic/versions/ # Database migration scripts
scripts/ # Utility scripts (migrate_legacy.py)
projects/ # Updated legacy hardware project connectors
tests/ # pytest test suite
package.json # Tailwind CSS CLI build (optional, for CSS rebuilds)
tailwind.config.js # Tailwind content paths

Tests

pytest

The test suite uses an in-memory SQLite database and does not require a running MySQL server or OIDC provider.

About

Unified API for machines, rentals, buying material/drinks/... Powered by OIDC login and NFC-cards

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages