This project serves as a platform to facilitate the creation, management, and viewing of academic proposals. The system allows users to create new proposals, view proposal details, manage feedback, and update proposal statuses. It categorizes proposals based on their topic area and differentiates between student and supervisor roles, ensuring a clear and organized workflow.
The motivation behind this project is to offer academic institutions an efficient mechanism to simplify the proposal submission, evaluation, and feedback cycles, thereby facilitating a smoother pairing of students with their respective supervisors.
- Node.js 24.x
- pnpm 11.9.x
- PostgreSQL database
How to install the project:
# Clone the repository
git clone https://github.com/uzh-bf/thesis-platform.git
# Navigate into the directory
cd thesis-platform
# Install dependencies
pnpm install❗️Make sure your IP address has access to the PostgreSQL database (include IP for Azure DB on Azure).❗️
For local database development, run PostgreSQL locally or use the configured development database. Commands read standard environment variables such as DATABASE_URL:
DATABASE_URL="postgresql://thesis:<local-password>@localhost:5432/thesis?sslmode=disable"For shared secrets, run infisical login and infisical init locally for the thesis platform project, then use Infisical to wrap the command:
infisical run --env=dev -- pnpm dev
infisical run --env=stg -- pnpm staging:db:auditFor a fully local development stack without shared secrets, copy
.env.local.template to .env.local, then run:
cp .env.local.template .env.local
docker compose up -d postgres azurite oidc mailhog
pnpm run prisma:setup:local
pnpm run dev:localThe local OIDC mock signs in admin@example.com as a development admin through
the Local OIDC provider. Azurite serves Blob Storage locally at
http://127.0.0.1:11000/devstoreaccount1, and Postgres is published on
localhost:15432. The compose next profile remains a container smoke path;
the documented local development path runs Next on the host.
Run the browser smoke test against the same local stack with:
pnpm run test:e2eThe E2E setup script starts and checks PostgreSQL, Azurite, and the OIDC mock, then verifies OpenAPI health, local sign-in, the admin UI, SAS upload from the browser, and Azurite blob readback.
Browser uploads use short-lived, blob-scoped SAS URLs generated by the server. The browser stores the returned blob name in the form payload; it does not receive a container-wide write token.
# Run the web app in developer mode (wrap with Infisical for shared secrets)
pnpm run dev
# Run the web app against the local Docker services
pnpm run dev:localThe web app should now be visible on http://localhost:3000.
- Node.js is pinned to
24.18.0and pnpm to11.9.0through Volta,engines, andpackageManager. - Next.js 16 uses Turbopack for development and production builds. React
Compiler is enabled in
next.config.jsthroughreactCompiler: true. - Auth stays on latest NextAuth v4 patch for this branch. Auth.js/NextAuth v5 is a separate migration and was deferred because local OIDC removes the development Auth0 dependency without changing the production auth contract.
- Development auth uses the local OIDC mock when
LOCAL_OIDC_*variables are present. Azure AD and Auth0 remain environment-gated providers for deployed environments. - The default app Docker image uses distroless Node 24. The
migration-runnerDocker target keeps Prisma tooling for database migrations. - Database migrations run as an Argo CD
PreSynchook indeploy/chart_new/templates/migration-job.yaml. Whenimage.runtimeisdistroless, Helm requiresmigration.image.tagso migrations use the migration-capable image instead of the app image. The only other supported runtime value isnode-runner, which keeps Prisma tooling in the app image for compatibility rollouts. - Deployment values created by the image-build workflows must update the app
tag,
image.runtime: distroless, andmigration.image.tagtogether. - Roll out through staging first and promote to production after staging proof. The IBW production values remain unchanged until a separate IBW rollout.
Deployment is GitOps-based. ArgoCD pulls this repository and renders:
deploy/chart_newdeploy/stg_new/values.yamldeploy/prd_new/values.yamldeploy/prd_ibw_new/values.yaml
Do not deploy this app with local Helmfile or envsubst scripts. Runtime secrets come from Infisical through Kubernetes ExternalSecrets.
GitHub Actions build images and commit deployment image tags directly to main.
ArgoCD then syncs the desired state from the _new values files.
Direct deployment commits use the repository Actions secret
DEPLOY_PUSH_TOKEN. Create a fine-grained personal access token owned by an
actor listed under Allow specified actors to bypass required pull requests
for main.
Configure the token with:
- Repository access: only
uzh-bf/thesis-platform - Repository permission: Contents — Read and write
Do not enable force pushes. The workflows fetch current main and use normal
fast-forward pushes. Add the token under Settings → Secrets and variables →
Actions → New repository secret as DEPLOY_PUSH_TOKEN.
- A qualifying push to
mainbuilds the staging ARM64 app and migration images, then commits the immutable image tag,image.runtime: distroless, and the matchingmigration.image.tagtodeploy/stg_new/values.yaml. pnpm release:publishcreates and pushes the release commit and tag. After both production ARM64 images build successfully, Actions commits the immutable image tag (plusimage.runtime: distrolessand the matchingmigration.image.tagfor DF) todeploy/prd_new/values.yamland the image tag todeploy/prd_ibw_new/values.yaml. The IBW image stays on thenode-runnertarget until its separate distroless rollout.pnpm releaseonly creates the release commit and tag locally. It does not start the production workflow until those refs are pushed.
Deployment commits contain [skip ci] and only modify the relevant values
files, preventing build loops.
# Restart the app
kubectl rollout restart -n thesis-platform deployment thesis-platformPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the AGPLv3 - see the LICENSE file for details.