Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion .build-files/docker-compose-local.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,107 @@ services:
- path: ${DOCKER_ENV_FILE} # relative to docker compose (has to be in same directory)
required: true

# Depends on database. Database must be healthy before starting server
server:
build:
context: ../ # relative to docker compose
dockerfile: ./.build-files/Dockerfile.server # relatvie to build context
args:
DOCKER_ENV_FILE: ${DOCKER_ENV_FILE}
ports:
- "8000:8000"
environment:
DJANGO_ENV_FILE: "/app/.env"
volumes:
# - ../apps/server/api/migrations:/app/api/migrations
- ${MIGRATIONS_SOURCE}:/app/api/migrations
- ${LOCAL_DATA_VOLUME_LOCATION}:/app/data
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
data:
condition: service_started

celery:
build:
context: ../ # relative to docker compose
dockerfile: ./.build-files/Dockerfile.celery # relatvie to build context
args:
DOCKER_ENV_FILE: ${DOCKER_ENV_FILE}
command:
[
"celery",
"--app",
"celery_app",
"worker",
"--loglevel",
"info",
"--without-heartbeat",
"-c",
"4",
]
# Docker Compose does not set the TTY width, which causes Celery errors
tty: false
environment:
DJANGO_ENV_FILE: "/app/.env"
volumes:
- ${LOCAL_DATA_VOLUME_LOCATION}:/app/data
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
data:
condition: service_started

# MySQL service. Has healthcheck which uses mysqladmin to ping before reporting healthy.
db:
image: mysql:latest
environment:
MYSQL_DATABASE: ${DATABASE_NAME}
MYSQL_USER: ${DATABASE_USER}
MYSQL_PASSWORD: ${DATABASE_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test:
[
"CMD",
"mysqladmin",
"ping",
"-h",
"localhost",
"-u",
"root",
"-p${DATABASE_ROOT_PASSWORD}",
]
timeout: 20s
retries: 10
env_file:
- path: ${DOCKER_ENV_FILE} # relative to docker compose (has to be in same directory)
required: true

redis:
image: redis:latest
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5

data:
build:
context: ./
dockerfile: Dockerfile.data
ports:
- "9000:9000"
- "9001:9000"
volumes:
- ${LOCAL_DATA_VOLUME_LOCATION}:/app/data # relative to docker compose
duckdb:
Expand All @@ -40,5 +135,6 @@ services:
- duckdb-data:/app/duckdb-data

volumes:
mysql-data:
duckdb-data:
migrations_volume:
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ certs
#Random
apps/server/api/*.jpg
apps/server/api/secrets.json
apps/server/passwords.json
.env
.env.production
.env.*
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,34 @@ In the root of the repository, run the following following command:
python3 build.py --prepare-dev
```

### Prepare Development Environment and Run Separately with Experiment Visibility Control
### User Authentication & Experiment Gating

Experiment visibility control allows users to control which experiments are visible to them. You specify which experiments are visible to users in the `aa_index.json` file.
Loon supports user-based access control. You can manage users and define which experiments are visible to specific users.

```bash
python3 build.py --prepare-dev --experiment-visibility-control
```
#### Managing Users
Admin users can manage accounts and passwords via the **Admin Control Panel** accessible at `/admin` within the application.

Experiment visibility control example:
#### Gating Experiments
Access is defined in the `aa_index.json` file using the `users` array. Experiments can be either public (string) or gated (object with `users` array).

Example `aa_index.json`:
```json
{
"experiments": [
{
"filename": "Data Discovery - Automated Cell Lineage Tracking.json",
"visible-by-default": true
"filename": "restricted_experiment.json",
"users": ["lab", "jess"]
},
"Quality Control - Cancer Triggering Microenvironments.json",
"Communication - Tumorigenic Cell States.json"
"public_experiment.json"
]
}
```

Experiments are filtered as follows:
- **Plain string**: Visible to everyone.
- **Admin**: Sees all experiments regardless of gating.
- **Gated (users list)**: Only visible to listed users and administrators.


This will generate the necessary environment file in the client directory while still running the rest of the container. Then, the development server can be started separately by the following:

Expand Down Expand Up @@ -133,7 +138,6 @@ The build script will do its best to validate the config before starting the doc
| -o, --overwrite | When set, any settings in your configuration file which are present as environment variables in the current session will be overwritten. | -o |
| -s, --disable-spinner | When set, disables inline spinner | -s |
| --prepare-dev | When set, will create the `.env` file based on the current configuration that is required to run a separate client development server. | --prepare-dev |
| --experiment-visibility-control | When set, will create the `.env` file based on the current configuration that is required to run a separate client development server. | --experiment-visibility-control|

In the repository, you will see two docker directories: `docker` and `docker-local`. The main deployment will use the `docker` directory. The `docker-local` directory is a separate local version of Loon which we will discuss shortly. Below are some examples.

Expand Down
22 changes: 22 additions & 0 deletions apps/client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import { useProvenanceStore } from '@/stores/misc/provenanceStore';
import { onKeyStroke } from '@vueuse/core';
import { router } from '@/router';
import LBtn from './components/custom/LBtn.vue';
import LoginModal from './components/auth/LoginModal.vue';
import { useAuthStore } from '@/stores/misc/authStore';

const $q = useQuasar();
const provenanceStore = useProvenanceStore();
const authStore = useAuthStore();

onKeyStroke(['z', 'Z'], (e: KeyboardEvent) => {
if (globalSettings.usingMac && !e.metaKey) return;
Expand All @@ -26,6 +29,8 @@ onKeyStroke(['z', 'Z'], (e: KeyboardEvent) => {
});

const globalSettings = useGlobalSettings();
const showLoginModal = ref(false);

watch(
() => globalSettings.darkMode,
(newDarkMode: boolean) => {
Expand Down Expand Up @@ -71,8 +76,25 @@ onBeforeMount(() => {
type="basic"
label="Upload"
/>
<q-btn
v-if="authStore.currentUser === 'admin'"
@click="router.push('/admin')"
flat
label="Admin"
class="q-ml-sm"
/>
<q-btn
flat
round
dense
icon="account_circle"
class="q-ml-sm"
@click="showLoginModal = true"
:color="globalSettings.darkMode ? 'white' : 'black'"
/>
</q-toolbar>
</q-header>
<LoginModal v-model="showLoginModal" />
<GlobalSettingsView></GlobalSettingsView>
<q-page-container>
<q-page>
Expand Down
Loading