diff --git a/.gitea/workflows/release_docker.yaml b/.gitea/workflows/release_docker.yaml new file mode 100644 index 0000000..8dc1876 --- /dev/null +++ b/.gitea/workflows/release_docker.yaml @@ -0,0 +1,31 @@ +name: release +run-name: release +on: + push: + branches: + - master +jobs: + tests: + uses: ./.gitea/workflows/test.yaml # use the callable tests job to run tests + secrets: inherit + deploy: + name: deploy + runs-on: ubuntu-latest + needs: [tests] # require tests to pass before deploy runs + container: + image: ovlach/docker-gitea:v0.0.2-alpha + steps: + - uses: https://github.com/actions/checkout.git@v4 + - name: Login to git.nanobyte.cz + uses: https://github.com/docker/login-action@v3 + with: + username: ${{ secrets.DEPLOYMENT_PACKAGE }} + password: ${{ secrets.DEPLOYMENT_PACKAGE }} + registry: git.nanobyte.cz + - name: Get short hash from git repository + id: git + run: echo "::set-output name=short_hash::$(git rev-parse --short HEAD)" + - name: Build and push docker + run: | + docker build . -t git.nanobyte.cz/$GITHUB_REPOSITORY_OWNER/ovlach_backend:${{steps.git.outputs.short_hash}} --build-arg GITEA_TOKEN="Bearer ${{secrets.DEPLOYMENT_PACKAGE}}" && \ + docker push git.nanobyte.cz/$GITHUB_REPOSITORY_OWNER/ovlach_backend:${{steps.git.outputs.short_hash}} diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index 4314ab3..b13cc51 100644 --- a/.gitea/workflows/test.yaml +++ b/.gitea/workflows/test.yaml @@ -2,52 +2,66 @@ name: test run-name: test on: workflow_call: + secrets: + DEPLOYMENT_PACKAGE: + description: 'needed for checkout depenediencies' + required: true push: branches: - '*' jobs: clippy: runs-on: ubuntu-latest + container: + image: git.nanobyte.cz/nanobyte/nano-rust-builder:1.74.5f84654 steps: - uses: https://github.com/actions/checkout.git@v4 + - name: Hash of Cargo.lock + id: get-hash + run: echo "::set-output name=hash::$(md5sum Cargo.lock | awk '{ print $1; }')" - uses: https://gitea.com/wolfogre/cache/restore@v3 id: cache with: + key: "${{github.repository}}-cache-cargo-clippy-v4-${{ steps.get-hash.outputs.hash }}" path: | .cache target - key: ${{github.repository}}-cache-cargo-clippy-v4-${{ hashFiles('Cargo.lock') }} restore-keys: ${{github.repository}}-cache-cargo-clippy-v4 - uses: nanobyte-public/rust-action@master with: - args: CARGO_HOME=./.cache cargo clippy -- -Dwarnings + args: CARGO_HOME=./.cache cargo-nan \"Bearer ${{secrets.DEPLOYMENT_PACKAGE}}\" cargo clippy -- -Dwarnings - uses: https://gitea.com/wolfogre/cache/save@v3 if: steps.cache.outputs.cache-hit != 'true' with: path: | .cache target - key: ${{github.repository}}-cache-cargo-clippy-v4-${{ hashFiles('Cargo.lock') }} + key: "${{github.repository}}-cache-cargo-clippy-v4-${{ steps.get-hash.outputs.hash }}" test: + container: + image: git.nanobyte.cz/nanobyte/nano-rust-builder:1.74.5f84654 runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout.git@v4 + - name: Hash of Cargo.lock + id: get-hash + run: echo "::set-output name=hash::$(md5sum Cargo.lock | awk '{ print $1; '})" - uses: https://gitea.com/wolfogre/cache/restore@v3 id: cache with: path: | .cache target - key: ${{github.repository}}-cache-cargo-test-v4-${{ hashFiles('Cargo.lock') }} - restore-keys: ${{github.repository}}-cache-cargo-test-v4 + key: "${{github.repository}}-cache-test-clippy-v4-${{ steps.get-hash.outputs.hash }}" + restore-keys: ${{github.repository}}-cache-test-test-v4 - uses: nanobyte-public/rust-action@master with: args: | - CARGO_HOME=./.cache cargo test + CARGO_HOME=./.cache cargo-nan \"Bearer ${{secrets.DEPLOYMENT_PACKAGE}}\" cargo test - uses: https://gitea.com/wolfogre/cache/save@v3 if: steps.cache.outputs.cache-hit != 'true' with: path: | .cache target - key: ${{github.repository}}-cache-cargo-test-v4-${{ hashFiles('Cargo.lock') }} + key: "${{github.repository}}-cache-test-clippy-v4-${{ steps.get-hash.outputs.hash }}" diff --git a/Cargo.lock b/Cargo.lock index 1945cb4..6ebad2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "async-stream" version = "0.3.5" @@ -60,7 +66,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -71,7 +77,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -95,6 +101,51 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -362,7 +413,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -383,7 +434,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -625,7 +676,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -681,6 +732,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets", +] + [[package]] name = "getrandom" version = "0.2.11" @@ -740,13 +801,19 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.3" @@ -832,6 +899,18 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -894,6 +973,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -901,7 +990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", "serde", ] @@ -967,6 +1056,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1066,6 +1164,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.6.4" @@ -1128,18 +1232,46 @@ dependencies = [ "version_check", ] +[[package]] +name = "nanobyte_opentelemetry" +version = "0.2.3" +source = "sparse+https://git.nanobyte.cz/api/packages/nanobyte/cargo/" +checksum = "053fac4ff7f3cab0a088383bc68774e402c07c92d19478bcbb51d1d49a5d42bf" +dependencies = [ + "gethostname", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry-stdout", + "opentelemetry_sdk", + "reqwest", + "rocket", + "tracing", + "tracing-appender", + "tracing-core", + "tracing-log", + "tracing-opentelemetry", + "tracing-subscriber", + "uuid", + "yansi 0.5.1", +] + [[package]] name = "nanobyte_tera" -version = "0.1.0" -source = "git+https://glpat-Us_EdFTzQLv4shViQXi_:glpat-Us_EdFTzQLv4shViQXi_@gitlab.nanobyte.cz/tools/nanobyte_tera.git?branch=master#75c20a9806663ca04c6f8a7afff64d7b5906d113" +version = "0.2.0" +source = "sparse+https://git.nanobyte.cz/api/packages/nanobyte/cargo/" +checksum = "6fa686074d8273526885446e3c7f4a6f35affab0dfcbba55c9b1aca6efae7c67" dependencies = [ "chrono", "fluent-bundle", "fluent-resmgr", "log", - "ovlach_data", "rocket_dyn_templates", + "serde", + "serde_json", "sha256", + "tracing", "unic-langid", ] @@ -1256,7 +1388,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1277,6 +1409,125 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.1.0", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c4bd073648dae8ac45cfc81588d74b3dc5f334119ac08567ddcbfe16f2d809" +dependencies = [ + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" +dependencies = [ + "async-trait", + "futures-core", + "http", + "opentelemetry", + "opentelemetry-proto", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "prost", + "thiserror", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "opentelemetry-stdout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13b2df4cd59c176099ac82806725ba340c8fa7b1a7004c0912daad30470f63e" +dependencies = [ + "async-trait", + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", + "ordered-float", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968ba3f2ca03e90e5187f5e4f46c791ef7f2c163ae87789c8ce5f5ca3b7b7de5" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "ordered-float", + "percent-encoding", + "rand", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "536900a8093134cf9ccf00a27deb3532421099e958d9dd431135d0c7543ca1e8" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -1285,8 +1536,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "ovlach_data" -version = "0.1.0" -source = "git+https://glpat-Ju_qUN9Yh8qa5rEnd6T7:glpat-Ju_qUN9Yh8qa5rEnd6T7@gitlab.nanobyte.cz/ondrej/ov-site-api-data.git?branch=add_missing_fields#c13748b039d812d3bf1aaa93312699da7b921868" +version = "0.1.2" +source = "sparse+https://git.nanobyte.cz/api/packages/ovlach/cargo/" +checksum = "98843b3cefbdbf054f13312e82493248501cdd8abb39379ca2546559358c1437" dependencies = [ "chrono", "rocket", @@ -1302,6 +1554,7 @@ dependencies = [ "fluent-bundle", "fluent-resmgr", "log", + "nanobyte_opentelemetry", "nanobyte_tera", "ovlach_data", "ovlach_tera", @@ -1310,16 +1563,19 @@ dependencies = [ "rocket", "rocket_dyn_templates", "serde", + "serde_json", "serde_yaml", "sha256", "tokio", + "tracing", "unic-langid", ] [[package]] name = "ovlach_tera" -version = "0.1.0" -source = "git+https://glpat-_yPuXbEzECyk3FaHudCN:glpat-_yPuXbEzECyk3FaHudCN@gitlab.nanobyte.cz/ondrej/ovlach_tera.git?branch=master#0ab1bbadd76c1336e00b2a38e572048003879ab6" +version = "0.2.0" +source = "sparse+https://git.nanobyte.cz/api/packages/ovlach/cargo/" +checksum = "fd6d3a0c415f223c68db7bd0290405b247f488180e6b96810b3be4acd0b37764" dependencies = [ "rocket_dyn_templates", "serde", @@ -1365,7 +1621,7 @@ checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" dependencies = [ "inlinable_string", "pear_codegen", - "yansi", + "yansi 1.0.0-rc.1", ] [[package]] @@ -1377,7 +1633,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1417,7 +1673,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1471,7 +1727,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1483,6 +1739,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1530,9 +1806,32 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "version_check", - "yansi", + "yansi 1.0.0-rc.1", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1609,7 +1908,7 @@ checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1708,7 +2007,7 @@ dependencies = [ "either", "figment", "futures", - "indexmap", + "indexmap 2.1.0", "log", "memchr", "multer", @@ -1729,7 +2028,7 @@ dependencies = [ "tokio-util", "ubyte", "version_check", - "yansi", + "yansi 1.0.0-rc.1", ] [[package]] @@ -1740,11 +2039,11 @@ checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c" dependencies = [ "devise", "glob", - "indexmap", + "indexmap 2.1.0", "proc-macro2", "quote", "rocket_http", - "syn", + "syn 2.0.39", "unicode-xid", "version_check", ] @@ -1773,7 +2072,7 @@ dependencies = [ "futures", "http", "hyper", - "indexmap", + "indexmap 2.1.0", "log", "memchr", "pear", @@ -1911,7 +2210,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1952,7 +2251,7 @@ version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap", + "indexmap 2.1.0", "itoa", "ryu", "serde", @@ -2082,6 +2381,17 @@ dependencies = [ "loom", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.39" @@ -2093,6 +2403,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -2166,7 +2482,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -2251,6 +2567,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.2.0" @@ -2259,7 +2585,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -2324,13 +2650,67 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2348,6 +2728,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" @@ -2356,7 +2748,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -2380,6 +2772,34 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -2390,12 +2810,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -2556,6 +2979,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" @@ -2620,7 +3058,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -2654,7 +3092,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2675,6 +3113,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2809,6 +3257,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yansi" version = "1.0.0-rc.1" diff --git a/Cargo.toml b/Cargo.toml index 28745f3..9137ebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,17 @@ rocket = { version = "0.5.0", features = ["json"] } rocket_dyn_templates = { version = "0.1.0", features = ["tera"]} reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } -ovlach_data = { git = "https://glpat-Ju_qUN9Yh8qa5rEnd6T7:glpat-Ju_qUN9Yh8qa5rEnd6T7@gitlab.nanobyte.cz/ondrej/ov-site-api-data.git", branch = "add_missing_fields"} +ovlach_data = { version = "0.1.2", registry = "gitea_ovlach"} +nanobyte_opentelemetry = { version = "0.2.3", registry = "gitea_nanobyte", features = ["rocket-reqwest"] } chrono = "0.4.31" sha256 = "1.4.0" fluent-bundle = "0.15.2" fluent-resmgr = "0.0.6" unic-langid = "0.9.1" phf = { version = "0.11.2", features = ["macros"] } -nanobyte_tera = { git = "https://glpat-Us_EdFTzQLv4shViQXi_:glpat-Us_EdFTzQLv4shViQXi_@gitlab.nanobyte.cz/tools/nanobyte_tera.git", branch = "master" } -ovlach_tera = { git = "https://glpat-_yPuXbEzECyk3FaHudCN:glpat-_yPuXbEzECyk3FaHudCN@gitlab.nanobyte.cz/ondrej/ovlach_tera.git", branch = "master" } +nanobyte_tera = { version = "0.2.0", registry = "gitea_nanobyte" } +ovlach_tera = { version="0.2.0", registry="gitea_ovlach" } +tracing = "0.1.40" + +[dev-dependencies] +serde_json = "1.0.108" diff --git a/Rocket.toml b/Rocket.toml index e690fcc..749c66e 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -4,4 +4,5 @@ cv_backend_path = "http://localhost:8002" [default] static_route = "http://localhost:8001" -cv_backend_path = "http://localhost:8002" \ No newline at end of file +cv_backend_path = "http://localhost:8002" +default_person_name = "ovlach" diff --git a/src/lib.rs b/src/lib.rs index 1c6aee5..27bbbea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use ovlach_tera::entity::lang_entity; use rocket::{*, fairing::AdHoc}; use rocket_dyn_templates::Template; use ::serde::Deserialize; +use tools::tera::advanced_filter; pub mod routes; pub mod services; @@ -21,13 +22,19 @@ pub struct CVBackendConfig { cv_backend_path: String, } +#[derive(Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct DefaultPerson { + default_person_name: String, +} -pub fn rocket_builder() -> Rocket { + +pub fn rocket_builder() -> Rocket { let rocket = rocket::build(); //let figment = rocket.figment(); // extract the entire config any `Deserialize` value //let config: PresentationConfig = figment.extract().expect("config"); - + rocket.attach( Template::try_custom(|engines| { engines.tera.register_filter("static", static_filter); @@ -39,6 +46,7 @@ pub fn rocket_builder() -> Rocket { engines.tera.register_filter("lang_entity", lang_entity); engines.tera.register_filter("format_date", get_year); // deprecated engines.tera.register_filter("get_year", get_year); + engines.tera.register_filter("advanced_filter", advanced_filter); Ok(()) }) ).attach( @@ -46,9 +54,11 @@ pub fn rocket_builder() -> Rocket { ).attach( AdHoc::config::() ).attach( - tools::rocket::RequestTimer + AdHoc::config::() + ).attach( + nanobyte_opentelemetry::rocket::TracingFairing::ignite() ).mount("/", routes![ routes::root::index, routes::root::index_without_lang ]) -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index a31f213..f218144 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,14 @@ +use nanobyte_opentelemetry::{install_panic_handler, default_filter_layer, LogLevel}; use ovlach_frontend::rocket_builder; -use rocket::launch; - -#[launch] -fn rocket() -> _ { - rocket_builder() -} \ No newline at end of file +#[rocket::main] +async fn main() { + install_panic_handler(); + let _opentelemetry = nanobyte_opentelemetry::init_telemetry( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + &std::env::var("OTLP_ENDPOINT").unwrap_or("".to_string()), + Some(default_filter_layer(LogLevel::DebugWithoutRs)) + ); + let _ = rocket_builder().launch().await; +} diff --git a/src/routes/root.rs b/src/routes/root.rs index 7f9a364..c3dbb08 100644 --- a/src/routes/root.rs +++ b/src/routes/root.rs @@ -1,35 +1,45 @@ +use std::{thread::sleep, time::Duration}; + use log::error; -use ovlach_data::cv::cv::CV; -use rocket::{get, State, response::Redirect, http::Status}; +use nanobyte_opentelemetry::rocket::{OtelReqwestClient, TracingSpan}; +use nanobyte_tera::l18n::LanguageDescription; +use ovlach_data::cv::data::CV; +use rocket::{get, State, response::Redirect, http::Status, futures::executor::enter}; use rocket_dyn_templates::Template; use serde::Serialize; -use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage}; +use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage, DefaultPerson}; #[derive(Serialize, Debug)] struct RootPage { static_host: String, cv: CV, download_cv_url: String, - lang: String, + lang: LanguageDescription, } #[get("/")] -pub async fn index(presentation_config: &State, cv_config: &State, language: RequestLanguage) -> Result { - let context = match fetch_cv_data_from_backend(cv_config.cv_backend_path.clone()).await { +pub async fn index(presentation_config: &State, cv_config: &State, language: RequestLanguage, client: OtelReqwestClient, +default_person: &State, span: TracingSpan) -> Result { + let span = span.0.enter(); + + let context = match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &default_person.default_person_name, &client.0).await { Ok(cv) => RootPage { static_host: presentation_config.static_route.clone(), cv, download_cv_url: "FIXME!".to_string(), - lang: language.language, + lang: LanguageDescription::new(&language.language.as_str(), "ovlach_frontend"), }, Err(e) => { error!("Can't fetch CV data from backend {:?}", e); + drop(span); return Err(Status::InternalServerError) } }; - Ok(Template::render("default", &context)) + let result = Ok(Template::render("default", &context)); + drop(span); + result } diff --git a/src/services/cv.rs b/src/services/cv.rs index ca6c790..3a5a015 100644 --- a/src/services/cv.rs +++ b/src/services/cv.rs @@ -1,4 +1,6 @@ -use ovlach_data::cv::cv::CV; +use ovlach_data::cv::data::CV; +use reqwest::Client; +use tracing::{debug, instrument}; #[derive(Debug)] @@ -12,10 +14,14 @@ impl From for FetchError { } } -pub async fn fetch_cv_data_from_backend(backend_host: String) -> Result { - let resp = reqwest::get(format!("{}/{}", backend_host, "/api/cv/ovlach")) +#[instrument] +pub async fn fetch_cv_data_from_backend(backend_host: &String, person_name: &String, client: &Client) -> Result { + let url = format!("{}/{}/{}", backend_host, "/api/v1/cv", person_name); + debug!("Fetching CV data from backend: {}", url); + let resp = client + .get(url).send() .await? .json::() .await?; Ok(resp) -} \ No newline at end of file +} diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 1d2a2de..1cb158d 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -1,2 +1,3 @@ -pub mod rocket; -pub mod lang; \ No newline at end of file +pub(crate) mod rocket; +pub(crate) mod lang; +pub(crate) mod tera; diff --git a/src/tools/rocket.rs b/src/tools/rocket.rs index 23ef136..edbd873 100644 --- a/src/tools/rocket.rs +++ b/src/tools/rocket.rs @@ -27,57 +27,3 @@ impl<'r> FromParam<'r> for RequestLanguage { } } } - - -/// Fairing for timing requests. -pub struct RequestTimer; - -/// Value stored in request-local state. -#[derive(Copy, Clone)] -struct TimerStart(Option); - -#[rocket::async_trait] -impl Fairing for RequestTimer { - fn info(&self) -> Info { - Info { - name: "Request Timer", - kind: Kind::Request | Kind::Response - } - } - - /// Stores the start time of the request in request-local state. - async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) { - // Store a `TimerStart` instead of directly storing a `SystemTime` - // to ensure that this usage doesn't conflict with anything else - // that might store a `SystemTime` in request-local cache. - request.local_cache(|| TimerStart(Some(SystemTime::now()))); - } - - /// Adds a header to the response indicating how long the server took to - /// process the request. - async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) { - let start_time = req.local_cache(|| TimerStart(None)); - if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) { - let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; - res.set_raw_header("X-Response-Time", format!("{} ms", ms)); - info!("Response time: {} ms", ms); - } - } -} - -/// Request guard used to retrieve the start time of a request. -#[derive(Copy, Clone)] -pub struct StartTime(pub SystemTime); - -// Allows a route to access the time a request was initiated. -#[rocket::async_trait] -impl<'r> FromRequest<'r> for StartTime { - type Error = (); - - async fn from_request(request: &'r Request<'_>) -> request::Outcome { - match *request.local_cache(|| TimerStart(None)) { - TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)), - TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())), - } - } -} \ No newline at end of file diff --git a/src/tools/tera.rs b/src/tools/tera.rs new file mode 100644 index 0000000..f2e9e30 --- /dev/null +++ b/src/tools/tera.rs @@ -0,0 +1,116 @@ +use std::collections::HashMap; + +use rocket_dyn_templates::tera::{try_get_value, Error, Value, to_value}; + +/// If the `value` is not passed, optionally discard all elements where the attribute is null. (include_null -> all, none, only) +pub fn advanced_filter(value: &Value, args: &HashMap) -> Result { + let mut arr = try_get_value!("filter", "value", Vec, value); + if arr.is_empty() { + return Ok(arr.into()); + } + + let key = match args.get("attribute") { + Some(val) => try_get_value!("filter", "attribute", String, val), + None => return Err(Error::msg("The `filter` filter has to have an `attribute` argument")), + }; + + let null = match args.get("include_null") { + Some(val) => try_get_value!("filter", "attribute", String, val), + None => "none".to_string(), + }; + + let against_value = match args.get("value") { + Some(val) => Some(try_get_value!("filter", "value", String, val)), + None => None + }; + + if null == "only" && against_value.is_some() { + return Err(Error::msg("The `filter` filter cannot have both `include_null=only` and `value`")) + } + + arr = arr + .into_iter() + .filter(|v| { + let tested_value = v.get(key.clone()).unwrap_or(&Value::Null); + if tested_value.is_null() { + return match null.as_str() { + "all" => true, + "none" => false, + "only" => true, + _ => false, + } + } else { + if null != "only" { + let val = tested_value.as_str(); + match val { + Some(match_v) => { + match against_value.clone() { + Some(against_v) => match_v == against_v, + None => true, + } + } + None => true, + } + } else { + false + } + } + }) + .collect::>(); + + Ok(to_value(arr).unwrap()) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::collections::HashMap; + + fn call_advanced_filter(data: &Vec, include_null: &str, value: Option<&str>) -> Result { + let mut args = HashMap::new(); + args.insert("attribute".to_string(), json!("against_value")); + args.insert("include_null".to_string(), json!(include_null)); + if let Some(val) = value { + args.insert("value".to_string(), json!(val)); + } + advanced_filter(&json!(data), &args) + } + + #[derive(serde::Serialize, serde::Deserialize, Debug)] + struct TestClass { + against_value: Option, + } + + impl TestClass { + pub fn new(against_value: Option) -> Self { + TestClass { against_value } + } + } + + fn generate_test_class(first_data: Option, second_data: Option) -> Vec { + vec![TestClass::new(first_data), TestClass::new(second_data)] + } + + #[test] + fn test_advanced_filter() { + let data = vec![TestClass::new(Some("foo".to_string())), TestClass::new(None)]; + let result = call_advanced_filter(&data, "all", None).unwrap(); + assert_eq!(result, json!(generate_test_class(Some("foo".to_string()), None))); + let result = call_advanced_filter(&data, "none", None).unwrap(); + assert_eq!(result, json!(vec![TestClass::new(Some("foo".to_string()))])); + let mut vec: Vec = Vec::new(); + vec.push(TestClass::new(None)); + let result = call_advanced_filter(&data, "only", None).unwrap(); + assert_eq!(result, json!(vec![TestClass::new(None)])); + + // Test filtering strings + let result = call_advanced_filter(&data, "all", Some("foo")).unwrap(); + assert_eq!(result, json!(vec![TestClass::new(Some("foo".to_string())), TestClass::new(None)])); + let result = call_advanced_filter(&data, "none", Some("bar")).unwrap(); + let vec: Vec = Vec::new(); + assert_eq!(result, json!(vec)); + let result = call_advanced_filter(&data, "only", Some("zz")).is_err(); + assert_eq!(result, true); + } +} diff --git a/templates/default.html.tera b/templates/default.html.tera index e5eedcd..04a5054 100644 --- a/templates/default.html.tera +++ b/templates/default.html.tera @@ -20,29 +20,29 @@