mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
chore: initial foundation — server skeleton, autodocs, CI
- minimal Axum server exposing GET /health - OpenAPI contract generated from handlers (gen_openapi -> openapi.json), drift-gated in CI so the spec can never silently diverge from the code - CI: secret-scan + gitleaks, openapi drift gate, fmt/clippy/build/test/doc-test, cargo-deny - Cargo.lock tracked for reproducible builds; publish=false; pinned toolchain - README, SECURITY.md, deny.toml, .env.example
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
//! Health / liveness endpoint.
|
||||
|
||||
use axum::Json;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Liveness response.
|
||||
#[derive(Serialize, utoipa::ToSchema)]
|
||||
pub struct HealthResponse {
|
||||
/// Always `"ok"` when the service is reachable.
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// Liveness probe.
|
||||
///
|
||||
/// Returns `200` with `{ "status": "ok" }` whenever the server is up. Used by
|
||||
/// load balancers and uptime checks; requires no authentication.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/health",
|
||||
responses((status = 200, description = "Service is up", body = HealthResponse)),
|
||||
tag = "system"
|
||||
)]
|
||||
pub async fn health() -> Json<HealthResponse> {
|
||||
Json(HealthResponse {
|
||||
status: "ok".to_string(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! HTTP API layer: handlers and the OpenAPI assembly.
|
||||
|
||||
pub mod health;
|
||||
pub mod openapi;
|
||||
@@ -0,0 +1,21 @@
|
||||
//! OpenAPI specification assembly.
|
||||
//!
|
||||
//! `ApiDoc` aggregates the annotated handlers into the spec that is the single
|
||||
//! source of truth for the API contract and the wallet's generated TypeScript
|
||||
//! client. Add a path here once its handler carries `#[utoipa::path(...)]` and
|
||||
//! its request/response types derive `utoipa::ToSchema`.
|
||||
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
info(
|
||||
title = "smirk-backend-core",
|
||||
version = "0.3.0",
|
||||
description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet."
|
||||
),
|
||||
paths(crate::api::health::health),
|
||||
components(schemas(crate::api::health::HealthResponse)),
|
||||
tags((name = "system", description = "Service health and metadata."))
|
||||
)]
|
||||
pub struct ApiDoc;
|
||||
@@ -0,0 +1,20 @@
|
||||
//! Emit the OpenAPI 3 specification to stdout.
|
||||
//!
|
||||
//! The spec is derived from the handlers (`utoipa` annotations) and is the
|
||||
//! single source of truth for the API contract and the generated TypeScript
|
||||
//! client. Regenerate the committed contract with:
|
||||
//!
|
||||
//! cargo run --bin gen_openapi > openapi.json
|
||||
//!
|
||||
//! CI regenerates and diffs against the committed `openapi.json`; a drift is a
|
||||
//! failed build, not a stale doc.
|
||||
|
||||
use smirk_backend_core::api::openapi::ApiDoc;
|
||||
use utoipa::OpenApi;
|
||||
|
||||
fn main() {
|
||||
let spec = ApiDoc::openapi()
|
||||
.to_pretty_json()
|
||||
.expect("serialize OpenAPI spec");
|
||||
println!("{spec}");
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
//! smirk-backend-core
|
||||
//!
|
||||
//! Open, self-hostable backend for the Smirk non-custodial multi-chain wallet:
|
||||
//! authentication, per-chain wallet access (Bitcoin, Litecoin, Monero, Wownero,
|
||||
//! Grin), the Grin slatepack relay, and Nostr-based identity.
|
||||
//!
|
||||
//! The HTTP contract is generated from the handlers (`utoipa`) into
|
||||
//! `openapi.json`, which is the single source of truth for the API and the
|
||||
//! wallet's generated TypeScript client.
|
||||
|
||||
pub mod api;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
//! smirk-backend-core server entry point.
|
||||
//!
|
||||
//! Minimal server skeleton. Configuration, application state, the full router,
|
||||
//! and background tasks are added as modules land; for now it serves the
|
||||
//! health endpoint so the build, the OpenAPI contract, and deployment wiring
|
||||
//! are exercised end to end.
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use smirk_backend_core::api::health;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let app = Router::new().route("/health", get(health::health));
|
||||
|
||||
let addr = "0.0.0.0:8080";
|
||||
let listener = tokio::net::TcpListener::bind(addr)
|
||||
.await
|
||||
.expect("bind listener");
|
||||
tracing::info!("smirk-backend-core listening on http://{addr}");
|
||||
axum::serve(listener, app).await.expect("server error");
|
||||
}
|
||||
Reference in New Issue
Block a user