Compare commits

..
35 Commits
Author SHA1 Message Date
phoenix 8b49b63d59 Adding license (#36)
Release Tagging / release (push) Successful in 1m25s
Rust Build / Check (push) Successful in 1m24s
Rust Build / Test Suite (push) Successful in 1m26s
Rust Build / Rustfmt (push) Successful in 53s
Rust Build / build (push) Successful in 1m13s
Rust Build / Clippy (push) Successful in 3m37s
Reviewed-on: #36
2026-07-14 11:15:19 -04:00
phoenix 597af4e84c Change name (#35)
Release Tagging / release (push) Successful in 32s
Rust Build / Rustfmt (push) Successful in 35s
Rust Build / Test Suite (push) Successful in 1m27s
Rust Build / Clippy (push) Successful in 1m12s
Rust Build / build (push) Successful in 55s
Rust Build / Check (push) Successful in 2m37s
Reviewed-on: #35
2026-07-13 11:28:29 -04:00
phoenix 02061bf7a3 Update rust (#34)
Rust Build / Check (push) Successful in 1m21s
Rust Build / build (push) Successful in 2m4s
Rust Build / Clippy (push) Successful in 2m8s
Release Tagging / release (push) Successful in 41s
Rust Build / Rustfmt (push) Successful in 37s
Rust Build / Test Suite (push) Successful in 1m41s
Reviewed-on: phoenix/textsender_models#34
2026-07-10 11:30:31 -04:00
phoenix 151eeaacf6 Update rust (#33)
Release Tagging / release (push) Successful in 45s
Rust Build / Check (push) Successful in 1m8s
Rust Build / Rustfmt (push) Successful in 28s
Rust Build / Test Suite (push) Successful in 1m31s
Rust Build / build (push) Successful in 1m5s
Rust Build / Clippy (push) Successful in 2m24s
Reviewed-on: phoenix/textsender_models#33
2026-07-05 16:49:32 -04:00
phoenix 137398405f Code refactoring (#32)
Release Tagging / release (push) Successful in 52s
Rust Build / Check (push) Successful in 1m9s
Rust Build / Rustfmt (push) Successful in 30s
Rust Build / Test Suite (push) Successful in 1m29s
Rust Build / Clippy (push) Successful in 1m2s
Rust Build / build (push) Successful in 57s
Reviewed-on: phoenix/textsender_models#32
2026-07-02 13:18:41 -04:00
phoenix 6508c6c535 tsk-30: Remove async from envy emodukle (#31)
Release Tagging / release (push) Successful in 58s
Rust Build / Check (push) Successful in 1m9s
Rust Build / Test Suite (push) Successful in 1m21s
Rust Build / Rustfmt (push) Successful in 46s
Rust Build / Clippy (push) Successful in 1m3s
Rust Build / build (push) Successful in 1m16s
Closes #30

Reviewed-on: phoenix/textsender_models#31
2026-07-01 23:00:39 -04:00
phoenix c1d391939b Update models (#29)
Release Tagging / release (push) Successful in 48s
Rust Build / Rustfmt (push) Successful in 35s
Rust Build / Test Suite (push) Successful in 1m26s
Rust Build / Clippy (push) Successful in 1m27s
Rust Build / build (push) Successful in 1m29s
Rust Build / Check (push) Successful in 1m12s
textsender_models PR / Rustfmt (pull_request) Successful in 54s
textsender_models PR / Check (pull_request) Successful in 1m20s
Release Tagging / release (pull_request) Successful in 37s
textsender_models PR / Clippy (pull_request) Successful in 1m5s
Reviewed-on: phoenix/textsender_models#29
2026-06-27 17:20:20 -04:00
phoenix 2722af5589 Feature usage (#28)
Release Tagging / release (push) Successful in 51s
Rust Build / Check (push) Successful in 1m7s
Rust Build / Test Suite (push) Successful in 1m9s
Rust Build / Rustfmt (push) Successful in 56s
Rust Build / Clippy (push) Successful in 47s
Rust Build / build (push) Successful in 1m3s
Reviewed-on: phoenix/textsender_models#28
2026-06-14 17:43:09 -04:00
phoenix c09fa36b15 Claim fix (#27)
Release Tagging / release (push) Successful in 35s
Rust Build / Rustfmt (push) Successful in 39s
Rust Build / Test Suite (push) Successful in 1m13s
Rust Build / Check (push) Successful in 1m30s
Rust Build / build (push) Successful in 47s
Rust Build / Clippy (push) Successful in 1m23s
Reviewed-on: phoenix/textsender_models#27
2026-06-13 19:00:22 -04:00
phoenix 8317f41e3c Claim update (#26)
Release Tagging / release (push) Successful in 47s
Rust Build / Check (push) Successful in 56s
Rust Build / Rustfmt (push) Successful in 27s
Rust Build / Test Suite (push) Successful in 1m34s
Rust Build / Clippy (push) Successful in 50s
Rust Build / build (push) Successful in 40s
Release Tagging / release (pull_request) Successful in 46s
Rust Build / Rustfmt (pull_request) Successful in 28s
Rust Build / Check (pull_request) Successful in 1m15s
Rust Build / Test Suite (pull_request) Successful in 1m20s
Rust Build / Clippy (pull_request) Successful in 43s
Rust Build / build (pull_request) Successful in 1m2s
Reviewed-on: phoenix/textsender_models#26
2026-06-13 18:02:25 -04:00
phoenix ba998096cb Contact update (#25)
Release Tagging / release (push) Successful in 52s
Rust Build / Check (push) Successful in 1m4s
Rust Build / Test Suite (push) Successful in 1m7s
Rust Build / Clippy (push) Successful in 34s
Rust Build / build (push) Successful in 51s
Rust Build / Rustfmt (push) Successful in 1m30s
Release Tagging / release (pull_request) Successful in 58s
Rust Build / Test Suite (pull_request) Successful in 1m14s
Rust Build / Check (pull_request) Successful in 1m24s
Rust Build / Rustfmt (pull_request) Successful in 49s
Rust Build / build (pull_request) Successful in 47s
Rust Build / Clippy (pull_request) Successful in 1m6s
Reviewed-on: phoenix/textsender_models#25
2026-06-13 16:25:48 -04:00
phoenix d504108745 Merge pull request 'V0.3.0' (#21) from v0.3.0-migrate into main
Rust Build / Check (push) Successful in 27s
Release Tagging / release (push) Successful in 55s
Rust Build / Test Suite (push) Successful in 52s
Rust Build / Rustfmt (push) Successful in 27s
Rust Build / build (push) Successful in 39s
Rust Build / Clippy (push) Successful in 44s
Rust Build / Check (pull_request) Successful in 52s
Release Tagging / release (pull_request) Successful in 1m1s
Rust Build / Test Suite (pull_request) Successful in 1m8s
Rust Build / Rustfmt (pull_request) Successful in 41s
Rust Build / build (pull_request) Successful in 57s
Rust Build / Clippy (pull_request) Successful in 1m58s
Reviewed-on: phoenix/textsender-models#21
2026-06-07 14:54:19 -04:00
phoenix c6d2d19619 Get ready (#23)
Release Tagging / release (pull_request) Failing after 39s
Rust Build / Check (pull_request) Successful in 39s
Rust Build / Test Suite (pull_request) Successful in 55s
Rust Build / Rustfmt (pull_request) Successful in 1m27s
Rust Build / Clippy (pull_request) Successful in 37s
Rust Build / build (pull_request) Successful in 36s
Reviewed-on: phoenix/textsender-models#23
2026-06-07 14:51:07 -04:00
phoenix 38dbcb09f7 Model changes (#22)
Release Tagging / release (push) Successful in 33s
Rust Build / Test Suite (push) Successful in 27s
Rust Build / Check (push) Successful in 1m3s
Rust Build / Rustfmt (push) Successful in 38s
Rust Build / Clippy (push) Successful in 37s
Rust Build / build (push) Successful in 27s
Release Tagging / release (pull_request) Successful in 2m29s
Rust Build / Check (pull_request) Successful in 3m42s
Rust Build / Test Suite (pull_request) Successful in 1m41s
Rust Build / Clippy (pull_request) Successful in 43s
Rust Build / Rustfmt (pull_request) Successful in 1m40s
Rust Build / build (pull_request) Successful in 41s
Reviewed-on: phoenix/textsender-models#22
2026-06-03 21:34:09 -04:00
phoenix 766d38ea0e Skipping serialization of fields in Contact (#20)
Release Tagging / release (push) Successful in 40s
Rust Build / Test Suite (push) Successful in 26s
Rust Build / Check (push) Successful in 1m28s
Rust Build / Rustfmt (push) Successful in 26s
Rust Build / Clippy (push) Successful in 40s
Rust Build / build (push) Successful in 1m11s
Reviewed-on: phoenix/textsender-models#20
2026-06-01 22:15:17 -04:00
phoenix 2be0337da0 User utoipa (#19)
Rust Build / Check (push) Successful in 36s
Rust Build / Rustfmt (push) Successful in 31s
Rust Build / Test Suite (push) Successful in 41s
Rust Build / build (push) Successful in 31s
Rust Build / Clippy (push) Successful in 45s
Release Tagging / release (push) Successful in 40s
Reviewed-on: phoenix/textsender-models#19
2026-05-31 17:48:09 -04:00
phoenix 910ecfc0e0 Extending user (#18)
Rust Build / Check (push) Successful in 39s
Rust Build / Rustfmt (push) Successful in 33s
Rust Build / Test Suite (push) Successful in 37s
Rust Build / build (push) Successful in 34s
Rust Build / Clippy (push) Successful in 40s
Release Tagging / release (push) Successful in 40s
Reviewed-on: phoenix/textsender-models#18
2026-05-31 17:22:52 -04:00
phoenix 43c636a280 Token create (#17)
Release Tagging / release (push) Failing after 28s
Rust Build / Check (push) Successful in 37s
Rust Build / Rustfmt (push) Successful in 25s
Rust Build / Test Suite (push) Successful in 1m1s
Rust Build / Clippy (push) Successful in 26s
Rust Build / build (push) Successful in 25s
Rust Build / Rustfmt (pull_request) Successful in 28s
Rust Build / Clippy (pull_request) Successful in 34s
Rust Build / build (pull_request) Successful in 33s
Release Tagging / release (pull_request) Successful in 33s
Rust Build / Check (pull_request) Successful in 42s
Rust Build / Test Suite (pull_request) Successful in 36s
Reviewed-on: phoenix/textsender-models#17
2026-05-31 17:14:16 -04:00
phoenix 1340440fec Envy (#16)
Rust Build / Rustfmt (push) Successful in 25s
Rust Build / Clippy (push) Successful in 31s
Rust Build / build (push) Successful in 58s
Rust Build / Check (push) Successful in 32s
Rust Build / Test Suite (push) Successful in 25s
Release Tagging / release (push) Successful in 1m24s
Reviewed-on: phoenix/textsender-models#16
2026-05-31 15:45:17 -04:00
phoenix 638da70649 Adding scheduling module (#15)
Release Tagging / release (push) Successful in 33s
Rust Build / Check (push) Successful in 52s
Rust Build / Test Suite (push) Successful in 43s
Rust Build / Rustfmt (push) Successful in 27s
Rust Build / Clippy (push) Successful in 29s
Rust Build / build (push) Successful in 32s
Reviewed-on: phoenix/textsender-models#15
2026-05-30 22:56:13 -04:00
phoenix 7bffed3f9d Adding statuses (#14)
Rust Build / Check (push) Successful in 33s
Release Tagging / release (push) Successful in 41s
Rust Build / Test Suite (push) Successful in 39s
Rust Build / Rustfmt (push) Successful in 35s
Rust Build / build (push) Successful in 35s
Rust Build / Clippy (push) Successful in 41s
Reviewed-on: phoenix/textsender-models#14
2026-05-30 16:15:35 -04:00
phoenix 3527f1aff3 Adding event module (#13)
Rust Build / Check (push) Successful in 37s
Release Tagging / release (push) Failing after 40s
Rust Build / Rustfmt (push) Successful in 34s
Rust Build / Test Suite (push) Successful in 39s
Rust Build / build (push) Successful in 26s
Rust Build / Clippy (push) Successful in 1m8s
Reviewed-on: phoenix/textsender-models#13
2026-05-30 15:56:13 -04:00
phoenix e965cdeed9 Adding message module (#12)
Rust Build / Check (push) Successful in 41s
Release Tagging / release (push) Successful in 1m18s
Rust Build / Rustfmt (push) Successful in 26s
Rust Build / Test Suite (push) Successful in 1m5s
Rust Build / Clippy (push) Successful in 27s
Rust Build / build (push) Successful in 33s
Reviewed-on: phoenix/textsender-models#12
2026-05-30 15:15:15 -04:00
phoenix 34c47ac529 Organization (#11)
Release Tagging / release (push) Successful in 28s
Rust Build / Check (push) Successful in 37s
Rust Build / Rustfmt (push) Successful in 28s
Rust Build / Clippy (push) Successful in 30s
Rust Build / build (push) Successful in 26s
Rust Build / Test Suite (push) Successful in 28s
Reviewed-on: phoenix/textsender-models#11
2026-05-29 22:55:32 -04:00
phoenix e5ae5e1828 Adding UserProfile (#10)
Release Tagging / release (push) Failing after 26s
Rust Build / Check (push) Successful in 35s
Rust Build / Test Suite (push) Successful in 27s
Rust Build / Rustfmt (push) Successful in 29s
Rust Build / Clippy (push) Successful in 30s
Rust Build / build (push) Successful in 24s
Reviewed-on: phoenix/textsender-models#10
2026-05-29 22:22:15 -04:00
phoenix 179a550034 Adding Login history (#9)
Release Tagging / release (push) Failing after 36s
Rust Build / Check (push) Successful in 46s
Rust Build / Test Suite (push) Successful in 39s
Rust Build / Rustfmt (push) Successful in 32s
Rust Build / Clippy (push) Successful in 33s
Rust Build / build (push) Successful in 33s
Reviewed-on: phoenix/textsender-models#9
2026-05-29 21:41:40 -04:00
phoenix a8e173d831 ServiceUser (#8)
Release Tagging / release (push) Successful in 29s
Rust Build / Check (push) Successful in 39s
Rust Build / Test Suite (push) Successful in 25s
Rust Build / Rustfmt (push) Successful in 27s
Rust Build / Clippy (push) Successful in 28s
Rust Build / build (push) Successful in 26s
Reviewed-on: phoenix/textsender-models#8
2026-05-29 10:02:51 -04:00
phoenix 6975475330 Added user module (#7)
Release Tagging / release (push) Successful in 34s
Rust Build / Check (push) Successful in 34s
Rust Build / Test Suite (push) Successful in 26s
Rust Build / Rustfmt (push) Successful in 35s
Rust Build / Clippy (push) Successful in 29s
Rust Build / build (push) Successful in 27s
Reviewed-on: phoenix/textsender-models#7
2026-05-29 09:50:59 -04:00
phoenix b28d26e26b Added fields to Claims (#6)
Release Tagging / release (push) Successful in 36s
Rust Build / Check (push) Successful in 36s
Rust Build / Rustfmt (push) Successful in 32s
Rust Build / Test Suite (push) Successful in 37s
Rust Build / Clippy (push) Successful in 33s
Rust Build / build (push) Successful in 34s
Reviewed-on: phoenix/textsender-models#6
2026-05-29 09:38:53 -04:00
phoenix c85ca95016 Adding token module (#5)
Release Tagging / release (push) Successful in 29s
Rust Build / Check (push) Successful in 37s
Rust Build / Test Suite (push) Successful in 29s
Rust Build / Rustfmt (push) Successful in 27s
Rust Build / Clippy (push) Successful in 31s
Rust Build / build (push) Successful in 28s
Reviewed-on: phoenix/textsender-models#5
2026-05-28 22:25:57 -04:00
phoenix cfd5050f98 Added code (#4)
Release Tagging / release (push) Successful in 36s
Rust Build / Check (push) Successful in 43s
Rust Build / Test Suite (push) Successful in 28s
Rust Build / Rustfmt (push) Successful in 29s
Rust Build / Clippy (push) Successful in 27s
Rust Build / build (push) Successful in 26s
Reviewed-on: phoenix/textsender-models#4
2026-05-28 21:42:42 -04:00
phoenix b4d1e59ba5 Clean slate 2026-05-28 20:41:34 -04:00
phoenix f39d48d85a Organization (#3)
Go Release / build-and-release (push) Successful in 6s
Go CI / Test and Lint (push) Successful in 7s
Reviewed-on: phoenix/textsender-models#3
2026-05-28 20:04:12 -04:00
phoenix 3ccc1a9a6d UserProfile (#1)
Go Release / build-and-release (push) Successful in 5s
Go CI / Test and Lint (push) Successful in 6s
Reviewed-on: phoenix/textsender-models#1
2026-05-27 22:59:45 -04:00
phoenix 5b36e5c42e Login History (#2)
Go Release / build-and-release (push) Successful in 6s
Go CI / Test and Lint (push) Successful in 12s
Reviewed-on: phoenix/textsender-models#2
2026-05-26 22:08:05 -04:00
36 changed files with 2801 additions and 269 deletions
+46
View File
@@ -0,0 +1,46 @@
name: schedtxt_models PR
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: cargo check
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- 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@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: rustup component add clippy
- run: cargo clippy -- -D warnings
@@ -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
uses: actions/checkout@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.97
components: cargo
- name: Extract Version from Cargo.toml
id: version
run: |
echo "Creating version"
VERSION="0.2.1"
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"
+68
View File
@@ -0,0 +1,68 @@
name: Rust Build
on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: cargo check
test:
name: Test Suite
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: cargo test
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- 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@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- 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@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- run: cargo build
-27
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
/vendor/
target/
Generated
+1865
View File
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
[package]
name = "schedtxt_models"
version = "0.5.3"
edition = "2024"
rust-version = "1.97"
license = "MIT"
description = "Models used for the schedtxt project"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.150" }
time = { version = "0.3.53", features = ["formatting", "macros", "parsing", "serde"] }
sqlx = { version = "0.9.0", features = ["runtime-tokio", "tls-native-tls", "postgres", "time", "uuid"] }
uuid = { version = "1.23.5", features = ["v4", "serde"] }
dotenvy = { version = "0.15.7" }
const_format = { version = "0.2.36" }
josekit = { version = "0.10.3" }
utoipa = { version = "5.5.0", features = ["uuid", "time"] }
[features]
default = ["full"]
full = ["user", "message", "contact", "config", "envy", "token"]
user = []
message = []
contact = []
config = []
envy = []
token = []
[dev-dependencies]
+22
View File
@@ -0,0 +1,22 @@
Copyright (c) 2026 Kun Deng.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
+2 -10
View File
@@ -1,10 +1,2 @@
# textsender-models
A library containing functions, structs, types, and other code for the textsender
project
### Building project
```SHELL
go mod tidy
go mod vendor
```
# schedtxt_models
Models library for the `schedtxt` project.
-8
View File
@@ -1,8 +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
)
-4
View File
@@ -1,4 +0,0 @@
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=
+43
View File
@@ -0,0 +1,43 @@
#[derive(Clone, Debug, Default, serde::Deserialize, serde::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);
}
}
pub fn load_config() -> Result<TwilioConfig, std::io::Error> {
let auth_sid_var = crate::envy::environment::get_env("TWILIO_AUTH_SID");
let service_sid_var = crate::envy::environment::get_env("TWILIO_SERVICE_SID");
let auth_token_var = crate::envy::environment::get_env("TWILIO_AUTH_TOKEN");
let phone_number_var = crate::envy::environment::get_env("TWILIO_PHONE_NUMBER");
if auth_sid_var.value.is_empty()
|| service_sid_var.value.is_empty()
|| auth_token_var.value.is_empty()
|| phone_number_var.value.is_empty()
{
Err(std::io::Error::other("Config not set"))
} else {
Ok(TwilioConfig {
service_sid: service_sid_var.value,
auth_token: auth_token_var.value,
account_sid: auth_sid_var.value,
phone_number: phone_number_var.value,
})
}
}
+1
View File
@@ -0,0 +1 @@
pub mod auxiliary;
+16
View File
@@ -0,0 +1,16 @@
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct Contact {
#[serde(skip_serializing_if = "crate::init::is_uuid_nil")]
pub id: Option<uuid::Uuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nickname: Option<String>,
pub phone_number: String,
#[serde(skip_serializing_if = "crate::init::is_uuid_nil")]
pub user_id: Option<uuid::Uuid>,
}
+106
View File
@@ -0,0 +1,106 @@
pub 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 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 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 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 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 fn get_app_base_api_url() -> crate::envy::EnvVar {
dotenvy::dotenv().ok();
let key = crate::envy::keys::SCHEDTXT_BASE_API_URL;
let value = std::env::var(key).expect(key);
crate::envy::init_envvar(key, &value)
}
pub fn get_app_auth_base_api_url() -> crate::envy::EnvVar {
dotenvy::dotenv().ok();
let key = crate::envy::keys::SCHEDTXT_AUTH_BASE_API_URL;
let value = std::env::var(key).expect(key);
crate::envy::init_envvar(key, &value)
}
pub 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 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 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 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 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 library
pub 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)
}
+53
View File
@@ -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 schedtxt 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 schedtxt api url
pub const SCHEDTXT_BASE_API_URL: &str = "SCHEDTXT_BASE_API_URL";
/// Environment key for textsender auth api url
pub const SCHEDTXT_AUTH_BASE_API_URL: &str = "SCHEDTXT_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 SCHEDTXT_BASE_API_URL: &str =
concatcp!(super::SCHEDTXT_BASE_API_URL, " ", GENERAL_ERROR);
pub const SCHEDTXT_AUTH_BASE_API_URL: &str =
concatcp!(super::SCHEDTXT_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);
}
+57
View File
@@ -0,0 +1,57 @@
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 amount_of_delimiters_found: i32 = {
let mut count = 0;
for v in envvar.value.chars() {
if v == delimiter {
count += 1;
}
}
count
};
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;
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_init_delimiter() {
let delimiter: char = ',';
let value = format!("red,green,white,black");
let mut env_var = super::EnvVar {
key: String::from("COLORS"),
value: value.clone(),
..Default::default()
};
super::init_delimiter(&mut env_var, delimiter);
assert_eq!(value, env_var.value, "Colors do not match");
}
}
+53
View File
@@ -0,0 +1,53 @@
/// 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<Vec<String>, std::io::Error> {
if var.has_delimiter {
let values: Vec<String> = match var
.value
.split(var.delimiter)
.map(|c| c.parse::<String>())
.collect()
{
Ok(v) => v,
Err(err) => {
return Err(std::io::Error::other(err));
}
};
Ok(values)
} else {
Err(std::io::Error::other(
"Environment variable does not have a delimiter",
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delimitize() {
let envvar = crate::envy::EnvVar {
key: "OCEANS".to_string(),
value: "Atlantic|Pacific|Indian|Arctic".to_string(),
has_delimiter: true,
delimiter: '|',
};
let all_values = vec![
"Atlantic".to_string(),
"Pacific".to_string(),
"Indian".to_string(),
"Arctic".to_string(),
];
match delimitize(&envvar) {
Ok(values) => {
assert_eq!(values, all_values, "Failure in delimitizing EnvVar values");
}
Err(err) => {
assert!(false, "Error delimitizing: {err:?}");
}
}
}
}
+15
View File
@@ -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<uuid::Uuid>) -> bool {
match uuid {
Some(id) => id.is_nil(),
None => true,
}
}
}
+21
View File
@@ -0,0 +1,21 @@
/// Message status - Instant
pub const MESSAGE_EVENT_RESPONSE_STATUS_INSTANT: &str = "INSTANT";
/// Message status - Scheduled
pub const MESSAGE_EVENT_RESPONSE_STATUS_SCHEDULED: &str = "SCHEDULED";
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct MessageEventResponse {
pub id: uuid::Uuid,
#[serde(skip_serializing_if = "crate::init::is_uuid_nil")]
pub scheduled_message_event_id: Option<uuid::Uuid>,
/// Stores a json response of the sent message
pub response: serde_json::Value,
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<time::OffsetDateTime>,
}
+13
View File
@@ -0,0 +1,13 @@
pub mod event;
pub mod scheduling;
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct Message {
#[serde(skip_serializing_if = "crate::init::is_uuid_nil")]
pub id: Option<uuid::Uuid>,
pub content: String,
#[serde(skip_serializing_if = "crate::init::is_uuid_nil")]
pub user_id: Option<uuid::Uuid>,
}
+29
View File
@@ -0,0 +1,29 @@
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct ScheduledMessageEvent {
pub id: uuid::Uuid,
pub contact_id: uuid::Uuid,
pub message_id: uuid::Uuid,
pub scheduled_message_id: uuid::Uuid,
#[serde(with = "time::serde::rfc3339::option")]
pub created: Option<time::OffsetDateTime>,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct ScheduledMessage {
pub id: uuid::Uuid,
#[serde(with = "time::serde::rfc3339::option")]
pub scheduled: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub created: Option<time::OffsetDateTime>,
pub status: String,
pub user_id: uuid::Uuid,
}
pub const READY: &str = "READY";
pub const PENDING: &str = "PENDING";
pub const PROCESSING: &str = "PROCESSING";
pub const DONE: &str = "DONE";
+217
View File
@@ -0,0 +1,217 @@
use serde::Deserialize;
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct Claims {
pub user_id: uuid::Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(alias = "iss")]
pub issued: String,
#[serde(alias = "exp")]
#[serde(deserialize_with = "deserialize_i64_from_f64")]
pub expired: i64,
#[serde(alias = "iat")]
#[serde(deserialize_with = "deserialize_i64_from_f64")]
pub issued_at: i64,
}
fn deserialize_i64_from_f64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
let val = f64::deserialize(deserializer)?;
// Handle NaN and infinity cases
if val.is_nan() || val.is_infinite() {
return Err(serde::de::Error::custom("invalid float value"));
}
// Round to nearest integer and convert
let rounded = val.round();
// Check if the rounded value can fit in i64
if rounded < (i64::MIN as f64) || rounded > (i64::MAX as f64) {
Err(serde::de::Error::custom("float out of i64 range"))
} else {
Ok(rounded as i64)
}
}
#[derive(Clone, Debug, Default, serde::Deserialize, serde::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<time::OffsetDateTime> {
Ok(time::OffsetDateTime::now_utc())
}
mod util {
pub fn time_to_std_time(
provided_time: &time::OffsetDateTime,
) -> Result<std::time::SystemTime, std::time::SystemTimeError> {
let converted: std::time::SystemTime = (*provided_time).into();
Ok(converted)
}
}
#[derive(Debug, utoipa::ToSchema)]
pub struct TokenResource {
pub message: String,
pub issuer: String,
pub audiences: Vec<String>,
pub user_id: uuid::Uuid,
}
/// Token type
pub const TOKEN_TYPE: &str = "JWT";
pub struct CreateTokenResult {
pub access_token: String,
pub issued: i64,
pub expires_in: i64,
pub token_issued: time::OffsetDateTime,
}
pub fn create_token(
key: &str,
token_resource: TokenResource,
duration: time::Duration,
) -> Result<CreateTokenResult, josekit::JoseError> {
let mut header = josekit::jws::JwsHeader::new();
header.set_token_type(TOKEN_TYPE);
match init_payload(token_resource, duration) {
Ok((payload, issued, expire)) => {
let signer = match josekit::jws::alg::hmac::HmacJwsAlgorithm::Hs256
.signer_from_bytes(key.as_bytes())
{
Ok(signer) => signer,
Err(err) => {
return Err(err);
}
};
let access_token = match josekit::jwt::encode_with_signer(&payload, &header, &signer) {
Ok(access_token) => access_token,
Err(err) => {
return Err(err);
}
};
Ok(CreateTokenResult {
access_token,
issued: issued.unix_timestamp(),
expires_in: expire.unix_timestamp(),
token_issued: issued,
})
}
Err(e) => Err(josekit::JoseError::InvalidClaim(e.into())),
}
}
fn init_payload(
token_resource: TokenResource,
duration: time::Duration,
) -> Result<
(
josekit::jwt::JwtPayload,
time::OffsetDateTime,
time::OffsetDateTime,
),
josekit::JoseError,
> {
let mut payload = josekit::jwt::JwtPayload::new();
payload.set_subject(token_resource.message);
payload.set_issuer(token_resource.issuer);
payload.set_audience(token_resource.audiences);
if !token_resource.user_id.is_nil() {
match payload.set_claim("user_id", Some(serde_json::json!(token_resource.user_id))) {
Ok(_) => {}
Err(err) => {
return Err(err);
}
}
}
match init_payload_issued(&mut payload, duration) {
Ok((issued, expire)) => Ok((payload, issued, expire)),
Err(err) => Err(err),
}
}
fn init_payload_issued(
payload: &mut josekit::jwt::JwtPayload,
duration: time::Duration,
) -> Result<(time::OffsetDateTime, time::OffsetDateTime), josekit::JoseError> {
match get_issued() {
Ok(issued) => {
let expire = issued + duration;
let issued_at = match util::time_to_std_time(&issued) {
Ok(issued_at) => issued_at,
Err(err) => {
return Err(josekit::JoseError::InvalidClaim(err.into()));
}
};
let expires_at = match util::time_to_std_time(&expire) {
Ok(expires_at) => expires_at,
Err(err) => {
return Err(josekit::JoseError::InvalidClaim(err.into()));
}
};
payload.set_issued_at(&issued_at);
payload.set_expires_at(&expires_at);
Ok((issued, expire))
}
Err(err) => Err(josekit::JoseError::InvalidClaim(err.into())),
}
}
#[cfg(test)]
mod tests {
const TEST_MESSAGE: &str = "Testing for schedtxt";
const TEST_ISSUER: &str = "schedtxt-test";
const TEST_AUDIENCE: &str = "area-test";
const TEST_USER_ID: uuid::Uuid = uuid::uuid!("9ab1c75f-d184-4913-ae99-544b4dcfcb41");
const TEST_KEY: &str = "8342nhf7ycrt4983q7ryfc93w478ryfc3w9487ryfc342w98i7cy";
#[test]
fn test_create_token() {
let token_resource = super::TokenResource {
message: String::from(TEST_MESSAGE),
issuer: String::from(TEST_ISSUER),
audiences: vec![String::from(TEST_AUDIENCE)],
user_id: TEST_USER_ID,
};
let token_duration = time::Duration::minutes(30);
let key = String::from(TEST_KEY);
match super::create_token(&key, token_resource, token_duration) {
Ok(cst) => match time::OffsetDateTime::from_unix_timestamp(cst.issued) {
Ok(d_result) => {
let they_match = {
d_result.year() == cst.token_issued.year()
&& d_result.month() == cst.token_issued.month()
&& d_result.day() == cst.token_issued.day()
&& d_result.hour() == cst.token_issued.hour()
&& d_result.minute() == cst.token_issued.minute()
&& d_result.second() == cst.token_issued.second()
};
assert!(they_match, "Issued times do not match");
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
},
Err(err) => {
assert!(false, "Error: {err:?}");
}
}
}
}
+130
View File
@@ -0,0 +1,130 @@
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct User {
pub id: uuid::Uuid,
pub phone_number: String,
pub username: String,
pub password: String,
pub firstname: String,
pub lastname: String,
#[serde(with = "time::serde::rfc3339::option")]
pub created: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub last_login: Option<time::OffsetDateTime>,
#[serde(skip)]
pub salt_id: uuid::Uuid,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct ServiceUser {
pub id: uuid::Uuid,
pub username: String,
pub passphrase: String,
#[serde(with = "time::serde::rfc3339::option")]
pub created: Option<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub last_login: Option<time::OffsetDateTime>,
#[serde(skip)]
pub salt_id: uuid::Uuid,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct Organization {
pub id: uuid::Uuid,
pub name: String,
#[serde(with = "time::serde::rfc3339::option")]
pub created: Option<time::OffsetDateTime>,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct UserLoginHistory {
pub id: uuid::Uuid,
#[serde(with = "time::serde::rfc3339::option")]
pub login_time: Option<time::OffsetDateTime>,
pub user_id: uuid::Uuid,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, utoipa::ToSchema,
)]
pub struct ServiceUserLoginHistory {
pub id: uuid::Uuid,
#[serde(with = "time::serde::rfc3339::option")]
pub login_time: Option<time::OffsetDateTime>,
pub service_user_id: uuid::Uuid,
}
#[derive(
Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow, 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<time::OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")]
pub last_login: Option<time::OffsetDateTime>,
}
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, sqlx::FromRow)]
pub struct Salt {
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<String, serde_json::Error> {
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"
);
}
}
-20
View File
@@ -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)
}
-14
View File
@@ -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"`
}
-18
View File
@@ -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"`
}
-6
View File
@@ -1,6 +0,0 @@
package event
const (
Message_Event_Response_Status_Instant = "INSTANT"
Message_Event_Response_Status_Scheduled = "SCHEDULED"
)
-11
View File
@@ -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"`
}
-15
View File
@@ -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"`
}
-15
View File
@@ -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"`
}
-8
View File
@@ -1,8 +0,0 @@
package scheduling
const (
Ready = "READY"
Pending = "PENDING"
Processing = "PROCESSING"
Done = "DONE"
)
-24
View File
@@ -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
-42
View File
@@ -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
}
}
-16
View File
@@ -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"`
}
-18
View File
@@ -1,18 +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 *string `json:"first_name,omitempty"`
Lastname *string `json:"last_name,omitempty"`
Created time.Time `json:"created"`
LastLogin *time.Time `json:"last_login,omitempty"`
}