Pokedexter

Pokémon TCG-samlerwebapp — bygget med FastAPI, PostgreSQL 16, Nginx og Podman/Docker.

Administratorer opretter serier, sæt og kort. Brugere kan logge ind, browse og søge. Ingen åben registrering.


Arkitektur

Browser ──► Nginx (:8085) ──► FastAPI (:8000) ──► PostgreSQL (:5432)
                ▲                   │
                └─── static/ ───────┘
  • Nginx — reverse proxy, serverer statiske filer på port 8085
  • FastAPI — async Python backend, Jinja2 templates
  • PostgreSQL 16 — database med SQLAlchemy (async/asyncpg)

Kom i gang

Billeder bygges lokalt først, deploy sker via Portainer.

Byg lokalt (podman)

podman build -t pokedexter-backend:latest -f backend/Dockerfile backend/
podman build -t pokedexter-nginx:latest -f nginx/Dockerfile nginx/

Start lokalt

podman-compose up -d        # podman
docker compose up -d        # docker

Stop

podman-compose down
docker compose down

Data i volumes (pokedexter_pgdata, pokedexter_images) bevares mellem genstarter.


Login

Brugernavn Adgangskode Rolle
admin admin123 Admin

Brugeren oprettes automatisk ved første opstart. Skift adgangskoden efter første login.


Sådan bruges det

Opret en serie

  1. Log ind på /login med admin / admin123
  2. Gå til Admin → Serier
  3. Indtast navn (f.eks. Sword & Shield) og evt. beskrivelse
  4. Tryk Opret

Opret sæt under en serie

  1. Gå til Admin → Serier
  2. Klik på serienavnet (link)
  3. Indtast kode (f.eks. EVS), navn (Evolving Skies) og evt. release_date
  4. Tryk Opret sæt

Eller bulk import: indsæt CSV direkte i tekstfeltet:

Stellar Crown,SCR,Stellar Crown,2024-09-13,
Surging Sparks,SSP,Surging Sparks,2024-11-08,

Tilføj kort

  1. Gå til Admin → Sæt
  2. Klik Vis kort på det ønskede sæt
  3. Udfyld nummer, navn og evt. image URL
  4. Tryk Opret kort

Eller bulk import via CSV:

SCR,001,Caterpie,https://images.pokemontcg.io/scarlet_ivy/1.png
SCR,002,Metapod,

Kortet får automatisk koden SCR-001.

Billedhåndtering

Når en URL angives ved kortoprettelse:

  • Billedet downloades automatisk og gemmes lokalt i volume'et
  • Hvis download fejler, bevares URL'en som image_path og et ⚠-badge vises
  • Card.image_url returnerer aldrig eksterne URLs — ingen streaming

Synkronisér billeder

Admin → Sæt → Vis kort findes en Synkronisér billeder-knap, der downloader alle eksterne billeder for sættet til lokal lagring.

Verificér & genopret

Admin-dashboardet findes:

  • Verificér & genopret — tjekker om alle lokale billedfiler findes på disken. Manglende filer re-downloades fra source_url
  • Force-genhent alle — genhenter ALLE billeder fra deres originale kilder (uanset om de findes)

Dette er nyttigt hvis volume'et afmonteres ved en fejl, eller billeder tabes ved genstart.

Opret brugere (admin)

Admin-dashboardet under "Brugere" kan du oprette og slette brugere. Du kan ikke slette dig selv.

Browse kort

  • /sets — liste alle serier med sæt
  • /sets/{code} — se alle kort i sæt EVS
  • /card/{full_code} — detaljer om et specifikt kort

Søg

  • /search?q=SCR-001 — søg på fuld kortkode
  • /search?q=pikachu — søg på kortnavn

API-oversigt

Offentlige endpoints

Metode Sti Beskrivelse
GET / Forside
GET /sets Liste alle serier
GET /sets/{code} Kort i et sæt
GET /search Søgeside
GET /api/search Søg (JSON)
GET /api/cards/{full_code} Enkelt kort (JSON)

Auth endpoints

Metode Sti Beskrivelse
GET /login Loginside
POST /api/login Login (sætter cookie)
GET /logout Logout

Admin endpoints (kræver admin)

