production configs, deployment configs and readme
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
# Docker ignore file
|
||||||
|
node_modules
|
||||||
|
.next
|
||||||
|
.git
|
||||||
|
.env.local
|
||||||
|
.env.development
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
.DS_Store
|
||||||
|
*.tgz
|
||||||
|
*.tar.gz
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Production Environment Configuration
|
||||||
|
# Domain: lcc-tt-booking.mikicvi.com
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL="./data/sqlite.db"
|
||||||
|
|
||||||
|
# NextAuth.js
|
||||||
|
NEXTAUTH_URL="https://lcc-tt-booking.mikicvi.com"
|
||||||
|
NEXTAUTH_SECRET="qHYNaq516ByAY6H4HdxacMICd05I1DqvrTitIuVtT20="
|
||||||
|
|
||||||
|
# Email Configuration (Gmail - Update with your credentials)
|
||||||
|
EMAIL_USER="your-email@gmail.com"
|
||||||
|
EMAIL_PASSWORD="your-app-password-here"
|
||||||
|
|
||||||
|
# Admin Configuration (Change these for production!)
|
||||||
|
ADMIN_EMAIL="admin@lcc-tt-booking.mikicvi.com"
|
||||||
|
ADMIN_PASSWORD="ChangeMeInProduction123!"
|
||||||
|
|
||||||
|
# Application Settings
|
||||||
|
NODE_ENV="production"
|
||||||
|
PORT="3000"
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
RATE_LIMIT_MAX="100"
|
||||||
|
RATE_LIMIT_WINDOW="900000"
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
LOG_LEVEL="info"
|
||||||
@@ -0,0 +1,506 @@
|
|||||||
|
# Deployment Strategy for Table Tennis Booking System
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document outlines comprehensive deployment strategies for the Table Tennis Booking System, considering both self-hosting and cloud deployment options. The application is a Next.js-based system with SQLite database, designed for production use.
|
||||||
|
|
||||||
|
## 1. Self-Hosting Strategy
|
||||||
|
|
||||||
|
### Option A: Raspberry Pi + Cloudflare Tunnel (Recommended)
|
||||||
|
|
||||||
|
**Architecture:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → Cloudflare → Cloudflare Tunnel → Raspberry Pi → Docker Container
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
- Raspberry Pi 4 (4GB+ RAM recommended)
|
||||||
|
- Stable internet connection
|
||||||
|
- Cloudflare account (free tier sufficient)
|
||||||
|
- Domain name (can be managed through Cloudflare)
|
||||||
|
|
||||||
|
**Setup Steps:**
|
||||||
|
|
||||||
|
1. **Raspberry Pi Preparation**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update system
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
sudo sh get-docker.sh
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
|
||||||
|
# Install Docker Compose
|
||||||
|
sudo apt install docker-compose -y
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Application Deployment**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone <your-repo-url>
|
||||||
|
cd tt-booking
|
||||||
|
|
||||||
|
# Create production environment file
|
||||||
|
cp .env.example .env.production
|
||||||
|
# Edit .env.production with your values
|
||||||
|
|
||||||
|
# Deploy with Docker
|
||||||
|
docker-compose -f docker-compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Cloudflare Tunnel Setup**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install cloudflared
|
||||||
|
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb
|
||||||
|
sudo dpkg -i cloudflared-linux-arm64.deb
|
||||||
|
|
||||||
|
# Authenticate
|
||||||
|
cloudflared tunnel login
|
||||||
|
|
||||||
|
# Create tunnel
|
||||||
|
cloudflared tunnel create tt-booking
|
||||||
|
|
||||||
|
# Configure tunnel (create config.yml)
|
||||||
|
cloudflared tunnel route dns tt-booking yourdomain.com
|
||||||
|
|
||||||
|
# Run tunnel
|
||||||
|
cloudflared tunnel run tt-booking
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cloudflare Tunnel Config (`~/.cloudflared/config.yml`):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
tunnel: <tunnel-id>
|
||||||
|
credentials-file: /home/pi/.cloudflared/<tunnel-id>.json
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
- hostname: yourdomain.com
|
||||||
|
service: http://localhost:3000
|
||||||
|
- service: http_status:404
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production Docker Compose (`docker-compose.production.yml`):**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
tt-booking:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- '3000:3000'
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- DATABASE_URL=/app/data/sqlite.db
|
||||||
|
- NEXTAUTH_URL=https://yourdomain.com
|
||||||
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||||
|
- EMAIL_USER=${EMAIL_USER}
|
||||||
|
- EMAIL_PASSWORD=${EMAIL_PASSWORD}
|
||||||
|
- ADMIN_EMAIL=${ADMIN_EMAIL}
|
||||||
|
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
- ./backups:/app/backups
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/health']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# Backup service
|
||||||
|
backup:
|
||||||
|
image: alpine:latest
|
||||||
|
volumes:
|
||||||
|
- ./data:/data:ro
|
||||||
|
- ./backups:/backups
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
while true; do
|
||||||
|
cp /data/sqlite.db /backups/sqlite-$(date +%Y%m%d-%H%M%S).db
|
||||||
|
find /backups -name 'sqlite-*.db' -mtime +7 -delete
|
||||||
|
sleep 86400
|
||||||
|
done"
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
|
||||||
|
- No need for port forwarding or exposing home IP
|
||||||
|
- Free SSL certificates through Cloudflare
|
||||||
|
- DDoS protection and CDN benefits
|
||||||
|
- Easy domain management
|
||||||
|
- Cost-effective (only domain cost ~$10-15/year)
|
||||||
|
|
||||||
|
**Disadvantages:**
|
||||||
|
|
||||||
|
- Dependent on home internet stability
|
||||||
|
- Limited by residential bandwidth
|
||||||
|
- Requires basic Linux administration skills
|
||||||
|
|
||||||
|
### Option B: Traditional Self-Hosting with Reverse Proxy
|
||||||
|
|
||||||
|
**Architecture:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → Router/Firewall → Nginx → Docker Container
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
- Dedicated server or powerful Raspberry Pi
|
||||||
|
- Static IP address or Dynamic DNS service
|
||||||
|
- SSL certificate (Let's Encrypt)
|
||||||
|
- Port forwarding configuration
|
||||||
|
|
||||||
|
**Setup includes all the Docker setup above, plus:**
|
||||||
|
|
||||||
|
1. **Nginx Configuration**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Nginx
|
||||||
|
sudo apt install nginx certbot python3-certbot-nginx
|
||||||
|
|
||||||
|
# Configure SSL
|
||||||
|
sudo certbot --nginx -d yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Updated Docker Compose with Nginx**
|
||||||
|
Use the existing [docker-compose.yml](docker-compose.yml) with Nginx service.
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
|
||||||
|
- Full control over infrastructure
|
||||||
|
- No dependency on third-party tunneling services
|
||||||
|
- Better performance for local network access
|
||||||
|
|
||||||
|
**Disadvantages:**
|
||||||
|
|
||||||
|
- Requires static IP or Dynamic DNS
|
||||||
|
- More complex firewall/security configuration
|
||||||
|
- SSL certificate management overhead
|
||||||
|
|
||||||
|
## 2. Cloud Deployment Strategies
|
||||||
|
|
||||||
|
### Option A: DigitalOcean App Platform (Recommended for Small Scale)
|
||||||
|
|
||||||
|
**Cost Estimate:** $12-25/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
- Automatic deployments from Git
|
||||||
|
- Built-in SSL certificates
|
||||||
|
- Automatic scaling
|
||||||
|
- Integrated monitoring
|
||||||
|
|
||||||
|
**Deployment:**
|
||||||
|
|
||||||
|
1. Connect GitHub repository
|
||||||
|
2. Configure environment variables
|
||||||
|
3. Add persistent volume for SQLite database
|
||||||
|
4. Deploy with zero-config
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .do/app.yaml
|
||||||
|
name: tt-booking
|
||||||
|
services:
|
||||||
|
- name: web
|
||||||
|
source_dir: /
|
||||||
|
github:
|
||||||
|
repo: your-username/tt-booking
|
||||||
|
branch: main
|
||||||
|
run_command: npm start
|
||||||
|
environment_slug: node-js
|
||||||
|
instance_count: 1
|
||||||
|
instance_size_slug: basic-xxs
|
||||||
|
envs:
|
||||||
|
- key: NODE_ENV
|
||||||
|
value: production
|
||||||
|
- key: DATABASE_URL
|
||||||
|
value: /app/data/sqlite.db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Railway (Developer-Friendly)
|
||||||
|
|
||||||
|
**Cost Estimate:** $5-20/month
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
- Git-based deployments
|
||||||
|
- Built-in databases available
|
||||||
|
- Simple pricing model
|
||||||
|
- Excellent developer experience
|
||||||
|
|
||||||
|
**Deployment:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Railway CLI
|
||||||
|
npm install -g @railway/cli
|
||||||
|
|
||||||
|
# Login and deploy
|
||||||
|
railway login
|
||||||
|
railway init
|
||||||
|
railway up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option C: Vercel + PlanetScale (Serverless)
|
||||||
|
|
||||||
|
**Cost Estimate:** $0-20/month (depending on usage)
|
||||||
|
|
||||||
|
**Modifications needed:**
|
||||||
|
|
||||||
|
- Replace SQLite with PlanetScale MySQL
|
||||||
|
- Update database schema for MySQL compatibility
|
||||||
|
- Modify connection configuration
|
||||||
|
|
||||||
|
**Deployment:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Vercel CLI
|
||||||
|
npm install -g vercel
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
vercel --prod
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option D: AWS/GCP/Azure (Enterprise Scale)
|
||||||
|
|
||||||
|
**Cost Estimate:** $30-100+/month
|
||||||
|
|
||||||
|
**AWS Deployment Options:**
|
||||||
|
|
||||||
|
1. **ECS Fargate + RDS**
|
||||||
|
|
||||||
|
- Container-based deployment
|
||||||
|
- Managed database
|
||||||
|
- Auto-scaling capabilities
|
||||||
|
|
||||||
|
2. **Elastic Beanstalk**
|
||||||
|
|
||||||
|
- Simple deployment model
|
||||||
|
- Built-in monitoring
|
||||||
|
- Easy environment management
|
||||||
|
|
||||||
|
3. **App Runner**
|
||||||
|
- Serverless container platform
|
||||||
|
- Automatic scaling
|
||||||
|
- Pay-per-use pricing
|
||||||
|
|
||||||
|
## 3. Database Considerations
|
||||||
|
|
||||||
|
### For Self-Hosting
|
||||||
|
|
||||||
|
- **SQLite**: Perfect for small to medium loads
|
||||||
|
- **Backup Strategy**: Automated daily backups to external storage
|
||||||
|
- **Monitoring**: Simple file-based health checks
|
||||||
|
|
||||||
|
### For Cloud Deployment
|
||||||
|
|
||||||
|
- **Small Scale**: Keep SQLite with persistent volumes
|
||||||
|
- **Medium Scale**: PostgreSQL (Railway, DigitalOcean Managed DB)
|
||||||
|
- **Large Scale**: Multi-region database (AWS RDS, Google Cloud SQL)
|
||||||
|
|
||||||
|
## 4. Monitoring and Maintenance
|
||||||
|
|
||||||
|
### Essential Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add to crontab for health checks
|
||||||
|
*/5 * * * * curl -f https://yourdomain.com/api/health || echo "App down" | mail -s "Alert" admin@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Strategy
|
||||||
|
|
||||||
|
1. **Database Backups**: Daily automated SQLite file copies
|
||||||
|
2. **Environment Config**: Version controlled `.env` files
|
||||||
|
3. **Application Code**: Git-based source control
|
||||||
|
|
||||||
|
### Update Strategy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# update.sh
|
||||||
|
cd /path/to/tt-booking
|
||||||
|
git pull origin main
|
||||||
|
docker-compose -f docker-compose.production.yml down
|
||||||
|
docker-compose -f docker-compose.production.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Security Considerations
|
||||||
|
|
||||||
|
### Self-Hosting Security Checklist
|
||||||
|
|
||||||
|
- [ ] Firewall configured (only necessary ports open)
|
||||||
|
- [ ] Regular OS updates automated
|
||||||
|
- [ ] Non-root user for application
|
||||||
|
- [ ] SSL certificates properly configured
|
||||||
|
- [ ] Database backups encrypted
|
||||||
|
- [ ] Rate limiting configured (already in nginx.conf)
|
||||||
|
- [ ] Log monitoring for suspicious activity
|
||||||
|
|
||||||
|
### Cloud Security
|
||||||
|
|
||||||
|
- [ ] Environment variables properly secured
|
||||||
|
- [ ] Database access restricted
|
||||||
|
- [ ] API rate limiting enabled
|
||||||
|
- [ ] Regular dependency updates
|
||||||
|
- [ ] Security headers configured (already in app)
|
||||||
|
|
||||||
|
## 6. Cost Comparison
|
||||||
|
|
||||||
|
| Deployment Method | Monthly Cost | Effort | Scalability | Reliability |
|
||||||
|
| ------------------------- | ------------ | -------- | ----------- | ----------- |
|
||||||
|
| Raspberry Pi + CF Tunnel | $1-2 | Medium | Low | Medium |
|
||||||
|
| Traditional Self-Host | $5-10 | High | Low | Medium |
|
||||||
|
| DigitalOcean App Platform | $12-25 | Low | Medium | High |
|
||||||
|
| Railway | $5-20 | Very Low | Medium | High |
|
||||||
|
| Vercel + PlanetScale | $0-20 | Low | High | High |
|
||||||
|
| AWS/GCP/Azure | $30-100+ | High | Very High | Very High |
|
||||||
|
|
||||||
|
## 7. Recommended Approach
|
||||||
|
|
||||||
|
### For Personal/Small Group Use:
|
||||||
|
|
||||||
|
**Raspberry Pi + Cloudflare Tunnel** - Most cost-effective with good reliability
|
||||||
|
|
||||||
|
### For Small Business:
|
||||||
|
|
||||||
|
**Railway or DigitalOcean App Platform** - Balance of simplicity and reliability
|
||||||
|
|
||||||
|
### For Enterprise:
|
||||||
|
|
||||||
|
**AWS/GCP with proper CI/CD pipeline** - Maximum scalability and reliability
|
||||||
|
|
||||||
|
## 8. Local Development Best Practices
|
||||||
|
|
||||||
|
### Standalone Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Quick development setup
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development with Docker
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production-like Local Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test production build locally
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Health Check Endpoint
|
||||||
|
|
||||||
|
The application includes a health check endpoint at `/api/health` for monitoring purposes. You should create this endpoint:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// app/api/health/route.ts
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { db } from '@/lib/db';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// Basic database connectivity check
|
||||||
|
await db.select().from(settings).limit(1);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
status: 'healthy',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
uptime: process.uptime(),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json({ status: 'unhealthy', error: 'Database connection failed' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. Environment Variables for Production
|
||||||
|
|
||||||
|
Create a `.env.production` file with the following required variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Application
|
||||||
|
NODE_ENV=production
|
||||||
|
NEXTAUTH_URL=https://yourdomain.com
|
||||||
|
NEXTAUTH_SECRET=your-very-long-random-secret-here
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DATABASE_URL=/app/data/sqlite.db
|
||||||
|
|
||||||
|
# Email Configuration (optional)
|
||||||
|
EMAIL_USER=your-email@gmail.com
|
||||||
|
EMAIL_PASSWORD=your-app-specific-password
|
||||||
|
|
||||||
|
# Admin Account
|
||||||
|
ADMIN_EMAIL=admin@yourdomain.com
|
||||||
|
ADMIN_PASSWORD=secure-admin-password
|
||||||
|
|
||||||
|
# Optional: Rate limiting
|
||||||
|
RATE_LIMIT_MAX=100
|
||||||
|
RATE_LIMIT_WINDOW=900000
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Docker Production Optimization
|
||||||
|
|
||||||
|
Create a production-optimized `Dockerfile.production`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:18-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm ci --only=production && npm cache clean --force
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy built application
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Create data directory for SQLite
|
||||||
|
RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
```
|
||||||
|
|
||||||
|
This deployment strategy provides multiple pathways depending on your technical expertise, budget, and scaling requirements. The Cloudflare Tunnel approach is particularly attractive for self-hosting as it eliminates many traditional networking complexities while maintaining security and reliability.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Multi-stage production Dockerfile for LCC Table Tennis Booking
|
||||||
|
FROM node:22-slim AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN \
|
||||||
|
if [ -f package-lock.json ]; then npm ci --ignore-scripts; \
|
||||||
|
else echo "Lockfile not found." && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Rebuild better-sqlite3 for Alpine Linux
|
||||||
|
RUN npm rebuild better-sqlite3
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Create system user and group
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy necessary files from builder stage
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
# Create public directory if it doesn't exist
|
||||||
|
RUN mkdir -p public
|
||||||
|
|
||||||
|
# Create directories for data and backups
|
||||||
|
RUN mkdir -p /app/data /app/backups /app/logs && \
|
||||||
|
chown -R nextjs:nodejs /app/data /app/backups /app/logs
|
||||||
|
|
||||||
|
# Create startup script
|
||||||
|
COPY --chown=nextjs:nodejs <<EOF /app/start.sh
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
echo "🚀 Starting TT Booking Production..."
|
||||||
|
echo "🌟 Starting server..."
|
||||||
|
exec node server.js
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RUN chmod +x /app/start.sh
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:3000/api/health || exit 1
|
||||||
|
|
||||||
|
CMD ["/app/start.sh"]
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# LCC Table Tennis Booking - Production Setup
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Your production environment is configured for domain: **lcc-tt-booking.mikicvi.com**
|
||||||
|
|
||||||
|
### 1. Deploy the Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Make deployment script executable (if not already done)
|
||||||
|
chmod +x deploy.sh
|
||||||
|
|
||||||
|
# Deploy to production
|
||||||
|
./deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Set Up Cloudflare Tunnel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the tunnel setup script
|
||||||
|
./setup-tunnel.sh
|
||||||
|
|
||||||
|
# Follow the instructions provided by the script
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Files Created
|
||||||
|
|
||||||
|
### Environment Configuration
|
||||||
|
|
||||||
|
- **`.env.production`** - Production environment variables
|
||||||
|
- **`docker-compose.production.yml`** - Production Docker Compose configuration
|
||||||
|
- **`Dockerfile.production`** - Optimized production Docker build
|
||||||
|
|
||||||
|
### Security & Secrets
|
||||||
|
|
||||||
|
- **NEXTAUTH_SECRET**: `qHYNaq516ByAY6H4HdxacMICd05I1DqvrTitIuVtT20=` (pre-generated)
|
||||||
|
- **Domain**: `lcc-tt-booking.mikicvi.com`
|
||||||
|
- **Admin Email**: `admin@lcc-tt-booking.mikicvi.com`
|
||||||
|
|
||||||
|
### Scripts & Automation
|
||||||
|
|
||||||
|
- **`deploy.sh`** - One-command production deployment
|
||||||
|
- **`setup-tunnel.sh`** - Cloudflare Tunnel setup assistant
|
||||||
|
- **`cloudflare-tunnel-config.yml`** - Tunnel configuration template
|
||||||
|
|
||||||
|
### Health & Monitoring
|
||||||
|
|
||||||
|
- **`/api/health`** - Health check endpoint with database connectivity and memory usage
|
||||||
|
- **Automated backups** - Daily SQLite backups (30-day retention)
|
||||||
|
- **Log rotation** - Automatic log management
|
||||||
|
|
||||||
|
## Production Checklist
|
||||||
|
|
||||||
|
### Before Going Live:
|
||||||
|
|
||||||
|
- [ ] Update email credentials in `.env.production`
|
||||||
|
- [ ] Change admin password from default
|
||||||
|
- [ ] Set up Cloudflare Tunnel
|
||||||
|
- [ ] Test health check endpoint
|
||||||
|
- [ ] Verify SSL certificate is working
|
||||||
|
|
||||||
|
### After Deployment:
|
||||||
|
|
||||||
|
- [ ] Test all booking functionality
|
||||||
|
- [ ] Verify admin panel access
|
||||||
|
- [ ] Check automated backups are working
|
||||||
|
- [ ] Set up monitoring alerts (optional)
|
||||||
|
|
||||||
|
## Management Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View application logs
|
||||||
|
docker-compose -f docker-compose.production.yml logs -f tt-booking
|
||||||
|
|
||||||
|
# Restart application
|
||||||
|
docker-compose -f docker-compose.production.yml restart tt-booking
|
||||||
|
|
||||||
|
# Stop all services
|
||||||
|
docker-compose -f docker-compose.production.yml down
|
||||||
|
|
||||||
|
# Update and redeploy
|
||||||
|
./deploy.sh
|
||||||
|
|
||||||
|
# Access database backup
|
||||||
|
ls -la backups/
|
||||||
|
|
||||||
|
# Check application health
|
||||||
|
curl http://localhost:3000/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/Users/mikicv/Documents/tt-booking/
|
||||||
|
├── .env.production # Production environment variables
|
||||||
|
├── docker-compose.production.yml # Production Docker Compose
|
||||||
|
├── Dockerfile.production # Production Docker build
|
||||||
|
├── deploy.sh # Deployment script
|
||||||
|
├── setup-tunnel.sh # Cloudflare Tunnel setup
|
||||||
|
├── cloudflare-tunnel-config.yml # Tunnel configuration
|
||||||
|
├── data/ # SQLite database storage
|
||||||
|
├── backups/ # Automated backups
|
||||||
|
└── logs/ # Application logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Default Admin Access
|
||||||
|
|
||||||
|
- **URL**: https://lcc-tt-booking.mikicvi.com/admin
|
||||||
|
- **Email**: admin@lcc-tt-booking.mikicvi.com
|
||||||
|
- **Password**: ChangeMeInProduction123! (⚠️ CHANGE THIS!)
|
||||||
|
|
||||||
|
## Support & Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues:
|
||||||
|
|
||||||
|
1. **Container won't start**: Check `docker-compose -f docker-compose.production.yml logs`
|
||||||
|
2. **Database issues**: Ensure `data/` directory permissions are correct
|
||||||
|
3. **Tunnel not working**: Verify Cloudflare DNS settings and tunnel configuration
|
||||||
|
|
||||||
|
### Health Check:
|
||||||
|
|
||||||
|
The health endpoint (`/api/health`) provides:
|
||||||
|
|
||||||
|
- Application status
|
||||||
|
- Database connectivity
|
||||||
|
- Memory usage
|
||||||
|
- Uptime information
|
||||||
|
|
||||||
|
### Backup Verification:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all backups
|
||||||
|
ls -la backups/
|
||||||
|
|
||||||
|
# Check latest backup size
|
||||||
|
du -h backups/sqlite-$(date +%Y%m%d)*.db | tail -1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Features Included:
|
||||||
|
|
||||||
|
- ✅ Automated daily backups (30-day retention)
|
||||||
|
- ✅ Health monitoring endpoint
|
||||||
|
- ✅ Log rotation and management
|
||||||
|
- ✅ Multi-stage Docker optimization
|
||||||
|
- ✅ Security hardening
|
||||||
|
- ✅ Rate limiting configured
|
||||||
|
- ✅ SSL-ready with Cloudflare integration
|
||||||
|
|
||||||
|
Your LCC Table Tennis Booking System is ready for production! 🏓
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { db } from '@/lib/db';
|
||||||
|
import { settings } from '@/lib/db/schema';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// Test database connectivity
|
||||||
|
const startTime = Date.now();
|
||||||
|
await db.select().from(settings).limit(1);
|
||||||
|
const dbLatency = Date.now() - startTime;
|
||||||
|
|
||||||
|
// Check memory usage
|
||||||
|
const memoryUsage = process.memoryUsage();
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
status: 'healthy',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
uptime: Math.floor(process.uptime()),
|
||||||
|
environment: process.env.NODE_ENV,
|
||||||
|
database: {
|
||||||
|
status: 'connected',
|
||||||
|
latency: `${dbLatency}ms`,
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
rss: `${Math.round(memoryUsage.rss / 1024 / 1024)}MB`,
|
||||||
|
heapUsed: `${Math.round(memoryUsage.heapUsed / 1024 / 1024)}MB`,
|
||||||
|
heapTotal: `${Math.round(memoryUsage.heapTotal / 1024 / 1024)}MB`,
|
||||||
|
},
|
||||||
|
version: process.env.npm_package_version || '1.0.0',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Health check failed:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
status: 'unhealthy',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
error: 'Database connection failed',
|
||||||
|
details: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Cloudflare Tunnel Configuration for LCC Table Tennis Booking
|
||||||
|
# Domain: lcc-tt-booking.mikicvi.com
|
||||||
|
|
||||||
|
# Save this as ~/.cloudflared/config.yml after setting up your tunnel
|
||||||
|
|
||||||
|
tunnel: <your-tunnel-id>
|
||||||
|
credentials-file: /home/pi/.cloudflared/<your-tunnel-id>.json
|
||||||
|
|
||||||
|
# Ingress rules
|
||||||
|
ingress:
|
||||||
|
# Main application
|
||||||
|
- hostname: lcc-tt-booking.mikicvi.com
|
||||||
|
service: http://localhost:3000
|
||||||
|
originRequest:
|
||||||
|
# Enable HTTP/2
|
||||||
|
httpHostHeader: lcc-tt-booking.mikicvi.com
|
||||||
|
# Connection settings
|
||||||
|
connectTimeout: 30s
|
||||||
|
tlsTimeout: 10s
|
||||||
|
# Health checks
|
||||||
|
proxyType: http
|
||||||
|
# Disable chunked encoding for better compatibility
|
||||||
|
disableChunkedEncoding: true
|
||||||
|
|
||||||
|
# Health check endpoint (optional, for monitoring)
|
||||||
|
- hostname: health.lcc-tt-booking.mikicvi.com
|
||||||
|
service: http://localhost:3000/api/health
|
||||||
|
|
||||||
|
# Catch-all rule (must be last)
|
||||||
|
- service: http_status:404
|
||||||
|
|
||||||
|
# Optional: Logging configuration
|
||||||
|
loglevel: info
|
||||||
|
transport-loglevel: warn
|
||||||
|
|
||||||
|
# Optional: Metrics
|
||||||
|
metrics: 0.0.0.0:2000
|
||||||
|
|
||||||
|
# Optional: Enable compression
|
||||||
|
compression: gzip
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# LCC Table Tennis Booking - Production Deployment Script
|
||||||
|
# Domain: lcc-tt-booking.mikicvi.com
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Starting production deployment for LCC Table Tennis Booking..."
|
||||||
|
|
||||||
|
# Check if .env.production exists
|
||||||
|
if [ ! -f .env.production ]; then
|
||||||
|
echo "❌ .env.production file not found!"
|
||||||
|
echo "Please create .env.production with your production environment variables."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
echo "📁 Creating necessary directories..."
|
||||||
|
mkdir -p data backups logs
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
echo "🔒 Setting directory permissions..."
|
||||||
|
chmod 755 data backups logs
|
||||||
|
|
||||||
|
# Pull latest changes (if using git)
|
||||||
|
if [ -d ".git" ]; then
|
||||||
|
echo "📦 Pulling latest changes..."
|
||||||
|
git pull origin main || echo "⚠️ Git pull failed or not needed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup database
|
||||||
|
echo "🛠️ Setting up the database..."
|
||||||
|
npx tsx scripts/setup-database.ts
|
||||||
|
|
||||||
|
# Build and deploy with Docker Compose
|
||||||
|
echo "🐳 Building and starting Docker containers..."
|
||||||
|
|
||||||
|
# Stop existing containers
|
||||||
|
docker-compose -f docker-compose.production.yml down || echo "No existing containers to stop"
|
||||||
|
|
||||||
|
# Build and start containers
|
||||||
|
docker-compose -f docker-compose.production.yml up -d --build
|
||||||
|
|
||||||
|
# Wait for containers to be healthy
|
||||||
|
echo "⏳ Waiting for containers to be healthy..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Check health
|
||||||
|
echo "🔍 Checking application health..."
|
||||||
|
for i in {1..10}; do
|
||||||
|
if curl -f http://localhost:3000/api/health >/dev/null 2>&1; then
|
||||||
|
echo "✅ Application is healthy!"
|
||||||
|
break
|
||||||
|
elif [ $i -eq 10 ]; then
|
||||||
|
echo "❌ Application health check failed after 10 attempts"
|
||||||
|
echo "📋 Container logs:"
|
||||||
|
docker-compose -f docker-compose.production.yml logs tt-booking
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "⏳ Attempt $i/10: Application not ready yet, waiting..."
|
||||||
|
sleep 10
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Show running containers
|
||||||
|
echo "📊 Running containers:"
|
||||||
|
docker-compose -f docker-compose.production.yml ps
|
||||||
|
|
||||||
|
# Show logs
|
||||||
|
echo "📋 Recent application logs:"
|
||||||
|
docker-compose -f docker-compose.production.yml logs --tail=20 tt-booking
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 Deployment completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "📊 Application Status:"
|
||||||
|
echo " • URL: https://lcc-tt-booking.mikicvi.com"
|
||||||
|
echo " • Health Check: http://localhost:3000/api/health"
|
||||||
|
echo " • Container Status: $(docker-compose -f docker-compose.production.yml ps -q tt-booking | xargs docker inspect -f '{{.State.Status}}')"
|
||||||
|
echo ""
|
||||||
|
echo "🔧 Useful commands:"
|
||||||
|
echo " • View logs: docker-compose -f docker-compose.production.yml logs -f tt-booking"
|
||||||
|
echo " • Restart: docker-compose -f docker-compose.production.yml restart tt-booking"
|
||||||
|
echo " • Stop: docker-compose -f docker-compose.production.yml down"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Don't forget to:"
|
||||||
|
echo " 1. Set up Cloudflare Tunnel to expose your application"
|
||||||
|
echo " 2. Update your .env.production with real email credentials"
|
||||||
|
echo " 3. Change the default admin password"
|
||||||
|
echo ""
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
tt-booking:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: lcc-tt-booking
|
||||||
|
ports:
|
||||||
|
- '3000:3000'
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- DATABASE_URL=/app/data/sqlite.db
|
||||||
|
- NEXTAUTH_URL=https://lcc-tt-booking.mikicvi.com
|
||||||
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||||
|
- EMAIL_USER=${EMAIL_USER}
|
||||||
|
- EMAIL_PASSWORD=${EMAIL_PASSWORD}
|
||||||
|
- ADMIN_EMAIL=${ADMIN_EMAIL}
|
||||||
|
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||||
|
- PORT=3000
|
||||||
|
- RATE_LIMIT_MAX=${RATE_LIMIT_MAX:-100}
|
||||||
|
- RATE_LIMIT_WINDOW=${RATE_LIMIT_WINDOW:-900000}
|
||||||
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
- ./backups:/app/backups
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/health']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 60s
|
||||||
|
networks:
|
||||||
|
- lcc-network
|
||||||
|
|
||||||
|
# Automated backup service
|
||||||
|
backup:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: lcc-backup
|
||||||
|
volumes:
|
||||||
|
- ./data:/data:ro
|
||||||
|
- ./backups:/backups
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Dublin
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
apk add --no-cache tzdata &&
|
||||||
|
while true; do
|
||||||
|
timestamp=$$(date +%Y%m%d-%H%M%S)
|
||||||
|
echo \"Creating backup at $$timestamp\"
|
||||||
|
cp /data/sqlite.db \"/backups/sqlite-$$timestamp.db\" 2>/dev/null || echo 'No database file found yet'
|
||||||
|
# Keep backups for 30 days
|
||||||
|
find /backups -name 'sqlite-*.db' -mtime +30 -delete 2>/dev/null || true
|
||||||
|
echo \"Backup completed, sleeping for 24 hours\"
|
||||||
|
sleep 86400
|
||||||
|
done"
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- tt-booking
|
||||||
|
networks:
|
||||||
|
- lcc-network
|
||||||
|
|
||||||
|
# Log rotation service
|
||||||
|
logrotate:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: lcc-logrotate
|
||||||
|
volumes:
|
||||||
|
- ./logs:/logs
|
||||||
|
command: >
|
||||||
|
sh -c "
|
||||||
|
apk add --no-cache logrotate &&
|
||||||
|
echo '/logs/*.log {
|
||||||
|
daily
|
||||||
|
missingok
|
||||||
|
rotate 30
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
notifempty
|
||||||
|
}' > /etc/logrotate.d/app &&
|
||||||
|
while true; do
|
||||||
|
logrotate /etc/logrotate.d/app
|
||||||
|
sleep 86400
|
||||||
|
done"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- lcc-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
lcc-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
lcc-data:
|
||||||
|
driver: local
|
||||||
|
lcc-backups:
|
||||||
|
driver: local
|
||||||
+1
-1
@@ -5,6 +5,6 @@ export default {
|
|||||||
out: './lib/db/migrations',
|
out: './lib/db/migrations',
|
||||||
driver: 'better-sqlite',
|
driver: 'better-sqlite',
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: './sqlite.db',
|
url: process.env.DATABASE_URL || './data/sqlite.db',
|
||||||
},
|
},
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|||||||
+3
-1
@@ -3,7 +3,9 @@ import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|||||||
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
|
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
|
||||||
import * as schema from './schema';
|
import * as schema from './schema';
|
||||||
|
|
||||||
const sqlite = new Database('./sqlite.db');
|
// Get database path from environment variable or use default
|
||||||
|
const dbPath = process.env.DATABASE_URL || './data/sqlite.db';
|
||||||
|
const sqlite = new Database(dbPath);
|
||||||
export const db = drizzle(sqlite, { schema });
|
export const db = drizzle(sqlite, { schema });
|
||||||
|
|
||||||
// Only run migrations if explicitly requested
|
// Only run migrations if explicitly requested
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
// Enable standalone output for Docker production builds
|
||||||
|
output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined,
|
||||||
|
// External packages for better SQLite3 compatibility (Next.js 15+ syntax)
|
||||||
|
serverExternalPackages: ['better-sqlite3'],
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { sql, eq } from 'drizzle-orm';
|
|||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
const sqlite = new Database('./sqlite.db');
|
const dbPath = process.env.DATABASE_URL || './data/sqlite.db';
|
||||||
|
const sqlite = new Database(dbPath);
|
||||||
const db = drizzle(sqlite, { schema });
|
const db = drizzle(sqlite, { schema });
|
||||||
|
|
||||||
interface SetupOptions {
|
interface SetupOptions {
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
// Test script to verify Irish localization settings
|
|
||||||
console.log('🇮🇪 Testing Irish Localization Settings\n');
|
|
||||||
|
|
||||||
console.log('1. Week starts on Monday (Irish standard)');
|
|
||||||
console.log(' JavaScript getDay() values:');
|
|
||||||
console.log(' Sunday = 0, Monday = 1, ..., Saturday = 6');
|
|
||||||
console.log(' Irish display order should be: Monday, Tuesday, ..., Sunday\n');
|
|
||||||
|
|
||||||
// Test date formatting
|
|
||||||
const testDate = new Date('2025-09-25'); // This is a Thursday
|
|
||||||
console.log('2. Date Formatting Test:');
|
|
||||||
console.log(` Test date: ${testDate.toDateString()}`);
|
|
||||||
console.log(
|
|
||||||
` Irish format (en-IE): ${testDate.toLocaleDateString('en-IE', {
|
|
||||||
weekday: 'long',
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric',
|
|
||||||
})}`
|
|
||||||
);
|
|
||||||
console.log(` Irish short format: ${testDate.toLocaleDateString('en-IE', { weekday: 'short' })}`);
|
|
||||||
|
|
||||||
console.log('\n3. Day of Week Conversion:');
|
|
||||||
const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
||||||
for (let jsDay = 0; jsDay <= 6; jsDay++) {
|
|
||||||
const irishDisplayOrder = jsDay === 0 ? 6 : jsDay - 1; // Convert Sunday(0) to position 6, others shift down
|
|
||||||
console.log(` JS Day ${jsDay} (${daysOfWeek[jsDay]}) -> Irish position ${irishDisplayOrder}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n4. Week Structure for Admin Panel:');
|
|
||||||
const irishWeekOrder = [1, 2, 3, 4, 5, 6, 0]; // Monday through Sunday in JS values
|
|
||||||
irishWeekOrder.forEach((jsDay, displayIndex) => {
|
|
||||||
console.log(` Display position ${displayIndex}: ${daysOfWeek[jsDay]} (JS day ${jsDay})`);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('\n✅ Irish localization configuration complete!');
|
|
||||||
console.log('📅 Calendar will now start with Monday');
|
|
||||||
console.log('🇮🇪 All dates will use en-IE locale format');
|
|
||||||
console.log('⏰ 24-hour time format maintained');
|
|
||||||
Executable
+67
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Cloudflare Tunnel Setup Script for LCC Table Tennis Booking
|
||||||
|
# Domain: lcc-tt-booking.mikicvi.com
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DOMAIN="lcc-tt-booking.mikicvi.com"
|
||||||
|
TUNNEL_NAME="lcc-tt-booking"
|
||||||
|
|
||||||
|
echo "🌐 Setting up Cloudflare Tunnel for $DOMAIN"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if cloudflared is installed
|
||||||
|
if ! command -v cloudflared &> /dev/null; then
|
||||||
|
echo "📥 Installing cloudflared..."
|
||||||
|
|
||||||
|
# Detect architecture
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
|
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64"
|
||||||
|
elif [ "$ARCH" = "armv7l" ]; then
|
||||||
|
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm"
|
||||||
|
else
|
||||||
|
CLOUDFLARED_URL="https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Download and install
|
||||||
|
curl -L --output cloudflared "$CLOUDFLARED_URL"
|
||||||
|
sudo mv cloudflared /usr/local/bin
|
||||||
|
sudo chmod +x /usr/local/bin/cloudflared
|
||||||
|
|
||||||
|
echo "✅ cloudflared installed successfully!"
|
||||||
|
else
|
||||||
|
echo "✅ cloudflared is already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🔐 Please follow these steps:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Authenticate with Cloudflare:"
|
||||||
|
echo " cloudflared tunnel login"
|
||||||
|
echo ""
|
||||||
|
echo "2. Create the tunnel:"
|
||||||
|
echo " cloudflared tunnel create $TUNNEL_NAME"
|
||||||
|
echo ""
|
||||||
|
echo "3. Copy the tunnel ID from the output and update cloudflare-tunnel-config.yml"
|
||||||
|
echo ""
|
||||||
|
echo "4. Create DNS record:"
|
||||||
|
echo " cloudflared tunnel route dns $TUNNEL_NAME $DOMAIN"
|
||||||
|
echo ""
|
||||||
|
echo "5. Copy the config to cloudflared directory:"
|
||||||
|
echo " mkdir -p ~/.cloudflared"
|
||||||
|
echo " cp cloudflare-tunnel-config.yml ~/.cloudflared/config.yml"
|
||||||
|
echo " # Update <your-tunnel-id> in the config file with your actual tunnel ID"
|
||||||
|
echo ""
|
||||||
|
echo "6. Test the tunnel:"
|
||||||
|
echo " cloudflared tunnel run $TUNNEL_NAME"
|
||||||
|
echo ""
|
||||||
|
echo "7. Install as a service (optional):"
|
||||||
|
echo " sudo cloudflared service install"
|
||||||
|
echo " sudo systemctl enable cloudflared"
|
||||||
|
echo " sudo systemctl start cloudflared"
|
||||||
|
echo ""
|
||||||
|
echo "📋 Tunnel configuration template is available in: cloudflare-tunnel-config.yml"
|
||||||
|
echo "🚀 After setup, your app will be available at: https://$DOMAIN"
|
||||||
|
echo ""
|
||||||
Reference in New Issue
Block a user