Skip to content

Commit

Permalink
Alex/add caregivers (#25)
Browse files Browse the repository at this point in the history
* Alex/form submission history (#20)

* added history of every form submission

* added history to the router

* created history search

* deleted file i accidentally created

* added tokens and caregiver adding function (#21)

* Add jager to default deployment

* Fix tracing and logging

Least disorganized git repository

* remove unneeded imports

* added form finding for caregivers

* added find caregivers listing, and remove caregiver functionality

* add medication tracking (#22)

* add medication tracking

also some other files got hit by `cargo fmt` 🤷

* forgot `$set`

* improved history and other features

* fixed cookie issue

* re-added medication

---------

Co-authored-by: Chris Graham <Chrisgraham908@gmail.com>
Co-authored-by: LeavesOfFall <131100486+LeavesOfFall@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 29, 2024
1 parent 0e0f052 commit 09dea11
Show file tree
Hide file tree
Showing 31 changed files with 1,067 additions and 97 deletions.
97 changes: 97 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# flyctl launch added from .gitignore
# no compiled
**\debug
**\target
target

# If you commit keys I will personally remove you from this earth
**\.env

**\**\*.rs.bk
**\*.pdb

# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
**\.idea\**\workspace.xml
**\.idea\**\tasks.xml
**\.idea\**\usage.statistics.xml
**\.idea\**\dictionaries
**\.idea\**\shelf

# AWS User-specific
**\.idea\**\aws.xml

# Generated files
**\.idea\**\contentModel.xml

# Sensitive or high-churn files
**\.idea\**\dataSources
**\.idea\**\dataSources.ids
**\.idea\**\dataSources.local.xml
**\.idea\**\sqlDataSources.xml
**\.idea\**\dynamic.xml
**\.idea\**\uiDesigner.xml
**\.idea\**\dbnavigator.xml

# Gradle
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
**\.idea\**\gradle.xml
**\.idea\**\libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
**\cmake-build-*

# Mongo Explorer plugin
**\.idea\**\mongoSettings.xml

# File-based project format
**\*.iws

# IntelliJ
**\out

# mpeltonen/sbt-idea plugin
**\.idea_modules

# JIRA plugin
**\atlassian-ide-plugin.xml

# Cursive Clojure plugin
**\.idea\replstate.xml

# SonarLint plugin
**\.idea\sonarlint

# Crashlytics plugin (for Android Studio and IntelliJ)
**\com_crashlytics_export_strings.xml
**\crashlytics.properties
**\crashlytics-build.properties
**\fabric.properties

# Editor-based Rest Client
**\.idea\httpRequests

# Android studio 3.1+ serialized cache file
**\.idea\caches\build_file_checksums.ser
fly.toml
18 changes: 18 additions & 0 deletions .github/workflows/fly-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/

name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group # optional: ensure only one action runs at a time
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
14 changes: 9 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[features]
default = ["local_log", "journal_log"]
default = ["local_log", "journal_log", "jaeger_tracing"]
local_log = []
journal_log = []
jaeger_tracing = []
Expand All @@ -20,10 +20,13 @@ serde_json = "1.0.120"
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3" }
tracing-journald = { version = "0.3" }
tracing-opentelemetry = "0.27.0"
opentelemetry = { version = "0.26", features = ["trace", "metrics"] }
opentelemetry-stdout = { version = "0.26", features = ["trace", "metrics"] }
opentelemetry_sdk = { version = "0.26.0", features = ["rt-tokio", "trace"] }
opentelemetry-otlp = { version = "0.26.0", features = ["grpc-tonic", "metrics"] }
opentelemetry-semantic-conventions = "0.26.0"

opentelemetry = "0.17.0"
tracing-opentelemetry = "0.17.2"
opentelemetry-jaeger = "0.16.0"
mongodb = "3.0.1"
dotenvy = "0.15.7"
anyhow = { version = "1.0.86", features = [] }
Expand All @@ -37,4 +40,5 @@ rand = "0.8.5"
rust-argon2 = "2.1.0"
tower-cookies = "0.10.0"
jsonwebtoken = "9.3.0"
bson = "2.13.0"
bson = { version = "2.13.0", features = ["chrono-0_4"] }
chrono-humanize = "0.2.3"
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ COPY --from=build /app/app /usr/local/bin/

HEALTHCHECK --start-period=10s --interval=10s --timeout=3s CMD ["bash", "./healthcheck.sh"]

EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/app"]
22 changes: 22 additions & 0 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# fly.toml app configuration file generated for parkinsons-pulse-service on 2024-10-23T11:24:44+13:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'parkinsons-pulse-service'
primary_region = 'syd'

[build]

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']

[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
4 changes: 3 additions & 1 deletion src/app/auth/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use mongodb::bson::oid::ObjectId;
use tower_cookies::Cookies;
use tracing::info;

use crate::app::{auth::utils::decode_jwt, auth::LoginUserBody, models::User};
use crate::app::{auth::utils::decode_jwt, models::User};

#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
pub struct UserClaims {
Expand All @@ -17,6 +17,7 @@ pub struct UserClaims {
pub last_name: String,
// national_health_identifer: String,
pub email_address: String,
pub is_patient: bool,
}

impl TryInto<UserClaims> for User {
Expand All @@ -27,6 +28,7 @@ impl TryInto<UserClaims> for User {
first_name: self.first_name,
last_name: self.last_name,
email_address: self.email_address,
is_patient: self.is_patient,
})
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ pub async fn login(
info!("{:#?}", auth_cookie);
cookies.add(auth_cookie);

StatusCode::OK.into_response()
(StatusCode::OK, Json(user_without_password)).into_response()

}

async fn logout(cookies: Cookies) -> Response {
Expand Down
6 changes: 3 additions & 3 deletions src/app/auth/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ impl<'a> AuthCookieBuilder<'a> {
pub fn new(value: String) -> Self {
Self {
cookie: CookieBuilder::new("auth_token", value)
// .domain(config::get_domain())
.domain(config::get_domain())
.path("/")
.http_only(true)
.same_site(tower_cookies::cookie::SameSite::Lax)
.secure(false),
.same_site(tower_cookies::cookie::SameSite::None)
.secure(config::get_is_production() == "PRODUCTION"),
}
}
#[must_use]
Expand Down
20 changes: 1 addition & 19 deletions src/app/caregiver/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,7 @@ use crate::app::{
},
};

async fn delete_expired_tokens(
collection: &Collection<CaregiverToken>,
) -> mongodb::error::Result<()> {
let now = DateTime::now();

let filter = doc! {
"expired_by": {
"$lt": now
}
};

let delete_result = collection.delete_many(filter).await?;
info!(
"Deleted {} expired caregiver tokens",
delete_result.deleted_count
);

Ok(())
}
use super::delete_expired_tokens;

#[tracing::instrument]
#[axum::debug_handler]
Expand Down
96 changes: 96 additions & 0 deletions src/app/caregiver/find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use axum::{
extract::{Path, State},
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use bson::DateTime;
use chrono::Utc;
use futures::StreamExt;
use mongodb::{
bson::{doc, oid::ObjectId, to_document},
Collection, Cursor, Database,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use tracing::info;

use crate::app::{
auth::{self, middleware::Auth},
models::{
dto::{caregiver::CaregiverTokenPath, form::CreateFormPayload},
CaregiverToken, Form, User,
},
};

#[derive(Serialize, Deserialize)]
struct CaregiverInfo {
id: ObjectId,
first_name: String,
last_name: String,
email_address: String,
}

#[tracing::instrument]
#[axum::debug_handler]
pub async fn find_caregiver(State(db): State<Database>, Auth(auth): Auth) -> Response {
let Some(auth) = auth else {
return (
StatusCode::UNAUTHORIZED,
String::from("You must be signed in to find caregivers"),
)
.into_response();
};

// Perform the aggregation using $lookup
let pipeline = vec![
doc! {
"$match": {
"_id": auth.id
}
},
doc! {
"$lookup": {
"from": "users", // The caregivers collection
"localField": "caregivers", // The field in users that holds caregiver IDs
"foreignField": "_id", // The field in caregivers that matches the user_id
"as": "caregivers_info" // The result will be stored in this field
}
},
doc! {
"$project": {
"caregivers_info._id": 1,
"caregivers_info.first_name": 1, // Only retrieve caregiver ID and name
"caregivers_info.last_name": 1, // Only retrieve caregiver ID and name
"caregivers_info.email_address": 1, // Only retrieve caregiver ID and name
}
},
];

let Ok(mut cursor): Result<Cursor<bson::document::Document>, mongodb::error::Error> =
db.collection::<User>("users").aggregate(pipeline).await
else {
return (
StatusCode::INTERNAL_SERVER_ERROR,
String::from("could not find caregivers"),
)
.into_response();
};

let Some(Ok(caregiver)) = cursor.next().await else {
return (
StatusCode::INTERNAL_SERVER_ERROR,
String::from("could not find caregiver list"),
)
.into_response();
};

let Some(caregiver_info) = caregiver.get("caregivers_info") else {
return (
StatusCode::INTERNAL_SERVER_ERROR,
String::from("cannot extract caregiver information"),
)
.into_response();
};
(StatusCode::OK, Json(caregiver_info)).into_response()
}
4 changes: 3 additions & 1 deletion src/app/caregiver/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ use crate::app::{
models::{dto::form::CreateFormPayload, CaregiverToken, Form, User},
};

use super::delete_expired_tokens;

#[tracing::instrument]
#[axum::debug_handler]
pub async fn generate(State(db): State<Database>, Auth(auth): Auth) -> Response {
let Some(auth) = auth else {
return (
StatusCode::UNAUTHORIZED,
String::from("You must be signed in to create a form"),
String::from("You must be signed in to generate a caregiver token"),
)
.into_response();
};
Expand Down
Loading

0 comments on commit 09dea11

Please sign in to comment.