freezmo/setup.sh
Martin Dvorak 3ea2cea100 Freezmo: self-hosted notes-to-activities study app
Next.js 15 app that turns photos, PDFs, DOCX/PPTX and text into a swipeable
feed of AI-generated activities (flashcards, multiple choice, true/false,
fill-in-the-blank, matching, ordering, short answer) using the user's own
Ollama models.

Highlights:
- All AI calls run in server route handlers; the browser never talks to Ollama.
- Bring-your-own-key mode (default): the key lives only in the browser and is
  sent per-request; server, local-daemon and shared-key modes also supported.
- Client-side document extraction (pdf.js, mammoth, jszip) with scanned-PDF
  page rendering for the vision model; notes stay in their original language.
- ChatGPT-style shell: collapsible sidebar / mobile drawer, neutral token-based
  light + dark themes, folder organisation, PWA (manifest, SW, offline page).
- Strict JSON validation with zod + auto-repair, rate limiting, and request
  guards (cross-origin, body-size, API-key sanitising).
- setup.sh bootstraps deps, .env.local and a production build.

Gitignore excludes .env*, the generated pdf.js worker and local scratch files.
2026-10-06 20:56:55 +00:00

102 lines
3.9 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# Freezmo setup — install dependencies, create a local env file and (by default)
# produce a production build.
#
# Usage:
# ./setup.sh install + create .env.local + production build
# ./setup.sh --dev install + create .env.local, but no build (for `npm run dev`)
# ./setup.sh --no-build install + create .env.local only
#
# Requires: Node.js 20+ and npm. Nothing here needs sudo or a global install.
set -euo pipefail
# --- pretty output ---------------------------------------------------------
bold="$(printf '\033[1m')"
dim="$(printf '\033[2m')"
green="$(printf '\033[32m')"
yellow="$(printf '\033[33m')"
red="$(printf '\033[31m')"
reset="$(printf '\033[0m')"
info() { printf '%s[freezmo]%s %s\n' "$green" "$reset" "$1"; }
warn() { printf '%s[freezmo]%s %s\n' "$yellow" "$reset" "$1"; }
fail() { printf '%s[freezmo]%s %s\n' "$red" "$reset" "$1" >&2; exit 1; }
# --- arguments -------------------------------------------------------------
mode="build"
for arg in "$@"; do
case "$arg" in
--dev|dev) mode="dev" ;;
--no-build|--install-only) mode="install" ;;
-h|--help)
sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
*) fail "Unknown argument: $arg (try --help)" ;;
esac
done
# Run from the directory that contains this script, so `./setup.sh` works from
# anywhere.
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
info "Setting up Freezmo in ${bold}$PWD${reset}"
# --- prerequisites ---------------------------------------------------------
command -v node >/dev/null 2>&1 || fail "Node.js is not installed. Install Node.js 20+ and try again."
command -v npm >/dev/null 2>&1 || fail "npm is not installed. It ships with Node.js."
node_major="$(node -p 'process.versions.node.split(".")[0]')"
if [ "$node_major" -lt 20 ]; then
fail "Node.js 20+ is required (found $(node -v))."
fi
info "Node $(node -v) · npm $(npm -v)"
# --- environment file ------------------------------------------------------
if [ ! -f .env.local ]; then
if [ -f .env.example ]; then
cp .env.example .env.local
info "Created ${bold}.env.local${reset} from .env.example (bring-your-own-key mode by default)."
else
warn ".env.example is missing — the app will fall back to built-in defaults."
fi
else
info "Keeping the existing .env.local (nothing overwritten)."
fi
# --- dependencies ----------------------------------------------------------
info "Installing dependencies with npm…"
if [ -f package-lock.json ]; then
npm ci
else
npm install
fi
# --- build -----------------------------------------------------------------
if [ "$mode" = "install" ]; then
info "Dependencies installed. Skipping the build (--no-build)."
elif [ "$mode" = "dev" ]; then
info "Dependencies installed. Skipping the build (--dev)."
else
info "Building the production bundle…"
npm run build
fi
# --- next steps ------------------------------------------------------------
printf '\n%sFreezmo is ready.%s\n\n' "$bold$green" "$reset"
if [ "$mode" = "dev" ] || [ "$mode" = "install" ]; then
printf ' Start the dev server: %snpm run dev%s → http://localhost:3207\n' "$bold" "$reset"
else
printf ' Start production: %snpm start%s → http://localhost:3207\n' "$bold" "$reset"
printf ' Or for development: %snpm run dev%s\n' "$bold" "$reset"
fi
printf '\n'
printf '%sAuthentication%s (edit .env.local, restart the server afterwards):\n' "$dim" "$reset"
printf ' OLLAMA_AUTH=user-key each browser pastes its own key in Settings (default)\n'
printf ' OLLAMA_AUTH=server one key from OLLAMA_API_KEY in .env.local\n'
printf ' OLLAMA_AUTH=local a local daemon signed in with `ollama signin`\n'
printf '\n'
printf 'Models are chosen in .env.local: VISION_MODEL (multimodal) and TEXT_MODEL.\n'
printf 'The app is served on 0.0.0.0:3207, so phones on the same network can reach it.\n'
printf 'Details: %sREADME.md%s\n' "$bold" "$reset"