Komplett pensumstudie-app med: - 120 flashcards, 49 quiz-spørsmål, 16 eksamenstrener-oppgaver - Sammendrag av alle 12 forelesninger - tl;dr-side for siste-minutts pugging - Søk gjennom hele pensumet - Dark/light mode, mobile-vennlig Cross-platform launchere (Start.sh + Start.bat) med auto-detect og auto-install av HTTP-server. PowerShell-fallback for Windows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
254 lines
9.1 KiB
Bash
Executable File
254 lines
9.1 KiB
Bash
Executable File
#!/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
|