Add Sigvild Gallery wedding photo application with automated deployment and improve Caddy plugin management
This commit is contained in:
@@ -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
|
||||
|
||||
262
docs/sigvild-gallery-deployment.md
Normal file
262
docs/sigvild-gallery-deployment.md
Normal 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.
|
||||
@@ -35,9 +35,23 @@ 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
|
||||
# =================================================================
|
||||
caddy_log_level: "INFO"
|
||||
caddy_log_format: "json"
|
||||
caddy_systemd_security: true
|
||||
caddy_systemd_security: true
|
||||
|
||||
52
playbooks/deploy-sigvild.yml
Normal file
52
playbooks/deploy-sigvild.yml
Normal 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"
|
||||
@@ -3,19 +3,35 @@
|
||||
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)
|
||||
get_url:
|
||||
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
|
||||
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
|
||||
- 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
|
||||
|
||||
180
roles/sigvild-gallery/README.md
Normal file
180
roles/sigvild-gallery/README.md
Normal 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
|
||||
36
roles/sigvild-gallery/defaults/main.yml
Normal file
36
roles/sigvild-gallery/defaults/main.yml
Normal 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
|
||||
16
roles/sigvild-gallery/handlers/main.yml
Normal file
16
roles/sigvild-gallery/handlers/main.yml
Normal 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
|
||||
20
roles/sigvild-gallery/meta/main.yml
Normal file
20
roles/sigvild-gallery/meta/main.yml
Normal 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
|
||||
43
roles/sigvild-gallery/tasks/deploy_backend.yml
Normal file
43
roles/sigvild-gallery/tasks/deploy_backend.yml
Normal 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]
|
||||
57
roles/sigvild-gallery/tasks/deploy_frontend.yml
Normal file
57
roles/sigvild-gallery/tasks/deploy_frontend.yml
Normal 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]
|
||||
91
roles/sigvild-gallery/tasks/main.yml
Normal file
91
roles/sigvild-gallery/tasks/main.yml
Normal 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]
|
||||
45
roles/sigvild-gallery/templates/api.caddy.j2
Normal file
45
roles/sigvild-gallery/templates/api.caddy.j2
Normal 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
|
||||
}
|
||||
}
|
||||
42
roles/sigvild-gallery/templates/frontend.caddy.j2
Normal file
42
roles/sigvild-gallery/templates/frontend.caddy.j2
Normal 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
|
||||
}
|
||||
}
|
||||
36
roles/sigvild-gallery/templates/sigvild-gallery.service.j2
Normal file
36
roles/sigvild-gallery/templates/sigvild-gallery.service.j2
Normal 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
|
||||
8
site.yml
8
site.yml
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user