From 09b1a26118c32ab7c2c627c3f7be336bcf33363c Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 10:24:15 +0700 Subject: [PATCH 1/7] Update shebangs to use /bin/bash and enhance Windows detection in portforward.sh --- canister/scripts/deployments/reinstall-staging.sh | 2 +- canister/scripts/upgrades/staging/upgrade-emr.sh | 2 +- .../scripts/upgrades/staging/upgrade-patient.sh | 2 +- .../scripts/upgrades/staging/upgrade-provider.sh | 2 +- canister/scripts/utils/add_controller.sh | 2 +- canister/scripts/utils/add_controller_all.sh | 2 +- canister/scripts/utils/add_metrics_collector.sh | 2 +- canister/scripts/utils/add_provider.sh | 2 +- canister/scripts/utils/log.sh | 2 +- canister/setup.sh | 2 +- portforward.sh | 15 ++++++++++++++- 11 files changed, 24 insertions(+), 11 deletions(-) diff --git a/canister/scripts/deployments/reinstall-staging.sh b/canister/scripts/deployments/reinstall-staging.sh index b74b33f2..9cfce089 100755 --- a/canister/scripts/deployments/reinstall-staging.sh +++ b/canister/scripts/deployments/reinstall-staging.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash # Get the directory of the current script SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" diff --git a/canister/scripts/upgrades/staging/upgrade-emr.sh b/canister/scripts/upgrades/staging/upgrade-emr.sh index 6fee6cde..20d5d1f5 100644 --- a/canister/scripts/upgrades/staging/upgrade-emr.sh +++ b/canister/scripts/upgrades/staging/upgrade-emr.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) bash $root/canister/build.sh emr_registry diff --git a/canister/scripts/upgrades/staging/upgrade-patient.sh b/canister/scripts/upgrades/staging/upgrade-patient.sh index b67a2a9c..ff745303 100644 --- a/canister/scripts/upgrades/staging/upgrade-patient.sh +++ b/canister/scripts/upgrades/staging/upgrade-patient.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) bash $root/canister/build.sh patient_registry diff --git a/canister/scripts/upgrades/staging/upgrade-provider.sh b/canister/scripts/upgrades/staging/upgrade-provider.sh index f0d377a2..94d6cd3b 100644 --- a/canister/scripts/upgrades/staging/upgrade-provider.sh +++ b/canister/scripts/upgrades/staging/upgrade-provider.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) bash $root/canister/build.sh provider_registry diff --git a/canister/scripts/utils/add_controller.sh b/canister/scripts/utils/add_controller.sh index 4b20eda3..032ed117 100755 --- a/canister/scripts/utils/add_controller.sh +++ b/canister/scripts/utils/add_controller.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) cd $root/canister canister=$1 diff --git a/canister/scripts/utils/add_controller_all.sh b/canister/scripts/utils/add_controller_all.sh index 463e3d7c..e6eaabd7 100755 --- a/canister/scripts/utils/add_controller_all.sh +++ b/canister/scripts/utils/add_controller_all.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) cd $root/canister controller=$1 diff --git a/canister/scripts/utils/add_metrics_collector.sh b/canister/scripts/utils/add_metrics_collector.sh index 853f73fd..575883d1 100644 --- a/canister/scripts/utils/add_metrics_collector.sh +++ b/canister/scripts/utils/add_metrics_collector.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash root=$(git rev-parse --show-toplevel) cd $root/canister canister=$1 diff --git a/canister/scripts/utils/add_provider.sh b/canister/scripts/utils/add_provider.sh index 9a92eb2b..42dc5793 100644 --- a/canister/scripts/utils/add_provider.sh +++ b/canister/scripts/utils/add_provider.sh @@ -1,5 +1,5 @@ +#!/bin/bash # TODO -#! bash root=$(git rev-parse --show-toplevel) cd $root/canister caller=$1 diff --git a/canister/scripts/utils/log.sh b/canister/scripts/utils/log.sh index 5fb649fb..48a639c1 100755 --- a/canister/scripts/utils/log.sh +++ b/canister/scripts/utils/log.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash # colors RED='\033[0;31m' diff --git a/canister/setup.sh b/canister/setup.sh index b9c702e9..fb7e9139 100755 --- a/canister/setup.sh +++ b/canister/setup.sh @@ -1,4 +1,4 @@ -#! bash +#!/bin/bash ROOT=$(git rev-parse --show-toplevel)/canister diff --git a/portforward.sh b/portforward.sh index 17e8a26d..371919d7 100755 --- a/portforward.sh +++ b/portforward.sh @@ -13,8 +13,21 @@ VPS_PORT="4943" SSH_USER="guest" SSH_PASSWORD="pw" -# Function to check if running on Windows +# Function to check if running on Windows (excluding WSL) is_windows() { + # check if running in WSL - if so, treat as Unix/Linux + if [[ -f /proc/version ]] && grep -q Microsoft /proc/version; then + return 1 + fi + # check if running in WSL2 + if [[ -f /proc/version ]] && grep -q WSL /proc/version; then + return 1 + fi + # check for WSL environment variable + if [[ -n "$WSL_DISTRO_NAME" ]] || [[ -n "$WSL_INTEROP" ]]; then + return 1 + fi + # check for native Windows environments [[ "$(uname)" =~ "MINGW"|"MSYS"|"CYGWIN" ]] || [[ -n "$WINDIR" ]] } From dfaba5e3c0b059c4297dd65973be6daaacb4fcdb Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 10:36:47 +0700 Subject: [PATCH 2/7] line-endings --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8964f66d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# ensure shell scripts always use LF line endings +*.sh text eol=lf + +# ensure other text files use LF as well +*.bash text eol=lf + From 5e5302a76772505250d035dd34b2cf16f7077ec0 Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 10:48:51 +0700 Subject: [PATCH 3/7] script --- .gitattributes | 4 +--- fix-line-endings.sh | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100755 fix-line-endings.sh diff --git a/.gitattributes b/.gitattributes index 8964f66d..0a21890c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,4 @@ -# ensure shell scripts always use LF line endings +# ensure shell scripts always use LF line endings (WSL compatible) *.sh text eol=lf - -# ensure other text files use LF as well *.bash text eol=lf diff --git a/fix-line-endings.sh b/fix-line-endings.sh new file mode 100755 index 00000000..036efe9e --- /dev/null +++ b/fix-line-endings.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# fix shell script line endings for WSL compatibility +# this script converts all .sh files from CRLF to LF +# works on both macOS and Linux/WSL + +echo "fixing line endings in shell scripts..." + +# detect OS and use appropriate sed command +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS requires backup extension (empty string = no backup) + find . -name "*.sh" -type f -exec sed -i '' 's/\r$//' {} \; +else + # Linux/WSL + find . -name "*.sh" -type f -exec sed -i 's/\r$//' {} \; +fi + +echo "done! all shell scripts now have LF line endings." + From e776735e64688280aff761ed9049fc5ad0de513e Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 10:59:31 +0700 Subject: [PATCH 4/7] fix(local.sh): improve process termination for existing services on FE_PORT to be cross-platform compatible feat(local.sh): add wait_for_dfx function to ensure dfx is ready before proceeding with deployments --- canister/scripts/deployments/local.sh | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/canister/scripts/deployments/local.sh b/canister/scripts/deployments/local.sh index 1bedf8d3..e9ca2cb5 100755 --- a/canister/scripts/deployments/local.sh +++ b/canister/scripts/deployments/local.sh @@ -14,12 +14,33 @@ cd $root/canister bash $root/canister/setup.sh # This script deploys the canister locally. FE_PORT=4943 -lsof -i tcp:${FE_PORT} | awk 'NR!=1 {print $2}' | xargs kill || true +# kill any existing processes on FE_PORT (cross-platform compatible) +pids=$(lsof -i tcp:${FE_PORT} 2>/dev/null | awk 'NR!=1 {print $2}') +if [ -n "$pids" ]; then + echo "$pids" | xargs kill 2>/dev/null || true +fi + +# wait for dfx to be ready (cross-platform polling) +wait_for_dfx() { + echo -e "${YELLOW}[WAIT]${NC} Waiting for dfx to start..." + max_attempts=60 + attempt=0 + while [ $attempt -lt $max_attempts ]; do + if dfx ping >/dev/null 2>&1; then + return 0 + fi + sleep 1 + attempt=$((attempt + 1)) + done + echo -e "${RED}[ERROR]${NC} dfx failed to start within $max_attempts seconds" + return 1 +} # Check if --background flag is passed if [[ "$1" == "--background" ]]; then echo -e "${BLUE}[INFO]${NC} Starting dfx in background mode..." dfx start --background + wait_for_dfx || exit 1 else echo -e "${BLUE}[INFO]${NC} Starting dfx in concurrent mode..." # Start dfx in the background but keep output visible @@ -28,9 +49,7 @@ else # Store the PID so we can terminate it later if needed echo $DFX_PID > /tmp/dfx.pid - # Wait for dfx to initialize - echo -e "${YELLOW}[WAIT]${NC} Waiting for dfx to start..." - sleep 5 + wait_for_dfx || exit 1 fi echo -e "${GREEN}[INFO]${NC} Installing canisters..." From 6a8387afe692465bb807a1e7bf0f03752d1d7134 Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 11:29:40 +0700 Subject: [PATCH 5/7] pocketic-checks --- canister/scripts/deployments/local.sh | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/canister/scripts/deployments/local.sh b/canister/scripts/deployments/local.sh index e9ca2cb5..3f0941f8 100755 --- a/canister/scripts/deployments/local.sh +++ b/canister/scripts/deployments/local.sh @@ -20,6 +20,40 @@ if [ -n "$pids" ]; then echo "$pids" | xargs kill 2>/dev/null || true fi +# check if PocketIC is installed (needed for integration tests) +check_pocketic() { + if command -v pocket-ic &>/dev/null; then + echo -e "${GREEN}[INFO]${NC} PocketIC found: $(which pocket-ic)" + return 0 + else + echo -e "${YELLOW}[WARNING]${NC} PocketIC not found in PATH" + echo -e "${YELLOW}[INFO]${NC} PocketIC is optional for local development but required for integration tests" + echo -e "${YELLOW}[INFO]${NC} Install from: https://github.com/dfinity/pocketic" + return 1 + fi +} + +# cleanup any existing PocketIC processes (cross-platform compatible) +cleanup_pocketic() { + # find and kill any running pocket-ic processes + if command -v pgrep &>/dev/null; then + # use pgrep if available (Linux/WSL) + pocketic_pids=$(pgrep -f "pocket-ic" 2>/dev/null) + else + # fallback for macOS (no pgrep) + pocketic_pids=$(ps aux | grep -i "[p]ocket-ic" | awk '{print $2}' 2>/dev/null) + fi + + if [ -n "$pocketic_pids" ]; then + echo -e "${BLUE}[INFO]${NC} Cleaning up existing PocketIC processes..." + echo "$pocketic_pids" | xargs kill 2>/dev/null || true + sleep 1 + fi +} + +check_pocketic +cleanup_pocketic + # wait for dfx to be ready (cross-platform polling) wait_for_dfx() { echo -e "${YELLOW}[WAIT]${NC} Waiting for dfx to start..." @@ -44,6 +78,7 @@ if [[ "$1" == "--background" ]]; then else echo -e "${BLUE}[INFO]${NC} Starting dfx in concurrent mode..." # Start dfx in the background but keep output visible + # show all output including PocketIC errors for debugging (dfx start 2>&1 | sed 's/^/[CANISTER] /') & DFX_PID=$! # Store the PID so we can terminate it later if needed From 8f409f32834f3eed0b8bb41dcaa8035eef791e3d Mon Sep 17 00:00:00 2001 From: mylo Date: Tue, 4 Nov 2025 14:19:02 +0700 Subject: [PATCH 6/7] chore: update .gitignore to exclude pocket-ic and improve README formatting for better clarity and organization fix: ensure bash is used for local deployment script and add checks for dependencies and canister existence refactor: improve error handling and output in build scripts and canister setup style: apply consistent formatting and spacing across various Rust files for better readability feat: enhance integration testing instructions and clarify setup steps in README fix: correct comments and improve code readability in Rust source files --- .gitignore | 1 + README.md | 24 +- canister/build.sh | 50 +- canister/scripts/deployments/local.sh | 35 +- canister/setup.sh | 12 +- canister/src/patient_registry/build.rs | 23 +- canister/src/patient_registry/src/config.rs | 11 +- canister/src/patient_registry/src/lib.rs | 67 ++- canister/src/patient_registry/src/registry.rs | 46 +- canister/src/provider_registry/build.rs | 22 +- canister/src/provider_registry/src/config.rs | 11 +- .../src/provider_registry/src/registry.rs | 449 +++++++++--------- 12 files changed, 457 insertions(+), 294 deletions(-) diff --git a/.gitignore b/.gitignore index 3ac223cc..d97f495a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ misc .DS_Store +pocket-ic diff --git a/README.md b/README.md index 4b53ae59..f360cd8a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Platform](https://img.shields.io/badge/Platform-Healthcare-red.svg?style=flat)](http://medblock.id/) ## 📘 Overview + Medblock is an Electronic Medical Record (EMR) registry system that leverages the power of the Internet Computer to provide secure and efficient healthcare solutions. The project is organized into three main canisters, each designed to fulfill specific responsibilities: 1. **Patient Registry**: Manages comprehensive patient information and records, ensuring easy access by authorized users. @@ -11,12 +12,16 @@ Medblock is an Electronic Medical Record (EMR) registry system that leverages th 3. **Provider Registry**: Handles essential information related to healthcare providers, including their credentials and specialties. ## 🏗️ Architecture Diagram + The architectural diagram below provides a visual representation of how the Medblock project is structured and how its components interact. This illustration assists in understanding the relationships and data flow between the Mobile App, various canisters, and the dashboards. ![](final_demo/pwa/public/arsitektur.png) ## 📋 Getting Started + ### Prerequisites + Ensure you have the following tools installed to work with the Medblock a project: + - **Rust `1.27.0`**: A systems programming language focused on safety and performance. - **Next.js `14.2.6`**: A React framework for building server-rendered applications. - **Tailwind CSS `3.4.1`**: A utility-first CSS framework for styling. @@ -41,7 +46,8 @@ Before diving into development, install the essential tools on your system: ``` ### Initial Setup -To set up the environment, execute the `setup.sh` script. This script automates the following tasks: + +To set up the environment, execute the `./canister/setup.sh` script. This script automates the following tasks: - Verifies the installation of the required tools. - Creates unique canister IDs for each registry. @@ -51,9 +57,11 @@ To set up the environment, execute the `setup.sh` script. This script automates > **Note**: The setup may take a few minutes depending on your system specifications. ### Development Workflow + When developing a feature for a canister, use the build.sh script. This script automatically regenerates the candid interface for the canister you're working on and recompiles the EMR registry canister, as the other two canisters depend on it. If you've already run the setup script, this step should be quick. Use the --all flag with the script to rebuild all canisters simultaneously. ### Deploy Locally + To deploy the canisters locally, navigate to the `scripts/deployments` directory and run the `local.sh` script: ```bash @@ -72,6 +80,7 @@ The `local.sh` script performs the following actions: ### Running Tests #### Unit Tests + To run unit tests, compile and execute them using the --release flag to ensure optimal performance: ```bash @@ -80,10 +89,12 @@ cargo test --release ``` #### Integration Tests + For integration testing, follow these steps: 1. Install **Pocket-IC** and follow the installation guidelines. 2. Build all canisters with the `--all` flag: + ```bash ./build.sh --all ``` @@ -97,7 +108,9 @@ For integration testing, follow these steps: ## Testing ### Integration Tests + To run the integration tests for the canister: + ```bash # Run all integration tests in release mode cargo integration-test @@ -113,22 +126,26 @@ cargo integration-test -- --nocapture ``` The integration tests are located in `canister/tests/integration-tests/` and test the full functionality of the canisters including: + - Group management - EMR access control - Patient registration - Provider interactions ### Unit Tests + For unit tests, you can use the standard cargo test command in each canister directory: + ```bash cd canister/src/patient_registry cargo test ``` ### Private VPS Node + We have setup a private VPS node to act as a psuedo-staging branch for the canisters. -You must have sshpass installed first! +You must have sshpass installed first! ```bash sudo apt-get install sshpass # linux @@ -136,8 +153,9 @@ brew install sshpass # mac ``` To connect to the node instead of local or mainnet, you can use the following command: + ```bash ./portforward.sh ``` -This will forward the port to the node hosted in the VPS and you can interact with the node as if it were local. \ No newline at end of file +This will forward the port to the node hosted in the VPS and you can interact with the node as if it were local. diff --git a/canister/build.sh b/canister/build.sh index 379ce510..e90d8f7c 100755 --- a/canister/build.sh +++ b/canister/build.sh @@ -52,23 +52,49 @@ build_canister() { local start_time=$(date +%s.%N) log_process "Building ${MAGENTA}$canister_name${NC} canister..." - dfx build $canister_name >/dev/null 2>&1 + # ensure we're in the canister directory (cross-platform compatible) + cd "$root" || { + echo -e "${RED}[ERROR]${NC} Failed to change to directory: $root" + return 1 + } + + # show build output on failure, hide on success + if ! dfx build $canister_name 2>&1; then + echo -e "${RED}[ERROR]${NC} Failed to build ${MAGENTA}$canister_name${NC} canister" + echo -e "${YELLOW}[INFO]${NC} Make sure you're running from the canister directory and dfx is running" + return 1 + fi + + # check if wasm file exists before proceeding + if [ ! -f "$wasm_path" ]; then + echo -e "${RED}[ERROR]${NC} WASM file not found: $wasm_path" + echo -e "${YELLOW}[INFO]${NC} Build may have failed. Check dfx build output above." + return 1 + fi log_info "Inserting placeholder candid..." echo "$dummy_did" >$did_path log_process "Extracting candid from wasm..." - candid-extractor $wasm_path >$did_path + if ! candid-extractor $wasm_path >$did_path 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} Failed to extract candid from $wasm_path" + return 1 + fi # add candid metadata and shrink wasm log_process "Processing WASM file..." - ic-wasm "$wasm_path" \ + if ! ic-wasm "$wasm_path" \ -o "$wasm_path" \ - metadata candid:service -v public -f $did_path + metadata candid:service -v public -f $did_path 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} Failed to add candid metadata to WASM" + return 1 + fi - ic-wasm "$wasm_path" \ + if ! ic-wasm "$wasm_path" \ -o "$wasm_path" \ - shrink + shrink 2>/dev/null; then + echo -e "${YELLOW}[WARNING]${NC} Failed to shrink WASM (non-fatal)" + fi local end_time=$(date +%s.%N) local build_time=$(echo "$end_time - $start_time" | bc) @@ -99,13 +125,17 @@ if [ -z "$canister" ]; then exit 0 fi -cd $root +# ensure we're in the canister directory from the start +cd "$root" || { + echo -e "${RED}[ERROR]${NC} Failed to change to directory: $root" + exit 1 +} total_start_time=$(date +%s.%N) # build EMR registry first since other canisters depend on it if [ "$canister" == "$emr_canister" ]; then - build_canister $emr_canister + build_canister $emr_canister || exit 1 total_end_time=$(date +%s.%N) total_time=$(echo "$total_end_time - $total_start_time" | bc) log_success "EMR Registry build completed in ${GREEN}$(printf "%.2f" $total_time)s${NC}" @@ -114,10 +144,10 @@ fi # for other canisters, build EMR registry first then build target canister log_warning "EMR Registry is a dependency, building it first..." -build_canister $emr_canister +build_canister $emr_canister || exit 1 log_info "Building target canister..." -build_canister $canister +build_canister $canister || exit 1 total_end_time=$(date +%s.%N) total_time=$(echo "$total_end_time - $total_start_time" | bc) diff --git a/canister/scripts/deployments/local.sh b/canister/scripts/deployments/local.sh index 3f0941f8..d129a7aa 100755 --- a/canister/scripts/deployments/local.sh +++ b/canister/scripts/deployments/local.sh @@ -1,5 +1,12 @@ #!/bin/bash +# ensure script is run with bash (cross-platform compatible) +if [ -z "$BASH_VERSION" ]; then + echo "Error: This script must be run with bash, not sh" + echo "Usage: bash $0" + exit 1 +fi + # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' @@ -14,16 +21,25 @@ cd $root/canister bash $root/canister/setup.sh # This script deploys the canister locally. FE_PORT=4943 + +# stop dfx first to ensure clean state (cross-platform compatible) +echo -e "${BLUE}[INFO]${NC} Stopping any existing dfx instances..." +dfx stop >/dev/null 2>&1 || true + # kill any existing processes on FE_PORT (cross-platform compatible) pids=$(lsof -i tcp:${FE_PORT} 2>/dev/null | awk 'NR!=1 {print $2}') if [ -n "$pids" ]; then echo "$pids" | xargs kill 2>/dev/null || true + sleep 2 fi # check if PocketIC is installed (needed for integration tests) check_pocketic() { - if command -v pocket-ic &>/dev/null; then - echo -e "${GREEN}[INFO]${NC} PocketIC found: $(which pocket-ic)" + local pocketic_path + # use which to find exact path, ensuring it's actually pocket-ic binary + pocketic_path=$(which pocket-ic 2>/dev/null) + if [ -n "$pocketic_path" ] && [ -x "$pocketic_path" ] && [ "$(basename "$pocketic_path")" = "pocket-ic" ]; then + echo -e "${GREEN}[INFO]${NC} PocketIC found: $pocketic_path" return 0 else echo -e "${YELLOW}[WARNING]${NC} PocketIC not found in PATH" @@ -51,6 +67,14 @@ cleanup_pocketic() { fi } +# ensure canisters exist before installing (cross-platform compatible) +ensure_canisters_exist() { + echo -e "${BLUE}[INFO]${NC} Ensuring canisters are created..." + dfx canister create emr_registry 2>/dev/null || true + dfx canister create patient_registry 2>/dev/null || true + dfx canister create provider_registry 2>/dev/null || true +} + check_pocketic cleanup_pocketic @@ -78,8 +102,8 @@ if [[ "$1" == "--background" ]]; then else echo -e "${BLUE}[INFO]${NC} Starting dfx in concurrent mode..." # Start dfx in the background but keep output visible - # show all output including PocketIC errors for debugging - (dfx start 2>&1 | sed 's/^/[CANISTER] /') & + # filter out harmless PocketIC panic messages (cross-platform compatible) + (dfx start 2>&1 | grep -v -E "(panicked at|SendError|Error from launcher process.*exited due to signal)" | sed 's/^/[CANISTER] /') & DFX_PID=$! # Store the PID so we can terminate it later if needed echo $DFX_PID > /tmp/dfx.pid @@ -87,6 +111,9 @@ else wait_for_dfx || exit 1 fi +# ensure canisters exist before installing +ensure_canisters_exist + echo -e "${GREEN}[INFO]${NC} Installing canisters..." dfx canister install emr_registry --wasm $root/canister/target/wasm32-unknown-unknown/release/emr_registry.wasm --mode=install -y diff --git a/canister/setup.sh b/canister/setup.sh index fb7e9139..cd00d75c 100755 --- a/canister/setup.sh +++ b/canister/setup.sh @@ -2,6 +2,9 @@ ROOT=$(git rev-parse --show-toplevel)/canister +# change to canister directory for dfx commands (cross-platform compatible) +cd $ROOT + echo "checking required dependencies" if ! command -v dfx &>/dev/null; then @@ -29,13 +32,16 @@ dfx canister create patient_registry dfx canister create emr_registry echo "building canisters for the first time" +echo "note: build order: emr_registry -> patient_registry -> provider_registry" echo "building emr registry" -bash $ROOT/build.sh emr_registry +bash $ROOT/build.sh emr_registry || exit 1 + echo "building patient registry" -bash $ROOT/build.sh patient_registry +bash $ROOT/build.sh patient_registry || exit 1 + echo "building provider registry" -bash $ROOT/build.sh provider_registry +bash $ROOT/build.sh provider_registry || exit 1 echo "stopping ic replica" dfx stop diff --git a/canister/src/patient_registry/build.rs b/canister/src/patient_registry/build.rs index 26427760..40d9c08b 100644 --- a/canister/src/patient_registry/build.rs +++ b/canister/src/patient_registry/build.rs @@ -1,10 +1,9 @@ -use ic_cdk_bindgen::{ Builder, Config }; -use std::{ panic::catch_unwind, path::PathBuf }; +use ic_cdk_bindgen::{Builder, Config}; +use std::{panic::catch_unwind, path::PathBuf}; fn get_workspace_root() -> PathBuf { - let manifest_dir = PathBuf::from( - std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir") - ); + let manifest_dir = + PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir")); for anc in manifest_dir.ancestors() { if anc.file_name().unwrap() == "canister" { @@ -17,13 +16,19 @@ fn get_workspace_root() -> PathBuf { // workaround for setting provider registry candid path because we run into circular issues. fn hardcode_set_provider_registry_candid_path() { - std::env::set_var("CANISTER_CANDID_PATH_PROVIDER_REGISTRY", "src/provider_registry/candid.did"); + std::env::set_var( + "CANISTER_CANDID_PATH_PROVIDER_REGISTRY", + "src/provider_registry/candid.did", + ); } fn main() { println!("cargo:rerun-if-changed=NULL"); - let link_flag = std::env::var("LINK").unwrap_or("true".to_string()).parse::().unwrap(); + let link_flag = std::env::var("LINK") + .unwrap_or("true".to_string()) + .parse::() + .unwrap(); // workaround to determine if this is invoked by dfx as dfx automatically inject this env var let candid_path_env = std::env::var("CANISTER_CANDID_PATH_EMR_REGISTRY").is_ok(); @@ -62,5 +67,7 @@ fn build_declaration() { builder.add(config); } - builder.build(Some(get_workspace_root().join("src/patient_registry/src/declarations"))); + builder.build(Some( + get_workspace_root().join("src/patient_registry/src/declarations"), + )); } diff --git a/canister/src/patient_registry/src/config.rs b/canister/src/patient_registry/src/config.rs index c862701c..94f27fb5 100644 --- a/canister/src/patient_registry/src/config.rs +++ b/canister/src/patient_registry/src/config.rs @@ -1,10 +1,8 @@ -use candid::{ CandidType, Principal }; +use candid::{CandidType, Principal}; use canister_common::{ - impl_max_size, - impl_mem_bound, - metrics, + impl_max_size, impl_mem_bound, metrics, mmgr::MemoryManager, - stable::{ Candid, Memory, Stable, ToStable }, + stable::{Candid, Memory, Stable, ToStable}, statistics::traits::Metrics, }; use ic_stable_structures::Cell; @@ -153,7 +151,8 @@ impl CanisterConfig { } pub fn remove_authorized_metrics_collector(&mut self, collector: Principal) { - self.authorized_metrics_collectors.retain(|c| c != &collector); + self.authorized_metrics_collectors + .retain(|c| c != &collector); } pub fn is_authorized_metrics_collector(&self, collector: &Principal) -> bool { diff --git a/canister/src/patient_registry/src/lib.rs b/canister/src/patient_registry/src/lib.rs index 8b899598..22df2520 100644 --- a/canister/src/patient_registry/src/lib.rs +++ b/canister/src/patient_registry/src/lib.rs @@ -1,7 +1,22 @@ use std::{borrow::BorrowMut, cell::RefCell, str::FromStr, time::Duration}; use api::{ - AddGroupMemberRequest, AuthorizedCallerRequest, BindAdminRequest, CheckNikRequest, ClaimConsentRequest, ClaimConsentResponse, ConsentListResponse, CreateConsentForGroupRequest, CreateConsentForGroupResponse, CreateConsentResponse, CreateGroupRequest, CreateGroupResponse, EmrHeaderWithStatus, EmrListConsentRequest, EmrListConsentResponse, EmrListPatientRequest, EmrListPatientResponse, FinishSessionRequest, GetGroupDetailsNoPaginatedRequest, GetGroupDetailsRequest, GetGroupDetailsResponse, GetPatientInfoBySessionRequest, GetPatientInfoResponse, GetUserGroupsResponse, GrantGroupAccessRequest, GroupDetail, IsConsentClaimedRequest, IsConsentClaimedResponse, IssueRequest, LeaveGroupRequest, LogResponse, PatientListAdminResponse, PatientListResponse, PatientWithNik, PatientWithNikAndSession, PingResult, ReadEmrByIdRequest, ReadEmrSessionRequest, ReadGroupMembersEmrInfoRequest, RegisterPatientRequest, RegisterPatientResponse, RegisterPatientStatus, RevokeConsentRequest, RevokeGroupAccessRequest, SearchPatientAdminResponse, SearchPatientRequest, SearchPatientResponse, UpdateEmrRegistryRequest, UpdateInitialPatientInfoRequest, UpdateKycStatusRequest, UpdateKycStatusResponse, UpdatePatientInfoRequest, UpdateRequest, ViewGroupMemberEmrInformationRequest + AddGroupMemberRequest, AuthorizedCallerRequest, BindAdminRequest, CheckNikRequest, + ClaimConsentRequest, ClaimConsentResponse, ConsentListResponse, CreateConsentForGroupRequest, + CreateConsentForGroupResponse, CreateConsentResponse, CreateGroupRequest, CreateGroupResponse, + EmrHeaderWithStatus, EmrListConsentRequest, EmrListConsentResponse, EmrListPatientRequest, + EmrListPatientResponse, FinishSessionRequest, GetGroupDetailsNoPaginatedRequest, + GetGroupDetailsRequest, GetGroupDetailsResponse, GetPatientInfoBySessionRequest, + GetPatientInfoResponse, GetUserGroupsResponse, GrantGroupAccessRequest, GroupDetail, + IsConsentClaimedRequest, IsConsentClaimedResponse, IssueRequest, LeaveGroupRequest, + LogResponse, PatientListAdminResponse, PatientListResponse, PatientWithNik, + PatientWithNikAndSession, PingResult, ReadEmrByIdRequest, ReadEmrSessionRequest, + ReadGroupMembersEmrInfoRequest, RegisterPatientRequest, RegisterPatientResponse, + RegisterPatientStatus, RevokeConsentRequest, RevokeGroupAccessRequest, + SearchPatientAdminResponse, SearchPatientRequest, SearchPatientResponse, + UpdateEmrRegistryRequest, UpdateInitialPatientInfoRequest, UpdateKycStatusRequest, + UpdateKycStatusResponse, UpdatePatientInfoRequest, UpdateRequest, + ViewGroupMemberEmrInformationRequest, }; use candid::{Decode, Encode, Principal}; use canister_common::{ @@ -1202,12 +1217,19 @@ fn revoke_group_access(req: RevokeGroupAccessRequest) -> Result<(), String> { .map_err(|_| "Invalid revokee NIK format".to_string())?; // verify both users are in the same group - let group = with_state(|s| s.registry.group_map.get_group(req.group_id.clone())) - .ok_or_else(|| format!("[ERR_GROUP_NOT_FOUND] Group {} does not exist", req.group_id))?; + let group = + with_state(|s| s.registry.group_map.get_group(req.group_id.clone())).ok_or_else(|| { + format!( + "[ERR_GROUP_NOT_FOUND] Group {} does not exist", + req.group_id + ) + })?; // verify both users are members of the group if !group.members.contains(&granter_nik) || !group.members.contains(&revokee_nik) { - return Err("[ERR_NOT_GROUP_MEMBERS] One or both users are not members of this group".to_string()); + return Err( + "[ERR_NOT_GROUP_MEMBERS] One or both users are not members of this group".to_string(), + ); } // Check if access exists before trying to revoke @@ -1219,8 +1241,12 @@ fn revoke_group_access(req: RevokeGroupAccessRequest) -> Result<(), String> { if !access_exists { // If no access exists, consider it a success since the end state is what was desired - log!("No access existed to revoke between granter {} and revokee {} in group {}", - granter_nik, revokee_nik, req.group_id); + log!( + "No access existed to revoke between granter {} and revokee {} in group {}", + granter_nik, + revokee_nik, + req.group_id + ); return Ok(()); } @@ -1595,7 +1621,7 @@ async fn read_group_members_emr_info( ) -> Result { let caller = verified_caller().unwrap(); let viewer_nik = with_state(|s| s.registry.owner_map.get_nik(&caller).unwrap()).into_inner(); - + // parse member NIK from string let member_nik = NIK::from_str(&req.member_nik) .map_err(|_| "[ERR_INVALID_NIK] Invalid member NIK format")?; @@ -1606,13 +1632,20 @@ async fn read_group_members_emr_info( // verify both users are members of the group if !group.members.contains(&viewer_nik) || !group.members.contains(&member_nik) { - return Err("[ERR_NOT_GROUP_MEMBERS] One or both users are not members of the group".to_string()); + return Err( + "[ERR_NOT_GROUP_MEMBERS] One or both users are not members of the group".to_string(), + ); } // verify access has been granted for this specific group let has_access = with_state(|s| { - s.registry.group_access_map.has_access(&member_nik, &viewer_nik) - && s.registry.group_access_map.get_access_group(&member_nik, &viewer_nik) == Some(req.group_id) + s.registry + .group_access_map + .has_access(&member_nik, &viewer_nik) + && s.registry + .group_access_map + .get_access_group(&member_nik, &viewer_nik) + == Some(req.group_id) }); if !has_access { @@ -1629,9 +1662,17 @@ async fn read_group_members_emr_info( emr_id: req.emr_id, registry_id: req.registry_id, }; - let args = with_state(|s| s.registry.construct_args_read_emr(sub_args, &member_principal)) - .map_err(|e| format!("[ERR_CONSTRUCT_ARGS] Failed to construct EMR read args: {:?}", e))?; - + let args = with_state(|s| { + s.registry + .construct_args_read_emr(sub_args, &member_principal) + }) + .map_err(|e| { + format!( + "[ERR_CONSTRUCT_ARGS] Failed to construct EMR read args: {:?}", + e + ) + })?; + Ok(PatientRegistry::do_call_read_emr(args, registry).await) } diff --git a/canister/src/patient_registry/src/registry.rs b/canister/src/patient_registry/src/registry.rs index 5d560fe8..bc3486ae 100644 --- a/canister/src/patient_registry/src/registry.rs +++ b/canister/src/patient_registry/src/registry.rs @@ -311,9 +311,14 @@ impl GroupAccessMap { Ok(()) } - pub fn revoke_access_for_group(&mut self, granter: NIK, revokee: NIK, group_id: GroupId) -> Result<(), String> { + pub fn revoke_access_for_group( + &mut self, + granter: NIK, + revokee: NIK, + group_id: GroupId, + ) -> Result<(), String> { let key = (Stable::from(granter), Stable::from(revokee)); - + // First check if access exists at all if !self.0.contains_key(&key) { return Err("[ERR_NO_ACCESS] No access exists between these users.".to_string()); @@ -329,7 +334,7 @@ impl GroupAccessMap { )); } - // If we get here, we can safely revoke + // If we get here, we can safely revoke self.0.remove(&key); Ok(()) } @@ -337,7 +342,7 @@ impl GroupAccessMap { pub fn has_access(&self, granter: &NIK, grantee: &NIK) -> bool { let key = (Stable::from(granter.clone()), Stable::from(grantee.clone())); let result = self.0.contains_key(&key); - + result } @@ -595,22 +600,28 @@ impl EmrBindingMap { pub fn emr_list_all(&self, nik: &NIK) -> PatientBindingMapResult>> { println!("DEBUG emr_list_all: checking EMRs for NIK: {:?}", nik); - + // check if the user exists and has any EMRs if !self.0.range_key_exists(&nik.clone().to_stable()) { - println!("DEBUG emr_list_all: no EMRs found for NIK (range_key_exists): {:?}", nik); + println!( + "DEBUG emr_list_all: no EMRs found for NIK (range_key_exists): {:?}", + nik + ); return Err(PatientRegistryError::UserNoEmrs); } // get all EMRs let all_emrs = self.0.get_set_associated_by_key(&nik.clone().to_stable()); - println!("DEBUG emr_list_all: found EMRs count: {:?}", all_emrs.clone().map(|e| e.len())); - + println!( + "DEBUG emr_list_all: found EMRs count: {:?}", + all_emrs.clone().map(|e| e.len()) + ); + match all_emrs { Some(emrs) if !emrs.is_empty() => { println!("DEBUG emr_list_all: returning {} EMRs", emrs.len()); Ok(emrs) - }, + } _ => { println!("DEBUG emr_list_all: no EMRs found for NIK: {:?}", nik); Err(PatientRegistryError::UserNoEmrs) @@ -624,7 +635,6 @@ impl EmrBindingMap { page: u8, limit: u8, ) -> PatientBindingMapResult>> { - // first check if the user exists and has any EMRs if !self.0.range_key_exists(&nik.clone().to_stable()) { return Err(PatientRegistryError::UserNoEmrs); @@ -632,21 +642,21 @@ impl EmrBindingMap { // get all EMRs first to verify we have them let all_emrs = self.0.get_set_associated_by_key(&nik.clone().to_stable()); - + if all_emrs.clone().unwrap().is_empty() { return Err(PatientRegistryError::UserNoEmrs); } // now get the paginated results - let paginated = self.0.get_set_associated_by_key_paged(&nik.clone().to_stable(), page as u64, limit as u64); + let paginated = self.0.get_set_associated_by_key_paged( + &nik.clone().to_stable(), + page as u64, + limit as u64, + ); match paginated { - Some(emrs) if !emrs.is_empty() => { - Ok(emrs) - }, - _ => { - Ok(all_emrs.unwrap()) - } + Some(emrs) if !emrs.is_empty() => Ok(emrs), + _ => Ok(all_emrs.unwrap()), } } diff --git a/canister/src/provider_registry/build.rs b/canister/src/provider_registry/build.rs index 8ea1d876..b334f74c 100644 --- a/canister/src/provider_registry/build.rs +++ b/canister/src/provider_registry/build.rs @@ -1,10 +1,9 @@ -use ic_cdk_bindgen::{ Builder, Config }; -use std::{ panic::catch_unwind, path::PathBuf }; +use ic_cdk_bindgen::{Builder, Config}; +use std::{panic::catch_unwind, path::PathBuf}; fn get_workspace_root() -> PathBuf { - let manifest_dir = PathBuf::from( - std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir") - ); + let manifest_dir = + PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir")); for anc in manifest_dir.ancestors() { if anc.file_name().unwrap() == "canister" { @@ -18,7 +17,10 @@ fn get_workspace_root() -> PathBuf { fn main() { println!("cargo:rerun-if-changed=NULL"); - let link_flag = std::env::var("LINK").unwrap_or("true".to_string()).parse::().unwrap(); + let link_flag = std::env::var("LINK") + .unwrap_or("true".to_string()) + .parse::() + .unwrap(); // workaround to determine if this is invoked by dfx as dfx automatically inject this env var let candid_path_env = std::env::var("CANISTER_CANDID_PATH_EMR_REGISTRY").is_ok(); @@ -49,10 +51,12 @@ fn build_declaration() { let configs = [get_config("emr_registry"), get_config("patient_registry")]; let mut builder = Builder::new(); - + for config in configs { - builder.add(config); + builder.add(config); } - builder.build(Some(get_workspace_root().join("src/provider_registry/src/declarations"))); + builder.build(Some( + get_workspace_root().join("src/provider_registry/src/declarations"), + )); } diff --git a/canister/src/provider_registry/src/config.rs b/canister/src/provider_registry/src/config.rs index 497d0785..6a7bcac1 100644 --- a/canister/src/provider_registry/src/config.rs +++ b/canister/src/provider_registry/src/config.rs @@ -1,10 +1,8 @@ -use candid::{ CandidType, Principal }; +use candid::{CandidType, Principal}; use canister_common::{ - impl_max_size, - impl_mem_bound, - metrics, + impl_max_size, impl_mem_bound, metrics, mmgr::MemoryManager, - stable::{ Candid, Memory, Stable, ToStable }, + stable::{Candid, Memory, Stable, ToStable}, statistics::traits::Metrics, }; use ic_stable_structures::Cell; @@ -165,7 +163,8 @@ impl CanisterConfig { } pub fn remove_authorized_metrics_collector(&mut self, collector: Principal) { - self.authorized_metrics_collectors.retain(|c| c != &collector); + self.authorized_metrics_collectors + .retain(|c| c != &collector); } pub fn is_authorized_metrics_collector(&self, collector: &Principal) -> bool { diff --git a/canister/src/provider_registry/src/registry.rs b/canister/src/provider_registry/src/registry.rs index e84c071e..e8b50fd1 100644 --- a/canister/src/provider_registry/src/registry.rs +++ b/canister/src/provider_registry/src/registry.rs @@ -1,42 +1,40 @@ -use std::ops::{ Add, Bound }; +use std::ops::{Add, Bound}; -use candid::{ CandidType }; +use candid::CandidType; +use canister_common::common::{self, EmrId, PrincipalBytes}; use canister_common::random::CallError; use canister_common::stable::Candid; -use canister_common::statistics::traits::{ Metrics }; -use canister_common::common::{ self, EmrId, PrincipalBytes }; +use canister_common::statistics::traits::Metrics; use ic_principal::Principal; -use ic_stable_structures::{ BTreeMap }; -use parity_scale_codec::{ Decode, Encode }; -use serde::{ Deserialize, Serialize }; +use ic_stable_structures::BTreeMap; +use parity_scale_codec::{Decode, Encode}; use provider::attr::*; +use serde::{Deserialize, Serialize}; use canister_common::{ - deref, - impl_max_size, - impl_mem_bound, - impl_range_bound, - metrics, - opaque_metrics, + common::{AsciiRecordsKey, Id, Timestamp}, + mmgr::MemoryManager, + stable::{Memory, Stable, StableSet, ToStable}, }; use canister_common::{ - common::{ AsciiRecordsKey, Id, Timestamp }, - stable::{ Memory, Stable, StableSet, ToStable }, - mmgr::MemoryManager, + deref, impl_max_size, impl_mem_bound, impl_range_bound, metrics, opaque_metrics, }; -use crate::api::{ IssueEmrRequest, GetProviderListResponse }; -use crate::declarations::emr_registry::{ CreateEmrRequest, CreateEmrResponse }; +use crate::api::{GetProviderListResponse, IssueEmrRequest}; +use crate::declarations::emr_registry::{CreateEmrRequest, CreateEmrResponse}; use crate::declarations::patient_registry::IssueRequest; -use self::provider::{ Provider, V1 }; +use self::provider::{Provider, V1}; #[derive(thiserror::Error, Debug, CandidType)] pub enum RegistryError { - #[error(transparent)] IssueMapError(#[from] IssueMapError), - #[error(transparent)] ProviderBindingMapError(#[from] ProviderBindingMapError), - #[error("{0}")] ExternalCallError(#[from] CallError), + #[error(transparent)] + IssueMapError(#[from] IssueMapError), + #[error(transparent)] + ProviderBindingMapError(#[from] ProviderBindingMapError), + #[error("{0}")] + ExternalCallError(#[from] CallError), } pub type ProviderRegistryResult = Result; @@ -48,11 +46,19 @@ pub struct ProviderRegistry { } impl ProviderRegistry { - pub fn get_all_providers(&self, page: u64, limit: u64) -> ProviderRegistryResult { + pub fn get_all_providers( + &self, + page: u64, + limit: u64, + ) -> ProviderRegistryResult { let paginated = self.providers.get_all_providers_paginated(page, limit); - + Ok(GetProviderListResponse { - providers: paginated.providers.into_iter().map(|p| p.into_inner()).collect(), + providers: paginated + .providers + .into_iter() + .map(|p| p.into_inner()) + .collect(), total_pages: paginated.total_pages, total_provider_count: paginated.total_provider_count, }) @@ -60,36 +66,30 @@ impl ProviderRegistry { pub fn provider_info_with_principal( &self, - principal: &Principal + principal: &Principal, ) -> ProviderRegistryResult { let internal_id = self.providers_bindings.get_internal_id(principal)?; - Ok( - self.providers - .get_provider(internal_id.into_inner()) - .ok_or( - RegistryError::ProviderBindingMapError( - ProviderBindingMapError::ProviderDoesNotExist - ) - )? - .into_inner() - ) + Ok(self + .providers + .get_provider(internal_id.into_inner()) + .ok_or(RegistryError::ProviderBindingMapError( + ProviderBindingMapError::ProviderDoesNotExist, + ))? + .into_inner()) } pub fn provider_info_with_internal_id( &self, - internal_id: &InternalProviderId + internal_id: &InternalProviderId, ) -> ProviderRegistryResult { - Ok( - self.providers - .get_provider(internal_id.clone()) - .ok_or( - RegistryError::ProviderBindingMapError( - ProviderBindingMapError::ProviderDoesNotExist - ) - )? - .into_inner() - ) + Ok(self + .providers + .get_provider(internal_id.clone()) + .ok_or(RegistryError::ProviderBindingMapError( + ProviderBindingMapError::ProviderDoesNotExist, + ))? + .into_inner()) } } @@ -98,7 +98,7 @@ impl ProviderRegistry { pub async fn do_call_update_emr( req: crate::api::UpdateEmrRequest, emr_registry: crate::declarations::emr_registry::EmrRegistry, - patient_registry: crate::declarations::patient_registry::PatientRegistry + patient_registry: crate::declarations::patient_registry::PatientRegistry, ) { ic_cdk::spawn(async move { let header = req.header.clone(); @@ -119,7 +119,10 @@ impl ProviderRegistry { } let args = IssueRequest { header }; - let result = patient_registry.notify_updated(args).await.map_err(CallError::from); + let result = patient_registry + .notify_updated(args) + .await + .map_err(CallError::from); match result { Ok(_) => (), @@ -134,18 +137,20 @@ impl ProviderRegistry { pub fn build_args_call_emr_canister( &self, req: IssueEmrRequest, - emr_id: EmrId + emr_id: EmrId, ) -> ProviderRegistryResult { // safe to unwrap since the public api calling this api should have already verified the caller using guard functions let provider_principal = common::guard::verified_caller().unwrap(); - let provider = self.providers_bindings.get_internal_id(&provider_principal)?; + let provider = self + .providers_bindings + .get_internal_id(&provider_principal)?; // assemble args and call emr canister to issue emr Ok(req.to_args(provider.into_inner(), emr_id)) } fn to_issue_request( - req: &CreateEmrResponse + req: &CreateEmrResponse, ) -> crate::declarations::patient_registry::IssueRequest { let provider_id = req.header.provider_id.to_owned(); let user_id = req.header.user_id.to_owned(); @@ -165,7 +170,7 @@ impl ProviderRegistry { pub async fn do_call_create_emr( args: CreateEmrRequest, emr_registry: crate::declarations::emr_registry::EmrRegistry, - patient_registry: crate::declarations::patient_registry::PatientRegistry + patient_registry: crate::declarations::patient_registry::PatientRegistry, ) -> CreateEmrResponse { let create_emr_response = emr_registry.create_emr(args).await.map_err(CallError::from); @@ -176,10 +181,15 @@ impl ProviderRegistry { Ok((response,)) => { let issue_request = Self::to_issue_request(&response); - match patient_registry.notify_issued(issue_request).await.map_err(CallError::from) { + match patient_registry + .notify_issued(issue_request) + .await + .map_err(CallError::from) + { Ok(_) => response, - Err(e) => - ic_cdk::trap(&format!("ERROR: error calling patient canister : {}", e)), + Err(e) => { + ic_cdk::trap(&format!("ERROR: error calling patient canister : {}", e)) + } } } Err(e) => ic_cdk::trap(&format!("ERROR: error calling emr canister : {}", e)), @@ -207,7 +217,8 @@ impl Metrics for ProviderRegistry { opaque_metrics!(self.providers), opaque_metrics!(self.providers_bindings), opaque_metrics!(self.issued), - ].join("\n") + ] + .join("\n") } } @@ -217,7 +228,11 @@ impl ProviderRegistry { let providers_bindings = ProvidersBindings::init(memory_manager); let issued = Issued::init(memory_manager); - Self { providers, providers_bindings, issued } + Self { + providers, + providers_bindings, + issued, + } } /// check a given emr id is validly issued by some provider principal, this function uses internal provider id to resolve the given provider. @@ -226,31 +241,32 @@ impl ProviderRegistry { &self, provider: &ProviderPrincipal, emr_id: Id, - canister_id: Principal + canister_id: Principal, ) -> bool { let Ok(id) = self.providers_bindings.get_internal_id(provider) else { return false; }; - self.issued.is_issued_by(id.into_inner(), emr_id, canister_id) + self.issued + .is_issued_by(id.into_inner(), emr_id, canister_id) } fn populate_issue_map( &mut self, provider: &Principal, emr_id: Id, - canister_id: Principal + canister_id: Principal, ) -> ProviderRegistryResult<()> { match self.providers_bindings.get(provider) { - Some(id) => { - self.providers.try_mutate( - id.into_inner(), - |provider| -> ProviderRegistryResult<()> { - provider.increment_session(); - Ok(self.issued.issue_emr(provider.internal_id(), emr_id, canister_id)?) - } - )? - } + Some(id) => self.providers.try_mutate( + id.into_inner(), + |provider| -> ProviderRegistryResult<()> { + provider.increment_session(); + Ok(self + .issued + .issue_emr(provider.internal_id(), emr_id, canister_id)?) + }, + )?, None => Err(ProviderBindingMapError::ProviderDoesNotExist)?, } } @@ -259,13 +275,13 @@ impl ProviderRegistry { pub fn issue_emr( &mut self, emr_id: EmrId, - provider_principal: &Principal + provider_principal: &Principal, ) -> ProviderRegistryResult<()> { // TODO : handle if we're using multiple emr canister self.populate_issue_map( provider_principal, emr_id, - crate::declarations::emr_registry::CANISTER_ID + crate::declarations::emr_registry::CANISTER_ID, )?; Ok(()) @@ -282,7 +298,7 @@ impl ProviderRegistry { provider_principal: ProviderPrincipal, display_name: AsciiRecordsKey<64>, address: AsciiRecordsKey<64>, - id: Id + id: Id, ) -> ProviderRegistryResult<()> { // IMPORTANT: dont forget to change to newer version if updating provider version. @@ -290,7 +306,8 @@ impl ProviderRegistry { let provider = V1::new(display_name, address, id, provider_principal).to_provider(); // bind the principal to the internal id - self.providers_bindings.bind(provider_principal, provider.internal_id().clone())?; + self.providers_bindings + .bind(provider_principal, provider.internal_id().clone())?; // add the provider to the provider map self.providers.add_provider(provider)?; @@ -302,16 +319,12 @@ impl ProviderRegistry { /// suspended provider can't do things such as issuing, and reading emr pub fn suspend_provider( &mut self, - provider_principal: ProviderPrincipal + provider_principal: ProviderPrincipal, ) -> ProviderRegistryResult<()> { match self.providers_bindings.get(&provider_principal) { - Some(id) => { - Ok( - self.providers.try_mutate(id.into_inner(), |provider| { - provider.suspend(); - })? - ) - } + Some(id) => Ok(self.providers.try_mutate(id.into_inner(), |provider| { + provider.suspend(); + })?), None => Err(ProviderBindingMapError::ProviderDoesNotExist)?, } } @@ -319,16 +332,12 @@ impl ProviderRegistry { /// unsuspend a provider pub fn unsuspend_provider( &mut self, - provider: &ProviderPrincipal + provider: &ProviderPrincipal, ) -> ProviderRegistryResult<()> { match self.providers_bindings.get(provider) { - Some(id) => { - Ok( - self.providers.try_mutate(id.into_inner(), |provider| { - provider.unsuspend(); - })? - ) - } + Some(id) => Ok(self.providers.try_mutate(id.into_inner(), |provider| { + provider.unsuspend(); + })?), None => Err(ProviderBindingMapError::ProviderDoesNotExist)?, } } @@ -336,14 +345,11 @@ impl ProviderRegistry { /// check if a provider is suspended pub fn is_provider_suspended(&self, provider: &Principal) -> ProviderRegistryResult { match self.providers_bindings.get(provider) { - Some(id) => { - Ok( - self.providers - .get_provider(id.into_inner()) - .map(|provider| provider.activation_status().is_suspended()) - .ok_or(ProviderBindingMapError::ProviderDoesNotExist)? - ) - } + Some(id) => Ok(self + .providers + .get_provider(id.into_inner()) + .map(|provider| provider.activation_status().is_suspended()) + .ok_or(ProviderBindingMapError::ProviderDoesNotExist)?), None => Err(ProviderBindingMapError::ProviderDoesNotExist)?, } } @@ -361,25 +367,27 @@ impl ProviderRegistry { &self, provider: &ProviderPrincipal, page: u64, - limit: u64 + limit: u64, ) -> ProviderRegistryResult> { let internal_id = self.providers_bindings.get_internal_id(provider)?; - Ok( - self.issued.get_issued(internal_id.into_inner(), page, limit).map(|ids| - ids - .into_iter() + Ok(self + .issued + .get_issued(internal_id.into_inner(), page, limit) + .map(|ids| { + ids.into_iter() .map(|ids| ids.into_inner()) .collect::>() - )? - ) + })?) } } pub type InternalProviderId = Id; pub type ProviderPrincipal = Principal; -#[derive(Debug, thiserror::Error, CandidType, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, thiserror::Error, CandidType, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, +)] pub enum IssueMapError { #[error("provider not found")] ProviderNotFound, @@ -391,17 +399,7 @@ pub enum IssueMapError { EmrNotFound, } #[derive( - Debug, - Clone, - Encode, - Decode, - PartialEq, - Eq, - PartialOrd, - Ord, - CandidType, - Deserialize, - Default + Debug, Clone, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, CandidType, Deserialize, Default, )] pub struct Emr { canister_id: PrincipalBytes, @@ -448,13 +446,14 @@ impl Issued { &self, provider: InternalProviderId, emr_id: Id, - canister_id: Principal + canister_id: Principal, ) -> bool { let key = provider.to_stable(); let value = (Emr { canister_id: PrincipalBytes::from(canister_id), id: emr_id, - }).to_stable(); + }) + .to_stable(); self.0.contains_key(key, value) } @@ -462,7 +461,7 @@ impl Issued { &mut self, provider: &InternalProviderId, emr_id: Id, - canister_id: Principal + canister_id: Principal, ) -> IssueMapResult<()> { if self.is_issued_by(provider.clone(), emr_id.clone(), canister_id) { return Err(IssueMapError::AlreadyIssued); @@ -470,7 +469,8 @@ impl Issued { let emr = (Emr { canister_id: PrincipalBytes::from(canister_id), id: emr_id, - }).to_stable(); + }) + .to_stable(); self.0.insert(provider.clone().to_stable(), emr); Ok(()) @@ -480,20 +480,17 @@ impl Issued { &self, provider: InternalProviderId, page: u64, - limit: u64 + limit: u64, ) -> IssueMapResult>> { if !self.provider_exists(provider.clone()) { return Err(IssueMapError::ProviderNotFound); } match self.get_set_associated_by_key_paged(&provider.to_stable(), page, limit) { - Some(emrs) => - Ok( - emrs - .into_iter() - .map(|emr| emr.into_inner().id.to_stable()) - .collect() - ), + Some(emrs) => Ok(emrs + .into_iter() + .map(|emr| emr.into_inner().id.to_stable()) + .collect()), None => Err(IssueMapError::EmrNotFound), } } @@ -503,7 +500,9 @@ impl Issued { /// this is used to track healthcare providers using their principal. this is needed because we want to be able to change the principal without costly update. we can just update the principal here. pub struct ProvidersBindings(BTreeMap, Memory>); deref!(mut ProvidersBindings: BTreeMap, Memory>); -#[derive(Debug, thiserror::Error, CandidType, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, thiserror::Error, CandidType, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, +)] pub enum ProviderBindingMapError { #[error("operation not permitted, provider exists")] ProviderExist, @@ -543,7 +542,7 @@ impl ProvidersBindings { pub fn bind( &mut self, provider: ProviderPrincipal, - internal_id: InternalProviderId + internal_id: InternalProviderId, ) -> ProviderBindingMapResult { if self.get_internal_id(&provider).is_ok() { return Err(ProviderBindingMapError::ProviderExist); @@ -556,7 +555,7 @@ impl ProvidersBindings { pub fn rebind( &mut self, provider: ProviderPrincipal, - internal_id: InternalProviderId + internal_id: InternalProviderId, ) -> ProviderBindingMapResult { if self.get_internal_id(&provider).is_err() { return Err(ProviderBindingMapError::ProviderDoesNotExist); @@ -569,9 +568,11 @@ impl ProvidersBindings { /// will return an error if owner does not exists pub fn get_internal_id( &self, - provider: &ProviderPrincipal + provider: &ProviderPrincipal, ) -> ProviderBindingMapResult> { - self.0.get(provider).ok_or(ProviderBindingMapError::ProviderDoesNotExist) + self.0 + .get(provider) + .ok_or(ProviderBindingMapError::ProviderDoesNotExist) } pub fn init(memory_manager: &MemoryManager) -> Self { @@ -591,7 +592,8 @@ metrics!(Providers: LengthMetrics); impl std::fmt::Debug for Providers { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let provider = self.map + let provider = self + .map .iter() .map(|(k, v)| (k.to_string(), v)) .collect::>(); @@ -617,23 +619,26 @@ impl Metrics for Providers { self.map.len().to_string() } } - #[derive(Debug)] - pub struct PaginatedProviders { - pub providers: Vec>, - pub total_pages: u64, - pub total_provider_count: u64, - } +#[derive(Debug)] +pub struct PaginatedProviders { + pub providers: Vec>, + pub total_pages: u64, + pub total_provider_count: u64, +} impl Providers { pub fn add_provider(&mut self, provider: Provider) -> ProviderBindingMapResult<()> { match self.is_exist(provider.internal_id().clone()) { true => Err(ProviderBindingMapError::ProviderExist), false => { - let _bytes_allocated_approx = - std::mem::size_of_val(&provider.internal_id()) + - std::mem::size_of_val(&provider); - - let result = self.map - .insert(provider.internal_id().clone().to_stable(), provider.to_stable()) + let _bytes_allocated_approx = std::mem::size_of_val(&provider.internal_id()) + + std::mem::size_of_val(&provider); + + let result = self + .map + .insert( + provider.internal_id().clone().to_stable(), + provider.to_stable(), + ) .map(|_| ()); assert!(result.is_none(), "provider does not exist, this is a bug"); @@ -644,14 +649,16 @@ impl Providers { } fn update_unchecked(&mut self, provider: Stable) { - let _ = self.map.insert(provider.internal_id().clone().to_stable(), provider); + let _ = self + .map + .insert(provider.internal_id().clone().to_stable(), provider); } /// try mutate a provider, will return [ProviderBindingMapError::ProviderDoesNotExist] if the provider does not exist pub fn try_mutate( &mut self, provider: InternalProviderId, - f: impl FnOnce(&mut Stable) -> T + f: impl FnOnce(&mut Stable) -> T, ) -> ProviderBindingMapResult { let raw = self.map.get(&provider.to_stable()); @@ -681,37 +688,37 @@ impl Providers { self.map.get(&provider.to_stable()) } - pub fn get_all_providers_paginated(&self, page: u64, limit: u64) -> PaginatedProviders { let total_provider_count = self.map.len() as u64; - - // Calculate total pages (ceiling division) - let total_pages = (total_provider_count + limit - 1) / limit; - - let skip_count = (page * limit) as usize; - - let remaining_items = if skip_count < total_provider_count as usize { - total_provider_count as usize - skip_count - } else { - 0 - }; - - let items_to_take = std::cmp::min(limit as usize, remaining_items); - - let providers = self.map.iter() - .skip(skip_count) - .take(items_to_take) - .map(|(_, v)| v.clone()) - .collect::>(); - - PaginatedProviders { - providers, - total_pages, - total_provider_count, - } + + // Calculate total pages (ceiling division) + let total_pages = (total_provider_count + limit - 1) / limit; + + let skip_count = (page * limit) as usize; + + let remaining_items = if skip_count < total_provider_count as usize { + total_provider_count as usize - skip_count + } else { + 0 + }; + + let items_to_take = std::cmp::min(limit as usize, remaining_items); + + let providers = self + .map + .iter() + .skip(skip_count) + .take(items_to_take) + .map(|(_, v)| v.clone()) + .collect::>(); + + PaginatedProviders { + providers, + total_pages, + total_provider_count, } } - +} #[cfg(test)] mod provider_test { @@ -735,8 +742,9 @@ mod provider_test { AsciiRecordsKey::<64>::new(name.clone()).unwrap(), AsciiRecordsKey::<64>::new(name).unwrap(), internal_id.clone(), - provider_principal.clone() - ).to_provider(); + provider_principal.clone(), + ) + .to_provider(); let _encoded_provider_size = Encode!(&provider).unwrap(); println!("{:?}", _encoded_provider_size.len()); @@ -761,8 +769,9 @@ mod provider_test { AsciiRecordsKey::<64>::new("test").unwrap(), AsciiRecordsKey::<64>::new("test").unwrap(), internal_id.clone(), - provider_principal - ).to_provider(); + provider_principal, + ) + .to_provider(); let _bytes_allocated_approx = std::mem::size_of_val(&internal_id) + std::mem::size_of_val(&provider); @@ -800,7 +809,7 @@ mod provider_test { "h5aet-waaaa-aaaab-qaamq-cai", "rrkah-fqaaa-aaaaa-aaaaq-cai", "aaaaa-aa", - "qoctq-giaaa-aaaaa-aaaea-cai" + "qoctq-giaaa-aaaaa-aaaea-cai", ]; // add 5 providers @@ -813,8 +822,9 @@ mod provider_test { AsciiRecordsKey::<64>::new(name.clone()).unwrap(), AsciiRecordsKey::<64>::new(name).unwrap(), internal_id.clone(), - provider_principal - ).to_provider(); + provider_principal, + ) + .to_provider(); providers.add_provider(provider).unwrap(); } @@ -826,9 +836,16 @@ mod provider_test { // test first page let page1 = providers.get_all_providers_paginated(0, page_size); - assert_eq!(page1.providers.len(), 2, "First page should have 2 providers"); + assert_eq!( + page1.providers.len(), + 2, + "First page should have 2 providers" + ); assert_eq!(page1.total_pages, 3, "Should have 3 pages total"); - assert_eq!(page1.total_provider_count, 5, "Should have 5 providers total"); + assert_eq!( + page1.total_provider_count, 5, + "Should have 5 providers total" + ); // verify first page providers let first_provider = page1.providers[0].internal_id().clone(); @@ -838,9 +855,16 @@ mod provider_test { // test second page let page2 = providers.get_all_providers_paginated(1, page_size); - assert_eq!(page2.providers.len(), 2, "Second page should have 2 providers"); + assert_eq!( + page2.providers.len(), + 2, + "Second page should have 2 providers" + ); assert_eq!(page2.total_pages, 3, "Should have 3 pages total"); - assert_eq!(page2.total_provider_count, 5, "Should have 5 providers total"); + assert_eq!( + page2.total_provider_count, 5, + "Should have 5 providers total" + ); // verify second page providers let third_provider = page2.providers[0].internal_id().clone(); @@ -852,7 +876,10 @@ mod provider_test { let page3 = providers.get_all_providers_paginated(2, page_size); assert_eq!(page3.providers.len(), 1, "Last page should have 1 provider"); assert_eq!(page3.total_pages, 3, "Should have 3 pages total"); - assert_eq!(page3.total_provider_count, 5, "Should have 5 providers total"); + assert_eq!( + page3.total_provider_count, 5, + "Should have 5 providers total" + ); // verify last page provider let fifth_provider = page3.providers[0].internal_id().clone(); @@ -869,7 +896,9 @@ pub mod provider { pub mod attr { use super::super::*; - #[derive(CandidType, Deserialize, Debug, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)] + #[derive( + CandidType, Deserialize, Debug, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord, + )] pub enum Status { Active, Suspended, @@ -916,21 +945,15 @@ pub mod provider { /// Provider session, 1 session is equal to 1 emr issued by a provider. used to bill the provider. #[derive( - Deserialize, - CandidType, - Debug, - Default, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord + Deserialize, CandidType, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, )] pub struct Session(u64); // blanket impl for session - impl From for Session where T: Into { + impl From for Session + where + T: Into, + { fn from(session: T) -> Self { Self(session.into()) } @@ -992,13 +1015,14 @@ pub mod provider { fn to_provider(self) -> super::Provider; } - pub trait BasicProvider: Billable + - ActivationSatus + - BasicProviderAttributes + - ToProvider {} - impl

