refactor: migrate monolithic crate to Cargo Workspace with Clean Architecture

Transform the single binary crate into a 9-crate workspace monorepo:

- Root Cargo.toml as [workspace] manager with resolver = "2"
- zesdex-entities: Domain entity types (session, settings, store, message, etc.)
- zesdex-utils: Pure utility functions (error, logger, pagination, slug, clipboard)
- zesdex-dto: Data Transfer Objects for LLM provider API communication
- zesdex-ipc: Unix-socket IPC layer (client/server/framing/protocol)
- zesdex-iam: Identity & Access Management (Clean Architecture: domain/application/infrastructure)
- zesdex-cms: Content Management (Clean Architecture: domain/application/infrastructure)
- zesdex-middleware: HTTP middleware (Auth, CORS, Rate Limiting)
- zesdex-libs: Composition root (AppContext, DB init, JWT, Argon2)
- zesdex-backend: Main binary entry point + seed/migrate binaries
- DevOps: Dockerfile, docker-compose, Nix (flake/shell/default), CI/CD updates
- Remove dead root src/ and src-misc/ directories

All crate re-exports maintain backward compatibility with original
crate::model::*, crate::dto::*, crate::ipc::* module paths.
Feature crates enforce strict layer separation: domain -> application
-> infrastructure with generic trait-based dependency injection.
This commit is contained in:
asepharyana
2026-07-17 09:08:41 +07:00
parent 86cc412395
commit be0a9582bb
248 changed files with 7901 additions and 1505 deletions
+6 -6
View File
@@ -21,11 +21,11 @@ jobs:
with:
components: clippy
- name: Build
run: cargo build --release
- name: Build workspace
run: cargo build --release --workspace
- name: Test
run: cargo test
- name: Test workspace
run: cargo test --workspace
- name: Clippy
run: cargo clippy -- -D warnings
- name: Clippy workspace
run: cargo clippy --workspace -- -D warnings
+2 -2
View File
@@ -20,10 +20,10 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build
run: cargo build --release
run: cargo build --release --workspace
- name: Test
run: cargo test
run: cargo test --workspace
release:
name: Semantic Release
Generated
+342 -14
View File
@@ -47,6 +47,18 @@ dependencies = [
"num-traits",
]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures 0.2.17",
"password-hash",
]
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -114,12 +126,82 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "axum"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
dependencies = [
"axum-core",
"axum-macros",
"bytes",
"form_urlencoded",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-macros"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "bincode"
version = "1.3.3"
@@ -171,6 +253,15 @@ version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest 0.10.7",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -191,9 +282,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.12.3"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
dependencies = [
"memchr",
"regex-automata",
@@ -620,6 +711,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"crypto-common 0.1.7",
"subtle",
]
[[package]]
@@ -1096,9 +1188,9 @@ checksum = "43503cc176394dd30a6525f5f36e838339b8b5619be33ed9a7783841580a97b6"
[[package]]
name = "globset"
version = "0.4.18"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
dependencies = [
"aho-corasick",
"bstr",
@@ -1224,6 +1316,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hybrid-array"
version = "0.4.13"
@@ -1247,6 +1345,7 @@ dependencies = [
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
@@ -1445,9 +1544,9 @@ dependencies = [
[[package]]
name = "ignore"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adf14691c72bcfc1058740436a35bdd3ae9c07d1a941ef00b749e9ea16aefa7"
checksum = "d4ffa3a0547a138e59ddd6fa3b7c672ed47e6ad6a3cd177984ff1116aa5ba742"
dependencies = [
"crossbeam-deque",
"globset",
@@ -1610,6 +1709,21 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonwebtoken"
version = "9.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
dependencies = [
"base64",
"js-sys",
"pem",
"ring",
"serde",
"serde_json",
"simple_asn1",
]
[[package]]
name = "kasuari"
version = "0.4.12"
@@ -1790,6 +1904,12 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.8.3"
@@ -1937,6 +2057,16 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "num-bigint"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.2"
@@ -1954,6 +2084,15 @@ dependencies = [
"syn 2.0.118",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -2118,12 +2257,33 @@ dependencies = [
"windows-link",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "pastey"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
"base64",
"serde_core",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -2475,6 +2635,9 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "rand_core"
@@ -2614,9 +2777,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
@@ -2626,9 +2789,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.15"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
dependencies = [
"aho-corasick",
"memchr",
@@ -3030,6 +3193,17 @@ dependencies = [
"zmij",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [
"itoa",
"serde",
"serde_core",
]
[[package]]
name = "serde_repr"
version = "0.1.20"
@@ -3151,9 +3325,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
version = "0.3.9"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "simd_cesu8"
@@ -3180,6 +3354,18 @@ dependencies = [
"bstr",
]
[[package]]
name = "simple_asn1"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
dependencies = [
"num-bigint",
"num-traits",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "siphasher"
version = "1.0.3"
@@ -3689,6 +3875,7 @@ dependencies = [
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@@ -3702,6 +3889,7 @@ dependencies = [
"futures-util",
"http",
"http-body",
"http-body-util",
"pin-project-lite",
"tower",
"tower-layer",
@@ -3727,6 +3915,7 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -4481,8 +4670,8 @@ dependencies = [
]
[[package]]
name = "zesdex"
version = "1.14.0"
name = "zesdex-backend"
version = "1.13.0"
dependencies = [
"anyhow",
"base64",
@@ -4521,6 +4710,145 @@ dependencies = [
"url",
"uuid",
"webbrowser",
"zesdex-cms",
"zesdex-dto",
"zesdex-entities",
"zesdex-iam",
"zesdex-ipc",
"zesdex-libs",
"zesdex-middleware",
"zesdex-utils",
]
[[package]]
name = "zesdex-cms"
version = "1.13.0"
dependencies = [
"anyhow",
"chrono",
"dirs",
"serde",
"serde_json",
"tracing",
"uuid",
"zesdex-entities",
"zesdex-utils",
]
[[package]]
name = "zesdex-dto"
version = "1.13.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
"tracing",
"zesdex-entities",
]
[[package]]
name = "zesdex-entities"
version = "1.13.0"
dependencies = [
"anyhow",
"base64",
"chrono",
"dirs",
"libc",
"reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
"tracing",
"url",
"uuid",
]
[[package]]
name = "zesdex-iam"
version = "1.13.0"
dependencies = [
"anyhow",
"base64",
"chrono",
"libc",
"reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
"tracing",
"url",
"uuid",
"zesdex-entities",
"zesdex-utils",
]
[[package]]
name = "zesdex-ipc"
version = "1.13.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
"tracing",
"zesdex-dto",
"zesdex-entities",
]
[[package]]
name = "zesdex-libs"
version = "1.13.0"
dependencies = [
"anyhow",
"argon2",
"axum",
"chrono",
"jsonwebtoken",
"rand_core 0.6.4",
"rusqlite",
"serde",
"serde_json",
"tokio",
"tracing",
"uuid",
"zesdex-cms",
"zesdex-entities",
"zesdex-iam",
"zesdex-middleware",
"zesdex-utils",
]
[[package]]
name = "zesdex-middleware"
version = "1.13.0"
dependencies = [
"anyhow",
"axum",
"chrono",
"serde",
"serde_json",
"tower",
"tower-http",
"zesdex-entities",
"zesdex-utils",
]
[[package]]
name = "zesdex-utils"
version = "1.13.0"
dependencies = [
"anyhow",
"base64",
"chrono",
"dirs",
"hex",
"serde",
"serde_json",
"sha2 0.11.0",
"thiserror 1.0.69",
"tracing",
"tracing-subscriber",
]
[[package]]
+53 -35
View File
@@ -1,11 +1,23 @@
[package]
name = "zesdex"
version = "1.14.0"
[workspace]
resolver = "2"
members = [
"crates/zesdex-entities",
"crates/zesdex-utils",
"crates/zesdex-dto",
"crates/zesdex-ipc",
"crates/zesdex-iam",
"crates/zesdex-cms",
"crates/zesdex-middleware",
"crates/zesdex-libs",
"crates/zesdex-backend",
]
[workspace.package]
version = "1.13.0"
edition = "2021"
authors = ["asepharyana <superaseph@gmail.com>"]
# Treat all warnings as errors, set strict clippy levels
[lints.rust]
[workspace.lints.rust]
unused = "deny"
dead_code = "deny"
unreachable_code = "deny"
@@ -17,49 +29,55 @@ deprecated = "deny"
trivial_casts = "deny"
trivial_numeric_casts = "deny"
[lints.clippy]
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -2 }
[dependencies]
ratatui = "0.30.2"
crossterm = "0.29"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "net", "io-util", "signal"] }
reqwest = { version = "0.13", features = ["json", "stream", "blocking", "native-tls-vendored", "form"] }
dom_smoothie = "0.18.0"
fast_html2md = "0.0.62"
scraper = "0.27.0"
url = "2"
percent-encoding = "2"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml_ng = "0.10"
anyhow = "1"
include_dir = "0.7"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4", "v5"] }
dirs = "6"
futures-util = "0.3"
pulldown-cmark = { version = "0.13", default-features = false }
similar = "3"
syntect = { version = "5", default-features = false, features = ["default-fancy"] }
anyhow = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "net", "io-util", "signal"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
reqwest = { version = "0.13", features = ["json", "stream", "blocking", "native-tls-vendored", "form"] }
ratatui = "0.30.2"
crossterm = "0.29"
rusqlite = { version = "0.40", features = ["bundled"] }
ignore = "0.4"
regex = "1"
globset = "0.4"
nucleo-matcher = "0.3"
infer = "0.19"
thiserror = "1"
base64 = "0.22"
sha2 = "0.11"
hex = "0.4"
libc = "0.2"
dirs = "6"
regex = "1"
globset = "0.4"
ignore = "0.4"
nucleo-matcher = "0.3"
futures-util = "0.3"
rmcp = { version = "2.2", default-features = false, features = ["client", "transport-child-process", "transport-streamable-http-client-reqwest", "macros"] }
tracing = "0.1"
chrono = { version = "0.4", features = ["serde"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
webbrowser = "1"
lsp-types = "0.97"
tiktoken-rs = "0.12"
similar = "3"
syntect = { version = "5", default-features = false, features = ["default-fancy"] }
pulldown-cmark = { version = "0.13", default-features = false }
infer = "0.19"
webbrowser = "1"
url = "2"
percent-encoding = "2"
dom_smoothie = "0.18.0"
fast_html2md = "0.0.62"
scraper = "0.27.0"
include_dir = "0.7"
axum = { version = "0.8", features = ["macros"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "limit"] }
argon2 = "0.5"
jsonwebtoken = "9"
[[bin]]
name = "zesdex"
path = "src/main.rs"
zesdex-entities = { path = "crates/zesdex-entities" }
zesdex-utils = { path = "crates/zesdex-utils" }
zesdex-dto = { path = "crates/zesdex-dto" }
+30
View File
@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1
# Zesdex — Multi-stage Docker build
# ===================================
# Stage 1: Build with Rust toolchain
FROM rust:1.85-slim-bookworm AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config libsqlite3-dev && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
# Build with release profile (treats warnings as errors via lints)
RUN cargo build --release -p zesdex-backend --bin zesdex
# Stage 2: Minimal runtime image
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates libsqlite3-0 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/zesdex /usr/local/bin/zesdex
ENV ZESDEX_DATA_DIR=/data
VOLUME ["/data"]
ENTRYPOINT ["/usr/local/bin/zesdex"]
+67
View File
@@ -0,0 +1,67 @@
[package]
name = "zesdex-backend"
version.workspace = true
edition.workspace = true
authors.workspace = true
[dependencies]
# Workspace crates
zesdex-entities = { path = "../zesdex-entities" }
zesdex-utils = { path = "../zesdex-utils" }
zesdex-dto = { path = "../zesdex-dto" }
zesdex-ipc = { path = "../zesdex-ipc" }
zesdex-iam = { path = "../zesdex-iam" }
zesdex-cms = { path = "../zesdex-cms" }
zesdex-middleware = { path = "../zesdex-middleware" }
zesdex-libs = { path = "../zesdex-libs" }
# External deps
serde.workspace = true
serde_json.workspace = true
serde_yaml_ng.workspace = true
chrono.workspace = true
uuid.workspace = true
anyhow.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
reqwest.workspace = true
ratatui.workspace = true
crossterm.workspace = true
rusqlite.workspace = true
base64.workspace = true
sha2.workspace = true
hex.workspace = true
libc.workspace = true
dirs.workspace = true
regex.workspace = true
globset.workspace = true
ignore.workspace = true
nucleo-matcher.workspace = true
futures-util.workspace = true
rmcp.workspace = true
lsp-types.workspace = true
tiktoken-rs.workspace = true
similar.workspace = true
syntect.workspace = true
pulldown-cmark.workspace = true
infer.workspace = true
webbrowser.workspace = true
url.workspace = true
percent-encoding.workspace = true
dom_smoothie.workspace = true
fast_html2md.workspace = true
scraper.workspace = true
include_dir.workspace = true
[[bin]]
name = "zesdex"
path = "src/main.rs"
[[bin]]
name = "seed"
path = "src/bin/seed.rs"
[[bin]]
name = "migrate"
path = "src/bin/migrate.rs"

Some files were not shown because too many files have changed in this diff Show More