wahnsinn vibe

This commit is contained in:
Marek Lenczewski
2026-04-16 19:42:06 +02:00
parent 9c5da44f64
commit e3e88cc58e
127 changed files with 9456 additions and 3 deletions

265
backend/core/seed.py Normal file
View File

@@ -0,0 +1,265 @@
"""Seed DB with admin + demo customer + categories + 12 clothing products."""
from __future__ import annotations
from core.db import SessionLocal
from core.events import event_bus
from core.security import hash_password
from core.settings_service import set_setting
from apps.auth.models import User
from apps.catalog.models import Category, Product
from apps.catalog.projector import rebuild_all
CATEGORIES = [
{"slug": "oberteile", "name": {"de": "Oberteile", "en": "Tops"}, "sort_order": 1},
{"slug": "hosen", "name": {"de": "Hosen", "en": "Pants"}, "sort_order": 2},
{"slug": "schuhe", "name": {"de": "Schuhe", "en": "Shoes"}, "sort_order": 3},
{"slug": "accessoires", "name": {"de": "Accessoires", "en": "Accessories"}, "sort_order": 4},
]
def _placeholder(color_hex: str, label: str) -> str:
# Simple SVG data URL — zero external dependencies
from urllib.parse import quote
svg = (
f"<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'>"
f"<rect width='400' height='400' fill='#{color_hex}'/>"
f"<text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' "
f"font-family='Arial' font-size='26' fill='#ffffff'>{label}</text>"
f"</svg>"
)
return "data:image/svg+xml;utf8," + quote(svg)
PRODUCTS = [
{
"sku": "PULLI-GRUEN-M",
"name": {"de": "Grüner Kuschelpulli", "en": "Green Cozy Sweater"},
"description": {
"de": "Weicher grüner Pullover aus Bio-Baumwolle, ideal für kühle Tage.",
"en": "Soft green sweater made of organic cotton, perfect for chilly days.",
},
"price": 49.90,
"stock": 20,
"cat": "oberteile",
"color": "green",
"img": ("2e7d32", "Grüner Pulli"),
},
{
"sku": "PULLI-BLAU-L",
"name": {"de": "Blauer Rundhalspullover", "en": "Blue Crewneck Sweater"},
"description": {
"de": "Klassischer blauer Pullover mit Rundhalsausschnitt.",
"en": "Classic blue crewneck sweater.",
},
"price": 42.00,
"stock": 15,
"cat": "oberteile",
"color": "blue",
"img": ("1565c0", "Blauer Pulli"),
},
{
"sku": "TSHIRT-WHITE-M",
"name": {"de": "Weißes Basic T-Shirt", "en": "White Basic T-Shirt"},
"description": {
"de": "Leichtes Basic-Shirt aus 100% Bio-Baumwolle.",
"en": "Light basic shirt made of 100% organic cotton.",
},
"price": 14.90,
"stock": 80,
"cat": "oberteile",
"color": "white",
"img": ("f5f5f5", "Weißes Shirt"),
},
{
"sku": "TSHIRT-BLACK-L",
"name": {"de": "Schwarzes T-Shirt", "en": "Black T-Shirt"},
"description": {
"de": "Klassisches schwarzes Shirt, regular fit.",
"en": "Classic black shirt, regular fit.",
},
"price": 15.90,
"stock": 80,
"cat": "oberteile",
"color": "black",
"img": ("212121", "Schwarzes Shirt"),
},
{
"sku": "JACKE-OUTDOOR-M",
"name": {"de": "Warme Outdoor-Jacke", "en": "Warm Outdoor Jacket"},
"description": {
"de": "Wasserabweisende, wärmende Jacke zum Wandern und Radeln im Herbst.",
"en": "Water-repellent, warm jacket for hiking and cycling in autumn.",
},
"price": 129.00,
"stock": 10,
"cat": "oberteile",
"color": "olive",
"img": ("556b2f", "Outdoor-Jacke"),
},
{
"sku": "JEANS-BLAU-32",
"name": {"de": "Blaue Jeans Straight", "en": "Blue Straight Jeans"},
"description": {
"de": "Gerade geschnittene klassische Jeans in Dunkelblau.",
"en": "Straight-cut classic jeans in dark blue.",
},
"price": 69.00,
"stock": 40,
"cat": "hosen",
"color": "blue",
"img": ("0d47a1", "Blaue Jeans"),
},
{
"sku": "JEANS-SCHWARZ-34",
"name": {"de": "Schwarze Slim Jeans", "en": "Black Slim Jeans"},
"description": {
"de": "Slim-Fit-Jeans in tiefem Schwarz, leicht elastisch.",
"en": "Slim-fit jeans in deep black, slightly stretchy.",
},
"price": 74.00,
"stock": 25,
"cat": "hosen",
"color": "black",
"img": ("1b1b1b", "Schwarze Jeans"),
},
{
"sku": "HOSE-WANDER-L",
"name": {"de": "Wanderhose robust", "en": "Robust Hiking Pants"},
"description": {
"de": "Robuste Wanderhose für alle Jahreszeiten, wasserabweisend.",
"en": "Robust hiking pants for all seasons, water-repellent.",
},
"price": 89.00,
"stock": 18,
"cat": "hosen",
"color": "khaki",
"img": ("6b8e23", "Wanderhose"),
},
{
"sku": "SNEAKER-WEISS-42",
"name": {"de": "Weiße Sneaker", "en": "White Sneakers"},
"description": {
"de": "Zeitlose weiße Sneaker für den Alltag.",
"en": "Timeless white sneakers for everyday use.",
},
"price": 79.00,
"stock": 30,
"cat": "schuhe",
"color": "white",
"img": ("eeeeee", "Weiße Sneaker"),
},
{
"sku": "WANDERSCHUH-43",
"name": {"de": "Wanderschuhe warm", "en": "Warm Hiking Boots"},
"description": {
"de": "Warme Wanderschuhe mit guter Dämpfung und rutschfester Sohle.",
"en": "Warm hiking boots with great cushioning and slip-resistant sole.",
},
"price": 149.00,
"stock": 12,
"cat": "schuhe",
"color": "brown",
"img": ("6d4c41", "Wanderschuhe"),
},
{
"sku": "MUETZE-WOLLE",
"name": {"de": "Wollmütze grün", "en": "Green Wool Beanie"},
"description": {
"de": "Warme grüne Wollmütze für kalte Tage.",
"en": "Warm green wool beanie for cold days.",
},
"price": 19.90,
"stock": 50,
"cat": "accessoires",
"color": "green",
"img": ("388e3c", "Mütze"),
},
{
"sku": "SCHAL-GRAU",
"name": {"de": "Grauer Schal", "en": "Grey Scarf"},
"description": {
"de": "Weicher grauer Schal aus Merinowolle.",
"en": "Soft grey scarf made of merino wool.",
},
"price": 29.00,
"stock": 35,
"cat": "accessoires",
"color": "grey",
"img": ("757575", "Grauer Schal"),
},
]
def seed() -> None:
db = SessionLocal()
try:
# Shop name setting
if not db.query(User).filter_by(email="admin@example.com").first():
set_setting(db, "core.shop_name", "Demo Shop")
# Users
if not db.query(User).filter_by(email="admin@example.com").first():
admin = User(
email="admin@example.com",
password_hash=hash_password("admin123"),
role="admin",
name="Admin",
locale="de",
)
db.add(admin)
if not db.query(User).filter_by(email="kunde@example.com").first():
customer = User(
email="kunde@example.com",
password_hash=hash_password("kunde123"),
role="customer",
name="Demo Kunde",
locale="de",
)
db.add(customer)
db.commit()
# Categories
cats_by_slug: dict[str, Category] = {}
for c in CATEGORIES:
row = db.query(Category).filter_by(slug=c["slug"]).first()
if not row:
row = Category(slug=c["slug"], name=c["name"], sort_order=c["sort_order"])
db.add(row)
db.commit()
db.refresh(row)
event_bus.publish("category.created", {"id": row.id}, db=db)
cats_by_slug[c["slug"]] = row
# Products
for pd in PRODUCTS:
if db.query(Product).filter_by(sku=pd["sku"]).first():
continue
image = _placeholder(*pd["img"])
p = Product(
sku=pd["sku"],
name=pd["name"],
description=pd["description"],
price=pd["price"],
currency="EUR",
stock=pd["stock"],
active=True,
image_url=image,
category_id=cats_by_slug[pd["cat"]].id,
attributes={"color": pd["color"]},
)
db.add(p)
db.commit()
db.refresh(p)
event_bus.publish("product.created", {"id": p.id, "sku": p.sku}, db=db)
# Rebuild Redis cache (idempotent)
rebuild_all(db)
print("Seed complete.")
finally:
db.close()
if __name__ == "__main__":
seed()