BasicProvider - for P - where P: Billable + ActivationSatus + BasicProviderAttributes + ToProvider {} + pub trait BasicProvider: + Billable + ActivationSatus + BasicProviderAttributes + ToProvider + { + } + impl

BasicProvider for P where + P: Billable + ActivationSatus + BasicProviderAttributes + ToProvider + { + } } // END ------------------------------ SESSION ------------------------------ END @@ -1162,7 +1186,7 @@ pub mod provider { #[test] fn test_len_encoded() { - use candid::{ Encode, Decode }; + use candid::{Decode, Encode}; let name = AsciiRecordsKey::<64>::new("a".repeat(64)).unwrap(); let provider_principal = ProviderPrincipal::from_text("aaaaa-aa").unwrap(); @@ -1170,8 +1194,9 @@ pub mod provider { name.clone(), name, id!("12a1bd26-4954-4cf4-87ac-57b4f9585987"), - provider_principal - ).to_provider(); + provider_principal, + ) + .to_provider(); let encoded = Encode!(&s).unwrap(); println!("encoded len: {}", encoded.len()); @@ -1245,7 +1270,3 @@ pub mod provider { } } } - - - - From 64ed345b9f583f0008b5e051864dacd5023fba9e Mon Sep 17 00:00:00 2001 From: mahesawp45 Date: Tue, 4 Nov 2025 16:55:27 +0800 Subject: [PATCH 7/7] feat(medisa): adjustment to medisa style --- final_demo/pwa/public/logo.png | Bin 9732 -> 19240 bytes final_demo/pwa/public/sw.js.map | 2 +- .../auth/(need-authentication)/guide/page.tsx | 34 ++++----- final_demo/pwa/src/app/auth/login/page.tsx | 20 ++---- final_demo/pwa/src/app/layout.tsx | 18 +++-- .../pwa/src/components/auth/guide/_header.css | 27 +++---- final_demo/pwa/src/components/auth/header.tsx | 59 +++++++-------- final_demo/pwa/src/providers/chakra-ui.tsx | 67 ++++++++---------- 8 files changed, 103 insertions(+), 124 deletions(-) diff --git a/final_demo/pwa/public/logo.png b/final_demo/pwa/public/logo.png index 86b3ff3746e2baaa0cf4bb317e623cb9aeadda3a..530dd43bf184e3655ff73ee0e5af178a7a257b76 100644 GIT binary patch literal 19240 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 ak53{I`1iBeI}l&=6;hWsB_mX$I9 diff --git a/final_demo/pwa/public/sw.js.map b/final_demo/pwa/public/sw.js.map index 58688e9c..e0a30a8a 100644 --- a/final_demo/pwa/public/sw.js.map +++ b/final_demo/pwa/public/sw.js.map @@ -1 +1 @@ -{"version":3,"file":"sw.js","sources":["C:/Users/Baliola/AppData/Local/Temp/6178d24fbd1ccf9fe172156cd47042e6/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from 'D:/hans-baliola/Medblock/final_demo/pwa/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from 'D:/hans-baliola/Medblock/final_demo/pwa/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from 'D:/hans-baliola/Medblock/final_demo/pwa/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from 'D:/hans-baliola/Medblock/final_demo/pwa/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({ request, response, event, state }) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, { status: 200, statusText: 'OK', headers: response.headers }) } return response } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"sw.js","sources":["C:/Users/MahesaWP/AppData/Local/Temp/7fc08dc790cbf8caf5189c5c1925f45b/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from 'X:/Medblock/final_demo/pwa/node_modules/workbox-routing/registerRoute.mjs';\nimport {NetworkFirst as workbox_strategies_NetworkFirst} from 'X:/Medblock/final_demo/pwa/node_modules/workbox-strategies/NetworkFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from 'X:/Medblock/final_demo/pwa/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from 'X:/Medblock/final_demo/pwa/node_modules/workbox-core/clientsClaim.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\nimportScripts(\n \n);\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n\nworkbox_routing_registerRoute(\"/\", new workbox_strategies_NetworkFirst({ \"cacheName\":\"start-url\", plugins: [{ cacheWillUpdate: async ({ request, response, event, state }) => { if (response && response.type === 'opaqueredirect') { return new Response(response.body, { status: 200, statusText: 'OK', headers: response.headers }) } return response } }] }), 'GET');\nworkbox_routing_registerRoute(/.*/i, new workbox_strategies_NetworkOnly({ \"cacheName\":\"dev\", plugins: [] }), 'GET');\n\n\n\n\n"],"names":["importScripts","self","skipWaiting","workbox_core_clientsClaim","workbox_routing_registerRoute","workbox_strategies_NetworkFirst","plugins","cacheWillUpdate","request","response","event","state","type","Response","body","status","statusText","headers","workbox_strategies_NetworkOnly"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAa,EAEZ,CAAA;EAQDC,CAAI,CAAA,CAAA,CAAA,CAACC,CAAW,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAE,CAAA;AAElBC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAyB,EAAE,CAAA;AAI3BC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAA6B,CAAC,CAAA,CAAA,CAAG,CAAE,CAAA,CAAA,CAAA,CAAA,CAAIC,oBAA+B,CAAC,CAAA;EAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAW,EAAC,CAAW,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EAAEC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAO,EAAE,CAAC,CAAA;GAAEC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAe,EAAE,CAAO,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;QAAEC,CAAO,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;QAAEC,CAAQ,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;QAAEC,CAAK,CAAA,CAAA,CAAA,CAAA,CAAA;AAAEC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA;AAAM,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAC,CAAK,CAAA,CAAA,CAAA,CAAA,CAAA;EAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAIF,QAAQ,CAAIA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAQ,CAACG,CAAI,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAK,gBAAgB,CAAE,CAAA,CAAA;AAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,OAAO,CAAIC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAQ,CAACJ,CAAQ,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAACK,IAAI,CAAE,CAAA,CAAA;EAAEC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAM,EAAE,CAAG,CAAA,CAAA,CAAA;EAAEC,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAU,EAAE,CAAI,CAAA,CAAA,CAAA,CAAA;YAAEC,CAAO,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAER,CAAQ,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAACQ,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA;AAAQ,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAC,CAAC,CAAA;EAAC,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAOR,QAAQ,CAAA;EAAC,CAAA,CAAA,CAAA,CAAA,CAAA;KAAG,CAAA;AAAE,CAAA,CAAA,CAAC,CAAC,CAAA,CAAE,CAAK,CAAA,CAAA,CAAA,CAAA,CAAC,CAAA;AACxWL,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAA6B,CAAC,CAAA,CAAA,CAAA,CAAA,CAAK,CAAE,CAAA,CAAA,CAAA,CAAA,CAAIc,mBAA8B,CAAC,CAAA;EAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAW,EAAC,CAAK,CAAA,CAAA,CAAA,CAAA,CAAA;EAAEZ,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAAA,CAAO,EAAE,CAAA,CAAA;EAAG,CAAC,CAAC,CAAE,CAAA,CAAA,CAAA,CAAA,CAAA,CAAK,CAAC,CAAA;;"} \ No newline at end of file diff --git a/final_demo/pwa/src/app/auth/(need-authentication)/guide/page.tsx b/final_demo/pwa/src/app/auth/(need-authentication)/guide/page.tsx index bb9b5bde..34886f4e 100644 --- a/final_demo/pwa/src/app/auth/(need-authentication)/guide/page.tsx +++ b/final_demo/pwa/src/app/auth/(need-authentication)/guide/page.tsx @@ -5,37 +5,37 @@ import AuthGuideHeader from "@/components/auth/guide/header"; export default function AuthGuidePage() { return ( - + - - + - By using Medblock pasport, you agree to the {" "} - + By using Medisa pasport, you agree to the{" "} + terms - - {""} and {" "} - + {" "} + and{" "} + privacy policy - ) -} \ No newline at end of file + ); +} diff --git a/final_demo/pwa/src/app/auth/login/page.tsx b/final_demo/pwa/src/app/auth/login/page.tsx index 8d974ea4..b0c85e4e 100644 --- a/final_demo/pwa/src/app/auth/login/page.tsx +++ b/final_demo/pwa/src/app/auth/login/page.tsx @@ -1,26 +1,20 @@ -"use client" +"use client"; import dynamic from "next/dynamic"; import { Flex, Text } from "@chakra-ui/react"; import { AuthHeader } from "@/components/auth/header"; import AuthOtherLoginOptions from "@/components/auth/login/other-login"; -const NFIDButtonLogin = dynamic( - () => import('@/components/auth/login/nfid'), { - ssr: false +const NFIDButtonLogin = dynamic(() => import("@/components/auth/login/nfid"), { + ssr: false, }); export default function LoginPage() { return ( - + - + {/* */} - ) -} \ No newline at end of file + ); +} diff --git a/final_demo/pwa/src/app/layout.tsx b/final_demo/pwa/src/app/layout.tsx index f044a042..d410a0cb 100644 --- a/final_demo/pwa/src/app/layout.tsx +++ b/final_demo/pwa/src/app/layout.tsx @@ -2,19 +2,19 @@ import { ChakraUIProvider } from "@/providers/chakra-ui"; import ICAgentProvider from "@/providers/ic-reactor"; import { ReactQueryProvider } from "@/providers/react-query"; import type { Metadata } from "next"; -import { Ubuntu } from "next/font/google"; +import { Poppins } from "next/font/google"; -const ubuntu = Ubuntu({ +const poppins = Poppins({ weight: ["300", "400", "500", "700"], subsets: ["latin"], }); export const metadata: Metadata = { - title: "Medblock Passport", - description: "Medblock Passport is a EMR based on blockchain technology", + title: "Medisa Passport", + description: "Medisa Passport is a EMR based on blockchain technology", generator: "Next.js", manifest: "/manifest.json", - icons: "/logo.png" + icons: "/logo.png", }; export default function RootLayout({ @@ -24,15 +24,13 @@ export default function RootLayout({ }>) { return ( - + - - {children} - + {children} - ) + ); } diff --git a/final_demo/pwa/src/components/auth/guide/_header.css b/final_demo/pwa/src/components/auth/guide/_header.css index c335b5b6..cad76844 100644 --- a/final_demo/pwa/src/components/auth/guide/_header.css +++ b/final_demo/pwa/src/components/auth/guide/_header.css @@ -1,26 +1,27 @@ -.custom-swiper{ +.custom-swiper { width: 100%; - height : 100%; + height: 100%; } -.custom-pagination{ +.custom-pagination { display: flex; justify-content: center; width: 100%; } + .custom-pagination .custom-bullet { width: 12px; height: 12px; - background-color: #e0e0e0; - opacity: 1; - border-radius: 50%; - margin: 0 4px; - transition: background-color 0.3s, width 0.3s, height 0.3s; + background-color: #e0e0e0; + opacity: 1; + border-radius: 50%; + margin: 0 4px; + transition: background-color 0.3s, width 0.3s, height 0.3s; } .custom-pagination .custom-bullet-active { - width: 28px; - height: 12px; - background-color: #c5c9fc; - border-radius: 24px; -} + width: 28px; + height: 12px; + background-color: #A3E6A0; + border-radius: 24px; +} \ No newline at end of file diff --git a/final_demo/pwa/src/components/auth/header.tsx b/final_demo/pwa/src/components/auth/header.tsx index 730a1eb8..29ca2979 100644 --- a/final_demo/pwa/src/components/auth/header.tsx +++ b/final_demo/pwa/src/components/auth/header.tsx @@ -2,52 +2,46 @@ import { LOGO } from "@/constants/logo"; import { Flex, Image, Text } from "@chakra-ui/react"; interface AuthHeaderProps { - size?: 'xs' | 'sm' | 'lg'; + size?: "xs" | "sm" | "lg"; } const headerVariants = { xs: { - imageWidth: 16, - titleFontSize: 'xl', - titleFontWeight: 'bold', - subtitleFontSize: 'sm', - subtitleFontWeight: 'normal', + imageWidth: 36, + titleFontSize: "xl", + titleFontWeight: "bold", + subtitleFontSize: "sm", + subtitleFontWeight: "normal", }, sm: { - imageWidth: 20, - titleFontSize: '2xl', - titleFontWeight: 'bold', - subtitleFontSize: 'md', - subtitleFontWeight: 'normal', + imageWidth: 36, + titleFontSize: "2xl", + titleFontWeight: "bold", + subtitleFontSize: "md", + subtitleFontWeight: "normal", }, lg: { - imageWidth: 24, - titleFontSize: '4xl', - titleFontWeight: 'bold', - subtitleFontSize: 'xl', - subtitleFontWeight: 'bold', + imageWidth: 40, + titleFontSize: "4xl", + titleFontWeight: "bold", + subtitleFontSize: "xl", + subtitleFontWeight: "bold", }, }; -export const AuthHeader = ({ - size = "lg" -}: AuthHeaderProps) => { - const variant = headerVariants[size] +export const AuthHeader = ({ size = "lg" }: AuthHeaderProps) => { + const variant = headerVariants[size]; return ( - + Medblock Passport - block - - */} + - ) -} \ No newline at end of file + ); +}; diff --git a/final_demo/pwa/src/providers/chakra-ui.tsx b/final_demo/pwa/src/providers/chakra-ui.tsx index 168eee48..85af7ad3 100644 --- a/final_demo/pwa/src/providers/chakra-ui.tsx +++ b/final_demo/pwa/src/providers/chakra-ui.tsx @@ -1,35 +1,34 @@ -'use client' - -import { ChakraProvider, extendTheme } from '@chakra-ui/react' +"use client"; +import { ChakraProvider, extendTheme } from "@chakra-ui/react"; const themes = extendTheme({ colors: { primary: { - 950: "#090B2A", - 900: "#0C0F38", - 800: "#151A62", - 700: "#242DA8", - 600: "#3E48D6", - 500: "#7178E1", - 400: "#9CA2EA", - 300: "#C0C3F2", - 200: "#DBDDF7", - 100: "#EFF0FC", - 50: "#FBFBFE", + 950: "#0F3B36", + 900: "#124F47", + 800: "#166458", + 700: "#1C7A6B", + 600: "#52A295", + 500: "#A3D0C9", + 400: "#D1E8E4", + 300: "#E3F1EF", + 200: "#F0F8F6", + 100: "#F9FBFA", + 50: "#FCFEFD", }, accent: { - 950: "#31020D", - 900: "#430212", - 800: "#790420", - 700: "#D40837", - 600: "#F72859", - 500: "#F96185", - 400: "#FB91AA", - 300: "#FCB9C9", - 200: "#FED8E0", - 100: "#FEEDF1", - 50: "#FFFBFC", + 950: "#1E4E1E", + 900: "#3E923F", + 800: "#57A757", + 700: "#70BC6F", + 600: "#8AD187", + 500: "#A3E6A0", + 400: "#B5F4B0", + 300: "#C8F8C4", + 200: "#DCFBDC", + 100: "#EEFDEF", + 50: "#FAFEFA", }, success: { 950: "#0A2A09", @@ -96,17 +95,9 @@ const themes = extendTheme({ 100: "#F2F2F2", 50: "#FCFCFC", }, - } -}) + }, +}); -export function ChakraUIProvider({ - children -}: { - children: React.ReactNode -}) { - return ( - - {children} - - ) -} \ No newline at end of file +export function ChakraUIProvider({ children }: { children: React.ReactNode }) { + return {children}; +}