Add Sigvild Gallery wedding photo application with automated deployment and improve Caddy plugin management

This commit is contained in:
2025-11-15 16:13:18 +01:00
parent 8162e789ee
commit 7c3b02e5ad
16 changed files with 923 additions and 10 deletions

View File

@@ -4,6 +4,7 @@ host_key_checking = False
remote_user = root
deprecation_warnings = False
vault_password_file = vault-password-file
roles_path = roles
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

View File

@@ -0,0 +1,262 @@
# Sigvild Gallery Deployment Guide
## Quick Start
Deploy the complete Sigvild Wedding Gallery with PocketBase API and SvelteKit frontend.
## Prerequisites Setup
### 1. Vault Password Configuration
Create encrypted passwords for the gallery authentication:
```bash
# Create vault passwords (run from rick-infra directory)
ansible-vault encrypt_string 'your-host-password-here' --name 'vault_sigvild_host_password'
ansible-vault encrypt_string 'your-guest-password-here' --name 'vault_sigvild_guest_password'
```
Add the encrypted strings to `host_vars/arch-vps/main.yml`:
```yaml
# Add to host_vars/arch-vps/main.yml
vault_sigvild_host_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653765386...
vault_sigvild_guest_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
33663065383834313...
```
### 2. DNS Configuration
Ensure these domains point to your server:
- `sigvild.no` → Frontend static site
- `api.sigvild.no` → API backend proxy
### 3. Project Structure
Ensure the sigvild-gallery project is adjacent to rick-infra:
```
~/
├── rick-infra/ # This repository
└── sigvild-gallery/ # Sigvild gallery project
├── build_tmp/ # Production builds
├── sigvild-kit/ # Frontend source
└── main.go # Backend source
```
## Deployment Commands
### Full Infrastructure + Gallery
Deploy everything including Sigvild Gallery:
```bash
ansible-playbook site.yml
```
### Gallery Only
Deploy just the Sigvild Gallery service:
```bash
ansible-playbook playbooks/deploy-sigvild.yml
```
### Selective Updates
Update specific components:
```bash
# Frontend only (quick static file updates)
ansible-playbook site.yml --tags="frontend"
# Backend only (API service updates)
ansible-playbook site.yml --tags="backend"
# Caddy configuration only
ansible-playbook site.yml --tags="caddy"
# Just build process (development)
ansible-playbook site.yml --tags="build"
```
## Architecture Overview
```
Internet
Caddy (Auto HTTPS)
├── sigvild.no → /var/www/sigvild-gallery/ (Static Files)
└── api.sigvild.no → localhost:8090 (PocketBase API)
Go Binary (sigvild-gallery-server)
SQLite Database + File Storage
```
## Service Management
### Status Checks
```bash
# Gallery API service
systemctl status sigvild-gallery
# Caddy web server
systemctl status caddy
# View gallery logs
journalctl -u sigvild-gallery -f
# View Caddy logs
journalctl -u caddy -f
```
### Manual Operations
```bash
# Restart gallery service
systemctl restart sigvild-gallery
# Reload Caddy configuration
systemctl reload caddy
# Check API health
curl https://api.sigvild.no/api/health
```
## Troubleshooting
### Build Issues
**Problem**: Go build fails
```bash
# Ensure Go is installed locally
go version
# Check if you're in the right directory
ls sigvild-gallery/main.go
```
**Problem**: Frontend build fails
```bash
# Check Node.js and npm
node --version && npm --version
# Ensure dependencies are installed
cd sigvild-gallery/sigvild-kit
npm install
```
### Service Issues
**Problem**: Service won't start
```bash
# Check service status
systemctl status sigvild-gallery
# Check service logs
journalctl -u sigvild-gallery --no-pager
# Verify binary permissions
ls -la /opt/sigvild-gallery/sigvild-gallery-server
```
**Problem**: Database permissions
```bash
# Check data directory ownership
ls -la /opt/sigvild-gallery/data/
# Fix ownership if needed
sudo chown -R sigvild:sigvild /opt/sigvild-gallery/
```
### Network Issues
**Problem**: Domain not resolving
```bash
# Test DNS resolution
dig sigvild.no
dig api.sigvild.no
# Test local connectivity
curl -H "Host: sigvild.no" http://localhost
curl -H "Host: api.sigvild.no" http://localhost
```
**Problem**: HTTPS certificate issues
```bash
# Check Caddy logs for ACME errors
journalctl -u caddy | grep -i "acme\|certificate"
# Verify DNS challenge credentials
# (Check Cloudflare API token in vault)
```
## Security Features
### Environment Protection
- **No .env files**: Secrets stored in systemd environment variables only
- **Vault encryption**: All passwords encrypted with ansible-vault
- **Memory isolation**: Secrets only exist in process memory
### SystemD Sandboxing
- **Read-only filesystem**: Application cannot modify system files
- **Isolated temporary**: Private /tmp directory
- **Limited capabilities**: No privilege escalation possible
- **Data directory only**: Write access restricted to /opt/sigvild-gallery/data/
### Web Security
- **Automatic HTTPS**: Let's Encrypt certificates via DNS challenge
- **Security headers**: XSS protection, frame options, content type sniffing prevention
- **CORS restrictions**: API access limited to frontend domain
- **Rate limiting**: API endpoint protection
## File Locations
### Application Files
- **Binary**: `/opt/sigvild-gallery/sigvild-gallery-server`
- **Database**: `/opt/sigvild-gallery/data/data.db`
- **File uploads**: `/opt/sigvild-gallery/data/storage/`
- **Frontend**: `/var/www/sigvild-gallery/`
### Configuration Files
- **Service**: `/etc/systemd/system/sigvild-gallery.service`
- **Caddy frontend**: `/etc/caddy/sites-enabled/sigvild-frontend.caddy`
- **Caddy API**: `/etc/caddy/sites-enabled/sigvild-api.caddy`
### Log Files
- **Service logs**: `journalctl -u sigvild-gallery`
- **Caddy logs**: `journalctl -u caddy`
- **Access logs**: `/var/log/caddy/sigvild-*.log`
## Next Steps After Deployment
1. **Verify services**: Check that both domains are accessible
2. **Test authentication**: Login with host/guest credentials
3. **Upload test photo**: Verify file upload functionality
4. **Monitor logs**: Watch for any errors in service logs
5. **Backup setup**: Configure regular database backups
## Development Workflow
For ongoing development:
```bash
# 1. Make changes to sigvild-gallery project
cd ../sigvild-gallery
# 2. Test locally
go run . serve &
cd sigvild-kit && npm run dev
# 3. Deploy updates
cd ../rick-infra
ansible-playbook site.yml --tags="sigvild"
```
The deployment system builds locally and transfers assets, so you don't need build tools on the server.

