mirror of
https://github.com/DeNNiiInc/Website-Stress-Test.git
synced 2026-04-17 12:36:00 +00:00
Add deployment scripts and update proxy config
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
deploy-config.json
|
||||||
|
.env
|
||||||
211
PROXMOX_DEPLOY_TEMPLATE.md
Normal file
211
PROXMOX_DEPLOY_TEMPLATE.md
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
# 🚀 Proxmox Deployment Template (TurnKey Node.js)
|
||||||
|
|
||||||
|
**Use this guide to deploy ANY Node.js application to a TurnKey Linux LXC Container.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Prerequisites
|
||||||
|
|
||||||
|
1. **Project**: A Node.js application (Express, Next.js, etc.) in a Git repository.
|
||||||
|
2. **Server**: A Proxmox TurnKey Node.js Container.
|
||||||
|
3. **Access**: Root SSH password for the container.
|
||||||
|
4. **Domain (Optional)**: If using Cloudflare Tunnel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Step 1: Prepare Your Project
|
||||||
|
|
||||||
|
Ensure your project is ready for production:
|
||||||
|
|
||||||
|
1. **Port Configuration**: Ensure your app listens on a configurable port or a fixed internal port (e.g., `4001`).
|
||||||
|
```javascript
|
||||||
|
// server.js
|
||||||
|
const PORT = process.env.PORT || 4001;
|
||||||
|
app.listen(PORT, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Git Ignore**: Ensure `node_modules` and config files with secrets are ignored.
|
||||||
|
```gitignore
|
||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ Step 2: One-Time Server Setup
|
||||||
|
|
||||||
|
SSH into your new container:
|
||||||
|
```bash
|
||||||
|
ssh root@<YOUR_SERVER_IP>
|
||||||
|
```
|
||||||
|
|
||||||
|
Run these commands to prepare the environment:
|
||||||
|
|
||||||
|
### 1. Install Essentials
|
||||||
|
```bash
|
||||||
|
apt-get update && apt-get install -y git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Prepare Directory
|
||||||
|
```bash
|
||||||
|
# Standard web directory
|
||||||
|
mkdir -p /var/www/<APP_NAME>
|
||||||
|
cd /var/www/<APP_NAME>
|
||||||
|
|
||||||
|
# Clone your repo (Use Basic Auth with Token if private)
|
||||||
|
# Format: https://<USER>:<TOKEN>@github.com/<ORG>/<REPO>.git
|
||||||
|
git clone <YOUR_REPO_URL> .
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Setup Permissions
|
||||||
|
```bash
|
||||||
|
# Give ownership to www-data (Nginx user)
|
||||||
|
chown -R www-data:www-data /var/www/<APP_NAME>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Step 3: Application Configuration
|
||||||
|
|
||||||
|
### 1. Systemd Service
|
||||||
|
Create a service file to keep your app running.
|
||||||
|
|
||||||
|
Create `/etc/systemd/system/<APP_NAME>.service`:
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=<APP_NAME> Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
# OR use 'www-data' if app doesn't need root ports
|
||||||
|
# User=www-data
|
||||||
|
WorkingDirectory=/var/www/<APP_NAME>
|
||||||
|
ExecStart=/usr/local/bin/node server.js
|
||||||
|
Restart=always
|
||||||
|
Environment=NODE_ENV=production
|
||||||
|
Environment=PORT=4001
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start:
|
||||||
|
```bash
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable <APP_NAME>
|
||||||
|
systemctl start <APP_NAME>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Nginx Reverse Proxy
|
||||||
|
Configure Nginx to forward port 80 to your app (Port 4001).
|
||||||
|
|
||||||
|
Create `/etc/nginx/sites-available/<APP_NAME>`:
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /var/www/<APP_NAME>;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Serve static files (Optional)
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API/Dynamic requests
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://localhost:4001;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable site:
|
||||||
|
```bash
|
||||||
|
# Remove defaults
|
||||||
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
rm -f /etc/nginx/sites-enabled/nodejs
|
||||||
|
|
||||||
|
# Link new site
|
||||||
|
ln -s /etc/nginx/sites-available/<APP_NAME> /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
# Reload
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ☁️ Step 4: Cloudflare Tunnel (Secure Access)
|
||||||
|
|
||||||
|
Expose your app securely without opening router ports.
|
||||||
|
|
||||||
|
### 1. Install Cloudflared
|
||||||
|
```bash
|
||||||
|
# Add Key
|
||||||
|
mkdir -p --mode=0755 /usr/share/keyrings
|
||||||
|
curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
|
||||||
|
|
||||||
|
# Add Repo
|
||||||
|
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared any main' | tee /etc/apt/sources.list.d/cloudflared.list
|
||||||
|
|
||||||
|
# Install
|
||||||
|
apt-get update && apt-get install -y cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create Tunnel
|
||||||
|
```bash
|
||||||
|
cloudflared tunnel login
|
||||||
|
cloudflared tunnel create <TUNNEL_NAME>
|
||||||
|
# Follow on-screen instructions to map domain -> http://localhost:4001
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Step 5: Automated Updates (PowerShell)
|
||||||
|
|
||||||
|
Create a script `deploy-remote.ps1` in your project root to automate updates.
|
||||||
|
|
||||||
|
**Pre-requisite**: Create `deploy-config.json` (Add to .gitignore!):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"host": "<SERVER_IP>",
|
||||||
|
"username": "root",
|
||||||
|
"password": "<SSH_PASSWORD>",
|
||||||
|
"remotePath": "/var/www/<APP_NAME>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Script `deploy-remote.ps1`**:
|
||||||
|
```powershell
|
||||||
|
# Reads config and updates remote server
|
||||||
|
$Config = Get-Content "deploy-config.json" | ConvertFrom-Json
|
||||||
|
$User = $Config.username; $HostName = $Config.host; $Pass = $Config.password
|
||||||
|
$RemotePath = $Config.remotePath
|
||||||
|
|
||||||
|
# Commands to run remotely
|
||||||
|
$Cmds = "
|
||||||
|
cd $RemotePath
|
||||||
|
echo '⬇️ Pulling code...'
|
||||||
|
git pull
|
||||||
|
echo '📦 Installing deps...'
|
||||||
|
npm install
|
||||||
|
echo '🚀 Restarting service...'
|
||||||
|
systemctl restart <APP_NAME>
|
||||||
|
systemctl status <APP_NAME> --no-pager
|
||||||
|
"
|
||||||
|
|
||||||
|
echo y | plink -ssh -t -pw $Pass "$User@$HostName" $Cmds
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage**: Just run `./deploy-remote.ps1` to deploy!
|
||||||
35
auto-sync.sh
Normal file
35
auto-sync.sh
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# auto-sync.sh - Run by Cron every 5 minutes
|
||||||
|
|
||||||
|
APP_DIR="/var/www/website-stress-test"
|
||||||
|
APP_NAME="website-stress-test"
|
||||||
|
|
||||||
|
cd "$APP_DIR" || exit
|
||||||
|
|
||||||
|
echo "[$(date)] Checking for updates..."
|
||||||
|
|
||||||
|
# Fetch latest changes
|
||||||
|
git remote update
|
||||||
|
|
||||||
|
# Check if we are behind
|
||||||
|
UPSTREAM=${1:-'@{u}'}
|
||||||
|
LOCAL=$(git rev-parse @)
|
||||||
|
REMOTE=$(git rev-parse "$UPSTREAM")
|
||||||
|
BASE=$(git merge-base @ "$UPSTREAM")
|
||||||
|
|
||||||
|
if [ "$LOCAL" = "$REMOTE" ]; then
|
||||||
|
echo "Up-to-date."
|
||||||
|
elif [ "$LOCAL" = "$BASE" ]; then
|
||||||
|
echo "Update available using git pull."
|
||||||
|
git pull
|
||||||
|
echo "Installing dependencies..."
|
||||||
|
npm install
|
||||||
|
echo "Restarting PM2 process..."
|
||||||
|
pm2 restart "$APP_NAME"
|
||||||
|
echo "✅ Updated and restarted."
|
||||||
|
elif [ "$REMOTE" = "$BASE" ]; then
|
||||||
|
echo "Need to push"
|
||||||
|
else
|
||||||
|
echo "Diverged"
|
||||||
|
fi
|
||||||
9
deploy-config.example.json
Normal file
9
deploy-config.example.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"host": "YOUR_SERVER_IP",
|
||||||
|
"username": "root",
|
||||||
|
"password": "YOUR_SSH_PASSWORD",
|
||||||
|
"remotePath": "/var/www/website-stress-test",
|
||||||
|
"repoUrl": "https://github.com/DeNNiiInc/Website-Stress-Test.git",
|
||||||
|
"githubToken": "YOUR_GITHUB_TOKEN",
|
||||||
|
"appName": "website-stress-test"
|
||||||
|
}
|
||||||
52
setup-nginx.sh
Normal file
52
setup-nginx.sh
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# setup-nginx.sh
|
||||||
|
|
||||||
|
APP_NAME="website-stress-test"
|
||||||
|
|
||||||
|
# Install Nginx if not present
|
||||||
|
if ! command -v nginx &> /dev/null; then
|
||||||
|
echo "Installing Nginx..."
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y nginx
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create Config
|
||||||
|
cat > /etc/nginx/sites-available/$APP_NAME <<EOL
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /var/www/$APP_NAME;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Serve static files
|
||||||
|
location / {
|
||||||
|
try_files \$uri \$uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API requests
|
||||||
|
location /proxy {
|
||||||
|
proxy_pass http://localhost:3000/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_cache_bypass \$http_upgrade;
|
||||||
|
|
||||||
|
# Rewrite /proxy to / for the backend if needed?
|
||||||
|
# proxy-server.js treats everything as a request.
|
||||||
|
# If I proxy_pass http://localhost:3000/ then /proxy becomes /
|
||||||
|
# If I proxy_pass http://localhost:3000 then /proxy/foo becomes /proxy/foo.
|
||||||
|
# proxy-server.js doesn't check path, so stripping /proxy is safer.
|
||||||
|
# Using trailing slash in proxy_pass implies stripping the location match.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOL
|
||||||
|
|
||||||
|
# Enable Site
|
||||||
|
ln -sf /etc/nginx/sites-available/$APP_NAME /etc/nginx/sites-enabled/
|
||||||
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
|
# Reload Nginx
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
echo "✅ Nginx Configured!"
|
||||||
56
setup-server.sh
Normal file
56
setup-server.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# setup-server.sh - Initial Setup Script
|
||||||
|
|
||||||
|
# 1. Install Global Dependencies
|
||||||
|
echo "Installing PM2..."
|
||||||
|
npm install -g pm2
|
||||||
|
|
||||||
|
# 2. Clone Repository
|
||||||
|
# Expects: REPO_URL, APP_DIR, GITHUB_TOKEN inside the script or env
|
||||||
|
# We'll use arguments passed to this script: $1=REPO_URL $2=APP_DIR $3=GITHUB_TOKEN
|
||||||
|
|
||||||
|
REPO_URL="$1"
|
||||||
|
APP_DIR="$2"
|
||||||
|
GITHUB_TOKEN="$3"
|
||||||
|
|
||||||
|
# Construct URL with token for auth
|
||||||
|
# Extract host and path from REPO_URL (assuming https://github.com/user/repo.git)
|
||||||
|
# We need to insert token: https://TOKEN@github.com/user/repo.git
|
||||||
|
# Simple replacement:
|
||||||
|
AUTH_REPO_URL="${REPO_URL/https:\/\//https:\/\/$GITHUB_TOKEN@}"
|
||||||
|
|
||||||
|
echo "Preparing application directory: $APP_DIR"
|
||||||
|
mkdir -p "$APP_DIR"
|
||||||
|
|
||||||
|
if [ -d "$APP_DIR/.git" ]; then
|
||||||
|
echo "Repo already exists. Pulling latest..."
|
||||||
|
cd "$APP_DIR"
|
||||||
|
git pull
|
||||||
|
else
|
||||||
|
echo "Cloning repository..."
|
||||||
|
git clone "$AUTH_REPO_URL" "$APP_DIR"
|
||||||
|
cd "$APP_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Install App Dependencies
|
||||||
|
echo "Installing application dependencies..."
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 4. Start Application with PM2
|
||||||
|
APP_NAME="website-stress-test"
|
||||||
|
echo "Starting application with PM2 ($APP_NAME)..."
|
||||||
|
pm2 start proxy-server.js --name "$APP_NAME" --watch --ignore-watch="node_modules"
|
||||||
|
pm2 save
|
||||||
|
pm2 startup | tail -n 1 | bash # Setup startup script
|
||||||
|
|
||||||
|
# 5. Setup Cron Job for Auto-Sync
|
||||||
|
echo "Setting up Cron Job for auto-sync..."
|
||||||
|
SCRIPT_PATH="$APP_DIR/auto-sync.sh"
|
||||||
|
chmod +x "$SCRIPT_PATH"
|
||||||
|
|
||||||
|
# Add to crontab if not exists
|
||||||
|
(crontab -l 2>/dev/null; echo "*/5 * * * * $SCRIPT_PATH >> /var/log/app-sync.log 2>&1") | crontab -
|
||||||
|
|
||||||
|
echo "✅ Setup Complete! Application is running."
|
||||||
|
pm2 status
|
||||||
73
start-deployment.ps1
Normal file
73
start-deployment.ps1
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# start-deployment.ps1
|
||||||
|
# Automates the deployment by reading config, uploading scripts, and executing setup.
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$ConfigPath = "deploy-config.json"
|
||||||
|
|
||||||
|
if (-not (Test-Path $ConfigPath)) {
|
||||||
|
Write-Error "Configuration file '$ConfigPath' not found. Please copy 'deploy-config.example.json' to '$ConfigPath' and fill in your details."
|
||||||
|
}
|
||||||
|
|
||||||
|
$Config = Get-Content $ConfigPath | ConvertFrom-Json
|
||||||
|
|
||||||
|
# Validate Config
|
||||||
|
$Required = @("host", "username", "password", "remotePath", "repoUrl", "githubToken")
|
||||||
|
foreach ($Key in $Required) {
|
||||||
|
if (-not $Config.$Key) {
|
||||||
|
Write-Error "Missing required config key: $Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$User = $Config.username
|
||||||
|
$HostName = $Config.host
|
||||||
|
$Pass = $Config.password
|
||||||
|
# Note: Using password directly in script is tricky with standard ssh/scp without key.
|
||||||
|
# We will check if 'sshpass' or 'plink' is available, or guide user to use keys.
|
||||||
|
# Since the user specifically mentioned providing credentials, they might expect us to use them.
|
||||||
|
# The template used 'plink -pw $Pass'. We will stick to that if available, or warn.
|
||||||
|
|
||||||
|
# Check for plink
|
||||||
|
if (Get-Command "plink.exe" -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "Using plink for connection..."
|
||||||
|
$UsePlink = $true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Warning "plink.exe not found. Falling back to standard scp/ssh. You may be prompted for password multiple times."
|
||||||
|
$UsePlink = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
$RemoteTmp = "/tmp"
|
||||||
|
$SetupScript = "setup-server.sh"
|
||||||
|
$SyncScript = "auto-sync.sh"
|
||||||
|
|
||||||
|
Write-Host "🚀 Starting Deployment to $HostName..."
|
||||||
|
|
||||||
|
# 1. Upload Scripts
|
||||||
|
Write-Host "Uploading scripts..."
|
||||||
|
if ($UsePlink) {
|
||||||
|
echo y | pscp -P 22 -pw $Pass $SetupScript "$User@$HostName`:$RemoteTmp/$SetupScript"
|
||||||
|
echo y | pscp -P 22 -pw $Pass $SyncScript "$User@$HostName`:$RemoteTmp/$SyncScript"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scp $SetupScript "$User@$HostName`:$RemoteTmp/$SetupScript"
|
||||||
|
scp $SyncScript "$User@$HostName`:$RemoteTmp/$SyncScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Execute Setup
|
||||||
|
Write-Host "Executing setup on remote server..."
|
||||||
|
$AppDir = $Config.remotePath
|
||||||
|
$Repo = $Config.repoUrl
|
||||||
|
$Token = $Config.githubToken
|
||||||
|
|
||||||
|
# Make scripts executable and run setup
|
||||||
|
$RemoteCmd = "chmod +x $RemoteTmp/$SetupScript $RemoteTmp/$SyncScript; $RemoteTmp/$SetupScript '$Repo' '$AppDir' '$Token'; rm $RemoteTmp/$SetupScript"
|
||||||
|
|
||||||
|
if ($UsePlink) {
|
||||||
|
echo y | plink -ssh -P 22 -t -pw $Pass "$User@$HostName" $RemoteCmd
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ssh -t "$User@$HostName" $RemoteCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "🎉 Deployment command sent!"
|
||||||
Reference in New Issue
Block a user