chore: update compose.prod.yml (worker+nats external), CI builds frontend, deprecate thoughts-backend
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Has been cancelled
test / unit (pull_request) Has been cancelled
test / integration (pull_request) Has been cancelled

This commit is contained in:
2026-05-15 01:32:09 +02:00
parent 61c82d77ba
commit e1bb7dde1f
3 changed files with 104 additions and 151 deletions

View File

@@ -7,10 +7,11 @@ on:
env:
REGISTRY: git.gabrielkaszewski.dev
IMAGE: git.gabrielkaszewski.dev/gkaszewski/thoughts
BACKEND_IMAGE: git.gabrielkaszewski.dev/gkaszewski/thoughts
FRONTEND_IMAGE: git.gabrielkaszewski.dev/gkaszewski/thoughts-frontend
jobs:
build-and-push:
build-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -26,25 +27,61 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE }}
images: ${{ env.BACKEND_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
- name: Build and push backend
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ env.IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache,mode=max
cache-from: type=registry,ref=${{ env.BACKEND_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.BACKEND_IMAGE }}:buildcache,mode=max
build-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FRONTEND_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push frontend
uses: docker/build-push-action@v6
with:
context: ./thoughts-frontend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_SERVER_SIDE_API_URL=${{ secrets.NEXT_PUBLIC_SERVER_SIDE_API_URL }}
cache-from: type=registry,ref=${{ env.FRONTEND_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.FRONTEND_IMAGE }}:buildcache,mode=max
deploy:
needs: build-and-push
needs: [build-backend, build-frontend]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
@@ -55,5 +92,6 @@ jobs:
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: |
docker pull ${{ env.IMAGE }}:latest
docker pull ${{ env.BACKEND_IMAGE }}:latest
docker pull ${{ env.FRONTEND_IMAGE }}:latest
docker compose -f /opt/thoughts/docker-compose.yml up -d

View File

@@ -1,6 +1,6 @@
services:
database:
image: postgres:15-alpine
image: postgres:16-alpine
container_name: thoughts-db
restart: unless-stopped
environment:
@@ -17,19 +17,21 @@ services:
networks:
- internal
backend:
container_name: thoughts-backend
image: thoughts-backend:latest
api:
container_name: thoughts-api
image: registry.gabrielkaszewski.dev/thoughts:latest
restart: unless-stopped
environment:
- RUST_LOG=info
- RUST_BACKTRACE=1
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database/${POSTGRES_DB}
- HOST=0.0.0.0
- PORT=8000
- PREFORK=1
- AUTH_SECRET=${AUTH_SECRET}
- BASE_URL=https://thoughts.gabrielkaszewski.dev
RUST_LOG: info
RUST_ENV: production
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database/${POSTGRES_DB}
HOST: 0.0.0.0
PORT: 8000
JWT_SECRET: ${JWT_SECRET}
BASE_URL: ${BASE_URL}
NATS_URL: ${NATS_URL}
CORS_ORIGINS: ${CORS_ORIGINS:-*}
ALLOW_REGISTRATION: ${ALLOW_REGISTRATION:-false}
depends_on:
database:
condition: service_healthy
@@ -40,22 +42,41 @@ services:
retries: 5
networks:
- internal
- nats
worker:
container_name: thoughts-worker
image: registry.gabrielkaszewski.dev/thoughts:latest
entrypoint: ["./thoughts-worker"]
restart: unless-stopped
environment:
RUST_LOG: info
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database/${POSTGRES_DB}
BASE_URL: ${BASE_URL}
NATS_URL: ${NATS_URL}
depends_on:
database:
condition: service_healthy
networks:
- internal
- nats
frontend:
container_name: thoughts-frontend
image: thoughts-frontend:latest
image: registry.gabrielkaszewski.dev/thoughts-frontend:latest
restart: unless-stopped
environment:
NEXT_PUBLIC_SERVER_SIDE_API_URL: http://api:8000
PORT: 3000
HOSTNAME: 0.0.0.0
depends_on:
- backend
api:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 10s
timeout: 5s
retries: 5
environment:
- NEXT_PUBLIC_SERVER_SIDE_API_URL=http://proxy/api
- PORT=3000
- HOSTNAME=0.0.0.0
networks:
- internal
@@ -66,7 +87,7 @@ services:
depends_on:
frontend:
condition: service_healthy
backend:
api:
condition: service_healthy
networks:
- internal
@@ -83,7 +104,13 @@ services:
volumes:
postgres_data:
driver: local
networks:
# Shared NATS network — must already exist on the host (external: true).
# Set NATS_NETWORK env var to match your shared network name (default: nats).
nats:
name: ${NATS_NETWORK:-nats}
external: true
traefik:
name: traefik
external: true

View File

@@ -1,129 +1,17 @@
# clean-axum
# ⚠️ DEPRECATED — thoughts-backend (v1)
Axum scaffold with clean architecture.
> **This directory is the original v1 implementation and is no longer maintained.**
> It will be removed in a future release.
You probably don't need [Rust on Rails](https://github.com/loco-rs/loco).
## Use v2 instead
Refer to [this post](https://kigawas.me/posts/rustacean-clean-architecture-approach/) for rationale and background.
The active codebase lives at the **repository root** (`/crates/`). It is a complete rewrite with:
## Features
- Hexagonal (Ports & Adapters) architecture
- Full ActivityPub federation
- Remote actor discovery and profile browsing
- NATS JetStream event bus
- Clean REST API with content negotiation
- Next.js frontend (`/thoughts-frontend/`)
- [Axum](https://github.com/tokio-rs/axum) framework
- [SeaORM](https://github.com/SeaQL/sea-orm) domain models
- Completely separated API routers and DB-related logic (named "persistence" layer)
- Completely separated input parameters, queries and output schemas
- OpenAPI documentation ([Swagger UI](https://clean-axum.shuttleapp.rs/docs) and [Scalar](https://clean-axum.shuttleapp.rs/scalar)) powered by [Utoipa](https://github.com/juhaku/utoipa)
- Error handling with [Anyhow](https://github.com/dtolnay/anyhow)
- Custom parameter validation with [validator](https://github.com/Keats/validator)
- Optional [Shuttle](https://www.shuttle.rs/) runtime
- Optional [prefork](https://docs.rs/prefork/latest/prefork/) workers for maximizing performance on Linux
## Module hierarchy
### API logic
- `api::routers`: Axum endpoints
- `api::error`: Models and traits for error handling
- `api::extractor` Custom Axum extractors
- `api::extractor::json`: `Json` for bodies and responses
- `api::extractor::valid`: `Valid` for JSON body validation
- `api::validation`: JSON validation model based on `validator`
- `api::models`: Non domain model API models
- `api::models::response`: JSON error response
### OpenAPI documentation
- `doc`: Utoipa doc declaration
### API-agonistic application logic
Main concept: Web framework is replaceable.
All modules here should not include any specific API web framework logic.
- `app::persistence`: DB manipulation (CRUD) functions
- `app::config`: DB or API server configuration
- `app::state`: APP state, e.g. DB connection
- `app::error`: APP errors used by `api::error`. e.g. "User not found"
### DB/API-agnostic domain models
Main concept: Database (Sqlite/MySQL/PostgreSQL) is replaceable.
Except `models::domains` and `migration`, all modules are ORM library agnostic.
- `models::domains`: SeaORM domain models
- `models::params`: Serde input parameters for creating/updating domain models in DB
- `models::schemas`: Serde output schemas for combining different domain models
- `models::queries`: Serde queries for filtering domain models
- `migration`: SeaORM migration files
### Unit and integration tests
- `tests::api`: API integration tests. Hierarchy is the same as `api::routers`
- `tests::app::persistence`: DB/ORM-related unit tests. Hierarchy is the same as `app::persistence`
### Others
- `utils`: Utility functions
- `main`: Tokio and Shuttle conditional entry point
## Run
### Start server
```bash
cp .env.example .env
# touch dev.db
# cargo install sea-orm-cli
# sea-orm-cli migrate up
cargo run
# or for production
cargo run --release
```
### Call API
```bash
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"username":"aaa"}'
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"username":"abc"}'
curl http://localhost:3000/users\?username\=a
```
### OpenAPI doc (Swagger UI/Scalar)
```bash
open http://localhost:3000/docs
open http://localhost:3000/scalar
```
## Start Shuttle local server
```bash
# cargo install cargo-shuttle
cargo shuttle run
```
Make sure docker engine is running, otherwise:
```bash
brew install colima docker
colima start
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
```
## Shuttle deployment
```bash
cargo shuttle login
cargo shuttle deploy
```
## Benchmark
```bash
# edit .env to use Postgres
cargo run --release
wrk --latency -t20 -c50 -d10s http://localhost:3000/users\?username\=
```
Do not build, run, or modify anything in this directory.