View File

@@ -35,6 +35,20 @@ caddy_server_name: "main"
# service_backend: "localhost:8080"
# notify: register service with caddy
# =================================================================
# Sigvild Gallery Configuration
# =================================================================
sigvild_gallery_frontend_domain: "sigvild.no"
sigvild_gallery_api_domain: "api.sigvild.no"
sigvild_gallery_local_project_path: "~/sigvild-gallery/"
# Vault-encrypted passwords (create with ansible-vault)
sigvild_gallery_pb_su_email: "{{ vault_pb_su_email}}"
sigvild_gallery_pb_su_password: "{{ vault_pb_su_password}}"
sigvild_gallery_host_password: "{{ vault_sigvild_host_password }}"
sigvild_gallery_guest_password: "{{ vault_sigvild_guest_password }}"
# =================================================================
# Security & Logging
# =================================================================

View File

@@ -0,0 +1,52 @@
---
# Sigvild Gallery Deployment Playbook
- name: Deploy Sigvild Wedding Gallery
hosts: arch-vps
become: yes
gather_facts: yes
vars:
# Local project path - adjust as needed
sigvild_gallery_local_project_path: "{{ ansible_env.PWD }}/../sigvild-gallery"
pre_tasks:
- name: Verify local sigvild-gallery project exists
local_action:
module: stat
path: "{{ sigvild_gallery_local_project_path }}"
register: project_exists
become: no
- name: Fail if project directory doesn't exist
fail:
msg: "Sigvild Gallery project not found at {{ sigvild_gallery_local_project_path }}"
when: not project_exists.stat.exists
- name: Display deployment information
debug:
msg:
- "Deploying Sigvild Gallery from: {{ sigvild_gallery_local_project_path }}"
- "Frontend domain: {{ sigvild_gallery_frontend_domain }}"
- "API domain: {{ sigvild_gallery_api_domain }}"
roles:
- role: sigvild-gallery
tags: ['sigvild', 'gallery', 'wedding']
post_tasks:
- name: Wait for API to be ready
wait_for:
port: "{{ sigvild_gallery_port }}"
host: "{{ sigvild_gallery_host }}"
timeout: 60
tags: [verify]
- name: Display deployment results
debug:
msg:
- "✅ Sigvild Gallery deployment completed!"
- "Frontend: https://{{ sigvild_gallery_frontend_domain }}"
- "API: https://{{ sigvild_gallery_api_domain }}"
- "Service status: systemctl status sigvild-gallery"
- "Logs: journalctl -u sigvild-gallery -f"

