diff --git a/Dockerfile b/Dockerfile index 7dd237e..480867b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,9 @@ RUN npm install --omit=dev COPY . . +# Ensure proper permissions for node user +RUN chown -R node:node /app + EXPOSE 3001 USER node diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..dd95521 --- /dev/null +++ b/public/app.js @@ -0,0 +1,34 @@ +// Fetch and display system status +async function loadStatus() { + try { + const response = await fetch('/api/status'); + const data = await response.json(); + + // Update status message + const statusEl = document.getElementById('status-message'); + statusEl.textContent = data.message; + statusEl.classList.remove('loading'); + statusEl.classList.add('healthy'); + + // Update timestamp + const timestamp = new Date(data.timestamp); + document.getElementById('status-timestamp').textContent = + `Last updated: ${timestamp.toLocaleString()}`; + + // Update domain + document.getElementById('domain').textContent = data.domain; + + // Update version + document.getElementById('version').textContent = data.version; + } catch (error) { + console.error('Error loading status:', error); + document.getElementById('status-message').textContent = 'Error loading status'; + document.getElementById('status-message').classList.remove('loading'); + } +} + +// Load status on page load +document.addEventListener('DOMContentLoaded', loadStatus); + +// Refresh status every 30 seconds +setInterval(loadStatus, 30000); diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..9952c2c --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..48a0496 --- /dev/null +++ b/public/index.html @@ -0,0 +1,48 @@ + + + + + + HomeBase + + + + +
+
+

HomeBase

+

Home automation hub

+
+ +
+
+

System Status

+
+

Loading...

+

+
+
+ +
+

Information

+
+
+

Version

+

1.0.0

+
+
+

Domain

+

Loading...

