Files
Momento/scripts/deploy.sh
Sepehr Ramezani dbd49d6fcb
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m25s
feat: 8 AI providers, rich text editor, agent notifications, UI contrast & font settings
- Add DeepSeek, OpenRouter, Mistral, Z.AI, LM Studio as AI providers
  with editable model names via Combobox in admin settings
- Fix OpenRouter broken by normalizeProvider bug in config.ts
- Convert agent-created notes from Markdown to HTML (TipTap rich text)
- Add Notification model + in-app notifications for agent results
- Agent notification click opens the created note directly
- Add note count display on notebook and inbox headers
- Fix checklist toggle in card view (persist state via localCheckItems)
- Add checklist creation option in tabs/list view (dropdown on + button)
- Fix image description ENOENT error with HTTP fallback
- Improve UI contrast across all themes (input, border, checkbox visibility)
- Add font family setting (Inter vs System Default) in Appearance settings
- Fix CSS font-sans variable conflict (removed dead Geist references)
- Update README with new features and 8 providers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-01 16:14:07 +02:00

305 lines
8.6 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# ============================================================
# Memento - Deploy Script
# ============================================================
# Usage: ./scripts/deploy.sh [--env-only | --build | --full]
#
# --env-only : Generate .env.docker interactively
# --build : Build and start containers (default)
# --full : env + build + first-time setup
# ============================================================
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ENV_FILE="$PROJECT_DIR/.env.docker"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
# -----------------------------------------------------------
# Generate .env.docker
# -----------------------------------------------------------
generate_env() {
if [ -f "$ENV_FILE" ]; then
warn "$ENV_FILE already exists."
read -rp "Overwrite? [y/N] " confirm
[[ "$confirm" != "y" && "$confirm" != "Y" ]] && return 0
fi
info "Generating $ENV_FILE ..."
# Auto-generate NEXTAUTH_SECRET
local secret
secret=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64)
# Auto-generate secure postgres password
local pg_pass
pg_pass=$(openssl rand -hex 16 2>/dev/null || head -c 16 /dev/urandom | hexdump -v -e '/1 "%02x"')
# Ask for URL
local url="http://192.168.1.190"
read -rp "NEXTAUTH_URL [$url]: " input_url
[ -n "$input_url" ] && url="$input_url"
# Ask for AI provider
echo ""
echo "AI Provider:"
echo " 1) OpenRouter (recommended, works out of the box)"
echo " 2) Ollama (requires ollama container)"
echo " 3) OpenAI"
echo " 4) DeepSeek"
echo " 5) Skip AI configuration"
read -rp "Choice [1]: " ai_choice
ai_choice="${ai_choice:-1}"
local ai_tags_provider="openrouter"
local ai_tags_model="google/gemma-3-27b-it"
local ai_embed_provider="openrouter"
local ai_embed_model="text-embedding-3-small"
local ai_chat_provider="openrouter"
local ai_chat_model="google/gemma-3-27b-it"
local custom_api_key=""
local custom_base_url=""
case "$ai_choice" in
1)
read -rp "OpenRouter API Key: " custom_api_key
[ -z "$custom_api_key" ] && error "API key is required"
custom_base_url="https://openrouter.ai/api/v1"
;;
2)
ai_tags_provider="ollama"
ai_tags_model="granite4:latest"
ai_embed_provider="ollama"
ai_embed_model="embeddinggemma:latest"
ai_chat_provider="ollama"
ai_chat_model="granite4:latest"
custom_base_url="http://ollama:11434"
;;
3)
ai_tags_provider="openai"
ai_tags_model="gpt-4o-mini"
ai_embed_provider="openai"
ai_embed_model="text-embedding-3-small"
ai_chat_provider="openai"
ai_chat_model="gpt-4o-mini"
read -rp "OpenAI API Key: " custom_api_key
[ -z "$custom_api_key" ] && error "API key is required"
;;
4)
ai_tags_provider="deepseek"
ai_tags_model="deepseek-chat"
ai_embed_provider="openrouter"
ai_embed_model="text-embedding-3-small"
ai_chat_provider="deepseek"
ai_chat_model="deepseek-chat"
read -rp "DeepSeek API Key: " custom_api_key
[ -z "$custom_api_key" ] && error "API key is required"
custom_base_url="https://api.deepseek.com/v1"
;;
5)
info "Skipping AI configuration"
;;
*)
error "Invalid choice"
;;
esac
# Ask for email provider
echo ""
echo "Email provider:"
echo " 1) Resend"
echo " 2) SMTP"
echo " 3) Skip"
read -rp "Choice [3]: " email_choice
email_choice="${email_choice:-3}"
local resend_key=""
local smtp_config=""
case "$email_choice" in
1)
read -rp "Resend API Key: " resend_key
;;
2)
local smtp_host smtp_port smtp_user smtp_pass smtp_from
read -rp "SMTP Host: " smtp_host
read -rp "SMTP Port [587]: " smtp_port; smtp_port="${smtp_port:-587}"
read -rp "SMTP User: " smtp_user
read -rp "SMTP Password: " smtp_pass
read -rp "SMTP From: " smtp_from
smtp_config="SMTP_HOST=\"$smtp_host\"
SMTP_PORT=\"$smtp_port\"
SMTP_USER=\"$smtp_user\"
SMTP_PASS=\"$smtp_pass\"
SMTP_FROM=\"$smtp_from\""
;;
esac
# Write .env.docker
cat > "$ENV_FILE" << ENVEOF
# =============================================================================
# Memento - Docker Environment (auto-generated)
# =============================================================================
NEXTAUTH_URL="${url}"
NEXTAUTH_SECRET="${secret}"
ALLOW_REGISTRATION="true"
# PostgreSQL
POSTGRES_PORT=5432
POSTGRES_DB=memento
POSTGRES_USER=memento
POSTGRES_PASSWORD="${pg_pass}"
# MCP Server
MCP_MODE="sse"
MCP_PORT="3001"
MCP_SERVER_MODE="sse"
MCP_SERVER_URL="${url}:3001"
# AI - Tags
AI_PROVIDER_TAGS=${ai_tags_provider}
AI_MODEL_TAGS="${ai_tags_model}"
# AI - Embeddings
AI_PROVIDER_EMBEDDING=${ai_embed_provider}
AI_MODEL_EMBEDDING="${ai_embed_model}"
# AI - Chat
AI_PROVIDER_CHAT=${ai_chat_provider}
AI_MODEL_CHAT="${ai_chat_model}"
ENVEOF
# Add API key config if applicable
if [ -n "$custom_api_key" ]; then
case "$ai_choice" in
1)
echo "CUSTOM_OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
echo "CUSTOM_OPENAI_BASE_URL=\"${custom_base_url}\"" >> "$ENV_FILE"
;;
3)
echo "OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
;;
4)
echo "CUSTOM_OPENAI_API_KEY=\"${custom_api_key}\"" >> "$ENV_FILE"
echo "CUSTOM_OPENAI_BASE_URL=\"${custom_base_url}\"" >> "$ENV_FILE"
;;
esac
fi
# Add Ollama URL if using ollama
if [ "$ai_choice" = "2" ]; then
echo "OLLAMA_BASE_URL=\"http://ollama:11434\"" >> "$ENV_FILE"
fi
# Add email config
if [ -n "$resend_key" ]; then
echo "RESEND_API_KEY=\"${resend_key}\"" >> "$ENV_FILE"
fi
if [ -n "$smtp_config" ]; then
echo "" >> "$ENV_FILE"
echo "$smtp_config" >> "$ENV_FILE"
fi
ok "$ENV_FILE created"
echo ""
info "Contents:"
grep -v "KEY\|PASSWORD\|SECRET" "$ENV_FILE" | sed 's/^/ /'
echo " (sensitive values hidden)"
}
# -----------------------------------------------------------
# Build and deploy
# -----------------------------------------------------------
deploy() {
[ -f "$ENV_FILE" ] || error "$ENV_FILE not found. Run with --env-only first."
cd "$PROJECT_DIR"
info "Pulling latest code..."
git pull origin main 2>/dev/null || warn "Could not pull (maybe already up to date or no git)"
info "Building containers..."
docker compose build --parallel 2>&1
info "Starting containers..."
docker compose up -d 2>&1
info "Waiting for healthchecks..."
local retries=0
local max_retries=30
while [ $retries -lt $max_retries ]; do
local unhealthy
unhealthy=$(docker compose ps --format '{{.Status}}' 2>/dev/null | grep -c -v "healthy\|Up" || true)
if [ "$unhealthy" -eq 0 ]; then
break
fi
retries=$((retries + 1))
sleep 2
printf "."
done
echo ""
info "Database migrations are handled by the container entrypoint on every start."
info "The entrypoint handles fresh installs, updates, and P3005 baseline recovery automatically."
echo ""
echo "=========================================="
docker compose ps
echo "=========================================="
echo ""
ok "Deploy complete!"
info "App: $(grep NEXTAUTH_URL "$ENV_FILE" | cut -d= -f2 | tr -d '"')"
# Show admin setup hint if first time
local user_count
user_count=$(docker compose exec -T postgres psql -U memento -d memento -t -c 'SELECT COUNT(*) FROM "User"' 2>/dev/null | tr -d ' ' || echo "0")
local admin_email
admin_email=$(grep '^ADMIN_EMAIL=' "$ENV_FILE" 2>/dev/null | cut -d= -f2 | tr -d '"' || echo "")
if [ "$user_count" = "0" ]; then
echo ""
warn "No users found."
if [ -n "$admin_email" ]; then
info "Register at $(grep NEXTAUTH_URL "$ENV_FILE" | cut -d= -f2 | tr -d '"')/register"
info "Use email: $admin_email (will automatically get ADMIN role)"
else
info "Register at $(grep NEXTAUTH_URL "$ENV_FILE" | cut -d= -f2 | tr -d '"')/register"
warn "ADMIN_EMAIL is not set. Set it in .env.docker for automatic admin role assignment."
fi
fi
}
# -----------------------------------------------------------
# Main
# -----------------------------------------------------------
ACTION="${1:---build}"
case "$ACTION" in
--env-only)
generate_env
;;
--build)
deploy
;;
--full)
generate_env
echo ""
deploy
;;
*)
echo "Usage: $0 [--env-only | --build | --full]"
exit 1
;;
esac