added jnss-web

This commit is contained in:
2025-12-23 23:36:42 +01:00
parent bfd6f22f0e
commit 9bb9405b90
9 changed files with 529 additions and 49 deletions

326
docs/jnss-web-deployment.md Normal file
View File

@@ -0,0 +1,326 @@
# jnss-web Static Site Deployment Guide
## Overview
This document describes the deployment process for jnss-web, a SvelteKit-based static website serving as the primary entrypoint for jnss.me.
## Architecture
- **Technology**: SvelteKit v2 with static adapter
- **Build Tool**: Bun
- **Deployment**: Ansible playbook with git-based artifact deployment
- **Web Server**: Caddy (direct file serving)
- **Repository**: git.jnss.me/joakim/jnss-web
- **Branch Strategy**:
- `main` - Source code
- `deploy` - Build artifacts only
## Server Architecture
```
/opt/jnss-web-repo/ # Git clone of deploy branch
/var/www/jnss-web/ # Synced build artifacts (served by Caddy)
/etc/caddy/sites-enabled/
└── jnss-web.caddy # Site config with www redirect
/var/log/caddy/jnss-web.log # Access logs
```
## Development Workflow
### 1. Local Development (main branch)
```bash
cd ~/dev/jnss-web
git checkout main
# Make changes to source code
# ...
# Test locally
bun run dev
# Build to verify
bun run build
```
### 2. Prepare Deploy Branch
```bash
# Build production version
bun run build
# Switch to deploy branch
git checkout deploy
# Merge changes from main
git merge main --no-commit
# Rebuild to ensure fresh build
bun run build
# Add only build artifacts
git add build/
# Commit with descriptive message
git commit -m "Deploy: Add new feature X"
# Push to Gitea
git push origin deploy
```
### 3. Deploy to Server
```bash
cd ~/rick-infra
# Run deployment playbook
ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml
# Watch logs (optional)
ssh root@arch-vps 'tail -f /var/log/caddy/jnss-web.log'
```
## Playbook Details
### Variables
Located in `playbooks/deploy-jnss-web.yml`:
- `jnss_web_repo_owner`: Your Gitea username (default: "joakim")
- `jnss_web_branch`: Branch to deploy (default: "deploy")
- `jnss_web_domain`: Primary domain (default: "jnss.me")
### Tags
- `jnss-web` - All jnss-web tasks
- `deploy` - Git clone/sync tasks
- `caddy` - Caddy configuration tasks
### Example Usage
```bash
# Full deployment
ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml
# Only update Caddy config (no git pull)
ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml --tags caddy
# Only sync files (skip Caddy reload)
ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml --tags deploy
```
## Initial Setup
### jnss-web Project Configuration
The following changes need to be made in the jnss-web project:
#### 1. Install Static Adapter
```bash
cd ~/dev/jnss-web
bun add -D @sveltejs/adapter-static
```
#### 2. Update svelte.config.js
```javascript
import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
}
};
export default config;
```
#### 3. Create src/routes/+layout.js
```javascript
export const prerender = true;
```
#### 4. Update .gitignore
Comment out or remove the build directory from .gitignore:
```gitignore
# build/ <- Comment this out or remove it
```
#### 5. Create Deploy Branch
```bash
# Build the site first
bun run build
# Create and switch to deploy branch
git checkout -b deploy
# Remove source files (keep only build artifacts)
git rm -r src static *.config.js package.json bun.lock jsconfig.json .npmrc
# Keep only build/ and documentation
git add build/ README.md
# Commit
git commit -m "Initial deploy branch with build artifacts"
# Push to Gitea
git push -u origin deploy
# Switch back to main
git checkout main
```
## Troubleshooting
### Build Directory Not Found
**Error**: "Build directory not found in repository"
**Solution**: Ensure you pushed the build artifacts to the deploy branch:
```bash
git checkout deploy
ls -la build/ # Should exist
git log --oneline -1 # Verify latest commit
```
### Caddy Not Serving New Content
**Solution**:
```bash
# SSH to server
ssh root@arch-vps
# Check Caddy status
systemctl status caddy
# Reload Caddy manually
systemctl reload caddy
# Check logs
journalctl -u caddy -f
```
### Permission Issues
**Solution**:
```bash
# SSH to server
ssh root@arch-vps
# Fix ownership
chown -R caddy:caddy /var/www/jnss-web
# Verify
ls -la /var/www/jnss-web
```
### Git Clone Fails
**Error**: Unable to clone from git.jnss.me
**Solution**:
- Verify repository is public in Gitea
- Test clone manually: `git clone https://git.jnss.me/joakim/jnss-web.git /tmp/test`
- Check Gitea service status
## Rollback Procedure
### Option 1: Rollback Deploy Branch
```bash
# In jnss-web repository
git checkout deploy
# Find previous commit
git log --oneline
# Reset to previous commit
git reset --hard <commit-hash>
# Force push
git push -f origin deploy
# Re-run deployment
cd ~/rick-infra
ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml
```
### Option 2: Quick Server-Side Rollback
```bash
# SSH to server
ssh root@arch-vps
# Go to repo directory
cd /opt/jnss-web-repo
# Reset to previous commit
git reset --hard HEAD~1
# Re-sync
rsync -av --delete build/ /var/www/jnss-web/
# Reload Caddy
systemctl reload caddy
```
## Security Considerations
- Repository is public (contains only build artifacts)
- No secrets in build output
- Caddy serves only /var/www/jnss-web (no parent directory access)
- Security headers configured in Caddy
- HTTPS enforced via Caddy with Let's Encrypt
## Performance Optimizations
- Static assets cached for 1 year (immutable)
- HTML cached for 1 hour with revalidation
- Gzip compression enabled
- No server-side processing required
## Monitoring
### Check Deployment Status
```bash
# From control machine
ansible homelab -i inventory/hosts.yml -m shell -a "ls -lh /var/www/jnss-web"
```
### View Access Logs
```bash
ssh root@arch-vps 'tail -100 /var/log/caddy/jnss-web.log | jq .'
```
### Check Site Health
```bash
curl -I https://jnss.me
curl -I https://www.jnss.me # Should redirect to jnss.me
```
## Files Created
This deployment adds the following files to rick-infra:
- `playbooks/deploy-jnss-web.yml` - Main deployment playbook
- `playbooks/templates/jnss-web.caddy.j2` - Caddy configuration template
- `docs/jnss-web-deployment.md` - This documentation
And modifies:
- `roles/caddy/templates/Caddyfile.j2` - Removed default site section