+
+
+
+
+ + +
+ + + + diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..3e42766 --- /dev/null +++ b/public/styles.css @@ -0,0 +1,176 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-primary: #1a1a1a; + --bg-secondary: #2d2d2d; + --bg-tertiary: #3d3d3d; + --text-primary: #e0e0e0; + --text-secondary: #b0b0b0; + --accent: #4a9eff; + --accent-hover: #5aafff; + --success: #4caf50; + --border: #404040; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + background-color: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; + width: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; +} + +header { + text-align: center; + margin-bottom: 3rem; + padding-bottom: 2rem; + border-bottom: 2px solid var(--border); +} + +header h1 { + font-size: 2.5rem; + color: var(--accent); + margin-bottom: 0.5rem; + font-weight: 700; +} + +.subtitle { + color: var(--text-secondary); + font-size: 1.1rem; + font-weight: 300; +} + +main { + flex-grow: 1; +} + +section { + margin-bottom: 3rem; +} + +section h2 { + font-size: 1.8rem; + margin-bottom: 1.5rem; + color: var(--text-primary); + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border); +} + +.status-card, +.info-card { + background-color: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 1.5rem; + transition: all 0.3s ease; +} + +.status-card:hover, +.info-card:hover { + background-color: var(--bg-tertiary); + border-color: var(--accent); +} + +.status-card { + margin-bottom: 1rem; +} + +#status-message { + font-size: 1.2rem; + margin-bottom: 0.5rem; +} + +#status-message.loading { + color: var(--accent); +} + +#status-message.healthy { + color: var(--success); +} + +.timestamp { + color: var(--text-secondary); + font-size: 0.9rem; +} + +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.5rem; +} + +.info-card { + text-align: center; +} + +.info-card h3 { + color: var(--accent); + margin-bottom: 0.75rem; + font-size: 0.95rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +.info-card p { + color: var(--text-primary); + font-size: 1.3rem; + font-weight: 500; +} + +footer { + text-align: center; + padding-top: 2rem; + margin-top: auto; + border-top: 1px solid var(--border); + color: var(--text-secondary); + font-size: 0.9rem; +} + +/* Responsive Design */ +@media (max-width: 768px) { + header h1 { + font-size: 2rem; + } + + .container { + padding: 1rem; + } + + section h2 { + font-size: 1.4rem; + } + + .info-grid { + grid-template-columns: 1fr; + } +} + +/* Animations */ +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.loading { + animation: pulse 1.5s ease-in-out infinite; +} diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 index 5ba894a..255038e 100644 --- a/scripts/deploy.ps1 +++ b/scripts/deploy.ps1 @@ -7,19 +7,19 @@ $REMOTE_USER = "spencer" $REMOTE_HOST = "beepc" $REMOTE_DIR = "/home/spencer/homebase" -Write-Host "🚀 Starting deployment to $REMOTE_USER@$REMOTE_HOST..." -ForegroundColor Cyan +Write-Host "[>>] Starting deployment to $REMOTE_USER@$REMOTE_HOST..." -ForegroundColor Cyan # Create remote directory -Write-Host "📁 Ensuring remote directory exists..." -ForegroundColor Yellow +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 + 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 +Write-Host "[*] Copying files to remote server..." -ForegroundColor Yellow $files = @( "Dockerfile", "docker-compose.yml", @@ -30,53 +30,65 @@ $files = @( ) foreach ($file in $files) { - Write-Host " Copying $file..." -ForegroundColor Gray + 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 + 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!" +# Copy public folder recursively +Write-Host " Copying public folder..." -ForegroundColor Gray +scp -r "public" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/" +if ($LASTEXITCODE -ne 0) { + Write-Host "[!!] Failed to copy public folder" -ForegroundColor Red exit 1 -fi -'@ +} + +# Fix permissions on the remote server +Write-Host " Fixing permissions..." -ForegroundColor Gray +ssh $REMOTE_USER@$REMOTE_HOST "chmod -R 755 $REMOTE_DIR" + +# Deploy and start the application +Write-Host "[*] Deploying application..." -ForegroundColor Yellow + +# Create the deployment script with proper Unix line endings +$deployScript = "cd /home/spencer/homebase`n" +$deployScript += "`n" +$deployScript += "echo '[*] Stopping existing container...'`n" +$deployScript += "docker compose down 2>/dev/null || true`n" +$deployScript += "`n" +$deployScript += "echo '[*] Building and starting container...'`n" +$deployScript += "docker compose up -d --build`n" +$deployScript += "`n" +$deployScript += "echo '[*] Setting up systemd service...'`n" +$deployScript += "sudo cp homebase.service /etc/systemd/system/homebase.service 2>/dev/null || echo 'Note: Could not set up systemd service (may need manual setup)'`n" +$deployScript += "sudo systemctl daemon-reload 2>/dev/null || true`n" +$deployScript += "sudo systemctl enable homebase.service 2>/dev/null || true`n" +$deployScript += "sudo systemctl start homebase.service 2>/dev/null || true`n" +$deployScript += "`n" +$deployScript += "echo '[*] Waiting for container to start...'`n" +$deployScript += "sleep 5`n" +$deployScript += "`n" +$deployScript += "echo '[*] Checking container status...'`n" +$deployScript += "docker compose ps`n" +$deployScript += "docker compose logs --tail=20`n" +$deployScript += "`n" +$deployScript += "if docker ps | grep -q homebase; then`n" +$deployScript += " echo '[OK] Container is running!'`n" +$deployScript += " exit 0`n" +$deployScript += "else`n" +$deployScript += " echo '[!!] Container failed to start!'`n" +$deployScript += " exit 1`n" +$deployScript += "fi`n" 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 + Write-Host "[OK] Deployment successful!" -ForegroundColor Green + Write-Host "[>>] App should be available at http://homebase.sketchferret.com" -ForegroundColor Cyan } else { - Write-Host "❌ Deployment failed!" -ForegroundColor Red + Write-Host "[!!] Deployment failed!" -ForegroundColor Red exit 1 } diff --git a/server.js b/server.js index 056b7f5..b8bf1b2 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,14 @@ const express = require('express'); +const path = require('path'); const app = express(); const PORT = process.env.PORT || 3001; const DOMAIN = process.env.DOMAIN || 'homebase.sketchferret.com'; -app.get('/', (req, res) => { +// Serve static files from public folder +app.use(express.static(path.join(__dirname, 'public'))); + +// API endpoint +app.get('/api/status', (req, res) => { res.json({ message: 'HomeBase is running!', domain: DOMAIN,