S1: Colony backend skeleton — Axum + SQLite, channels + messages CRUD

Monorepo structure:
- crates/colony-types: API types (serde + ts-rs), separate from DB models
- crates/colony: Axum server, SQLite via sqlx, migrations

Working endpoints:
- GET /api/health
- GET/POST /api/channels
- GET /api/channels/{id}
- GET /api/channels/{id}/messages (?since=, ?type=, ?user_id=)
- POST /api/channels/{id}/messages (with type + metadata)

Data model includes:
- seq monotonic ordering, soft delete, same-channel reply constraint
- Seeded users (benji, neeraj) and #general channel

Also: codex-review skill, .gitignore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 18:54:43 +02:00
parent 983221df33
commit e940afde52
11 changed files with 3144 additions and 0 deletions

53
crates/colony/src/main.rs Normal file
View File

@@ -0,0 +1,53 @@
mod db;
mod routes;
use axum::{routing::get, Router};
use sqlx::sqlite::SqlitePoolOptions;
use std::env;
#[tokio::main]
async fn main() {
let db_url = env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite:colony.db?mode=rwc".into());
let port = env::var("PORT").unwrap_or_else(|_| "3001".into());
let pool = SqlitePoolOptions::new()
.max_connections(5)
.connect(&db_url)
.await
.expect("Failed to connect to database");
// Enable WAL mode
sqlx::query("PRAGMA journal_mode=WAL")
.execute(&pool)
.await
.unwrap();
// Run migrations
let migration_sql = include_str!("../migrations/001_init.sql");
for statement in migration_sql.split(';') {
let stmt = statement.trim();
if !stmt.is_empty() {
if let Err(e) = sqlx::query(stmt).execute(&pool).await {
eprintln!("Migration warning (may be OK): {}", e);
}
}
}
println!("Colony running on port {}", port);
let app = Router::new()
.route("/api/health", get(routes::health))
.route("/api/channels", get(routes::list_channels).post(routes::create_channel))
.route("/api/channels/{id}", get(routes::get_channel))
.route(
"/api/channels/{channel_id}/messages",
get(routes::list_messages).post(routes::post_message),
)
.with_state(pool);
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port))
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}