diff --git a/README.md b/README.md index 5c60954..b42b761 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ water_level{station_code="P.1"} ```sql -- Latest readings from all stations SELECT s.station_code, s.english_name, m.water_level, m.discharge -FROM stations s +FROM stations s JOIN water_measurements m ON s.id = m.station_id WHERE m.timestamp = (SELECT MAX(timestamp) FROM water_measurements WHERE station_id = s.id); ``` @@ -267,14 +267,32 @@ docker run -d \ ### Systemd Service (Linux) -```bash -# Copy service file -sudo cp scripts/water-monitor.service /etc/systemd/system/ +The install script sets everything up: a dedicated `water-monitor` system user, +a deploy to `/opt/thailand-water-monitor`, a uv-managed virtualenv, and the +enabled systemd unit. -# Enable and start +```bash +# From a checkout of the repo, as root: +sudo bash scripts/install.sh + +# Then start and check: +sudo systemctl start water-monitor.service +systemctl status water-monitor.service +``` + +Fill in `/opt/thailand-water-monitor/.env` (Matrix token/room, DB settings) +before starting if the script reports it is missing. + +
+Manual setup (if you prefer not to use the script) + +```bash +sudo useradd --system --no-create-home --shell /usr/sbin/nologin water-monitor +sudo cp scripts/water-monitor.service /etc/systemd/system/ sudo systemctl enable water-monitor.service sudo systemctl start water-monitor.service ``` +
### Migration for Existing Systems @@ -475,7 +493,7 @@ See [docs/PROJECT_STRUCTURE.md](docs/PROJECT_STRUCTURE.md) for detailed architec The project includes comprehensive Gitea Actions workflows: - **๐Ÿงช CI/CD Pipeline** - Automated testing, building, and deployment -- **๐Ÿ”’ Security Scanning** - Daily vulnerability and dependency checks +- **๐Ÿ”’ Security Scanning** - Daily vulnerability and dependency checks - **๐Ÿ“š Documentation** - Automated API docs and validation - **๐Ÿš€ Release Management** - Automated releases with multi-arch Docker builds diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..e43109b --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# +# Install the Thailand Water Level Monitor as a hardened systemd service. +# +# Creates a dedicated system user, deploys the code to /opt, builds a uv-managed +# virtualenv, installs the systemd unit, and enables the service. Idempotent: +# safe to re-run to update an existing install. +# +# Usage (as root, from a checkout of the repo): +# sudo bash scripts/install.sh +# +# Override defaults via environment variables: +# APP_DIR=/opt/thailand-water-monitor SERVICE_USER=water-monitor sudo -E bash scripts/install.sh +# +set -euo pipefail + +APP_DIR="${APP_DIR:-/opt/thailand-water-monitor}" +SERVICE_USER="${SERVICE_USER:-water-monitor}" +SERVICE_GROUP="${SERVICE_GROUP:-${SERVICE_USER}}" +SERVICE_NAME="water-monitor.service" + +# Resolve the repo root (parent of this scripts/ directory). +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +log() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } +die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; } + +[ "$(id -u)" -eq 0 ] || die "This script must be run as root (use sudo)." + +# 1. Dedicated system user/group (no login, no home) -------------------------- +if ! getent group "${SERVICE_GROUP}" >/dev/null; then + log "Creating group ${SERVICE_GROUP}" + groupadd --system "${SERVICE_GROUP}" +fi +if ! id "${SERVICE_USER}" >/dev/null 2>&1; then + log "Creating system user ${SERVICE_USER}" + useradd --system --no-create-home --shell /usr/sbin/nologin \ + --gid "${SERVICE_GROUP}" "${SERVICE_USER}" +fi + +# 2. Deploy code to APP_DIR ---------------------------------------------------- +log "Deploying code to ${APP_DIR}" +mkdir -p "${APP_DIR}" +if command -v rsync >/dev/null 2>&1; then + rsync -a --delete \ + --exclude '.git' --exclude '.venv' --exclude 'venv' \ + --exclude '__pycache__' --exclude '*.pyc' \ + --exclude '*.db' --exclude '.env' --exclude 'stations.json' \ + "${REPO_DIR}/" "${APP_DIR}/" +else + warn "rsync not found; falling back to cp (will not prune deleted files)" + cp -r "${REPO_DIR}/." "${APP_DIR}/" +fi + +# 3. Build the uv-managed virtualenv ------------------------------------------ +if ! command -v uv >/dev/null 2>&1; then + log "Installing uv" + curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh +fi +UV="$(command -v uv)" + +log "Creating virtualenv at ${APP_DIR}/venv" +cd "${APP_DIR}" +# Named 'venv' (not uv's default .venv) to match the systemd unit's ExecStart. +"${UV}" venv venv +"${UV}" pip install --python venv/bin/python -r requirements.txt + +# 4. Environment file ---------------------------------------------------------- +if [ ! -f "${APP_DIR}/.env" ]; then + if [ -f "${REPO_DIR}/.env" ]; then + log "Copying .env from checkout" + cp "${REPO_DIR}/.env" "${APP_DIR}/.env" + else + warn "No .env found. Copy .env.example to ${APP_DIR}/.env and fill in" + warn "MATRIX_ACCESS_TOKEN / MATRIX_ROOM_ID and DB settings before starting." + fi +fi + +# 5. Ownership (service user needs write access for logs / stations.json) ------ +log "Setting ownership to ${SERVICE_USER}:${SERVICE_GROUP}" +chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${APP_DIR}" + +# 6. Install and enable the systemd unit -------------------------------------- +log "Installing systemd unit" +install -m 0644 "${SCRIPT_DIR}/${SERVICE_NAME}" "/etc/systemd/system/${SERVICE_NAME}" +systemctl daemon-reload +systemctl enable "${SERVICE_NAME}" + +log "Done." +echo +echo "Next steps:" +echo " sudo systemctl start ${SERVICE_NAME}" +echo " systemctl status ${SERVICE_NAME}" +echo " sudo journalctl -u ${SERVICE_NAME} -f" diff --git a/scripts/water-monitor.service b/scripts/water-monitor.service index a670fb2..28e3b2d 100644 --- a/scripts/water-monitor.service +++ b/scripts/water-monitor.service @@ -1,6 +1,6 @@ [Unit] Description=Thailand Water Level Monitor -Documentation=https://github.com/your-username/thailand-water-monitor +Documentation=https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor After=network.target Wants=network-online.target