dockerized version instead of nixpack
This commit is contained in:
parent
7b98d2c894
commit
a76b371bda
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-*.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
docker-compose.yml
|
||||||
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
ARG NODE_VERSION=20.15.0
|
||||||
|
FROM node:${NODE_VERSION}-alpine AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install deps first for better caching
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci || npm install
|
||||||
|
|
||||||
|
# Build
|
||||||
|
COPY . .
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
# Limit Node heap during build to avoid OOM on small builders
|
||||||
|
ENV NODE_OPTIONS=--max-old-space-size=512
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ---- Runtime (Nginx) ----
|
||||||
|
FROM nginx:1.27-alpine AS runtime
|
||||||
|
# Remove default site content
|
||||||
|
RUN rm -rf /usr/share/nginx/html/*
|
||||||
|
# SPA config (history fallback)
|
||||||
|
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
# Static assets
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost/ >/dev/null 2>&1 || exit 1
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
34
README.md
34
README.md
@ -1,29 +1,31 @@
|
|||||||
# Reading Goal App
|
# Reading Goal App (Dockerized)
|
||||||
|
|
||||||
A React + Vite + Tailwind single-page app. Dates are DD/MM/YYYY everywhere and the calendar starts on Monday.
|
React + Vite + Tailwind. Dates use **DD/MM/YYYY** and the calendar week starts on **Monday**.
|
||||||
|
Includes a multi-stage Dockerfile: Node build → Nginx runtime with SPA fallback.
|
||||||
## Local Development
|
|
||||||
|
|
||||||
|
## Local Dev
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Production Build
|
## Production Build (local)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
npm run preview
|
npm run preview
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploy with Coolify (Nixpacks)
|
## Docker (build locally)
|
||||||
|
```bash
|
||||||
|
docker build -t reading-goal-app:latest .
|
||||||
|
docker run -d --name reading-goal-app -p 8080:80 --restart unless-stopped reading-goal-app:latest
|
||||||
|
# Open http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
1. Push this repo to GitHub.
|
## Deploy in Coolify
|
||||||
2. In Coolify: **Create Application → Git**, select your repo/branch.
|
- Deployment Type: **Dockerfile**
|
||||||
3. **Build Pack**: choose **Nixpacks**.
|
- Dockerfile path: `Dockerfile`
|
||||||
4. No base directory unless you're using a monorepo.
|
- **Internal Port**: `80` (Nginx)
|
||||||
5. (Optional) **Environment**: set `NODE_ENV=production`.
|
- No extra start command needed.
|
||||||
6. Deploy. The container will:
|
|
||||||
- install dependencies
|
The multi-stage image builds the app (`vite build`) and serves static files via Nginx with history API fallback.
|
||||||
- build with `vite build` (output in `dist/`)
|
|
||||||
- run `serve -s dist -l $PORT` (Coolify injects `$PORT`)
|
|
||||||
|
|||||||
18
docker/nginx.conf
Normal file
18
docker/nginx.conf
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Serve files if they exist, else fallback to index.html (SPA)
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static assets (cache-friendly)
|
||||||
|
location ~* \.(?:js|css|png|jpg|jpeg|gif|svg|ico|woff2?)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview --host 0.0.0.0 --port 5173",
|
"preview": "vite preview --host 0.0.0.0 --port 5173"
|
||||||
"start": "serve -s dist -l ${PORT:-3000}"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lucide-react": "^0.452.0",
|
"lucide-react": "^0.452.0",
|
||||||
@ -20,7 +19,6 @@
|
|||||||
"@vitejs/plugin-react": "^4.2.0",
|
"@vitejs/plugin-react": "^4.2.0",
|
||||||
"autoprefixer": "^10.4.18",
|
"autoprefixer": "^10.4.18",
|
||||||
"postcss": "^8.4.33",
|
"postcss": "^8.4.33",
|
||||||
"serve": "^14.2.1",
|
|
||||||
"tailwindcss": "^3.4.9",
|
"tailwindcss": "^3.4.9",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^5.3.4"
|
"vite": "^5.3.4"
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { BookOpen, Plus, X, Edit2, Check, ChevronLeft, ChevronRight } from 'lucide-react';
|
import { BookOpen, Plus, X, Edit2, Check, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
|
|
||||||
@ -598,7 +597,7 @@ const ReadingGoalApp = () => {
|
|||||||
<h3 className="font-semibold mb-3">Progress Overview</h3>
|
<h3 className="font-semibold mb-3">Progress Overview</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-between"><span className="text-gray-600">Current Progress</span><span className="font-medium">{book.currentPage} / {book.totalPages} pages</span></div>
|
<div className="flex justify-between"><span className="text-gray-600">Current Progress</span><span className="font-medium">{book.currentPage} / {book.totalPages} pages</span></div>
|
||||||
<div className="w.full bg-gray-200 rounded-full h-3"><div className="bg-blue-500 h-3 rounded-full transition-all" style={{ width: `${Math.min(progressPercent, 100)}%` }} /></div>
|
<div className="w-full bg-gray-200 rounded-full h-3"><div className="bg-blue-500 h-3 rounded-full transition-all" style={{ width: `${Math.min(progressPercent, 100)}%` }} /></div>
|
||||||
<div className="flex justify-between"><span className="text-gray-600">Completion</span><span className="font-medium">{progressPercent.toFixed(1)}%</span></div>
|
<div className="flex justify-between"><span className="text-gray-600">Completion</span><span className="font-medium">{progressPercent.toFixed(1)}%</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user