Skip to content

About

The SafeInsights management app serves as an interface for researchers to submit and members to approve study proposals

Topics

Resources

Stars

3 stars

Watchers

2 watching

Forks

Latest commit

Β 

History

7,470 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SafeInsights Management Application

Overview

The management app (also called Basic Management App or BMA) serves as an interface for researchers to submit and reviewers to approve study proposals.

It's responsible for:

  • Researcher creates/modifies/deletes study
  • Reviewer is notified when a study proposal is submitted
  • Researcher is notified when study is approved
  • Reviewer reviews/approves/denies study
  • Reports study status in response to enclave's SetupApp requests

Requirements

Production Environment

  • Node.js version 20.x or higher
  • PostgreSQL

Development Environment

Either:

  • Option 1: Node.js + Docker and Docker Compose (installs PostgreSQL in container)
  • Option 2: Node.js + PostgreSQL installed locally

Development

Local Setup Options

Option 1: Using Docker Compose 🐳

You can use Docker compose to run the app and a PostgreSQL database:

docker compose up

Open http://localhost:4000 with your browser to access the app.

Option 2: Local Setup without Docker πŸ’»

For developing locally without docker compose, you will need to:

  1. Install PostgreSQL and add a .env file that contains a valid DATABASE_URL to access it. Homebrew instructions

  2. Install SeaweedFS: brew install seaweedfs and provision it using:

    • Start server: ./bin/local-seaweedfs
    • Create bucket (only needed once): ./bin/local-createbucket
  3. Install dependencies:

    corepack enable
    pnpm install
  4. Run the development server:

    pnpm run dev

Roles and screens

  • dashboard is located at: /dashboard
  • Reviewers can access the review dashboard at: /<org slug>/dashboard
  • There are two admin types and screens:
    • An organization admin is a member of an organization who can invite other users to that organization. Their admin screen is located at: /admin/team/<org slug>. From there they can administer the users in their organization.
    • An SI Staff admin is a user who belongs to the safe-insights organization (defined as CLERK_ADMIN_ORG_SLUG in the codebase). The screen at /admin/safeinsights allows administrating Organizations. SI Staff administrators are super-admins and can also visit the organization admin screens noted above.

Type-Safe Routing 🧭

The application uses a type-safe routing system located in src/lib/routes that provides compile-time and runtime validation for routes and parameters using Zod schemas. All routes must be defined in the Routes object in src/lib/routes and accessed using it's type safe methods as shown below:

Usage example:

import { Routes, useTypedParams } from '@/lib/routes'

// Build type-safe routes
const route = Routes.studyView({ orgSlug: 'acme', studyId: '123' })

// Use type-safe params in components
const params = useTypedParams(Routes.studyView.schema)
// params.orgSlug and params.studyId are guaranteed to be valid

Creating Your Admin Account πŸ”

To develop locally, you'll need to create your own SI Staff admin account:

  1. Copy the .env.sample file to .env, replacing the XXX strings with values obtained from your teammates. This will set up the app with a sandbox Clerk backend. Your .env file MUST have valid CLERK_SECRET_KEY and DATABASE_URL values (get values from teammates)

  2. Run the admin user creation script:

    pnpm run create-admin-user

    Or if using Docker (with docker compose up running):

    docker compose exec mgmnt-app pnpm run create-admin-user
  3. Follow the interactive prompts to enter your email, password, and name

  4. Sign in at http://localhost:4000 with your new credentials

Note: This creates an SI Staff admin with full access to administer all organizations.

Useful Docker Commands πŸ‹

  • docker compose build - Rebuild the docker image (needed after packages are installed)
  • docker compose exec mgmnt-app ./bin/migrate-dev-db - Run migrations (needs running docker compose up at same time)
  • docker volume rm management-app_pgdata - Delete the database, allowing it to be migrated freshly
  • docker compose down -v - Gentler "reset switch" that stops and removes containers, networks, volumes, but keeps images
  • docker compose down -v --rmi all - Full "reset switch" for DB errors (stops and removes Docker containers, networks, volumes, and all images)
  • docker system prune -a or docker builder prune - Clear your docker cache in case of emergency

