Compare commits
7 Commits
6a86c56e3b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 61a7f57f1f | |||
| 03f07ebee5 | |||
| 972137acfb | |||
| fd23d89e71 | |||
| d9d547ef53 | |||
| 349a0cb821 | |||
| ad4ac6cf8c |
@@ -5,7 +5,7 @@ status: todo
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:50Z
|
||||
updated_at: 2026-02-09T15:31:15Z
|
||||
updated_at: 2026-02-09T16:53:13Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
blocking:
|
||||
- nuzlocke-tracker-vpn5
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-ahza
|
||||
title: Deployment Strategy
|
||||
status: todo
|
||||
status: in-progress
|
||||
type: epic
|
||||
priority: normal
|
||||
created_at: 2026-02-09T14:03:53Z
|
||||
updated_at: 2026-02-09T15:08:29Z
|
||||
updated_at: 2026-02-09T16:53:58Z
|
||||
---
|
||||
|
||||
Define and implement a deployment strategy for running the nuzlocke-tracker in production on a local Unraid server while keeping laptop/PC as the development environment.
|
||||
@@ -20,14 +20,14 @@ Define and implement a deployment strategy for running the nuzlocke-tracker in p
|
||||
|
||||
## Decided Approach
|
||||
|
||||
**Docker Compose + Portainer + Local Docker Registry**
|
||||
**Docker Compose + Portainer + Gitea (source hosting, container registry, CI/CD)**
|
||||
|
||||
1. **A local Docker registry** runs on Unraid as a container, accessible on the LAN (e.g., `unraid:5000` or behind Nginx Proxy Manager).
|
||||
2. **Images are built on the dev machine** and pushed to the local registry.
|
||||
3. **Production runs docker-compose** on Unraid, pulling images from the local registry instead of mounting source.
|
||||
1. **Gitea** runs on Unraid behind Nginx Proxy Manager with SSL (e.g., `gitea.nerdboden.de`). It serves as the self-hosted Git remote, container registry, and (optionally) CI/CD via Gitea Actions.
|
||||
2. **Images are built on the dev machine** and pushed to Gitea's container registry as **user-level packages** (e.g., `gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest`, `gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest`).
|
||||
3. **Production runs docker-compose** on Unraid, pulling images from the Gitea container registry instead of mounting source.
|
||||
4. **Portainer** is installed on Unraid to manage stacks, provide a web UI, and enable webhook-triggered redeployments.
|
||||
5. **A deploy script** on the dev machine automates the full flow: build images → push to local registry → trigger Portainer webhook to redeploy.
|
||||
6. **Nginx Proxy Manager** handles routing on the LAN (e.g., `nuzlocke.local` → frontend container).
|
||||
5. **A deploy script** on the dev machine automates the full flow: build images → push to Gitea registry → trigger Portainer webhook to redeploy.
|
||||
6. **Nginx Proxy Manager** handles routing on the LAN (e.g., `nuzlocke.nerdboden.de` → frontend container, `gitea.nerdboden.de` → Gitea).
|
||||
7. **Database** uses a named Docker volume for persistence; migrations run automatically on API container startup.
|
||||
|
||||
## Branching Strategy
|
||||
@@ -42,19 +42,19 @@ Define and implement a deployment strategy for running the nuzlocke-tracker in p
|
||||
1. Create `feature/xyz` from `develop`
|
||||
2. Work on the feature, commit, merge into `develop`
|
||||
3. When ready to deploy: merge `develop` → `main`
|
||||
4. Run `./deploy.sh` (builds from `main`, pushes to local registry, triggers Portainer webhook)
|
||||
4. Run `./deploy.sh` (builds from `main`, pushes to Gitea registry, triggers Portainer webhook)
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] **Set up branching structure** — create `develop` branch from `main`, establish the `main`/`develop`/`feature/*` workflow
|
||||
- [ ] **Update CLAUDE.md with branching rules** — once the branching structure is in place, add instructions to CLAUDE.md that the branching strategy must be adhered to (always work on feature branches, never commit directly to `main`, merge flow is `feature/*` → `develop` → `main`)
|
||||
- [ ] **Set up local Docker registry on Unraid** — run the `registry:2` container, configure storage volume, optionally put it behind Nginx Proxy Manager with a hostname (e.g., `registry.local`)
|
||||
- [ ] **Create production docker-compose file** (`docker-compose.prod.yml`) — uses images from the local registry, production env vars, no source volume mounts, proper restart policies
|
||||
- [ ] **Create production Dockerfiles (or multi-stage builds)** — ensure frontend is built and served statically (e.g., via the API or a lightweight nginx container), API runs without debug mode
|
||||
- [ ] **Set up Portainer on Unraid** — install Portainer CE as a Docker container, configure the stack from the production compose file
|
||||
- [ ] **Configure Portainer webhook for automated redeployment** — add a webhook trigger in Portainer that pulls latest images and restarts the stack
|
||||
- [ ] **Create deploy script** — a script (e.g., `./deploy.sh`) that builds images from `main`, tags them for the local registry, pushes them, and triggers the Portainer webhook to redeploy
|
||||
- [ ] **Configure Nginx Proxy Manager** — add proxy host entry pointing to the frontend/API containers on the appropriate ports
|
||||
- [ ] **Configure Gitea container registry** — create an access token with `read:package` and `write:package` scopes, verify `docker login gitea.nerdboden.de` works, test pushing and pulling an image as a user-level package
|
||||
- [x] **Create production docker-compose file** (`docker-compose.prod.yml`) — uses images from the Gitea container registry, production env vars, no source volume mounts, proper restart policies
|
||||
- [x] **Create production Dockerfiles (or multi-stage builds)** — ensure frontend is built and served statically (e.g., via the API or a lightweight nginx container), API runs without debug mode
|
||||
- [x] **Set up Portainer on Unraid** — install Portainer CE as a Docker container, configure the stack from the production compose file
|
||||
- [x] **Configure Portainer API for automated redeployment** — deploy script uses Portainer CE REST API to pull latest images and restart the stack
|
||||
- [x] **Create deploy script** — `./deploy.sh` builds images, pushes to Gitea registry, triggers Portainer API redeployment
|
||||
- [ ] **Configure Nginx Proxy Manager** — add proxy host entries for Gitea and the nuzlocke-tracker frontend/API on the appropriate ports
|
||||
- [ ] **Environment & secrets management** — create a `.env.prod` template, document required variables, decide on secret handling (`.env` file on Unraid, Portainer env vars, etc.)
|
||||
- [ ] **Database backup strategy** — set up a simple scheduled backup for the PostgreSQL volume/data (e.g., cron + `pg_dump` script on Unraid)
|
||||
- [ ] **Document the deployment workflow** — README or docs covering how to deploy, redeploy, rollback, and manage the production instance
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-aiw6
|
||||
title: Create deploy script
|
||||
status: todo
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:48Z
|
||||
updated_at: 2026-02-09T15:31:15Z
|
||||
updated_at: 2026-02-09T17:28:22Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
blocking:
|
||||
- nuzlocke-tracker-izf6
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-hwyk
|
||||
title: Set up Portainer on Unraid
|
||||
status: todo
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:44Z
|
||||
updated_at: 2026-02-09T15:30:44Z
|
||||
updated_at: 2026-02-09T16:53:41Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
---
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
---
|
||||
# nuzlocke-tracker-izf6
|
||||
title: Set up local Docker registry on Unraid
|
||||
status: todo
|
||||
title: Configure Gitea container registry
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:40Z
|
||||
updated_at: 2026-02-09T15:30:40Z
|
||||
updated_at: 2026-02-09T16:53:09Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
---
|
||||
|
||||
Run a `registry:2` container on the Unraid server for storing production Docker images locally.
|
||||
Set up and verify the Gitea container registry for hosting Docker images as user-level packages.
|
||||
|
||||
- Run `registry:2` as a Docker container on Unraid
|
||||
- Configure a persistent storage volume for the registry data
|
||||
- Optionally put it behind Nginx Proxy Manager with a hostname (e.g., `registry.local`)
|
||||
- Verify pushing/pulling images from the dev machine works
|
||||
## Checklist
|
||||
|
||||
- [ ] Create a Gitea access token with `read:package` and `write:package` scopes
|
||||
- [ ] Verify `docker login gitea.nerdboden.de` works from the dev machine
|
||||
- [ ] Test pushing a Docker image as a user-level package (e.g., `gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest`)
|
||||
- [ ] Verify the image appears under the user's Packages tab in Gitea
|
||||
- [ ] Test pulling the image back (from Unraid or dev machine)
|
||||
@@ -1,18 +1,28 @@
|
||||
---
|
||||
# nuzlocke-tracker-jzqz
|
||||
title: Configure Portainer webhook for automated redeployment
|
||||
status: todo
|
||||
title: Configure Portainer API for automated redeployment
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:45Z
|
||||
updated_at: 2026-02-09T15:31:15Z
|
||||
updated_at: 2026-02-09T17:28:22Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
blocking:
|
||||
- nuzlocke-tracker-hwyk
|
||||
---
|
||||
|
||||
Set up a webhook in Portainer that triggers a stack redeployment when called.
|
||||
Use the Portainer CE REST API to trigger stack redeployments from the deploy script.
|
||||
|
||||
- Create a webhook trigger in Portainer for the nuzlocke-tracker stack
|
||||
- The webhook should pull the latest images from the local registry and restart the stack
|
||||
- Note the webhook URL for use in the deploy script
|
||||
Portainer webhooks are a Business-only feature, so we use the API directly instead.
|
||||
|
||||
## Approach
|
||||
|
||||
1. Authenticate with the Portainer API to get a JWT token
|
||||
2. Call the stack update endpoint with `pullImage: true` to pull latest images and recreate containers
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Identify the stack ID in Portainer (via API or UI)
|
||||
- [ ] Test API authentication (`POST /api/auth`)
|
||||
- [ ] Test triggering a stack redeploy via API
|
||||
- [ ] Integrate into the deploy script
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-re0m
|
||||
title: Document the deployment workflow
|
||||
status: todo
|
||||
status: in-progress
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:57Z
|
||||
updated_at: 2026-02-09T15:31:15Z
|
||||
updated_at: 2026-02-09T16:55:02Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
blocking:
|
||||
- nuzlocke-tracker-aiw6
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-vpn5
|
||||
title: Create production docker-compose file
|
||||
status: todo
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:41Z
|
||||
updated_at: 2026-02-09T15:30:41Z
|
||||
updated_at: 2026-02-09T16:59:00Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
---
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
---
|
||||
# nuzlocke-tracker-xmyh
|
||||
title: Create production Dockerfiles
|
||||
status: todo
|
||||
status: completed
|
||||
type: task
|
||||
priority: normal
|
||||
created_at: 2026-02-09T15:30:42Z
|
||||
updated_at: 2026-02-09T15:30:42Z
|
||||
updated_at: 2026-02-09T17:00:32Z
|
||||
parent: nuzlocke-tracker-ahza
|
||||
---
|
||||
|
||||
|
||||
152
DEPLOYMENT.md
Normal file
152
DEPLOYMENT.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Deployment
|
||||
|
||||
This document describes the deployment architecture and workflows for the nuzlocke-tracker. It is a living document — sections marked with **TODO** are planned but not yet implemented.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
| Component | Dev (Laptop/PC) | Production (Unraid) |
|
||||
|---|---|---|
|
||||
| API | `docker-compose.yml` (hot reload) | `docker-compose.prod.yml` (built image) |
|
||||
| Frontend | `docker-compose.yml` (Vite dev server) | `docker-compose.prod.yml` (built image) |
|
||||
| Database | PostgreSQL 16 (Docker volume) | PostgreSQL 16 (Docker volume) |
|
||||
| Container Registry | — | Gitea (user-level packages) |
|
||||
| Container Management | — | Portainer CE |
|
||||
| Reverse Proxy | — | Nginx Proxy Manager |
|
||||
|
||||
### Services
|
||||
|
||||
- **Gitea** — self-hosted Git server, container registry, and (future) CI/CD. Accessible at `gitea.nerdboden.de` via SSL.
|
||||
- **Portainer** — Docker management UI. Accessible at `portainer.nerdboden.de` via SSL. Manages the production stack and provides webhook-triggered redeployments.
|
||||
- **Nginx Proxy Manager** — reverse proxy with SSL termination for all services on the Unraid server.
|
||||
|
||||
## Container Registry
|
||||
|
||||
Docker images are hosted on Gitea's built-in container registry as **user-level packages**.
|
||||
|
||||
### Image naming
|
||||
|
||||
Images use the format `gitea.nerdboden.de/<user>/<image>:<tag>`:
|
||||
|
||||
```
|
||||
gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
1. Create a Gitea access token at **Settings > Applications** with `read:package` and `write:package` scopes.
|
||||
2. Log in from the dev machine:
|
||||
```bash
|
||||
docker login gitea.nerdboden.de
|
||||
```
|
||||
Use your Gitea username and the access token as password.
|
||||
|
||||
### Pushing images
|
||||
|
||||
```bash
|
||||
# Build and tag
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest ./backend
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest ./frontend
|
||||
|
||||
# Push
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
```
|
||||
|
||||
Pushed images are visible under the **Packages** tab on your Gitea user profile.
|
||||
|
||||
## Branching Strategy
|
||||
|
||||
The project uses a `main` / `develop` / `feature/*` branching model.
|
||||
|
||||
| Branch | Purpose |
|
||||
|---|---|
|
||||
| `main` | Always production-ready. Deploy script builds from here. |
|
||||
| `develop` | Integration branch for day-to-day work. |
|
||||
| `feature/*` | Short-lived branches off `develop` for individual features/fixes. |
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Create `feature/xyz` from `develop`
|
||||
2. Work on the feature, commit, merge into `develop`
|
||||
3. When ready to deploy: merge `develop` into `main`
|
||||
4. Run the deploy script (see below)
|
||||
|
||||
## Deploying
|
||||
|
||||
> **TODO** — deploy script (`./deploy.sh`) not yet created.
|
||||
|
||||
The deploy script will automate:
|
||||
|
||||
1. Build Docker images from `main`
|
||||
2. Tag and push to the Gitea container registry
|
||||
3. Trigger the Portainer webhook to pull new images and restart the stack
|
||||
|
||||
### Manual deployment
|
||||
|
||||
Until the deploy script is in place, deploy manually:
|
||||
|
||||
```bash
|
||||
# 1. Ensure you're on main with latest changes
|
||||
git checkout main
|
||||
|
||||
# 2. Build and push images
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest ./backend
|
||||
docker build -t gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest ./frontend
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
docker push gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
|
||||
# 3. On Unraid (or via Portainer): pull and restart
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
## Production Compose
|
||||
|
||||
> **TODO** — `docker-compose.prod.yml` not yet created.
|
||||
|
||||
The production compose file will differ from the dev compose in:
|
||||
|
||||
- Uses pre-built images from the Gitea registry (no source volume mounts)
|
||||
- No hot reload / debug mode
|
||||
- Production environment variables
|
||||
- Proper restart policies
|
||||
- Frontend served as a static build (not Vite dev server)
|
||||
|
||||
## Portainer
|
||||
|
||||
Portainer CE is running on Unraid at `portainer.nerdboden.de`.
|
||||
|
||||
- Manages the production Docker stack
|
||||
- **TODO**: Configure a webhook for automated redeployment (pull latest images + restart on trigger)
|
||||
|
||||
## Nginx Proxy Manager
|
||||
|
||||
NPM runs on Unraid and handles SSL termination and routing for:
|
||||
|
||||
- `gitea.nerdboden.de` → Gitea
|
||||
- `portainer.nerdboden.de` → Portainer
|
||||
- **TODO**: `nuzlocke.nerdboden.de` (or similar) → nuzlocke-tracker frontend/API
|
||||
|
||||
## Environment & Secrets
|
||||
|
||||
> **TODO** — `.env.prod` template not yet created.
|
||||
|
||||
Production environment variables to configure:
|
||||
|
||||
- `DATABASE_URL` — PostgreSQL connection string
|
||||
- `DEBUG` — must be `false` in production
|
||||
- Additional secrets TBD
|
||||
|
||||
## Database
|
||||
|
||||
PostgreSQL 16 with data stored in a named Docker volume.
|
||||
|
||||
- Migrations run automatically on API container startup (Alembic)
|
||||
- **TODO**: Set up scheduled backups (`pg_dump` cron job on Unraid)
|
||||
|
||||
## Rollback
|
||||
|
||||
> **TODO** — rollback procedure to be documented once image tagging strategy is finalized.
|
||||
|
||||
General approach: tag images with version/commit hash in addition to `latest`, so rolling back means redeploying a previous tag.
|
||||
19
backend/Dockerfile.prod
Normal file
19
backend/Dockerfile.prod
Normal file
@@ -0,0 +1,19 @@
|
||||
# Production Dockerfile for the backend API
|
||||
FROM python:3.14-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies
|
||||
COPY pyproject.toml README.md alembic.ini ./
|
||||
COPY src/ ./src/
|
||||
|
||||
RUN pip install --no-cache-dir .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--app-dir", "src"]
|
||||
81
deploy.sh
Executable file
81
deploy.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ──────────────────────────────────────────────
|
||||
REGISTRY="gitea.nerdboden.de"
|
||||
OWNER="thefurya"
|
||||
IMAGES=("nuzlocke-tracker-api" "nuzlocke-tracker-frontend")
|
||||
DOCKERFILES=("backend/Dockerfile.prod" "frontend/Dockerfile.prod")
|
||||
CONTEXTS=("./backend" "./frontend")
|
||||
|
||||
PORTAINER_URL="${PORTAINER_URL:-https://portainer.nerdboden.de}"
|
||||
PORTAINER_API_KEY="${PORTAINER_API_KEY:-}"
|
||||
PORTAINER_STACK_ID="${PORTAINER_STACK_ID:-}"
|
||||
PORTAINER_ENDPOINT_ID="${PORTAINER_ENDPOINT_ID:-1}"
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
||||
|
||||
# ── Preflight checks ──────────────────────────────────────────
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
if [[ "$BRANCH" != "main" ]]; then
|
||||
warn "You are on branch '$BRANCH', not 'main'."
|
||||
read -rp "Continue anyway? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || exit 0
|
||||
fi
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
warn "You have uncommitted changes."
|
||||
read -rp "Continue anyway? [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || exit 0
|
||||
fi
|
||||
|
||||
# ── Build and push images ─────────────────────────────────────
|
||||
for i in "${!IMAGES[@]}"; do
|
||||
IMAGE="${REGISTRY}/${OWNER}/${IMAGES[$i]}:latest"
|
||||
info "Building ${IMAGES[$i]}..."
|
||||
docker build -t "$IMAGE" -f "${DOCKERFILES[$i]}" "${CONTEXTS[$i]}"
|
||||
info "Pushing ${IMAGES[$i]}..."
|
||||
docker push "$IMAGE"
|
||||
done
|
||||
|
||||
info "All images built and pushed."
|
||||
|
||||
# ── Trigger Portainer redeployment ─────────────────────────────
|
||||
if [[ -z "$PORTAINER_API_KEY" ]]; then
|
||||
warn "PORTAINER_API_KEY not set — skipping Portainer redeployment."
|
||||
warn "Set it in your environment or .env.deploy file to enable auto-redeploy."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "$PORTAINER_STACK_ID" ]]; then
|
||||
warn "PORTAINER_STACK_ID not set — skipping Portainer redeployment."
|
||||
warn "Find your stack ID in Portainer and set it in your environment."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info "Fetching stack file from Portainer..."
|
||||
STACK_FILE=$(curl -sf \
|
||||
-H "X-API-Key: ${PORTAINER_API_KEY}" \
|
||||
"${PORTAINER_URL}/api/stacks/${PORTAINER_STACK_ID}/file") \
|
||||
|| error "Failed to fetch stack file from Portainer."
|
||||
|
||||
STACK_CONTENT=$(echo "$STACK_FILE" | jq -r '.StackFileContent')
|
||||
|
||||
info "Triggering stack redeployment..."
|
||||
curl -sf -X PUT \
|
||||
-H "X-API-Key: ${PORTAINER_API_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg content "$STACK_CONTENT" '{"pullImage": true, "stackFileContent": $content}')" \
|
||||
"${PORTAINER_URL}/api/stacks/${PORTAINER_STACK_ID}?endpointId=${PORTAINER_ENDPOINT_ID}" \
|
||||
> /dev/null \
|
||||
|| error "Failed to trigger Portainer redeployment."
|
||||
|
||||
info "Stack redeployment triggered successfully!"
|
||||
38
docker-compose.prod.yml
Normal file
38
docker-compose.prod.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
services:
|
||||
api:
|
||||
image: gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest
|
||||
command: >
|
||||
sh -c "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000 --app-dir src"
|
||||
environment:
|
||||
- DEBUG=false
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/nuzlocke
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
image: gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest
|
||||
ports:
|
||||
- "9080:80"
|
||||
depends_on:
|
||||
- api
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=nuzlocke
|
||||
volumes:
|
||||
- prod_postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
prod_postgres_data:
|
||||
21
frontend/Dockerfile.prod
Normal file
21
frontend/Dockerfile.prod
Normal file
@@ -0,0 +1,21 @@
|
||||
# Production Dockerfile for the frontend
|
||||
# Stage 1: Build
|
||||
FROM node:24-slim AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Serve
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
20
frontend/nginx.conf
Normal file
20
frontend/nginx.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Proxy API requests to the backend service
|
||||
location /api/ {
|
||||
proxy_pass http://api:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Serve static files, fall back to index.html for SPA routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export function BossBattleFormModal({
|
||||
<label className="block text-sm font-medium mb-1">Type</label>
|
||||
<select
|
||||
value={bossType}
|
||||
onChange={(e) => setBossType(e.target.value)}
|
||||
onChange={(e) => setBossType(e.target.value as typeof bossType)}
|
||||
className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
{BOSS_TYPES.map((t) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { advanceLeg, createGenlocke, getGamesByRegion, getGenlockes, getGenlocke, getGenlockeGraveyard, getGenlockeLineages, getLegSurvivors } from '../api/genlockes'
|
||||
import type { AdvanceLegInput, CreateGenlockeInput } from '../types/game'
|
||||
import type { CreateGenlockeInput } from '../types/game'
|
||||
|
||||
export function useGenlockes() {
|
||||
return useQuery({
|
||||
|
||||
Reference in New Issue
Block a user