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.
This commit is contained in:
parent
3baa0fa8ce
commit
3ea2cea100
83 changed files with 10909 additions and 165 deletions
102
setup.sh
Executable file
102
setup.sh
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
#!/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"
|
||||
Loading…
Add table
Add a link
Reference in a new issue