View File

@@ -0,0 +1,133 @@
---
# ================================================================
# jnss-web Static Site Deployment Playbook
# ================================================================
# Deploys the jnss-web SvelteKit static site to jnss.me
#
# Usage:
# ansible-playbook -i inventory/hosts.yml playbooks/deploy-jnss-web.yml
#
# This playbook:
# - Clones the jnss-web repository (deploy branch) to a temp directory
# - Syncs build artifacts to /var/www/jnss-web
# - Deploys Caddy configuration for jnss.me with www redirect
# - Reloads Caddy to serve the new site
# ================================================================
- name: Deploy jnss-web static site
hosts: homelab
become: true
vars:
# Git repository configuration
jnss_web_repo_url: "https://git.jnss.me/joakim/jnss-web.git"
jnss_web_branch: "deploy"
# Server paths
jnss_web_root: "/var/www/jnss-web"
# Domain configuration
jnss_web_domain: "jnss.me"
# Caddy configuration
caddy_user: "caddy"
caddy_sites_enabled_dir: "/etc/caddy/sites-enabled"
tasks:
# ============================================================
# Git Repository Management
# ============================================================
- name: Create temporary directory for git clone
tempfile:
state: directory
suffix: -jnss-web
register: temp_clone_dir
tags: [jnss-web, deploy]
- name: Clone jnss-web repository to temp directory
git:
repo: "{{ jnss_web_repo_url }}"
dest: "{{ temp_clone_dir.path }}"
version: "{{ jnss_web_branch }}"
depth: 1
tags: [jnss-web, deploy]
- name: Verify build directory exists in repository
stat:
path: "{{ temp_clone_dir.path }}/index.html"
register: build_dir
tags: [jnss-web, deploy]
- name: Fail if index.html not found
fail:
msg: "Build index.html not found in repository root. Ensure the deploy branch contains the built artifacts."
when: not build_dir.stat.exists
tags: [jnss-web, deploy]
# ============================================================
# Web Root Deployment
# ============================================================
- name: Remove old web root
file:
path: "{{ jnss_web_root }}"
state: absent
tags: [jnss-web, deploy]
- name: Create fresh web root directory
file:
path: "{{ jnss_web_root }}"
state: directory
owner: "{{ caddy_user }}"
group: "{{ caddy_user }}"
mode: '0755'
tags: [jnss-web, deploy]
- name: Copy build files to web root
copy:
src: "{{ temp_clone_dir.path }}/"
dest: "{{ jnss_web_root }}/"
owner: "{{ caddy_user }}"
group: "{{ caddy_user }}"
mode: '0755'
remote_src: true
tags: [jnss-web, deploy]
- name: Clean up temporary clone directory
file:
path: "{{ temp_clone_dir.path }}"
state: absent
tags: [jnss-web, deploy]
# ============================================================
# Caddy Configuration
# ============================================================
- name: Deploy Caddy configuration for jnss-web
template:
src: templates/jnss-web.caddy.j2
dest: "{{ caddy_sites_enabled_dir }}/jnss-web.caddy"
owner: root
group: "{{ caddy_user }}"
mode: '0644'
notify: reload caddy
tags: [jnss-web, caddy]
- name: Validate Caddy configuration
command: caddy validate --config /etc/caddy/Caddyfile
register: caddy_validate
changed_when: false
tags: [jnss-web, caddy]
- name: Display Caddy validation result
debug:
msg: "Caddy configuration is valid"
when: caddy_validate.rc == 0
tags: [jnss-web, caddy]
handlers:
- name: reload caddy
systemd:
name: caddy
state: reloaded