Database Management 🐘

  • pnpm exec kysely migrate:make your_migration_name - Creates a migration file, we should use snake_case for migration names
  • pnpm run db:migrate - Run database migrations
  • pnpm exec kysely migrate:down -1 - Rollback the last applied migration

Database Visualization with DBGate πŸ“Š

When running the application using docker compose up, a DBGate instance is also started. DBGate provides a web-based interface for visualizing and interacting with the PostgreSQL database used by the application.

You can access DBGate in your browser at: http://localhost:3000

The connection details for the development database (mgmnt_dev) are pre-configured.

Enclave API Routes πŸ”’

API routes are protected by an authorization header containing a JWT Bearer which is signed with an RSA private key held by the organization. The public key is stored in the organization admin panel.

To generate a public/private key pair you can run:

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private_key.pem -out public_key.pem

Component Explorer (Ladle) 🧩

We use Ladle to develop and preview UI in isolation β€” design-system primitives, page layouts, modals, and auth/MFA flows β€” without running the full app, a session, or a database. Stories live next to their components as *.stories.tsx; the Ladle config and the Vite shims that let app components render outside Next.js live under .ladle/.

Develop

pnpm ladle        # starts the explorer (default http://localhost:61000) with hot reload

Build a shareable, offline copy

To hand someone a self-contained copy they can open with no server and no install:

pnpm ladle:build:standalone   # β†’ .ladle/dist-standalone/index.html

This produces a single (~2 MB) index.html with all JS/CSS inlined and no code-split chunks, so it opens from a plain file:// double-click. A normal Ladle build can't do this β€” it emits ES-module scripts and lazy-loaded chunks that browsers block over file://. Zip it to share:

( cd .ladle/dist-standalone && zip -r ../dist-standalone.zip index.html favicon-*.svg touch-icon-*.png manifest-*.webmanifest )
# β†’ .ladle/dist-standalone.zip β€” recipient unzips and double-clicks index.html

The brand font (Open Sans) still loads from Google Fonts, so fully offline it falls back to a system sans-serif; everything else is embedded.

Build for static hosting

pnpm ladle:build   # β†’ .ladle/dist/ (code-split; serve over HTTP, e.g. `pnpm dlx serve .ladle/dist`)

Published to GitHub Pages

Every push to main builds the explorer and publishes it via the Ladle Pages workflow β€” browse it at https://safeinsights.github.io/management-app/. The build runs ladle:build:pages, which passes the Pages base path through --base so assets resolve under the project subpath. Publishing happens only from main so the Pages deploy credentials are never exposed to PR-controlled code (see OTTER-545).

One-time setup (maintainer, in repo Settings β†’ Pages): set Source to GitHub Actions. After that the workflow deploys on its own.

Testing

Unit Testing

pnpm run test:unit

This needs a local Postgres and SeaweedFS (see Option 2 above).

Unit Testing in Docker 🐳

./bin/docker-test runs the same Vitest suite in containers, against a throwaway database it migrates and seeds itself. It is self-contained: no local Postgres, S3, or .env file needed. Each checkout also gets its own containers and volumes, so several clones can run their unit tests in parallel.

Command What it does
./bin/docker-test Run the whole suite
./bin/docker-test -- --run PATTERN Run specific files; PATTERN is a substring match on the path, not a regex (several can be space-separated)
./bin/docker-test -- --run 'foo' -t 'REGEX' Also filter by test name (describe/it) with a regex
./bin/docker-test --clean Fresh database and node_modules; rarely needed, since migrations are applied on each run
./bin/docker-test --help All flags, including the --down and --prune cleanup options

To have Claude Code use docker unit tests, see the "Claude Code integration" section of PR #787.

E2E Testing with Playwright 🎭

To run playwright tests locally, you'll need to install playwright:

  • pnpm install
  • pnpm exec playwright install

And then run playwright: pnpm exec playwright test --ui to view status, or run pnpm exec playwright test --headed to interact with browser as it runs.

If there are failures, a trace file will be stored under the ./test-results directory. For instance to view a failure with the researcher creating a study, you can run: pnpm exec playwright show-trace ./test-results/researcher-create-study-app-researcher-creates-a-study-chromium/trace.zip

