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:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
.env
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
13
Dockerfile
Normal file
13
Dockerfile
Normal 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
57
README.md
Normal 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
68
docker-compose.yml
Normal 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
18
package.json
Normal 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
166
public/index.html
Normal 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
75
server.js
Normal 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}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user