The volume mount was overwriting the public directory from the Docker image with an empty directory from the host, causing ENOENT errors. Public files are now served from the Docker image via COPY command.
Ryans Recruit Firm - AI Recruitment Site
A professional recruitment website built with Node.js, Express, and PostgreSQL. Features include job listings, application management, CV storage, and admin dashboard.
Features
Public Features
- Job Listings - Browse open positions with detailed information
- Online Applications - Submit applications with CV upload
- Contact Form - Get in touch with the recruitment team
- Responsive Design - Works on desktop, tablet, and mobile
Admin Features
- Dashboard - Overview statistics and quick access
- Application Management - View, filter, and manage applicants
- CV Downloads - Download applicant CVs directly
- Job Management - Create, edit, and manage job postings
- First-time Setup - Automatic admin account creation on first login
Technology Stack
- Backend: Node.js + Express
- Database: PostgreSQL with BYTEA for CV storage
- Authentication: Session-based with bcrypt
- File Upload: Multer (PDF, DOC, DOCX support)
- Deployment: Docker + Docker Compose + Coolify
Quick Start
Local Development
-
Clone the repository
git clone <repository-url> cd ai-recruit-site-template -
Install dependencies
npm install -
Set up environment
cp .env.example .env # Edit .env with your configuration -
Start PostgreSQL (if not using Docker)
# Install PostgreSQL and create database 'recruitment' psql -U postgres -c "CREATE DATABASE recruitment;" -
Run migrations
psql -U postgres -d recruitment -f migrations/001_init_schema.sql psql -U postgres -d recruitment -f migrations/002_seed_data.sql -
Start the application
npm start -
Access the site
- Public site: http://localhost:3000
- Admin panel: http://localhost:3000/admin/login
Docker Development
-
Start all services
docker-compose up -d -
View logs
docker-compose logs -f app -
Stop services
docker-compose down
Deployment to Coolify
Prerequisites
- Coolify account and API token
- Gitea repository
- Cloudflare account (for DNS)
Environment Variables
export COOLIFY_TOKEN="your-coolify-api-token"
export GITEA_TOKEN="your-gitea-access-token"
export CLOUDFLARE_TOKEN="your-cloudflare-api-token"
export COOLIFY_API="https://app.coolify.io/api/v1"
export GITEA_API="https://git.startanaicompany.com/api/v1"
export CLOUDFLARE_API="https://api.cloudflare.com/client/v4"
Step 1: Generate SSH Deploy Keys
ssh-keygen -t ed25519 -f /tmp/recruitai_deploy_key -C "coolify-recruitai-deploy" -N ""
Step 2: Create Coolify Project
curl -s -X POST "${COOLIFY_API}/projects" \
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "RecruitAI",
"description": "AI Recruitment Site"
}'
Step 3: Add Deploy Key to Coolify
PRIVATE_KEY=$(cat /tmp/recruitai_deploy_key | jq -R -s .)
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 repository\",
\"private_key\": ${PRIVATE_KEY}
}"
Step 4: Push to Gitea
git remote add origin git@git.startanaicompany.com:username/ai-recruit-site-template.git
git add .
git commit -m "Initial commit: AI Recruitment Site"
git push -u origin main
Step 5: Add Deploy Key to Gitea
cat > /tmp/gitea_deploy_key.json << EOF
{
"title": "Coolify Deploy Key",
"key": "$(cat /tmp/recruitai_deploy_key.pub)",
"read_only": true
}
EOF
curl -s -X POST "${GITEA_API}/repos/username/ai-recruit-site-template/keys" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @/tmp/gitea_deploy_key.json
Step 6: Create Coolify Application
curl -s -X POST "${COOLIFY_API}/applications/public" \
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"project_uuid": "YOUR_PROJECT_UUID",
"environment_name": "production",
"server_uuid": 0,
"type": "public",
"source": {
"type": "git",
"url": "git@git.startanaicompany.com:username/ai-recruit-site-template",
"branch": "main",
"private_key_uuid": "YOUR_PRIVATE_KEY_UUID"
},
"build_pack": "dockerfile",
"ports_exposes": "3000",
"name": "recruitai"
}'
Step 7: Add Webhook to Gitea
curl -s -X POST "${GITEA_API}/repos/username/ai-recruit-site-template/hooks" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"type": "gitea",
"config": {
"url": "https://app.coolify.io/api/v1/deploy?uuid=YOUR_APP_UUID",
"content_type": "json",
"http_method": "GET"
},
"events": ["push"],
"active": true
}'
Step 8: Configure Cloudflare DNS
curl -s -X POST "${CLOUDFLARE_API}/zones/YOUR_ZONE_ID/dns_records" \
-H "Authorization: Bearer ${CLOUDFLARE_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"type": "CNAME",
"name": "recruitai",
"content": "YOUR_COOLIFY_FQDN",
"ttl": 1,
"proxied": false
}'
Step 9: Update Application Domain
curl -s -X PATCH "${COOLIFY_API}/applications/YOUR_APP_UUID" \
-H "Authorization: Bearer ${COOLIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"domains": "http://recruitai.startanaicompany.com"
}'
Step 10: Deploy
curl -s -X GET "${COOLIFY_API}/deploy?uuid=YOUR_APP_UUID&force=true" \
-H "Authorization: Bearer ${COOLIFY_TOKEN}"
First Time Setup
Admin Account
- Navigate to
/admin/login - On first visit, you'll be prompted to create an admin account
- Enter your email, password, and full name
- Click "Create Admin Account"
- You'll be logged in automatically
Add Jobs
- Go to Admin Dashboard → Jobs
- Click "Create New Job"
- Fill in job details
- Click "Save"
Database Schema
Tables
- admins - Admin user accounts
- job_postings - Job listings
- applicants - Applicant information
- applications - Application submissions with CV storage (BYTEA)
- contact_submissions - Contact form messages
Migrations
Migrations are located in /migrations/ and run automatically on first Docker startup.
API Endpoints
Public APIs
GET /api/jobs- List all active jobsGET /api/jobs/:id- Get job detailsPOST /api/apply- Submit application (multipart/form-data)POST /api/contact- Submit contact form
Admin APIs (Authentication Required)
POST /api/admin/login- Login / Create first adminGET /api/admin/check- Check auth statusPOST /api/admin/logout- LogoutGET /api/admin/stats- Dashboard statisticsGET /api/admin/applications- List applicationsGET /api/admin/applications/:id- Get application detailsGET /api/admin/applications/:id/cv- Download CVPATCH /api/admin/applications/:id- Update applicationGET /api/admin/jobs- List all jobsPOST /api/admin/jobs- Create jobPATCH /api/admin/jobs/:id- Update jobDELETE /api/admin/jobs/:id- Delete job
Security Features
- Password Hashing - bcrypt with 10 rounds
- Session Management - Secure HTTP-only cookies
- SQL Injection Protection - Parameterized queries
- File Upload Validation - Type and size checks
- CSRF Protection - Session-based authentication
- Rate Limiting - (Recommended to add in production)
Environment Variables
| Variable | Description | Default |
|---|---|---|
DB_HOST |
PostgreSQL host | postgres |
DB_PORT |
PostgreSQL port | 5432 |
DB_NAME |
Database name | recruitment |
DB_USER |
Database user | postgres |
DB_PASSWORD |
Database password | changeme123 |
PORT |
Application port | 3000 |
NODE_ENV |
Environment | production |
SESSION_SECRET |
Session encryption key | (required) |
File Upload Limits
- Max CV Size: 5MB
- Allowed Types: PDF, DOC, DOCX
- Storage: PostgreSQL BYTEA column
Troubleshooting
Database Connection Issues
# Check if PostgreSQL is running
docker-compose ps
# View PostgreSQL logs
docker-compose logs postgres
# Restart PostgreSQL
docker-compose restart postgres
Migration Errors
# Connect to database
docker-compose exec postgres psql -U postgres -d recruitment
# Check tables
\dt
# Re-run migrations manually
docker-compose exec postgres psql -U postgres -d recruitment -f /docker-entrypoint-initdb.d/001_init_schema.sql
Application Won't Start
# Check application logs
docker-compose logs app
# Rebuild and restart
docker-compose down
docker-compose build --no-cache
docker-compose up -d
License
MIT
Support
For issues or questions, contact: info@ryansrecruit.com
Description
🎯 Fully customizable AI recruitment website template. Environment-based configuration, one-click deployment, AI agent friendly. Built with Node.js + PostgreSQL + Docker.
Languages
HTML
49.4%
JavaScript
33.5%
CSS
7.5%
Shell
5.6%
PLpgSQL
3.6%
Other
0.4%