app #0: cube.famzheng.me 入口门户 + 平台脚手架
deploy cube / build-and-deploy (push) Has been cancelled
deploy cube / build-and-deploy (push) Has been cancelled
monorepo 第一刀: - workspace + crates/cube-core(base router / healthz / ServeDir SPA fallback / JSON tracing / SIGTERM shutdown) - apps/cube:axum 主程序 + Vite + Vue 3 + TS 门户(暗色调 + 渐变 logo + app 卡片网格) - Dockerfile:scratch + musl 静态二进制,镜像 2.6MB - k8s/:cube-cube ns + Deployment + Service + Ingress(cube.famzheng.me,traefik LE 自动签) - registry:新增 registry.famzheng.me ingress 反代到 gitea 内置 container registry, 自动化身份用 mochi(registry.famzheng.me/mochi/cube) - CI:.gitea/workflows/deploy-cube.yml,host shell runner(gnoc), build → push → kubectl rollout 五步流水 - README:把宪法段改成 monorepo 模式 + monorepo 目录结构 - 新增宪法条款:前端视图状态走 URL(path + query)保证可 bookmark
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "cube-core"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
description = "Shared scaffolding for cube apps: base router, healthz, tracing, graceful shutdown"
|
||||
|
||||
[dependencies]
|
||||
axum = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tower-http = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
@@ -0,0 +1,58 @@
|
||||
//! cube-core: 共享脚手架。所有 cube app 通过这个 crate 拿基础 router、tracing、shutdown。
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::signal;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
/// 拼一个带 healthz + 静态前端 SPA fallback 的基础 router。
|
||||
///
|
||||
/// `dist_dir` 是前端 vite build 输出目录(容器内一般是 `/dist`)。
|
||||
pub fn base(dist_dir: impl AsRef<Path>) -> Router {
|
||||
let dist = dist_dir.as_ref().to_path_buf();
|
||||
let index = dist.join("index.html");
|
||||
let static_svc = ServeDir::new(&dist).fallback(ServeFile::new(index));
|
||||
|
||||
Router::new()
|
||||
.route("/healthz", get(healthz))
|
||||
.fallback_service(static_svc)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
}
|
||||
|
||||
async fn healthz() -> &'static str {
|
||||
"ok"
|
||||
}
|
||||
|
||||
/// 初始化 tracing:JSON to stdout,吃 RUST_LOG env。
|
||||
pub fn init_tracing() {
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
fmt().json().with_env_filter(filter).init();
|
||||
}
|
||||
|
||||
/// 起服务,绑定 0.0.0.0:port,挂 SIGTERM/SIGINT 优雅 shutdown。
|
||||
pub async fn serve(app: Router, port: u16) -> std::io::Result<()> {
|
||||
let addr = format!("0.0.0.0:{port}");
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
tracing::info!(%addr, "cube app listening");
|
||||
axum::serve(listener, app)
|
||||
.with_graceful_shutdown(shutdown_signal())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn shutdown_signal() {
|
||||
let ctrl_c = async { signal::ctrl_c().await.expect("install ctrl_c handler") };
|
||||
let term = async {
|
||||
signal::unix::signal(signal::unix::SignalKind::terminate())
|
||||
.expect("install SIGTERM handler")
|
||||
.recv()
|
||||
.await;
|
||||
};
|
||||
tokio::select! {
|
||||
_ = ctrl_c => tracing::info!("ctrl-c received, shutting down"),
|
||||
_ = term => tracing::info!("SIGTERM received, shutting down"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user