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.
102 lines
3.9 KiB
Bash
Executable file
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"
|