254 lines
9.1 KiB
Bash
254 lines
9.1 KiB
Bash
|
|
#!/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
|
|||
|
|
|
|||
|
|
# Notes-mappen er en symlink — sjekk at den peker rett
|
|||
|
|
if [ -L "$APP_DIR/notes" ] && [ ! -e "$APP_DIR/notes" ]; then
|
|||
|
|
warn "notes-symlink er ødelagt — gjenoppretter …"
|
|||
|
|
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
|