View File

@@ -3,11 +3,25 @@
set_fact:
dns_challenge_needed: "{{ caddy_dns_provider == 'cloudflare' and cloudflare_api_token != '' }}"
- name: Check if Caddy is already installed
command: /usr/bin/caddy version
register: caddy_version_check
failed_when: false
changed_when: false
when: dns_challenge_needed | bool
- name: Check if installed Caddy has Cloudflare plugin
command: /usr/bin/caddy list-modules --packages
register: caddy_modules_check
failed_when: false
changed_when: false
when: dns_challenge_needed | bool and caddy_version_check.rc == 0
- name: Install standard Caddy (if no DNS challenge needed)
pacman:
name: caddy
state: present
when: not dns_challenge_needed | bool
when: not dns_challenge_needed and not caddy_version_check | bool
notify: restart caddy
- name: Download Caddy with Cloudflare plugin (if DNS challenge needed)
@@ -15,7 +29,9 @@
url: "https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com/caddy-dns/cloudflare"
dest: /tmp/caddy-with-cloudflare
mode: '0755'
when: dns_challenge_needed | bool
when:
- dns_challenge_needed | bool
- caddy_version_check.rc != 0 or 'github.com/caddy-dns/cloudflare' not in caddy_modules_check.stdout | default('')
- name: Install Caddy with Cloudflare plugin
copy:
@@ -24,7 +40,7 @@
mode: '0755'
remote_src: yes
backup: yes
when: dns_challenge_needed | bool
when: dns_challenge_needed and caddy_version_check | bool
notify: restart caddy
- name: Clean up temporary Caddy binary

View File

