- Generate random 32-byte session secret if not set via env var - Persist to $UPAAS_DATA_DIR/session.key for container restarts - Load existing secret from file on subsequent startups - Change container data directory to /var/lib/upaas
182 lines
5.4 KiB
Markdown
182 lines
5.4 KiB
Markdown
# upaas
|
|
|
|
A simple self-hosted PaaS that auto-deploys Docker containers from Git repositories via Gitea webhooks.
|
|
|
|
## Features
|
|
|
|
- Single admin user with argon2id password hashing
|
|
- Per-app SSH keypairs for read-only deploy keys
|
|
- Per-app UUID-based webhook URLs for Gitea integration
|
|
- Branch filtering - only deploy on configured branch changes
|
|
- Environment variables, labels, and volume mounts per app
|
|
- Docker builds via socket access
|
|
- Notifications via ntfy and Slack-compatible webhooks
|
|
- Simple server-rendered UI with Tailwind CSS
|
|
|
|
## Non-Goals
|
|
|
|
- Multi-user support
|
|
- Complex CI pipelines
|
|
- Multiple container orchestration
|
|
- SPA/API-first design
|
|
- Support for non-Gitea webhooks
|
|
|
|
## Architecture
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
upaas/
|
|
├── cmd/upaasd/ # Application entry point
|
|
├── internal/
|
|
│ ├── config/ # Configuration via Viper
|
|
│ ├── database/ # SQLite database with migrations
|
|
│ ├── docker/ # Docker client for builds/deploys
|
|
│ ├── globals/ # Build-time variables (version, etc.)
|
|
│ ├── handlers/ # HTTP request handlers
|
|
│ ├── healthcheck/ # Health status service
|
|
│ ├── logger/ # Structured logging (slog)
|
|
│ ├── middleware/ # HTTP middleware (auth, logging, CORS)
|
|
│ ├── models/ # Active Record style database models
|
|
│ ├── server/ # HTTP server and routes
|
|
│ ├── service/
|
|
│ │ ├── app/ # App management service
|
|
│ │ ├── auth/ # Authentication service
|
|
│ │ ├── deploy/ # Deployment orchestration
|
|
│ │ ├── notify/ # Notifications (ntfy, Slack)
|
|
│ │ └── webhook/ # Gitea webhook processing
|
|
│ └── ssh/ # SSH key generation
|
|
├── static/ # Embedded CSS/JS assets
|
|
└── templates/ # Embedded HTML templates
|
|
```
|
|
|
|
### Dependency Injection
|
|
|
|
Uses Uber fx for dependency injection. Components are wired in this order:
|
|
|
|
1. `globals` - Build-time variables
|
|
2. `logger` - Structured logging
|
|
3. `config` - Configuration loading
|
|
4. `database` - SQLite connection + migrations
|
|
5. `healthcheck` - Health status
|
|
6. `auth` - Authentication service
|
|
7. `app` - App management
|
|
8. `docker` - Docker client
|
|
9. `notify` - Notification service
|
|
10. `deploy` - Deployment service
|
|
11. `webhook` - Webhook processing
|
|
12. `middleware` - HTTP middleware
|
|
13. `handlers` - HTTP handlers
|
|
14. `server` - HTTP server
|
|
|
|
### Request Flow
|
|
|
|
```
|
|
HTTP Request
|
|
│
|
|
▼
|
|
chi Router ──► Middleware Stack ──► Handler
|
|
│
|
|
(Logging, Auth, CORS, etc.)
|
|
│
|
|
▼
|
|
Handler Function
|
|
│
|
|
▼
|
|
Service Layer (app, auth, deploy, etc.)
|
|
│
|
|
▼
|
|
Models (Active Record)
|
|
│
|
|
▼
|
|
Database
|
|
```
|
|
|
|
### Key Patterns
|
|
|
|
- **Closure-based handlers**: Handlers return `http.HandlerFunc` allowing one-time initialization
|
|
- **Active Record models**: Models encapsulate database operations (`Save()`, `Delete()`, `Reload()`)
|
|
- **Async deployments**: Webhook triggers deploy via goroutine with `context.WithoutCancel()`
|
|
- **Embedded assets**: Templates and static files embedded via `//go:embed`
|
|
|
|
## Development
|
|
|
|
### Prerequisites
|
|
|
|
- Go 1.23+
|
|
- golangci-lint
|
|
- Docker (for running)
|
|
|
|
### Commands
|
|
|
|
```bash
|
|
make fmt # Format code
|
|
make lint # Run comprehensive linting
|
|
make test # Run tests with race detection
|
|
make check # Verify everything passes (lint, test, build, format)
|
|
make build # Build binary
|
|
```
|
|
|
|
### Commit Requirements
|
|
|
|
**All commits must pass `make check` before being committed.**
|
|
|
|
Before every commit:
|
|
|
|
1. **Format**: Run `make fmt` to format all code
|
|
2. **Lint**: Run `make lint` and fix all errors/warnings
|
|
- Do not disable linters or add nolint comments without good reason
|
|
- Fix the code, don't hide the problem
|
|
3. **Test**: Run `make test` and ensure all tests pass
|
|
- Fix failing tests by fixing the code, not by modifying tests to pass
|
|
- Add tests for new functionality
|
|
4. **Verify**: Run `make check` to confirm everything passes
|
|
|
|
```bash
|
|
# Standard workflow before commit:
|
|
make fmt
|
|
make lint # Fix any issues
|
|
make test # Fix any failures
|
|
make check # Final verification
|
|
git add .
|
|
git commit -m "Your message"
|
|
```
|
|
|
|
The Docker build runs `make check` and will fail if:
|
|
- Code is not formatted
|
|
- Linting errors exist
|
|
- Tests fail
|
|
- Code doesn't compile
|
|
|
|
This ensures the main branch always contains clean, tested, working code.
|
|
|
|
## Configuration
|
|
|
|
Environment variables:
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `UPAAS_PORT` | HTTP listen port | 8080 |
|
|
| `UPAAS_DATA_DIR` | Data directory for SQLite and keys | ./data |
|
|
| `UPAAS_DOCKER_HOST` | Docker socket path | unix:///var/run/docker.sock |
|
|
| `DEBUG` | Enable debug logging | false |
|
|
| `SENTRY_DSN` | Sentry error reporting DSN | "" |
|
|
| `METRICS_USERNAME` | Basic auth for /metrics | "" |
|
|
| `METRICS_PASSWORD` | Basic auth for /metrics | "" |
|
|
|
|
## Running with Docker
|
|
|
|
```bash
|
|
docker run -d \
|
|
-p 8080:8080 \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-v upaas-data:/var/lib/upaas \
|
|
upaas
|
|
```
|
|
|
|
Session secrets are automatically generated on first startup and persisted to `$UPAAS_DATA_DIR/session.key`.
|
|
|
|
## License
|
|
|
|
MIT
|