Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
node_modules
build
.git
.gitignore
.docker
*.md
.env*
.DS_Store
# --- Dependencies ---
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# --- Build Artifacts ---
build/
dist/
.cache/

# --- Environment & Secrets ---
.env*

# --- Docker Files ---
# We ignore these because they aren't needed inside the image
Dockerfile
Dockerfile.*
docker-compose.yml
docker-compose.*.yml
.dockerignore
.docker
# --- Git ---
.git
.gitignore

# --- OS Junk ---
.DS_Store
Thumbs.db
*.md
15 changes: 10 additions & 5 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
PROJECT_NAME=api
GIT_URL=git@github.com:mainstreamer/docker-config.git
BRANCH='flags-api'
GIT_REMOTE_URL=git@github.com:mainstreamer/flagsapp.git
CODE_PATH=.docker
# Production environment (default)
#REACT_APP_API_URL=https://api.flags.izeebot.top
#REACT_APP_AUTH_URL=https://auth.izeebot.top
REACT_APP_API_URL=https://localhost:8000

# Auth service endpoint (auth/openid service)
# Local dev: https://localhost:8548
# Production: https://auth.izeebot.top
#REACT_APP_AUTH_URL=https://localhost:8548
REACT_APP_AUTH_URL=https://localhost:8547
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Local development environment
REACT_APP_API_URL=http://localhost:8000
5 changes: 5 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Local overrides (copy to .env.local)
# This file is gitignored

