Skip to content

Commit

Permalink
Add AWS S3 Aidbox integration example
Browse files Browse the repository at this point in the history
  • Loading branch information
spicyfalafel committed Jan 24, 2025
1 parent ae8e824 commit dedd05f
Show file tree
Hide file tree
Showing 28 changed files with 7,164 additions and 0 deletions.
41 changes: 41 additions & 0 deletions attachments/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
142 changes: 142 additions & 0 deletions attachments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# AWS S3 Aidbox integration example

This example demonstrates how to use [AWS S3 Aidbox integration](https://docs.aidbox.app/storage-1/s3-compatible-storages/aws-s3) using front-end application only.
This application allows front-end to save Patient photo to the AWS S3 bucket, and also access it.
Aidbox is a middleware between front-end and AWS S3. Aidbox knows the access key id and secret access key for an IAM user or role in AWS from created AwsAccount Aidbox resource.

## The save file to bucket flow
1. Front-end sends a POST request to Aidbox with filename to write to.
```http
POST /aws/storage/<your-account-id-in-aidbox>/<your-bucket-name>
filename: patient.png
```
2. Aidbox answers with [presigned AWS URL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
```json
{
"url": "https://<your-bucket-name>.s3.amazonaws.com/patient.png?..."
}
```
3. Front-end sends POST request to this url. The body is a binary file, e.g. patient photo. The file as saved in the bucket.

## The get file from bucket flow
1. Front-end sends a request to Aidbox with filename to get to.
```http
GET /aws/storage/<your-account-id-in-aidbox>/<your-bucket-name>/<filename>
```
2. Aidbox answers with [presigned AWS URL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
```json
{
"url": "https://<your-bucket-name>.s3.amazonaws.com/<filename>?..."
}
```
3. Front-end sends GET request to this url. If it is image, `<img>` html tag can be used to render the image.
```react
<img
src="https://<your-bucket-name>.s3.amazonaws.com/<filename>?..."
/>
```

## FHIR & binary files
In this example, we use [Attachment](https://build.fhir.org/datatypes.html#attachment) FHIR datatype to store the file url.
More specifically, we use Patient.photo, which is Attachment.
```json
{
"resourceType": "Patient",
"id": "e5ca087b-ec71-40a7-8c9b-e6093e8f1fdc",
"photo": [
{
"url": "https://thebucket.s3.amazonaws.com/john_smith_20000101.png",
"title": "john_smith_20000101.png",
"contentType": "image/png"
}
],
"name": [
{
"given": [
"john"
],
"family": "smith"
}
],
"birthDate": "2000-01-01"
}

```
We also create [DocumentReference](https://build.fhir.org/documentreference.html) resource to save the image. See [DocumentReference's scope and usage](https://build.fhir.org/documentreference.html#scope).
```json
{
"resourceType": "DocumentReference",
"id": "f2473702-99eb-4efd-be07-8fa8ff21828c",
"status": "current",
"content": [
{
"attachment": {
"url": "https://thebucket.s3.amazonaws.com/john_smith_20000101.png",
"title": "john_smith_20000101.png",
"contentType": "image/png"
}
}
]
}

```

## Setup Aidbox

1. Create an instance of AwsAccount that contains access credentials and region settings.

```http
PUT /AwsAccount
id: my-account
access-key-id: <your-key-id> # e.g. AKIAIOSFODNN7EXAMPLE
secret-access-key: <your-secret-access-key> # e.g. wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
region: us-east-1
```

2. Create [Basic Client](https://docs.aidbox.app/modules/security-and-access-control/auth/basic-auth) to allow front-end any request.

```http
PUT/Client/basic?_pretty=true
content-type: application/json
accept: application/json
{
"secret": "secret",
"grant_types": [
"basic"
]
}
```

```http
PUT/AccessPolicy/basic-policy?_pretty=true
content-type: application/json
accept: application/json
{
"engine": "allow",
"link": [
{
"id": "basic",
"resourceType": "Client"
}
]
}
```

3. Now you can send requests from front-end using basic authorization header:
```
"Authorization": "Basic YmFzaWM6c2VjcmV0"
```

## Run application

```bash
npm install
```

```bash
npm run dev
```
39 changes: 39 additions & 0 deletions attachments/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
volumes:
aidbox_pg_data: {}
services:
aidbox_db:
image: healthsamurai/aidboxdb:17
volumes:
- aidbox_pg_data:/var/lib/postgresql/data:delegated
environment:
POSTGRES_USER: aidbox
POSTGRES_PORT: "5432"
POSTGRES_DB: aidbox
POSTGRES_PASSWORD: M3ya7uZxdJ
aidbox:
extra_hosts:
- "host.docker.internal:host-gateway"
image: healthsamurai/aidboxone:edge
depends_on:
- aidbox_db
ports:
- 8888:8888
environment:
AIDBOX_TERMINOLOGY_SERVICE_BASE_URL: https://tx.fhir.org/r4
AIDBOX_FHIR_PACKAGES: hl7.fhir.r4.core#4.0.1
AIDBOX_FHIR_SCHEMA_VALIDATION: true
AIDBOX_CREATED_AT_URL: https://aidbox.app/ex/createdAt
AIDBOX_CLIENT_SECRET: secret
AIDBOX_CORRECT_AIDBOX_FORMAT: true
AIDBOX_ADMIN_PASSWORD: cwSgexVsRb
AIDBOX_COMPLIANCE: enabled
BOX_SEARCH_FHIR__COMPARISONS: true
PGHOST: aidbox_db
BOX_COMPATIBILITY_VALIDATION_JSON__SCHEMA_REGEX: "#{:fhir-datetime}"
BOX_SEARCH_AUTHORIZE_INLINE_REQUESTS: true
PGUSER: aidbox
AIDBOX_PORT: 8888
PGDATABASE: aidbox
PGPASSWORD: M3ya7uZxdJ
PGPORT: "5432"
BOX_SEARCH_INCLUDE_CONFORMANT: true
16 changes: 16 additions & 0 deletions attachments/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];

export default eslintConfig;
7 changes: 7 additions & 0 deletions attachments/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading

0 comments on commit dedd05f

Please sign in to comment.