diff --git a/README.md b/README.md index 405756a..f44e08d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Nexus Timer 🕰️✨ +# Table of Contents +1. [Core Concept](#core-concept) +2. [Target Audience](#target-audience) +3. [Hardware Recommendations (Optional Enhancement)](#hardware-recommendations-optional-enhancement) +4. [Key Features](#key-features) + Nexus Timer is a dynamic multi-player timer designed for games, workshops, or any activity where turns pass sequentially in a circular fashion. It provides a clear visual focus on the current participant and their immediate successor, ensuring everyone stays engaged and aware of who is next. This document serves as a detailed specification for a Progressive Web App (PWA) prototype aimed at game enthusiasts. ## Core Concept diff --git a/docs/architecture.md b/docs/architecture.md index c0a0e7a..cd89508 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,3 +1,9 @@ +# Table of Contents +1. [UI/UX Considerations](#uiux-considerations) +2. [Tech Stack](#tech-stack) +3. [Data Model (For AI Generation)](#data-model-for-ai-generation) +4. [Build-time Information & Service Worker Versioning](#build-time-information--service-worker-versioning) + ## UI/UX Considerations * **Minimalist Design:** Focus on clarity and ease of use. Avoid clutter. * **Large, Clear Timers:** Timers should be easily readable at a glance. diff --git a/docs/deployment.md b/docs/deployment.md index e4f7890..3418a29 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,76 +1,169 @@ -## Deployment Setup +# Deployment Guide: Nexus Timer + +This guide outlines the steps to deploy and manage the Nexus Timer application on a server using Docker, Traefik (as a reverse proxy), and systemd, with an optional automated deployment via Gitea webhooks. + +## Table of Contents +1. [Initial Server Setup](#initial-server-setup) +2. [Exposing the App Behind Traefik](#exposing-the-app-behind-traefik-reverse-proxy) +3. [Automating Updates with Webhooks (Gitea)](#automating-updates-with-webhooks-gitea) +4. [Manual Updates (Fallback)](#manual-updates-fallback) +5. [Troubleshooting & Logs](#troubleshooting--logs) + +## Initial Server Setup + ### On the Server -Navigate to the service directory on the server +Navigate to your preferred service directory on the server (e.g., `/virt`). ```bash cd /virt ``` -Clone the repository +Clone the repository (only the latest commit for faster cloning). ```bash git clone --depth 1 https://gitea.virtonline.eu/2HoursProject/nexus-timer.git cd nexus-timer ``` -If you will run the container on the docker network `traefik` find its IP subnet +If you will run the container on the Docker network `traefik` (or any other pre-existing network), find its IP subnet to allow Nginx inside the container to correctly identify the real client IP. ```bash docker network inspect traefik --format '{{(index .IPAM.Config 0).Subnet}}' ``` -Set the subnet in the `nginx.conf`. For example: +You'll get an output like `172.22.0.0/16`. + +Set this subnet in the `nginx.conf` file within your cloned repository before building the image. For example: ```bash -set_real_ip_from 172.22.0.0/16 +set_real_ip_from 172.22.0.0/16; ``` -Build the docker image +Build the Docker image for the application. ```bash docker build -t virt-nexus-timer . ``` ## Exposing the App Behind Traefik (Reverse Proxy) +This setup assumes you have Traefik running and configured to watch for Docker labels. + ### Review the provided docker labels and systemd service file -Copy the example label file to its destination +The `docker/traefik.labels` file contains Docker labels that Traefik uses for routing and HTTPS. Review and adjust them if necessary. +Copy the example label file to its destination (one level up, to be read by systemd). ```bash cp docker/traefik.labels labels ``` -View the example service definition: +View the example systemd service definition to understand how the Docker container will be managed. ```bash cat systemd/virt-nexus-timer.service ``` ### Create the systemd service -The editor opens to create or overwrite the service: +Use `systemctl edit` to create or overwrite the service file. This is the recommended way to manage custom systemd units. ```bash sudo systemctl edit --force --full virt-nexus-timer.service ``` -Paste the content from `systemd/virt-nexus-timer.service`, then save and exit. +The editor will open. Paste the content from your `systemd/virt-nexus-timer.service` file into the editor, then save and exit (e.g., Ctrl+X, then Y, then Enter in nano). -Enable on system boot and start the service +Enable the service to start on system boot and start it immediately. ```bash sudo systemctl enable --now virt-nexus-timer.service ``` -Check the service status +Check the service status to ensure it's running correctly. ```bash systemctl status virt-nexus-timer.service ``` -View real-time logs -```bash -journalctl -fu virt-nexus-timer.service -``` +Look for "active (running)". + ### Test the web application -Verify that the application is accessible via HTTPS: +Verify that the application is accessible via HTTPS through Traefik: ```bash curl https://nexus-timer.virtonline.eu ``` Or open it in your browser: [https://nexus-timer.virtonline.eu](https://nexus-timer.virtonline.eu) -## Release the update -### On the Server -Navigate to the app directory on your server +## Automating Updates with Webhooks (Gitea) +Instead of manually pulling, building, and restarting on the server, you can automate this process using a webhook. Gitea will notify a webhook service running on your server, which will then execute a deployment script. + +Install the `webhook` service +```bash +sudo install webhook +``` +Allow your Gitea instance to reach the webhook service on your server (e.g., `10.0.0.1:9000`). Ensure Gitea's `ALLOWED_HOST_LIST` in its `app.ini` includes this IP. +### The Redeployment Script +The `webhook` service will execute the script `hooks/redeploy.sh`. If webhook runs as a non-root user (recommended), that user will need passwordless sudo permission to restart the `virt-nexus-timer.service`. +You can grant this by editing the sudoers file: +```bash +sudo visudo +``` + and adding a line like: +```bash +webhooksvc ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart virt-nexus-timer.service +``` +Replace `webhooksvc` with the actual user webhook runs as. If webhook runs as root, this is not necessary but less secure overall. +### Configure the `webhook` Service +Create or edit the main webhook configuration file, typically at `/etc/webhook.conf`. Add the JSON object from `hooks/webhook.conf` to the array in the file (or create the file if it's new). +**Note:** Replace `YOUR_VERY_STRONG_SECRET_TOKEN_HERE_REPLACE_ME` with a strong, unique secret. +### Set Up webhook as a Systemd Service +Use `systemctl edit` to create or overwrite the service file. This is the recommended way to manage custom systemd units. +```bash +sudo systemctl edit --force --full webhook.service +``` +The editor will open. Paste the content from your `systemd/webhook.service` file into the editor, then save and exit (e.g., Ctrl+X, then Y, then Enter in nano). + +Enable, and start the webhook service: +```bash +sudo systemctl enable --now webhook.service +sudo systemctl status webhook.service +``` +### Configure Webhook in Gitea +1. Navigate to your Gitea repository: `https://gitea.virtonline.eu/2HoursProject/nexus-timer` +2. Go to `Settings -> Webhooks`. +3. Click Add Webhook and choose `Gitea`. +4. **Target URL**: `http://10.0.0.1:9000/hooks/redeploy-nexus-timer` +*(The redeploy-nexus-timer part must match the id in your /etc/webhook.conf)* +5. **HTTP Method**: POST +6. **POST Content Type**: application/json +7. **Secret**: Enter the exact same strong secret token you used in `/etc/webhook.conf` (e.g., `YOUR_VERY_STRONG_SECRET_TOKEN_HERE_REPLACE_ME`). +8. **Trigger On**: +Select `Push Events`. +You can further refine this to specific branches if your `webhook.conf` doesn't already filter by branch (though redundant filtering is fine). +9. Ensure `Enable this webhook` is checked. +10. Click `Add Webhook`. +### Test the Webhook +* In Gitea, on the Webhooks settings page for the webhook you just created, click `Test Delivery`. +* Check the Gitea UI for the response. It should show a `200 OK` and include the output from your `redeploy.sh` script. +* Push a small change to your `main` (or configured) branch to trigger a real deployment. +### Firewall Considerations +If your server has a firewall (e.g., `ufw`), ensure that port `9000` (or whichever port you configured for `webhook`) is allowed for incoming connections from your Gitea server's IP address or network. + +Example for `ufw` allowing any connection to `10.0.0.1:9000` (restrict source IP if possible): +```bash +sudo ufw allow to 10.0.0.1 port 9000 proto tcp comment 'Gitea Webhook' +``` +If Gitea is external, use its specific source IP instead of 'any' for better security. +```bash +sudo ufw allow from to 10.0.0.1 port 9000 proto tcp comment 'Gitea Webhook' +``` +Reload `ufw` if changes are made: +```bash +sudo ufw reload +``` + +## Manual Updates (Fallback) +Navigate to the application directory on your server. ```bash cd /virt/nexus-timer ``` -Pull the changes, build the docker image and restart the service +Pull the latest changes from the repository, rebuild the Docker image, and restart the systemd service. ```bash -git pull && docker build -t virt-nexus-timer . && systemctl restart virt-nexus-timer.service +git pull && docker build -t virt-nexus-timer . && sudo systemctl restart virt-nexus-timer.service ``` -View real-time logs +The previously installed Progressive Web App (PWA) should update automatically upon next launch or offer an upgrade prompt. + +## Troubleshooting & Logs +View real-time logs for the application service: ```bash journalctl -fu virt-nexus-timer.service ``` -The previously installed PWA should update automatically or offer an upgrade \ No newline at end of file +View real-time logs for the `webhook` service: +```bash +journalctl -fu webhook.service +``` +Check the custom log file for the redeployment script (if configured): +```bash +tail -f /var/log/webhook-redeploy-nexus-timer.log +``` +Check Gitea's webhook delivery logs for request/response details and errors. diff --git a/docs/development.md b/docs/development.md index 076b3e6..46f0719 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,3 +1,9 @@ +# Table of Contents +1. [Developer Setup](#developer-setup) +2. [Modify the app](#modify-the-app) +3. [Test the PWA locally](#test-the-pwa-locally) +4. [Commit & Push](#commit--push) + ## Developer Setup Clone the repository ```bash diff --git a/hooks/redeploy.sh b/hooks/redeploy.sh index d88180a..91e9aaf 100755 --- a/hooks/redeploy.sh +++ b/hooks/redeploy.sh @@ -1,15 +1,44 @@ #!/bin/bash -set -e -echo "[webhook] Pulling new code..." +set -eo pipefail # Exit on error, treat unset variables as an error, and propagate pipeline errors -cd /virt/nexus-timer || exit 1 -git pull origin main +APP_DIR="/virt/nexus-timer" +IMAGE_NAME="virt-nexus-timer" +SERVICE_NAME="virt-nexus-timer.service" +LOG_FILE="/var/log/webhook-redeploy-nexus-timer.log" # Optional: for script-specific logging -echo "[webhook] Building image..." -docker build -t virt-nexus-timer . +# Redirect stdout and stderr to a log file and also to the console (for webhook response) +exec > >(tee -a "$LOG_FILE") 2>&1 -echo "[webhook] Restarting systemd service..." -systemctl restart virt-nexus-timer.service +echo "----------------------------------------------------" +echo "Webhook redeploy-nexus-timer triggered at $(date)" +echo "Branch/Ref: $1" +echo "Repository: $2" +echo "----------------------------------------------------" -echo "[webhook] Done." +# Ensure script is run from the app directory +cd "$APP_DIR" || { echo "ERROR: Failed to cd to $APP_DIR. Exiting."; exit 1; } + +# Optional: Check if the trigger is for the correct branch (e.g., main or master) +# TARGET_BRANCH="refs/heads/main" # Adjust to your primary branch name +# if [ "$1" != "$TARGET_BRANCH" ]; then +# echo "Webhook triggered for branch $1, but only deploying $TARGET_BRANCH. Exiting." +# exit 0 # Exit successfully to not show an error in Gitea for non-target branches +# fi + +echo "Pulling latest changes from Git (main branch)..." +# Ensure you are on the correct branch first or specify it in the pull +# git checkout main # Uncomment if your repo might be on other branches +git pull origin main || { echo "ERROR: git pull failed. Exiting."; exit 1; } # Adjust 'main' if needed + +echo "Building Docker image $IMAGE_NAME..." +docker build -t "$IMAGE_NAME" . || { echo "ERROR: docker build failed. Exiting."; exit 1; } + +echo "Restarting systemd service $SERVICE_NAME..." +# This command might require sudo privileges. See note below. +sudo systemctl restart "$SERVICE_NAME" || { echo "ERROR: systemctl restart failed. Exiting."; exit 1; } + +echo "Deployment finished successfully at $(date)." +echo "----------------------------------------------------" + +exit 0 \ No newline at end of file diff --git a/hooks/webhook.conf b/hooks/webhook.conf new file mode 100644 index 0000000..8b0d542 --- /dev/null +++ b/hooks/webhook.conf @@ -0,0 +1,38 @@ +[ + { + "id": "redeploy-nexus-timer", + "execute-command": "/virt/nexus-timer/hooks/redeploy.sh", + "command-working-directory": "/virt/nexus-timer", + "pass-arguments-to-command": [ + { "source": "payload", "name": "ref" }, + { "source": "payload", "name": "repository.full_name" } + ], + "trigger-rule": { + "and": [ + { + "match": { + "type": "payload-hmac-sha256", + "secret": "YOUR_VERY_STRONG_SECRET_TOKEN_HERE_REPLACE_ME", + "parameter": { + "source": "header", + "name": "X-Gitea-Signature" + } + } + }, + { + "match": { + "type": "value", + "value": "refs/heads/main", + "parameter": { + "source": "payload", + "name": "ref" + } + } + } + ] + }, + "include-command-output-in-response": true, + "include-command-output-in-response-on-error": true, + "response-message": "Webhook processed." + } +] \ No newline at end of file diff --git a/systemd/virt-nexus-timer.service b/systemd/virt-nexus-timer.service index e80d1f2..f977e26 100644 --- a/systemd/virt-nexus-timer.service +++ b/systemd/virt-nexus-timer.service @@ -7,7 +7,7 @@ DefaultDependencies=no [Service] Type=simple Environment="HOME=/root" -ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true' +ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker stop -t 3 virt-nexus-timer 2>/dev/null || true' ExecStartPre=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true' ExecStart=/usr/bin/env docker run \ @@ -17,7 +17,7 @@ ExecStart=/usr/bin/env docker run \ --label-file /virt/nexus-timer/labels \ virt-nexus-timer -ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker kill virt-nexus-timer 2>/dev/null || true' +ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker stop -t 3 virt-nexus-timer 2>/dev/null || true' ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker rm virt-nexus-timer 2>/dev/null || true' Restart=always diff --git a/systemd/webhook.service b/systemd/webhook.service new file mode 100644 index 0000000..59c0ad9 --- /dev/null +++ b/systemd/webhook.service @@ -0,0 +1,35 @@ +[Unit] +Description=Small server for creating HTTP endpoints (hooks) +Documentation=https://github.com/adnanh/webhook/ +After=network-online.target +Wants=network-online.target +ConditionPathExists=/etc/webhook.conf + + +[Service] +Type=simple + +# Clear any existing ExecStart from a base unit file, if any +ExecStart= +# Path to webhook, IP, port, path to hooks config, verbose logging, hot-reloading config +ExecStart=/usr/bin/webhook -ip 10.0.0.1 -port 9000 -verbose -nopanic -hooks /etc/webhook.conf + +# --- Security & User (Recommended) --- +# 1. Create a dedicated user: +# sudo useradd --system --no-create-home --shell /bin/false webhooksvc +# 2. Ensure this user can read /etc/webhook.conf and execute redeploy.sh +# sudo chown webhooksvc:webhooksvc /etc/webhook.conf && sudo chmod 640 /etc/webhook.conf +# Also grant sudo rights for systemctl restart as mentioned in Step 3.1. +# Uncomment and use if you created the 'webhooksvc' user: +# User=webhooksvc +# Group=webhooksvc +# If running as root (less secure), leave User/Group commented. + +Restart=on-failure +RestartSec=5s +TimeoutStopSec=30s +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target \ No newline at end of file