# 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) ```bash podman build -t pokedexter-backend:latest -f Dockerfile_backend . podman build -t pokedexter-nginx:latest -f Dockerfile_nginx . ``` ### Byg fra Gitea (Portainer) I Portainer under **Image → Build from repository**: | Felt | Værdi | |--------------------|------------------------------------------| | Repository URL | `https://git.itedb.dk/glennigen/pokedexter.git` | | Dockerfile path | `Dockerfile_backend` (eller `Dockerfile_nginx`) | ### Start lokalt ```bash podman-compose up -d # podman docker compose up -d # docker ``` ### Stop ```bash 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 På **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 På **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) På **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 ├── Dockerfile_backend ├── Dockerfile_nginx ├── README.md ├── backend/ │ ├── 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 ```bash # Byg nyt image podman build -t pokedexter-backend:latest -f 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 `` — 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 (`ar` → `Arceus`, `pika` → `Pikachu`) - [ ] **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) - [ ] **Billeder forsvinder efter genstart** — billeder gemt i mountet volume forsvinder alligevel efter container-genstart. Måske permission issue eller forkert mount-path. Skal fejlfindes. - [ ] Personlig mappe — brugere kan markere kort de ejer/samler - [ ] Eksport af samling til CSV