fix(ci): trigger workflow on main branch to enable :latest tag
Changes:
- Create Gitea workflow for ai-stack-deployer
- Trigger on main branch (default branch)
- Use oussamadouhou + REGISTRY_TOKEN for authentication
- Build from ./Dockerfile
This enables :latest tag creation via {{is_default_branch}}.
Tags created:
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:latest
- git.app.flexinit.nl/oussamadouhou/ai-stack-deployer:<sha>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
378
README.md
Normal file
378
README.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# AI Stack Deployer
|
||||
|
||||
> Self-service portal for deploying personal OpenCode AI coding assistant stacks
|
||||
|
||||
[](https://dokploy.com)
|
||||
[](https://bun.sh)
|
||||
[](https://hono.dev)
|
||||
|
||||
## Overview
|
||||
|
||||
AI Stack Deployer is a production-ready web application that allows users to deploy their own personal AI coding assistant in seconds. Each deployment creates a fully functional OpenCode instance at `{name}.ai.flexinit.nl` with automatic HTTPS via wildcard SSL certificate.
|
||||
|
||||
### Key Features
|
||||
|
||||
- **One-Click Deployment**: Deploy AI stacks with a single form submission
|
||||
- **Real-Time Progress**: SSE-powered live updates during deployment
|
||||
- **Name Validation**: Real-time availability checking and format validation
|
||||
- **Modern UI**: Responsive design with smooth animations
|
||||
- **Production Ready**: Docker containerized with health checks
|
||||
- **No DNS Setup**: Leverages pre-configured wildcard DNS and SSL
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
User Browser
|
||||
↓
|
||||
AI Stack Deployer (Hono + Bun)
|
||||
↓
|
||||
Dokploy API (10.100.0.20:3000)
|
||||
↓
|
||||
Traefik (*.ai.flexinit.nl → 144.76.116.169)
|
||||
↓
|
||||
User's AI Stack Container (OpenCode + ttyd)
|
||||
```
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Runtime**: Bun 1.3+
|
||||
- **Framework**: Hono 4.11.3
|
||||
- **Language**: TypeScript
|
||||
- **Container**: Docker with multi-stage builds
|
||||
- **Orchestration**: Dokploy
|
||||
- **Reverse Proxy**: Traefik with wildcard SSL
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Bun 1.3 or higher
|
||||
- Docker (for containerized deployment)
|
||||
- Valid Dokploy API token
|
||||
- Access to Dokploy instance at `http://10.100.0.20:3000`
|
||||
|
||||
### Local Development
|
||||
|
||||
1. **Clone and Install**:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd ai-stack-deployer
|
||||
bun install
|
||||
```
|
||||
|
||||
2. **Configure Environment**:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Edit .env and add your DOKPLOY_API_TOKEN
|
||||
```
|
||||
|
||||
3. **Run Development Server**:
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
4. **Access the Application**:
|
||||
Open http://localhost:3000 in your browser
|
||||
|
||||
### Production Deployment
|
||||
|
||||
#### Option 1: Docker Compose (Recommended)
|
||||
|
||||
```bash
|
||||
# Build and run
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### Option 2: Manual Docker
|
||||
|
||||
```bash
|
||||
# Build
|
||||
docker build -t ai-stack-deployer:latest .
|
||||
|
||||
# Run
|
||||
docker run -d \
|
||||
--name ai-stack-deployer \
|
||||
-p 3000:3000 \
|
||||
--env-file .env \
|
||||
ai-stack-deployer:latest
|
||||
|
||||
# Check health
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
#### Option 3: Deploy to Dokploy
|
||||
|
||||
1. Build and push to registry:
|
||||
```bash
|
||||
docker build -t your-registry/ai-stack-deployer:latest .
|
||||
docker push your-registry/ai-stack-deployer:latest
|
||||
```
|
||||
|
||||
2. In Dokploy UI:
|
||||
- Create project: `ai-stack-deployer-portal`
|
||||
- Create application from Docker image
|
||||
- Set domain: `portal.ai.flexinit.nl`
|
||||
- Configure environment variables
|
||||
- Deploy
|
||||
|
||||
3. Verify:
|
||||
```bash
|
||||
curl https://portal.ai.flexinit.nl/health
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### Health Check
|
||||
```http
|
||||
GET /health
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"timestamp": "2026-01-09T12:00:00.000Z",
|
||||
"version": "0.1.0",
|
||||
"service": "ai-stack-deployer",
|
||||
"activeDeployments": 0
|
||||
}
|
||||
```
|
||||
|
||||
#### Check Name Availability
|
||||
```http
|
||||
GET /api/check/:name
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"available": true,
|
||||
"valid": true,
|
||||
"name": "john-dev"
|
||||
}
|
||||
```
|
||||
|
||||
#### Deploy Stack
|
||||
```http
|
||||
POST /api/deploy
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "john-dev"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"deploymentId": "dep_1234567890_abc123",
|
||||
"url": "https://john-dev.ai.flexinit.nl",
|
||||
"statusEndpoint": "/api/status/dep_1234567890_abc123"
|
||||
}
|
||||
```
|
||||
|
||||
#### Deployment Status (SSE)
|
||||
```http
|
||||
GET /api/status/:deploymentId
|
||||
```
|
||||
|
||||
Server-Sent Events stream with progress updates:
|
||||
```
|
||||
event: progress
|
||||
data: {"status":"creating_project","progress":25,"currentStep":"Creating Dokploy project"}
|
||||
|
||||
event: progress
|
||||
data: {"status":"creating_application","progress":50,"currentStep":"Creating application container"}
|
||||
|
||||
event: progress
|
||||
data: {"status":"deploying","progress":85,"currentStep":"Deploying application"}
|
||||
|
||||
event: complete
|
||||
data: {"url":"https://john-dev.ai.flexinit.nl","status":"ready"}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Required
|
||||
|
||||
- `DOKPLOY_URL` - Dokploy API URL (default: `http://10.100.0.20:3000`)
|
||||
- `DOKPLOY_API_TOKEN` - Dokploy API authentication token
|
||||
|
||||
### Optional
|
||||
|
||||
- `PORT` - HTTP server port (default: `3000`)
|
||||
- `HOST` - Bind address (default: `0.0.0.0`)
|
||||
- `STACK_DOMAIN_SUFFIX` - Domain suffix for stacks (default: `ai.flexinit.nl`)
|
||||
- `STACK_IMAGE` - Docker image for user stacks (default: `git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest`)
|
||||
- `RESERVED_NAMES` - Comma-separated forbidden names (default: `admin,api,www,root,system,test,demo,portal`)
|
||||
|
||||
### Not Used in Deployment
|
||||
|
||||
- `HETZNER_API_TOKEN` - Only for testing/manual DNS operations
|
||||
- `HETZNER_ZONE_ID` - DNS zone ID (343733 for flexinit.nl)
|
||||
- `TRAEFIK_IP` - Public IP reference (144.76.116.169)
|
||||
|
||||
## Name Validation Rules
|
||||
|
||||
Stack names must follow these rules:
|
||||
- 3-20 characters long
|
||||
- Lowercase letters (a-z), numbers (0-9), and hyphens (-) only
|
||||
- Cannot start or end with a hyphen
|
||||
- Cannot be a reserved name (admin, api, www, root, system, test, demo, portal)
|
||||
|
||||
## Development
|
||||
|
||||
### Commands
|
||||
|
||||
```bash
|
||||
# Development server with hot reload
|
||||
bun run dev
|
||||
|
||||
# Production server
|
||||
bun run start
|
||||
|
||||
# MCP server (for Claude Code integration)
|
||||
bun run mcp
|
||||
|
||||
# Type checking
|
||||
bun run typecheck
|
||||
|
||||
# Build for production
|
||||
bun run build
|
||||
|
||||
# Test API clients
|
||||
bun run src/test-clients.ts
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
ai-stack-deployer/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ │ ├── dokploy.ts # Dokploy API client
|
||||
│ │ └── hetzner.ts # Hetzner DNS client (testing only)
|
||||
│ ├── frontend/
|
||||
│ │ ├── index.html # Web UI
|
||||
│ │ ├── style.css # Styles
|
||||
│ │ └── app.js # Frontend logic
|
||||
│ ├── index.ts # HTTP server (production)
|
||||
│ ├── mcp-server.ts # MCP server (development)
|
||||
│ └── test-clients.ts # API client tests
|
||||
├── Dockerfile # Multi-stage Docker build
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
├── .dockerignore # Docker build exclusions
|
||||
├── CLAUDE.md # Claude Code guidance
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
### MCP Server (Development Tool)
|
||||
|
||||
The MCP server provides Claude Code integration for deployment automation:
|
||||
|
||||
```bash
|
||||
# Start MCP server
|
||||
bun run mcp
|
||||
```
|
||||
|
||||
Available MCP tools:
|
||||
- `deploy_stack` - Deploy a new AI stack
|
||||
- `check_deployment_status` - Query deployment progress
|
||||
- `list_deployments` - List all deployments
|
||||
- `check_name_availability` - Validate stack name
|
||||
- `test_api_connections` - Test Dokploy connectivity
|
||||
|
||||
## Infrastructure Requirements
|
||||
|
||||
### Pre-configured Components
|
||||
|
||||
- **Wildcard DNS**: `*.ai.flexinit.nl` → `144.76.116.169`
|
||||
- **Traefik**: Wildcard SSL certificate for `*.ai.flexinit.nl`
|
||||
- **Dokploy**: Running at `http://10.100.0.20:3000`
|
||||
- **OpenCode Image**: `git.app.flexinit.nl/oussamadouhou/oh-my-opencode-free:latest`
|
||||
|
||||
### Network Access
|
||||
|
||||
The deployer must be able to reach:
|
||||
- Dokploy API at `10.100.0.20:3000` (internal network)
|
||||
- No public internet access required for deployment
|
||||
- Frontend users connect via Traefik at `144.76.116.169`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Health Check Failing
|
||||
|
||||
```bash
|
||||
# Check if server is running
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Check Docker logs
|
||||
docker-compose logs -f ai-stack-deployer
|
||||
|
||||
# Restart container
|
||||
docker-compose restart
|
||||
```
|
||||
|
||||
### Deployment Stuck
|
||||
|
||||
1. Check Dokploy API connectivity:
|
||||
```bash
|
||||
curl -H "x-api-key: YOUR_TOKEN" http://10.100.0.20:3000/api/project.all
|
||||
```
|
||||
|
||||
2. View deployment logs in browser console (F12)
|
||||
|
||||
3. Check server logs for errors:
|
||||
```bash
|
||||
docker-compose logs -f | grep ERROR
|
||||
```
|
||||
|
||||
### Name Already Taken
|
||||
|
||||
If a deployment fails but the name is marked as taken:
|
||||
1. Check Dokploy UI for the project `ai-stack-{name}`
|
||||
2. Delete the partial deployment if present
|
||||
3. Try deployment again
|
||||
|
||||
## Security Notes
|
||||
|
||||
- All API tokens stored in environment variables (never in code)
|
||||
- Dokploy API accessible only on internal network
|
||||
- No authentication on HTTP endpoints (add if exposing publicly)
|
||||
- Name validation prevents injection attacks
|
||||
- Container runs as non-root user (nodejs:1001)
|
||||
|
||||
## Performance
|
||||
|
||||
- **Deployment Time**: ~2-3 minutes per stack
|
||||
- **Concurrent Deployments**: No limit (background processing)
|
||||
- **Memory Usage**: ~50MB base + ~10MB per active deployment
|
||||
- **State Persistence**: In-memory only (implement database for persistence)
|
||||
|
||||
## Contributing
|
||||
|
||||
See `CLAUDE.md` for development guidelines and architecture documentation.
|
||||
|
||||
## License
|
||||
|
||||
[Your License Here]
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Check the troubleshooting section above
|
||||
- Review logs: `docker-compose logs -f`
|
||||
- Verify environment variables in `.env`
|
||||
|
||||
---
|
||||
|
||||
**Built with ❤️ using Bun, Hono, and Dokploy**
|
||||
Reference in New Issue
Block a user