@@ -0,0 +1,180 @@
# Sigvild Gallery Ansible Role
Deploys the Sigvild Wedding Gallery application with PocketBase API backend and SvelteKit frontend.
## Architecture
- **Backend**: PocketBase-based Go application serving API on localhost:8090
- **Frontend**: SvelteKit static site served by Caddy
- **Database**: SQLite via PocketBase (file-based storage)
- **Authentication**: Shared password system (host/guest users)
- **Domains**:
- `sigvild.no` → Frontend static files
- `api.sigvild.no` → Backend API proxy
## Prerequisites
- Caddy role deployed and configured
- Local sigvild-gallery project with built assets in `build_tmp/`
- Vault-encrypted passwords configured in inventory
## Variables
### Required Variables
```yaml
# Domains
sigvild_gallery_frontend_domain: "sigvild.no"
sigvild_gallery_api_domain: "api.sigvild.no"
# Vault-encrypted passwords
vault_sigvild_host_password: "your-encrypted-host-password"
vault_sigvild_guest_password: "your-encrypted-guest-password"
```
### Optional Variables
```yaml
# Service configuration
sigvild_gallery_user: "sigvild"
sigvild_gallery_port: 8090
sigvild_gallery_host: "127.0.0.1"
# Paths
sigvild_gallery_home: "/opt/sigvild-gallery"
sigvild_gallery_web_root: "/var/www/sigvild-gallery"
sigvild_gallery_local_project_path: "{{ ansible_env.PWD }}/sigvild-gallery"
```
## Usage
### Full Deployment
```bash
# Deploy complete infrastructure including Sigvild Gallery
ansible-playbook site.yml
# Deploy just Sigvild Gallery
ansible-playbook playbooks/deploy-sigvild.yml
```
### Selective Updates
```bash
# Update just the frontend
ansible-playbook site.yml --tags="frontend"
# Update just the backend API
ansible-playbook site.yml --tags="backend"
# Update Caddy configuration
ansible-playbook site.yml --tags="caddy"
```
## Security Features
### Environment Variables
- **No .env files**: Secrets managed via systemd Environment directives
- **Vault encrypted**: Passwords stored in Ansible vault
- **Memory-only**: Environment variables only exist in process memory
### SystemD Sandboxing
- `NoNewPrivileges=yes`: Prevents privilege escalation
- `PrivateTmp=yes`: Isolated temporary directory
- `ProtectSystem=strict`: Read-only filesystem protection
- `ProtectHome=yes`: Home directory protection
- `ReadWritePaths`: Only data directory is writable
### Caddy Security
- **Security headers**: XSS protection, frame options, content type sniffing prevention
- **CORS configuration**: Restricted to frontend domain
- **Rate limiting**: API endpoint protection
- **HTTPS only**: Automatic TLS with Let's Encrypt
## Directory Structure
```
/opt/sigvild-gallery/ # Application home
├── sigvild-gallery-server # Go binary
└── data/ # PocketBase data directory
├── data.db # SQLite database
└── storage/ # File uploads
/var/www/sigvild-gallery/ # Frontend web root
├── index.html # SvelteKit build
├── _app/ # Application assets
└── assets/ # Static assets
/etc/systemd/system/
└── sigvild-gallery.service # SystemD service
/etc/caddy/sites-enabled/
├── sigvild-frontend.caddy # Frontend configuration
└── sigvild-api.caddy # API proxy configuration
```
## Build Process
The role performs local builds then transfers assets:
1. **Backend**: `GOOS=linux GOARCH=amd64 go build -o sigvild-gallery-server .`
2. **Frontend**: `npm run build` in `sigvild-kit/` directory
3. **Transfer**: Copy binary and sync frontend build to server
4. **Deploy**: Update systemd service and Caddy configuration
## Service Management
```bash
# Check service status
systemctl status sigvild-gallery
# View logs
journalctl -u sigvild-gallery -f
# Restart service
systemctl restart sigvild-gallery
# Reload Caddy configuration
systemctl reload caddy
```
## Troubleshooting
### Build Failures
- Ensure Go toolchain is available locally
- Verify `sigvild-kit/` directory exists with `package.json`
- Check Node.js and npm are installed for frontend builds
### Service Startup Issues
- Check systemd logs: `journalctl -u sigvild-gallery`
- Verify binary permissions and ownership
- Ensure data directory is writable by service user
### Domain Resolution
- Verify DNS records point to server IP
- Check Caddy logs: `journalctl -u caddy`
- Test local connectivity: `curl -H "Host: api.sigvild.no" http://localhost:8090`
## Dependencies
- **caddy**: Required for web server and reverse proxy
- **systemd**: Service management
- **Local build tools**: Go compiler, Node.js/npm
## Files Created
- `/etc/systemd/system/sigvild-gallery.service`
- `/etc/caddy/sites-enabled/sigvild-frontend.caddy`
- `/etc/caddy/sites-enabled/sigvild-api.caddy`
- `/opt/sigvild-gallery/` (application directory)
- `/var/www/sigvild-gallery/` (frontend files)
## Tags
- `sigvild`: Complete Sigvild Gallery deployment
- `backend`: API service deployment
- `frontend`: Static site deployment
- `build`: Local build processes
- `service`: SystemD service management
- `caddy`: Caddy configuration
- `verify`: Post-deployment verification

