Initial commit: Template site for Start an AI Company deployment

- Node.js Express application with modern white UI
- PostgreSQL and Redis integration
- Docker Compose configuration without host port mappings
- Traefik-ready with proper labels
- Health check endpoint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ryan.gogo
2026-02-14 05:52:41 +01:00
commit 95c13a841f
8 changed files with 408 additions and 0 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
npm-debug.log
.git
.gitignore
README.md
.env

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules/
npm-debug.log
.env
.DS_Store
*.log

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

57
README.md Normal file
View File

@@ -0,0 +1,57 @@
# Template 001 - Start an AI Company Deployment Template
This is a template site demonstrating how to deploy applications on Start an AI Company servers using Docker Compose and Traefik.
## Features
- Node.js Express application
- PostgreSQL database
- Redis caching
- Docker Compose configuration
- Traefik-ready (no host port mappings)
- Modern, clean white UI
## Architecture
- **App**: Node.js Express server running on port 3000 (internal)
- **Database**: PostgreSQL 15
- **Cache**: Redis 7
- **Routing**: Traefik handles external routing and SSL
## Deployment
Deploy using the SAAC command:
```bash
saac create application
```
This will deploy the application to https://template-001.startanaicompany.com
## Local Development
```bash
npm install
npm run dev
```
## Docker Deployment
```bash
docker-compose up -d
```
## Environment Variables
- `PORT`: Application port (default: 3000)
- `POSTGRES_HOST`: PostgreSQL host
- `POSTGRES_PORT`: PostgreSQL port
- `POSTGRES_USER`: PostgreSQL user
- `POSTGRES_PASSWORD`: PostgreSQL password
- `POSTGRES_DB`: PostgreSQL database name
- `REDIS_HOST`: Redis host
- `REDIS_PORT`: Redis port
## Health Check
Visit `/health` endpoint to check service status.

68
docker-compose.yml Normal file
View File

@@ -0,0 +1,68 @@
version: '3.8'
services:
app:
build: .
container_name: template-001-app
restart: unless-stopped
environment:
- NODE_ENV=production
- PORT=3000
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=template_db
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
- postgres
- redis
networks:
- app-network
expose:
- "3000"
labels:
- "traefik.enable=true"
- "traefik.http.routers.template-001.rule=Host(`template-001.startanaicompany.com`)"
- "traefik.http.routers.template-001.entrypoints=websecure"
- "traefik.http.routers.template-001.tls=true"
- "traefik.http.routers.template-001.tls.certresolver=letsencrypt"
- "traefik.http.services.template-001.loadbalancer.server.port=3000"
postgres:
image: postgres:15-alpine
container_name: template-001-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=template_db
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
expose:
- "5432"
redis:
image: redis:7-alpine
container_name: template-001-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis-data:/data
networks:
- app-network
expose:
- "6379"
volumes:
postgres-data:
driver: local
redis-data:
driver: local
networks:
app-network:
driver: bridge

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "template-001",
"version": "1.0.0",
"description": "Template site for Start an AI Company deployment",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3",
"redis": "^4.6.12"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}

166
public/index.html Normal file
View File

@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template Site - Start an AI Company</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
padding: 60px 40px;
max-width: 800px;
text-align: center;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.logo {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
margin: 0 auto 30px;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
color: white;
font-weight: bold;
}
h1 {
font-size: 2.5rem;
color: #1a202c;
margin-bottom: 20px;
font-weight: 700;
line-height: 1.2;
}
p {
font-size: 1.2rem;
color: #4a5568;
line-height: 1.8;
margin-bottom: 30px;
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 40px;
}
.feature {
background: #f7fafc;
padding: 25px 20px;
border-radius: 12px;
border: 1px solid #e2e8f0;
transition: transform 0.2s, box-shadow 0.2s;
}
.feature:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.feature-icon {
font-size: 2rem;
margin-bottom: 10px;
}
.feature-title {
font-weight: 600;
color: #2d3748;
margin-bottom: 8px;
font-size: 1rem;
}
.feature-desc {
font-size: 0.875rem;
color: #718096;
margin: 0;
}
.footer {
margin-top: 50px;
padding-top: 30px;
border-top: 1px solid #e2e8f0;
color: #718096;
font-size: 0.9rem;
}
.status-indicator {
display: inline-block;
width: 10px;
height: 10px;
background: #48bb78;
border-radius: 50%;
margin-right: 8px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
</style>
</head>
<body>
<div class="container">
<div class="logo">T</div>
<h1>This site is a template site on how to deploy on Start an AI Company servers</h1>
<p>A modern, scalable deployment template featuring Docker Compose, PostgreSQL, Redis, and Traefik integration.</p>
<div class="features">
<div class="feature">
<div class="feature-icon">🐳</div>
<div class="feature-title">Docker Ready</div>
<p class="feature-desc">Containerized with Docker Compose</p>
</div>
<div class="feature">
<div class="feature-icon">🗄️</div>
<div class="feature-title">PostgreSQL</div>
<p class="feature-desc">Robust database solution</p>
</div>
<div class="feature">
<div class="feature-icon"></div>
<div class="feature-title">Redis Cache</div>
<p class="feature-desc">High-performance caching</p>
</div>
<div class="feature">
<div class="feature-icon">🚀</div>
<div class="feature-title">Traefik Ready</div>
<p class="feature-desc">Automatic routing & SSL</p>
</div>
</div>
<div class="footer">
<p>
<span class="status-indicator"></span>
Status: Running on Start an AI Company infrastructure
</p>
</div>
</div>
</body>
</html>

75
server.js Normal file
View File

@@ -0,0 +1,75 @@
const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
// PostgreSQL connection
const pool = new Pool({
host: process.env.POSTGRES_HOST || 'postgres',
port: process.env.POSTGRES_PORT || 5432,
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'postgres',
database: process.env.POSTGRES_DB || 'template_db'
});
// Redis connection
const redisClient = redis.createClient({
socket: {
host: process.env.REDIS_HOST || 'redis',
port: process.env.REDIS_PORT || 6379
}
});
// Connect to Redis
redisClient.connect().catch(console.error);
// Test database connections
pool.query('SELECT NOW()', (err, res) => {
if (err) {
console.error('PostgreSQL connection error:', err);
} else {
console.log('PostgreSQL connected successfully at:', res.rows[0].now);
}
});
redisClient.on('connect', () => {
console.log('Redis connected successfully');
});
redisClient.on('error', (err) => {
console.error('Redis connection error:', err);
});
// Serve static files
app.use(express.static('public'));
// Health check endpoint
app.get('/health', async (req, res) => {
try {
const pgResult = await pool.query('SELECT NOW()');
await redisClient.ping();
res.json({
status: 'healthy',
postgres: 'connected',
redis: 'connected',
timestamp: pgResult.rows[0].now
});
} catch (error) {
res.status(500).json({
status: 'unhealthy',
error: error.message
});
}
});
// Main route
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});