From 51081c1b5df45c89f5bc65758ecbbe800fb9ccb3 Mon Sep 17 00:00:00 2001 From: Tanya Date: Mon, 19 Jan 2026 15:20:39 -0500 Subject: [PATCH] chore: Add deployment checklist and PM2 configuration examples This commit introduces a new `DEPLOYMENT_CHECKLIST.md` file that outlines the necessary steps for configuring server-specific settings after pulling from Git. It includes instructions for environment files, PM2 configuration, firewall rules, database setup, and building frontends. Additionally, it adds an example `ecosystem.config.js.example` file for PM2 configuration, providing a template for users to customize for their deployment environment. The `.gitignore` file is updated to include the new PM2 ecosystem config file. --- .gitignore | 4 + DEPLOYMENT_CHECKLIST.md | 93 ++++ admin-frontend/serve.sh | 3 + admin-frontend/src/context/AuthContext.tsx | 5 +- backend/api/auth.py | 15 +- backend/app.py | 8 +- ecosystem.config.js.example | 69 +++ start_all.sh | 23 + .../app/api/auth/[...nextauth]/route.ts | 1 + viewer-frontend/package-lock.json | 451 ------------------ viewer-frontend/package.json | 1 + viewer-frontend/scripts/with-sharp-libpath.sh | 7 + 12 files changed, 224 insertions(+), 456 deletions(-) create mode 100644 DEPLOYMENT_CHECKLIST.md create mode 100644 admin-frontend/serve.sh create mode 100644 ecosystem.config.js.example diff --git a/.gitignore b/.gitignore index 7b7cfb7..954d7f3 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,7 @@ archive/ demo_photos/ data/uploads/ data/thumbnails/ + + +# PM2 ecosystem config (server-specific paths) +ecosystem.config.js diff --git a/DEPLOYMENT_CHECKLIST.md b/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000..391e718 --- /dev/null +++ b/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,93 @@ +# Deployment Checklist + +After pulling from Git, configure the following server-specific settings: + +## 1. Environment Files (gitignored - safe to modify) + +### Root `.env` +```bash +# Database connections +DATABASE_URL=postgresql+psycopg2://user:password@10.0.10.181:5432/punimtag +DATABASE_URL_AUTH=postgresql+psycopg2://user:password@10.0.10.181:5432/punimtag_auth + +# JWT Secrets +SECRET_KEY=your-secret-key-here +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin + +# Photo storage +PHOTO_STORAGE_DIR=/opt/punimtag/data/uploads +``` + +### `admin-frontend/.env` +```bash +VITE_API_URL=http://10.0.10.121:8000 +``` + +### `viewer-frontend/.env` +```bash +DATABASE_URL=postgresql://user:password@10.0.10.181:5432/punimtag +DATABASE_URL_AUTH=postgresql://user:password@10.0.10.181:5432/punimtag_auth +NEXTAUTH_URL=http://10.0.10.121:3001 +NEXTAUTH_SECRET=your-secret-key-here +AUTH_URL=http://10.0.10.121:3001 +``` + +## 2. PM2 Configuration + +Copy the template and customize for your server: + +```bash +cp ecosystem.config.js.example ecosystem.config.js +``` + +Edit `ecosystem.config.js` and update: +- All `cwd` paths to your deployment directory +- All `error_file` and `out_file` paths to your user's home directory +- `PYTHONPATH` and `PATH` environment variables + +## 3. System Configuration (One-time setup) + +### Firewall Rules +```bash +sudo ufw allow 3000/tcp # Admin frontend +sudo ufw allow 3001/tcp # Viewer frontend +sudo ufw allow 8000/tcp # Backend API +``` + +### Database Setup +Create admin user in auth database: +```bash +cd viewer-frontend +npx tsx scripts/fix-admin-user.ts +``` + +## 4. Build Frontends + +```bash +# Admin frontend +cd admin-frontend +npm install +npm run build + +# Viewer frontend +cd viewer-frontend +npm install +npm run prisma:generate:all +npm run build +``` + +## 5. Start Services + +```bash +pm2 start ecosystem.config.js +pm2 save +``` + +## Notes + +- `.env` files are gitignored and safe to modify +- `ecosystem.config.js` is gitignored and server-specific +- Database changes (admin user) persist across pulls +- Firewall rules are system-level and persist + diff --git a/admin-frontend/serve.sh b/admin-frontend/serve.sh new file mode 100644 index 0000000..006a06f --- /dev/null +++ b/admin-frontend/serve.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(dirname "$0")" +PORT=3000 HOST=0.0.0.0 exec npx --yes serve dist \ No newline at end of file diff --git a/admin-frontend/src/context/AuthContext.tsx b/admin-frontend/src/context/AuthContext.tsx index 91e6a18..dce4063 100644 --- a/admin-frontend/src/context/AuthContext.tsx +++ b/admin-frontend/src/context/AuthContext.tsx @@ -76,6 +76,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { const login = async (username: string, password: string) => { try { + setAuthState((prev) => ({ ...prev, isLoading: true })) const tokens: TokenResponse = await authApi.login({ username, password }) localStorage.setItem('access_token', tokens.access_token) localStorage.setItem('refresh_token', tokens.refresh_token) @@ -92,9 +93,11 @@ export function AuthProvider({ children }: { children: ReactNode }) { }) return { success: true, passwordChangeRequired } } catch (error: any) { + setAuthState((prev) => ({ ...prev, isLoading: false })) + console.error('Login error:', error) return { success: false, - error: error.response?.data?.detail || 'Login failed', + error: error.response?.data?.detail || error.message || 'Login failed', } } } diff --git a/backend/api/auth.py b/backend/api/auth.py index 0b219df..efad6ad 100644 --- a/backend/api/auth.py +++ b/backend/api/auth.py @@ -344,9 +344,18 @@ def get_current_user_info( is_admin = user.is_admin if user else False role_value = _resolve_user_role(user, is_admin) - permissions_map = fetch_role_permissions_map(db) - permissions = permissions_map.get(role_value, {}) - + + # Fetch permissions - if it fails, return empty permissions to avoid blocking login + try: + permissions_map = fetch_role_permissions_map(db) + permissions = permissions_map.get(role_value, {}) + except Exception as e: + # If permissions fetch fails, return empty permissions to avoid blocking login + # Log the error but don't fail the request + import traceback + print(f"⚠️ Failed to fetch permissions for /me endpoint: {e}") + print(f" Traceback: {traceback.format_exc()}") + permissions = {} return UserResponse( username=username, is_admin=is_admin, diff --git a/backend/app.py b/backend/app.py index b5deacd..8642817 100644 --- a/backend/app.py +++ b/backend/app.py @@ -634,7 +634,13 @@ async def lifespan(app: FastAPI): # This must happen BEFORE we try to use the engine ensure_postgresql_database(database_url) - # Note: Auth database is managed by the frontend, not created here + # Ensure auth database exists if configured + try: + auth_db_url = get_auth_database_url() + ensure_postgresql_database(auth_db_url) + except ValueError: + # DATABASE_URL_AUTH not set - that's okay + pass # Only create tables if they don't already exist (safety check) inspector = inspect(engine) diff --git a/ecosystem.config.js.example b/ecosystem.config.js.example new file mode 100644 index 0000000..bb7da44 --- /dev/null +++ b/ecosystem.config.js.example @@ -0,0 +1,69 @@ +module.exports = { + apps: [ + { + name: 'punimtag-api', + script: 'venv/bin/uvicorn', + args: 'backend.app:app --host 0.0.0.0 --port 8000', + cwd: '/opt/punimtag', // CHANGE: Update to your deployment directory + interpreter: 'none', + env: { + PYTHONPATH: '/opt/punimtag', // CHANGE: Update to your deployment directory + PATH: '/opt/punimtag/venv/bin:/usr/local/bin:/usr/bin:/bin', // CHANGE: Update paths + }, + error_file: '/home/appuser/.pm2/logs/punimtag-api-error.log', // CHANGE: Update user home directory + out_file: '/home/appuser/.pm2/logs/punimtag-api-out.log', // CHANGE: Update user home directory + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + autorestart: true, + watch: false, + max_memory_restart: '1G', + }, + { + name: 'punimtag-worker', + script: 'venv/bin/python', + args: '-m backend.worker', + cwd: '/opt/punimtag', // CHANGE: Update to your deployment directory + interpreter: 'none', + env: { + PYTHONPATH: '/opt/punimtag', // CHANGE: Update to your deployment directory + PATH: '/opt/punimtag/venv/bin:/usr/local/bin:/usr/bin:/bin', // CHANGE: Update paths + }, + error_file: '/home/appuser/.pm2/logs/punimtag-worker-error.log', // CHANGE: Update user home directory + out_file: '/home/appuser/.pm2/logs/punimtag-worker-out.log', // CHANGE: Update user home directory + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + autorestart: true, + watch: false, + max_memory_restart: '1G', + }, + { + name: 'punimtag-admin', + script: './serve.sh', + cwd: '/opt/punimtag/admin-frontend', // CHANGE: Update to your deployment directory + interpreter: 'bash', + error_file: '/home/appuser/.pm2/logs/punimtag-admin-error.log', // CHANGE: Update user home directory + out_file: '/home/appuser/.pm2/logs/punimtag-admin-out.log', // CHANGE: Update user home directory + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + autorestart: true, + watch: false, + }, + { + name: 'punimtag-viewer', + script: 'npm', + args: 'run start:3001', + cwd: '/opt/punimtag/viewer-frontend', // CHANGE: Update to your deployment directory + interpreter: 'node', + env: { + PORT: '3001', + }, + error_file: '/home/appuser/.pm2/logs/punimtag-viewer-error.log', // CHANGE: Update user home directory + out_file: '/home/appuser/.pm2/logs/punimtag-viewer-out.log', // CHANGE: Update user home directory + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + autorestart: true, + watch: false, + }, + ], +}; + diff --git a/start_all.sh b/start_all.sh index c38f691..fef6f65 100755 --- a/start_all.sh +++ b/start_all.sh @@ -18,6 +18,7 @@ echo "" cleanup() { echo "" echo -e "${YELLOW}Shutting down all servers...${NC}" + kill $WORKER_PID 2>/dev/null || true kill $BACKEND_PID 2>/dev/null || true kill $ADMIN_PID 2>/dev/null || true kill $VIEWER_PID 2>/dev/null || true @@ -45,6 +46,27 @@ if [ -d "venv" ]; then source venv/bin/activate fi export PYTHONPATH="$(pwd)" + +# Check if Redis is running +if ! redis-cli ping > /dev/null 2>&1; then + echo -e "${YELLOW}⚠️ Redis is not running. Starting Redis...${NC}" + redis-server --daemonize yes 2>/dev/null || { + echo -e "${YELLOW}❌ Failed to start Redis. Please start it manually: redis-server${NC}" + exit 1 + } + sleep 1 +fi + +# Start RQ worker in background +echo -e "${GREEN}🚀 Starting RQ worker...${NC}" +env PYTHONPATH="$(pwd)" "$PYTHON_BIN" -m backend.worker > /tmp/worker.log 2>&1 & +WORKER_PID=$! + +# Give worker a moment to start +sleep 2 + +# Start FastAPI server +echo -e "${GREEN}🚀 Starting FastAPI server...${NC}" "$PYTHON_BIN" -m uvicorn backend.app:app --host 127.0.0.1 --port 8000 --reload > /tmp/backend.log 2>&1 & BACKEND_PID=$! @@ -75,6 +97,7 @@ echo -e " Admin Frontend: ${GREEN}http://127.0.0.1:3000${NC}" echo -e " Viewer Frontend: ${GREEN}http://127.0.0.1:3001${NC}" echo "" echo -e "${YELLOW}📋 Logs:${NC}" +echo -e " Worker: ${BLUE}/tmp/worker.log${NC}" echo -e " Backend: ${BLUE}/tmp/backend.log${NC}" echo -e " Admin: ${BLUE}/tmp/admin-frontend.log${NC}" echo -e " Viewer: ${BLUE}/tmp/viewer-frontend.log${NC}" diff --git a/viewer-frontend/app/api/auth/[...nextauth]/route.ts b/viewer-frontend/app/api/auth/[...nextauth]/route.ts index b0a3fbe..244c9b4 100644 --- a/viewer-frontend/app/api/auth/[...nextauth]/route.ts +++ b/viewer-frontend/app/api/auth/[...nextauth]/route.ts @@ -5,6 +5,7 @@ import bcrypt from 'bcryptjs'; export const { handlers, signIn, signOut, auth } = NextAuth({ secret: process.env.NEXTAUTH_SECRET, + trustHost: true, // Trust all hosts (for development/internal network) providers: [ CredentialsProvider({ name: 'Credentials', diff --git a/viewer-frontend/package-lock.json b/viewer-frontend/package-lock.json index acaf34c..3f9432f 100644 --- a/viewer-frontend/package-lock.json +++ b/viewer-frontend/package-lock.json @@ -39,11 +39,9 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/better-sqlite3": "^7.6.13", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "better-sqlite3": "^12.5.0", "dotenv": "^17.2.3", "eslint": "^9", "eslint-config-next": "16.0.3", @@ -1993,16 +1991,6 @@ "version": "2.4.6", "license": "MIT" }, - "node_modules/@types/better-sqlite3": { - "version": "7.6.13", - "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", - "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "dev": true, @@ -2585,27 +2573,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { "version": "2.9.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", @@ -2622,58 +2589,6 @@ "bcrypt": "bin/bcrypt" } }, - "node_modules/better-sqlite3": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz", - "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - }, - "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "dev": true, @@ -2726,31 +2641,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/c12": { "version": "3.1.0", "license": "MIT", @@ -2918,13 +2808,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC" - }, "node_modules/citty": { "version": "0.1.6", "license": "MIT", @@ -3093,32 +2976,6 @@ } } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -3253,16 +3110,6 @@ "iconv-lite": "^0.6.2" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.18.4", "dev": true, @@ -4323,16 +4170,6 @@ "node": ">=0.10.0" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -4444,13 +4281,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT" - }, "node_modules/fill-range": { "version": "7.1.1", "dev": true, @@ -4533,13 +4363,6 @@ } } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT" - }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -4688,13 +4511,6 @@ "giget": "dist/cli.mjs" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT" - }, "node_modules/glob": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", @@ -4924,27 +4740,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "dev": true, @@ -4984,13 +4779,6 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/internal-slot": { "version": "1.1.0", "dev": true, @@ -5908,19 +5696,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.1.2", "dev": true, @@ -6093,13 +5868,6 @@ "node": ">= 18" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT" - }, "node_modules/motion-dom": { "version": "12.23.23", "license": "MIT", @@ -6132,13 +5900,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "dev": true, - "license": "MIT" - }, "node_modules/napi-postinstall": { "version": "0.3.4", "dev": true, @@ -6272,32 +6033,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/node-abi": { - "version": "3.85.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", - "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-addon-api": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", @@ -6532,16 +6267,6 @@ "version": "2.0.11", "license": "MIT" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { "version": "0.9.4", "dev": true, @@ -6762,33 +6487,6 @@ "preact": ">=10" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "dev": true, @@ -6858,17 +6556,6 @@ "react-is": "^16.13.1" } }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "dev": true, @@ -6914,32 +6601,6 @@ ], "license": "MIT" }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rc9": { "version": "2.1.2", "license": "MIT", @@ -7938,53 +7599,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -8294,51 +7908,6 @@ "node": ">=18" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tar/node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -8462,19 +8031,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/tw-animate-css": { "version": "1.4.0", "dev": true, @@ -9140,13 +8696,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, "node_modules/yallist": { "version": "3.1.1", "dev": true, diff --git a/viewer-frontend/package.json b/viewer-frontend/package.json index 9ea58da..4c98fed 100644 --- a/viewer-frontend/package.json +++ b/viewer-frontend/package.json @@ -7,6 +7,7 @@ "prebuild": "npm run prisma:generate:all", "build": "./scripts/with-sharp-libpath.sh next build", "start": "./scripts/with-sharp-libpath.sh next start", + "start:3001": "PORT=3001 ./scripts/with-sharp-libpath.sh next start", "lint": "next lint", "type-check": "tsc --noEmit", "prisma:generate": "prisma generate", diff --git a/viewer-frontend/scripts/with-sharp-libpath.sh b/viewer-frontend/scripts/with-sharp-libpath.sh index 98e6346..f1dc94c 100755 --- a/viewer-frontend/scripts/with-sharp-libpath.sh +++ b/viewer-frontend/scripts/with-sharp-libpath.sh @@ -6,6 +6,13 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" SHARP_LIB_PATH="$PROJECT_DIR/node_modules/sharp/node_modules/@img/sharp-libvips-linux-x64/lib" +# Add node_modules/.bin to PATH if it exists +if [ -d "$PROJECT_DIR/node_modules/.bin" ]; then + export PATH="$PROJECT_DIR/node_modules/.bin:$PATH" +fi + +# Change to project directory to ensure relative paths work +cd "$PROJECT_DIR" || exit 1 if [ -d "$SHARP_LIB_PATH" ]; then export LD_LIBRARY_PATH="$SHARP_LIB_PATH:${LD_LIBRARY_PATH:-}" exec "$@"