View File

@@ -0,0 +1,36 @@
---
# Sigvild Gallery Ansible Role - Default Variables
# Service Configuration
sigvild_gallery_user: sigvild
# Paths
sigvild_gallery_home: /opt/sigvild-gallery
sigvild_gallery_web_root: /var/www/sigvild-gallery
sigvild_gallery_binary: "{{ sigvild_gallery_home }}/sigvild-gallery"
sigvild_gallery_data_dir: "{{ sigvild_gallery_home }}/pb_data"
# Domains
sigvild_gallery_frontend_domain: sigvild.no
sigvild_gallery_api_domain: api.sigvild.no
# Backend Service
sigvild_gallery_port: 8090
sigvild_gallery_host: "127.0.0.1"
# Environment Variables (for SystemD service)
sigvild_gallery_host_username: host
sigvild_gallery_host_password: "{{ vault_sigvild_host_password }}"
sigvild_gallery_guest_username: guest
sigvild_gallery_guest_password: "{{ vault_sigvild_guest_password }}"
# Build configuration
sigvild_gallery_local_project_path: "{{ ansible_env.PWD }}/sigvild-gallery"
# Service configuration
sigvild_gallery_service_enabled: true
sigvild_gallery_service_state: started
# Caddy integration (assumes caddy role provides these)
# caddy_sites_enabled_dir: /etc/caddy/sites-enabled
# caddy_user: caddy

View File

@@ -0,0 +1,16 @@
---
# Sigvild Gallery Handlers
- name: reload systemd
systemd:
daemon_reload: yes
- name: restart sigvild-gallery
systemd:
name: sigvild-gallery
state: restarted
- name: reload caddy
systemd:
name: caddy
state: reloaded

View File

@@ -0,0 +1,20 @@
---
# Role Dependencies
dependencies:
- role: caddy
galaxy_info:
role_name: sigvild-gallery
author: "Rick Infrastructure Team"
description: "Deploys Sigvild Wedding Gallery with PocketBase API and SvelteKit frontend"
company: ""
license: "license (MIT)"
min_ansible_version: "2.9"
platforms:
- name: Archlinux
versions:
- all
- name: Ubuntu
versions:
- all

View File

@@ -0,0 +1,43 @@
---
# Backend Deployment Tasks
- name: Build Go binary locally
local_action:
module: shell
cmd: GOOS=linux GOARCH=amd64 go build -o sigvild-gallery .
chdir: "{{ sigvild_gallery_local_project_path }}"
become: no
tags: [backend, build]
- name: Check if binary was built successfully
local_action:
module: stat
path: "{{ sigvild_gallery_local_project_path }}/sigvild-gallery"
register: binary_stat
become: no
tags: [backend, build]
- name: Fail if binary doesn't exist
fail:
msg: "Failed to build sigvild-gallery binary"
when: not binary_stat.stat.exists
tags: [backend, build]
- name: Transfer Go binary
copy:
src: "{{ sigvild_gallery_local_project_path }}/sigvild-gallery"
dest: "{{ sigvild_gallery_binary }}"
owner: "{{ sigvild_gallery_user }}"
group: "{{ sigvild_gallery_user }}"
mode: '0755'
notify: restart sigvild-gallery
tags: [backend]
- name: Create data directory for PocketBase
file:
path: "{{ sigvild_gallery_data_dir }}"
state: directory
owner: "{{ sigvild_gallery_user }}"
group: "{{ sigvild_gallery_user }}"
mode: '0755'
tags: [backend]

View File

