add autokanban

This commit is contained in:
Aron Petau 2025-11-27 13:59:18 +01:00
parent b05d8a8ab3
commit 745bf9a29a
9 changed files with 181 additions and 3 deletions

View file

@ -27,6 +27,7 @@
- **📓 JupyterHub**: Multi-user Jupyter notebook server for drone programming (port 8001) - **📓 JupyterHub**: Multi-user Jupyter notebook server for drone programming (port 8001)
- **🦊 Forgejo**: Self-hosted Git service for code collaboration (port 3003) - **🦊 Forgejo**: Self-hosted Git service for code collaboration (port 3003)
- **🔄 Watchtower**: Automatic container updates every 24 hours - **🔄 Watchtower**: Automatic container updates every 24 hours
- **🗂️ Autokanban**: Kollektive ToDoListe / Aufgabenboard (port 8000)
--- ---
@ -132,6 +133,7 @@ graph TB
- **curl** - for testing and healthchecks (install: `brew install curl` on macOS) - **curl** - for testing and healthchecks (install: `brew install curl` on macOS)
- **Poppler** (for pdf2image - included in Docker) - **Poppler** (for pdf2image - included in Docker)
- **Port availability**: 80, 3003, 6875, 8001, 8008, 8080, 8082, 9000, 11434 - **Port availability**: 80, 3003, 6875, 8001, 8008, 8080, 8082, 9000, 11434
- **Port availability**: 80, 3003, 6875, 8000, 8001, 8008, 8080, 8082, 9000, 11434
**Note:** Python dependencies are containerized and don't need to be installed on the host. **Note:** Python dependencies are containerized and don't need to be installed on the host.
@ -164,9 +166,9 @@ graph TB
- Portainer: <http://localhost:9000> - Portainer: <http://localhost:9000>
- Matrix: <http://localhost:8008> - Matrix: <http://localhost:8008>
- Element Web: <http://localhost:8082> - Element Web: <http://localhost:8082>
- Autokanban: <http://localhost:8000>
- JupyterHub: <http://localhost:8001> - JupyterHub: <http://localhost:8001>
- Forgejo: <http://localhost:3003> - Forgejo: <http://localhost:3003>
- Forgejo: <http://localhost:3003>
### First-Time Setup ### First-Time Setup
@ -339,7 +341,32 @@ Creates timestamped backups of:
--- ---
## 🚢 Deployment & Portability ## <20> BookStack API (create pages programmatically)
You can automate content creation in BookStack using the HTTP API and a Personal Access Token (User Settings → API Tokens).
Example `curl` to list books:
```bash
curl -H "Authorization: Token token=\"$BOOKSTACK_API_TOKEN\"" \
"$BOOKSTACK_APP_URL/api/books"
```
Create a page with `curl`:
```bash
curl -X POST -H "Authorization: Token token=\"$BOOKSTACK_API_TOKEN\"" \
-H "Content-Type: application/json" \
-d '{"name":"My Page","book_id":1,"html":"<h1>Hello</h1>"}' \
"$BOOKSTACK_APP_URL/api/pages"
```
There's a small helper script in the repo: `bookstack_api_create_page.py` which demonstrates listing books and creating a page interactively.
---
## <20>🚢 Deployment & Portability
This application is designed to be **fully portable**. To move to a new server: This application is designed to be **fully portable**. To move to a new server:

View file