Metode Sti Beskrivelse
GET /admin Admin dashboard
GET /admin/series Administrér serier
GET /admin/series/{name} Serie-detail med sæt
GET /admin/sets Administrér sæt
GET /admin/sets/{code} Administrér kort i sæt
POST /api/admin/users Opret bruger
DELETE /api/admin/users/{id} Slet bruger (ikke dig selv)
POST /api/admin/series Opret serie
POST /api/admin/series/csv Bulk import af serier (CSV)
DELETE /api/admin/series/{id} Slet serie
POST /api/admin/series/{name}/sets Opret sæt under serie
POST /api/admin/series/{name}/sets/csv Bulk import af sæt (CSV)
POST /api/admin/sets/{code}/cards Opret kort
POST /api/admin/sets/{code}/cards/csv Bulk import af kort (CSV)
DELETE /api/admin/cards/{id} Slet kort
POST /api/admin/cards/{id}/upload Upload billede (fil)
POST /api/admin/sets/{code}/sync-images Synkronisér billeder for sæt
POST /api/admin/sync-all-images Synkronisér alle billeder
POST /api/admin/verify-images Verificér og genopret billeder
POST /api/admin/verify-images?force=true Force-genhent alle billeder

Datamodel

User
  id              Integer (PK)
  username        String (unique)
  password_hash   String (bcrypt)
  role            String ("admin" | "user")
  created_at      DateTime

Serie
  id              Integer (PK)
  name            String (unique) — "Sword & Shield"
  description     Text (optional)
  created_at      DateTime
  sets            ► Set[]

Set
  id              Integer (PK)
  serie_id        Integer (FK → series.id)
  code            String (unique) — "EVS"
  name            String — "Evolving Skies"
  release_date    String (optional)
  description     Text (optional)
  created_at      DateTime
  serie           ► Serie
  cards           ► Card[]

Card
  id              Integer (PK)
  set_id          Integer (FK → sets.id)
  number          String — "050"
  name            String — "Raichu"
  image_path      String (optional) — lokal sti eller URL
  source_url      String (optional) — original ekstern URL til re-sync
  set             ► Set
  full_code       [property] — "EVS-050"
  image_url       [property] — "/static/..." eller None (ingen eksterne URLs)

Miljøvariable

Variabel Standard Beskrivelse
DATABASE_URL postgresql+asyncpg://... PostgreSQL forbindelse
SECRET_KEY skift-mig-til-en-sikker-noegle JWT signeringsnøgle

Projektstruktur

pokedexter/
├── docker-compose.yml
├── start.sh                  # podman start
├── stop.sh                   # podman stop
├── README.md
├── backend/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app/
│       ├── main.py           # FastAPI app, middleware
│       ├── database.py       # async engine + session
│       ├── models.py         # SQLAlchemy modeller
│       ├── auth.py           # bcrypt + JWT
│       ├── static/
│       │   └── images/       # uploadet/downloadede billeder (volume)
│       ├── templates/        # Jinja2 HTML templates
│       │   ├── base.html     # layout m. dark mode toggle
│       │   └── admin/        # admin UI templates
│       └── routers/
│           ├── auth.py       # login/logout + admin user mgmt
│           ├── admin.py      # admin CRUD for series/sets/cards
│           └── cards.py      # browse + search
├── nginx/
│   ├── Dockerfile
│   └── nginx.conf
└── db/
    └── init.sql

Udvikling

Genstart backend efter kodeændringer

# Byg nyt image
podman build -t pokedexter-backend:latest -f backend/Dockerfile backend/

# Genstart via Portainer, eller:
podman stop pokedexter-backend && podman rm pokedexter-backend
podman-compose up -d backend

Dark mode

Dark mode gemmes i localStorage og sættes via render-blokerende script i <head> — ingen flash ved side-skift.

Billeder og volumes

Billeder gemmes i volume pokedexter_images mountet på /app/static/images.
Volume'et skal være mountet før første hentning — ellers går billeder tabt ved container-genstart.

Hvis billeder mangler: brug Verificér & genopret på admin-dashboardet.


Køreplan / To-do

  • Søgning optimeres — PostgreSQL full-text search (tsvector/tsquery) eller trigram similarity (pg_trgm). Nuværende LIKE-søgning rammer ikke forkortelser, delvise navne eller stavefejl (arArceus, pikaPikachu)
  • Opdater billede på kort — admin skal kunne ændre source-billed-URL på et eksisterende kort, hvorefter billedet re-downloades og lokale sti opdateres
  • Galleri/bladring i sæt — vis kortbilleder i et sæt som et galleri/carousel, så man kan bladre frem/tilbage uden at gå til listen
  • Internal Docker-netværk — backend og DB skal isoleres på et internt netværk, så de ikke er tilgængelige udefra
  • Nginx-proxy mod NPM — tilføj proxy-konfiguration så containeren kan snakke med Nginx Proxy Manager (eller anden reverse proxy-container)
  • Personlig mappe — brugere kan markere kort de ejer/samler
  • Eksport af samling til CSV
S
Description
No description provided
Readme 134 KiB
Languages
Python 51.8%
HTML 48.2%