#!/usr/bin/env bash # ===================================================================== # SMF2290 Pensum — Start.sh # Universal launcher: setter opp alt som trengs og starter studie-appen. # Fungerer på Linux, macOS, og WSL/Git-Bash på Windows. # ===================================================================== set -e # --- Resolve script directory (works with symlinks too) ----------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" APP_DIR="$SCRIPT_DIR/app" VENDOR_DIR="$APP_DIR/vendor" DEFAULT_PORT=8765 # --- ANSI colours -------------------------------------------------- if [ -t 1 ] && command -v tput >/dev/null 2>&1; then BOLD="$(tput bold)"; DIM="$(tput dim)"; RESET="$(tput sgr0)" RED="$(tput setaf 1)"; GREEN="$(tput setaf 2)"; YELLOW="$(tput setaf 3)" BLUE="$(tput setaf 4)"; ORANGE="$(tput setaf 208 2>/dev/null || tput setaf 3)" else BOLD=""; DIM=""; RESET=""; RED=""; GREEN=""; YELLOW=""; BLUE=""; ORANGE="" fi say() { printf "%s\n" "$*"; } info() { printf " ${BLUE}▸${RESET} %s\n" "$*"; } ok() { printf " ${GREEN}✓${RESET} %s\n" "$*"; } warn() { printf " ${YELLOW}!${RESET} %s\n" "$*"; } err() { printf " ${RED}✗${RESET} %s\n" "$*"; } # --- Banner -------------------------------------------------------- banner() { printf "\n" printf " ${ORANGE}${BOLD}SMF · 2290${RESET} ${DIM}Etikk, bærekraft og samfunnsansvar${RESET}\n" printf " ${BOLD}${BOLD}Pensum${RESET} ${DIM}studie-app · v1.0${RESET}\n" printf "\n" } banner # --- Sanity check -------------------------------------------------- if [ ! -d "$APP_DIR" ]; then err "Fant ikke app-mappen: $APP_DIR" err "Kjør Start.sh fra SMF-prosjektmappen." exit 1 fi # --- OS detection -------------------------------------------------- OS_NAME="$(uname -s 2>/dev/null || echo unknown)" case "$OS_NAME" in Linux*) PLATFORM="linux" ;; Darwin*) PLATFORM="macos" ;; MINGW*|MSYS*|CYGWIN*) PLATFORM="windows" ;; *) PLATFORM="unknown" ;; esac info "Plattform: ${BOLD}$PLATFORM${RESET}" # --- Detect / install HTTP server --------------------------------- SERVER_CMD="" SERVER_NAME="" find_server() { if command -v python3 >/dev/null 2>&1; then SERVER_CMD="python3 -m http.server PORT --bind 127.0.0.1" SERVER_NAME="Python 3 ($(python3 --version 2>&1 | awk '{print $2}'))" return 0 fi if command -v python >/dev/null 2>&1 && python --version 2>&1 | grep -q "Python 3"; then SERVER_CMD="python -m http.server PORT --bind 127.0.0.1" SERVER_NAME="Python 3 ($(python --version 2>&1 | awk '{print $2}'))" return 0 fi if command -v php >/dev/null 2>&1; then SERVER_CMD="php -S 127.0.0.1:PORT -t ." SERVER_NAME="PHP $(php --version 2>&1 | head -1 | awk '{print $2}')" return 0 fi if command -v ruby >/dev/null 2>&1; then SERVER_CMD="ruby -run -e httpd . --port=PORT --bind-address=127.0.0.1" SERVER_NAME="Ruby $(ruby --version 2>&1 | awk '{print $2}')" return 0 fi if command -v node >/dev/null 2>&1 && command -v npx >/dev/null 2>&1; then SERVER_CMD="npx --yes http-server -p PORT -a 127.0.0.1 --silent" SERVER_NAME="Node.js $(node --version 2>&1)" return 0 fi return 1 } try_install_python() { warn "Ingen webserver funnet. Forsøker å installere Python 3 …" local installed=0 if command -v apt-get >/dev/null 2>&1; then info "Bruker apt (Debian/Ubuntu)" if sudo -n true 2>/dev/null || sudo -v; then sudo apt-get update -y && sudo apt-get install -y python3 && installed=1 fi elif command -v dnf >/dev/null 2>&1; then info "Bruker dnf (Fedora/RHEL)" sudo dnf install -y python3 && installed=1 elif command -v yum >/dev/null 2>&1; then info "Bruker yum" sudo yum install -y python3 && installed=1 elif command -v pacman >/dev/null 2>&1; then info "Bruker pacman (Arch)" sudo pacman -Sy --noconfirm python && installed=1 elif command -v zypper >/dev/null 2>&1; then info "Bruker zypper (openSUSE)" sudo zypper install -y python3 && installed=1 elif command -v apk >/dev/null 2>&1; then info "Bruker apk (Alpine)" sudo apk add python3 && installed=1 elif command -v brew >/dev/null 2>&1; then info "Bruker Homebrew (macOS)" brew install python3 && installed=1 elif [ "$PLATFORM" = "macos" ]; then warn "Homebrew finnes ikke. Installerer Homebrew først …" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \ && brew install python3 && installed=1 fi [ $installed -eq 1 ] } info "Søker etter HTTP-server …" if find_server; then ok "Server-runtime: ${BOLD}$SERVER_NAME${RESET}" else if try_install_python && find_server; then ok "Server-runtime: ${BOLD}$SERVER_NAME${RESET} (nylig installert)" else err "Klarte ikke installere en webserver automatisk." say "" say " Installer ${BOLD}én av disse${RESET} manuelt og prøv igjen:" say " • Python 3: https://www.python.org/downloads/" say " • Node.js: https://nodejs.org/" say " • PHP eller Ruby via pakkesystemet ditt" say "" exit 1 fi fi # --- Verify marked.js (markdown renderer) ------------------------- if [ ! -s "$VENDOR_DIR/marked.min.js" ]; then warn "marked.min.js mangler — laster ned …" mkdir -p "$VENDOR_DIR" if command -v curl >/dev/null 2>&1; then curl -fsSL https://cdn.jsdelivr.net/npm/marked/marked.min.js -o "$VENDOR_DIR/marked.min.js" \ && ok "marked.js lastet ned" \ || { err "Klarte ikke laste ned marked.js. Trenger internett ved første kjøring."; exit 1; } elif command -v wget >/dev/null 2>&1; then wget -q https://cdn.jsdelivr.net/npm/marked/marked.min.js -O "$VENDOR_DIR/marked.min.js" \ && ok "marked.js lastet ned" \ || { err "Klarte ikke laste ned marked.js."; exit 1; } else err "Hverken curl eller wget funnet. Installer en av dem og kjør Start.sh igjen." exit 1 fi else ok "marked.js OK ($(wc -c < "$VENDOR_DIR/marked.min.js") bytes)" fi # --- Sanity-sjekk dataene ---------------------------------------- required_files=( "index.html" "css/style.css" "js/app.js" "data/flashcards.json" "data/quiz.json" "data/exam.json" "notes/uke17-eksamen.md" ) missing=0 for f in "${required_files[@]}"; do if [ ! -e "$APP_DIR/$f" ] && [ ! -L "$APP_DIR/$f" ]; then err "Mangler: app/$f" missing=$((missing + 1)) fi done # app/notes opprettes per plattform (gitignorert). Den skal være en symlink # til ../notes. Håndter ødelagt symlink ELLER en git-materialisert tekstfil. if [ -L "$APP_DIR/notes" ] && [ ! -e "$APP_DIR/notes" ]; then warn "notes-symlink er ødelagt — gjenoppretter …" rm -f "$APP_DIR/notes" elif [ -f "$APP_DIR/notes" ]; then warn "app/notes er en vanlig fil (git-symlink fra Windows) — erstatter med symlink …" rm -f "$APP_DIR/notes" fi if [ ! -d "$APP_DIR/notes" ]; then if [ -d "$SCRIPT_DIR/notes" ]; then ln -sfn "../notes" "$APP_DIR/notes" ok "Symlink notes/ gjenopprettet" else err "Fant ikke notes/-mappen i prosjektet" missing=$((missing + 1)) fi fi [ $missing -eq 0 ] && ok "Alle nødvendige filer på plass" # --- Pick a free port -------------------------------------------- port_free() { local p="$1" if command -v lsof >/dev/null 2>&1; then ! lsof -i :"$p" -sTCP:LISTEN >/dev/null 2>&1 elif command -v ss >/dev/null 2>&1; then ! ss -ltn 2>/dev/null | awk '{print $4}' | grep -q ":$p$" elif command -v netstat >/dev/null 2>&1; then ! netstat -ltn 2>/dev/null | awk '{print $4}' | grep -q ":$p$" else # No tool — assume free, the server will error out if not return 0 fi } PORT="$DEFAULT_PORT" while ! port_free "$PORT"; do warn "Port $PORT er opptatt — prøver $((PORT + 1))" PORT=$((PORT + 1)) if [ "$PORT" -gt 8800 ]; then err "Fant ingen ledig port i 8765–8800. Lukk noen prosesser og prøv igjen." exit 1 fi done URL="http://localhost:$PORT" SERVER_CMD="${SERVER_CMD//PORT/$PORT}" # --- Auto-open browser ------------------------------------------- open_browser() { ( sleep 1.5 if command -v xdg-open >/dev/null 2>&1; then xdg-open "$URL" >/dev/null 2>&1 & elif command -v open >/dev/null 2>&1; then open "$URL" >/dev/null 2>&1 & elif command -v start >/dev/null 2>&1; then start "$URL" >/dev/null 2>&1 & fi ) & } # --- Banner og start ---------------------------------------------- printf "\n" printf " ${BOLD}┌──────────────────────────────────────────────┐${RESET}\n" printf " ${BOLD}│${RESET} ${ORANGE}${BOLD}SMF2290${RESET} · Pensum ${BOLD}│${RESET}\n" printf " ${BOLD}│${RESET} ${BOLD}│${RESET}\n" printf " ${BOLD}│${RESET} ${GREEN}→ %-42s${RESET}${BOLD}│${RESET}\n" "$URL" printf " ${BOLD}│${RESET} ${BOLD}│${RESET}\n" SHORT_NAME="${SERVER_NAME:0:36}" printf " ${BOLD}│${RESET} ${DIM}Server: %-38s${RESET}${BOLD}│${RESET}\n" "$SHORT_NAME" printf " ${BOLD}│${RESET} ${DIM}Trykk Ctrl+C for å stoppe${RESET} ${BOLD}│${RESET}\n" printf " ${BOLD}└──────────────────────────────────────────────┘${RESET}\n\n" open_browser cd "$APP_DIR" # shellcheck disable=SC2086 exec $SERVER_CMD