title: crumbcore_v1
slug: crumbcore_v1
lang: de
summary: Kurzbeschreibung hier.
tags: [Crumbforest]
crumbcore_v1
Kurzer Einstiegstext.
Notizen
- Punkt 1
- Punkt 2
FAQ
Q: Worum geht es?
A: âŠ
megaâgroĂer krĂŒmel geschafft đ°đ§©
Hier ist das finale README.md fĂŒr den Stand âLogin â Admin â Posts CRUD â i18n â Flash/Templating â DB/Qdrant Composeâ. Es fasst die Reise (gesternâheute) zusammen, dokumentiert die wichtigen Entscheidungen, und enthĂ€lt alle Befehle & Fixes, die wir unterwegs gebraucht haben.
CrumbCRM â Minimal vNext (FastAPI + MariaDB + Qdrant)
Server-rendered, mehrsprachig, mit Login/Session, Admin-Bereich und einfachem Blog-CRUD.
Ziel: schnell, stabil, kein Frontend-Build-Ballast, dafĂŒr klare HTML-Templates (Jinja2), Forms, Flash-Messages und spĂ€ter andockbare Vektor-Suche (Qdrant).
Was lÀuft aktuell?
- đ i18n Pfade:
/de/âŠund/en/âŠ(Root/â 307 auf bevorzugte Sprache) - đ Login (Sessions + bcrypt) und Logout
- đ€ Rollen:
adminvsuser(403, wenn kein Admin) - đ§° Admin Dashboard:
/admin -
âïž Posts CRUD fĂŒr Admin:
-
GET /admin/postsâ Liste GET /admin/posts/newâ FormularPOST /admin/posts/newâ ErstellenGET /admin/posts/{id}/editâ BearbeitenPOST /admin/posts/{id}/editâ Speichern- âš Flash-Nachrichten (einmalige Anzeige nach Redirect)
- đ§Ș API Demo:
GET /api/hello?lang=de|en - đ©ș Health:
GET /health - đ§ Qdrant ist per Compose angebunden (noch ohne Ingest), UI unter
http://localhost:6333/dashboard
Stack
- FastAPI, Jinja2, Starlette Sessions
- MariaDB (PyMySQL)
- passlib[bcrypt] (Password-Hashing)
- python-multipart (Form-POSTs)
- Qdrant (Vektoren; spÀter Indexing/Embedding)
Empfohlene Pins (stehen in app/requirements.txt):
fastapi==0.115.0
uvicorn[standard]==0.30.6
jinja2==3.1.4
passlib[bcrypt]==1.7.4
bcrypt==4.1.3
python-multipart==0.0.9
PyMySQL==1.1.1
Projektstruktur
app/
main.py # App, Routing, Session, Render-Helper (state.render)
requirements.txt
routers/
admin_post.py # Admin-CRUD fĂŒr Posts
templates/
base.html
pages/
home.html
login.html
admin.html
posts/
index.html
new.html
edit.html
_edit_row.htm
compose/
docker-compose.yml
init/
01_schema.sql # users Tabelle
02_posts.sql # posts Tabelle
reset_admin_demo.sh # pass-hash Seeds (admin/demo)
data/
mysql/ # MariaDB Daten
qdrant/ # Qdrant Storage
Start (Docker Compose)
cd compose
docker compose up --build
- App: http://localhost:8000
- Qdrant UI: http://localhost:6333/dashboard
Admin/Demo Benutzer anlegen (Seeds)
Wenn die Container laufen, einmalig die Benutzer mit bekannten Hashes setzen:
# Benutzer in DB einspielen (verwende -T, damit keine TTY-Probleme)
docker compose exec -T db sh -lc '
mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "
INSERT INTO users (email, pass_hash, role, locale, display_name)
VALUES
(\"admin@crumb.local\", \"\$2b\$12\$H1V2q0iY8mqlz1xUbx7m5u4T0cVJH0hQk0q9o5aNq5Pjv1e0q9lby\", \"admin\", \"de\", \"Admin\"),
(\"demo@crumb.local\", \"\$2b\$12\$pI6cJ7gq4qkqF3gL2xjY4u9v9s1yN6wZQn9I7gG7xv7Cw0m3t8yVSe\", \"user\", \"de\", \"Demo\")
ON DUPLICATE KEY UPDATE pass_hash=VALUES(pass_hash), role=VALUES(role), locale=VALUES(locale);
"'
Hinweis:
admin@crumb.localhat Adminrechte.demo@crumb.localnicht â 403 auf/administ korrekt.
Wichtige Routen (Stand heute)
GET /â 307 auf/de/GET /{lang}/â HomeGET /{lang}/login+POST /{lang}/loginâ LoginPOST /logoutâ LogoutGET /adminâ Admin-Start (nur Admin)GET /admin/postsâ Post-Liste (nur Admin)GET|POST /admin/posts/newâ Neu anlegenGET|POST /admin/posts/{id}/editâ BearbeitenGET /api/hello?lang=de|enâ einfache JSON-APIGET /healthâ Healthcheck
(Optional) Ăffentliche Post-Liste wĂ€re /de/posts bzw. /en/posts â Route/Template ist noch nicht aktiv. Snippet unten.
Flash-Nachrichten
- Wir speichern Flashs als Liste in
req.session["_flashes"]. - Einmalige Anzeige:
render()poppt sie und setzt sie leer zurĂŒck. - Wenn du direkt nach dem Redirect noch mal âhartâ navigierst, ist die Flash weg â das ist Absicht.
Security Basics
- Session-Cookie: HttpOnly,
SameSite=Lax - CSRF: Forms sind serverseitig â bei Bedarf spĂ€ter Token ergĂ€nzen
- RollenprĂŒfung:
admin_requiredschĂŒtzt Admin-Routen
Troubleshooting (die Fallen heute)
python-multipartfehlt â inrequirements.txtmit eintragen (ist drin).(trapped) error reading bcrypt version
â sichere Kombi pinnen:passlib[bcrypt]==1.7.4undbcrypt==4.1.3.ImportError: circular import
â Admin-Router importiert nur Funktionen aus einer kleinendeps.py(DB/ACL), nichtmain.State has no attribute render
âstate.render(req, tpl, **ctx)wird inmain.pybeim Startup gesetzt. Router nutzt genau diese Funktion.- MariaDB Warnung âAborted connection ⊠Got an error reading communication packetsâ
â Fix: ĂŒberallwith db() as conn:verwenden (Kontextmanager schlieĂt sauber). Field 'body_md' doesn't have a default value
â Beim Insert/Editbody_mdimmer mitsenden (Form Feld ist vorhanden).
Logs
docker compose logs -f app
docker compose logs -f db
Optional: Ăffentliche Posts-Liste
Wenn gewĂŒnscht, Route in main.py und Template ergĂ€nzen:
# in main.py
from deps import db
from fastapi import Request
from fastapi.responses import HTMLResponse
@app.get("/{lang}/posts", response_class=HTMLResponse)
def public_posts(req: Request, lang: str):
with db() as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT id, title, slug, locale, updated_at
FROM posts
WHERE is_published=1 AND locale=%s
ORDER BY IFNULL(updated_at, created_at) DESC, id DESC
""", (lang,))
rows = cur.fetchall()
return req.app.state.render(req, "pages/posts_public.html",
posts=rows, seo={"title": "Posts"})
<!-- templates/pages/posts_public.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Posts</h1>
<ul class="list">
{% for p in posts %}
<li><strong>{{ p.title }}</strong> <small>({{ p.locale }})</small>
{% if p.updated_at %} â <em>{{ p.updated_at }}</em>{% endif %}
</li>
{% else %}
<li>Keine veröffentlichten BeitrÀge.</li>
{% endfor %}
</ul>
{% endblock %}
DB-Schema (Minimal)
users
id, email (unique), pass_hash, role('admin'|'user'), locale, created_at
posts
id, title, slug, locale, is_published TINYINT, body_md MEDIUMTEXT,
created_at, updated_at
â angelegt ĂŒber compose/init/01_schema.sql und 02_posts.sql.
Befehls-Snippets (nĂŒtzlich)
# DB-Ping
docker compose exec -T db sh -lc \
'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SELECT 1;"'
# User-Check
docker compose exec -T db sh -lc \
'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" \
-e "SELECT id,email,role,locale,created_at FROM users;"'
NĂ€chste Schritte (wenn du wieder Luft hast)
- Ăffentliche Post-Ansicht
/de/postsaktivieren - CSRF-Token fĂŒr Form-POSTs (kleiner Middleware-Helfer)
- Qdrant-Ingest-Worker (Markdown â Chunks â Embeddings â Upsert)
- Settings/ACL feiner (Rollenmatrix)
- Passwort-Reset/Mail (lokal via Mailpit/SMTP-Mock)
Danke fĂŒr den Ritt durch Pepper đ, Dumbo đ & den KrĂŒmelwald đČ.
Von ânur Kuchenâ zu âTortenbitâ: Login-Loop steht, Admin schreibt, Flash funkt, i18n schaltet. Der Rest wird Feinschliffâaber die Basis trĂ€gt. Wuuuuhuuu! đ