Files
Tautvydas Derzinskas 358dfc1a03 feat: system notifications updates, Thingport grab in System health check, node 24+ (#52)
* feat: update system notifications

* feat: add Thingport grab to system health check

* ci: upgrade to Node.js 24 and centralize version management
2026-10-17 10:44:13 +02:00

11 KiB
Raw Permalink Blame History

Development

How to run Thingport locally with hot reload, so you can work on it. If you just want to run an instance, follow the Docker Compose instructions in the README instead.

What's in the repo

Directory What it is
backend/ Express + Prisma API (TypeScript). Serves everything under /api/*.
frontend/ React + Vite single-page app.
extension/ "Thingport Grab" browser extension -- TypeScript + SCSS, built with esbuild.
bridge/ Slicer bridge helper app (Go).
docs/ This documentation.

Prerequisites

  • Node.js 24 or newer. The root .nvmrc says 24 (nvm use picks it up), and CI and the Docker images run on it.
  • Docker, to run Postgres. Everything else runs natively so you get hot reload.
  • Go 1.21+ -- only if you're changing bridge/.

1. Install dependencies

git clone https://github.com/TautvydasDerzinskas/Thingport.git
cd Thingport

npm install # root -- installs the husky pre-commit hook
npm --prefix backend ci
npm --prefix frontend ci

Each project has its own package-lock.json; there is no workspace root tying them together.

2. Start Postgres

The db service in docker-compose.yml has no host port mapping on purpose -- it's only reachable from inside the Compose network, so a natively-run backend cannot connect to it. Run your own container for development:

docker run -d --name thingport-db \
  -e POSTGRES_USER=thingport \
  -e POSTGRES_PASSWORD=thingport \
  -e POSTGRES_DB=thingport_dev \
  -p 5432:5432 \
  postgres:16-alpine

If something already listens on 5432, map a different host port and adjust DATABASE_URL below.

3. Configure the backend

cp backend/.env.example backend/.env

backend/.env.example is written for the Docker Compose deployment, so two values must change for local development:

Variable Set it to Why
DATABASE_URL postgresql://thingport:thingport@localhost:5432/thingport_dev?schema=public The example points at db:5432 -- the Compose service hostname, which doesn't resolve outside Docker.
FILE_STORAGE an absolute or relative path you own, e.g. ./storage The example uses /app/storage, the path inside the container.

Also set AUTH_SECRET to any non-empty random string -- it signs login tokens, and the example value is a placeholder.

Leave API_PORT=8000. The Vite dev server proxies /api to http://localhost:8000 (frontend/vite.config.js), so changing one means changing both.

backend/.env is gitignored. Never commit it.

4. Create the database schema

cd backend
npm run prisma:generate   # required -- there is no postinstall hook, so the client isn't generated by `npm ci`
npx prisma migrate deploy # applies the 27 committed migrations

5. Run it

In two terminals:

npm --prefix backend run dev  # tsx watch  -> http://localhost:8000
npm --prefix frontend run dev # vite --host -> http://localhost:5173

Open http://localhost:5173. The first account you register becomes the admin -- see backend/src/routes/auth.ts, which bootstraps the first user as ADMIN and everyone after as MEMBER.

Optional: FlareSolverr

Thingiverse sits behind Cloudflare, and some imports need FlareSolverr to get through. It's optional -- without it those imports will simply fail.

docker run -d --name thingport-flaresolverr -p 8191:8191 ghcr.io/flaresolverr/flaresolverr:latest

Then set its URL (http://localhost:8191, with or without /v1) in Administration → Connections → FlareSolverr. FLARESOLVERR_URL in backend/.env is copied into that setting on the first startup; after that the saved value wins and the env var is ignored.

Running the tests

Backend

19 test files, 151 tests. They run against a real Postgres database, registering accounts and creating and deleting rows as they go.

Warning

npm test loads DATABASE_URL from backend/.env through dotenv. If that points at your development database, the suite will mutate it. Always pass the variable on the command line -- an explicit shell variable takes precedence over .env, so this is safe.

docker run -d --name thingport-test-db \
  -e POSTGRES_USER=thingport -e POSTGRES_PASSWORD=thingport -e POSTGRES_DB=thingport_test \
  -p 55432:5432 postgres:16-alpine

cd backend
export TEST_DB="postgresql://thingport:thingport@localhost:55432/thingport_test?schema=public"

DATABASE_URL="$TEST_DB" npx prisma migrate deploy
DATABASE_URL="$TEST_DB" FILE_STORAGE=/tmp/thingport-test-storage npm test

The suite takes about 30 seconds. It runs test files sequentially on purpose -- several files mutate the same global Setting rows, and parallel workers race each other. See the comment in backend/vitest.config.ts.

Frontend

There are no frontend tests yet. npm run test:run uses --passWithNoTests, so it succeeds against an empty suite. New tests are welcome.

Regression pack

Playwright scenarios in regression/ that drive the real app in Chromium: sign-up, sign-in, uploading (single and multiple files, zips, folder trees), the consume folder, collections, and the sidebar (navigation, the admin-only route, bookmarks and their drag-to-reorder, collapsing). Upload fixtures are generated in tests/fixtures.ts rather than checked in. The consume folder is regression/consume/, bind-mounted into the backend for the run. run.sh builds the backend and frontend images from your checkout, starts them with Postgres on port 8090, runs the scenarios, and then removes the stack. The database and storage live on tmpfs, so every run starts empty and leaves nothing behind. It never touches your development database. It needs Docker.

cd regression
npm install
npx playwright install chromium
./run.sh                      # whole pack
./run.sh tests/models.spec.ts # extra arguments go to `playwright test`

CI runs it every Saturday at 00:00 UTC (.github/workflows/regression.yml). You can also start it from the Actions tab. When it fails, the HTML report, traces and container logs are uploaded as the regression-report artifact.

App screenshots

The README and website screenshots in frontend/src/assets/screenshots/ are captured from a running instance with a populated library, by regression/screenshots.mjs (the extension's screenshots aren't covered). It signs in, captures every shot at 1400×790 and 2× scale, and overwrites the files. The account's theme and Categories/Folders choice are switched for the shots and restored afterwards.

cd regression
THINGPORT_PASSWORD='…' npm run screenshots -- --url https://thingport.example --email you@example.com
npm run screenshots -- --url … --email … --only 03,04     # just some shots
npm run screenshots -- --url … --email … --model "Benchy" # model for the details and 3D shots

--out writes somewhere else instead, e.g. to compare before replacing. Self-signed certificates are accepted.

Quality checks

Command What it runs
npm run format Prettier, gofmt, prisma format
npm run format:check the same, check only (CI)
npm --prefix backend run lint oxlint
npm --prefix frontend run lint oxlint
npm --prefix frontend run typecheck tsc --noEmit
npm --prefix frontend run verify typecheck + lint + tests + build

A husky pre-commit hook formats staged files and runs oxlint over whichever project you touched (see lint-staged.config.js). It does not typecheck, so run typecheck yourself before pushing frontend changes -- CI will.

Extension

cd extension
npm install
npm run dev # watch build into dist/chrome (npm run dev:firefox for dist/firefox)
  • Chrome / Edge: chrome://extensions -> enable Developer mode -> Load unpacked -> select extension/dist/chrome.
  • Firefox: about:debugging#/runtime/this-firefox -> Load Temporary Add-on -> select extension/dist/firefox/manifest.json.

Reload the extension after each rebuild, and point it at http://localhost:5173 in the popup. Chrome and Edge run the background bundle as a service worker while Firefox runs it as an event page -- if you add top-level code there, make sure it works in both. See extension/CONTRIBUTING.md for the code layout, the per-browser builds, the store zips and the screenshot generator.

Bridge

cd bridge
go test ./...
go run ./cmd/thingport-bridge

macOS URL-scheme handling uses Cocoa and needs cgo, so the macOS build only works on a Mac. Windows and Linux binaries cross-compile with CGO_ENABLED=0.

System notifications

Announcements for every instance, such as a known bug or a new feature, live in system-notifications.json at the repo root. Each backend fetches it from main at startup and every 6 hours, and sends each entry once to each user it's meant for, as an ordinary notification (the System tab). To post one, add an entry to entries and merge it:

{
  "entries": [
    {
      "id": "2026-10-imports-fail-on-printables",
      "title": "Printables imports fail since Oct 10",
      "body": "Printables changed its API. A fix is on the way; update when it lands.",
      "url": "https://github.com/TautvydasDerzinskas/Thingport/issues/123",
      "published_at": "2026-10-11T12:00:00Z",
      "audience": "admins",
      "version": "<1.2.0"
    }
  ]
}
  • id must be unique and never reused: it's what stops an entry being sent twice.
  • body and url are optional; url must be https://, and the notification shows its domain.
  • path is an optional in-app page opened on click, e.g. /admin.
  • published_at is the date shown; a future date waits until then.
  • audience is admins (the default) or all for every user.
  • version limits it to instances on certain versions: 1.2.0 exactly, or <, <=, >, >= a version. Leave it out for all versions. An instance that later updates into the range still gets it.
  • Versions before 1.0.0 read only the notifications list and ignore version, so add there only what every one of them should get; everything else goes in entries.
  • Entries older than 90 days aren't sent, so old ones can stay in the file or be removed.
  • SYSTEM_NOTIFICATIONS_URL points a backend at another file; an empty value turns fetching off.