34 changed files with 3331 additions and 552 deletions
@ -0,0 +1,171 @@ |
|||
# TimeSafari Docker Ignore File |
|||
# Author: Matthew Raymer |
|||
# Description: Excludes unnecessary files from Docker build context |
|||
# |
|||
# Benefits: |
|||
# - Faster build times |
|||
# - Smaller build context |
|||
# - Reduced image size |
|||
# - Better security (excludes sensitive files) |
|||
|
|||
# Dependencies |
|||
node_modules |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
|
|||
# Build outputs |
|||
dist |
|||
dist-* |
|||
build |
|||
*.tsbuildinfo |
|||
|
|||
# Development files |
|||
.git |
|||
.gitignore |
|||
README.md |
|||
CHANGELOG.md |
|||
CONTRIBUTING.md |
|||
BUILDING.md |
|||
LICENSE |
|||
|
|||
# IDE and editor files |
|||
.vscode |
|||
.idea |
|||
*.swp |
|||
*.swo |
|||
*~ |
|||
|
|||
# OS generated files |
|||
.DS_Store |
|||
.DS_Store? |
|||
._* |
|||
.Spotlight-V100 |
|||
.Trashes |
|||
ehthumbs.db |
|||
Thumbs.db |
|||
|
|||
# Logs |
|||
logs |
|||
*.log |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
*.pid.lock |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
*.lcov |
|||
|
|||
# nyc test coverage |
|||
.nyc_output |
|||
|
|||
# Dependency directories |
|||
jspm_packages/ |
|||
|
|||
# Optional npm cache directory |
|||
.npm |
|||
|
|||
# Optional eslint cache |
|||
.eslintcache |
|||
|
|||
# Optional REPL history |
|||
.node_repl_history |
|||
|
|||
# Output of 'npm pack' |
|||
*.tgz |
|||
|
|||
# Yarn Integrity file |
|||
.yarn-integrity |
|||
|
|||
# dotenv environment variables file |
|||
.env |
|||
.env.local |
|||
.env.development.local |
|||
.env.test.local |
|||
.env.production.local |
|||
|
|||
# parcel-bundler cache (https://parceljs.org/) |
|||
.cache |
|||
.parcel-cache |
|||
|
|||
# next.js build output |
|||
.next |
|||
|
|||
# nuxt.js build output |
|||
.nuxt |
|||
|
|||
# vuepress build output |
|||
.vuepress/dist |
|||
|
|||
# Serverless directories |
|||
.serverless |
|||
|
|||
# FuseBox cache |
|||
.fusebox/ |
|||
|
|||
# DynamoDB Local files |
|||
.dynamodb/ |
|||
|
|||
# TernJS port file |
|||
.tern-port |
|||
|
|||
# Stores VSCode versions used for testing VSCode extensions |
|||
.vscode-test |
|||
|
|||
# Test files |
|||
test-playwright |
|||
test-playwright-results |
|||
test-results |
|||
test-scripts |
|||
|
|||
# Documentation |
|||
doc |
|||
|
|||
# Scripts (keep only what's needed for build) |
|||
scripts/test-*.sh |
|||
scripts/*.js |
|||
scripts/README.md |
|||
|
|||
# Platform-specific files |
|||
android |
|||
ios |
|||
electron |
|||
|
|||
# Docker files (avoid recursive copying) |
|||
Dockerfile* |
|||
docker-compose* |
|||
.dockerignore |
|||
|
|||
# CI/CD files |
|||
.github |
|||
.gitlab-ci.yml |
|||
.travis.yml |
|||
.circleci |
|||
|
|||
# Temporary files |
|||
tmp |
|||
temp |
|||
|
|||
# Backup files |
|||
*.bak |
|||
*.backup |
|||
|
|||
# Archive files |
|||
*.tar |
|||
*.tar.gz |
|||
*.zip |
|||
*.rar |
|||
|
|||
# Certificate files |
|||
*.pem |
|||
*.key |
|||
*.crt |
|||
*.p12 |
|||
|
|||
# Configuration files that might contain secrets |
|||
*.secrets |
|||
secrets.json |
|||
config.local.json |
@ -1,36 +1,209 @@ |
|||
# Build stage |
|||
FROM node:22-alpine3.20 AS builder |
|||
# TimeSafari Docker Build |
|||
# Author: Matthew Raymer |
|||
# Description: Multi-stage Docker build for TimeSafari web application |
|||
# |
|||
# Build Process: |
|||
# 1. Base stage: Node.js with build dependencies |
|||
# 2. Builder stage: Compile web assets with Vite |
|||
# 3. Production stage: Nginx server with optimized assets |
|||
# |
|||
# Security Features: |
|||
# - Non-root user execution |
|||
# - Minimal attack surface with Alpine Linux |
|||
# - Multi-stage build to reduce image size |
|||
# - No build dependencies in final image |
|||
# |
|||
# Usage: |
|||
# Production: docker build -t timesafari:latest . |
|||
# Staging: docker build --build-arg BUILD_MODE=staging -t timesafari:staging . |
|||
# Development: docker build --build-arg BUILD_MODE=development -t timesafari:dev . |
|||
# |
|||
# Build Arguments: |
|||
# BUILD_MODE: development, staging, or production (default: production) |
|||
# NODE_ENV: node environment (default: production) |
|||
# VITE_PLATFORM: vite platform (default: web) |
|||
# VITE_PWA_ENABLED: enable PWA (default: true) |
|||
# VITE_DISABLE_PWA: disable PWA (default: false) |
|||
# |
|||
# Environment Variables: |
|||
# NODE_ENV: Build environment (development/production) |
|||
# VITE_APP_SERVER: Application server URL |
|||
# VITE_DEFAULT_ENDORSER_API_SERVER: Endorser API server URL |
|||
# VITE_DEFAULT_IMAGE_API_SERVER: Image API server URL |
|||
# VITE_DEFAULT_PARTNER_API_SERVER: Partner API server URL |
|||
# VITE_DEFAULT_PUSH_SERVER: Push notification server URL |
|||
# VITE_PASSKEYS_ENABLED: Enable passkeys feature |
|||
|
|||
# Install build dependencies |
|||
# ============================================================================= |
|||
# BASE STAGE - Common dependencies and setup |
|||
# ============================================================================= |
|||
FROM node:22-alpine3.20 AS base |
|||
|
|||
RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc |
|||
# Install system dependencies for build process |
|||
RUN apk add --no-cache \ |
|||
bash \ |
|||
git \ |
|||
python3 \ |
|||
py3-pip \ |
|||
py3-setuptools \ |
|||
make \ |
|||
g++ \ |
|||
gcc \ |
|||
&& rm -rf /var/cache/apk/* |
|||
|
|||
# Create non-root user for security |
|||
RUN addgroup -g 1001 -S nodejs && \ |
|||
adduser -S nextjs -u 1001 |
|||
|
|||
# Set working directory |
|||
WORKDIR /app |
|||
|
|||
# Copy package files |
|||
# Copy package files for dependency installation |
|||
COPY package*.json ./ |
|||
|
|||
# Install dependencies |
|||
RUN npm ci |
|||
# Install dependencies with security audit |
|||
RUN npm ci --only=production --audit --fund=false && \ |
|||
npm audit fix --audit-level=moderate || true |
|||
|
|||
# ============================================================================= |
|||
# BUILDER STAGE - Compile web assets |
|||
# ============================================================================= |
|||
FROM base AS builder |
|||
|
|||
# Define build arguments with defaults |
|||
ARG BUILD_MODE=production |
|||
ARG NODE_ENV=production |
|||
ARG VITE_PLATFORM=web |
|||
ARG VITE_PWA_ENABLED=true |
|||
ARG VITE_DISABLE_PWA=false |
|||
|
|||
# Set environment variables from build arguments |
|||
ENV BUILD_MODE=${BUILD_MODE} |
|||
ENV NODE_ENV=${NODE_ENV} |
|||
ENV VITE_PLATFORM=${VITE_PLATFORM} |
|||
ENV VITE_PWA_ENABLED=${VITE_PWA_ENABLED} |
|||
ENV VITE_DISABLE_PWA=${VITE_DISABLE_PWA} |
|||
|
|||
# Install all dependencies (including dev dependencies) |
|||
RUN npm ci --audit --fund=false && \ |
|||
npm audit fix --audit-level=moderate || true |
|||
|
|||
# Copy source code |
|||
COPY . . |
|||
|
|||
# Build the application |
|||
RUN npm run build:web |
|||
# Build the application with proper error handling |
|||
RUN echo "Building TimeSafari in ${BUILD_MODE} mode..." && \ |
|||
npm run build:web || (echo "Build failed. Check the logs above." && exit 1) |
|||
|
|||
# Verify build output exists |
|||
RUN ls -la dist/ || (echo "Build output not found in dist/ directory" && exit 1) |
|||
|
|||
# ============================================================================= |
|||
# PRODUCTION STAGE - Nginx server |
|||
# ============================================================================= |
|||
FROM nginx:alpine AS production |
|||
|
|||
# Define build arguments for production stage |
|||
ARG BUILD_MODE=production |
|||
ARG NODE_ENV=production |
|||
|
|||
# Set environment variables |
|||
ENV BUILD_MODE=${BUILD_MODE} |
|||
ENV NODE_ENV=${NODE_ENV} |
|||
|
|||
# Production stage |
|||
FROM nginx:alpine |
|||
# Install security updates and clean cache |
|||
RUN apk update && \ |
|||
apk upgrade && \ |
|||
apk add --no-cache \ |
|||
curl \ |
|||
&& rm -rf /var/cache/apk/* |
|||
|
|||
# Create non-root user for nginx |
|||
RUN addgroup -g 1001 -S nginx && \ |
|||
adduser -S nginx -u 1001 -G nginx |
|||
|
|||
# Copy appropriate nginx configuration based on build mode |
|||
COPY docker/nginx.conf /etc/nginx/nginx.conf |
|||
COPY docker/default.conf /etc/nginx/conf.d/default.conf |
|||
|
|||
# Copy staging configuration if needed |
|||
COPY docker/staging.conf /etc/nginx/conf.d/staging.conf |
|||
|
|||
# Copy built assets from builder stage |
|||
COPY --from=builder /app/dist /usr/share/nginx/html |
|||
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html |
|||
|
|||
# Create necessary directories with proper permissions |
|||
RUN mkdir -p /var/cache/nginx /var/log/nginx /var/run && \ |
|||
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /var/run && \ |
|||
chown -R nginx:nginx /usr/share/nginx/html |
|||
|
|||
# Copy nginx configuration if needed |
|||
# COPY nginx.conf /etc/nginx/conf.d/default.conf |
|||
# Switch to non-root user |
|||
USER nginx |
|||
|
|||
# Expose port 80 |
|||
EXPOSE 80 |
|||
|
|||
# Health check |
|||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ |
|||
CMD curl -f http://localhost/ || exit 1 |
|||
|
|||
# Start nginx with proper signal handling |
|||
CMD ["nginx", "-g", "daemon off;"] |
|||
|
|||
# ============================================================================= |
|||
# DEVELOPMENT STAGE - For development with hot reloading |
|||
# ============================================================================= |
|||
FROM base AS development |
|||
|
|||
# Define build arguments for development stage |
|||
ARG BUILD_MODE=development |
|||
ARG NODE_ENV=development |
|||
ARG VITE_PLATFORM=web |
|||
ARG VITE_PWA_ENABLED=true |
|||
ARG VITE_DISABLE_PWA=false |
|||
|
|||
# Set environment variables |
|||
ENV BUILD_MODE=${BUILD_MODE} |
|||
ENV NODE_ENV=${NODE_ENV} |
|||
ENV VITE_PLATFORM=${VITE_PLATFORM} |
|||
ENV VITE_PWA_ENABLED=${VITE_PWA_ENABLED} |
|||
ENV VITE_DISABLE_PWA=${VITE_DISABLE_PWA} |
|||
|
|||
# Install all dependencies including dev dependencies |
|||
RUN npm ci --audit --fund=false && \ |
|||
npm audit fix --audit-level=moderate || true |
|||
|
|||
# Copy source code |
|||
COPY . . |
|||
|
|||
# Expose development port |
|||
EXPOSE 5173 |
|||
|
|||
# Start development server |
|||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] |
|||
|
|||
# ============================================================================= |
|||
# STAGING STAGE - For staging environment testing |
|||
# ============================================================================= |
|||
FROM production AS staging |
|||
|
|||
# Define build arguments for staging stage |
|||
ARG BUILD_MODE=staging |
|||
ARG NODE_ENV=staging |
|||
|
|||
# Set environment variables |
|||
ENV BUILD_MODE=${BUILD_MODE} |
|||
ENV NODE_ENV=${NODE_ENV} |
|||
|
|||
# Copy staging-specific nginx configuration |
|||
COPY docker/staging.conf /etc/nginx/conf.d/default.conf |
|||
|
|||
# Expose port 80 |
|||
EXPOSE 80 |
|||
|
|||
# Health check for staging |
|||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ |
|||
CMD curl -f http://localhost/health || exit 1 |
|||
|
|||
# Start nginx |
|||
CMD ["nginx", "-g", "daemon off;"] |
@ -0,0 +1,210 @@ |
|||
# TimeSafari Docker Compose Configuration |
|||
# Author: Matthew Raymer |
|||
# Description: Multi-environment Docker Compose setup for TimeSafari |
|||
# |
|||
# Usage: |
|||
# Development: docker-compose up dev |
|||
# Staging: docker-compose up staging |
|||
# Production: docker-compose up production |
|||
# Custom: BUILD_MODE=staging docker-compose up custom |
|||
# |
|||
# Environment Variables: |
|||
# BUILD_MODE: development, staging, or production (default: production) |
|||
# NODE_ENV: node environment (default: production) |
|||
# VITE_PLATFORM: vite platform (default: web) |
|||
# VITE_PWA_ENABLED: enable PWA (default: true) |
|||
# VITE_DISABLE_PWA: disable PWA (default: false) |
|||
# PORT: port to expose (default: 80 for production, 5173 for dev) |
|||
# ENV_FILE: environment file to use (default: .env.production) |
|||
# |
|||
# See .env files for application-specific configuration |
|||
# VITE_APP_SERVER: Application server URL |
|||
# VITE_DEFAULT_ENDORSER_API_SERVER: Endorser API server URL |
|||
|
|||
version: '3.8' |
|||
|
|||
# Default values that can be overridden |
|||
x-defaults: &defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
args: |
|||
BUILD_MODE: ${BUILD_MODE:-production} |
|||
NODE_ENV: ${NODE_ENV:-production} |
|||
VITE_PLATFORM: ${VITE_PLATFORM:-web} |
|||
VITE_PWA_ENABLED: ${VITE_PWA_ENABLED:-true} |
|||
VITE_DISABLE_PWA: ${VITE_DISABLE_PWA:-false} |
|||
restart: unless-stopped |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "http://localhost/health"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 40s |
|||
|
|||
services: |
|||
# Development service with hot reloading |
|||
dev: |
|||
<<: *defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
target: development |
|||
args: |
|||
BUILD_MODE: development |
|||
NODE_ENV: development |
|||
VITE_PLATFORM: web |
|||
VITE_PWA_ENABLED: true |
|||
VITE_DISABLE_PWA: false |
|||
ports: |
|||
- "${DEV_PORT:-5173}:5173" |
|||
volumes: |
|||
- .:/app |
|||
- /app/node_modules |
|||
environment: |
|||
- NODE_ENV=development |
|||
- VITE_PLATFORM=web |
|||
- VITE_PWA_ENABLED=true |
|||
- VITE_DISABLE_PWA=false |
|||
env_file: |
|||
- ${DEV_ENV_FILE:-.env.development} |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "http://localhost:5173"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 40s |
|||
|
|||
# Staging service for testing |
|||
staging: |
|||
<<: *defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
target: staging |
|||
args: |
|||
BUILD_MODE: staging |
|||
NODE_ENV: staging |
|||
VITE_PLATFORM: web |
|||
VITE_PWA_ENABLED: true |
|||
VITE_DISABLE_PWA: false |
|||
ports: |
|||
- "${STAGING_PORT:-8080}:80" |
|||
environment: |
|||
- NODE_ENV=staging |
|||
- VITE_PLATFORM=web |
|||
- VITE_PWA_ENABLED=true |
|||
- VITE_DISABLE_PWA=false |
|||
env_file: |
|||
- ${STAGING_ENV_FILE:-.env.staging} |
|||
|
|||
# Production service |
|||
production: |
|||
<<: *defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
target: production |
|||
args: |
|||
BUILD_MODE: production |
|||
NODE_ENV: production |
|||
VITE_PLATFORM: web |
|||
VITE_PWA_ENABLED: true |
|||
VITE_DISABLE_PWA: false |
|||
ports: |
|||
- "${PROD_PORT:-80}:80" |
|||
environment: |
|||
- NODE_ENV=production |
|||
- VITE_PLATFORM=web |
|||
- VITE_PWA_ENABLED=true |
|||
- VITE_DISABLE_PWA=false |
|||
env_file: |
|||
- ${PROD_ENV_FILE:-.env.production} |
|||
|
|||
# Production service with SSL (requires certificates) |
|||
production-ssl: |
|||
<<: *defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
target: production |
|||
args: |
|||
BUILD_MODE: production |
|||
NODE_ENV: production |
|||
VITE_PLATFORM: web |
|||
VITE_PWA_ENABLED: true |
|||
VITE_DISABLE_PWA: false |
|||
ports: |
|||
- "${SSL_PORT:-443}:443" |
|||
- "${HTTP_PORT:-80}:80" |
|||
environment: |
|||
- NODE_ENV=production |
|||
- VITE_PLATFORM=web |
|||
- VITE_PWA_ENABLED=true |
|||
- VITE_DISABLE_PWA=false |
|||
env_file: |
|||
- ${PROD_ENV_FILE:-.env.production} |
|||
volumes: |
|||
- ./ssl:/etc/nginx/ssl:ro |
|||
- ./docker/nginx-ssl.conf:/etc/nginx/conf.d/default.conf:ro |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "https://localhost/health"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 40s |
|||
|
|||
# Custom service - configurable via environment variables |
|||
custom: |
|||
<<: *defaults |
|||
build: |
|||
context: . |
|||
dockerfile: Dockerfile |
|||
target: ${BUILD_TARGET:-production} |
|||
args: |
|||
BUILD_MODE: ${BUILD_MODE:-production} |
|||
NODE_ENV: ${NODE_ENV:-production} |
|||
VITE_PLATFORM: ${VITE_PLATFORM:-web} |
|||
VITE_PWA_ENABLED: ${VITE_PWA_ENABLED:-true} |
|||
VITE_DISABLE_PWA: ${VITE_DISABLE_PWA:-false} |
|||
ports: |
|||
- "${CUSTOM_PORT:-8080}:${CUSTOM_INTERNAL_PORT:-80}" |
|||
environment: |
|||
- NODE_ENV=${NODE_ENV:-production} |
|||
- VITE_PLATFORM=${VITE_PLATFORM:-web} |
|||
- VITE_PWA_ENABLED=${VITE_PWA_ENABLED:-true} |
|||
- VITE_DISABLE_PWA=${VITE_DISABLE_PWA:-false} |
|||
env_file: |
|||
- ${CUSTOM_ENV_FILE:-.env.production} |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "http://localhost:${CUSTOM_INTERNAL_PORT:-80}/health"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 40s |
|||
|
|||
# Load balancer for production (optional) |
|||
nginx-lb: |
|||
image: nginx:alpine |
|||
ports: |
|||
- "${LB_PORT:-80}:80" |
|||
- "${LB_SSL_PORT:-443}:443" |
|||
volumes: |
|||
- ./docker/nginx-lb.conf:/etc/nginx/nginx.conf:ro |
|||
- ./ssl:/etc/nginx/ssl:ro |
|||
depends_on: |
|||
- production |
|||
restart: unless-stopped |
|||
healthcheck: |
|||
test: ["CMD", "curl", "-f", "http://localhost/health"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 40s |
|||
|
|||
networks: |
|||
default: |
|||
driver: bridge |
|||
ipam: |
|||
config: |
|||
- subnet: 172.20.0.0/16 |
@ -0,0 +1,509 @@ |
|||
# TimeSafari Docker Setup |
|||
|
|||
## Overview |
|||
|
|||
This directory contains Docker configuration files for building and deploying TimeSafari across different environments with full configurability. |
|||
|
|||
## Files |
|||
|
|||
- `Dockerfile` - Multi-stage Docker build for TimeSafari |
|||
- `nginx.conf` - Main nginx configuration with security headers |
|||
- `default.conf` - Production server configuration |
|||
- `staging.conf` - Staging server configuration with relaxed caching |
|||
- `docker-compose.yml` - Multi-environment Docker Compose setup |
|||
- `.dockerignore` - Optimizes build context |
|||
- `run.sh` - Convenient script to run different configurations |
|||
|
|||
## Quick Start |
|||
|
|||
### Using the Run Script (Recommended) |
|||
|
|||
```bash |
|||
# Development mode with hot reloading |
|||
./docker/run.sh dev |
|||
|
|||
# Staging mode for testing |
|||
./docker/run.sh staging |
|||
|
|||
# Production mode |
|||
./docker/run.sh production |
|||
|
|||
# Custom mode with environment variables |
|||
BUILD_MODE=staging ./docker/run.sh custom |
|||
|
|||
# Show build arguments for a mode |
|||
./docker/run.sh dev --build-args |
|||
|
|||
# Custom port and environment file |
|||
./docker/run.sh staging --port 9000 --env .env.test |
|||
``` |
|||
|
|||
### Using Docker Compose |
|||
|
|||
```bash |
|||
# Development environment with hot reloading |
|||
docker-compose up dev |
|||
|
|||
# Staging environment |
|||
docker-compose up staging |
|||
|
|||
# Production environment |
|||
docker-compose up production |
|||
|
|||
# Custom environment with variables |
|||
BUILD_MODE=staging docker-compose up custom |
|||
``` |
|||
|
|||
## Build Commands |
|||
|
|||
### Manual Docker Build |
|||
|
|||
```bash |
|||
# Build production image (default) |
|||
docker build -t timesafari:latest . |
|||
|
|||
# Build staging image |
|||
docker build --build-arg BUILD_MODE=staging -t timesafari:staging . |
|||
|
|||
# Build development image |
|||
docker build --build-arg BUILD_MODE=development -t timesafari:dev . |
|||
|
|||
# Build with custom arguments |
|||
docker build \ |
|||
--build-arg BUILD_MODE=staging \ |
|||
--build-arg NODE_ENV=staging \ |
|||
--build-arg VITE_PWA_ENABLED=true \ |
|||
-t timesafari:custom . |
|||
``` |
|||
|
|||
### Run Container |
|||
|
|||
```bash |
|||
# Run production container |
|||
docker run -d -p 80:80 timesafari:latest |
|||
|
|||
# Run with environment file |
|||
docker run -d -p 80:80 --env-file .env.production timesafari:latest |
|||
|
|||
# Run with custom environment variables |
|||
docker run -d -p 80:80 \ |
|||
-e VITE_APP_SERVER=https://myapp.com \ |
|||
-e VITE_DEFAULT_ENDORSER_API_SERVER=https://api.myapp.com \ |
|||
timesafari:latest |
|||
``` |
|||
|
|||
## Configuration Options |
|||
|
|||
### Build Arguments |
|||
|
|||
The Dockerfile supports these build arguments: |
|||
|
|||
| Argument | Default | Description | |
|||
|----------|---------|-------------| |
|||
| `BUILD_MODE` | `production` | Build mode: development, staging, or production | |
|||
| `NODE_ENV` | `production` | Node.js environment | |
|||
| `VITE_PLATFORM` | `web` | Vite platform type | |
|||
| `VITE_PWA_ENABLED` | `true` | Enable PWA features | |
|||
| `VITE_DISABLE_PWA` | `false` | Disable PWA features | |
|||
|
|||
### Environment Variables |
|||
|
|||
Docker Compose supports these environment variables: |
|||
|
|||
| Variable | Default | Description | |
|||
|----------|---------|-------------| |
|||
| `BUILD_MODE` | `production` | Build mode | |
|||
| `NODE_ENV` | `production` | Node environment | |
|||
| `VITE_PLATFORM` | `web` | Vite platform | |
|||
| `VITE_PWA_ENABLED` | `true` | Enable PWA | |
|||
| `VITE_DISABLE_PWA` | `false` | Disable PWA | |
|||
| `DEV_PORT` | `5173` | Development port | |
|||
| `STAGING_PORT` | `8080` | Staging port | |
|||
| `PROD_PORT` | `80` | Production port | |
|||
| `DEV_ENV_FILE` | `.env.development` | Development env file | |
|||
| `STAGING_ENV_FILE` | `.env.staging` | Staging env file | |
|||
| `PROD_ENV_FILE` | `.env.production` | Production env file | |
|||
|
|||
### Environment Files |
|||
|
|||
Create environment files for different deployments: |
|||
|
|||
```bash |
|||
# .env.development |
|||
VITE_APP_SERVER=https://dev.timesafari.app |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://dev-api.endorser.ch |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://dev-image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://dev-partner-api.endorser.ch |
|||
VITE_DEFAULT_PUSH_SERVER=https://dev.timesafari.app |
|||
VITE_PASSKEYS_ENABLED=true |
|||
|
|||
# .env.staging |
|||
VITE_APP_SERVER=https://staging.timesafari.app |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://staging-api.endorser.ch |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://staging-image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://staging-partner-api.endorser.ch |
|||
VITE_DEFAULT_PUSH_SERVER=https://staging.timesafari.app |
|||
VITE_PASSKEYS_ENABLED=true |
|||
|
|||
# .env.production |
|||
VITE_APP_SERVER=https://timesafari.app |
|||
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch |
|||
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app |
|||
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch |
|||
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app |
|||
VITE_PASSKEYS_ENABLED=true |
|||
``` |
|||
|
|||
## Build Modes |
|||
|
|||
### Development Mode |
|||
- **Target**: `development` |
|||
- **Features**: Hot reloading, development server |
|||
- **Port**: 5173 |
|||
- **Caching**: Disabled |
|||
- **Use Case**: Local development |
|||
|
|||
```bash |
|||
./docker/run.sh dev |
|||
# or |
|||
docker build --target development -t timesafari:dev . |
|||
``` |
|||
|
|||
### Staging Mode |
|||
- **Target**: `staging` |
|||
- **Features**: Production build with relaxed caching |
|||
- **Port**: 8080 (mapped from 80) |
|||
- **Caching**: Short-term (1 hour) |
|||
- **Use Case**: Testing and QA |
|||
|
|||
```bash |
|||
./docker/run.sh staging |
|||
# or |
|||
docker build --build-arg BUILD_MODE=staging -t timesafari:staging . |
|||
``` |
|||
|
|||
### Production Mode |
|||
- **Target**: `production` |
|||
- **Features**: Optimized production build |
|||
- **Port**: 80 |
|||
- **Caching**: Long-term (1 year for assets) |
|||
- **Use Case**: Live deployment |
|||
|
|||
```bash |
|||
./docker/run.sh production |
|||
# or |
|||
docker build -t timesafari:latest . |
|||
``` |
|||
|
|||
### Custom Mode |
|||
- **Target**: Configurable via `BUILD_TARGET` |
|||
- **Features**: Fully configurable |
|||
- **Port**: Configurable via `CUSTOM_PORT` |
|||
- **Use Case**: Special deployments |
|||
|
|||
```bash |
|||
BUILD_MODE=staging NODE_ENV=staging ./docker/run.sh custom |
|||
``` |
|||
|
|||
## Advanced Usage |
|||
|
|||
### Custom Build Configuration |
|||
|
|||
```bash |
|||
# Build with specific environment |
|||
docker build \ |
|||
--build-arg BUILD_MODE=staging \ |
|||
--build-arg NODE_ENV=staging \ |
|||
--build-arg VITE_PWA_ENABLED=false \ |
|||
-t timesafari:staging-no-pwa . |
|||
|
|||
# Run with custom configuration |
|||
docker run -d -p 9000:80 \ |
|||
-e VITE_APP_SERVER=https://test.example.com \ |
|||
timesafari:staging-no-pwa |
|||
``` |
|||
|
|||
### Docker Compose with Custom Variables |
|||
|
|||
```bash |
|||
# Set environment variables |
|||
export BUILD_MODE=staging |
|||
export NODE_ENV=staging |
|||
export STAGING_PORT=9000 |
|||
export STAGING_ENV_FILE=.env.test |
|||
|
|||
# Run staging with custom config |
|||
docker-compose up staging |
|||
``` |
|||
|
|||
### Multi-Environment Deployment |
|||
|
|||
```bash |
|||
# Development |
|||
./docker/run.sh dev |
|||
|
|||
# Staging in another terminal |
|||
./docker/run.sh staging --port 8081 |
|||
|
|||
# Production in another terminal |
|||
./docker/run.sh production --port 8082 |
|||
``` |
|||
|
|||
## Security Features |
|||
|
|||
### Built-in Security |
|||
- **Non-root user execution**: All containers run as non-root users |
|||
- **Security headers**: XSS protection, content type options, frame options |
|||
- **Rate limiting**: API request rate limiting |
|||
- **File access restrictions**: Hidden files and backup files blocked |
|||
- **Minimal attack surface**: Alpine Linux base images |
|||
|
|||
### Security Headers |
|||
- `X-Frame-Options: SAMEORIGIN` |
|||
- `X-Content-Type-Options: nosniff` |
|||
- `X-XSS-Protection: 1; mode=block` |
|||
- `Referrer-Policy: strict-origin-when-cross-origin` |
|||
- `Content-Security-Policy`: Comprehensive CSP policy |
|||
|
|||
## Performance Optimizations |
|||
|
|||
### Caching Strategy |
|||
- **Static assets**: 1 year cache with immutable flag (production) |
|||
- **HTML files**: 1 hour cache (production) / no cache (staging) |
|||
- **Service worker**: No cache |
|||
- **Manifest**: 1 day cache (production) / 1 hour cache (staging) |
|||
|
|||
### Compression |
|||
- **Gzip compression**: Enabled for text-based files |
|||
- **Compression level**: 6 (balanced) |
|||
- **Minimum size**: 1024 bytes |
|||
|
|||
### Nginx Optimizations |
|||
- **Sendfile**: Enabled for efficient file serving |
|||
- **TCP optimizations**: nopush and nodelay enabled |
|||
- **Keepalive**: 65 second timeout |
|||
- **Worker processes**: Auto-detected based on CPU cores |
|||
|
|||
## Health Checks |
|||
|
|||
### Built-in Health Checks |
|||
All services include health checks that: |
|||
- Check every 30 seconds |
|||
- Timeout after 10 seconds |
|||
- Retry 3 times before marking unhealthy |
|||
- Start checking after 40 seconds |
|||
|
|||
### Health Check Endpoints |
|||
- **Production/Staging**: `http://localhost/health` |
|||
- **Development**: `http://localhost:5173` |
|||
|
|||
## SSL/HTTPS Setup |
|||
|
|||
### SSL Certificates |
|||
For SSL deployment, create an `ssl` directory with certificates: |
|||
|
|||
```bash |
|||
mkdir ssl |
|||
# Copy your certificates to ssl/ directory |
|||
cp your-cert.pem ssl/ |
|||
cp your-key.pem ssl/ |
|||
``` |
|||
|
|||
### SSL Configuration |
|||
Use the `production-ssl` service in docker-compose: |
|||
|
|||
```bash |
|||
docker-compose up production-ssl |
|||
``` |
|||
|
|||
## Monitoring and Logging |
|||
|
|||
### Log Locations |
|||
- **Access logs**: `/var/log/nginx/access.log` |
|||
- **Error logs**: `/var/log/nginx/error.log` |
|||
|
|||
### Log Format |
|||
``` |
|||
$remote_addr - $remote_user [$time_local] "$request" |
|||
$status $body_bytes_sent "$http_referer" |
|||
"$http_user_agent" "$http_x_forwarded_for" |
|||
``` |
|||
|
|||
### Log Levels |
|||
- **Production**: `warn` level |
|||
- **Staging**: `debug` level |
|||
- **Development**: Full logging |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Common Issues |
|||
|
|||
#### Build Failures |
|||
```bash |
|||
# Check build logs |
|||
docker build -t timesafari:latest . 2>&1 | tee build.log |
|||
|
|||
# Verify dependencies |
|||
docker run --rm timesafari:latest npm list --depth=0 |
|||
|
|||
# Check build arguments |
|||
./docker/run.sh dev --build-args |
|||
``` |
|||
|
|||
#### Container Won't Start |
|||
```bash |
|||
# Check container logs |
|||
docker logs <container_id> |
|||
|
|||
# Check health status |
|||
docker inspect <container_id> | grep -A 10 "Health" |
|||
|
|||
# Verify port availability |
|||
netstat -tulpn | grep :80 |
|||
``` |
|||
|
|||
#### Environment Variables Not Set |
|||
```bash |
|||
# Check environment in container |
|||
docker exec <container_id> env | grep VITE_ |
|||
|
|||
# Verify .env file |
|||
cat .env.production |
|||
|
|||
# Check build arguments |
|||
./docker/run.sh production --build-args |
|||
``` |
|||
|
|||
#### Performance Issues |
|||
```bash |
|||
# Check container resources |
|||
docker stats <container_id> |
|||
|
|||
# Check nginx configuration |
|||
docker exec <container_id> nginx -t |
|||
|
|||
# Monitor access logs |
|||
docker exec <container_id> tail -f /var/log/nginx/access.log |
|||
``` |
|||
|
|||
### Debug Commands |
|||
|
|||
#### Container Debugging |
|||
```bash |
|||
# Enter running container |
|||
docker exec -it <container_id> /bin/sh |
|||
|
|||
# Check nginx status |
|||
docker exec <container_id> nginx -t |
|||
|
|||
# Check file permissions |
|||
docker exec <container_id> ls -la /usr/share/nginx/html |
|||
``` |
|||
|
|||
#### Network Debugging |
|||
```bash |
|||
# Check container network |
|||
docker network inspect bridge |
|||
|
|||
# Test connectivity |
|||
docker exec <container_id> curl -I http://localhost |
|||
|
|||
# Check DNS resolution |
|||
docker exec <container_id> nslookup google.com |
|||
``` |
|||
|
|||
## Production Deployment |
|||
|
|||
### Recommended Production Setup |
|||
1. **Use specific version tags**: `timesafari:1.0.0` |
|||
2. **Implement health checks**: Already included |
|||
3. **Configure proper logging**: Use external log aggregation |
|||
4. **Set up reverse proxy**: Use nginx-lb service |
|||
5. **Use Docker secrets**: For sensitive data |
|||
|
|||
### Production Commands |
|||
```bash |
|||
# Build with specific version |
|||
docker build -t timesafari:1.0.0 . |
|||
|
|||
# Run with production settings |
|||
docker run -d \ |
|||
--name timesafari \ |
|||
-p 80:80 \ |
|||
--restart unless-stopped \ |
|||
--env-file .env.production \ |
|||
timesafari:1.0.0 |
|||
|
|||
# Update production deployment |
|||
docker stop timesafari |
|||
docker rm timesafari |
|||
docker build -t timesafari:1.0.1 . |
|||
docker run -d --name timesafari -p 80:80 --restart unless-stopped --env-file .env.production timesafari:1.0.1 |
|||
``` |
|||
|
|||
## Development Workflow |
|||
|
|||
### Local Development |
|||
```bash |
|||
# Start development environment |
|||
./docker/run.sh dev |
|||
|
|||
# Make changes to code (hot reloading enabled) |
|||
# Access at http://localhost:5173 |
|||
|
|||
# Stop development environment |
|||
docker-compose down dev |
|||
``` |
|||
|
|||
### Testing Changes |
|||
```bash |
|||
# Build and test staging |
|||
./docker/run.sh staging |
|||
|
|||
# Test production build locally |
|||
./docker/run.sh production |
|||
``` |
|||
|
|||
### Continuous Integration |
|||
```bash |
|||
# Build and test in CI |
|||
docker build -t timesafari:test . |
|||
docker run -d --name timesafari-test -p 8080:80 timesafari:test |
|||
|
|||
# Run tests against container |
|||
curl -f http://localhost:8080/health |
|||
|
|||
# Cleanup |
|||
docker stop timesafari-test |
|||
docker rm timesafari-test |
|||
``` |
|||
|
|||
## Best Practices |
|||
|
|||
### Security |
|||
- Always use non-root users |
|||
- Keep base images updated |
|||
- Scan images for vulnerabilities |
|||
- Use secrets for sensitive data |
|||
- Implement proper access controls |
|||
|
|||
### Performance |
|||
- Use multi-stage builds |
|||
- Optimize layer caching |
|||
- Minimize image size |
|||
- Use appropriate base images |
|||
- Implement proper caching |
|||
|
|||
### Monitoring |
|||
- Use health checks |
|||
- Monitor resource usage |
|||
- Set up log aggregation |
|||
- Implement metrics collection |
|||
- Use proper error handling |
|||
|
|||
### Maintenance |
|||
- Regular security updates |
|||
- Monitor for vulnerabilities |
|||
- Keep dependencies updated |
|||
- Document configuration changes |
|||
- Test deployment procedures |
@ -0,0 +1,110 @@ |
|||
# TimeSafari Default Server Configuration |
|||
# Author: Matthew Raymer |
|||
# Description: Production server configuration for TimeSafari web application |
|||
# |
|||
# Features: |
|||
# - Vue.js SPA routing support |
|||
# - Static file caching optimization |
|||
# - Security hardening |
|||
# - Performance optimization |
|||
# - Proper error handling |
|||
|
|||
server { |
|||
listen 80; |
|||
server_name _; |
|||
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 "strict-origin-when-cross-origin" always; |
|||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; |
|||
|
|||
# Handle Vue.js SPA routing |
|||
location / { |
|||
try_files $uri $uri/ /index.html; |
|||
|
|||
# Cache static assets |
|||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { |
|||
expires 1y; |
|||
add_header Cache-Control "public, immutable"; |
|||
add_header Vary "Accept-Encoding"; |
|||
} |
|||
|
|||
# Cache HTML files for a shorter time |
|||
location ~* \.html$ { |
|||
expires 1h; |
|||
add_header Cache-Control "public, must-revalidate"; |
|||
} |
|||
} |
|||
|
|||
# Handle service worker |
|||
location /sw.js { |
|||
expires 0; |
|||
add_header Cache-Control "no-cache, no-store, must-revalidate"; |
|||
add_header Pragma "no-cache"; |
|||
} |
|||
|
|||
# Handle manifest file |
|||
location /manifest.json { |
|||
expires 1d; |
|||
add_header Cache-Control "public"; |
|||
} |
|||
|
|||
# Handle API requests (if needed) |
|||
location /api/ { |
|||
limit_req zone=api burst=20 nodelay; |
|||
proxy_pass http://backend:3000; |
|||
proxy_set_header Host $host; |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_set_header X-Forwarded-Proto $scheme; |
|||
} |
|||
|
|||
# Handle health check |
|||
location /health { |
|||
access_log off; |
|||
return 200 "healthy\n"; |
|||
add_header Content-Type text/plain; |
|||
} |
|||
|
|||
# Handle robots.txt |
|||
location /robots.txt { |
|||
expires 1d; |
|||
add_header Cache-Control "public"; |
|||
} |
|||
|
|||
# Handle favicon |
|||
location /favicon.ico { |
|||
expires 1y; |
|||
add_header Cache-Control "public, immutable"; |
|||
} |
|||
|
|||
# Security: Deny access to hidden files |
|||
location ~ /\. { |
|||
deny all; |
|||
access_log off; |
|||
log_not_found off; |
|||
} |
|||
|
|||
# Security: Deny access to backup files |
|||
location ~ ~$ { |
|||
deny all; |
|||
access_log off; |
|||
log_not_found off; |
|||
} |
|||
|
|||
# Error pages |
|||
error_page 404 /index.html; |
|||
error_page 500 502 503 504 /50x.html; |
|||
|
|||
location = /50x.html { |
|||
root /usr/share/nginx/html; |
|||
} |
|||
|
|||
# Logging |
|||
access_log /var/log/nginx/access.log main; |
|||
error_log /var/log/nginx/error.log warn; |
|||
} |
@ -0,0 +1,72 @@ |
|||
# TimeSafari Nginx Configuration |
|||
# Author: Matthew Raymer |
|||
# Description: Main nginx configuration for TimeSafari web application |
|||
# |
|||
# Features: |
|||
# - Security headers for web application |
|||
# - Gzip compression for better performance |
|||
# - Proper handling of Vue.js SPA routing |
|||
# - Static file caching optimization |
|||
# - Security hardening |
|||
|
|||
user nginx; |
|||
worker_processes auto; |
|||
error_log /var/log/nginx/error.log warn; |
|||
pid /var/run/nginx.pid; |
|||
|
|||
events { |
|||
worker_connections 1024; |
|||
use epoll; |
|||
multi_accept on; |
|||
} |
|||
|
|||
http { |
|||
include /etc/nginx/mime.types; |
|||
default_type application/octet-stream; |
|||
|
|||
# Logging format |
|||
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; |
|||
|
|||
# Performance optimizations |
|||
sendfile on; |
|||
tcp_nopush on; |
|||
tcp_nodelay on; |
|||
keepalive_timeout 65; |
|||
types_hash_max_size 2048; |
|||
client_max_body_size 16M; |
|||
|
|||
# Gzip compression |
|||
gzip on; |
|||
gzip_vary on; |
|||
gzip_min_length 1024; |
|||
gzip_proxied any; |
|||
gzip_comp_level 6; |
|||
gzip_types |
|||
text/plain |
|||
text/css |
|||
text/xml |
|||
text/javascript |
|||
application/json |
|||
application/javascript |
|||
application/xml+rss |
|||
application/atom+xml |
|||
image/svg+xml; |
|||
|
|||
# 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 "strict-origin-when-cross-origin" always; |
|||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'self';" always; |
|||
|
|||
# Rate limiting |
|||
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; |
|||
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; |
|||
|
|||
# Include server configurations |
|||
include /etc/nginx/conf.d/*.conf; |
|||
} |
@ -0,0 +1,272 @@ |
|||
#!/bin/bash |
|||
# TimeSafari Docker Run Script |
|||
# Author: Matthew Raymer |
|||
# Description: Convenient script to run TimeSafari in different Docker configurations |
|||
# |
|||
# Usage: |
|||
# ./docker/run.sh dev # Run development mode |
|||
# ./docker/run.sh staging # Run staging mode |
|||
# ./docker/run.sh production # Run production mode |
|||
# ./docker/run.sh custom # Run custom mode with environment variables |
|||
# |
|||
# Environment Variables: |
|||
# BUILD_MODE: development, staging, or production |
|||
# NODE_ENV: node environment |
|||
# VITE_PLATFORM: vite platform |
|||
# VITE_PWA_ENABLED: enable PWA |
|||
# VITE_DISABLE_PWA: disable PWA |
|||
# PORT: port to expose |
|||
# ENV_FILE: environment file to use |
|||
|
|||
set -e |
|||
|
|||
# Colors for output |
|||
readonly RED='\033[0;31m' |
|||
readonly GREEN='\033[0;32m' |
|||
readonly YELLOW='\033[1;33m' |
|||
readonly BLUE='\033[0;34m' |
|||
readonly NC='\033[0m' # No Color |
|||
|
|||
# Logging functions |
|||
log_info() { |
|||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" |
|||
} |
|||
|
|||
log_success() { |
|||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" |
|||
} |
|||
|
|||
log_warn() { |
|||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" |
|||
} |
|||
|
|||
log_error() { |
|||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" |
|||
} |
|||
|
|||
# Function to show usage |
|||
show_usage() { |
|||
echo "TimeSafari Docker Run Script" |
|||
echo "" |
|||
echo "Usage: $0 <mode> [options]" |
|||
echo "" |
|||
echo "Modes:" |
|||
echo " dev - Development mode with hot reloading" |
|||
echo " staging - Staging mode for testing" |
|||
echo " production - Production mode" |
|||
echo " custom - Custom mode with environment variables" |
|||
echo "" |
|||
echo "Options:" |
|||
echo " --port <port> - Custom port (default: 5173 for dev, 8080 for staging, 80 for production)" |
|||
echo " --env <file> - Environment file (default: .env.<mode>)" |
|||
echo " --build-args - Show build arguments for the mode" |
|||
echo " --help - Show this help message" |
|||
echo "" |
|||
echo "Examples:" |
|||
echo " $0 dev" |
|||
echo " $0 staging --port 9000" |
|||
echo " $0 production --env .env.prod" |
|||
echo " BUILD_MODE=staging $0 custom" |
|||
echo "" |
|||
echo "Environment Variables:" |
|||
echo " BUILD_MODE: development, staging, or production" |
|||
echo " NODE_ENV: node environment" |
|||
echo " VITE_PLATFORM: vite platform" |
|||
echo " VITE_PWA_ENABLED: enable PWA" |
|||
echo " VITE_DISABLE_PWA: disable PWA" |
|||
echo " PORT: port to expose" |
|||
echo " ENV_FILE: environment file to use" |
|||
} |
|||
|
|||
# Function to show build arguments for a mode |
|||
show_build_args() { |
|||
local mode=$1 |
|||
echo "Build arguments for $mode mode:" |
|||
echo "" |
|||
case $mode in |
|||
dev) |
|||
echo " BUILD_MODE: development" |
|||
echo " NODE_ENV: development" |
|||
echo " VITE_PLATFORM: web" |
|||
echo " VITE_PWA_ENABLED: true" |
|||
echo " VITE_DISABLE_PWA: false" |
|||
echo " Target: development" |
|||
echo " Port: 5173" |
|||
;; |
|||
staging) |
|||
echo " BUILD_MODE: staging" |
|||
echo " NODE_ENV: staging" |
|||
echo " VITE_PLATFORM: web" |
|||
echo " VITE_PWA_ENABLED: true" |
|||
echo " VITE_DISABLE_PWA: false" |
|||
echo " Target: staging" |
|||
echo " Port: 80 (mapped to 8080)" |
|||
;; |
|||
production) |
|||
echo " BUILD_MODE: production" |
|||
echo " NODE_ENV: production" |
|||
echo " VITE_PLATFORM: web" |
|||
echo " VITE_PWA_ENABLED: true" |
|||
echo " VITE_DISABLE_PWA: false" |
|||
echo " Target: production" |
|||
echo " Port: 80" |
|||
;; |
|||
custom) |
|||
echo " BUILD_MODE: \${BUILD_MODE:-production}" |
|||
echo " NODE_ENV: \${NODE_ENV:-production}" |
|||
echo " VITE_PLATFORM: \${VITE_PLATFORM:-web}" |
|||
echo " VITE_PWA_ENABLED: \${VITE_PWA_ENABLED:-true}" |
|||
echo " VITE_DISABLE_PWA: \${VITE_DISABLE_PWA:-false}" |
|||
echo " Target: \${BUILD_TARGET:-production}" |
|||
echo " Port: \${CUSTOM_PORT:-8080}:\${CUSTOM_INTERNAL_PORT:-80}" |
|||
;; |
|||
*) |
|||
log_error "Unknown mode: $mode" |
|||
exit 1 |
|||
;; |
|||
esac |
|||
} |
|||
|
|||
# Function to check if Docker is running |
|||
check_docker() { |
|||
if ! docker info > /dev/null 2>&1; then |
|||
log_error "Docker is not running. Please start Docker and try again." |
|||
exit 1 |
|||
fi |
|||
} |
|||
|
|||
# Function to check if docker-compose is available |
|||
check_docker_compose() { |
|||
if ! command -v docker-compose > /dev/null 2>&1; then |
|||
log_error "docker-compose is not installed. Please install docker-compose and try again." |
|||
exit 1 |
|||
fi |
|||
} |
|||
|
|||
# Function to check if required files exist |
|||
check_files() { |
|||
local mode=$1 |
|||
local env_file=$2 |
|||
|
|||
if [ ! -f "Dockerfile" ]; then |
|||
log_error "Dockerfile not found. Please run this script from the project root." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ ! -f "docker-compose.yml" ]; then |
|||
log_error "docker-compose.yml not found. Please run this script from the project root." |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -n "$env_file" ] && [ ! -f "$env_file" ]; then |
|||
log_warn "Environment file $env_file not found. Using defaults." |
|||
fi |
|||
} |
|||
|
|||
# Function to run the container |
|||
run_container() { |
|||
local mode=$1 |
|||
local port=$2 |
|||
local env_file=$3 |
|||
|
|||
log_info "Starting TimeSafari in $mode mode..." |
|||
|
|||
# Set environment variables based on mode |
|||
case $mode in |
|||
dev) |
|||
export DEV_PORT=${port:-5173} |
|||
if [ -n "$env_file" ]; then |
|||
export DEV_ENV_FILE="$env_file" |
|||
fi |
|||
docker-compose up dev |
|||
;; |
|||
staging) |
|||
export STAGING_PORT=${port:-8080} |
|||
if [ -n "$env_file" ]; then |
|||
export STAGING_ENV_FILE="$env_file" |
|||
fi |
|||
docker-compose up staging |
|||
;; |
|||
production) |
|||
export PROD_PORT=${port:-80} |
|||
if [ -n "$env_file" ]; then |
|||
export PROD_ENV_FILE="$env_file" |
|||
fi |
|||
docker-compose up production |
|||
;; |
|||
custom) |
|||
export CUSTOM_PORT=${port:-8080} |
|||
if [ -n "$env_file" ]; then |
|||
export CUSTOM_ENV_FILE="$env_file" |
|||
fi |
|||
docker-compose up custom |
|||
;; |
|||
*) |
|||
log_error "Unknown mode: $mode" |
|||
exit 1 |
|||
;; |
|||
esac |
|||
} |
|||
|
|||
# Main script |
|||
main() { |
|||
local mode="" |
|||
local port="" |
|||
local env_file="" |
|||
local show_args=false |
|||
|
|||
# Parse command line arguments |
|||
while [[ $# -gt 0 ]]; do |
|||
case $1 in |
|||
dev|staging|production|custom) |
|||
mode="$1" |
|||
shift |
|||
;; |
|||
--port) |
|||
port="$2" |
|||
shift 2 |
|||
;; |
|||
--env) |
|||
env_file="$2" |
|||
shift 2 |
|||
;; |
|||
--build-args) |
|||
show_args=true |
|||
shift |
|||
;; |
|||
--help|-h) |
|||
show_usage |
|||
exit 0 |
|||
;; |
|||
*) |
|||
log_error "Unknown option: $1" |
|||
show_usage |
|||
exit 1 |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
# Check if mode is provided |
|||
if [ -z "$mode" ]; then |
|||
log_error "No mode specified." |
|||
show_usage |
|||
exit 1 |
|||
fi |
|||
|
|||
# Show build arguments if requested |
|||
if [ "$show_args" = true ]; then |
|||
show_build_args "$mode" |
|||
exit 0 |
|||
fi |
|||
|
|||
# Check prerequisites |
|||
check_docker |
|||
check_docker_compose |
|||
check_files "$mode" "$env_file" |
|||
|
|||
# Run the container |
|||
run_container "$mode" "$port" "$env_file" |
|||
} |
|||
|
|||
# Run main function with all arguments |
|||
main "$@" |
@ -0,0 +1,110 @@ |
|||
# TimeSafari Staging Server Configuration |
|||
# Author: Matthew Raymer |
|||
# Description: Staging server configuration for TimeSafari web application |
|||
# |
|||
# Features: |
|||
# - Relaxed caching for testing |
|||
# - Debug-friendly settings |
|||
# - Same security as production |
|||
# - Development-friendly error handling |
|||
|
|||
server { |
|||
listen 80; |
|||
server_name _; |
|||
root /usr/share/nginx/html; |
|||
index index.html; |
|||
|
|||
# Security headers (same as production) |
|||
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 "strict-origin-when-cross-origin" always; |
|||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; |
|||
|
|||
# Handle Vue.js SPA routing |
|||
location / { |
|||
try_files $uri $uri/ /index.html; |
|||
|
|||
# Relaxed caching for staging |
|||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { |
|||
expires 1h; |
|||
add_header Cache-Control "public, must-revalidate"; |
|||
add_header Vary "Accept-Encoding"; |
|||
} |
|||
|
|||
# No caching for HTML files in staging |
|||
location ~* \.html$ { |
|||
expires 0; |
|||
add_header Cache-Control "no-cache, no-store, must-revalidate"; |
|||
add_header Pragma "no-cache"; |
|||
} |
|||
} |
|||
|
|||
# Handle service worker (no caching) |
|||
location /sw.js { |
|||
expires 0; |
|||
add_header Cache-Control "no-cache, no-store, must-revalidate"; |
|||
add_header Pragma "no-cache"; |
|||
} |
|||
|
|||
# Handle manifest file (short cache) |
|||
location /manifest.json { |
|||
expires 1h; |
|||
add_header Cache-Control "public, must-revalidate"; |
|||
} |
|||
|
|||
# Handle API requests (if needed) |
|||
location /api/ { |
|||
limit_req zone=api burst=20 nodelay; |
|||
proxy_pass http://backend:3000; |
|||
proxy_set_header Host $host; |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_set_header X-Forwarded-Proto $scheme; |
|||
} |
|||
|
|||
# Handle health check |
|||
location /health { |
|||
access_log off; |
|||
return 200 "healthy-staging\n"; |
|||
add_header Content-Type text/plain; |
|||
} |
|||
|
|||
# Handle robots.txt (no caching in staging) |
|||
location /robots.txt { |
|||
expires 0; |
|||
add_header Cache-Control "no-cache, no-store, must-revalidate"; |
|||
} |
|||
|
|||
# Handle favicon (short cache) |
|||
location /favicon.ico { |
|||
expires 1h; |
|||
add_header Cache-Control "public, must-revalidate"; |
|||
} |
|||
|
|||
# Security: Deny access to hidden files |
|||
location ~ /\. { |
|||
deny all; |
|||
access_log off; |
|||
log_not_found off; |
|||
} |
|||
|
|||
# Security: Deny access to backup files |
|||
location ~ ~$ { |
|||
deny all; |
|||
access_log off; |
|||
log_not_found off; |
|||
} |
|||
|
|||
# Error pages (more verbose for staging) |
|||
error_page 404 /index.html; |
|||
error_page 500 502 503 504 /50x.html; |
|||
|
|||
location = /50x.html { |
|||
root /usr/share/nginx/html; |
|||
} |
|||
|
|||
# Enhanced logging for staging |
|||
access_log /var/log/nginx/access.log main; |
|||
error_log /var/log/nginx/error.log debug; |
|||
} |
@ -0,0 +1,155 @@ |
|||
#!/bin/bash |
|||
# experiment.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Build script for TimeSafari Electron application |
|||
# This script handles the complete build process for the TimeSafari Electron app, |
|||
# including web asset compilation and Capacitor sync. |
|||
# |
|||
# Build Process: |
|||
# 1. Environment setup and dependency checks |
|||
# 2. Web asset compilation (Vite) |
|||
# 3. Capacitor sync |
|||
# 4. Electron start |
|||
# |
|||
# Dependencies: |
|||
# - Node.js and npm |
|||
# - TypeScript |
|||
# - Vite |
|||
# - @capacitor-community/electron |
|||
# |
|||
# Usage: ./experiment.sh |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Required command not found |
|||
# 2 - TypeScript installation failed |
|||
# 3 - Build process failed |
|||
# 4 - Capacitor sync failed |
|||
# 5 - Electron start failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# ANSI color codes for better output formatting |
|||
readonly RED='\033[0;31m' |
|||
readonly GREEN='\033[0;32m' |
|||
readonly YELLOW='\033[1;33m' |
|||
readonly BLUE='\033[0;34m' |
|||
readonly NC='\033[0m' # No Color |
|||
|
|||
# Logging functions |
|||
log_info() { |
|||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" |
|||
} |
|||
|
|||
log_success() { |
|||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" |
|||
} |
|||
|
|||
log_warn() { |
|||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" |
|||
} |
|||
|
|||
log_error() { |
|||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" |
|||
} |
|||
|
|||
# Function to check if a command exists |
|||
check_command() { |
|||
if ! command -v "$1" &> /dev/null; then |
|||
log_error "$1 is required but not installed." |
|||
exit 1 |
|||
fi |
|||
log_info "Found $1: $(command -v "$1")" |
|||
} |
|||
|
|||
# Function to measure and log execution time |
|||
measure_time() { |
|||
local start_time=$(date +%s) |
|||
"$@" |
|||
local end_time=$(date +%s) |
|||
local duration=$((end_time - start_time)) |
|||
log_success "Completed in ${duration} seconds" |
|||
} |
|||
|
|||
# Print build header |
|||
echo -e "\n${BLUE}=== TimeSafari Electron Build Process ===${NC}\n" |
|||
log_info "Starting build process at $(date)" |
|||
|
|||
# Check required commands |
|||
log_info "Checking required dependencies..." |
|||
check_command node |
|||
check_command npm |
|||
check_command git |
|||
|
|||
# Create application data directory |
|||
log_info "Setting up application directories..." |
|||
mkdir -p ~/.local/share/TimeSafari/timesafari |
|||
|
|||
# Clean up previous builds |
|||
log_info "Cleaning previous builds..." |
|||
rm -rf dist* || log_warn "No previous builds to clean" |
|||
|
|||
# Set environment variables for the build |
|||
log_info "Configuring build environment..." |
|||
export VITE_PLATFORM=electron |
|||
export VITE_PWA_ENABLED=false |
|||
export VITE_DISABLE_PWA=true |
|||
export DEBUG_MIGRATIONS=0 |
|||
|
|||
# Ensure TypeScript is installed |
|||
log_info "Verifying TypeScript installation..." |
|||
if [ ! -f "./node_modules/.bin/tsc" ]; then |
|||
log_info "Installing TypeScript..." |
|||
if ! npm install --save-dev typescript@~5.2.2; then |
|||
log_error "TypeScript installation failed!" |
|||
exit 2 |
|||
fi |
|||
# Verify installation |
|||
if [ ! -f "./node_modules/.bin/tsc" ]; then |
|||
log_error "TypeScript installation verification failed!" |
|||
exit 2 |
|||
fi |
|||
log_success "TypeScript installed successfully" |
|||
else |
|||
log_info "TypeScript already installed" |
|||
fi |
|||
|
|||
# Get git hash for versioning |
|||
GIT_HASH=$(git log -1 --pretty=format:%h) |
|||
log_info "Using git hash: ${GIT_HASH}" |
|||
|
|||
# Build web assets |
|||
log_info "Building web assets with Vite..." |
|||
if ! measure_time env VITE_GIT_HASH="$GIT_HASH" npx vite build --config vite.config.app.electron.mts --mode electron; then |
|||
log_error "Web asset build failed!" |
|||
exit 3 |
|||
fi |
|||
|
|||
# Sync with Capacitor |
|||
log_info "Syncing with Capacitor..." |
|||
if ! measure_time npx cap sync electron; then |
|||
log_error "Capacitor sync failed!" |
|||
exit 4 |
|||
fi |
|||
|
|||
# Restore capacitor config |
|||
log_info "Restoring capacitor config..." |
|||
if ! git checkout electron/capacitor.config.json; then |
|||
log_error "Failed to restore capacitor config!" |
|||
exit 4 |
|||
fi |
|||
|
|||
# Start Electron |
|||
log_info "Starting Electron..." |
|||
cd electron/ |
|||
if ! measure_time npm run electron:start; then |
|||
log_error "Electron start failed!" |
|||
exit 5 |
|||
fi |
|||
|
|||
# Print build summary |
|||
log_success "Build and start completed successfully!" |
|||
echo -e "\n${GREEN}=== End of Build Process ===${NC}\n" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -1,6 +0,0 @@ |
|||
eth_keys |
|||
pywebview |
|||
pyinstaller>=6.12.0 |
|||
setuptools>=69.0.0 # Required for distutils for electron-builder on macOS |
|||
# For development |
|||
watchdog>=3.0.0 # For file watching support |
@ -0,0 +1,285 @@ |
|||
# TimeSafari Build Scripts |
|||
|
|||
This directory contains unified build and test scripts for the TimeSafari application. All scripts use a common utilities library to eliminate redundancy and provide consistent logging, error handling, timing, and environment variable management. |
|||
|
|||
## Architecture |
|||
|
|||
### Common Utilities (`common.sh`) |
|||
|
|||
The `common.sh` script provides shared functionality used by all build scripts: |
|||
|
|||
- **Logging Functions**: `log_info`, `log_success`, `log_warn`, `log_error`, `log_debug`, `log_step` |
|||
- **Timing**: `measure_time` for execution time tracking |
|||
- **Headers/Footers**: `print_header`, `print_footer` for consistent output formatting |
|||
- **Validation**: `check_command`, `check_directory`, `check_file`, `check_venv` |
|||
- **Execution**: `safe_execute` for error-handled command execution |
|||
- **Utilities**: `get_git_hash`, `clean_build_artifacts`, `validate_env_vars` |
|||
- **Environment Management**: `setup_build_env`, `setup_app_directories`, `load_env_file`, `print_env_vars` |
|||
- **CLI**: `parse_args`, `print_usage` for command-line argument handling |
|||
|
|||
### Environment Variable Management |
|||
|
|||
All scripts automatically handle environment variables for different build types: |
|||
|
|||
#### Build Types and Environment Variables |
|||
|
|||
| Platform | Mode | PWA Enabled | Native Features | Build Script | |
|||
|----------|------|-------------|-----------------|--------------| |
|||
| `web` | web | true | false | `build-web.sh` | |
|||
| `capacitor` | capacitor | false | true | `build-capacitor.sh` | |
|||
| `electron` | electron | false | true | `build-electron.sh` | |
|||
|
|||
#### Automatic Environment Setup |
|||
|
|||
Each script automatically: |
|||
1. **Sets platform-specific variables** based on build type |
|||
2. **Gets git hash** for versioning (`VITE_GIT_HASH`) |
|||
3. **Creates application directories** (`~/.local/share/TimeSafari/timesafari`) |
|||
4. **Loads .env file** if it exists |
|||
5. **Validates required variables** when needed |
|||
|
|||
#### Environment Functions |
|||
|
|||
- `setup_build_env(build_type, production)` - Sets environment for specific build type |
|||
- `setup_app_directories()` - Creates necessary application directories |
|||
- `load_env_file(filename)` - Loads variables from .env file |
|||
- `print_env_vars(prefix)` - Displays current environment variables |
|||
- `validate_env_vars(var1, var2, ...)` - Validates required variables exist |
|||
|
|||
### Script Structure |
|||
|
|||
All scripts follow this unified pattern: |
|||
|
|||
```bash |
|||
#!/bin/bash |
|||
# script-name.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Brief description of what the script does |
|||
# |
|||
# Exit Codes: List of exit codes and their meanings |
|||
# Usage: ./scripts/script-name.sh [options] |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print header |
|||
print_header "Script Title" |
|||
log_info "Starting process at $(date)" |
|||
|
|||
# Setup environment (automatic) |
|||
setup_build_env "build_type" |
|||
setup_app_directories |
|||
load_env_file ".env" |
|||
|
|||
# Execute steps with safe_execute |
|||
safe_execute "Step description" "command to execute" || exit 1 |
|||
|
|||
# Print footer |
|||
print_footer "Script Title" |
|||
exit 0 |
|||
``` |
|||
|
|||
## Available Scripts |
|||
|
|||
### Test Scripts |
|||
|
|||
- **`test-all.sh`**: Comprehensive test suite (prerequisites, build, web tests, mobile tests) |
|||
- **`test-mobile.sh`**: Mobile test suite (Capacitor build, Android tests, iOS tests) |
|||
- **`test-common.sh`**: Test script to verify common utilities work correctly |
|||
- **`test-env.sh`**: Test script to verify environment variable handling |
|||
|
|||
### Build Scripts |
|||
|
|||
- **`build-electron.sh`**: Complete Electron build process |
|||
- **`build-android.sh`**: Complete Android build process |
|||
- **`build-electron-linux.sh`**: Linux Electron packaging (AppImage, .deb) |
|||
- **`build-electron-mac.sh`**: macOS Electron packaging (standard, universal) |
|||
|
|||
### Development Scripts |
|||
|
|||
- **`electron-dev.sh`**: Electron development workflow |
|||
|
|||
## Benefits of Unification |
|||
|
|||
### Before (Redundant) |
|||
```bash |
|||
# Each script had 50+ lines of duplicate code: |
|||
readonly RED='\033[0;31m' |
|||
readonly GREEN='\033[0;32m' |
|||
# ... 40+ more lines of duplicate logging functions |
|||
log_info "Step 1/4: Doing something..." |
|||
if ! measure_time some_command; then |
|||
log_error "Step failed!" |
|||
exit 1 |
|||
fi |
|||
# Manual environment variable setup |
|||
export VITE_PLATFORM=electron |
|||
export VITE_PWA_ENABLED=false |
|||
# ... more manual exports |
|||
``` |
|||
|
|||
### After (Unified) |
|||
```bash |
|||
# Each script is now ~20 lines of focused logic: |
|||
source "$(dirname "$0")/common.sh" |
|||
print_header "Script Title" |
|||
setup_build_env "electron" # Automatic environment setup |
|||
safe_execute "Step description" "some_command" || exit 1 |
|||
print_footer "Script Title" |
|||
``` |
|||
|
|||
## Usage Examples |
|||
|
|||
### Running Tests |
|||
```bash |
|||
# Run all tests |
|||
./scripts/test-all.sh |
|||
|
|||
# Run mobile tests only |
|||
./scripts/test-mobile.sh |
|||
|
|||
# Run with verbose logging |
|||
./scripts/test-all.sh --verbose |
|||
|
|||
# Show environment variables |
|||
./scripts/test-env.sh --env |
|||
``` |
|||
|
|||
### Building Applications |
|||
```bash |
|||
# Build Electron |
|||
./scripts/build-electron.sh |
|||
|
|||
# Build Android |
|||
./scripts/build-android.sh |
|||
|
|||
# Build Linux package |
|||
./scripts/build-electron-linux.sh deb |
|||
|
|||
# Build universal Mac package |
|||
./scripts/build-electron-mac.sh universal |
|||
|
|||
# Show environment variables for build |
|||
./scripts/build-electron.sh --env |
|||
``` |
|||
|
|||
### Development Workflows |
|||
```bash |
|||
# Start Electron development |
|||
./scripts/electron-dev.sh |
|||
``` |
|||
|
|||
## Environment Variable Features |
|||
|
|||
### Automatic Setup |
|||
All scripts automatically configure the correct environment variables for their build type: |
|||
|
|||
```bash |
|||
# Electron builds automatically get: |
|||
export VITE_PLATFORM=electron |
|||
export VITE_PWA_ENABLED=false |
|||
export VITE_DISABLE_PWA=true |
|||
export DEBUG_MIGRATIONS=0 |
|||
export VITE_GIT_HASH=<git-hash> |
|||
|
|||
# Production builds also get: |
|||
export NODE_ENV=production |
|||
``` |
|||
|
|||
### .env File Support |
|||
Scripts automatically load variables from `.env` files if they exist: |
|||
|
|||
```bash |
|||
# .env file example: |
|||
VITE_API_URL=https://api.example.com |
|||
VITE_DEBUG=true |
|||
CUSTOM_VAR=value |
|||
``` |
|||
|
|||
### Environment Validation |
|||
Required environment variables can be validated: |
|||
|
|||
```bash |
|||
# In your script |
|||
validate_env_vars "VITE_API_URL" "VITE_DEBUG" || exit 1 |
|||
``` |
|||
|
|||
### Environment Inspection |
|||
View current environment variables with the `--env` flag: |
|||
|
|||
```bash |
|||
./scripts/build-electron.sh --env |
|||
./scripts/test-env.sh --env |
|||
``` |
|||
|
|||
## Error Handling |
|||
|
|||
All scripts use consistent error handling: |
|||
|
|||
- **Exit Codes**: Each script documents specific exit codes |
|||
- **Safe Execution**: `safe_execute` provides timing and error handling |
|||
- **Graceful Failure**: Scripts stop on first error with clear messages |
|||
- **Logging**: All operations are logged with timestamps and colors |
|||
- **Environment Validation**: Required variables are checked before execution |
|||
|
|||
## Testing |
|||
|
|||
To verify the common utilities work correctly: |
|||
|
|||
```bash |
|||
# Test all common functions |
|||
./scripts/test-common.sh |
|||
|
|||
# Test environment variable handling |
|||
./scripts/test-env.sh |
|||
|
|||
# Test with verbose logging |
|||
./scripts/test-env.sh --verbose |
|||
``` |
|||
|
|||
## Maintenance |
|||
|
|||
### Adding New Scripts |
|||
|
|||
1. Create new script following the unified pattern |
|||
2. Source `common.sh` at the top |
|||
3. Use `setup_build_env()` for environment setup |
|||
4. Use `safe_execute` for command execution |
|||
5. Document exit codes and usage |
|||
6. Make executable: `chmod +x scripts/new-script.sh` |
|||
|
|||
### Modifying Common Utilities |
|||
|
|||
1. Update `common.sh` with new functions |
|||
2. Export new functions with `export -f function_name` |
|||
3. Update this README if adding new categories |
|||
4. Test with `test-common.sh` and `test-env.sh` |
|||
|
|||
### Adding New Build Types |
|||
|
|||
1. Add new case to `setup_build_env()` function |
|||
2. Define appropriate environment variables |
|||
3. Update this README with new build type |
|||
4. Test with `test-env.sh` |
|||
|
|||
## Security Considerations |
|||
|
|||
- All scripts use `set -e` for immediate failure on errors |
|||
- Commands are executed through `safe_execute` for consistent error handling |
|||
- No direct execution of user input without validation |
|||
- Environment variables are validated when required |
|||
- .env files are loaded safely with proper parsing |
|||
|
|||
## Performance |
|||
|
|||
- Common utilities are sourced once per script execution |
|||
- Timing information is automatically collected for all operations |
|||
- Build artifacts are cleaned up automatically |
|||
- No redundant command execution or file operations |
|||
- Environment variables are set efficiently with minimal overhead |
@ -0,0 +1,71 @@ |
|||
#!/bin/bash |
|||
# build-android.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Android build script for TimeSafari application |
|||
# This script handles the complete Android build process including cleanup, |
|||
# web build, Capacitor build, Gradle build, and Android Studio launch. |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Android cleanup failed |
|||
# 2 - Web build failed |
|||
# 3 - Capacitor build failed |
|||
# 4 - Gradle clean failed |
|||
# 5 - Gradle assemble failed |
|||
# 6 - Capacitor sync failed |
|||
# 7 - Asset generation failed |
|||
# 8 - Android Studio launch failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print build header |
|||
print_header "TimeSafari Android Build Process" |
|||
log_info "Starting Android build process at $(date)" |
|||
|
|||
# Setup environment for Capacitor build |
|||
setup_build_env "capacitor" |
|||
|
|||
# Setup application directories |
|||
setup_app_directories |
|||
|
|||
# Load environment from .env file if it exists |
|||
load_env_file ".env" |
|||
|
|||
# Step 1: Clean Android app |
|||
safe_execute "Cleaning Android app" "npm run clean:android" || exit 1 |
|||
|
|||
# Step 2: Clean dist directory |
|||
log_info "Cleaning dist directory..." |
|||
clean_build_artifacts "dist" |
|||
|
|||
# Step 3: Build web assets |
|||
safe_execute "Building web assets" "npm run build:web" || exit 2 |
|||
|
|||
# Step 4: Build Capacitor version |
|||
safe_execute "Building Capacitor version" "npm run build:capacitor" || exit 3 |
|||
|
|||
# Step 5: Clean Gradle build |
|||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4 |
|||
|
|||
# Step 6: Assemble debug build |
|||
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5 |
|||
|
|||
# Step 7: Sync with Capacitor |
|||
safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6 |
|||
|
|||
# Step 8: Generate assets and open Android Studio |
|||
safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7 |
|||
safe_execute "Opening Android Studio" "npx cap open android" || exit 8 |
|||
|
|||
# Print build summary |
|||
log_success "Android build completed successfully!" |
|||
print_footer "Android Build" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,82 @@ |
|||
#!/bin/bash |
|||
# build-electron-linux.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Electron Linux build script for TimeSafari application |
|||
# This script builds Electron packages for Linux with support for different formats. |
|||
# |
|||
# Usage: ./scripts/build-electron-linux.sh [deb|prod] |
|||
# - No argument: Builds AppImage |
|||
# - deb: Builds .deb package |
|||
# - prod: Builds production AppImage |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Build failed |
|||
# 2 - Invalid argument |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Parse build type argument |
|||
BUILD_TYPE=${1:-"appimage"} |
|||
PRODUCTION=false |
|||
|
|||
case $BUILD_TYPE in |
|||
"deb") |
|||
BUILD_TARGET="deb" |
|||
log_info "Building .deb package" |
|||
;; |
|||
"prod") |
|||
BUILD_TARGET="AppImage" |
|||
PRODUCTION=true |
|||
log_info "Building production AppImage" |
|||
;; |
|||
"appimage"|"") |
|||
BUILD_TARGET="AppImage" |
|||
log_info "Building AppImage" |
|||
;; |
|||
*) |
|||
log_error "Invalid build type: $BUILD_TYPE" |
|||
log_error "Usage: $0 [deb|prod]" |
|||
exit 2 |
|||
;; |
|||
esac |
|||
|
|||
# Print build header |
|||
print_header "TimeSafari Electron Linux Build" |
|||
log_info "Starting Linux build process at $(date)" |
|||
log_info "Build type: $BUILD_TYPE" |
|||
log_info "Build target: $BUILD_TARGET" |
|||
log_info "Production mode: $PRODUCTION" |
|||
|
|||
# Setup environment for Electron build |
|||
setup_build_env "electron" "$PRODUCTION" |
|||
|
|||
# Setup application directories |
|||
setup_app_directories |
|||
|
|||
# Load environment from .env file if it exists |
|||
load_env_file ".env" |
|||
|
|||
# Step 1: Build Electron application |
|||
if [ "$PRODUCTION" = true ]; then |
|||
safe_execute "Building production Electron application" "npm run build:electron-prod" || exit 1 |
|||
else |
|||
safe_execute "Building Electron application" "npm run build:electron" || exit 1 |
|||
fi |
|||
|
|||
# Step 2: Build package |
|||
safe_execute "Building Linux package" "npx electron-builder --linux $BUILD_TARGET" || exit 1 |
|||
|
|||
# Print build summary |
|||
log_success "Linux build completed successfully!" |
|||
log_info "Package type: $BUILD_TARGET" |
|||
print_footer "Linux Build" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,74 @@ |
|||
#!/bin/bash |
|||
# build-electron-mac.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Electron Mac build script for TimeSafari application |
|||
# This script builds Electron packages for macOS with support for universal builds. |
|||
# |
|||
# Usage: ./scripts/build-electron-mac.sh [universal] |
|||
# - No argument: Builds standard Mac package |
|||
# - universal: Builds universal Mac package (Intel + Apple Silicon) |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Build failed |
|||
# 2 - Invalid argument |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Parse build type argument |
|||
BUILD_TYPE=${1:-"standard"} |
|||
UNIVERSAL=false |
|||
|
|||
case $BUILD_TYPE in |
|||
"universal") |
|||
UNIVERSAL=true |
|||
log_info "Building universal Mac package (Intel + Apple Silicon)" |
|||
;; |
|||
"standard"|"") |
|||
log_info "Building standard Mac package" |
|||
;; |
|||
*) |
|||
log_error "Invalid build type: $BUILD_TYPE" |
|||
log_error "Usage: $0 [universal]" |
|||
exit 2 |
|||
;; |
|||
esac |
|||
|
|||
# Print build header |
|||
print_header "TimeSafari Electron Mac Build" |
|||
log_info "Starting Mac build process at $(date)" |
|||
log_info "Build type: $BUILD_TYPE" |
|||
log_info "Universal build: $UNIVERSAL" |
|||
|
|||
# Setup environment for Electron build (production mode for packaging) |
|||
setup_build_env "electron" "true" |
|||
|
|||
# Setup application directories |
|||
setup_app_directories |
|||
|
|||
# Load environment from .env file if it exists |
|||
load_env_file ".env" |
|||
|
|||
# Step 1: Build Electron application |
|||
safe_execute "Building Electron application" "npm run build:electron-prod" || exit 1 |
|||
|
|||
# Step 2: Build package |
|||
if [ "$UNIVERSAL" = true ]; then |
|||
safe_execute "Building universal Mac package" "npx electron-builder --mac --universal" || exit 1 |
|||
else |
|||
safe_execute "Building Mac package" "npx electron-builder --mac" || exit 1 |
|||
fi |
|||
|
|||
# Print build summary |
|||
log_success "Mac build completed successfully!" |
|||
log_info "Package type: $([ "$UNIVERSAL" = true ] && echo "Universal" || echo "Standard")" |
|||
print_footer "Mac Build" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,53 @@ |
|||
#!/bin/bash |
|||
# build-electron.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Electron build script for TimeSafari application |
|||
# This script handles the complete Electron build process including cleanup, |
|||
# TypeScript compilation, Vite build, and Electron-specific setup. |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Cleanup failed |
|||
# 2 - TypeScript compilation failed |
|||
# 3 - Vite build failed |
|||
# 4 - Electron build script failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print build header |
|||
print_header "TimeSafari Electron Build Process" |
|||
log_info "Starting Electron build process at $(date)" |
|||
|
|||
# Setup environment for Electron build |
|||
setup_build_env "electron" |
|||
|
|||
# Setup application directories |
|||
setup_app_directories |
|||
|
|||
# Load environment from .env file if it exists |
|||
load_env_file ".env" |
|||
|
|||
# Step 1: Clean previous builds |
|||
safe_execute "Cleaning previous builds" "npm run clean:electron" || exit 1 |
|||
|
|||
# Step 2: Compile TypeScript for Electron |
|||
safe_execute "Compiling TypeScript for Electron" "npx tsc -p tsconfig.electron.json" || exit 2 |
|||
|
|||
# Step 3: Build with Vite |
|||
safe_execute "Building with Vite" "npx vite build --config vite.config.electron.mts" || exit 3 |
|||
|
|||
# Step 4: Run Electron build script |
|||
safe_execute "Running Electron build script" "node scripts/build-electron.js" || exit 4 |
|||
|
|||
# Print build summary |
|||
log_success "Electron build completed successfully!" |
|||
print_footer "Electron Build" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,331 @@ |
|||
#!/bin/bash |
|||
# common.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Common utilities and functions for TimeSafari build scripts |
|||
# This script provides shared logging, timing, and utility functions |
|||
# that can be sourced by other build scripts to eliminate redundancy. |
|||
# |
|||
# Usage: source ./scripts/common.sh |
|||
# |
|||
# Provides: |
|||
# - Color constants |
|||
# - Logging functions (log_info, log_success, log_warn, log_error) |
|||
# - Timing function (measure_time) |
|||
# - Common utility functions |
|||
# - Environment variable management |
|||
|
|||
# ANSI color codes for better output formatting |
|||
readonly RED='\033[0;31m' |
|||
readonly GREEN='\033[0;32m' |
|||
readonly YELLOW='\033[1;33m' |
|||
readonly BLUE='\033[0;34m' |
|||
readonly PURPLE='\033[0;35m' |
|||
readonly CYAN='\033[0;36m' |
|||
readonly NC='\033[0m' # No Color |
|||
|
|||
# Logging functions |
|||
log_info() { |
|||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]${NC} $1" |
|||
} |
|||
|
|||
log_success() { |
|||
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS]${NC} $1" |
|||
} |
|||
|
|||
log_warn() { |
|||
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]${NC} $1" |
|||
} |
|||
|
|||
log_error() { |
|||
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR]${NC} $1" |
|||
} |
|||
|
|||
log_debug() { |
|||
echo -e "${PURPLE}[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG]${NC} $1" |
|||
} |
|||
|
|||
log_step() { |
|||
echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')] [STEP]${NC} $1" |
|||
} |
|||
|
|||
# Function to measure and log execution time |
|||
measure_time() { |
|||
local start_time=$(date +%s) |
|||
"$@" |
|||
local end_time=$(date +%s) |
|||
local duration=$((end_time - start_time)) |
|||
log_success "Completed in ${duration} seconds" |
|||
} |
|||
|
|||
# Function to print section headers |
|||
print_header() { |
|||
local title="$1" |
|||
echo -e "\n${BLUE}=== $title ===${NC}\n" |
|||
} |
|||
|
|||
print_footer() { |
|||
local title="$1" |
|||
echo -e "\n${GREEN}=== $title Complete ===${NC}\n" |
|||
} |
|||
|
|||
# Function to check if a command exists |
|||
check_command() { |
|||
if ! command -v "$1" &> /dev/null; then |
|||
log_error "$1 is required but not installed." |
|||
return 1 |
|||
fi |
|||
log_debug "Found $1: $(command -v "$1")" |
|||
return 0 |
|||
} |
|||
|
|||
# Function to check if a directory exists |
|||
check_directory() { |
|||
if [ ! -d "$1" ]; then |
|||
log_error "Directory not found: $1" |
|||
return 1 |
|||
fi |
|||
log_debug "Directory exists: $1" |
|||
return 0 |
|||
} |
|||
|
|||
# Function to check if a file exists |
|||
check_file() { |
|||
if [ ! -f "$1" ]; then |
|||
log_error "File not found: $1" |
|||
return 1 |
|||
fi |
|||
log_debug "File exists: $1" |
|||
return 0 |
|||
} |
|||
|
|||
# Function to safely execute a command with error handling |
|||
safe_execute() { |
|||
local step_name="$1" |
|||
local command="$2" |
|||
|
|||
log_step "$step_name" |
|||
if ! measure_time eval "$command"; then |
|||
log_error "$step_name failed!" |
|||
return 1 |
|||
fi |
|||
return 0 |
|||
} |
|||
|
|||
# Function to check virtual environment for Python scripts |
|||
check_venv() { |
|||
if [ ! -d ".venv" ]; then |
|||
log_error "Virtual environment not found. Please create it first:" |
|||
log_error "python -m venv .venv" |
|||
log_error "source .venv/bin/activate" |
|||
log_error "pip install -r requirements.txt" |
|||
return 1 |
|||
fi |
|||
log_debug "Virtual environment found: .venv" |
|||
return 0 |
|||
} |
|||
|
|||
# Function to get git hash for versioning |
|||
get_git_hash() { |
|||
if command -v git &> /dev/null; then |
|||
git log -1 --pretty=format:%h 2>/dev/null || echo "unknown" |
|||
else |
|||
echo "unknown" |
|||
fi |
|||
} |
|||
|
|||
# Function to clean build artifacts |
|||
clean_build_artifacts() { |
|||
local artifacts=("$@") |
|||
for artifact in "${artifacts[@]}"; do |
|||
if [ -e "$artifact" ]; then |
|||
log_info "Cleaning $artifact" |
|||
rm -rf "$artifact" |
|||
fi |
|||
done |
|||
} |
|||
|
|||
# Function to validate environment variables |
|||
validate_env_vars() { |
|||
local required_vars=("$@") |
|||
local missing_vars=() |
|||
|
|||
for var in "${required_vars[@]}"; do |
|||
if [ -z "${!var}" ]; then |
|||
missing_vars+=("$var") |
|||
fi |
|||
done |
|||
|
|||
if [ ${#missing_vars[@]} -gt 0 ]; then |
|||
log_error "Missing required environment variables: ${missing_vars[*]}" |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
# Function to set environment variables for different build types |
|||
setup_build_env() { |
|||
local build_type="$1" |
|||
local production="${2:-false}" |
|||
|
|||
log_info "Setting up environment for $build_type build" |
|||
|
|||
# Get git hash for versioning |
|||
local git_hash=$(get_git_hash) |
|||
export VITE_GIT_HASH="$git_hash" |
|||
log_debug "Set VITE_GIT_HASH=$git_hash" |
|||
|
|||
case $build_type in |
|||
"electron") |
|||
export VITE_PLATFORM=electron |
|||
export VITE_PWA_ENABLED=false |
|||
export VITE_DISABLE_PWA=true |
|||
export DEBUG_MIGRATIONS=0 |
|||
if [ "$production" = true ]; then |
|||
export NODE_ENV=production |
|||
log_debug "Set production mode for Electron" |
|||
fi |
|||
;; |
|||
"capacitor") |
|||
export VITE_PLATFORM=capacitor |
|||
export VITE_PWA_ENABLED=false |
|||
export VITE_DISABLE_PWA=true |
|||
export DEBUG_MIGRATIONS=0 |
|||
;; |
|||
"web") |
|||
export VITE_PLATFORM=web |
|||
export VITE_PWA_ENABLED=true |
|||
export VITE_DISABLE_PWA=false |
|||
export DEBUG_MIGRATIONS=0 |
|||
;; |
|||
*) |
|||
log_warn "Unknown build type: $build_type, using default environment" |
|||
export VITE_PLATFORM=web |
|||
export VITE_PWA_ENABLED=true |
|||
export VITE_DISABLE_PWA=false |
|||
export DEBUG_MIGRATIONS=0 |
|||
;; |
|||
esac |
|||
|
|||
# Log environment setup |
|||
log_debug "Environment variables set:" |
|||
log_debug " VITE_PLATFORM=$VITE_PLATFORM" |
|||
log_debug " VITE_PWA_ENABLED=$VITE_PWA_ENABLED" |
|||
log_debug " VITE_DISABLE_PWA=$VITE_DISABLE_PWA" |
|||
log_debug " DEBUG_MIGRATIONS=$DEBUG_MIGRATIONS" |
|||
if [ -n "$NODE_ENV" ]; then |
|||
log_debug " NODE_ENV=$NODE_ENV" |
|||
fi |
|||
} |
|||
|
|||
# Function to create application directories |
|||
setup_app_directories() { |
|||
log_info "Setting up application directories..." |
|||
|
|||
# Create TimeSafari data directory |
|||
mkdir -p ~/.local/share/TimeSafari/timesafari |
|||
|
|||
# Create build directories if they don't exist |
|||
mkdir -p dist |
|||
mkdir -p dist-electron |
|||
|
|||
log_debug "Application directories created" |
|||
} |
|||
|
|||
# Function to load environment from .env file if it exists |
|||
load_env_file() { |
|||
local env_file="$1" |
|||
|
|||
if [ -f "$env_file" ]; then |
|||
log_info "Loading environment from $env_file" |
|||
# Export variables from .env file (simple key=value format) |
|||
while IFS='=' read -r key value; do |
|||
# Skip comments and empty lines |
|||
[[ $key =~ ^#.*$ ]] && continue |
|||
[[ -z $key ]] && continue |
|||
|
|||
# Remove quotes from value if present |
|||
value=$(echo "$value" | sed 's/^["'\'']//;s/["'\'']$//') |
|||
|
|||
export "$key=$value" |
|||
log_debug "Loaded: $key=$value" |
|||
done < "$env_file" |
|||
else |
|||
log_debug "No $env_file file found" |
|||
fi |
|||
} |
|||
|
|||
# Function to print current environment variables |
|||
print_env_vars() { |
|||
local prefix="$1" |
|||
|
|||
if [ -n "$prefix" ]; then |
|||
log_info "Environment variables with prefix '$prefix':" |
|||
env | grep "^$prefix" | sort | while read -r line; do |
|||
log_debug " $line" |
|||
done |
|||
else |
|||
log_info "Current environment variables:" |
|||
env | sort | while read -r line; do |
|||
log_debug " $line" |
|||
done |
|||
fi |
|||
} |
|||
|
|||
# Function to print script usage |
|||
print_usage() { |
|||
local script_name="$1" |
|||
local usage_text="$2" |
|||
|
|||
echo "Usage: $script_name $usage_text" |
|||
echo "" |
|||
echo "Options:" |
|||
echo " -h, --help Show this help message" |
|||
echo " -v, --verbose Enable verbose logging" |
|||
echo " -e, --env Show environment variables" |
|||
echo "" |
|||
} |
|||
|
|||
# Function to parse command line arguments |
|||
parse_args() { |
|||
local args=("$@") |
|||
local verbose=false |
|||
local show_env=false |
|||
|
|||
for arg in "${args[@]}"; do |
|||
case $arg in |
|||
-h|--help) |
|||
print_usage "$0" "[options]" |
|||
exit 0 |
|||
;; |
|||
-v|--verbose) |
|||
verbose=true |
|||
;; |
|||
-e|--env) |
|||
show_env=true |
|||
;; |
|||
*) |
|||
# Handle other arguments in child scripts |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
if [ "$verbose" = true ]; then |
|||
# Enable debug logging |
|||
set -x |
|||
fi |
|||
|
|||
if [ "$show_env" = true ]; then |
|||
print_env_vars "VITE_" |
|||
exit 0 |
|||
fi |
|||
} |
|||
|
|||
# Export functions for use in child scripts |
|||
export -f log_info log_success log_warn log_error log_debug log_step |
|||
export -f measure_time print_header print_footer |
|||
export -f check_command check_directory check_file |
|||
export -f safe_execute check_venv get_git_hash |
|||
export -f clean_build_artifacts validate_env_vars |
|||
export -f setup_build_env setup_app_directories load_env_file print_env_vars |
|||
export -f print_usage parse_args |
@ -0,0 +1,44 @@ |
|||
#!/bin/bash |
|||
# electron-dev.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Electron development script for TimeSafari application |
|||
# This script builds the application and starts Electron for development. |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Build failed |
|||
# 2 - Electron start failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print dev header |
|||
print_header "TimeSafari Electron Development" |
|||
log_info "Starting Electron development at $(date)" |
|||
|
|||
# Setup environment for Electron development |
|||
setup_build_env "electron" |
|||
|
|||
# Setup application directories |
|||
setup_app_directories |
|||
|
|||
# Load environment from .env file if it exists |
|||
load_env_file ".env" |
|||
|
|||
# Step 1: Build the application |
|||
safe_execute "Building application" "npm run build" || exit 1 |
|||
|
|||
# Step 2: Start Electron |
|||
safe_execute "Starting Electron" "electron ." || exit 2 |
|||
|
|||
# Print dev summary |
|||
log_success "Electron development session ended" |
|||
print_footer "Electron Development" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,44 @@ |
|||
#!/bin/bash |
|||
# test-all.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Comprehensive test suite for TimeSafari application |
|||
# This script runs all tests including prerequisites, web tests, and mobile tests |
|||
# with proper error handling and logging. |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Prerequisites check failed |
|||
# 2 - Build failed |
|||
# 3 - Web tests failed |
|||
# 4 - Mobile tests failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print test header |
|||
print_header "TimeSafari Test Suite" |
|||
log_info "Starting comprehensive test suite at $(date)" |
|||
|
|||
# Step 1: Check prerequisites |
|||
safe_execute "Checking prerequisites" "npm run test:prerequisites" || exit 1 |
|||
|
|||
# Step 2: Build the application |
|||
safe_execute "Building application" "npm run build" || exit 2 |
|||
|
|||
# Step 3: Run web tests |
|||
safe_execute "Running web tests" "npm run test:web" || exit 3 |
|||
|
|||
# Step 4: Run mobile tests |
|||
safe_execute "Running mobile tests" "npm run test:mobile" || exit 4 |
|||
|
|||
# Print test summary |
|||
log_success "All tests completed successfully!" |
|||
print_footer "Test Suite" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,74 @@ |
|||
#!/bin/bash |
|||
# test-common.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Test script to verify common utilities work correctly |
|||
# This script tests the common.sh utilities to ensure they function properly. |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print test header |
|||
print_header "Common Utilities Test" |
|||
log_info "Testing common utilities at $(date)" |
|||
|
|||
# Test logging functions |
|||
log_info "Testing info logging" |
|||
log_success "Testing success logging" |
|||
log_warn "Testing warning logging" |
|||
log_error "Testing error logging (this is expected)" |
|||
log_debug "Testing debug logging" |
|||
log_step "Testing step logging" |
|||
|
|||
# Test timing function |
|||
log_info "Testing timing function..." |
|||
measure_time sleep 1 |
|||
|
|||
# Test command checking |
|||
log_info "Testing command checking..." |
|||
if check_command "echo"; then |
|||
log_success "echo command found" |
|||
else |
|||
log_error "echo command not found" |
|||
fi |
|||
|
|||
# Test directory checking |
|||
log_info "Testing directory checking..." |
|||
if check_directory "scripts"; then |
|||
log_success "scripts directory found" |
|||
else |
|||
log_error "scripts directory not found" |
|||
fi |
|||
|
|||
# Test file checking |
|||
log_info "Testing file checking..." |
|||
if check_file "scripts/common.sh"; then |
|||
log_success "common.sh file found" |
|||
else |
|||
log_error "common.sh file not found" |
|||
fi |
|||
|
|||
# Test git hash function |
|||
log_info "Testing git hash function..." |
|||
GIT_HASH=$(get_git_hash) |
|||
log_info "Git hash: $GIT_HASH" |
|||
|
|||
# Test safe execute |
|||
log_info "Testing safe execute..." |
|||
safe_execute "Testing safe execute" "echo 'Hello from safe_execute'" |
|||
|
|||
# Test build artifact cleaning |
|||
log_info "Testing build artifact cleaning..." |
|||
clean_build_artifacts "test-file-1" "test-file-2" |
|||
|
|||
# Print test summary |
|||
log_success "All common utilities tests completed successfully!" |
|||
print_footer "Common Utilities Test" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,59 @@ |
|||
#!/bin/bash |
|||
# test-env.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Test script to verify environment variable handling |
|||
# This script tests the environment variable setup functions. |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print test header |
|||
print_header "Environment Variable Test" |
|||
log_info "Testing environment variable handling at $(date)" |
|||
|
|||
# Test 1: Electron environment |
|||
log_info "Test 1: Setting up Electron environment..." |
|||
setup_build_env "electron" |
|||
print_env_vars "VITE_" |
|||
|
|||
# Test 2: Capacitor environment |
|||
log_info "Test 2: Setting up Capacitor environment..." |
|||
setup_build_env "capacitor" |
|||
print_env_vars "VITE_" |
|||
|
|||
# Test 3: Web environment |
|||
log_info "Test 3: Setting up Web environment..." |
|||
setup_build_env "web" |
|||
print_env_vars "VITE_" |
|||
|
|||
# Test 4: Production Electron environment |
|||
log_info "Test 4: Setting up Production Electron environment..." |
|||
setup_build_env "electron" "true" |
|||
print_env_vars "VITE_" |
|||
print_env_vars "NODE_ENV" |
|||
|
|||
# Test 5: Application directories |
|||
log_info "Test 5: Setting up application directories..." |
|||
setup_app_directories |
|||
|
|||
# Test 6: Load .env file (if it exists) |
|||
log_info "Test 6: Loading .env file..." |
|||
load_env_file ".env" |
|||
|
|||
# Test 7: Git hash |
|||
log_info "Test 7: Getting git hash..." |
|||
GIT_HASH=$(get_git_hash) |
|||
log_info "Git hash: $GIT_HASH" |
|||
|
|||
# Print test summary |
|||
log_success "All environment variable tests completed successfully!" |
|||
print_footer "Environment Variable Test" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -0,0 +1,40 @@ |
|||
#!/bin/bash |
|||
# test-mobile.sh |
|||
# Author: Matthew Raymer |
|||
# Description: Mobile test suite for TimeSafari application |
|||
# This script builds the Capacitor version and runs Android and iOS tests |
|||
# with proper error handling and logging. |
|||
# |
|||
# Exit Codes: |
|||
# 1 - Capacitor build failed |
|||
# 2 - Android tests failed |
|||
# 3 - iOS tests failed |
|||
|
|||
# Exit on any error |
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Parse command line arguments |
|||
parse_args "$@" |
|||
|
|||
# Print test header |
|||
print_header "TimeSafari Mobile Test Suite" |
|||
log_info "Starting mobile test suite at $(date)" |
|||
|
|||
# Step 1: Build Capacitor version |
|||
safe_execute "Building Capacitor version" "npm run build:capacitor" || exit 1 |
|||
|
|||
# Step 2: Run Android tests |
|||
safe_execute "Running Android tests" "npm run test:android" || exit 2 |
|||
|
|||
# Step 3: Run iOS tests |
|||
safe_execute "Running iOS tests" "npm run test:ios" || exit 3 |
|||
|
|||
# Print test summary |
|||
log_success "Mobile test suite completed successfully!" |
|||
print_footer "Mobile Test Suite" |
|||
|
|||
# Exit with success |
|||
exit 0 |
@ -1,4 +0,0 @@ |
|||
import { initializeApp } from "./main.common"; |
|||
|
|||
const app = initializeApp(); |
|||
app.mount("#app"); |
@ -1,59 +0,0 @@ |
|||
import webview |
|||
import os |
|||
import sys |
|||
from http.server import HTTPServer, SimpleHTTPRequestHandler |
|||
import threading |
|||
|
|||
def get_dist_path(): |
|||
if getattr(sys, 'frozen', False): |
|||
base_path = sys._MEIPASS |
|||
else: |
|||
base_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|||
|
|||
dist_path = os.path.join(base_path, 'dist') |
|||
print(f"Dist path: {dist_path}") |
|||
return dist_path |
|||
|
|||
class CustomHandler(SimpleHTTPRequestHandler): |
|||
def __init__(self, *args, **kwargs): |
|||
dist_dir = get_dist_path() |
|||
print(f"Serving from directory: {dist_dir}") |
|||
super().__init__(*args, directory=dist_dir, **kwargs) |
|||
|
|||
def log_message(self, format, *args): |
|||
# Override to show more detailed logging |
|||
print(f"Request: {format%args}") |
|||
if hasattr(self, 'path'): |
|||
print(f"Requested path: {self.path}") |
|||
full_path = os.path.join(self.directory, self.path.lstrip('/')) |
|||
print(f"Full path: {full_path}") |
|||
print(f"File exists: {os.path.exists(full_path)}") |
|||
if self.path.endswith('.html'): |
|||
print(f"HTML content: {open(full_path).read()[:200]}...") |
|||
|
|||
def run_server(port=8000): |
|||
server_address = ('localhost', port) |
|||
httpd = HTTPServer(server_address, CustomHandler) |
|||
print(f"Serving files from {get_dist_path()} at http://localhost:{port}") |
|||
httpd.serve_forever() |
|||
|
|||
def main(): |
|||
dist_path = get_dist_path() |
|||
|
|||
# Start local server |
|||
server_thread = threading.Thread(target=run_server) |
|||
server_thread.daemon = True |
|||
server_thread.start() |
|||
|
|||
# Create window using local server |
|||
window = webview.create_window( |
|||
'TimeSafari', |
|||
url='http://localhost:8000', |
|||
width=1200, |
|||
height=800 |
|||
) |
|||
|
|||
webview.start(debug=True) |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -1,135 +0,0 @@ |
|||
import { |
|||
ImageResult, |
|||
PlatformService, |
|||
PlatformCapabilities, |
|||
} from "../PlatformService"; |
|||
import { logger } from "../../utils/logger"; |
|||
import { QueryExecResult } from "@/interfaces/database"; |
|||
|
|||
/** |
|||
* Platform service implementation for PyWebView platform. |
|||
* Note: This is a placeholder implementation with most methods currently unimplemented. |
|||
* Implements the PlatformService interface but throws "Not implemented" errors for most operations. |
|||
* |
|||
* @remarks |
|||
* This service is intended for Python-based desktop applications using pywebview. |
|||
* Future implementations should provide: |
|||
* - Integration with Python backend file operations |
|||
* - System camera access through Python |
|||
* - Native system dialogs via pywebview |
|||
* - Python-JavaScript bridge functionality |
|||
*/ |
|||
export class PyWebViewPlatformService implements PlatformService { |
|||
/** |
|||
* Gets the capabilities of the PyWebView platform |
|||
* @returns Platform capabilities object |
|||
*/ |
|||
getCapabilities(): PlatformCapabilities { |
|||
return { |
|||
hasFileSystem: false, // Not implemented yet
|
|||
hasCamera: false, // Not implemented yet
|
|||
isMobile: false, |
|||
isIOS: false, |
|||
hasFileDownload: false, // Not implemented yet
|
|||
needsFileHandlingInstructions: false, |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Reads a file using the Python backend. |
|||
* @param _path - Path to the file to read |
|||
* @returns Promise that should resolve to file contents |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement file reading through pywebview's Python-JavaScript bridge |
|||
*/ |
|||
async readFile(_path: string): Promise<string> { |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Writes content to a file using the Python backend. |
|||
* @param _path - Path where to write the file |
|||
* @param _content - Content to write to the file |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement file writing through pywebview's Python-JavaScript bridge |
|||
*/ |
|||
async writeFile(_path: string, _content: string): Promise<void> { |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Deletes a file using the Python backend. |
|||
* @param _path - Path to the file to delete |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement file deletion through pywebview's Python-JavaScript bridge |
|||
*/ |
|||
async deleteFile(_path: string): Promise<void> { |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Lists files in the specified directory using the Python backend. |
|||
* @param _directory - Path to the directory to list |
|||
* @returns Promise that should resolve to array of filenames |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement directory listing through pywebview's Python-JavaScript bridge |
|||
*/ |
|||
async listFiles(_directory: string): Promise<string[]> { |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Should open system camera through Python backend. |
|||
* @returns Promise that should resolve to captured image data |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement camera access using Python's camera libraries |
|||
*/ |
|||
async takePicture(): Promise<ImageResult> { |
|||
logger.error("takePicture not implemented in PyWebView platform"); |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Should open system file picker through pywebview. |
|||
* @returns Promise that should resolve to selected image data |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement file picker using pywebview's file dialog API |
|||
*/ |
|||
async pickImage(): Promise<ImageResult> { |
|||
logger.error("pickImage not implemented in PyWebView platform"); |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
/** |
|||
* Should handle deep link URLs through the Python backend. |
|||
* @param _url - The deep link URL to handle |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement deep link handling using Python's URL handling capabilities |
|||
*/ |
|||
async handleDeepLink(_url: string): Promise<void> { |
|||
logger.error("handleDeepLink not implemented in PyWebView platform"); |
|||
throw new Error("Not implemented"); |
|||
} |
|||
|
|||
dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> { |
|||
throw new Error("Not implemented for " + sql + " with params " + params); |
|||
} |
|||
dbExec( |
|||
sql: string, |
|||
params?: unknown[], |
|||
): Promise<{ changes: number; lastId?: number }> { |
|||
throw new Error("Not implemented for " + sql + " with params " + params); |
|||
} |
|||
|
|||
/** |
|||
* Should write and share a file using the Python backend. |
|||
* @param _fileName - Name of the file to write and share |
|||
* @param _content - Content to write to the file |
|||
* @throws Error with "Not implemented" message |
|||
* @todo Implement file writing and sharing through pywebview's Python-JavaScript bridge |
|||
*/ |
|||
async writeAndShareFile(_fileName: string, _content: string): Promise<void> { |
|||
logger.error("writeAndShareFile not implemented in PyWebView platform"); |
|||
throw new Error("Not implemented"); |
|||
} |
|||
} |
@ -1,4 +0,0 @@ |
|||
import { defineConfig } from "vite"; |
|||
import { createBuildConfig } from "./vite.config.common.mts"; |
|||
|
|||
export default defineConfig(async () => createBuildConfig('pywebview')); |
Loading…
Reference in new issue