diff --git a/.gitea/workflows/tag_release.yaml b/.gitea/workflows/release.yml similarity index 77% rename from .gitea/workflows/tag_release.yaml rename to .gitea/workflows/release.yml index 802b773..220e516 100644 --- a/.gitea/workflows/tag_release.yaml +++ b/.gitea/workflows/release.yml @@ -1,4 +1,4 @@ -name: Go Release +name: Release Tagging on: push: @@ -9,27 +9,28 @@ on: - main jobs: - build-and-release: + release: runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v5 - - - name: Set up Go - uses: actions/setup-go@v6 with: - go-version: '1.26.2' + fetch-depth: 0 # Important for git describe --tags - - name: Create version + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.95 + components: cargo + + - name: Extract Version from Cargo.toml id: version run: | - echo "Creating version" - - VERSION="0.2.4" + VERSION=$(grep '^version = "' Cargo.toml | sed -E 's/version = "([^"]+)"/\1/') PROJECT_COMMIT_HASH=$(git rev-parse HEAD | cut -c 1-10) BRANCH_REF="${{ gitea.ref }}" BRANCH_NAME=$(echo "$BRANCH_REF" | cut -d '/' -f 3) - PROJECT_TAG_RELEASE="v$VERSION-$BRANCH_NAME-$PROJECT_COMMIT_HASH-556" + PROJECT_TAG_RELEASE="v$VERSION-$BRANCH_NAME-$PROJECT_COMMIT_HASH-111" echo "::set-output name=project_tag_release::$PROJECT_TAG_RELEASE" diff --git a/.gitea/workflows/rust.yml b/.gitea/workflows/rust.yml new file mode 100644 index 0000000..ddad6a8 --- /dev/null +++ b/.gitea/workflows/rust.yml @@ -0,0 +1,67 @@ +name: Rust Build + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + check: + name: Check + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + - uses: Swatinem/rust-cache@v2 + - run: cargo check + + test: + name: Test Suite + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + - uses: Swatinem/rust-cache@v2 + - run: cargo test + + fmt: + name: Rustfmt + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + - uses: Swatinem/rust-cache@v2 + - run: rustup component add rustfmt + - run: cargo fmt --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + - uses: Swatinem/rust-cache@v2 + - run: rustup component add clippy + - run: cargo clippy -- -D warnings + + build: + name: build + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: 1.95 + - uses: Swatinem/rust-cache@v2 + - run: cargo build diff --git a/.gitea/workflows/workflow.yaml b/.gitea/workflows/workflow.yaml deleted file mode 100644 index 19c6482..0000000 --- a/.gitea/workflows/workflow.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: Go CI - -on: [push, pull_request] # Triggers on push or pull request events - -jobs: - test: - name: Test and Lint - runs-on: ubuntu-24.04 - steps: - - name: Check out repository code - uses: actions/checkout@v5 - - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version: '1.26.2' - - name: Configure Git for SSH - run: | - git config --global url."git@${{ secrets.GIT_HOST_ROOT }}:".insteadOf "https://${{ secrets.GIT_HOST_ROOT }}/" - go env -w GOPRIVATE=${{ secrets.GIT_HOST_ROOT }}/phoenix/* - - - name: Download dependencies - run: | - go mod download - - - name: Run tests - run: go test -v ./... # Runs all tests in the project diff --git a/.gitignore b/.gitignore index 57872d0..2f7896d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/vendor/ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..840357b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,838 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "josekit" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" +dependencies = [ + "anyhow", + "base64", + "flate2", + "openssl", + "regex", + "serde", + "serde_json", + "thiserror", + "time", +] + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textsender_models" +version = "0.3.0" +dependencies = [ + "const_format", + "dotenvy", + "josekit", + "serde", + "serde_json", + "time", + "utoipa", + "uuid", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utoipa" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bde15df68e80b16c7d16b9616e80770ad158988daa56a27dccd1e55558b0160" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba0b99ee52df3028635d93840c797102da61f8a7bb3cf751032455895b52ef8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "uuid" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +dependencies = [ + "getrandom", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2663808 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "textsender_models" +version = "0.3.0" +edition = "2024" +rust-version = "1.95" +description = "Models used for the textsender project" + +[dependencies] +serde = { version = "1.0.228", features = ["derive"] } +serde_json = { version = "1.0.149" } +time = { version = "0.3.47", features = ["formatting", "macros", "parsing", "serde"] } +uuid = { version = "1.23.1", features = ["v4", "serde"] } +dotenvy = { version = "0.15.7" } +const_format = { version = "0.2.36" } +josekit = { version = "0.10.3" } +utoipa = { version = "5.4.0", features = ["uuid", "time"] } + +[dev-dependencies] diff --git a/README.md b/README.md deleted file mode 100644 index 622a843..0000000 --- a/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# textsender-models -A library containing functions, structs, types, and other code for the textsender -project - - -### Building project -```SHELL -go mod tidy -go mod vendor -``` diff --git a/go.mod b/go.mod deleted file mode 100644 index 36f7a6e..0000000 --- a/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module git.kundeng.us/phoenix/textsender-models - -go 1.26.2 - -require ( - github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/google/uuid v1.6.0 - github.com/stretchr/testify v1.11.1 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 346e255..0000000 --- a/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= -github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/config/auxiliary/mod.rs b/src/config/auxiliary/mod.rs new file mode 100644 index 0000000..398668b --- /dev/null +++ b/src/config/auxiliary/mod.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct TwilioConfig { + #[serde(alias = "auth_id")] + pub account_sid: String, + #[serde(skip_serializing_if = "String::is_empty")] + pub service_sid: String, + #[serde(skip_serializing_if = "String::is_empty")] + pub auth_token: String, + #[serde(skip_serializing_if = "String::is_empty")] + pub phone_number: String, +} + +impl TwilioConfig { + pub fn print_config(&self) { + println!("Twilio config"); + println!("Account SID: {:?}", self.account_sid); + println!("Service SID: {:?}", self.service_sid); + println!("Auth Token: {:?}", self.auth_token); + println!("Number: {:?}", self.phone_number); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..22124de --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1 @@ +pub mod auxiliary; diff --git a/src/contact/mod.rs b/src/contact/mod.rs new file mode 100644 index 0000000..9eba279 --- /dev/null +++ b/src/contact/mod.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Contact { + #[serde(skip_serializing_if = "crate::init::is_uuid_nil")] + pub id: Option, + // empty? + #[serde(skip_serializing_if = "Option::is_none")] + pub firstname: Option, + // empty? + #[serde(skip_serializing_if = "Option::is_none")] + pub lastname: Option, + // empty? + #[serde(skip_serializing_if = "Option::is_none")] + pub nickname: Option, + pub phone_number: String, + // empty + #[serde(skip_serializing_if = "crate::init::is_uuid_nil")] + pub user_id: Option, +} diff --git a/src/envy/environment.rs b/src/envy/environment.rs new file mode 100644 index 0000000..b05d433 --- /dev/null +++ b/src/envy/environment.rs @@ -0,0 +1,106 @@ +pub async fn get_db_url() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::DB_URL; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_secret_main_key() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::SECRET_MAIN_KEY; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_service_passphrase() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::SERVICE_PASSPHRASE; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_secret_key() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::SECRET_KEY; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_root_directory() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::ROOT_DIRECTORY; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_app_base_api_url() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::TEXTSENDER_BASE_API_URL; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_app_auth_base_api_url() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::TEXTSENDER_AUTH_BASE_API_URL; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_app_env() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::APP_ENV; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_backend_port() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::BACKEND_PORT; + let value = std::env::var(key).expect(key); + crate::envy::init_envvar(key, &value) +} + +pub async fn get_frontend_url() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::FRONTEND_URL; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_rust_log() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::RUST_LOG; + let value = std::env::var(key).expect(key); + + crate::envy::init_envvar(key, &value) +} + +pub async fn get_allowed_origins() -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let key = crate::envy::keys::ALLOWED_ORIGINS; + let value = std::env::var(key).expect(key); + + let mut envvar = crate::envy::init_envvar(key, &value); + crate::envy::init_delimiter(&mut envvar, ','); + + envvar +} + +/// Get environment not specified in the code +pub async fn get_env(environment: &str) -> crate::envy::EnvVar { + dotenvy::dotenv().ok(); + let my_error = format!("{environment} {}", crate::envy::keys::error::GENERAL_ERROR); + let value = std::env::var(environment).expect(&my_error); + + crate::envy::init_envvar(environment, &value) +} diff --git a/src/envy/keys.rs b/src/envy/keys.rs new file mode 100644 index 0000000..24e84a9 --- /dev/null +++ b/src/envy/keys.rs @@ -0,0 +1,53 @@ +/// Environment key for Database management +pub const DB_URL: &str = "DATABASE_URL"; + +/// Environment key for secret main key +/// Used for the textsender app +pub const SECRET_MAIN_KEY: &str = "SECRET_MAIN_KEY"; + +/// Environment key for service logins +pub const SERVICE_PASSPHRASE: &str = "SERVICE_PASSPHRASE"; + +/// Environment key for secret key +/// Generic use of secret key that could be found in various apps +pub const SECRET_KEY: &str = "SECRET_KEY"; + +/// Environment key for root directory for the textsender app +pub const ROOT_DIRECTORY: &str = "ROOT_DIRECTORY"; + +/// Environment key for textsender api url +pub const TEXTSENDER_BASE_API_URL: &str = "TEXTSENDER_BASE_API_URL"; + +/// Environment key for textsender auth api url +pub const TEXTSENDER_AUTH_BASE_API_URL: &str = "TEXTSENDER_AUTH_BASE_API_URL"; + +/// Environment key for App status +pub const APP_ENV: &str = "APP_ENV"; +/// Environment key for backend port. Used for both auth and core functionality +pub const BACKEND_PORT: &str = "BACKEND_PORT"; +/// Environment key for frontend url +pub const FRONTEND_URL: &str = "FRONTEND_URL"; +/// Environment key for application logging +pub const RUST_LOG: &str = "RUST_LOG"; +/// Environment key for allowed origins for CORS support +pub const ALLOWED_ORIGINS: &str = "ALLOWED_ORIGINS"; + +pub mod error { + use const_format::concatcp; + + pub const GENERAL_ERROR: &str = "must not be set in enviornment file"; + pub const DB_URL: &str = concatcp!(super::DB_URL, " ", GENERAL_ERROR); + pub const SECRET_MAIN_KEY: &str = concatcp!(super::SECRET_MAIN_KEY, " ", GENERAL_ERROR); + pub const SERVICE_LOGIN: &str = concatcp!(super::SERVICE_PASSPHRASE, " ", GENERAL_ERROR); + pub const SECRET_KEY: &str = concatcp!(super::SECRET_KEY, " ", GENERAL_ERROR); + pub const ROOT_DIRECTORY: &str = concatcp!(super::ROOT_DIRECTORY, " ", GENERAL_ERROR); + pub const TEXTSENDER_BASE_API_URL: &str = + concatcp!(super::TEXTSENDER_BASE_API_URL, " ", GENERAL_ERROR); + pub const TEXTSENDER_AUTH_BASE_API_URL: &str = + concatcp!(super::TEXTSENDER_AUTH_BASE_API_URL, " ", GENERAL_ERROR); + pub const APP_ENV: &str = concatcp!(super::APP_ENV, " ", GENERAL_ERROR); + pub const BACKEND_PORT: &str = concatcp!(super::BACKEND_PORT, " ", GENERAL_ERROR); + pub const FRONTEND_URL: &str = concatcp!(super::FRONTEND_URL, " ", GENERAL_ERROR); + pub const RUST_LOG: &str = concatcp!(super::RUST_LOG, " ", GENERAL_ERROR); + pub const ALLOWED_ORIGINS: &str = concatcp!(super::ALLOWED_ORIGINS, " ", GENERAL_ERROR); +} diff --git a/src/envy/mod.rs b/src/envy/mod.rs new file mode 100644 index 0000000..d3d1bde --- /dev/null +++ b/src/envy/mod.rs @@ -0,0 +1,39 @@ +pub mod environment; +pub mod keys; +pub mod utility; + +#[derive(Debug, Default, Clone)] +pub struct EnvVar { + pub key: String, + pub value: String, + pub has_delimiter: bool, + pub delimiter: char, +} + +pub fn init_envvar(key: &str, value: &str) -> EnvVar { + EnvVar { + key: key.to_string(), + value: value.to_string(), + has_delimiter: false, + ..Default::default() + } +} + +pub fn init_delimiter(envvar: &mut EnvVar, delimiter: char) { + let mut amount_of_delimiters_found: i32 = 0; + + for v in envvar.value.chars() { + if v == delimiter { + amount_of_delimiters_found += 1; + } + } + + let has_delimiter = amount_of_delimiters_found >= 1; + + if has_delimiter { + envvar.has_delimiter = has_delimiter; + envvar.delimiter = delimiter; + } else { + envvar.has_delimiter = has_delimiter; + } +} diff --git a/src/envy/utility.rs b/src/envy/utility.rs new file mode 100644 index 0000000..6ca8cce --- /dev/null +++ b/src/envy/utility.rs @@ -0,0 +1,15 @@ +/// Take the Environment variable and delimitize it. If the value has a delimiter, +/// extract it into some strings +pub fn delimitize(var: &crate::envy::EnvVar) -> Result, std::io::Error> { + if var.has_delimiter { + Ok(var + .value + .split(var.delimiter) + .map(|c| c.parse::().unwrap()) + .collect()) + } else { + Err(std::io::Error::other( + "Environment variable does not have a delimiter", + )) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6dd050f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +pub mod config; +pub mod contact; +pub mod envy; +pub mod message; +pub mod token; +pub mod user; + +pub mod init { + pub fn is_uuid_nil(uuid: &Option) -> bool { + match uuid { + Some(id) => id.is_nil(), + None => true, + } + } +} diff --git a/src/message/event/mod.rs b/src/message/event/mod.rs new file mode 100644 index 0000000..15f2f95 --- /dev/null +++ b/src/message/event/mod.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +pub const MESSAGE_EVENT_RESPONSE_STATUS_INSTANT: &str = "INSTANT"; +pub const MESSAGE_EVENT_RESPONSE_STATUS_SCHEDULED: &str = "SCHEDULED"; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct MessageEventResponse { + pub id: uuid::Uuid, + pub scheduled_message_event_id: uuid::Uuid, + // Json field + pub response: String, + pub user_id: uuid::Uuid, + pub contact_id: uuid::Uuid, + pub message_id: uuid::Uuid, + pub status: String, + #[serde(with = "time::serde::rfc3339::option")] + pub sent: Option, +} diff --git a/src/message/mod.rs b/src/message/mod.rs new file mode 100644 index 0000000..19bd6dc --- /dev/null +++ b/src/message/mod.rs @@ -0,0 +1,13 @@ +pub mod event; +pub mod scheduling; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct Message { + #[serde(skip_serializing_if = "crate::init::is_uuid_nil")] + pub id: Option, + pub content: String, + #[serde(skip_serializing_if = "crate::init::is_uuid_nil")] + pub user_id: Option, +} diff --git a/src/message/scheduling/mod.rs b/src/message/scheduling/mod.rs new file mode 100644 index 0000000..2146791 --- /dev/null +++ b/src/message/scheduling/mod.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct ScheduledMessageEvent { + id: uuid::Uuid, + contact_id: uuid::Uuid, + message_id: uuid::Uuid, + scheduled_message_id: uuid::Uuid, + #[serde(with = "time::serde::rfc3339::option")] + created: Option, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct ScheduledMessage { + id: uuid::Uuid, + #[serde(with = "time::serde::rfc3339::option")] + scheduled: Option, + #[serde(with = "time::serde::rfc3339::option")] + created: Option, + status: String, + user_id: uuid::Uuid, +} + +pub const READY: &str = "READY"; +pub const PENDING: &str = "PENDING"; +pub const PROCESSING: &str = "PROCESSING"; +pub const DONE: &str = "DONE"; diff --git a/src/token/mod.rs b/src/token/mod.rs new file mode 100644 index 0000000..2f713d8 --- /dev/null +++ b/src/token/mod.rs @@ -0,0 +1,89 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Claims { + pub user_id: uuid::Uuid, + pub role: String, + #[serde(alias = "iss")] + pub issued: String, + #[serde(alias = "exp")] + pub expired: i64, + #[serde(alias = "iat")] + pub issued_at: i64, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct LoginResult { + pub user_id: uuid::Uuid, + pub access_token: String, + pub token_type: String, + pub expires_in: i64, + pub issued_at: i64, +} + +// Alias for LoginResult +pub type Login = LoginResult; + +pub fn get_issued() -> time::Result { + Ok(time::OffsetDateTime::now_utc()) +} + +mod util { + pub fn time_to_std_time( + provided_time: &time::OffsetDateTime, + ) -> Result { + let converted = std::time::SystemTime::from(*provided_time); + Ok(converted) + } +} + +#[derive(Debug, utoipa::ToSchema)] +pub struct TokenResource { + pub message: String, + pub issuer: String, + pub audiences: Vec, + pub id: uuid::Uuid, +} + +pub const TOKEN_TYPE: &str = "JWT"; + +pub fn create_token( + key: &String, + token_resource: &TokenResource, + duration: time::Duration, +) -> Result<(String, i64), josekit::JoseError> { + let mut header = josekit::jws::JwsHeader::new(); + header.set_token_type(TOKEN_TYPE); + + let mut payload = josekit::jwt::JwtPayload::new(); + let message = &token_resource.message; + let issuer = &token_resource.issuer; + let audiences: &Vec = &token_resource.audiences; + payload.set_subject(message); + payload.set_issuer(issuer); + payload.set_audience(audiences.clone()); + if !token_resource.id.is_nil() { + match payload.set_claim("id", Some(serde_json::json!(token_resource.id))) { + Ok(_) => {} + Err(err) => { + return Err(err); + } + } + } + match get_issued() { + Ok(issued) => { + let expire = issued + duration; + payload.set_issued_at(&util::time_to_std_time(&issued).unwrap()); + payload.set_expires_at(&util::time_to_std_time(&expire).unwrap()); + + let signer = josekit::jws::alg::hmac::HmacJwsAlgorithm::Hs256 + .signer_from_bytes(key.as_bytes()) + .unwrap(); + Ok(( + josekit::jwt::encode_with_signer(&payload, &header, &signer).unwrap(), + (expire - time::OffsetDateTime::UNIX_EPOCH).whole_seconds(), + )) + } + Err(e) => Err(josekit::JoseError::InvalidClaim(e.into())), + } +} diff --git a/src/user/mod.rs b/src/user/mod.rs new file mode 100644 index 0000000..e2d4f66 --- /dev/null +++ b/src/user/mod.rs @@ -0,0 +1,124 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct User { + pub id: uuid::Uuid, + pub phone_number: String, + pub username: String, + pub password: String, + // Firstname is a pointer + pub firstname: String, + // Lastname is a pointer + pub lastname: String, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option, + #[serde(with = "time::serde::rfc3339::option")] + pub last_login: Option, + #[serde(skip)] + pub salt_id: uuid::Uuid, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct ServiceUser { + pub id: uuid::Uuid, + pub username: String, + pub passphrase: String, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option, + // When serializing to json, if empty do not populate + #[serde(with = "time::serde::rfc3339::option")] + pub last_login: Option, + #[serde(skip)] + pub salt_id: uuid::Uuid, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct Organization { + pub id: uuid::Uuid, + pub name: String, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct UserLoginHistory { + pub id: uuid::Uuid, + #[serde(with = "time::serde::rfc3339::option")] + pub login_time: Option, + pub user_id: uuid::Uuid, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct ServiceUserLoginHistory { + pub id: uuid::Uuid, + #[serde(with = "time::serde::rfc3339::option")] + pub login_time: Option, + pub service_user_id: uuid::Uuid, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] +pub struct UserProfile { + pub user_id: uuid::Uuid, + pub phone_number: String, + pub username: String, + pub firstname: String, + pub lastname: String, + #[serde(with = "time::serde::rfc3339::option")] + pub created: Option, + #[serde(with = "time::serde::rfc3339::option")] + pub last_login: Option, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Salt { + // #[serde(skip_serializing_if = "crate::init::is_uuid_nil")] + pub id: uuid::Uuid, + #[serde(skip_serializing_if = "String::is_empty")] + pub salt: String, +} + +impl Salt { + pub fn to_json(&self, output_pretty: bool) -> Result { + if output_pretty { + serde_json::to_string_pretty(&self) + } else { + serde_json::to_string(&self) + } + } +} + +pub fn init_user_profile(usr: &User) -> UserProfile { + UserProfile { + user_id: usr.id, + phone_number: usr.phone_number.clone(), + username: usr.username.clone(), + firstname: usr.firstname.clone(), + lastname: usr.lastname.clone(), + created: usr.created, + last_login: usr.last_login, + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_init_user_profile() { + let usr = super::User { + id: uuid::Uuid::new_v4(), + phone_number: String::from("+12308438462"), + username: String::from("Cowboy99"), + password: String::from("238n7cyr2q89d3xhe2"), + firstname: String::from("BillyBob"), + lastname: String::from("Jones"), + created: Some(time::OffsetDateTime::now_utc()), + last_login: Some(time::OffsetDateTime::now_utc()), + ..Default::default() + }; + + let profile = super::init_user_profile(&usr); + assert_eq!( + usr.phone_number, profile.phone_number, + "Phone numbers are not equal" + ); + } +} diff --git a/tx0/config/auxiliary/twilio_config.go b/tx0/config/auxiliary/twilio_config.go deleted file mode 100644 index f03332c..0000000 --- a/tx0/config/auxiliary/twilio_config.go +++ /dev/null @@ -1,20 +0,0 @@ -package auxiliary - -import ( - "fmt" -) - -type TwilioConfig struct { - AccountSID string `json:"auth_sid"` - ServiceSID string `json:"service_sid"` - AuthToken string `json:"auth_token"` - Number string `json:"phone_number"` -} - -func (config *TwilioConfig) PrintConfig() { - fmt.Println("Twilio config") - fmt.Println("Account SID:", config.AccountSID) - fmt.Println("Service SID:", config.ServiceSID) - fmt.Println("Auth Token:", config.AuthToken) - fmt.Println("Number:", config.Number) -} diff --git a/tx0/contact/contact.go b/tx0/contact/contact.go deleted file mode 100644 index 6baee60..0000000 --- a/tx0/contact/contact.go +++ /dev/null @@ -1,14 +0,0 @@ -package contact - -import ( - "github.com/google/uuid" -) - -type Contact struct { - Id *uuid.UUID `json:"id,omitempty"` - Firstname *string `json:"first_name,omitempty"` - Lastname *string `json:"last_name,omitempty"` - Nickname *string `json:"nickname,omitempty"` - PhoneNumber string `json:"phone_number"` - UserId *uuid.UUID `json:"user_id,omitempty"` -} diff --git a/tx0/message/event/response.go b/tx0/message/event/response.go deleted file mode 100644 index 720dd6b..0000000 --- a/tx0/message/event/response.go +++ /dev/null @@ -1,18 +0,0 @@ -package event - -import ( - "time" - - "github.com/google/uuid" -) - -type MessageEventResponse struct { - Id uuid.UUID `json:"id,omitempty"` - ScheduledMessageEventId uuid.UUID `json:"scheduled_message_event_id,omitempty"` - Response map[string]any `json:"response"` - UserId uuid.UUID `json:"user_id,omitempty"` - ContactId *uuid.UUID `json:"contact_id,omitempty"` - MessageId *uuid.UUID `json:"message_id,omitempty"` - Status string `json:"status"` - Sent time.Time `json:"sent"` -} diff --git a/tx0/message/event/status.go b/tx0/message/event/status.go deleted file mode 100644 index f979152..0000000 --- a/tx0/message/event/status.go +++ /dev/null @@ -1,6 +0,0 @@ -package event - -const ( - Message_Event_Response_Status_Instant = "INSTANT" - Message_Event_Response_Status_Scheduled = "SCHEDULED" -) diff --git a/tx0/message/message.go b/tx0/message/message.go deleted file mode 100644 index 71fe125..0000000 --- a/tx0/message/message.go +++ /dev/null @@ -1,11 +0,0 @@ -package message - -import ( - "github.com/google/uuid" -) - -type Message struct { - Id *uuid.UUID `json:"id,omitempty"` - Content string `json:"content"` - UserId *uuid.UUID `json:"user_id,omitempty"` -} diff --git a/tx0/message/scheduling/event.go b/tx0/message/scheduling/event.go deleted file mode 100644 index e8d7fa5..0000000 --- a/tx0/message/scheduling/event.go +++ /dev/null @@ -1,15 +0,0 @@ -package scheduling - -import ( - "time" - - "github.com/google/uuid" -) - -type ScheduledMessageEvent struct { - Id uuid.UUID `json:"id"` - ContactId uuid.UUID `json:"contact_id"` - MessageId uuid.UUID `json:"message_id"` - ScheduledMessageId uuid.UUID `json:"scheduled_message_id"` - Created time.Time `json:"created"` -} diff --git a/tx0/message/scheduling/message.go b/tx0/message/scheduling/message.go deleted file mode 100644 index dcdec5b..0000000 --- a/tx0/message/scheduling/message.go +++ /dev/null @@ -1,15 +0,0 @@ -package scheduling - -import ( - "time" - - "github.com/google/uuid" -) - -type ScheduledMessage struct { - Id uuid.UUID `json:"id"` - Scheduled time.Time `json:"scheduled"` - Created time.Time `json:"created"` - Status string `json:"status"` - UserId uuid.UUID `json:"user_id"` -} diff --git a/tx0/message/scheduling/status.go b/tx0/message/scheduling/status.go deleted file mode 100644 index 9198cbd..0000000 --- a/tx0/message/scheduling/status.go +++ /dev/null @@ -1,8 +0,0 @@ -package scheduling - -const ( - Ready = "READY" - Pending = "PENDING" - Processing = "PROCESSING" - Done = "DONE" -) diff --git a/tx0/token/token.go b/tx0/token/token.go deleted file mode 100644 index b6fb11e..0000000 --- a/tx0/token/token.go +++ /dev/null @@ -1,24 +0,0 @@ -package token - -import ( - "github.com/golang-jwt/jwt/v5" - "github.com/google/uuid" -) - -type Claims struct { - UserId uuid.UUID `json:"user_id"` - Role string `json:"role"` - jwt.RegisteredClaims -} - -type LoginResult struct { - UserId uuid.UUID `json:"user_id"` - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int64 `json:"expires_in"` - IssuedAt int64 `json:"issued_at"` -} - -// Alias for LoginResult -// Will be deprecated at some point -type Login = LoginResult diff --git a/tx0/types/jsonb.go b/tx0/types/jsonb.go deleted file mode 100644 index d8dffbf..0000000 --- a/tx0/types/jsonb.go +++ /dev/null @@ -1,42 +0,0 @@ -package types - -import ( - "database/sql/driver" - "encoding/json" - "fmt" -) - -// NOTE: Might end up removing this -type JSONB json.RawMessage - -// Scan implements sql.Scanner -func (j *JSONB) Scan(value interface{}) error { - if value == nil { - *j = nil - return nil - } - - var data []byte - switch v := value.(type) { - case []byte: - // pgx may pass data as []byte in binary format - data = v - case string: - // pgx may pass data as string in text format - data = []byte(v) - default: - return fmt.Errorf("unsupported type: %T", v) - } - - *j = JSONB(data) - return nil -} - -// Value implements driver.Valuer -func (j JSONB) Value() (driver.Value, error) { - if j == nil { - return nil, nil - } else { - return []byte(j), nil - } -} diff --git a/tx0/user/login_history.go b/tx0/user/login_history.go deleted file mode 100644 index 7f39e33..0000000 --- a/tx0/user/login_history.go +++ /dev/null @@ -1,18 +0,0 @@ -package user - -import ( - "github.com/google/uuid" - "time" -) - -type UserLoginHistory struct { - Id uuid.UUID `json:"id"` - LoginTime time.Time `json:"login_time"` - UserId uuid.UUID `json:"user_id"` -} - -type ServiceUserLoginHistory struct { - Id uuid.UUID `json:"id"` - LoginTime time.Time `json:"login_time"` - ServiceUserId uuid.UUID `json:"service_user_id"` -} diff --git a/tx0/user/organization.go b/tx0/user/organization.go deleted file mode 100644 index 2314d51..0000000 --- a/tx0/user/organization.go +++ /dev/null @@ -1,13 +0,0 @@ -package user - -import ( - "time" - - "github.com/google/uuid" -) - -type Organization struct { - Id uuid.UUID `json:"id"` - Name string `json:"name"` - Created *time.Time `json:"created,omitempty"` -} diff --git a/tx0/user/service_user.go b/tx0/user/service_user.go deleted file mode 100644 index c613a7c..0000000 --- a/tx0/user/service_user.go +++ /dev/null @@ -1,16 +0,0 @@ -package user - -import ( - "time" - - "github.com/google/uuid" -) - -type ServiceUser struct { - Id uuid.UUID `json:"id"` - // TODO: Remove the omitempty tags at a later point - Username string `json:"username,omitempty"` - Passphrase string `json:"passphrase,omitempty"` - Created time.Time `json:"created"` - LastLogin *time.Time `json:"last_login,omitempty"` -} diff --git a/tx0/user/user.go b/tx0/user/user.go deleted file mode 100644 index 3bbf161..0000000 --- a/tx0/user/user.go +++ /dev/null @@ -1,20 +0,0 @@ -package user - -import ( - "time" - - "github.com/google/uuid" -) - -type User struct { - Id uuid.UUID `json:"id"` - PhoneNumber string `json:"phone_number"` - Username string `json:"username"` - Password string `json:"password"` - // Firstname is a pointer - Firstname *string `json:"first_name,omitempty"` - // Lastname is a pointer - Lastname *string `json:"last_name,omitempty"` - Created time.Time `json:"created"` - LastLogin *time.Time `json:"last_login,omitempty"` -} diff --git a/tx0/user/user_profile.go b/tx0/user/user_profile.go deleted file mode 100644 index 37b7f4a..0000000 --- a/tx0/user/user_profile.go +++ /dev/null @@ -1,41 +0,0 @@ -package user - -import ( - "fmt" - "time" - - "github.com/google/uuid" -) - -type UserProfile struct { - UserId uuid.UUID `json:"user_id"` - PhoneNumber string `json:"phone_number"` - Username string `json:"username"` - Firstname *string `json:"firstname,omitempty"` - Lastname *string `json:"lastname,omitempty"` - Created time.Time `json:"created"` - LastLogin *time.Time `json:"last_login,omitempty"` -} - -func InitUserProfile(usr *User) (usrProfile *UserProfile, err error) { - if usr == nil { - return nil, fmt.Errorf("User is empty") - } else { - usrProfile = new(UserProfile) - usrProfile.UserId = usr.Id - usrProfile.PhoneNumber = usr.PhoneNumber - usrProfile.Username = usr.Username - if usr.Firstname != nil { - usrProfile.Firstname = usr.Firstname - } - if usr.Lastname != nil { - usrProfile.Lastname = usr.Lastname - } - usrProfile.Created = usr.Created - if usr.LastLogin != nil { - usrProfile.LastLogin = usr.LastLogin - } - - return usrProfile, nil - } -} diff --git a/tx0/user/user_profile_test.go b/tx0/user/user_profile_test.go deleted file mode 100644 index 31e7bbd..0000000 --- a/tx0/user/user_profile_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package user - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestInitUserProfile(t *testing.T) { - usr := User{Username: "Bob", PhoneNumber: "+12224572351", Password: "TheNight!"} - - profile, err := InitUserProfile(&usr) - - assert.NoError(t, err, "Error %v", err) - assert.NotEmpty(t, profile, "UserProfile is empty") -}