@@ -0,0 +1,57 @@
---
# Frontend Deployment Tasks
- name: Check if frontend source exists
local_action:
module: stat
path: "{{ sigvild_gallery_local_project_path }}/sigvild-kit"
register: frontend_source
become: no
tags: [frontend, build]
- name: Fail if frontend source doesn't exist
fail:
msg: "Frontend source directory not found at {{ sigvild_gallery_local_project_path }}/sigvild-kit"
when: not frontend_source.stat.exists
tags: [frontend, build]
- name: Install frontend dependencies
local_action:
module: shell
cmd: npm install
chdir: "{{ sigvild_gallery_local_project_path }}/sigvild-kit"
become: no
tags: [frontend, build]
- name: Build frontend for production
local_action:
module: shell
cmd: npm run build:production
chdir: "{{ sigvild_gallery_local_project_path }}/sigvild-kit"
become: no
tags: [frontend, build]
- name: Check if frontend build exists
local_action:
module: stat
path: "{{ sigvild_gallery_local_project_path }}/sigvild-kit/build"
register: frontend_build
become: no
tags: [frontend, build]
- name: Fail if frontend build doesn't exist
fail:
msg: "Frontend build failed - build directory not found"
when: not frontend_build.stat.exists
become: no
tags: [frontend, build]
- name: Sync frontend files to web root
synchronize:
src: "{{ sigvild_gallery_local_project_path }}/sigvild-kit/build/"
dest: "{{ sigvild_gallery_web_root }}/"
delete: yes
rsync_opts:
- "--exclude=.git"
- "--chown={{ sigvild_gallery_user }}:{{ sigvild_gallery_user }}"
tags: [frontend]

View File

@@ -0,0 +1,91 @@
---
# Sigvild Gallery Deployment Tasks
- name: Install required packages
pacman:
name:
- rsync
state: present
- name: Create sigvild gallery user
user:
name: "{{ sigvild_gallery_user }}"
system: yes
shell: /bin/bash
home: "{{ sigvild_gallery_home }}"
create_home: yes
- name: Create directories
file:
path: "{{ item }}"
state: directory
owner: "{{ sigvild_gallery_user }}"
group: "{{ sigvild_gallery_user }}"
mode: '0755'
loop:
- "{{ sigvild_gallery_home }}"
- "{{ sigvild_gallery_data_dir }}"
- "{{ sigvild_gallery_web_root }}"
- name: Build and deploy backend
include_tasks: deploy_backend.yml
tags: [backend, build]
- name: Build and deploy frontend
include_tasks: deploy_frontend.yml
tags: [frontend, build]
- name: Deploy systemd service
template:
src: sigvild-gallery.service.j2
dest: /etc/systemd/system/sigvild-gallery.service
owner: root
group: root
mode: '0644'
notify:
- reload systemd
- restart sigvild-gallery
tags: [backend, service]
- name: Deploy Caddy configurations
template:
src: "{{ item.src }}"
dest: "{{ caddy_sites_enabled_dir }}/{{ item.dest }}"
owner: root
group: "{{ caddy_user }}"
mode: '0644'
loop:
- { src: 'frontend.caddy.j2', dest: 'sigvild-frontend.caddy' }
- { src: 'api.caddy.j2', dest: 'sigvild-api.caddy' }
notify: reload caddy
tags: [caddy, frontend, backend]
- name: Enable and start sigvild-gallery service
systemd:
name: sigvild-gallery
enabled: "{{ sigvild_gallery_service_enabled }}"
state: "{{ sigvild_gallery_service_state }}"
daemon_reload: yes
tags: [backend, service]
- name: Create superuser account
command: >
{{ sigvild_gallery_binary }} superuser upsert
"{{ vault_pb_su_email }}"
"{{ vault_pb_su_password }}"
args:
chdir: "{{ sigvild_gallery_home }}"
become: yes
become_user: "{{ sigvild_gallery_user }}"
register: superuser_result
failed_when: superuser_result.rc != 0
- name: Verify gallery health
uri:
url: "https://{{ sigvild_gallery_api_domain }}/api/health"
method: GET
status_code: [200, 404] # 404 is ok if health endpoint doesn't exist yet
timeout: 15
retries: 5
delay: 5
ignore_errors: yes
tags: [verify]

