- docs/ARCHITECTURE.md - Clean Architecture patterns and conventions - docs/API_INTEGRATION.md - Core API and Tatlock API endpoints - docs/DEPLOYMENT.md - Docker, NPM, Portainer setup - docs/DATAGRID.md - DataGrid component specification - docs/THEMING.md - Material 3 theming guide Also update seed color to teal for consistency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
Deployment Guide
This document covers deploying Tatlock UI to the Tower of Joy infrastructure.
Overview
Internet → NPM (home.schweitz.net:443) → tatlock-ui container (port 8092) → Flutter web
Web Build
Local Build
# Generate version info
dart run tool/generate_version.dart
# Build for web release
flutter build web --release
Build output: build/web/
Build Optimizations
For production builds, consider:
flutter build web --release \
--dart-define=FLUTTER_WEB_USE_SKIA=true \
--tree-shake-icons
Docker
Dockerfile
# Build stage
FROM ghcr.io/cirruslabs/flutter:stable AS build
WORKDIR /app
# Copy dependency files
COPY pubspec.* ./
RUN flutter pub get
# Copy source and build
COPY . .
RUN dart run tool/generate_version.dart
RUN flutter build web --release
# Production stage
FROM nginx:alpine
# Copy built web app
COPY --from=build /app/build/web /usr/share/nginx/html
# Copy nginx config (optional)
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf (Optional)
For SPA routing support:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# SPA routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Don't cache index.html
location = /index.html {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
Build Image Locally
docker build -t tatlock-ui:latest .
# Test locally
docker run -p 8092:80 tatlock-ui:latest
Portainer Stack
docker-compose.yml
version: '3.8'
services:
tatlock-ui:
image: git.schweitz.net/jpmschweitzer/tatlock-ui:latest
container_name: tatlock-ui
restart: unless-stopped
ports:
- "8092:80"
networks:
- docker-dataplane
labels:
- "com.centurylinklabs.watchtower.enable=true"
networks:
docker-dataplane:
external: true
Deploy to Portainer
- Go to Portainer: https://portainer.schweitz.net
- Navigate to Stacks → Add Stack
- Name:
tatlock-ui - Paste docker-compose.yml content
- Deploy
Nginx Proxy Manager
Proxy Host Configuration
| Setting | Value |
|---|---|
| Domain | home.schweitz.net |
| Scheme | http |
| Forward Hostname | tatlock-ui (container name) |
| Forward Port | 80 |
SSL
| Setting | Value |
|---|---|
| SSL Certificate | Let's Encrypt |
| Force SSL | Yes |
| HTTP/2 | Yes |
| HSTS | Yes |
Advanced (Custom Nginx)
# WebSocket support (if needed for future features)
location /ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
CI/CD Pipeline
Gitea Actions Workflow
Create .gitea/workflows/build.yml:
name: Build and Deploy
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
test:
runs-on: ubuntu-latest
container:
image: ghcr.io/cirruslabs/flutter:stable
steps:
- uses: actions/checkout@v4
- name: Get dependencies
run: flutter pub get
- name: Generate version
run: dart run tool/generate_version.dart
- name: Analyze
run: flutter analyze
- name: Test
run: flutter test
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Registry
uses: docker/login-action@v3
with:
registry: git.schweitz.net
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: git.schweitz.net/jpmschweitzer/tatlock-ui:latest
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
steps:
- name: Trigger Watchtower
run: |
curl -X POST "http://watchtower:8080/v1/update" \
-H "Authorization: Bearer ${{ secrets.WATCHTOWER_TOKEN }}"
Environment Configuration
Runtime Configuration
For environment-specific settings, use compile-time defines:
flutter build web --release \
--dart-define=API_URL=https://api.schweitz.net \
--dart-define=AUTH_URL=https://auth.schweitz.net
Access in code:
class AppConfig {
static const apiUrl = String.fromEnvironment(
'API_URL',
defaultValue: 'https://api.schweitz.net',
);
static const authUrl = String.fromEnvironment(
'AUTH_URL',
defaultValue: 'https://auth.schweitz.net',
);
}
Health Monitoring
Container Health Check
Add to Dockerfile:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/ || exit 1
Uptime Monitoring
Add to your monitoring solution (Uptime Kuma, etc.):
- URL:
https://home.schweitz.net - Interval: 60s
- Expected status: 200
Rollback
Quick Rollback
If issues arise after deployment:
# In Portainer, update image tag to previous version
git.schweitz.net/jpmschweitzer/tatlock-ui:previous-tag
# Or via CLI
docker pull git.schweitz.net/jpmschweitzer/tatlock-ui:v0.1.0
docker stop tatlock-ui
docker rm tatlock-ui
docker run -d --name tatlock-ui -p 8092:80 git.schweitz.net/jpmschweitzer/tatlock-ui:v0.1.0
Troubleshooting
Container won't start
# Check logs
docker logs tatlock-ui
# Common issues:
# - Port 8092 already in use
# - Network not found (create docker-dataplane network)
502 Bad Gateway in NPM
- Verify container is running:
docker ps | grep tatlock-ui - Check container is on correct network
- Verify port mapping in docker-compose
Assets not loading
Check nginx is serving from correct path and CORS headers if loading from different domain.