Compare commits
10 Commits
ae0c04a4f3
...
a270632e87
| Author | SHA1 | Date | |
|---|---|---|---|
| a270632e87 | |||
| f76559356a | |||
| be6a81ea67 | |||
| 699f7b7128 | |||
| 0486f4881f | |||
| 01ea36bce9 | |||
| 9973b79fa3 | |||
| 525fb23849 | |||
| 5f2d60cb06 | |||
| 9215f3db69 |
@@ -11,3 +11,13 @@ spa/node_modules/
|
|||||||
spa/dist/
|
spa/dist/
|
||||||
spa/.env
|
spa/.env
|
||||||
spa/.env.local
|
spa/.env.local
|
||||||
|
.sqlx/
|
||||||
|
.superpowers/
|
||||||
|
.worktrees/
|
||||||
|
.claude/
|
||||||
|
.github/
|
||||||
|
.gitea/
|
||||||
|
imgs/
|
||||||
|
screenshots/
|
||||||
|
test_data/
|
||||||
|
*.md
|
||||||
|
|||||||
@@ -22,15 +22,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
# Caches ~/.cargo registry/git and target/, keyed on toolchain + Cargo.lock;
|
||||||
|
# prunes workspace-crate artifacts so the cache holds only compiled deps.
|
||||||
- name: Cache cargo
|
- name: Cache cargo
|
||||||
uses: actions/cache@v4
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
target
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cargo-
|
|
||||||
|
|
||||||
- name: fmt
|
- name: fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@@ -38,8 +33,9 @@ jobs:
|
|||||||
- name: clippy
|
- name: clippy
|
||||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
|
# --workspace covers tui (it is outside default-members).
|
||||||
- name: test
|
- name: test
|
||||||
run: cargo test
|
run: cargo test --workspace
|
||||||
|
|
||||||
- name: test (federation off)
|
- name: test (federation off)
|
||||||
run: cargo test --workspace --no-default-features
|
run: cargo test --workspace --no-default-features
|
||||||
@@ -47,4 +43,4 @@ jobs:
|
|||||||
# Layering guards. These live in the Makefile and were previously local-only,
|
# Layering guards. These live in the Makefile and were previously local-only,
|
||||||
# so a PR could go green in CI while violating them.
|
# so a PR could go green in CI while violating them.
|
||||||
- name: guards
|
- name: guards
|
||||||
run: make check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-dockerfile-manifests
|
run: make check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-federation-off-deps check-nats-isolated check-no-second-web-stack
|
||||||
|
|||||||
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
@@ -22,15 +22,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
# Caches ~/.cargo registry/git and target/, keyed on toolchain + Cargo.lock;
|
||||||
|
# prunes workspace-crate artifacts so the cache holds only compiled deps.
|
||||||
- name: Cache cargo
|
- name: Cache cargo
|
||||||
uses: actions/cache@v4
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
target
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
restore-keys: ${{ runner.os }}-cargo-
|
|
||||||
|
|
||||||
- name: fmt
|
- name: fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@@ -38,8 +33,9 @@ jobs:
|
|||||||
- name: clippy
|
- name: clippy
|
||||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
|
# --workspace covers tui (it is outside default-members).
|
||||||
- name: test
|
- name: test
|
||||||
run: cargo test
|
run: cargo test --workspace
|
||||||
|
|
||||||
- name: test (federation off)
|
- name: test (federation off)
|
||||||
run: cargo test --workspace --no-default-features
|
run: cargo test --workspace --no-default-features
|
||||||
@@ -47,7 +43,7 @@ jobs:
|
|||||||
# Layering guards. These live in the Makefile and were previously local-only,
|
# Layering guards. These live in the Makefile and were previously local-only,
|
||||||
# so a PR could go green in CI while violating them.
|
# so a PR could go green in CI while violating them.
|
||||||
- name: guards
|
- name: guards
|
||||||
run: make check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-dockerfile-manifests
|
run: make check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-federation-off-deps check-nats-isolated check-no-second-web-stack
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
name: Build & Push Docker Image
|
name: Build & Push Docker Image
|
||||||
@@ -61,6 +57,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -87,3 +86,5 @@ jobs:
|
|||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-args: FEATURES=federation
|
build-args: FEATURES=federation
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ cd spa && bun install && bun run dev
|
|||||||
make check # the full gate; plain `make` runs it too
|
make check # the full gate; plain `make` runs it too
|
||||||
```
|
```
|
||||||
|
|
||||||
`make check` runs, in order: `fmt-check`, `clippy` (workspace, all targets, `-D warnings`), `test`, `test-federation-off` (workspace tests with default features off), then the layering guards (`check-appcontext`, `check-handler-deps`, `check-handler-repos`, `check-http-adapter-deps`, `check-federation-off-social`, `check-sqlite-is-ap-free`, `check-binaries-are-thin`, `check-dockerfile-manifests`). CI runs the same steps.
|
`make check` runs, in order: `fmt-check`, `clippy` (workspace, all targets, `-D warnings`), `test`, `test-federation-off` (workspace tests with default features off), then the layering guards (`check-appcontext`, `check-handler-deps`, `check-handler-repos`, `check-http-adapter-deps`, `check-federation-off-social`, `check-sqlite-is-ap-free`, `check-binaries-are-thin`, `check-federation-off-deps`, `check-nats-isolated`, `check-no-second-web-stack`). CI runs the same steps.
|
||||||
|
|
||||||
For the SPA also run:
|
For the SPA also run:
|
||||||
|
|
||||||
@@ -57,6 +57,8 @@ spa/ → React SPA (TanStack Router + shadcn/ui)
|
|||||||
|
|
||||||
Cargo features: `federation` (default; ActivityPub) and `nats`. `sqlite` and `sqlite-federation` remain as deprecated aliases so old build commands keep working.
|
Cargo features: `federation` (default; ActivityPub) and `nats`. `sqlite` and `sqlite-federation` remain as deprecated aliases so old build commands keep working.
|
||||||
|
|
||||||
|
**Dependencies.** Every external crate is declared once in the root `[workspace.dependencies]`, usually with `default-features = false`; member crates write `foo = { workspace = true, features = [...] }` and enable only the features they use. A new crate goes into both `members` and `default-members` in the root `Cargo.toml` (only `tui` is left out of `default-members`). `cargo machete` should stay clean, and each crate must compile on its own (`cargo check -p <crate> --all-targets`), not just through feature unification in the binaries.
|
||||||
|
|
||||||
### Adding a new feature
|
### Adding a new feature
|
||||||
|
|
||||||
1. **Domain first** — models in `domain/src/models/`, ports in `domain/src/ports/`, events in `domain/src/events.rs`
|
1. **Domain first** — models in `domain/src/models/`, ports in `domain/src/ports/`, events in `domain/src/events.rs`
|
||||||
|
|||||||
154
Cargo.lock
generated
154
Cargo.lock
generated
@@ -757,7 +757,7 @@ dependencies = [
|
|||||||
"object-storage",
|
"object-storage",
|
||||||
"poster-fetcher",
|
"poster-fetcher",
|
||||||
"reqwest 0.13.3",
|
"reqwest 0.13.3",
|
||||||
"rss 0.1.0",
|
"rss-adapter",
|
||||||
"rustls",
|
"rustls",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
@@ -2022,11 +2022,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 5.3.0",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2036,11 +2034,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 6.0.0",
|
"r-efi 6.0.0",
|
||||||
"rand_core 0.10.1",
|
"rand_core 0.10.1",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasip3",
|
"wasip3",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2220,7 +2220,6 @@ dependencies = [
|
|||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"utoipa-scalar",
|
|
||||||
"utoipa-swagger-ui",
|
"utoipa-swagger-ui",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@@ -2932,9 +2931,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru-slab"
|
name = "lru-slab"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac"
|
name = "mac"
|
||||||
@@ -3053,6 +3052,7 @@ dependencies = [
|
|||||||
"config",
|
"config",
|
||||||
"domain",
|
"domain",
|
||||||
"reqwest 0.13.3",
|
"reqwest 0.13.3",
|
||||||
|
"rustls",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3749,6 +3749,7 @@ dependencies = [
|
|||||||
"config",
|
"config",
|
||||||
"domain",
|
"domain",
|
||||||
"reqwest 0.13.3",
|
"reqwest 0.13.3",
|
||||||
|
"rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3862,9 +3863,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quinn"
|
name = "quinn"
|
||||||
version = "0.11.9"
|
version = "0.11.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
@@ -3882,14 +3883,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quinn-proto"
|
name = "quinn-proto"
|
||||||
version = "0.11.14"
|
version = "0.11.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.4.2",
|
||||||
"lru-slab",
|
"lru-slab",
|
||||||
"rand 0.9.4",
|
"rand 0.10.1",
|
||||||
|
"rand_pcg",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -3903,16 +3905,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quinn-udp"
|
name = "quinn-udp"
|
||||||
version = "0.5.14"
|
version = "0.5.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4012,6 +4014,15 @@ version = "0.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_pcg"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ratatui"
|
name = "ratatui"
|
||||||
version = "0.30.0"
|
version = "0.30.0"
|
||||||
@@ -4343,15 +4354,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rss"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"domain",
|
|
||||||
"rss 2.0.13",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rss"
|
name = "rss"
|
||||||
version = "2.0.13"
|
version = "2.0.13"
|
||||||
@@ -4363,6 +4365,15 @@ dependencies = [
|
|||||||
"quick-xml 0.39.4",
|
"quick-xml 0.39.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rss-adapter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"domain",
|
||||||
|
"rss",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.11.0"
|
version = "8.11.0"
|
||||||
@@ -5456,9 +5467,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
@@ -5530,6 +5541,7 @@ dependencies = [
|
|||||||
"config",
|
"config",
|
||||||
"domain",
|
"domain",
|
||||||
"reqwest 0.13.3",
|
"reqwest 0.13.3",
|
||||||
|
"rustls",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6005,18 +6017,6 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utoipa-scalar"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59559e1509172f6b26c1cdbc7247c4ddd1ac6560fe94b584f81ee489b141f719"
|
|
||||||
dependencies = [
|
|
||||||
"axum",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"utoipa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utoipa-swagger-ui"
|
name = "utoipa-swagger-ui"
|
||||||
version = "9.0.2"
|
version = "9.0.2"
|
||||||
@@ -6437,7 +6437,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6547,15 +6547,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.60.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.53.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
@@ -6589,30 +6580,13 @@ dependencies = [
|
|||||||
"windows_aarch64_gnullvm 0.52.6",
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
"windows_aarch64_msvc 0.52.6",
|
"windows_aarch64_msvc 0.52.6",
|
||||||
"windows_i686_gnu 0.52.6",
|
"windows_i686_gnu 0.52.6",
|
||||||
"windows_i686_gnullvm 0.52.6",
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc 0.52.6",
|
"windows_i686_msvc 0.52.6",
|
||||||
"windows_x86_64_gnu 0.52.6",
|
"windows_x86_64_gnu 0.52.6",
|
||||||
"windows_x86_64_gnullvm 0.52.6",
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
"windows_x86_64_msvc 0.52.6",
|
"windows_x86_64_msvc 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.53.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
"windows_aarch64_gnullvm 0.53.1",
|
|
||||||
"windows_aarch64_msvc 0.53.1",
|
|
||||||
"windows_i686_gnu 0.53.1",
|
|
||||||
"windows_i686_gnullvm 0.53.1",
|
|
||||||
"windows_i686_msvc 0.53.1",
|
|
||||||
"windows_x86_64_gnu 0.53.1",
|
|
||||||
"windows_x86_64_gnullvm 0.53.1",
|
|
||||||
"windows_x86_64_msvc 0.53.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6625,12 +6599,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6643,12 +6611,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6661,24 +6623,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6691,12 +6641,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6709,12 +6653,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6727,12 +6665,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -6745,12 +6677,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.53.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.15"
|
version = "0.7.15"
|
||||||
|
|||||||
76
Cargo.toml
76
Cargo.toml
@@ -1,11 +1,37 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
default-members = [
|
||||||
|
"crates/adapters/auth",
|
||||||
|
"crates/adapters/event-publisher",
|
||||||
|
"crates/adapters/metadata",
|
||||||
|
"crates/adapters/poster-fetcher",
|
||||||
|
"crates/adapters/object-storage",
|
||||||
|
"crates/adapters/rss-adapter",
|
||||||
|
"crates/adapters/sqlite",
|
||||||
|
"crates/adapters/sqlite-federation",
|
||||||
|
"crates/adapters/activitypub",
|
||||||
|
"crates/adapters/export",
|
||||||
|
"crates/adapters/nats",
|
||||||
|
"crates/adapters/tmdb-enrichment",
|
||||||
|
"crates/adapters/image-converter",
|
||||||
|
"crates/adapters/http-axum",
|
||||||
|
"crates/adapters/importer",
|
||||||
|
"crates/adapters/media-server",
|
||||||
|
"crates/event-payload",
|
||||||
|
"crates/api-types",
|
||||||
|
"crates/application",
|
||||||
|
"crates/domain",
|
||||||
|
"crates/bootstrap",
|
||||||
|
"crates/server",
|
||||||
|
"crates/worker",
|
||||||
|
"crates/config",
|
||||||
|
]
|
||||||
members = [
|
members = [
|
||||||
"crates/adapters/auth",
|
"crates/adapters/auth",
|
||||||
"crates/adapters/event-publisher",
|
"crates/adapters/event-publisher",
|
||||||
"crates/adapters/metadata",
|
"crates/adapters/metadata",
|
||||||
"crates/adapters/poster-fetcher",
|
"crates/adapters/poster-fetcher",
|
||||||
"crates/adapters/object-storage",
|
"crates/adapters/object-storage",
|
||||||
"crates/adapters/rss",
|
"crates/adapters/rss-adapter",
|
||||||
"crates/adapters/sqlite",
|
"crates/adapters/sqlite",
|
||||||
"crates/adapters/sqlite-federation",
|
"crates/adapters/sqlite-federation",
|
||||||
"crates/adapters/activitypub",
|
"crates/adapters/activitypub",
|
||||||
@@ -32,20 +58,20 @@ resolver = "2"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
tokio = { version = "1.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] }
|
tokio = { version = "1.0", default-features = false }
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", default-features = false, features = ["std"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.23", default-features = false }
|
||||||
async-trait = "0.1.92"
|
async-trait = "0.1.92"
|
||||||
uuid = { version = "1.23.0", features = ["v4", "v5", "serde"] }
|
uuid = { version = "1.23.0", default-features = false, features = ["std"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
chrono-tz = "0.10"
|
chrono-tz = "0.10"
|
||||||
sqlx = { version = "0.8.6", default-features = false, features = [
|
sqlx = { version = "0.8.6", default-features = false, features = [
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
@@ -59,10 +85,38 @@ tempfile = "3"
|
|||||||
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "charset", "http2", "system-proxy", "rustls-no-provider"] }
|
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "charset", "http2", "system-proxy", "rustls-no-provider"] }
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
object_store = { version = "0.11", features = ["aws"] }
|
object_store = { version = "0.11", default-features = false, features = ["aws"] }
|
||||||
axum = { version = "0.8.8", features = ["macros", "multipart"] }
|
axum = { version = "0.8.8", default-features = false }
|
||||||
csv = "1"
|
csv = "1"
|
||||||
figment = { version = "0.10", features = ["toml"] }
|
figment = { version = "0.10", features = ["toml"] }
|
||||||
|
rustls = { version = "0.23", default-features = false }
|
||||||
|
tower = { version = "0.5", default-features = false }
|
||||||
|
tower-http = { version = "0.6.8", default-features = false }
|
||||||
|
utoipa = "5.5.0"
|
||||||
|
utoipa-swagger-ui = "9.0.2"
|
||||||
|
askama = "0.16.0"
|
||||||
|
infer = "0.19.0"
|
||||||
|
percent-encoding = "2"
|
||||||
|
axum-governor = "2"
|
||||||
|
email_address = "0.2.9"
|
||||||
|
jsonwebtoken = "9"
|
||||||
|
argon2 = { version = "0.5", default-features = false }
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
async-nats = "0.48.0"
|
||||||
|
k-ap = { version = "0.5.1", registry = "gitea" }
|
||||||
|
url = "2"
|
||||||
|
ammonia = "4"
|
||||||
|
calamine = "0.35"
|
||||||
|
image = { version = "0.25", default-features = false }
|
||||||
|
ravif = { version = "0.11", default-features = false }
|
||||||
|
webp = "0.3"
|
||||||
|
rss = { version = "2" }
|
||||||
|
ratatui = "0.30.0"
|
||||||
|
keyring-core = "1.0.0"
|
||||||
|
apple-native-keyring-store = { version = "1.0.0", features = ["keychain"] }
|
||||||
|
zbus-secret-service-keyring-store = { version = "1.0.0" }
|
||||||
|
windows-native-keyring-store = { version = "1.0.0" }
|
||||||
|
directories = "6"
|
||||||
|
|
||||||
api-types = { path = "crates/api-types" }
|
api-types = { path = "crates/api-types" }
|
||||||
domain = { path = "crates/domain" }
|
domain = { path = "crates/domain" }
|
||||||
@@ -75,7 +129,7 @@ metadata = { path = "crates/adapters/metadata" }
|
|||||||
poster-fetcher = { path = "crates/adapters/poster-fetcher" }
|
poster-fetcher = { path = "crates/adapters/poster-fetcher" }
|
||||||
object-storage = { path = "crates/adapters/object-storage" }
|
object-storage = { path = "crates/adapters/object-storage" }
|
||||||
event-publisher = { path = "crates/adapters/event-publisher" }
|
event-publisher = { path = "crates/adapters/event-publisher" }
|
||||||
rss = { path = "crates/adapters/rss" }
|
rss-adapter = { path = "crates/adapters/rss-adapter" }
|
||||||
export = { path = "crates/adapters/export" }
|
export = { path = "crates/adapters/export" }
|
||||||
sqlite = { path = "crates/adapters/sqlite" }
|
sqlite = { path = "crates/adapters/sqlite" }
|
||||||
sqlite-federation = { path = "crates/adapters/sqlite-federation" }
|
sqlite-federation = { path = "crates/adapters/sqlite-federation" }
|
||||||
@@ -92,3 +146,7 @@ debug = 1 # line tables only — still debuggable, much faste
|
|||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 2 # compile deps faster at runtime; paid once, cached after
|
opt-level = 2 # compile deps faster at runtime; paid once, cached after
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
strip = true
|
||||||
|
|||||||
77
Dockerfile
77
Dockerfile
@@ -6,67 +6,46 @@ RUN npm ci
|
|||||||
COPY spa/ .
|
COPY spa/ .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# ----- build -----
|
# ----- chef base -----
|
||||||
FROM rust:slim-bookworm AS builder
|
FROM rust:slim-bookworm AS chef
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Cache dependency compilation separately from source
|
|
||||||
#
|
|
||||||
# Every workspace member's Cargo.toml must be listed here by hand — `cargo fetch`
|
|
||||||
# below reads the whole workspace graph, so a missing manifest fails the build with
|
|
||||||
# "failed to read .../Cargo.toml". Adding a crate to crates/ means adding a line here.
|
|
||||||
COPY Cargo.toml Cargo.lock ./
|
|
||||||
COPY .cargo ./.cargo
|
|
||||||
COPY crates/adapters/activitypub/Cargo.toml crates/adapters/activitypub/Cargo.toml
|
|
||||||
COPY crates/adapters/auth/Cargo.toml crates/adapters/auth/Cargo.toml
|
|
||||||
COPY crates/event-payload/Cargo.toml crates/event-payload/Cargo.toml
|
|
||||||
COPY crates/adapters/event-publisher/Cargo.toml crates/adapters/event-publisher/Cargo.toml
|
|
||||||
COPY crates/adapters/nats/Cargo.toml crates/adapters/nats/Cargo.toml
|
|
||||||
COPY crates/adapters/metadata/Cargo.toml crates/adapters/metadata/Cargo.toml
|
|
||||||
COPY crates/adapters/poster-fetcher/Cargo.toml crates/adapters/poster-fetcher/Cargo.toml
|
|
||||||
COPY crates/adapters/object-storage/Cargo.toml crates/adapters/object-storage/Cargo.toml
|
|
||||||
COPY crates/adapters/export/Cargo.toml crates/adapters/export/Cargo.toml
|
|
||||||
COPY crates/adapters/importer/Cargo.toml crates/adapters/importer/Cargo.toml
|
|
||||||
COPY crates/adapters/media-server/Cargo.toml crates/adapters/media-server/Cargo.toml
|
|
||||||
COPY crates/adapters/rss/Cargo.toml crates/adapters/rss/Cargo.toml
|
|
||||||
COPY crates/adapters/sqlite/Cargo.toml crates/adapters/sqlite/Cargo.toml
|
|
||||||
COPY crates/adapters/sqlite-federation/Cargo.toml crates/adapters/sqlite-federation/Cargo.toml
|
|
||||||
COPY crates/api-types/Cargo.toml crates/api-types/Cargo.toml
|
|
||||||
COPY crates/application/Cargo.toml crates/application/Cargo.toml
|
|
||||||
COPY crates/adapters/tmdb-enrichment/Cargo.toml crates/adapters/tmdb-enrichment/Cargo.toml
|
|
||||||
COPY crates/domain/Cargo.toml crates/domain/Cargo.toml
|
|
||||||
COPY crates/bootstrap/Cargo.toml crates/bootstrap/Cargo.toml
|
|
||||||
COPY crates/adapters/http-axum/Cargo.toml crates/adapters/http-axum/Cargo.toml
|
|
||||||
COPY crates/server/Cargo.toml crates/server/Cargo.toml
|
|
||||||
COPY crates/tui/Cargo.toml crates/tui/Cargo.toml
|
|
||||||
COPY crates/adapters/image-converter/Cargo.toml crates/adapters/image-converter/Cargo.toml
|
|
||||||
COPY crates/worker/Cargo.toml crates/worker/Cargo.toml
|
|
||||||
COPY crates/config/Cargo.toml crates/config/Cargo.toml
|
|
||||||
|
|
||||||
# Stub every crate so cargo can resolve and fetch deps
|
|
||||||
RUN find crates -name "Cargo.toml" | sed 's|/Cargo.toml||' | \
|
|
||||||
xargs -I{} sh -c 'mkdir -p {}/src && echo "fn main(){}" > {}/src/main.rs && echo "" > {}/src/lib.rs'
|
|
||||||
|
|
||||||
# libwebp-dev: required at build time by the `webp` crate (C bindings)
|
# libwebp-dev: required at build time by the `webp` crate (C bindings)
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
libwebp-dev \
|
libwebp-dev \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN cargo install cargo-chef --locked --version 0.1.78
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
RUN cargo fetch
|
# ----- planner: dependency recipe (changes only when manifests/lockfile change) -----
|
||||||
|
FROM chef AS planner
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
# Now copy real sources (invalidates cache only on source changes)
|
# ----- build -----
|
||||||
COPY crates ./crates
|
FROM chef AS builder
|
||||||
|
|
||||||
# All sqlx queries use the runtime API (no query! macros), so no database
|
|
||||||
# or .sqlx cache is needed at compile time.
|
|
||||||
#
|
|
||||||
# Features: federation (ActivityPub), nats (EVENT_BUS_BACKEND=nats).
|
# Features: federation (ActivityPub), nats (EVENT_BUS_BACKEND=nats).
|
||||||
# Build without federation: --build-arg FEATURES=
|
# Build without federation: --build-arg FEATURES=
|
||||||
# To add NATS support: --build-arg FEATURES=federation,nats
|
# To add NATS support: --build-arg FEATURES=federation,nats
|
||||||
|
# Declared before cook so cook and build compile identical feature sets.
|
||||||
ARG FEATURES=federation
|
ARG FEATURES=federation
|
||||||
RUN cargo build --release -p server -p worker --no-default-features --features "${FEATURES}"
|
|
||||||
|
# .cargo carries the registry config needed to fetch private crates
|
||||||
|
COPY .cargo ./.cargo
|
||||||
|
COPY --from=planner /build/recipe.json recipe.json
|
||||||
|
# target/ is a normal layer (no cache mount) so the cooked deps live in the image
|
||||||
|
# layer cache; only the registry/git download caches are mounted.
|
||||||
|
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
|
||||||
|
cargo chef cook --release -p server -p worker --no-default-features --features "${FEATURES}" --recipe-path recipe.json
|
||||||
|
|
||||||
|
# All sqlx queries use the runtime API (no query! macros), so no database
|
||||||
|
# or .sqlx cache is needed at compile time.
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates ./crates
|
||||||
|
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
|
||||||
|
cargo build --release -p server -p worker --no-default-features --features "${FEATURES}"
|
||||||
|
|
||||||
# ----- runtime -----
|
# ----- runtime -----
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|||||||
52
Makefile
52
Makefile
@@ -1,6 +1,6 @@
|
|||||||
.DEFAULT_GOAL := check
|
.DEFAULT_GOAL := check
|
||||||
|
|
||||||
check: fmt-check clippy test test-federation-off check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-dockerfile-manifests
|
check: fmt-check clippy test test-federation-off check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-federation-off-deps check-nats-isolated check-no-second-web-stack
|
||||||
@echo "✅ All checks passed"
|
@echo "✅ All checks passed"
|
||||||
|
|
||||||
check-appcontext:
|
check-appcontext:
|
||||||
@@ -87,21 +87,43 @@ check-sqlite-is-ap-free:
|
|||||||
fi
|
fi
|
||||||
@echo "✅ sqlite crate depends on no k-ap, activitypub, or *-federation crate"
|
@echo "✅ sqlite crate depends on no k-ap, activitypub, or *-federation crate"
|
||||||
|
|
||||||
check-dockerfile-manifests:
|
CARGO_TREE_NAMES := cargo tree -p server -p worker -e normal --prefix none
|
||||||
@members=$$(awk '/^members = \[/{f=1;next} f&&/^\]/{f=0} f' Cargo.toml | sed -n 's/^[[:space:]]*"\(.*\)",\{0,1\}[[:space:]]*$$/\1/p'); \
|
|
||||||
copied=$$(sed -n 's/^COPY \([^[:space:]]*\)\/Cargo\.toml[[:space:]].*/\1/p' Dockerfile | grep -v '^\(Cargo\.toml\|\.\)$$' || true); \
|
check-federation-off-deps:
|
||||||
bad=""; \
|
@tree=$$($(CARGO_TREE_NAMES) --no-default-features) || exit 1; \
|
||||||
for m in $$members; do \
|
hits=$$(echo "$$tree" | grep -E '^(k-ap|activitypub_federation|activitypub|sqlite-federation) ' | sort -u); \
|
||||||
echo "$$copied" | grep -qxF "$$m" || bad="$$bad missing-COPY:$$m"; \
|
if [ -n "$$hits" ]; then \
|
||||||
done; \
|
echo "❌ federation-only crates in the --no-default-features server/worker tree:"; \
|
||||||
for c in $$copied; do \
|
echo "$$hits"; \
|
||||||
echo "$$members" | grep -qxF "$$c" || bad="$$bad not-a-member:$$c"; \
|
|
||||||
done; \
|
|
||||||
if [ -n "$$bad" ]; then \
|
|
||||||
echo "❌ Dockerfile manifest COPY lines out of sync with workspace members:$$bad"; \
|
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi; \
|
fi; \
|
||||||
echo "✅ Dockerfile copies a manifest for every workspace member"
|
echo "✅ federation-off tree has no k-ap, activitypub_federation, activitypub, or sqlite-federation"
|
||||||
|
|
||||||
|
check-nats-isolated:
|
||||||
|
@tree=$$($(CARGO_TREE_NAMES)) || exit 1; \
|
||||||
|
nats_tree=$$($(CARGO_TREE_NAMES) --features nats) || exit 1; \
|
||||||
|
if echo "$$tree" | grep -qE '^(async-nats|nats) '; then \
|
||||||
|
echo "❌ nats crates in the default server/worker tree:"; \
|
||||||
|
echo "$$tree" | grep -E '^(async-nats|nats) ' | sort -u; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
if ! echo "$$nats_tree" | grep -qE '^async-nats '; then \
|
||||||
|
echo "❌ async-nats missing from the --features nats server/worker tree — feature is not wired"; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
echo "✅ async-nats only appears with --features nats"
|
||||||
|
|
||||||
|
check-no-second-web-stack:
|
||||||
|
@for f in "" "--features nats"; do \
|
||||||
|
tree=$$($(CARGO_TREE_NAMES) $$f) || exit 1; \
|
||||||
|
hits=$$(echo "$$tree" | grep -E '^(actix|aws-lc)' | sort -u); \
|
||||||
|
if [ -n "$$hits" ]; then \
|
||||||
|
echo "❌ actix/aws-lc crates in the server/worker tree ($${f:-default}):"; \
|
||||||
|
echo "$$hits"; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
done; \
|
||||||
|
echo "✅ no actix-* or aws-lc-* in the default or nats server/worker tree"
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
cargo fmt
|
cargo fmt
|
||||||
@@ -145,4 +167,4 @@ dev-spa:
|
|||||||
spa-dev:
|
spa-dev:
|
||||||
@npm --prefix spa run dev
|
@npm --prefix spa run dev
|
||||||
|
|
||||||
.PHONY: check fmt fmt-check clippy test test-federation-off fix dev dev-stop dev-worker dev-spa spa-dev check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-dockerfile-manifests
|
.PHONY: check fmt fmt-check clippy test test-federation-off fix dev dev-stop dev-worker dev-spa spa-dev check-appcontext check-handler-deps check-handler-repos check-http-adapter-deps check-federation-off-social check-sqlite-is-ap-free check-binaries-are-thin check-federation-off-deps check-nats-isolated check-no-second-web-stack
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -68,7 +68,7 @@ Open `http://localhost:3000`. The HTTP server and background worker start togeth
|
|||||||
- CSV and JSON diary export
|
- CSV and JSON diary export
|
||||||
- File importer: upload CSV, TSV, JSON, or XLSX from any source (Letterboxd, IMDb, etc.), map columns to domain fields via a step-by-step wizard or REST API, save mapping profiles for repeat imports
|
- File importer: upload CSV, TSV, JSON, or XLSX from any source (Letterboxd, IMDb, etc.), map columns to domain fields via a step-by-step wizard or REST API, save mapping profiles for repeat imports
|
||||||
- REST API v1 (`/api/v1/`) with full feature parity with the HTML interface
|
- REST API v1 (`/api/v1/`) with full feature parity with the HTML interface
|
||||||
- OpenAPI documentation at `/docs` (Swagger UI) and `/scalar` (Scalar)
|
- OpenAPI documentation at `/docs` (Swagger UI)
|
||||||
- CSRF protection on all HTML form routes (double-submit cookie, defense-in-depth on top of `SameSite=Strict`)
|
- CSRF protection on all HTML form routes (double-submit cookie, defense-in-depth on top of `SameSite=Strict`)
|
||||||
- Per-IP rate limiting via token bucket (production-grade, backed by `axum-governor`)
|
- Per-IP rate limiting via token bucket (production-grade, backed by `axum-governor`)
|
||||||
- Single-page app at `/app/` — React + TanStack Router + shadcn/ui, built with Vite, served from the backend with client-side routing fallback
|
- Single-page app at `/app/` — React + TanStack Router + shadcn/ui, built with Vite, served from the backend with client-side routing fallback
|
||||||
@@ -100,7 +100,7 @@ application — use cases (commands + queries), business logic orchestra
|
|||||||
server — thin HTTP binary (dotenv, config, bootstrap::build_server, axum::serve); features (federation, nats) forward to bootstrap
|
server — thin HTTP binary (dotenv, config, bootstrap::build_server, axum::serve); features (federation, nats) forward to bootstrap
|
||||||
worker — thin worker binary (bootstrap::build_worker, run until ctrl-c)
|
worker — thin worker binary (bootstrap::build_worker, run until ctrl-c)
|
||||||
adapters/
|
adapters/
|
||||||
http-axum — Axum HTTP adapter: JSON API, classic HTML UI (askama templates), OpenAPI spec, Swagger UI + Scalar, federation-gated routes; library crate, no binary of its own
|
http-axum — Axum HTTP adapter: JSON API, classic HTML UI (askama templates), OpenAPI spec, Swagger UI, federation-gated routes; library crate, no binary of its own
|
||||||
auth — JWT issuance and validation (Argon2 passwords)
|
auth — JWT issuance and validation (Argon2 passwords)
|
||||||
sqlite — SQLite repositories (one module per aggregate under repositories/), search (FTS5), social graph, durable event queue, connection factory + migrations; the only storage adapter
|
sqlite — SQLite repositories (one module per aggregate under repositories/), search (FTS5), social graph, durable event queue, connection factory + migrations; the only storage adapter
|
||||||
metadata — TMDB / OMDb HTTP client
|
metadata — TMDB / OMDb HTTP client
|
||||||
@@ -174,6 +174,13 @@ Copy `.env.example` to `.env` and set the values you need. Malformed values (a n
|
|||||||
|
|
||||||
Build without federation with `--no-default-features`. `sqlite` and `sqlite-federation` are deprecated aliases kept so old build commands still work.
|
Build without federation with `--no-default-features`. `sqlite` and `sqlite-federation` are deprecated aliases kept so old build commands still work.
|
||||||
|
|
||||||
|
### Build notes
|
||||||
|
|
||||||
|
- `cargo build` / `cargo test` build every crate except the deprecated `tui`, which sits outside `default-members`. Use `-p tui` for it; `make check` and CI use `--workspace`, so it is still linted and tested.
|
||||||
|
- Release builds use `codegen-units = 1` and `strip = true` (smaller binaries; thin LTO was measured and made builds slower and binaries larger, so it is off).
|
||||||
|
- SQLite is compiled from source (bundled `libsqlite3-sys`), so the first clean build spends about a minute on it; incremental builds and the Docker/CI caches don't repeat that.
|
||||||
|
- TLS uses rustls with the `ring` provider only; `aws-lc` and `actix` are kept out of the graph by `make check-no-second-web-stack`.
|
||||||
|
|
||||||
The `worker` binary must run alongside `server` to process events:
|
The `worker` binary must run alongside `server` to process events:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -198,7 +205,6 @@ All REST endpoints are under `/api/v1/`. Authentication uses `Authorization: Bea
|
|||||||
Interactive API documentation is available at runtime:
|
Interactive API documentation is available at runtime:
|
||||||
|
|
||||||
- **Swagger UI** — `http://localhost:3000/docs`
|
- **Swagger UI** — `http://localhost:3000/docs`
|
||||||
- **Scalar** — `http://localhost:3000/scalar`
|
|
||||||
|
|
||||||
An [Insomnia](https://insomnia.rest/) collection covering all endpoints is included at [`movies-diary.insomnia.json`](movies-diary.insomnia.json). Import it via **File → Import**, set `base_url` and `token` in the environment, and you're ready to go.
|
An [Insomnia](https://insomnia.rest/) collection covering all endpoints is included at [`movies-diary.insomnia.json`](movies-diary.insomnia.json). Import it via **File → Import**, set `base_url` and `token` in the environment, and you're ready to go.
|
||||||
|
|
||||||
@@ -273,7 +279,7 @@ docker run -p 3000:3000 --env-file .env -v movies-diary-data:/data movies-diary
|
|||||||
docker run --env-file .env -v movies-diary-data:/data --entrypoint ./worker movies-diary
|
docker run --env-file .env -v movies-diary-data:/data --entrypoint ./worker movies-diary
|
||||||
```
|
```
|
||||||
|
|
||||||
Default build enables `federation`; add NATS with `--build-arg FEATURES=federation,nats`. SQLite is the only database backend (see [ADR-0012](docs/adr/0012-sqlite-is-the-only-backend.md)).
|
Default build enables `federation`; add NATS with `--build-arg FEATURES=federation,nats`. The Dockerfile uses [cargo-chef](https://github.com/LukeMathWalker/cargo-chef): dependencies are compiled in their own layer keyed on the manifests, so a source-only change rebuilds just the workspace crates. BuildKit cache mounts keep the cargo registry between builds. No Dockerfile edit is needed when a crate is added. SQLite is the only database backend (see [ADR-0012](docs/adr/0012-sqlite-is-the-only-backend.md)).
|
||||||
|
|
||||||
## Media Server Integration
|
## Media Server Integration
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,22 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
k-ap = { version = "0.5.1", registry = "gitea" }
|
k-ap = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4", "serde"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock", "serde"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
url = { version = "2", features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
ammonia = "4"
|
ammonia = { workspace = true }
|
||||||
|
|
||||||
|
tokio = { workspace = true, features = ["time", "sync"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ config = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
jsonwebtoken = "9"
|
jsonwebtoken = { workspace = true }
|
||||||
argon2 = { version = "0.5", features = ["std"] }
|
argon2 = { workspace = true, features = ["std"] }
|
||||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
rand_core = { workspace = true, features = ["getrandom"] }
|
||||||
|
|||||||
@@ -7,5 +7,8 @@ edition.workspace = true
|
|||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["sync"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ async-stream = { workspace = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
|
|||||||
@@ -11,31 +11,30 @@ default = ["federation"]
|
|||||||
federation = ["application/federation"]
|
federation = ["application/federation"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tower-http = { version = "0.6.8", features = ["cors", "fs", "trace", "tracing"] }
|
tower-http = { workspace = true, features = ["cors", "fs", "trace", "tracing"] }
|
||||||
infer = "0.19.0"
|
infer = { workspace = true }
|
||||||
percent-encoding = "2"
|
percent-encoding = { workspace = true }
|
||||||
axum-governor = "2"
|
axum-governor = { workspace = true }
|
||||||
|
|
||||||
axum = { workspace = true }
|
axum = { workspace = true, features = ["macros", "multipart", "http1", "json", "query", "form", "tokio", "matched-path", "original-uri", "tower-log", "tracing"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tokio = { workspace = true }
|
uuid = { workspace = true, features = ["v4", "serde"] }
|
||||||
uuid = { workspace = true }
|
chrono = { workspace = true, features = ["clock", "serde"] }
|
||||||
chrono = { workspace = true }
|
|
||||||
chrono-tz = { workspace = true }
|
chrono-tz = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|
||||||
api-types = { workspace = true }
|
api-types = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
application = { workspace = true }
|
application = { workspace = true }
|
||||||
askama = { version = "0.16.0" }
|
askama = { workspace = true }
|
||||||
utoipa = { version = "5.5.0", features = ["axum_extras", "uuid"] }
|
utoipa = { workspace = true, features = ["axum_extras", "uuid"] }
|
||||||
utoipa-scalar = { version = "0.3.0", features = ["axum"], default-features = false }
|
utoipa-swagger-ui = { workspace = true, features = ["axum", "vendored"] }
|
||||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum", "vendored"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tower = { version = "0.5", features = ["util"] }
|
tower = { workspace = true, features = ["util"] }
|
||||||
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use utoipa::{
|
|||||||
Modify, OpenApi,
|
Modify, OpenApi,
|
||||||
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
|
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
|
||||||
};
|
};
|
||||||
use utoipa_scalar::{Scalar, Servable};
|
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
struct SecurityAddon;
|
struct SecurityAddon;
|
||||||
@@ -62,9 +61,7 @@ pub fn spec() -> utoipa::openapi::OpenApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve(router: Router) -> Router {
|
pub fn serve(router: Router) -> Router {
|
||||||
tracing::info!("API docs at /docs (Swagger) and /scalar");
|
tracing::info!("API docs at /docs (Swagger)");
|
||||||
let spec = spec();
|
let spec = spec();
|
||||||
router
|
router.merge(SwaggerUi::new("/docs").url("/openapi.json", spec))
|
||||||
.merge(SwaggerUi::new("/docs").url("/openapi.json", spec.clone()))
|
|
||||||
.merge(Scalar::with_url("/scalar", spec))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ domain = { workspace = true }
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt"] }
|
||||||
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] }
|
image = { workspace = true, features = ["jpeg", "png", "webp"] }
|
||||||
ravif = { version = "0.11", default-features = false }
|
ravif = { workspace = true }
|
||||||
webp = "0.3"
|
webp = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
object-storage = { workspace = true }
|
object-storage = { workspace = true }
|
||||||
object_store = { workspace = true }
|
object_store = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ xlsx = ["dep:calamine"]
|
|||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
csv = { workspace = true }
|
csv = { workspace = true }
|
||||||
calamine = { version = "0.35", optional = true }
|
calamine = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ config = { workspace = true }
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
serde = { workspace = true }
|
rustls = { workspace = true, features = ["ring", "std"] }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub(crate) struct OmdbProvider {
|
|||||||
|
|
||||||
impl OmdbProvider {
|
impl OmdbProvider {
|
||||||
pub(crate) fn new(api_key: String) -> Self {
|
pub(crate) fn new(api_key: String) -> Self {
|
||||||
|
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
Self {
|
Self {
|
||||||
client: reqwest::Client::new(),
|
client: reqwest::Client::new(),
|
||||||
api_key,
|
api_key,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ pub(crate) struct TmdbProvider {
|
|||||||
|
|
||||||
impl TmdbProvider {
|
impl TmdbProvider {
|
||||||
pub(crate) fn new(api_key: String) -> Self {
|
pub(crate) fn new(api_key: String) -> Self {
|
||||||
|
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
Self {
|
Self {
|
||||||
client: reqwest::Client::new(),
|
client: reqwest::Client::new(),
|
||||||
api_key,
|
api_key,
|
||||||
|
|||||||
6
crates/adapters/metadata/tests/client_tls_omdb.rs
Normal file
6
crates/adapters/metadata/tests/client_tls_omdb.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//! Fresh test binary, no bootstrap: constructing the client must not panic
|
||||||
|
//! for lack of a rustls CryptoProvider.
|
||||||
|
#[test]
|
||||||
|
fn omdb_client_builds_without_bootstrap() {
|
||||||
|
let _ = metadata::MetadataClientImpl::new_omdb("key".into());
|
||||||
|
}
|
||||||
6
crates/adapters/metadata/tests/client_tls_tmdb.rs
Normal file
6
crates/adapters/metadata/tests/client_tls_tmdb.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//! Fresh test binary, no bootstrap: constructing the client must not panic
|
||||||
|
//! for lack of a rustls CryptoProvider.
|
||||||
|
#[test]
|
||||||
|
fn tmdb_client_builds_without_bootstrap() {
|
||||||
|
let _ = metadata::MetadataClientImpl::new_tmdb("key".into());
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
async-nats = "0.48.0"
|
async-nats = { workspace = true }
|
||||||
|
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
event-payload = { workspace = true }
|
event-payload = { workspace = true }
|
||||||
@@ -13,7 +13,7 @@ async-trait = { workspace = true }
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["serde"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "sync"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|||||||
@@ -14,5 +14,5 @@ futures = { workspace = true }
|
|||||||
object_store = { workspace = true }
|
object_store = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ config = { workspace = true }
|
|||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
rustls = { workspace = true, features = ["ring", "std"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub struct ReqwestPosterFetcher {
|
|||||||
|
|
||||||
impl ReqwestPosterFetcher {
|
impl ReqwestPosterFetcher {
|
||||||
pub fn new(config: PosterFetcherConfig) -> anyhow::Result<Self> {
|
pub fn new(config: PosterFetcherConfig) -> anyhow::Result<Self> {
|
||||||
|
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
let client = reqwest::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
.timeout(Duration::from_secs(config.timeout_seconds))
|
.timeout(Duration::from_secs(config.timeout_seconds))
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|||||||
8
crates/adapters/poster-fetcher/tests/client_tls.rs
Normal file
8
crates/adapters/poster-fetcher/tests/client_tls.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//! Fresh test binary, no bootstrap: constructing the client must not panic
|
||||||
|
//! for lack of a rustls CryptoProvider.
|
||||||
|
use poster_fetcher::{PosterFetcherConfig, ReqwestPosterFetcher};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn poster_fetcher_builds_without_bootstrap() {
|
||||||
|
ReqwestPosterFetcher::new(PosterFetcherConfig { timeout_seconds: 5 }).expect("client builds");
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rss"
|
name = "rss-adapter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rss-feed = { package = "rss", version = "2", features = ["atom"] }
|
rss = { workspace = true, features = ["atom"] }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use domain::errors::DomainError;
|
use domain::errors::DomainError;
|
||||||
use domain::models::DiaryEntry;
|
use domain::models::DiaryEntry;
|
||||||
use domain::ports::{FeedChannel, RssFeedRenderer};
|
use domain::ports::{FeedChannel, RssFeedRenderer};
|
||||||
use rss_feed::extension::atom::{self, AtomExtensionBuilder, Link};
|
use rss::extension::atom::{self, AtomExtensionBuilder, Link};
|
||||||
use rss_feed::{ChannelBuilder, GuidBuilder, ItemBuilder};
|
use rss::{ChannelBuilder, GuidBuilder, ItemBuilder};
|
||||||
|
|
||||||
pub struct RssAdapter {
|
pub struct RssAdapter {
|
||||||
base_url: String,
|
base_url: String,
|
||||||
@@ -3,7 +3,7 @@ use domain::models::ReviewEdit;
|
|||||||
use domain::ports::{FeedChannel, RssFeedRenderer};
|
use domain::ports::{FeedChannel, RssFeedRenderer};
|
||||||
use domain::testing::{test_movie_with_year, test_review};
|
use domain::testing::{test_movie_with_year, test_review};
|
||||||
use domain::value_objects::{Comment, UserId};
|
use domain::value_objects::{Comment, UserId};
|
||||||
use rss::RssAdapter;
|
use rss_adapter::RssAdapter;
|
||||||
|
|
||||||
fn channel<'a>(title: &'a str) -> FeedChannel<'a> {
|
fn channel<'a>(title: &'a str) -> FeedChannel<'a> {
|
||||||
FeedChannel {
|
FeedChannel {
|
||||||
@@ -6,17 +6,17 @@ edition.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
activitypub = { workspace = true }
|
activitypub = { workspace = true }
|
||||||
k-ap = { version = "0.5.1", registry = "gitea" }
|
k-ap = { workspace = true }
|
||||||
sqlite = { workspace = true }
|
sqlite = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4", "serde"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock", "serde"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ event-payload = { workspace = true }
|
|||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4", "serde"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock", "serde"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "sync", "time", "macros", "rt-multi-thread"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
async-stream = { workspace = true }
|
async-stream = { workspace = true }
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition.workspace = true
|
|||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
serde = { workspace = true }
|
rustls = { workspace = true, features = ["ring", "std"] }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ impl TmdbEnrichmentClient {
|
|||||||
.tmdb_api_key
|
.tmdb_api_key
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or_else(|| DomainError::InfrastructureError("TMDB_API_KEY is not set".into()))?;
|
.ok_or_else(|| DomainError::InfrastructureError("TMDB_API_KEY is not set".into()))?;
|
||||||
|
// reqwest is built with rustls-no-provider; install ring so this works without bootstrap.
|
||||||
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
api_key,
|
api_key,
|
||||||
http: reqwest::Client::new(),
|
http: reqwest::Client::new(),
|
||||||
|
|||||||
12
crates/adapters/tmdb-enrichment/tests/client_tls.rs
Normal file
12
crates/adapters/tmdb-enrichment/tests/client_tls.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//! Fresh test binary, no bootstrap: constructing the client must not panic
|
||||||
|
//! for lack of a rustls CryptoProvider.
|
||||||
|
use tmdb_enrichment::TmdbEnrichmentClient;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tmdb_enrichment_builds_without_bootstrap() {
|
||||||
|
let cfg = config::MetadataConfig {
|
||||||
|
tmdb_api_key: Some("key".into()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
TmdbEnrichmentClient::from_config(&cfg).expect("client builds");
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@ version = "0.1.0"
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
utoipa = { version = "5.5.0", features = ["axum_extras", "uuid"] }
|
utoipa = { workspace = true, features = ["axum_extras", "uuid"] }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ edition.workspace = true
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "sync", "time", "macros"] }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
@@ -22,5 +22,5 @@ xlsx = []
|
|||||||
federation = []
|
federation = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt-multi-thread"] }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
|
|||||||
@@ -26,18 +26,18 @@ tmdb-enrichment = { workspace = true }
|
|||||||
image-converter = { workspace = true }
|
image-converter = { workspace = true }
|
||||||
export = { workspace = true }
|
export = { workspace = true }
|
||||||
importer = { workspace = true }
|
importer = { workspace = true }
|
||||||
rss = { workspace = true }
|
rss-adapter = { workspace = true }
|
||||||
media-server = { workspace = true }
|
media-server = { workspace = true }
|
||||||
sqlite = { workspace = true }
|
sqlite = { workspace = true }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros", "sync", "time", "net"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "ansi", "tracing-log", "smallvec"] }
|
||||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12", "logging"] }
|
rustls = { workspace = true, features = ["ring", "std", "tls12", "logging"] }
|
||||||
|
|
||||||
nats = { workspace = true, optional = true }
|
nats = { workspace = true, optional = true }
|
||||||
activitypub = { workspace = true, optional = true }
|
activitypub = { workspace = true, optional = true }
|
||||||
@@ -45,8 +45,8 @@ sqlite-federation = { workspace = true, optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tower = { version = "0.5", features = ["util"] }
|
tower = { workspace = true, features = ["util"] }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true, features = ["test-helpers"] }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use export::ExportAdapter;
|
|||||||
use http_axum::context::AppContext;
|
use http_axum::context::AppContext;
|
||||||
use http_axum::{openapi, router, state::AppState};
|
use http_axum::{openapi, router, state::AppState};
|
||||||
use importer::ImporterDocumentParser;
|
use importer::ImporterDocumentParser;
|
||||||
use rss::RssAdapter;
|
use rss_adapter::RssAdapter;
|
||||||
|
|
||||||
use crate::database::{self, DatabaseOutput};
|
use crate::database::{self, DatabaseOutput};
|
||||||
use crate::{Repositories, event_bus, federation};
|
use crate::{Repositories, event_bus, federation};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ pub fn install_crypto_provider() {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Runs alone in the lib test binary, so no other code (k-ap included)
|
/// Only test in this crate's lib test binary, so nothing else in this
|
||||||
/// has installed a provider first.
|
/// process installs a provider before `install_crypto_provider` runs.
|
||||||
#[test]
|
#[test]
|
||||||
fn https_client_builds_after_install() {
|
fn https_client_builds_after_install() {
|
||||||
install_crypto_provider();
|
install_crypto_provider();
|
||||||
|
|||||||
@@ -252,7 +252,6 @@ async fn feeds_and_openapi_are_served() {
|
|||||||
("/openapi.json", 200),
|
("/openapi.json", 200),
|
||||||
("/docs", 303),
|
("/docs", 303),
|
||||||
("/docs/", 200),
|
("/docs/", 200),
|
||||||
("/scalar", 200),
|
|
||||||
] {
|
] {
|
||||||
let uri = expand(&seed, template);
|
let uri = expand(&seed, template);
|
||||||
let (status, _, _) = send(&seed.router, get(&uri, None)).await;
|
let (status, _, _) = send(&seed.router, get(&uri, None)).await;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
serde.workspace = true
|
serde = { workspace = true, features = ["derive"] }
|
||||||
figment.workspace = true
|
figment.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ version = "0.1.0"
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["v4", "v5", "serde"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["clock", "serde"] }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
email_address = "0.2.9"
|
email_address = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
domain = { path = ".", features = ["test-helpers"] }
|
domain = { path = ".", features = ["test-helpers"] }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-helpers = []
|
test-helpers = []
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
domain = { workspace = true }
|
domain = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["serde"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ sqlite-federation = ["federation"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bootstrap = { workspace = true }
|
bootstrap = { workspace = true }
|
||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true, features = ["http1", "tokio"] }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "net"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|||||||
@@ -13,24 +13,22 @@ linux-zbus = [
|
|||||||
windows = ["dep:windows-native-keyring-store"]
|
windows = ["dep:windows-native-keyring-store"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ratatui = "0.30.0"
|
ratatui = { workspace = true }
|
||||||
keyring-core = "1.0.0"
|
keyring-core = { workspace = true }
|
||||||
directories = "6"
|
directories = { workspace = true }
|
||||||
csv = { workspace = true }
|
csv = { workspace = true }
|
||||||
|
|
||||||
apple-native-keyring-store = { version = "1.0.0", optional = true, features = [
|
apple-native-keyring-store = { workspace = true, optional = true }
|
||||||
"keychain",
|
zbus-secret-service-keyring-store = { workspace = true, optional = true }
|
||||||
] }
|
windows-native-keyring-store = { workspace = true, optional = true }
|
||||||
zbus-secret-service-keyring-store = { version = "1.0.0", optional = true }
|
|
||||||
windows-native-keyring-store = { version = "1.0.0", optional = true }
|
|
||||||
|
|
||||||
api-types = { workspace = true }
|
api-types = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12", "logging"] }
|
rustls = { workspace = true, features = ["ring", "std", "tls12", "logging"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["rt", "rt-multi-thread", "sync", "macros", "time"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true, features = ["serde"] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ sqlite-federation = ["federation"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bootstrap = { workspace = true }
|
bootstrap = { workspace = true }
|
||||||
config = { workspace = true }
|
config = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["signal"] }
|
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal", "sync"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|||||||
Reference in New Issue
Block a user