Skip to content
Open
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
99 changes: 99 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Production build (will be built inside container)
.next
out

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Git
.git
.gitignore

# Docker
Dockerfile
.dockerignore

# IDE
.vscode
.idea
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage

# Dependency directories
jspm_packages/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Storybook build outputs
.out
.storybook-out

# Temporary folders
tmp/
temp/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,35 @@ See `test/LIGHTNING_TESTING_SETUP.md` for a full end-to-end walkthrough and trou

## Production

Build and run a production server:
### Static Export (Recommended)

Build as static files for hosting anywhere:

```bash
npm run build # Generates static files in out/
npm run static # Build and serve locally for testing
```

Deploy the `out/` folder to any static hosting:

- Netlify, Vercel, GitHub Pages, AWS S3, Firebase Hosting, etc.
- No server required - pure client-side app

### Docker

```bash
npm run docker:build # Build container with nginx
npm run docker:run # Run on port 3000
```

### Traditional Server

```bash
npm run build
npm run start
```

You can deploy using any platform that supports Next.js 15 (Node.js 18+). Ensure required environment variables are set in your hosting provider.
Deploy to any platform supporting Next.js 15 (Node.js 18+).

## Troubleshooting

Expand All @@ -126,4 +147,4 @@ Key directories:

## License

MIT
MIT
34 changes: 34 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Multi-stage build
FROM node:22-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies (including dev dependencies needed for build)
RUN npm ci

# Copy source code
COPY . .

# Build the static export
RUN npm run build

# Production stage with nginx
FROM nginx:alpine

# Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy built static files from builder stage
COPY --from=builder /app/out /usr/share/nginx/html

# Copy nginx configuration
COPY docker/nginx.conf /etc/nginx/nginx.conf

# Expose port 80
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]
38 changes: 38 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Docker Setup

Simple Docker setup to serve your static Next.js app with nginx.

## Usage

```bash
# Build the Docker image
npm run docker:build

# Run the container (serves on port 3000)
npm run docker:run

# Stop the container
npm run docker:stop
```

## Manual Docker Commands

```bash
# Build
docker build -f docker/Dockerfile -t routstr-chat .

# Run
docker run -d -p 3000:80 --name routstr-chat routstr-chat

# Stop
docker stop routstr-chat && docker rm routstr-chat
```

## What it does

1. Builds your Next.js app as static files
2. Serves them with nginx
3. Handles SPA routing (all routes fallback to index.html)
4. Optimizes static assets with caching and compression

Final image size: ~27MB
96 changes: 96 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;

server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'self';" always;

# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}

# Cache Next.js static files
location /_next/static/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}

# Handle Next.js dynamic routes - fallback to index.html for client-side routing
location / {
try_files $uri $uri/ /index.html;
}

# Handle 404s
error_page 404 /404.html;
location = /404.html {
internal;
}

# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"static": "next build && npx serve out",
"docker:build": "docker build -f docker/Dockerfile -t routstr-chat .",
"docker:run": "docker run -d -p 3000:80 --name routstr-chat routstr-chat",
"docker:stop": "docker stop routstr-chat && docker rm routstr-chat",
"test:invoices": "node test/invoice-persistence.test.js",
"test:invoices:integration": "node test/invoice-integration.test.js",
"test:setup": "./test/setup-regtest-mint.sh"
Expand Down Expand Up @@ -64,4 +68,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
}
}