All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m25s
- 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>
305 lines
8.6 KiB
Bash
Executable File
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
|