diff --git a/.gitignore b/.gitignore index a502adf..37517f9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ yarn-error.log* *.log .vscode/ .idea/ +deploy_key diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..29d64b9 --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -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) diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..b328c56 --- /dev/null +++ b/deploy.sh @@ -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." diff --git a/deploy_key.pub b/deploy_key.pub new file mode 100644 index 0000000..628959f --- /dev/null +++ b/deploy_key.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeCe9jD8IRdeIuJlwnVc422p9ZhzWak/9s+h7q7VUF4 coolify-recruitai-deploy