# Flags API - local: http://localhost:8000, prod: https://api.flags.izeebot.top
REACT_APP_API_URL=http://localhost:8000
4 changes: 4 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Production environment
# Used by `yarn build` locally and as defaults in Dockerfile
REACT_APP_API_URL=https://api.flags.izeebot.top
REACT_APP_AUTH_URL=https://auth.izeebot.top
2 changes: 2 additions & 0 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_VERSION=v${{ needs.version.outputs.new_version }}
REACT_APP_API_URL=${{ vars.REACT_APP_API_URL }}
REACT_APP_AUTH_URL=${{ vars.REACT_APP_AUTH_URL }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

Expand Down
24 changes: 13 additions & 11 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
:80 {
root * /srv
root * /usr/share/caddy

# Enable gzip compression
encode gzip
# Enable compression
encode zstd gzip

# SPA fallback - serve index.html for client-side routing
try_files {path} /index.html

file_server

# Cache static assets
# 1. Hashed Assets: Cache forever
# IMPROVEMENT: Match files inside /static/ folders specifically
# CRA/Vite usually puts hashed assets in /static/js or /assets/
@static {
path *.js *.css *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2
}
header @static Cache-Control "public, max-age=31536000, immutable"

# Don't cache index.html
# 2. HTML & Service Workers: Never cache
@html {
path *.html
path *.html sw.js
}
header @html Cache-Control "no-cache, no-store, must-revalidate"

# SPA fallback
try_files {path} /index.html

file_server
}
65 changes: 43 additions & 22 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,36 +1,57 @@
# Stage 1: Build the React application
FROM node:18-alpine AS builder
# --- Stage 1: Base ---
FROM node:18-alpine AS base

# Receive IDs from docker-compose
ARG USER_ID=1000
ARG GROUP_ID=1000

# Alpine-specific way to adjust 'node' user to match your host
RUN apk add --no-cache shadow && \
if [ ${USER_ID:-0} -ne 0 ] && [ ${GROUP_ID:-0} -ne 0 ]; then \
userdel -f node && \
if getent group node ; then groupdel node; fi && \
groupadd -g ${GROUP_ID} node && \
useradd -l -u ${USER_ID} -g node node && \
install -d -m 0755 -o node -g node /app; \
fi

WORKDIR /app
# From now on, the container acts as "you"
USER node

# Copy package files
COPY package.json yarn.lock* package-lock.json* ./
# 3. Copy dependency files first (for caching)
COPY --chown=node:node package.json yarn.lock* package-lock.json* ./

# Install dependencies
# Use frozen-lockfile for consistency
RUN if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else npm install; fi
# --- Stage 2: Development (Hot Reload) ---
FROM base AS development
ENV NODE_OPTIONS=--openssl-legacy-provider
# No COPY . . here! We use bind mounts in docker-compose.
EXPOSE 3000
CMD ["npm", "start"]

# Copy source code
# --- Stage 3: Builder (Compilation) ---
FROM base AS builder
COPY . .

# Build argument for version (passed from CI/CD pipeline)
ARG APP_VERSION=""
# Args to bake URLs into the JS bundle
ARG REACT_APP_API_URL
ARG REACT_APP_AUTH_URL
ENV REACT_APP_API_URL=$REACT_APP_API_URL
ENV REACT_APP_AUTH_URL=$REACT_APP_AUTH_URL
ENV NODE_OPTIONS=--openssl-legacy-provider
ENV REACT_APP_VERSION=${APP_VERSION}

# Build the production bundle with version baked in
RUN if [ -f yarn.lock ]; then yarn build; else npm run build; fi

# Stage 2: Serve with Caddy
FROM caddy:2-alpine

# Copy built files from builder stage
COPY --from=builder /app/build /srv

# Copy Caddyfile
# --- Stage 4: Production (Caddy) ---
FROM caddy:2-alpine AS production
# Set the working directory for Caddy
WORKDIR /usr/share/caddy
# Copy built files from the builder stage
COPY --from=builder /app/build /usr/share/caddy
# Copy your optimized Caddyfile
COPY Caddyfile /etc/caddy/Caddyfile

EXPOSE 80

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
# Use the config file we just copied
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
41 changes: 41 additions & 0 deletions Dockerfile.bkp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Stage 1: Build the React application
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package.json yarn.lock* package-lock.json* ./

# Install dependencies
RUN if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else npm install; fi

# Copy source code
COPY . .

# Build arguments (passed from CI/CD pipeline)
ARG APP_VERSION=""
ARG REACT_APP_API_URL=https://api.flags.izeebot.top
ARG REACT_APP_AUTH_URL=https://auth.izeebot.top

ENV NODE_OPTIONS=--openssl-legacy-provider
ENV REACT_APP_VERSION=${APP_VERSION}
ENV REACT_APP_API_URL=${REACT_APP_API_URL}
ENV REACT_APP_AUTH_URL=${REACT_APP_AUTH_URL}

# Build the production bundle with version baked in
RUN if [ -f yarn.lock ]; then yarn build; else npm run build; fi

# Stage 2: Serve with Caddy
FROM caddy:2-alpine

# Copy built files from builder stage
COPY --from=builder /app/build /srv

# Copy Caddyfile
COPY Caddyfile /etc/caddy/Caddyfile

EXPOSE 80

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
32 changes: 32 additions & 0 deletions Dockerfile.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Local development Dockerfile with configurable API endpoints
FROM node:18-alpine AS builder

WORKDIR /app

COPY package.json yarn.lock* package-lock.json* ./

RUN if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else npm install; fi

COPY . .

# Build arguments for API endpoints (HTTP for local dev)
ARG REACT_APP_API_URL=http://localhost:8000
ARG REACT_APP_AUTH_URL=http://localhost:8547

ENV NODE_OPTIONS=--openssl-legacy-provider
ENV REACT_APP_API_URL=${REACT_APP_API_URL}
ENV REACT_APP_AUTH_URL=${REACT_APP_AUTH_URL}

RUN yarn build

# Serve with Caddy
FROM caddy:2-alpine

COPY --from=builder /app/build /srv
COPY Caddyfile /etc/caddy/Caddyfile

EXPOSE 80

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
73 changes: 73 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.PHONY: help network dev prod-test build-prod down clean-docker sh logs

#CYAN := \033[0;36m
#RESET := \033[0m
# Use printf to set variables so they work with any 'echo'
CYAN := $(shell printf ' \033[0;36m')
RESET := $(shell printf ' \033[0m')

# Settings
COMPOSE_DEV := docker compose
COMPOSE_PROD := docker compose -f docker-compose.yml -f docker-compose.local-prod.yml

init: network ## First-time setup: Create networks, clean environment, and start dev
@echo "$(CYAN)Initializing Docker environment...$(RESET)"
@$(COMPOSE_DEV) down -v --remove-orphans
@echo "$(CYAN)Building and starting containers (this may take a few minutes)...$(RESET)"
@$(COMPOSE_DEV) up --build -d
@echo "$(CYAN)Installation complete!$(RESET)"
@echo "Checking container status..."
@$(COMPOSE_DEV) ps
@echo "Follow logs with: $(CYAN)make logs$(RESET)"
@echo "App will be available at: http://localhost:3000"

help: ## Show this help message
@printf "\nUsage: make $(CYAN)[target]$(RESET)\n\n"
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " $(CYAN)%-22s$(RESET) %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort -f

network: ## Create required Docker networks
@docker network create backend-flags 2>/dev/null || true
@docker network create openid_network 2>/dev/null || true
@echo "Networks verified."

# --- DEVELOPMENT ---
dev: network ## Start HOT-RELOAD development (Port 3000)
@echo "Starting Dev Server (Node 18)..."
@$(COMPOSE_DEV) up --build

# --- LOCAL PRODUCTION TESTING ---
prod-test: network ## Start LOCAL PROD container (Caddy, Port 3001)
@echo "Building & Starting Production-like container (Caddy)..."
@$(COMPOSE_PROD) up --build -d
@echo "App running at http://localhost:3001"

# --- CI / REAL PRODUCTION ---
build-prod: ## Manually build the final production image (Caddy stage)
@docker build --target production -t flags-app:latest .

# --- UTILS ---
down: ## Stop all local containers
@$(COMPOSE_DEV) down --remove-orphans
@$(COMPOSE_PROD) down --remove-orphans

sh: ## Drop into the running dev container shell
@$(COMPOSE_DEV) exec react sh

logs: ## Tail logs from the dev container
@$(COMPOSE_DEV) logs -f react

clean-docker: ## Nuclear option: remove volumes and orphans
@$(COMPOSE_DEV) down -v --remove-orphans
@docker system prune -f --filter "label=com.docker.compose.project"
@echo "Docker environment cleaned."

reinstall: ## Wipe container modules and reinstall from scratch
@echo "Hard resetting container dependencies..."
@$(COMPOSE_DEV) down -v
@$(COMPOSE_DEV) build --no-cache
@$(COMPOSE_DEV) up -d
@echo "Fresh dependencies installed inside the container."

npm-add: ## Add a new package (Usage: make npm-add PKG=axios)
@$(COMPOSE_DEV) exec react npm install $(PKG)
@echo "Package $(PKG) added and package.json updated via container."
12 changes: 12 additions & 0 deletions docker-compose.local.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
react:
build:
target: production # <--- Switches to the Caddy stage
args:
# Bakes your LOCAL ports into the production build
- REACT_APP_API_URL=${REACT_APP_API_URL:-http://localhost:3001}
- REACT_APP_AUTH_URL=${REACT_APP_AUTH_URL:-http://localhost:8547}
container_name: flags-local-prod
ports:
- "3001:80" # Run on 3001 so it doesn't clash with your dev server
volumes: !reset [] # Wipes the dev volumes (no code mounting!)
28 changes: 28 additions & 0 deletions docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
app:
build:
context: .
dockerfile: Dockerfile.local
args:
REACT_APP_API_URL: ${REACT_APP_API_URL:-http://localhost:8000}
REACT_APP_AUTH_URL: ${REACT_APP_AUTH_URL:-http://localhost:8547}
container_name: flagsapp-local
ports:
- "3000:3000"
networks:
- backend-flags
- openid_network
volumes:
- .:/app
- /app/node_modules # Anonymous volume to protect container modules
environment:
- CHOKIDAR_USEPOLLING=true # Essential for HMR on some OS (Fedora/Windows)
- WATCHPACK_POLLING=true # For newer Webpack/Vite versions
stdin_open: true # Keeps the terminal active
tty: true

networks:
backend-flags:
external: true
openid_network:
external: true
Loading