Rebrand to volundr; add deploy artifacts
- User-facing brand "bike-admin" -> "volundr" (page titles, top bar, login header, FastAPI title, README/AGENTS). Directory, remote, BIKE_ADMIN_* env prefix and bike_admin.db filename are unchanged. - Add deploy/: systemd unit (bike-admin.service) and a production .env template (env.production.example).
This commit is contained in:
@@ -4,7 +4,7 @@ Guidance for working in this repo.
|
|||||||
|
|
||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
`bike-admin` is a small single-user web app for logging bike service/repair
|
**volundr** (packaged as `bike_admin`) is a small single-user web app for logging bike service/repair
|
||||||
"events" (type, date, mileage, cost, notes) and seeing per-bike hints: a
|
"events" (type, date, mileage, cost, notes) and seeing per-bike hints: a
|
||||||
**service-due** status derived from the newest mileage and the most recent
|
**service-due** status derived from the newest mileage and the most recent
|
||||||
`service` event, plus **MOT / insurance renewal** statuses derived from the most
|
`service` event, plus **MOT / insurance renewal** statuses derived from the most
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# bike-admin
|
# volundr
|
||||||
|
|
||||||
A small, secure, single-user web app for logging bike service and repair
|
A small, secure, single-user web app for logging bike service and repair
|
||||||
"events" (type, date, mileage, cost, notes) and getting a clear **service-due**
|
"events" (type, date, mileage, cost, notes) and getting a clear **service-due**
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
"""bike-admin package."""
|
"""volundr package."""
|
||||||
|
|||||||
+1
-1
@@ -215,7 +215,7 @@ async def lifespan(_: FastAPI):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="bike-admin", lifespan=lifespan)
|
app = FastAPI(title="volundr", lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(FastAPIUnauthorized)
|
@app.exception_handler(FastAPIUnauthorized)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}bike-admin{% endblock %}</title>
|
<title>{% block title %}volundr{% endblock %}</title>
|
||||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
<script src="/static/htmx.min.js" defer></script>
|
<script src="/static/htmx.min.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<a class="brand" href="/">bike-admin</a>
|
<a class="brand" href="/">volundr</a>
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a href="/bikes/new" class="btn btn-primary">+ Bike</a>
|
<a href="/bikes/new" class="btn btn-primary">+ Bike</a>
|
||||||
<a href="/logout"
|
<a href="/logout"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{{ bike.name }} - bike-admin{% endblock %}
|
{% block title %}{{ bike.name }} - volundr{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<h1>{{ bike.name }}</h1>
|
<h1>{{ bike.name }}</h1>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Edit bike - bike-admin{% endblock %}
|
{% block title %}Edit bike - volundr{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-head"><h1>Edit {{ bike.name }}</h1></div>
|
<div class="page-head"><h1>Edit {{ bike.name }}</h1></div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}New bike - bike-admin{% endblock %}
|
{% block title %}New bike - volundr{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-head"><h1>New bike</h1></div>
|
<div class="page-head"><h1>New bike</h1></div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}Dashboard - bike-admin{% endblock %}
|
{% block title %}Dashboard - volundr{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<h1>Your bikes</h1>
|
<h1>Your bikes</h1>
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Log in - bike-admin</title>
|
<title>Log in - volundr</title>
|
||||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="auth">
|
<body class="auth">
|
||||||
<main class="container auth-card">
|
<main class="container auth-card">
|
||||||
<h1 style="margin-top:0">bike-admin</h1>
|
<h1 style="margin-top:0">volundr</h1>
|
||||||
<p class="muted">Log in to manage your bikes.</p>
|
<p class="muted">Log in to manage your bikes.</p>
|
||||||
|
|
||||||
{% if error %}
|
{% if error %}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=volundr (bike service-due + MOT/insurance tracker)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=bike
|
||||||
|
Group=bike
|
||||||
|
WorkingDirectory=/opt/bike_admin
|
||||||
|
# Leading `-` = optional: the app still boots (and prompts for a password)
|
||||||
|
# if the .env has not been created yet.
|
||||||
|
EnvironmentFile=-/opt/bike_admin/.env
|
||||||
|
ExecStart=/opt/bike_admin/.venv/bin/uvicorn app.main:app \
|
||||||
|
--host 0.0.0.0 --port 8000 \
|
||||||
|
--forwarded-allow-ips 192.168.0.0/16 --proxy-headers
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=true
|
||||||
|
ReadWritePaths=/opt/bike_admin/data
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# bike_admin production environment (copied to /opt/bike_admin/.env on the server)
|
||||||
|
# All values are BIKE_ADMIN_-prefixed. This file is NOT committed (see .gitignore).
|
||||||
|
|
||||||
|
# --- Required -------------------------------------------------
|
||||||
|
BIKE_ADMIN_USERNAME=jake
|
||||||
|
BIKE_ADMIN_PASSWORD=change-me-to-something-strong
|
||||||
|
|
||||||
|
# --- Required in production -----------------------------------
|
||||||
|
# Secret used to derive the CSRF / session tokens. Generate with:
|
||||||
|
# python3 -c "import secrets;print(secrets.token_urlsafe(32))"
|
||||||
|
BIKE_ADMIN_SECRET_KEY=
|
||||||
|
|
||||||
|
# Adds the Secure flag to the session cookie (we're served over HTTPS via Caddy).
|
||||||
|
BIKE_ADMIN_IS_PRODUCTION=true
|
||||||
|
|
||||||
|
# Bind to all interfaces so the hermodr reverse proxy (on another host) can reach it.
|
||||||
|
# The app trusts X-Forwarded-* headers from the proxy (BIKE_ADMIN_TRUST_PROXY defaults true).
|
||||||
|
BIKE_ADMIN_BIND_HOST=0.0.0.0
|
||||||
|
BIKE_ADMIN_BIND_PORT=8000
|
||||||
|
|
||||||
|
# --- Optional fine-tuning (defaults shown) ---------------------
|
||||||
|
# BIKE_ADMIN_SERVICE_DUE_SOON_MILES=200 # service "book soon" lead time
|
||||||
|
# BIKE_ADMIN_RENEWAL_DUE_SOON_DAYS=60 # MOT/insurance "due soon" lead time
|
||||||
|
# BIKE_ADMIN_RENEWAL_MONTHS=12 # MOT/insurance validity (months)
|
||||||
|
# BIKE_ADMIN_DATA_DIR=/opt/bike_admin/data
|
||||||
Reference in New Issue
Block a user