#!/usr/bin/env bash
#
# Node bootstrap for the `node` service.
#
# Invoked by the kooldev image's /entrypoint via the ENTRYPOINT env var, which runs
# `bash $ENTRYPOINT` before exec'ing the service command. Two consequences worth knowing:
#
#   1. It is run with `bash`, so this file does NOT need an execute bit. That matters:
#      the host is macOS and mode bits from the bind mount carry into the container.
#   2. /entrypoint runs under `set -e`, so a non-zero exit here aborts container start.
#      That is intentional — we do not want Vite booting on a broken install.
#
# node_modules is a Docker-managed volume (see compose.yml), not the macOS bind
# mount. So it starts empty on a fresh volume and must be populated here.

set -euo pipefail

# Force sane permissions on anything npm writes. The previous host-installed tree arrived
# with mode 0600 on every binary, which made `npm run dev` die with
# "sh: vite: Permission denied" (root still needs an x bit to exec). Pinning the umask
# keeps that from recurring inside the volume.
umask 022

APP_DIR=/app
MODULES_DIR="$APP_DIR/node_modules"
LOCKFILE="$APP_DIR/package-lock.json"
STAMP="$MODULES_DIR/.dep-stamp"

cd "$APP_DIR"

log() { printf '[node-entrypoint] %s\n' "$1"; }

# Fingerprint of the dependency inputs, so we reinstall when they change and skip when
# they have not. Without this, every `docker compose up`/`restart` reinstalls.
current_fingerprint() {
    # Hash file *contents* only. Piping through cat keeps filenames out of the digest, so
    # the value does not depend on whether paths are absolute or relative.
    if [ -f "$LOCKFILE" ]; then
        cat "$LOCKFILE" package.json | sha256sum | awk '{print $1}'
    else
        cat package.json | sha256sum | awk '{print $1}'
    fi
}

install_deps() {
    if [ -f "$LOCKFILE" ]; then
        # `npm ci` is reproducible and prunes extraneous packages. It wipes node_modules
        # first, which is cheap here because the volume is on the VM's native fs.
        log 'installing dependencies with `npm ci`'
        npm ci --no-audit --no-fund
    else
        log 'no package-lock.json; falling back to `npm install`'
        npm install --no-audit --no-fund
    fi
}

mkdir -p "$MODULES_DIR"

fingerprint="$(current_fingerprint)"

if [ ! -x "$MODULES_DIR/.bin/vite" ]; then
    # Covers a fresh/empty volume and a tree whose binaries lost their execute bit.
    log 'vite binary missing or not executable; installing'
    install_deps
elif [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$fingerprint" ]; then
    log 'dependency manifest changed since last install; reinstalling'
    install_deps
else
    log 'dependencies already up to date; skipping install'
fi

printf '%s' "$fingerprint" > "$STAMP"

# Fail loudly now rather than letting the service command produce a confusing error.
if [ ! -x "$MODULES_DIR/.bin/vite" ]; then
    log 'ERROR: node_modules/.bin/vite is still missing or not executable after install'
    exit 1
fi

log "ready ($(node -v), npm $(npm -v))"
