Skip to content

Commit

Permalink
Merge pull request #15 from fractal-analytics-platform/vizarr-update
Browse files Browse the repository at this point in the history
Updated to use newer vizarr, Docker setup
  • Loading branch information
zonia3000 authored Jul 23, 2024
2 parents f800281 + cdfc187 commit 2efc461
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PORT=3000
FRACTAL_SERVER_URL=http://localhost:8000
ZARR_DATA_BASE_PATH=/path/to/zarr-files
VIZARR_STATIC_FILES_PATH=/path/to/vizarr/out
VIZARR_STATIC_FILES_PATH=/path/to/vizarr/dist
BASE_PATH=/vizarr
ALLOWED_USERS=/path/to/allowed-users.txt
CACHE_EXPIRATION_TIME=60
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Checklist before merging
- [ ] I added an appropriate entry to `CHANGELOG.md`
21 changes: 21 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Changes on main branch

on:
push:
branches: ["main"]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Login
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Build the Docker image
run: docker build . --tag ghcr.io/fractal-analytics-platform/fractal-vizarr-viewer:dev

- name: Push the Docker Image
run: docker push ghcr.io/fractal-analytics-platform/fractal-vizarr-viewer:dev
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Note: Numbers like (#123) point to closed Pull Requests on the fractal-vizarr-viewer repository.

# 0.1.0

First official release

* Used newer vizarr version, based on zarrita.js and pnpm (\#15);
* Added Docker setup and configured CI to publish docker images on tags (\#15);
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM node:20

RUN npm install -g pnpm

WORKDIR /

ADD vizarr.patch .
RUN git clone https://github.com/hms-dbmi/vizarr.git
WORKDIR /vizarr

RUN git checkout 55845ffb658fa04ee2fb649a434c4c16c587233e
RUN git apply ../vizarr.patch
RUN pnpm install
RUN pnpm run build

RUN mkdir /fractal-vizarr-viewer

WORKDIR /fractal-vizarr-viewer

ADD src .
ADD package* .
ADD tsconfig.json .

RUN npm install
RUN npm run build

ENV VIZARR_STATIC_FILES_PATH=/vizarr/dist

CMD ["node", "/fractal-vizarr-viewer/dist/app.js"]
114 changes: 80 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# fractal-data
# fractal-vizarr-viewer

Prototype to explore serving/viewing zarr data.

Expand All @@ -11,19 +11,19 @@ The application has 2 endpoints:

## How it works

When a user logins to fractal-web, the browser receives a cookie that is generated by fractal-server. The same cookie is sent by the browser to other services on the same domain. The fractal-data service forwards that cookie back to fractal-server in order to obtain the user details and then decides if the user is authorized to retrieve the requested file or not:
When a user logins to fractal-web, the browser receives a cookie that is generated by fractal-server. The same cookie is sent by the browser to other services on the same domain. The fractal-vizarr-viewer service forwards that cookie back to fractal-server in order to obtain the user details and then decides if the user is authorized to retrieve the requested file or not:

![Fractal Data cookie flow](./fractal-data-cookie-flow.png)
![Fractal Data cookie flow](./fractal-vizarr-viewer-cookie-flow.png)

Currently the authorization check verifies if the user email retrieved from the cookie has been added to the file specified by the `ALLOWED_USERS` environment variable. The file contains the email addresses of authorized users separated by newlines. In the future more complex authorization mechanisms can be set up, also using an additional table in fractal-server to check allowed paths.

### Note about the domain constraint

This cookie-based technique can be used only if fractal-server and fractal-data are reachable from the same domain (or different subdomains of the same main domain). The single applications can be located on different servers, but a common reverse proxy must be used to expose them on the same domain.
This cookie-based technique can be used only if fractal-server and fractal-vizarr-viewer are reachable from the same domain (or different subdomains of the same main domain). The single applications can be located on different servers, but a common reverse proxy must be used to expose them on the same domain.

If different subdomains are used for fractal-web and fractal-data, the fractal-web environment variable `AUTH_COOKIE_DOMAIN` must contain the common parent domain.
If different subdomains are used for fractal-web and fractal-vizarr-viewer, the fractal-web environment variable `AUTH_COOKIE_DOMAIN` must contain the common parent domain.

Example: if fractal-data is served on `fractal-data.mydomain.net` and fractal-web is served on `fractal-web.mydomain.net`, then `AUTH_COOKIE_DOMAIN` must be set to `mydomain.net`.
Example: if fractal-vizarr-viewer is served on `fractal-vizarr-viewer.mydomain.net` and fractal-web is served on `fractal-web.mydomain.net`, then `AUTH_COOKIE_DOMAIN` must be set to `mydomain.net`.

If we need to serve these services on different domains a different authentication strategy has to be chosen, for example something token-based. That results in a more complicated setup, possibly involving some extra changes on the vizarr code.

Expand All @@ -47,7 +47,7 @@ if (value.status !== 200 && value.status !== 404) {

Run `npm run build`. This will generate the static files inside the `out` folder. These files will be served by the app contained in this repo.

## Fractal-data setup
## Fractal-vizarr-viewer setup

Copy the file `.env.example` to `.env` and define proper values for the environment variables.

Expand All @@ -66,39 +66,32 @@ http://localhost:3000/?source=http://localhost:3000/data/20200812-CardiomyocyteD

1. You need to have an active instance of `fractal-server` and an active instance of `fractal-web`.
2. You need to log-in to `fractal-web` from the browser, as the `[email protected]` user.
3. Get and install the `fractal-data` application
3. Get and install the `fractal-vizarr-viewer` application

```bash
git clone https://github.com/fractal-analytics-platform/fractal-data.git
cd fractal-data
git clone https://github.com/fractal-analytics-platform/fractal-vizarr-viewer.git
cd fractal-vizarr-viewer
npm install
```

4. Get/patch/install/patch/build `vizarr`

> Note: for simplicity, we assume that `fractal-data` and `vizarr` are subfolders of the same folder:
> Vizarr needs to be built using **pnpm**. To install it you can use `npm install -g pnpm`.
> Note: for simplicity, we assume that `fractal-vizarr-viewer` and `vizarr` are subfolders of the same folder:
```bash
git clone https://github.com/hms-dbmi/vizarr.git
cd vizarr
git checkout ca1b1c5693f3cdc355a1e2f2f6b7bb57ba62d4ed
git apply ../fractal-data/vizarr.patch
npm install
cat node_modules/zarr/core.mjs | sed '3188 i \ if (value.status !== 200 && value.status !== 404) {throw new HTTPError(String(value.status));}' > node_modules/zarr/core.mjs.tmp
mv node_modules/zarr/core.mjs.tmp node_modules/zarr/core.mjs
npm run build
git checkout 55845ffb658fa04ee2fb649a434c4c16c587233e
git apply ../fractal-vizarr-viewer/vizarr.patch
pnpm install
pnpm run build
```

> NOTE that we are applying two patches:
> * A git patch to `vizarr` itself, defined in `fractal-data/vizarr.patch`.
> * A patch to `zarr.js` (as in the PR as in https://github.com/gzuidhof/zarr.js/pull/151), which makes lines 3187-3189 of `node_modules/zarr/core.mjs` look like:
> ```js
> const value = await fetch(url, { ...this.fetchOptions, method });^M
> if (value.status !== 200 && value.status !== 404) {throw new HTTPError(String(value.status));}
> return value.status === 200;^M
> ```
The output is located in the `dist` folder.

5. Create and fill data folder for `fractal-data`:
5. Create and fill data folder for `fractal-vizarr-viewer`:

```bash
mkdir zarr-files
Expand All @@ -107,18 +100,18 @@ wget https://zenodo.org/records/10424292/files/20200812-CardiomyocyteDifferentia
unzip 20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr.zip?download=1
```

6. Set up environment variables for `fractal-data`.
From the `fractal-data` main folder, copy `.env.example` into `.env`, and modify `.env` so that it looks like
6. Set up environment variables for `fractal-vizarr-viewer`.
From the `fractal-vizarr-viewer` main folder, copy `.env.example` into `.env`, and modify `.env` so that it looks like
```
PORT=3000
FRACTAL_SERVER_URL=http://localhost:8000
ZARR_DATA_BASE_PATH=/somewhere/zarr-files/
VIZARR_STATIC_FILES_PATH=/somewhere/vizarr/out/
VIZARR_STATIC_FILES_PATH=/somewhere/vizarr/dist/
BASE_PATH=/vizarr
CACHE_EXPIRATION_TIME=60
```

7. Startup `fractal-data`
7. Startup `fractal-vizarr-viewer`
```bash
npm start
```
Expand All @@ -127,20 +120,73 @@ npm start

## Environment variables

* `PORT`: the port where fractal-data app is served;
* `PORT`: the port where fractal-vizarr-viewer app is served;
* `FRACTAL_SERVER_URL`: the base URL of fractal-server;
* `ZARR_DATA_BASE_PATH`: path to Zarr files served by fractal-data; the app reads files only in this directory;
* `ZARR_DATA_BASE_PATH`: path to Zarr files served by fractal-vizarr-viewer; the app reads files only in this directory;
* `VIZARR_STATIC_FILES_PATH`: path to the files generated running `npm run build` in vizarr source folder;
* `BASE_PATH`: base path of fractal-data application;
* `BASE_PATH`: base path of fractal-vizarr-viewer application;
* `CACHE_EXPIRATION_TIME`: cookie cache TTL in seconds; when user info is retrieved from a cookie calling the current user endpoint on fractal-server the information is cached for the specified amount of seconds, to reduce the number of calls to fractal-server;

## Production setup

Add an Apache configuration to expose fractal-data service on a given path of the public server. The specified location must have the same value set in fractal-data `BASE_PATH` environment variable (the default value is `/vizarr`).
Add an Apache configuration to expose fractal-vizarr-viewer service on a given path of the public server. The specified location must have the same value set in fractal-vizarr-viewer `BASE_PATH` environment variable (the default value is `/vizarr`).

```
<Location /vizarr>
ProxyPass http://127.0.0.1:3000/vizarr
ProxyPassReverse http://127.0.0.1:3000/vizarr
</Location>
```

Add a systemd unit file in `/etc/systemd/system/fractal-vizarr-viewer.service`:

```
[Unit]
Description=Fractal Vizarr Viewer service
After=syslog.target
[Service]
User=fractal
Environment="PORT=3000"
Environment="FRACTAL_SERVER_URL=https://fractal-server.example.com/"
Environment="ZARR_DATA_BASE_PATH=/path/to/zarr-files"
Environment="VIZARR_STATIC_FILES_PATH=/path/to/vizarr/dist"
Environment="BASE_PATH=/vizarr"
Environment="ALLOWED_USERS=/path/to/allowed-users.txt"
Environment="CACHE_EXPIRATION_TIME=60"
ExecStart=/path/to/node /path/to/fractal-vizarr-viewer/dist/app.js
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
```

Enable the service and start it:

```sh
sudo systemctl enable fractal-vizarr-viewer
sudo systemctl start fractal-vizarr-viewer
```

## Docker setup

Build the docker image:

```sh
docker build . -t fractal-vizarr-viewer
```

The following command can be used to start the docker image for testing:

```sh
docker run --network host \
-v /path/to/allowed_users.txt:/allowed_users.txt \
-v /path/to/zarr-files:/zarr-files \
-e ZARR_DATA_BASE_PATH=/zarr-files \
-e FRACTAL_SERVER_URL=http://localhost:8000 \
-e ALLOWED_USERS=/allowed_users.txt \
fractal-vizarr-viewer
```

For production replace the `--network host` option with a proper published port `-p 3000:3000` and set `FRACTAL_SERVER_URL` as an URL using a public domain.
File renamed without changes
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fractal-data",
"version": "1.0.0",
"name": "fractal-vizarr-viewer",
"version": "0.1.0",
"description": "",
"main": "dist/app.js",
"scripts": {
Expand Down
13 changes: 10 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ async function getAuthorizedPath(req: Request): Promise<string | undefined> {
}
const user = await getUserFromCookie(cookie);
if (!user || !allowedUsers.includes(user.email)) {
// Only allowed users can access fractal-data
// Only allowed users can access fractal-vizarr-viewer
return undefined;
}
const completePath = requestPath.startsWith(ZARR_DATA_BASE_PATH) ?
Expand Down Expand Up @@ -122,6 +122,13 @@ async function getUserFromCookie(cookie: string): Promise<{ email: string } | un
app.use(`${basePath}`, express.static(VIZARR_STATIC_FILES_PATH));

// Start server
app.listen(port, () => {
return console.log(`fractal-data is listening at http://localhost:${port}${basePath}`);
const server = app.listen(port, () => {
return console.log(`fractal-vizarr-viewer is listening at http://localhost:${port}${basePath}`);
});

for (const signal of ['SIGINT', 'SIGTERM', 'SIGQUIT']) {
process.on(signal, (signal) => {
console.log(`Process received a ${signal} signal`);
server.close();
});
}
Loading

0 comments on commit 2efc461

Please sign in to comment.