From 8a1409b3d61ee783b53cb5ca7e9389bb84b02791 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 3 Feb 2026 15:40:41 -0500 Subject: [PATCH] Initial commit: Add HomeBase application with Docker support, deployment scripts, and health check endpoint --- .dockerignore | 6 + .github/workflows/deploy.yml | 47 +++++++ .gitignore | 5 + Dockerfile | 15 +++ README.md | 236 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 11 ++ homebase.service | 16 +++ package.json | 16 +++ scripts/deploy.bat | 30 +++++ scripts/deploy.ps1 | 82 ++++++++++++ scripts/deploy.sh | 68 ++++++++++ server.js | 25 ++++ 12 files changed, 557 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/deploy.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 homebase.service create mode 100644 package.json create mode 100644 scripts/deploy.bat create mode 100644 scripts/deploy.ps1 create mode 100644 scripts/deploy.sh create mode 100644 server.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6daecd0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git/ +.github/ +node_modules/ +*.log +README.md +.gitignore diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..f1c2576 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,47 @@ +name: Deploy to BeePC + +on: + push: + branches: [ main ] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H beepc >> ~/.ssh/known_hosts 2>/dev/null || true + + - name: Deploy to server + run: | + ssh spencer@beepc "mkdir -p /home/spencer/homebase" + rsync -avz --delete \ + --exclude 'node_modules' \ + --exclude '.git' \ + --exclude '.github' \ + --exclude '*.log' \ + ./ spencer@beepc:/home/spencer/homebase/ + + - name: Run Docker Compose + run: | + ssh spencer@beepc << 'ENDSSH' + cd /home/spencer/homebase + docker compose down 2>/dev/null || true + docker compose up -d --build + sleep 5 + docker compose ps + docker compose logs --tail=20 + ENDSSH + + - name: Verify deployment + run: | + sleep 10 + curl -f http://homebase.sketchferret.com/health || exit 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94dd5ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +npm-debug.log +.env +*.log +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7dd237e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY package*.json ./ + +RUN npm install --omit=dev + +COPY . . + +EXPOSE 3001 + +USER node + +CMD ["node", "server.js"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab1ce5e --- /dev/null +++ b/README.md @@ -0,0 +1,236 @@ +# HomeBase + +Simple self-hosted Node.js Docker application with automated SSH deployment. + +## Features + +- 🚀 Simple Express.js server +- đŸŗ Docker containerized +- 🔄 Automated deployment via SSH +- ✅ Health check endpoint +- 🔐 SSH key-based authentication + +## Prerequisites + +On your local machine: +- Node.js (for local testing) +- Docker (for local testing) +- SSH access to beepc server + +On the remote server (beepc): +- Docker installed +- Docker Compose installed +- SSH access configured for user `spencer` + +## Quick Start + +### Local Development + +```bash +# Install dependencies +npm install + +# Run locally +npm start + +# Visit http://localhost:3001 +``` + +### Deployment + +### Option 1: Manual Deployment (Windows) + +```bash +# Run the deployment script from project root +.\scripts\deploy.bat +``` + +### Option 2: Manual Deployment (Linux/Mac) + +```bash +# Make script executable +chmod +x scripts/deploy.sh + +# Run deployment +bash scripts/deploy.sh +``` + +### Option 3: Manual Deployment (PowerShell) + +```powershell +# Run the deployment script from project root +.\scripts\deploy.ps1 +``` + +### Option 4: Automated Deployment (GitHub Actions) + +1. Add your SSH private key to GitHub Secrets: + - Go to your repository Settings > Secrets and variables > Actions + - Add a new secret named `SSH_PRIVATE_KEY` + - Paste your SSH private key content + +2. Push to main branch: + ```bash + git add . + git commit -m "Initial commit" + git push origin main + ``` + +3. The app will automatically deploy to spencer@beepc + +## SSH Setup + +Ensure you have SSH key-based authentication set up: + +```bash +# Generate SSH key if you don't have one +ssh-keygen -t ed25519 -C "your_email@example.com" + +# Copy key to remote server +ssh-copy-id spencer@beepc + +# Test connection +ssh spencer@beepc +``` + +## Remote Server Setup + +On beepc, ensure Docker is installed: + +```bash +# Install Docker (Ubuntu/Debian) +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh + +# Add user to docker group +sudo usermod -aG docker spencer + +# Install Docker Compose +sudo apt-get update +sudo apt-get install docker-compose-plugin + +# Logout and login again for group changes to take effect +``` + +## API Endpoints + +- `GET /` - Main endpoint, returns app status +- `GET /health` - Health check endpoint + +## Project Structure + +``` +HomeBase/ +├── scripts/ +│ ├── deploy.ps1 # PowerShell deployment script +│ ├── deploy.sh # Bash deployment script +│ └── deploy.bat # Windows batch deployment script +├── .github/ +│ └── workflows/ +│ └── deploy.yml # GitHub Actions workflow +├── .dockerignore # Docker ignore file +├── .gitignore # Git ignore file +├── Dockerfile # Docker configuration +├── docker-compose.yml # Docker Compose configuration +├── homebase.service # Systemd service file +├── package.json # Node.js dependencies +├── server.js # Express server +└── README.md # This file +``` + +## Deployment Process + +The deployment script does the following: + +1. Creates remote directory if it doesn't exist +2. Syncs files to remote server via rsync/scp +3. Stops existing Docker container +4. Builds new Docker image +5. Starts new container +6. Verifies container is running + +## Troubleshooting + +### Check logs on remote server + +```bash +ssh spencer@beepc +cd /home/spencer/homebase +docker compose logs -f +``` + +### Check container status + +```bash +ssh spencer@beepc "docker ps" +``` + +### Restart container + +```bash +ssh spencer@beepc "cd /home/spencer/homebase && docker compose restart" +``` + +### Remove and rebuild + +```bash +ssh spencer@beepc "cd /home/spencer/homebase && docker compose down && docker compose up -d --build" +``` + +## Accessing the App + +Once deployed, access the app at: +- `http://homebase.sketchferret.com` (reverse proxied, no port needed) +- `http://beepc:3001` (direct access from local network) +- `http://localhost:3001` (direct access on the server) + +**Note:** The app runs on port 3001 internally but is accessed via reverse proxy at the standard HTTP port. + +## Auto-Start on Boot + +The deployment script automatically sets up a systemd service that: +- Starts the Docker container on server boot +- Restarts the container if it crashes +- Runs as the spencer user + +To manually manage the service: + +```bash +# Check status +ssh spencer@beepc "sudo systemctl status homebase" + +# Stop service +ssh spencer@beepc "sudo systemctl stop homebase" + +# Start service +ssh spencer@beepc "sudo systemctl start homebase" + +# Restart service +ssh spencer@beepc "sudo systemctl restart homebase" + +# Disable auto-start +ssh spencer@beepc "sudo systemctl disable homebase" +``` + +## Environment Variables + +You can customize settings by modifying [docker-compose.yml](docker-compose.yml): + +```yaml +environment: + - PORT=3001 # Internal port (reverse proxy maps to standard HTTP) + - DOMAIN=homebase.sketchferret.com # Your domain +ports: + - "3001:3001" # Port mapping for reverse proxy backend +``` + +## Security Notes + +- Uses SSH key authentication (no passwords) +- Container runs as non-root user +- Only production dependencies installed in Docker image +- Health check endpoint for monitoring + +## License + +MIT diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ed0b4b9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + app: + build: . + container_name: homebase + restart: unless-stopped + ports: + - "3001:3001" + environment: + - NODE_ENV=production + - PORT=3001 + - DOMAIN=homebase.sketchferret.com diff --git a/homebase.service b/homebase.service new file mode 100644 index 0000000..f667322 --- /dev/null +++ b/homebase.service @@ -0,0 +1,16 @@ +[Unit] +Description=HomeBase Docker Application +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/home/spencer/homebase +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +User=spencer +Group=spencer + +[Install] +WantedBy=multi-user.target diff --git a/package.json b/package.json new file mode 100644 index 0000000..2909430 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "homebase", + "version": "1.0.0", + "description": "Simple self-hosted Node.js app", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "node server.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/scripts/deploy.bat b/scripts/deploy.bat new file mode 100644 index 0000000..fd6c0ee --- /dev/null +++ b/scripts/deploy.bat @@ -0,0 +1,30 @@ +@echo off +REM Batch deployment script for HomeBase (Windows) +REM Run this from the project root: scripts\deploy.bat + +set REMOTE_USER=spencer +set REMOTE_HOST=beepc +set REMOTE_DIR=/home/spencer/homebase +set APP_NAME=homebase + +echo Starting deployment to %REMOTE_USER%@%REMOTE_HOST%... + +REM Create remote directory if it doesn't exist +echo Ensuring remote directory exists... +ssh %REMOTE_USER%@%REMOTE_HOST% "mkdir -p %REMOTE_DIR%" + +REM Sync files to remote server +echo Syncing files to remote server... +scp -r Dockerfile docker-compose.yml package.json server.js .dockerignore homebase.service %REMOTE_USER%@%REMOTE_HOST%:%REMOTE_DIR%/ + +REM Deploy on remote server +echo Deploying application on remote server... +ssh %REMOTE_USER%@%REMOTE_HOST% "cd %REMOTE_DIR% && docker compose down 2>nul & docker compose up -d --build && timeout /t 5 && docker ps | findstr homebase" + +if %ERRORLEVEL% EQU 0 ( + echo Deployment successful! + echo App should be available at http://homebase.sketchferret.com +) else ( + echo Deployment failed! + exit /b 1 +) diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 new file mode 100644 index 0000000..5ba894a --- /dev/null +++ b/scripts/deploy.ps1 @@ -0,0 +1,82 @@ +# PowerShell deployment script for HomeBase +# Run this from the project root directory: .\scripts\deploy.ps1 + +$ErrorActionPreference = "Stop" + +$REMOTE_USER = "spencer" +$REMOTE_HOST = "beepc" +$REMOTE_DIR = "/home/spencer/homebase" + +Write-Host "🚀 Starting deployment to $REMOTE_USER@$REMOTE_HOST..." -ForegroundColor Cyan + +# Create remote directory +Write-Host "📁 Ensuring remote directory exists..." -ForegroundColor Yellow +ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_DIR" + +if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Failed to create remote directory" -ForegroundColor Red + exit 1 +} + +# Copy files to remote server +Write-Host "đŸ“Ļ Copying files to remote server..." -ForegroundColor Yellow +$files = @( + "Dockerfile", + "docker-compose.yml", + "package.json", + "server.js", + ".dockerignore", + "homebase.service" +) + +foreach ($file in $files) { + Write-Host " Copying $file..." -ForegroundColor Gray + scp $file "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/" + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Failed to copy $file" -ForegroundColor Red + exit 1 + } +} + +# Deploy and start the application +Write-Host "đŸŗ Deploying application..." -ForegroundColor Yellow +$deployScript = @' +cd /home/spencer/homebase + +echo "âšī¸ Stopping existing container..." +docker compose down 2>/dev/null || true + +echo "🔨 Building and starting container..." +docker compose up -d --build + +echo "âš™ī¸ Setting up systemd service..." +sudo cp homebase.service /etc/systemd/system/homebase.service 2>/dev/null || echo "Note: Could not set up systemd service (may need manual setup)" +sudo systemctl daemon-reload 2>/dev/null || true +sudo systemctl enable homebase.service 2>/dev/null || true +sudo systemctl start homebase.service 2>/dev/null || true + +echo "âŗ Waiting for container to start..." +sleep 5 + +echo "✅ Checking container status..." +docker compose ps +docker compose logs --tail=20 + +if docker ps | grep -q homebase; then + echo "✅ Container is running!" + exit 0 +else + echo "❌ Container failed to start!" + exit 1 +fi +'@ + +ssh $REMOTE_USER@$REMOTE_HOST $deployScript + +if ($LASTEXITCODE -eq 0) { + Write-Host "✅ Deployment successful!" -ForegroundColor Green + Write-Host "🌐 App should be available at http://homebase.sketchferret.com" -ForegroundColor Cyan +} else { + Write-Host "❌ Deployment failed!" -ForegroundColor Red + exit 1 +} diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..298c99a --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Bash deployment script for HomeBase +# Run this from the project root: bash scripts/deploy.sh + +# Configuration +REMOTE_USER="spencer" +REMOTE_HOST="beepc" +REMOTE_DIR="/home/spencer/homebase" +APP_NAME="homebase" + +echo "🚀 Starting deployment to ${REMOTE_USER}@${REMOTE_HOST}..." + +# Create remote directory if it doesn't exist +echo "📁 Ensuring remote directory exists..." +ssh ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${REMOTE_DIR}" + +# Sync files to remote server (excluding unnecessary files) +echo "đŸ“Ļ Syncing files to remote server..." +rsync -avz --delete \ + --exclude 'node_modules' \ + --exclude '.git' \ + --exclude '.github' \ + --exclude '*.log' \ + ./ ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/ + +# Deploy on remote server +echo "đŸŗ Deploying application on remote server..." +ssh ${REMOTE_USER}@${REMOTE_HOST} << 'ENDSSH' +cd /home/spencer/homebase + +# Stop and remove existing container +echo "âšī¸ Stopping existing container..." +docker compose down 2>/dev/null || true + +# Build and start new container +echo "🔨 Building and starting container..." +docker compose up -d --build + +# Set up systemd service for auto-start on boot +echo "âš™ī¸ Setting up systemd service..." +sudo cp homebase.service /etc/systemd/system/homebase.service +sudo systemctl daemon-reload +sudo systemctl enable homebase.service +sudo systemctl start homebase.service 2>/dev/null || true + +# Wait for container to be healthy +echo "âŗ Waiting for container to start..." +sleep 5 + +# Check if container is running +if docker ps | grep -q homebase; then + echo "✅ Container is running!" + docker compose logs --tail=20 +else + echo "❌ Container failed to start!" + docker compose logs --tail=50 + exit 1 +fi +ENDSSH + +if [ $? -eq 0 ]; then + echo "✅ Deployment successful!" + echo "🌐 App should be available at http://homebase.sketchferret.com" +else + echo "❌ Deployment failed!" + exit 1 +fi diff --git a/server.js b/server.js new file mode 100644 index 0000000..056b7f5 --- /dev/null +++ b/server.js @@ -0,0 +1,25 @@ +const express = require('express'); +const app = express(); +const PORT = process.env.PORT || 3001; +const DOMAIN = process.env.DOMAIN || 'homebase.sketchferret.com'; + +app.get('/', (req, res) => { + res.json({ + message: 'HomeBase is running!', + domain: DOMAIN, + timestamp: new Date().toISOString(), + version: '1.0.0' + }); +}); + +app.get('/health', (req, res) => { + res.json({ status: 'healthy' }); +}); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server running on port ${PORT}`); + console.log(`Domain: ${DOMAIN}`); + console.log(`Visit: http://${DOMAIN}`); +}); + +