@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""
Small helper to demonstrate creating a BookStack page via the BookStack HTTP API.
Usage:
- Create a Personal Access Token in BookStack (User Settings API Tokens).
- Add the token and app URL to your `.env` or export env vars:
BOOKSTACK_APP_URL=http://124.local:6875
BOOKSTACK_API_TOKEN=your_token_here
- Run: python bookstack_api_create_page.py
This script will list available books and create a page in the chosen book.
"""
import os
import sys
import json
import requests
from urllib.parse import urljoin
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
pass
BOOKSTACK_APP_URL = os.environ.get("BOOKSTACK_APP_URL")
BOOKSTACK_API_TOKEN = os.environ.get("BOOKSTACK_API_TOKEN")
if not BOOKSTACK_APP_URL or not BOOKSTACK_API_TOKEN:
print("Please set BOOKSTACK_APP_URL and BOOKSTACK_API_TOKEN in the environment or .env file.")
sys.exit(1)
def auth_headers():
# BookStack expects: Authorization: Token token="<token>"
return {
"Authorization": f'Token token="{BOOKSTACK_API_TOKEN}"',
"Content-Type": "application/json",
"Accept": "application/json",
}
def list_books():
url = urljoin(BOOKSTACK_APP_URL, "/api/books")
r = requests.get(url, headers=auth_headers(), timeout=10)
r.raise_for_status()
data = r.json()
return data.get("data", [])
def create_page(book_id: int, name: str, html: str):
url = urljoin(BOOKSTACK_APP_URL, "/api/pages")
payload = {
"name": name,
"book_id": book_id,
"html": html,
"markdown": None,
"draft": False,
}
r = requests.post(url, headers=auth_headers(), data=json.dumps(payload), timeout=10)
r.raise_for_status()
return r.json()
def main():
print(f"Using BookStack: {BOOKSTACK_APP_URL}")
books = list_books()
if not books:
print("No books found. Create a book in BookStack first or verify token permissions.")
return
print("Available books:")
for i, b in enumerate(books):
print(f" [{i}] {b.get('name')} (id={b.get('id')})")
choice = input("Choose a book index to create the page in (default 0): ") or "0"
try:
idx = int(choice)
except ValueError:
idx = 0
book = books[idx]
book_id = book.get("id")
title = input("Page title: ") or "Automated Page"
content = input("Simple HTML content (or leave empty for a sample): ")
if not content:
content = f"<h2>{title}</h2><p>Created automatically via API.</p>"
resp = create_page(book_id, title, content)
print("Created page:")
print(json.dumps(resp, indent=2))
if __name__ == "__main__":
main()

View file

@ -1 +1 @@
MANIFEST-000004 MANIFEST-000014

View file

@ -14,3 +14,48 @@
14:41:11.539093 version@stat F·[1] S·542B[542B] Sc·[0.25] 14:41:11.539093 version@stat F·[1] S·542B[542B] Sc·[0.25]
14:41:11.543912 db@janitor F·3 G·0 14:41:11.543912 db@janitor F·3 G·0
14:41:11.543982 db@open done T·6.225416ms 14:41:11.543982 db@open done T·6.225416ms
=============== Nov 12, 2025 (CET) ===============
09:57:26.179138 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
09:57:26.186435 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:57:26.191524 db@open opening
09:57:26.194711 journal@recovery F·1
09:57:26.195667 journal@recovery recovering @3
09:57:26.197988 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:57:26.216087 db@janitor F·3 G·0
09:57:26.217023 db@open done T·24.267292ms
=============== Nov 22, 2025 (CET) ===============
10:01:25.477377 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
10:01:25.477990 version@stat F·[1] S·542B[542B] Sc·[0.25]
10:01:25.478025 db@open opening
10:01:25.478154 journal@recovery F·1
10:01:25.478214 journal@recovery recovering @5
10:01:25.478556 version@stat F·[1] S·542B[542B] Sc·[0.25]
10:01:25.480013 db@janitor F·3 G·0
10:01:25.480040 db@open done T·1.995458ms
=============== Nov 27, 2025 (CET) ===============
09:57:27.459640 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
09:57:27.463226 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:57:27.463855 db@open opening
09:57:27.464798 journal@recovery F·1
09:57:27.465256 journal@recovery recovering @7
09:57:27.472782 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:57:27.511286 db@janitor F·3 G·0
09:57:27.511538 db@open done T·47.508458ms
=============== Nov 27, 2025 (CET) ===============
09:58:40.940724 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
09:58:40.944445 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:58:40.945532 db@open opening
09:58:40.946059 journal@recovery F·1
09:58:40.946299 journal@recovery recovering @9
09:58:40.951856 version@stat F·[1] S·542B[542B] Sc·[0.25]
09:58:40.957677 db@janitor F·3 G·0
09:58:40.958035 db@open done T·12.392458ms
=============== Nov 27, 2025 (CET) ===============
10:01:13.275972 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
10:01:13.278440 version@stat F·[1] S·542B[542B] Sc·[0.25]
10:01:13.278524 db@open opening
10:01:13.278925 journal@recovery F·1
10:01:13.279002 journal@recovery recovering @11
10:01:13.279809 version@stat F·[1] S·542B[542B] Sc·[0.25]
10:01:13.283533 db@janitor F·3 G·0
10:01:13.283640 db@open done T·4.933416ms

Binary file not shown.

View file

@ -102,6 +102,15 @@
</div> </div>
</a> </a>
<a class="link-card" href="http://autokanban.local:8000" target="_blank">
<i class="fas fa-tasks card-icon"></i>
<div class="card-content">
<div class="title">Autokanban</div>
<div class="tagline">Kollektive ToDoListe: Jede*r kann Aufgaben posten und lösen. Für das Lösen
von Aufgaben gibt es Badges — sammle Anerkennung für deinen Beitrag!</div>
</div>
</a>
<a class="link-card" href="mailto:einszwovier@gvb-gymnasium.de" target="_blank"> <a class="link-card" href="mailto:einszwovier@gvb-gymnasium.de" target="_blank">
<i class="fas fa-envelope card-icon"></i> <i class="fas fa-envelope card-icon"></i>
<div class="card-content"> <div class="card-content">