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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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,