summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-03-15 15:46:00 +0900
committerAdam Malczewski <[email protected]>2026-03-15 15:46:00 +0900
commit0254a6936139801ee10fac27cc5032258d13051d (patch)
tree2169ab1a0d7c4979947f999e9e57a8e1be19b299
parente30116153f29ea8b634c31c080130f1a5d6f0d06 (diff)
downloadtirecalc-0254a6936139801ee10fac27cc5032258d13051d.tar.gz
tirecalc-0254a6936139801ee10fac27cc5032258d13051d.zip
update to work with dokployHEADmain
-rw-r--r--.dockerignore5
-rw-r--r--Dockerfile.prod44
-rw-r--r--README.md139
-rw-r--r--docker-compose.prod.yml27
-rw-r--r--nginx.conf29
5 files changed, 234 insertions, 10 deletions
diff --git a/.dockerignore b/.dockerignore
index e06b666..7b46340 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,3 +10,8 @@ node_modules
dist
.git
*.log
+.ask_user.md
+ai_prompt.md
+docker_readme.md
+README.md
+.tool-versions
diff --git a/Dockerfile.prod b/Dockerfile.prod
new file mode 100644
index 0000000..a1a792c
--- /dev/null
+++ b/Dockerfile.prod
@@ -0,0 +1,44 @@
+# --------------------------------------------------------------------------
+# Dockerfile.prod — Production multi-stage build for the Bicycle Wheel
+# Circumference app.
+#
+# Stage 1 (build):
+# • Installs dependencies and runs `npm run build` to produce a static
+# bundle in /app/dist.
+#
+# Stage 2 (production):
+# • Copies the built assets into an Nginx Alpine image.
+# • Uses a custom nginx.conf that handles SPA routing (all paths
+# fall back to index.html).
+# • Serves on port 80 — Traefik/Dokploy will handle TLS termination.
+# --------------------------------------------------------------------------
+
+# ---- Build stage ----
+FROM node:25-alpine AS build
+
+WORKDIR /app
+
+# Install dependencies first (layer caching optimisation)
+COPY package.json package-lock.json ./
+RUN npm ci
+
+# Copy the rest of the source and build
+COPY . .
+RUN npm run build
+
+# ---- Production stage ----
+FROM nginx:stable-alpine AS production
+
+# Remove default Nginx site config
+RUN rm /etc/nginx/conf.d/default.conf
+
+# Add custom Nginx config for SPA routing
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+
+# Copy built assets from the build stage
+COPY --from=build /app/dist /usr/share/nginx/html
+
+EXPOSE 80
+
+# Nginx runs in the foreground by default with the official image
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/README.md b/README.md
index 722df19..b7e8499 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,139 @@
A small learning app (React + TypeScript + Vite) to measure bicycle wheel circumference by rolling the wheel or by entering diameter. This project is a port of the tire size calculator that once lived on the Cateye website but was removed. It was reimplemented here for usefulness and learning.
-## Quick start
+## Tech
-- Build:
- docker compose build
+React · TypeScript · Vite · TailwindCSS · DaisyUI · Docker · Nginx
-- Start (detached):
- docker compose up -d
+---
-- Stop and remove containers/networks:
- docker compose down
+## Development (Docker)
-Served on port 5173 — open http://127.0.0.1:5173 or http://localhost:5173
+```bash
+docker compose build
+docker compose up -d
+```
-## Tech
+Served on port 5173 — open http://localhost:5173
+
+See [docker_readme.md](docker_readme.md) for the full development Docker guide.
+
+---
+
+## Production Static Build (Docker)
+
+The production setup uses a **multi-stage Docker build**:
+
+1. **Build stage** — Installs dependencies and runs `npm run build` to produce static assets in `dist/`.
+2. **Production stage** — Copies the built assets into an Nginx Alpine image that serves them on port 80.
+
+### Build and run locally
+
+```bash
+docker compose -f docker-compose.prod.yml up -d --build
+```
+
+Then open http://localhost:80.
+
+> **Note:** The `ports` mapping in `docker-compose.prod.yml` is commented out by default (intended for Dokploy/Traefik). Uncomment the `ports` line for local testing:
+>
+> ```yaml
+> ports:
+> - "80:80"
+> ```
+
+### Files involved
+
+| File | Purpose |
+| ------------------------- | -------------------------------------------------------- |
+| `Dockerfile.prod` | Multi-stage build: Node (build) → Nginx (serve) |
+| `docker-compose.prod.yml` | Production compose file (single `web` service) |
+| `nginx.conf` | Nginx config with SPA fallback routing and gzip |
+
+---
+
+## Deploying to Dokploy
+
+Dokploy is a self-hosted PaaS that uses Traefik as a reverse proxy. The production compose file is ready for Dokploy out of the box.
+
+### Prerequisites
+
+- A VPS with [Dokploy installed](https://docs.dokploy.com/get-started/installation)
+- A domain name with a DNS **A record** pointing to your server's IP
+- Your GitHub repository connected to Dokploy (see below)
+
+### Step 1 — Connect GitHub to Dokploy
+
+1. Log in to the Dokploy dashboard (`http://<your-server-ip>:3000`).
+2. Go to **Settings → Git Providers**.
+3. Click **"+ Add Git Provider"** and follow the prompts to create a **GitHub App**.
+4. After creation, click **"Install"** next to the provider and grant access to this repository (or all repositories).
+
+### Step 2 — Create a Project
+
+1. Go to **Dashboard → Projects**.
+2. Click **"+ Create Project"**, give it a name (e.g. "Tire Calculator"), and click **"Create"**.
+
+### Step 3 — Create a Compose Service
+
+1. Inside the project, click **"+ Create Service"** → **"Compose"**.
+2. Fill in:
+ - **Name**: e.g. "tirecalc"
+ - **Compose Type**: `docker-compose` (default)
+3. Click **"Create"**.
+
+### Step 4 — Configure the Source
+
+1. On the compose page, set the source to **"GitHub"**.
+2. Select your **GitHub Provider**, **Repository**, and **Branch** (e.g. `main`).
+3. Set **Compose Path** to:
+ ```
+ ./docker-compose.prod.yml
+ ```
+4. Click **"Save"**.
+
+### Step 5 — Add a Domain
+
+1. Go to the **"Domains"** tab.
+2. Click **"+ Add Domain"** and fill in:
+ - **Host**: your domain (e.g. `tirecalc.example.com`)
+ - **Service Name**: `web` (must match the service name in `docker-compose.prod.yml`)
+ - **Port**: `80`
+ - **HTTPS**: toggle **on**
+ - **Certificate Type**: `letsencrypt`
+3. Click **"Save"**.
+
+> Make sure your DNS A record is already pointing to the server. Let's Encrypt needs this to issue a certificate.
+
+### Step 6 — Deploy
+
+1. Click the **"Deploy"** button.
+2. Monitor progress in the **"Deployments"** tab.
+3. Once the status shows **"Done" ✅**, the app is live at your domain.
+
+### Step 7 — Enable Auto-Deploy (optional)
+
+Auto-deploy is enabled by default when using the GitHub App integration. Every push to the configured branch will trigger a new deployment automatically.
+
+If you prefer manual webhook control:
+
+1. Find the **webhook URL** in the compose settings:
+ ```
+ https://<your-dokploy-host>/api/deploy/compose/<refreshToken>
+ ```
+2. In your GitHub repo, go to **Settings → Webhooks → Add webhook**.
+3. Set:
+ - **Payload URL**: the webhook URL above
+ - **Content type**: `application/json`
+ - **Events**: "Just the push event"
+4. Click **"Add webhook"**.
-React - TypeScript - Vite - TailwindCSS - DaisyUI - Docker
+### Troubleshooting
+| Problem | Likely cause | Fix |
+| --- | --- | --- |
+| 502 Bad Gateway | Container not running or wrong port | Check deployment logs; verify domain port is `80` and service name is `web` |
+| Site not loading | DNS not configured | Add an A record pointing your domain to the server IP |
+| SSL error | DNS not propagated yet | Wait a few minutes for propagation; check Traefik logs |
+| Build fails | Missing dependencies or TypeScript errors | Check deployment logs for build output; run `npm run build` locally to reproduce |
+| Stale content after deploy | Browser cache | Hard-refresh (`Ctrl-Shift-R`); Vite hashes asset filenames so this is rare |
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
new file mode 100644
index 0000000..131e865
--- /dev/null
+++ b/docker-compose.prod.yml
@@ -0,0 +1,27 @@
+# --------------------------------------------------------------------------
+# docker-compose.prod.yml — Production deployment for the Bicycle Wheel
+# Circumference app.
+#
+# Builds a static bundle and serves it with Nginx on port 80.
+# Designed for use with Dokploy (Traefik handles TLS and routing).
+#
+# Local usage:
+# docker compose -f docker-compose.prod.yml up -d --build
+# → open http://localhost:80
+#
+# Dokploy usage:
+# Set the Compose Path to ./docker-compose.prod.yml in Dokploy.
+# Dokploy will inject Traefik labels and the dokploy-network
+# automatically — do NOT add them here.
+# --------------------------------------------------------------------------
+
+services:
+ web:
+ build:
+ context: .
+ dockerfile: Dockerfile.prod
+ restart: unless-stopped
+ # No ports mapping needed when behind Traefik/Dokploy.
+ # Uncomment the line below for local testing without a reverse proxy.
+ # ports:
+ # - "80:80"
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..f36fef4
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,29 @@
+server {
+ listen 80;
+ server_name _;
+
+ root /usr/share/nginx/html;
+ index index.html;
+
+ # Serve static files directly; fall back to index.html for SPA routing
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Cache static assets aggressively (Vite hashes filenames)
+ location /assets/ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+
+ # Don't log favicon/robots requests
+ location = /favicon.ico { access_log off; log_not_found off; }
+ location = /robots.txt { access_log off; log_not_found off; }
+
+ # Gzip compression for text-based assets
+ gzip on;
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
+}