View File

@@ -0,0 +1,45 @@
{{ sigvild_gallery_api_domain }} {
reverse_proxy {{ sigvild_gallery_host }}:{{ sigvild_gallery_port }} {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
# Health check
health_uri /api/health
health_timeout 5s
health_interval 30s
}
# CORS headers for frontend domain
@cors {
header Origin https://{{ sigvild_gallery_frontend_domain }}
}
header @cors {
Access-Control-Allow-Origin "https://{{ sigvild_gallery_frontend_domain }}"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
Access-Control-Allow-Credentials true
Access-Control-Max-Age 86400
}
# Handle preflight requests
@preflight {
method OPTIONS
}
respond @preflight 204
# Security headers for API
header {
X-Frame-Options DENY
X-Content-Type-Options nosniff
X-XSS-Protection "1; mode=block"
Referrer-Policy strict-origin-when-cross-origin
}
# API logging
log {
output file /var/log/caddy/sigvild-api.log
level INFO
format json
}
}

View File

@@ -0,0 +1,42 @@
{{ sigvild_gallery_frontend_domain }} {
root * {{ sigvild_gallery_web_root }}
file_server
# SPA routing - serve index.html for all routes
try_files {path} /index.html
# Security headers
header {
X-Frame-Options DENY
X-Content-Type-Options nosniff
X-XSS-Protection "1; mode=block"
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy "geolocation=(), microphone=(), camera=()"
}
# Cache static assets aggressively
@static {
path /_app/* /assets/* /icons/* *.ico *.png *.jpg *.jpeg *.svg *.webp *.woff *.woff2
}
header @static {
Cache-Control "public, max-age=31536000, immutable"
Vary "Accept-Encoding"
}
# Cache HTML with shorter duration
@html {
path *.html /
}
header @html {
Cache-Control "public, max-age=3600, must-revalidate"
}
# Enable compression
encode gzip
# Logging for debugging (can be removed in production)
log {
output file /var/log/caddy/sigvild-frontend.log
level INFO
}
}

View File

@@ -0,0 +1,36 @@
[Unit]
Description=Sigvild Wedding Gallery API
After=network.target
[Service]
Type=simple
User={{ sigvild_gallery_user }}
Group={{ sigvild_gallery_user }}
WorkingDirectory={{ sigvild_gallery_home }}
ExecStart={{ sigvild_gallery_binary }} serve --http={{ sigvild_gallery_host }}:{{ sigvild_gallery_port }}
# Environment variables
Environment="SIGVILD_ENVIRONMENT"="production" # Lets caddy handle CORS
Environment="HOST_USERNAME={{ sigvild_gallery_host_username }}"
Environment="HOST_PASSWORD={{ sigvild_gallery_host_password }}"
Environment="HOST_DISPLAY_NAME=Wedding Host"
Environment="GUEST_USERNAME={{ sigvild_gallery_guest_username }}"
Environment="GUEST_PASSWORD={{ sigvild_gallery_guest_password }}"
Environment="GUEST_DISPLAY_NAME=Wedding Guest"
# Restart configuration
Restart=always
RestartSec=3
# Security sandboxing
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths={{ sigvild_gallery_data_dir }}
# Allow binding to port (if needed)
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

View File

@@ -2,7 +2,7 @@
# Core infrastructure deployment with security hardening first
# Security hardening establishes secure foundation before web services
- import_playbook: playbooks/security.yml
# - import_playbook: playbooks/security.yml
- name: Deploy Core Infrastructure
hosts: arch-vps
@@ -10,8 +10,10 @@
gather_facts: yes
roles:
- role: caddy
tags: ['caddy', 'infrastructure', 'web']
# - role: caddy
# tags: ['caddy', 'infrastructure', 'web']
- role: sigvild-gallery
tags: ['sigvild', 'gallery', 'wedding']
post_tasks:
- name: Verify Caddy API is accessible