If there are playwright failures on GitHub actions, the trace file will be stored under the "Artifacts" section of the code run. You can download the trace.zip and then run pnpm exec playwright show-trace <path to download> to view the failure details.

Accessibility checks

tests/a11y.spec.ts scans a fixed list of pages with axe. It runs as the a11y Playwright project. All e2e projects run with reduced motion, so no element is scanned halfway through a transition. Run it alone with pnpm exec playwright test --project=a11y.

  • Any violation tagged WCAG 2.0, 2.1 or 2.2 level A or AA fails the test, whatever its impact. Best-practice findings are reported but do not fail.
  • Each scan writes its full axe JSON (axe-*.json) to the test's output folder and attaches it to the report. CI uploads all of them as the a11y-results artifact on every run, pass or fail. Files named axe-selftest-*.json come from the gate self-tests and contain violations on purpose.
  • A failure lists the rule, its impact, the help text and the CSS selectors of the elements that fail.
  • The JSON also lists axe's incomplete results: checks that axe could not decide, often color contrast over images or gradients. They do not fail the test and need a manual look.
  • No rule is disabled and no part of the app is excluded. Fix the markup instead of disabling a rule.
  • On a table that other workers fill in parallel, pass onlyRowsWith with text from the rows the test seeded. The scan then leaves out the other workers' rows, so every run scans the same input.
  • To scan a new page, add a test that waits for the page's loaded content and then calls expectNoA11yViolations(page, label) from tests/a11y.helpers.ts. Any other spec can call it too.
  • Automated scanning finds only part of the WCAG issues. Keyboard use, focus order and screen-reader checks remain manual.

trivy vulnerability scanner

To run trivy locally, you'll need to install it. For mac that would be e.g. brew install trivy

And then run it to get report back on your terminal

trivy fs --config trivy.yaml --format table .

Deployment πŸš€

Suggested Way of Deploying Lambda Functions (for future)

The new way to deploy is from the IaC repo, run:

./bin/deploy-lambda -p <AWS CLI profile> -c <management app dir> -a managementApp

Troubleshooting/Notes πŸ”§

There are a few CLI applications to debug the API end endpoints:

pnpm exec tsx bin/debug/fetch-runnable.ts -u http://localhost:4000 -o openstax -k <path to private key>
pnpm exec tsx bin/debug/set-status.ts -u http://localhost:4000 -o openstax -k <path to private key> -s <status: JOB-PROVISIONING | JOB-RUNNING | JOB-ERRORED> -j <uuid of job>
pnpm exec tsx bin/debug/upload-results.ts -u http://localhost:4000 -o openstax -k <path to private key> -j <uuid of job> -r <path to file to upload as results> -l <path of file to upload as logs>
pnpm exec tsx bin/debug/keys.ts -u http://localhost:4000 -o openstax -k <path to private key> -j <uuid of job>

The scripts will use default values tailored for local development:

  • origin will default to http://localhost:4000
  • organization to openstax and openstax-lab
  • key to tests/support/private_key.pem (local dev openstax defaults to using the public key pair of this)

Examples:

  • view runnable jobs details (useful for obtaining job uuids): pnpm exec tsx bin/debug/fetch-runnable.ts
  • set a job as running: pnpm exec tsx bin/debug/set-status.ts -s JOB-RUNNING -j <job uuid>
  • upload results: pnpm exec tsx bin/debug/set-status.ts -f tests/assets/results-with-pii.csv -j <job uuid>

Currently, it is possible to upload results and then set status back to RUNNING to force the run to re-appear in the runnable api results and repeatedly upload files. While useful for testing, do not depend on that behavior: it's likely we'll not allow it in later versions.

Resources πŸ“š

License

This application is released under the GNU Affero General Public License v3.0. It bundles third-party components covered by separate licenses, including LGPL-3.0-or-later code (libvips, transitively via sharp). See THIRD_PARTY_LICENSES.md for the complete list, source-availability information, and notes on how to exercise LGPL rights against the distributed image.

About

The SafeInsights management app serves as an interface for researchers to submit and members to approve study proposals

Topics

Resources

Stars

3 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages