docs: add from-scratch deploy script and env templates #12
20
.env_example
Normal file
20
.env_example
Normal file
@ -0,0 +1,20 @@
|
||||
# PunimTag root environment (copy to ".env" and edit values)
|
||||
|
||||
# PostgreSQL (main application DB)
|
||||
DATABASE_URL=postgresql+psycopg2://punimtag:CHANGE_ME@127.0.0.1:5432/punimtag
|
||||
|
||||
# PostgreSQL (auth DB)
|
||||
DATABASE_URL_AUTH=postgresql+psycopg2://punimtag_auth:CHANGE_ME@127.0.0.1:5432/punimtag_auth
|
||||
|
||||
# JWT / bootstrap admin (change these!)
|
||||
SECRET_KEY=CHANGE_ME_TO_A_LONG_RANDOM_STRING
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=CHANGE_ME
|
||||
|
||||
# Photo storage
|
||||
PHOTO_STORAGE_DIR=/punimtag/data/uploads
|
||||
|
||||
# Redis (RQ jobs)
|
||||
REDIS_URL=redis://127.0.0.1:6379/0
|
||||
|
||||
|
||||
6
admin-frontend/.env_example
Normal file
6
admin-frontend/.env_example
Normal file
@ -0,0 +1,6 @@
|
||||
# Admin frontend env (copy to ".env" )
|
||||
|
||||
# Backend API base URL (must be reachable from the browser)
|
||||
VITE_API_URL=
|
||||
|
||||
|
||||
276
docs/DEPLOY_FROM_SCRATCH.md
Normal file
276
docs/DEPLOY_FROM_SCRATCH.md
Normal file
@ -0,0 +1,276 @@
|
||||
# Deploying PunimTag (From Scratch, Simple)
|
||||
|
||||
This guide is for a **fresh install** where the databases do **not** need to be migrated.
|
||||
You will start with **empty PostgreSQL databases** and deploy the app from a copy of the repo
|
||||
(e.g., downloaded from **SharePoint**).
|
||||
|
||||
PunimTag is a monorepo with:
|
||||
- **Backend**: FastAPI (`backend/`) on port **8000**
|
||||
- **Admin**: React/Vite (`admin-frontend/`) on port **3000**
|
||||
- **Viewer**: Next.js (`viewer-frontend/`) on port **3001**
|
||||
- **Jobs**: Redis + RQ worker (`backend/worker.py`)
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites (One-time)
|
||||
|
||||
On the server you deploy to, install:
|
||||
- **Python 3.12+**
|
||||
- **Node.js 18+** and npm
|
||||
- **PostgreSQL 12+**
|
||||
- **Redis 6+**
|
||||
- **PM2** (`npm i -g pm2`)
|
||||
|
||||
Also make sure the server has:
|
||||
- A path for uploaded photos (example: `/punimtag/data/uploads`)
|
||||
- Network access to Postgres + Redis (local or remote)
|
||||
|
||||
### Quick install (Ubuntu/Debian)
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
python3 python3-venv python3-pip \
|
||||
nodejs npm \
|
||||
postgresql-client \
|
||||
redis-server
|
||||
|
||||
sudo systemctl enable --now redis-server
|
||||
redis-cli ping
|
||||
|
||||
# PM2 (process manager)
|
||||
sudo npm i -g pm2
|
||||
```
|
||||
|
||||
Notes:
|
||||
- If you manage Postgres on a separate host, you only need `postgresql-client` on this server.
|
||||
- If you install Postgres locally, install `postgresql` (server) too, not just the client.
|
||||
|
||||
---
|
||||
|
||||
## Fast path (recommended): run the deploy script
|
||||
|
||||
On Ubuntu/Debian you can do most of the setup with one script:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag
|
||||
chmod +x scripts/deploy_from_scratch.sh
|
||||
./scripts/deploy_from_scratch.sh
|
||||
```
|
||||
|
||||
The script will:
|
||||
- Install system packages (including Redis)
|
||||
- Copy `*_example` env files to real `.env` files (if missing)
|
||||
- Install Python + Node dependencies
|
||||
- Generate Prisma clients for the viewer
|
||||
- Create auth DB tables and admin user (idempotent)
|
||||
- Start services with PM2
|
||||
|
||||
If you prefer manual steps, continue below.
|
||||
|
||||
## Step 1 — Put the code on the server
|
||||
|
||||
If you received the code via SharePoint:
|
||||
1. Download the repo ZIP from SharePoint.
|
||||
2. Copy it to the server (SCP/SFTP).
|
||||
3. Extract it into a stable path (example used below):
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/punimtag
|
||||
sudo chown -R $USER:$USER /opt/punimtag
|
||||
# then extract/copy the repository contents into /opt/punimtag
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Create environment files (rename `_example` → real)
|
||||
|
||||
### 2.1 Root env: `/opt/punimtag/.env`
|
||||
|
||||
1. Copy and rename:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag
|
||||
cp .env_example .env
|
||||
```
|
||||
|
||||
2. Edit `.env` and set the real values. The template includes **at least**:
|
||||
|
||||
```bash
|
||||
# PostgreSQL (main database)
|
||||
DATABASE_URL=postgresql+psycopg2://USER:PASSWORD@HOST:5432/punimtag
|
||||
|
||||
# PostgreSQL (auth database)
|
||||
DATABASE_URL_AUTH=postgresql+psycopg2://USER:PASSWORD@HOST:5432/punimtag_auth
|
||||
|
||||
# JWT / admin bootstrap (change these!)
|
||||
SECRET_KEY=change-me
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=change-me
|
||||
|
||||
# Photo uploads storage
|
||||
PHOTO_STORAGE_DIR=/opt/punimtag/data/uploads
|
||||
|
||||
# Redis (background jobs)
|
||||
REDIS_URL=redis://127.0.0.1:6379/0
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The backend **auto-creates tables** on first run if they are missing.
|
||||
- The backend will also attempt to create the databases **if** the configured Postgres user has
|
||||
privileges (otherwise create the DBs manually).
|
||||
|
||||
### 2.2 Admin env: `/opt/punimtag/admin-frontend/.env`
|
||||
|
||||
1. Copy and rename:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag/admin-frontend
|
||||
cp .env_example .env
|
||||
```
|
||||
|
||||
2. Edit `.env`:
|
||||
|
||||
```bash
|
||||
VITE_API_URL=http://YOUR_SERVER_IP_OR_DOMAIN:8000
|
||||
```
|
||||
|
||||
### 2.3 Viewer env: `/opt/punimtag/viewer-frontend/.env`
|
||||
|
||||
1. Copy and rename:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag/viewer-frontend
|
||||
cp .env_example .env
|
||||
```
|
||||
|
||||
2. Edit `.env`:
|
||||
|
||||
```bash
|
||||
# Main DB (same as backend, but Prisma URL format)
|
||||
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/punimtag
|
||||
|
||||
# Auth DB (same as backend, but Prisma URL format)
|
||||
DATABASE_URL_AUTH=postgresql://USER:PASSWORD@HOST:5432/punimtag_auth
|
||||
|
||||
# Optional write-capable DB user (falls back to DATABASE_URL if not set)
|
||||
# DATABASE_URL_WRITE=postgresql://USER:PASSWORD@HOST:5432/punimtag
|
||||
|
||||
# NextAuth
|
||||
NEXTAUTH_URL=http://YOUR_SERVER_IP_OR_DOMAIN:3001
|
||||
NEXTAUTH_SECRET=change-me
|
||||
AUTH_URL=http://YOUR_SERVER_IP_OR_DOMAIN:3001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Install dependencies
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag
|
||||
|
||||
# Backend venv
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
|
||||
# Frontends
|
||||
cd admin-frontend
|
||||
npm ci
|
||||
cd ../viewer-frontend
|
||||
npm ci
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Initialize the viewer Prisma clients
|
||||
|
||||
The viewer uses Prisma clients for both DBs.
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag/viewer-frontend
|
||||
npm run prisma:generate:all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Create the auth DB tables + admin user
|
||||
|
||||
The auth DB schema is set up by the viewer scripts.
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag/viewer-frontend
|
||||
|
||||
# Creates required auth tables / columns (idempotent)
|
||||
npx tsx scripts/setup-auth.ts
|
||||
|
||||
# Ensures an admin user exists (idempotent)
|
||||
npx tsx scripts/fix-admin-user.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Start the services (PM2)
|
||||
|
||||
This repo includes a PM2 config at `ecosystem.config.js`.
|
||||
Verify the paths/ports match your server, then:
|
||||
|
||||
```bash
|
||||
cd /opt/punimtag
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 save
|
||||
```
|
||||
|
||||
Optional (auto-start on reboot):
|
||||
|
||||
```bash
|
||||
pm2 startup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — First-run DB initialization (automatic)
|
||||
|
||||
On first startup, the backend will connect to Postgres and create missing tables automatically.
|
||||
|
||||
To confirm:
|
||||
|
||||
```bash
|
||||
curl -sS http://127.0.0.1:8000/api/v1/health
|
||||
```
|
||||
|
||||
Viewer health check (verifies DB permissions):
|
||||
|
||||
```bash
|
||||
curl -sS http://127.0.0.1:3001/api/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — Open the apps
|
||||
|
||||
- **Admin**: `http://YOUR_SERVER:3000`
|
||||
- **Viewer**: `http://YOUR_SERVER:3001`
|
||||
- **API docs**: `http://YOUR_SERVER:8000/docs`
|
||||
|
||||
---
|
||||
|
||||
## Common fixes
|
||||
|
||||
### Viewer `/api/health` says permission denied
|
||||
|
||||
Run the provided grant script on the DB server (as a privileged Postgres user):
|
||||
- `viewer-frontend/grant_readonly_permissions.sql`
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
pm2 status
|
||||
pm2 logs punimtag-api --lines 200
|
||||
pm2 logs punimtag-viewer --lines 200
|
||||
pm2 logs punimtag-admin --lines 200
|
||||
pm2 logs punimtag-worker --lines 200
|
||||
```
|
||||
|
||||
|
||||
128
scripts/deploy_from_scratch.sh
Executable file
128
scripts/deploy_from_scratch.sh
Executable file
@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
echo "== PunimTag deploy (from scratch) =="
|
||||
echo "Project root: ${PROJECT_ROOT}"
|
||||
echo ""
|
||||
|
||||
if [[ "$(id -u)" -eq 0 ]]; then
|
||||
echo "❌ Do not run as root. Run as a normal user with sudo."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
ensure_file_from_example() {
|
||||
local example_path="$1"
|
||||
local target_path="$2"
|
||||
|
||||
if [[ -f "${target_path}" ]]; then
|
||||
echo "✅ Found ${target_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ! -f "${example_path}" ]]; then
|
||||
echo "❌ Missing ${example_path}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "${example_path}" "${target_path}"
|
||||
echo "✅ Created ${target_path} from ${example_path}"
|
||||
}
|
||||
|
||||
echo "== 1) Install system dependencies (Ubuntu/Debian) =="
|
||||
if command_exists apt-get; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
python3 python3-venv python3-pip \
|
||||
postgresql-client \
|
||||
redis-server \
|
||||
nodejs npm
|
||||
else
|
||||
echo "❌ This script currently supports Ubuntu/Debian (apt-get)."
|
||||
echo " Install dependencies manually, then re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "== 2) Ensure Redis is running =="
|
||||
sudo systemctl enable --now redis-server || true
|
||||
if command_exists redis-cli; then
|
||||
if ! redis-cli ping >/dev/null 2>&1; then
|
||||
echo "❌ Redis is not reachable on localhost:6379"
|
||||
echo " Fix Redis (service, firewall, config) and retry."
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Redis OK (redis-cli ping)"
|
||||
else
|
||||
echo "❌ redis-cli not found after install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "== 3) Ensure env files exist (copied from *_example) =="
|
||||
ensure_file_from_example "${PROJECT_ROOT}/.env_example" "${PROJECT_ROOT}/.env"
|
||||
ensure_file_from_example "${PROJECT_ROOT}/admin-frontend/.env_example" \
|
||||
"${PROJECT_ROOT}/admin-frontend/.env"
|
||||
ensure_file_from_example "${PROJECT_ROOT}/viewer-frontend/.env_example" \
|
||||
"${PROJECT_ROOT}/viewer-frontend/.env"
|
||||
|
||||
echo ""
|
||||
echo "⚠️ IMPORTANT: Edit these files NOW before continuing:"
|
||||
echo " - ${PROJECT_ROOT}/.env"
|
||||
echo " - ${PROJECT_ROOT}/admin-frontend/.env"
|
||||
echo " - ${PROJECT_ROOT}/viewer-frontend/.env"
|
||||
echo ""
|
||||
echo "Press Enter once they are updated..."
|
||||
read -r
|
||||
|
||||
echo ""
|
||||
echo "== 4) Backend Python venv + deps =="
|
||||
cd "${PROJECT_ROOT}"
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
echo "✅ Backend dependencies installed"
|
||||
|
||||
echo ""
|
||||
echo "== 5) Admin frontend deps =="
|
||||
cd "${PROJECT_ROOT}/admin-frontend"
|
||||
npm ci
|
||||
echo "✅ Admin dependencies installed"
|
||||
|
||||
echo ""
|
||||
echo "== 6) Viewer frontend deps + Prisma clients =="
|
||||
cd "${PROJECT_ROOT}/viewer-frontend"
|
||||
npm ci
|
||||
npm run prisma:generate:all
|
||||
echo "✅ Viewer dependencies installed and Prisma clients generated"
|
||||
|
||||
echo ""
|
||||
echo "== 7) Auth DB setup scripts (viewer) =="
|
||||
cd "${PROJECT_ROOT}/viewer-frontend"
|
||||
npx tsx scripts/setup-auth.ts
|
||||
npx tsx scripts/fix-admin-user.ts
|
||||
echo "✅ Auth DB setup done"
|
||||
|
||||
echo ""
|
||||
echo "== 8) Start services (PM2) =="
|
||||
if ! command_exists pm2; then
|
||||
echo "Installing PM2..."
|
||||
sudo npm i -g pm2
|
||||
fi
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 save
|
||||
|
||||
echo ""
|
||||
echo "✅ Done."
|
||||
echo "Admin: http://<server>:3000"
|
||||
echo "Viewer: http://<server>:3001"
|
||||
echo "API: http://<server>:8000/docs"
|
||||
|
||||
|
||||
15
viewer-frontend/.env_example
Normal file
15
viewer-frontend/.env_example
Normal file
@ -0,0 +1,15 @@
|
||||
# Viewer frontend env (copy to ".env" and edit values)
|
||||
|
||||
# Prisma DB URLs (note: no "+psycopg2" here)
|
||||
DATABASE_URL=postgresql://punimtag:CHANGE_ME@127.0.0.1:5432/punimtag
|
||||
DATABASE_URL_AUTH=postgresql://punimtag_auth:CHANGE_ME@127.0.0.1:5432/punimtag_auth
|
||||
|
||||
|
||||
# NextAuth
|
||||
NEXTAUTH_URL=http://127.0.0.1:3001
|
||||
NEXTAUTH_SECRET=CHANGE_ME_TO_A_LONG_RANDOM_STRING
|
||||
AUTH_URL=http://127.0.0.1:3001
|
||||
|
||||
RESEND_API_KEY=CHANGE_ME_secret-key
|
||||
RESEND_FROM_EMAIL="onboarding@resend.dev"
|
||||
UPLOAD_DIR="/mnt/db-server-uploads/pending-photos"
|
||||
Loading…
x
Reference in New Issue
Block a user