Add deployment script and documentation
- Deployment script for Coolify setup - Comprehensive deployment guide - SSH public key included - Updated .gitignore to exclude private key
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ yarn-error.log*
|
|||||||
*.log
|
*.log
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
deploy_key
|
||||||
|
|||||||
305
DEPLOYMENT_GUIDE.md
Normal file
305
DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
# Deployment Guide - AI Recruitment Site
|
||||||
|
|
||||||
|
## ✅ What's Been Completed
|
||||||
|
|
||||||
|
### 1. Project Setup
|
||||||
|
- ✅ Complete project structure created
|
||||||
|
- ✅ Git repository initialized and first commit made
|
||||||
|
- ✅ All code pushed to Gitea: `git@git.startanaicompany.com:mikael.westoo/ai-recruit-site-template.git`
|
||||||
|
|
||||||
|
### 2. Application Built
|
||||||
|
- ✅ Backend API server with authentication
|
||||||
|
- ✅ PostgreSQL database schema and migrations
|
||||||
|
- ✅ Public website (6 pages: home, about, services, jobs, apply, contact)
|
||||||
|
- ✅ Admin dashboard (4 pages: login, dashboard, applicants, jobs)
|
||||||
|
- ✅ CV upload and storage system (PostgreSQL BYTEA)
|
||||||
|
- ✅ Docker + Docker Compose configuration
|
||||||
|
|
||||||
|
### 3. Coolify Setup
|
||||||
|
- ✅ Coolify project created: "RecruitAI" (UUID: `y8804s80goowsccwk8400kwo`)
|
||||||
|
- ✅ SSH deploy keys generated (stored in `./deploy_key` and `./deploy_key.pub`)
|
||||||
|
- ✅ Deploy key added to Gitea repository
|
||||||
|
|
||||||
|
### 4. DNS Setup
|
||||||
|
- ✅ CNAME record created: `recruitai.startanaicompany.com`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Manual Steps Required
|
||||||
|
|
||||||
|
The Coolify API doesn't support adding private keys programmatically. You need to complete the following steps via the Coolify web UI:
|
||||||
|
|
||||||
|
### Step 1: Add Private Key to Coolify
|
||||||
|
|
||||||
|
1. **Login to Coolify**: https://app.coolify.io
|
||||||
|
2. **Navigate to**: Security → Private Keys
|
||||||
|
3. **Click**: "Add Private Key"
|
||||||
|
4. **Fill in**:
|
||||||
|
- **Name**: `recruitai-deploy-key`
|
||||||
|
- **Description**: `Deploy key for RecruitAI Gitea repository`
|
||||||
|
- **Private Key**: Copy the contents of `./deploy_key` file
|
||||||
|
|
||||||
|
**Get the private key**:
|
||||||
|
```bash
|
||||||
|
cd /home/milko/projects/ryan/airecruit-site/ai-recruit-site-template
|
||||||
|
cat ./deploy_key
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Save** the private key
|
||||||
|
6. **Note down** the UUID of the created private key (you'll see it in the URL or key list)
|
||||||
|
|
||||||
|
### Step 2: Create Application in Coolify
|
||||||
|
|
||||||
|
1. **Navigate to**: Projects → RecruitAI → production environment
|
||||||
|
2. **Click**: "+ Add Resource" → "Public Repository (with deploy key)"
|
||||||
|
3. **Fill in the form**:
|
||||||
|
|
||||||
|
**General:**
|
||||||
|
- **Name**: `recruitai`
|
||||||
|
- **Description**: `AI Recruitment Site for Ryans Recruit Firm`
|
||||||
|
|
||||||
|
**Source:**
|
||||||
|
- **Git URL**: `git@git.startanaicompany.com:mikael.westoo/ai-recruit-site-template`
|
||||||
|
- **Branch**: `master`
|
||||||
|
- **Private Key**: Select `recruitai-deploy-key` (the one you just added)
|
||||||
|
|
||||||
|
**Build:**
|
||||||
|
- **Build Pack**: `Dockerfile`
|
||||||
|
- **Dockerfile Location**: `./Dockerfile` (default)
|
||||||
|
- **Docker Compose Location**: Leave empty (we're deploying the app container only)
|
||||||
|
|
||||||
|
**Network:**
|
||||||
|
- **Port**: `3000`
|
||||||
|
- **Domains**: `http://recruitai.startanaicompany.com`
|
||||||
|
|
||||||
|
**Environment Variables** (click "Add Environment Variable" for each):
|
||||||
|
```
|
||||||
|
DB_HOST=postgres
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=recruitment
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_PASSWORD=your-secure-password-here
|
||||||
|
SESSION_SECRET=your-very-secure-session-secret-here
|
||||||
|
NODE_ENV=production
|
||||||
|
PORT=3000
|
||||||
|
```
|
||||||
|
|
||||||
|
**IMPORTANT**: Generate secure passwords for `DB_PASSWORD` and `SESSION_SECRET`
|
||||||
|
|
||||||
|
4. **Add PostgreSQL Database**:
|
||||||
|
- In the same environment, click "+ Add Resource" → "PostgreSQL"
|
||||||
|
- **Name**: `recruitai-postgres`
|
||||||
|
- **Database Name**: `recruitment`
|
||||||
|
- **Username**: `postgres`
|
||||||
|
- **Password**: Use the same password as `DB_PASSWORD` above
|
||||||
|
- **Port**: `5432`
|
||||||
|
- **Image**: `postgres:15-alpine`
|
||||||
|
|
||||||
|
5. **Deploy**: Click "Deploy" button
|
||||||
|
|
||||||
|
### Step 3: Configure Webhook (Optional but Recommended)
|
||||||
|
|
||||||
|
The webhook has already been added to Gitea, but it's pointing to a null UUID. Update it:
|
||||||
|
|
||||||
|
1. **Get the Application UUID** from Coolify (visible in the app URL or details page)
|
||||||
|
2. **Update Gitea webhook**:
|
||||||
|
- Go to: https://git.startanaicompany.com/mikael.westoo/ai-recruit-site-template/settings/hooks
|
||||||
|
- Edit the webhook
|
||||||
|
- Update URL to: `https://app.coolify.io/api/v1/deploy?uuid=YOUR_APP_UUID`
|
||||||
|
- Save
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Accessing the Site
|
||||||
|
|
||||||
|
Once deployed:
|
||||||
|
|
||||||
|
- **Public Site**: http://recruitai.startanaicompany.com
|
||||||
|
- **Admin Login**: http://recruitai.startanaicompany.com/admin/login
|
||||||
|
|
||||||
|
### First Time Admin Setup
|
||||||
|
|
||||||
|
1. Navigate to the admin login page
|
||||||
|
2. On first visit, you'll see a prompt to create the first admin account
|
||||||
|
3. Enter:
|
||||||
|
- **Email**: Your email address
|
||||||
|
- **Password**: A secure password
|
||||||
|
- **Full Name**: Your name
|
||||||
|
4. Click "Create Admin Account"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Database Migrations
|
||||||
|
|
||||||
|
Migrations will run automatically when the PostgreSQL container starts for the first time. They're located in `/migrations/`:
|
||||||
|
|
||||||
|
- `001_init_schema.sql` - Creates all tables, indexes, and constraints
|
||||||
|
- `002_seed_data.sql` - Adds 5 sample job postings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testing the Site
|
||||||
|
|
||||||
|
### Public Pages
|
||||||
|
1. **Home page** - http://recruitai.startanaicompany.com
|
||||||
|
2. **Jobs page** - http://recruitai.startanaicompany.com/jobs
|
||||||
|
3. **Apply page** - Click "Apply Now" on any job
|
||||||
|
4. **Test application submission**:
|
||||||
|
- Fill in the form
|
||||||
|
- Upload a CV (PDF, DOC, or DOCX, max 5MB)
|
||||||
|
- Submit
|
||||||
|
|
||||||
|
### Admin Panel
|
||||||
|
1. **Login** at /admin/login
|
||||||
|
2. **Dashboard** - View statistics
|
||||||
|
3. **Applications** - See submitted applications
|
||||||
|
4. **Download CV** - Test CV download functionality
|
||||||
|
5. **Jobs Management** - Create, edit, activate/deactivate jobs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Checklist
|
||||||
|
|
||||||
|
- [x] Session-based authentication implemented
|
||||||
|
- [x] Passwords hashed with bcrypt
|
||||||
|
- [x] SQL injection protection (parameterized queries)
|
||||||
|
- [x] File upload validation (type and size)
|
||||||
|
- [ ] Change default DB_PASSWORD
|
||||||
|
- [ ] Change SESSION_SECRET
|
||||||
|
- [ ] Enable HTTPS (via Coolify or Cloudflare proxy)
|
||||||
|
- [ ] Review and test all endpoints
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ai-recruit-site-template/
|
||||||
|
├── server.js # Main Express server
|
||||||
|
├── package.json # Node.js dependencies
|
||||||
|
├── Dockerfile # Docker build configuration
|
||||||
|
├── docker-compose.yml # Local development setup
|
||||||
|
├── deploy.sh # Deployment script
|
||||||
|
├── deploy_key # SSH private key (DO NOT COMMIT)
|
||||||
|
├── deploy_key.pub # SSH public key
|
||||||
|
├── migrations/
|
||||||
|
│ ├── 001_init_schema.sql # Database schema
|
||||||
|
│ └── 002_seed_data.sql # Sample data
|
||||||
|
└── public/
|
||||||
|
├── index.html # Home page
|
||||||
|
├── about.html # About page
|
||||||
|
├── services.html # Services page
|
||||||
|
├── jobs.html # Job listings
|
||||||
|
├── apply.html # Application form
|
||||||
|
├── contact.html # Contact form
|
||||||
|
├── admin/
|
||||||
|
│ ├── login.html # Admin login
|
||||||
|
│ ├── dashboard.html # Admin dashboard
|
||||||
|
│ ├── applicants.html # Applications management
|
||||||
|
│ └── jobs.html # Jobs management
|
||||||
|
├── css/
|
||||||
|
│ └── styles.css # Ryan brand styles
|
||||||
|
└── js/
|
||||||
|
├── main.js # Public site JavaScript
|
||||||
|
└── admin.js # Admin panel JavaScript
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Database Connection Errors
|
||||||
|
```bash
|
||||||
|
# Check if PostgreSQL is running
|
||||||
|
docker ps | grep postgres
|
||||||
|
|
||||||
|
# View PostgreSQL logs
|
||||||
|
docker logs recruitai-postgres
|
||||||
|
|
||||||
|
# Connect to database manually
|
||||||
|
docker exec -it recruitai-postgres psql -U postgres -d recruitment
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Won't Start
|
||||||
|
```bash
|
||||||
|
# View application logs in Coolify dashboard
|
||||||
|
# Or check Docker logs
|
||||||
|
docker logs recruitai-app
|
||||||
|
|
||||||
|
# Common issues:
|
||||||
|
# 1. Database not ready - wait 30 seconds and redeploy
|
||||||
|
# 2. Environment variables missing - check Coolify settings
|
||||||
|
# 3. Port already in use - change PORT environment variable
|
||||||
|
```
|
||||||
|
|
||||||
|
### CV Upload Fails
|
||||||
|
- Check file size (max 5MB)
|
||||||
|
- Check file type (PDF, DOC, DOCX only)
|
||||||
|
- Verify PostgreSQL has enough space
|
||||||
|
- Check application logs for errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Resource Details
|
||||||
|
|
||||||
|
### Gitea Repository
|
||||||
|
- **URL**: https://git.startanaicompany.com/mikael.westoo/ai-recruit-site-template
|
||||||
|
- **SSH**: git@git.startanaicompany.com:mikael.westoo/ai-recruit-site-template.git
|
||||||
|
- **Branch**: master
|
||||||
|
|
||||||
|
### Coolify
|
||||||
|
- **Project**: RecruitAI (`y8804s80goowsccwk8400kwo`)
|
||||||
|
- **Environment**: production (`a4g8gwwo48wkkck80og0g84k`)
|
||||||
|
- **Server**: h001 (`ngkwo8css8og0s00c4ows44o`)
|
||||||
|
|
||||||
|
### Cloudflare
|
||||||
|
- **Zone ID**: `e6ade38a28032c3542140a9cbf592838`
|
||||||
|
- **Domain**: recruitai.startanaicompany.com
|
||||||
|
- **DNS Record**: CNAME pointing to Coolify FQDN
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
- **Email**: info@ryansrecruit.com
|
||||||
|
- **Repository**: https://git.startanaicompany.com/mikael.westoo/ai-recruit-site-template
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features Summary
|
||||||
|
|
||||||
|
### Public Features
|
||||||
|
- Professional recruitment website with Ryan brand design
|
||||||
|
- Job listings with search and filtering
|
||||||
|
- Online application form with CV upload (PDF/DOC/DOCX)
|
||||||
|
- Contact form
|
||||||
|
- Fully responsive (mobile, tablet, desktop)
|
||||||
|
|
||||||
|
### Admin Features
|
||||||
|
- Secure authentication (first-time admin creation)
|
||||||
|
- Dashboard with statistics
|
||||||
|
- Application management
|
||||||
|
- View all applications
|
||||||
|
- Filter by status, job, or search
|
||||||
|
- Download CVs
|
||||||
|
- Update application status
|
||||||
|
- Add notes
|
||||||
|
- Job management
|
||||||
|
- Create/edit/delete jobs
|
||||||
|
- Activate/deactivate listings
|
||||||
|
- View all jobs (active and inactive)
|
||||||
|
|
||||||
|
### Technical Features
|
||||||
|
- PostgreSQL database with proper indexes
|
||||||
|
- CV storage in BYTEA columns (efficient binary storage)
|
||||||
|
- Session-based authentication
|
||||||
|
- Docker containerization
|
||||||
|
- Auto-deployment via webhook
|
||||||
|
- Ryan brand colors and design system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2026-01-23
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Status**: Ready for deployment (pending manual Coolify setup)
|
||||||
127
deploy.sh
Executable file
127
deploy.sh
Executable file
@@ -0,0 +1,127 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# API Configuration
|
||||||
|
COOLIFY_TOKEN="1790|8Klldu1hXjtODfZhl0dFn5D6Jhd7X285CuPzcQBdffaed898"
|
||||||
|
GITEA_TOKEN="968f51fc9f957c567f592c691bafd453c839b7e9"
|
||||||
|
CLOUDFLARE_TOKEN="h5mN_lhWW5Rl4aVP0eyVU_Z7u3PndZVtW4q7ExLK"
|
||||||
|
COOLIFY_API="https://app.coolify.io/api/v1"
|
||||||
|
GITEA_API="https://git.startanaicompany.com/api/v1"
|
||||||
|
CLOUDFLARE_API="https://api.cloudflare.com/client/v4"
|
||||||
|
|
||||||
|
# Project details
|
||||||
|
PROJECT_UUID="y8804s80goowsccwk8400kwo"
|
||||||
|
REPO_NAME="ai-recruit-site-template"
|
||||||
|
GITEA_USER="mikael.westoo"
|
||||||
|
ZONE_ID="e6ade38a28032c3542140a9cbf592838"
|
||||||
|
|
||||||
|
echo "=== Step 1: Adding Private Key to Coolify ==="
|
||||||
|
PRIVATE_KEY_JSON=$(jq -Rs . < ./deploy_key)
|
||||||
|
PRIVATE_KEY_RESPONSE=$(curl -s -X POST "${COOLIFY_API}/private-keys" \
|
||||||
|
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"name\": \"recruitai-deploy-key\", \"description\": \"Deploy key for RecruitAI\", \"private_key\": ${PRIVATE_KEY_JSON}}")
|
||||||
|
|
||||||
|
echo "$PRIVATE_KEY_RESPONSE"
|
||||||
|
PRIVATE_KEY_UUID=$(echo "$PRIVATE_KEY_RESPONSE" | jq -r '.uuid')
|
||||||
|
echo "Private Key UUID: ${PRIVATE_KEY_UUID}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 2: Adding Deploy Key to Gitea ==="
|
||||||
|
PUBLIC_KEY=$(cat ./deploy_key.pub)
|
||||||
|
curl -s -X POST "${GITEA_API}/repos/${GITEA_USER}/${REPO_NAME}/keys" \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"title\": \"Coolify Deploy Key\", \"key\": \"${PUBLIC_KEY}\", \"read_only\": true}" | jq '.'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 3: Pushing to Gitea ==="
|
||||||
|
git remote remove origin 2>/dev/null || true
|
||||||
|
git remote add origin git@git.startanaicompany.com:${GITEA_USER}/${REPO_NAME}.git
|
||||||
|
git push -u origin master
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 4: Creating Coolify Application ==="
|
||||||
|
APP_RESPONSE=$(curl -s -X POST "${COOLIFY_API}/applications/public" \
|
||||||
|
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"project_uuid\": \"${PROJECT_UUID}\",
|
||||||
|
\"environment_name\": \"production\",
|
||||||
|
\"server_uuid\": 0,
|
||||||
|
\"type\": \"public\",
|
||||||
|
\"source\": {
|
||||||
|
\"type\": \"git\",
|
||||||
|
\"url\": \"git@git.startanaicompany.com:${GITEA_USER}/${REPO_NAME}\",
|
||||||
|
\"branch\": \"master\",
|
||||||
|
\"private_key_uuid\": \"${PRIVATE_KEY_UUID}\"
|
||||||
|
},
|
||||||
|
\"build_pack\": \"dockerfile\",
|
||||||
|
\"ports_exposes\": \"3000\",
|
||||||
|
\"name\": \"recruitai\"
|
||||||
|
}")
|
||||||
|
|
||||||
|
echo "$APP_RESPONSE" | jq '.'
|
||||||
|
APP_UUID=$(echo "$APP_RESPONSE" | jq -r '.uuid')
|
||||||
|
DEFAULT_FQDN=$(echo "$APP_RESPONSE" | jq -r '.fqdn' | sed 's|http://||' | sed 's|https://||')
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "App UUID: ${APP_UUID}"
|
||||||
|
echo "Default FQDN: ${DEFAULT_FQDN}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 5: Adding Webhook to Gitea ==="
|
||||||
|
curl -s -X POST "${GITEA_API}/repos/${GITEA_USER}/${REPO_NAME}/hooks" \
|
||||||
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"type\": \"gitea\",
|
||||||
|
\"config\": {
|
||||||
|
\"url\": \"https://app.coolify.io/api/v1/deploy?uuid=${APP_UUID}\",
|
||||||
|
\"content_type\": \"json\",
|
||||||
|
\"http_method\": \"GET\"
|
||||||
|
},
|
||||||
|
\"events\": [\"push\"],
|
||||||
|
\"active\": true
|
||||||
|
}" | jq '.'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 6: Creating DNS Record ==="
|
||||||
|
DNS_RESPONSE=$(curl -s -X POST "${CLOUDFLARE_API}/zones/${ZONE_ID}/dns_records" \
|
||||||
|
-H "Authorization: Bearer ${CLOUDFLARE_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"type\": \"CNAME\",
|
||||||
|
\"name\": \"recruitai\",
|
||||||
|
\"content\": \"${DEFAULT_FQDN}\",
|
||||||
|
\"ttl\": 1,
|
||||||
|
\"proxied\": false
|
||||||
|
}")
|
||||||
|
|
||||||
|
echo "$DNS_RESPONSE" | jq '.'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 7: Updating Application Domain ==="
|
||||||
|
curl -s -X PATCH "${COOLIFY_API}/applications/${APP_UUID}" \
|
||||||
|
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"domains\": \"http://recruitai.startanaicompany.com\"}" | jq '.'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 8: Deploying Application ==="
|
||||||
|
curl -s -X GET "${COOLIFY_API}/deploy?uuid=${APP_UUID}&force=true" \
|
||||||
|
-H "Authorization: Bearer ${COOLIFY_TOKEN}" | jq '.'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "=== Deployment Complete! ==="
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Site URL: http://recruitai.startanaicompany.com"
|
||||||
|
echo "Admin Panel: http://recruitai.startanaicompany.com/admin/login"
|
||||||
|
echo ""
|
||||||
|
echo "App UUID: ${APP_UUID}"
|
||||||
|
echo "Project UUID: ${PROJECT_UUID}"
|
||||||
|
echo ""
|
||||||
|
echo "Please wait 2-3 minutes for the deployment to complete."
|
||||||
|
echo "You can check the deployment status in the Coolify dashboard."
|
||||||
1
deploy_key.pub
Normal file
1
deploy_key.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeCe9jD8IRdeIuJlwnVc422p9ZhzWak/9s+h7q7VUF4 coolify-recruitai-deploy
|
||||||
Reference in New Issue
Block a user