View File

@@ -0,0 +1,57 @@
# jnss-web Static Site Configuration
# Generated by Ansible - DO NOT EDIT MANUALLY
# WWW Redirect - apex is primary
www.{{ jnss_web_domain }} {
redir https://{{ jnss_web_domain }}{uri} permanent
}
# Primary Domain
{{ jnss_web_domain }} {
root * {{ jnss_web_root }}
file_server
# SPA routing - serve index.html for all routes
try_files {path} /index.html
# Security headers
header {
X-Frame-Options SAMEORIGIN
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 *.css *.js
}
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
log {
output file /var/log/caddy/jnss-web.log {
roll_size 100mb
roll_keep 5
}
format json {
time_format "2006-01-02T15:04:05.000Z07:00"
}
level INFO
}
}

View File

@@ -21,6 +21,10 @@
gather_facts: true
tasks:
- name: Deploy Caddy
include_role:
name: caddy
tags: ['caddy']
# - name: Deploy Authentik
# include_role:
# name: authentik
@@ -36,8 +40,8 @@
# name: nextcloud
# tags: ['nextcloud', 'cloud', 'storage']
- name: Deploy Vaultwarden
include_role:
name: vaultwarden
tags: ['vaultwarden', 'vault', 'password-manager', 'security']
# - name: Deploy Vaultwarden
# include_role:
# name: vaultwarden
# tags: ['vaultwarden', 'vault', 'password-manager', 'security']

View File

@@ -120,7 +120,7 @@
owner: root
group: "{{ caddy_user }}"
mode: '0640'
backup: yes
backup: no
notify: reload caddy
- name: Check Caddyfile syntax (basic check)

View File

@@ -21,43 +21,3 @@
# Import service configurations
import {{ caddy_sites_enabled_dir }}/*
# Primary domain: {{ caddy_domain }}
{{ caddy_domain }} {
{% if caddy_tls_enabled %}
{% if caddy_dns_provider == "cloudflare" and cloudflare_api_token %}
# DNS challenge for automatic TLS
tls {
dns cloudflare {{ cloudflare_api_token }}
resolvers {{ caddy_dns_resolvers | join(' ') }}
}
{% elif caddy_tls_email %}
# HTTP challenge for automatic TLS
tls {{ caddy_tls_email }}
{% endif %}
{% endif %}
# Serve static content
root * {{ caddy_default_site_root }}
file_server
# Logging
log {
{% if caddy_log_format == "json" %}
output file {{ caddy_log_dir }}/{{ caddy_domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
format json {
time_format "2006-01-02T15:04:05.000Z07:00"
}
level {{ caddy_log_level }}
{% else %}
output file {{ caddy_log_dir }}/{{ caddy_domain | replace('.', '_') }}.log {
roll_size 100mb
roll_keep 5
}
level {{ caddy_log_level }}
{% endif %}
}
}

View File

@@ -63,7 +63,7 @@ gitea_enable_lfs: true
# Access Control - Private server with public repos allowed
gitea_disable_registration: true # No public registration (admin only)
gitea_require_signin: true # Require sign-in (unauthorized users read-only)
gitea_require_signin: false # Require sign-in (unauthorized users read-only)
gitea_show_registration_button: false # Hide registration UI
# OAuth Configuration - Preferred but not forced
@@ -86,7 +86,7 @@ gitea_smtp_addr: "smtp.titan.email"
gitea_smtp_port: 587
gitea_mailer_from: "hello@jnss.me"
gitea_mailer_user: "hello@jnss.me"
gitea_mailer_password: "{{ vault_gitea_smtp_password }}"
gitea_mailer_password: "{{ vault_smtp_password }}"
gitea_mailer_subject_prefix: "[Gitea]"
# =================================================================

View File

@@ -105,7 +105,7 @@ nextcloud_smtp_secure: "tls" # tls, ssl, or empty string for no encryption
nextcloud_smtp_auth: true # Enable SMTP authentication
nextcloud_smtp_authtype: "PLAIN" # LOGIN or PLAIN
nextcloud_smtp_username: "hello@jnss.me" # SMTP username
nextcloud_smtp_password: "{{ vault_nextcloud_smtp_password | default('') }}"
nextcloud_smtp_password: "{{ vault_smtp_password | default('') }}"
# Email Addressing
nextcloud_mail_from_address: "hello" # Local part only (before @)

View File

@@ -64,7 +64,7 @@ vaultwarden_smtp_host: "smtp.titan.email"
vaultwarden_smtp_port: 587
vaultwarden_smtp_from: "hello@jnss.me"
vaultwarden_smtp_username: "hello@jnss.me"
vaultwarden_smtp_password: "{{ vault_vaultwarden_smtp_password | default('') }}"
vaultwarden_smtp_password: "{{ vault_smtp_password | default('') }}"
vaultwarden_smtp_security: "starttls" # Options: starttls, force_tls, off
# =================================================================