185 lines
7.4 KiB
Markdown
185 lines
7.4 KiB
Markdown
# Homarr Workshop
|
|
|
|
Workshop is the public, community-driven PocketBase service behind the Workshop pages in Homarr's Docusaurus site. It
|
|
stores Custom JSX v2 widgets and Custom CSS submissions. Installation-time validation inside Homarr remains
|
|
authoritative: public content must be reviewed before it is installed.
|
|
|
|
The runtime base is PocketBase 0.39.9 pinned by its multi-platform OCI digest in `apps/workshop/Dockerfile`. Update the
|
|
version and digest together, then run the Workshop integration and production-image smoke suites.
|
|
|
|
The internal PocketBase field `users.isAdmin` means **Workshop moderator**. It does not grant Homarr administrator
|
|
access. Moderators can review private reports, dismiss reports, and remove submissions or comments.
|
|
|
|
## Run locally
|
|
|
|
From the repository root:
|
|
|
|
```sh
|
|
cp apps/workshop/.env.example apps/workshop/.env
|
|
pnpm install --frozen-lockfile
|
|
docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
|
|
```
|
|
|
|
This starts persistent PocketBase at `http://127.0.0.1:8090`. Start Docusaurus in another terminal:
|
|
|
|
```sh
|
|
WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
|
|
```
|
|
|
|
Open:
|
|
|
|
- PocketBase administration: `http://127.0.0.1:8090/_/`
|
|
- Workshop: `http://127.0.0.1:3003/workshop`
|
|
|
|
Create the first PocketBase superuser in the administration UI, or use:
|
|
|
|
```sh
|
|
docker compose -f apps/workshop/docker-compose.yml exec workshop \
|
|
pocketbase superuser create you@example.com 'replace-with-a-strong-password' --dir=/pb_data
|
|
```
|
|
|
|
Stopping the Compose process preserves `homarr-workshop_pb_data`. To inspect or stop the service separately:
|
|
|
|
```sh
|
|
docker compose -f apps/workshop/docker-compose.yml logs -f workshop
|
|
docker compose -f apps/workshop/docker-compose.yml stop workshop
|
|
```
|
|
|
|
### Pre-release migration reset
|
|
|
|
The v2 branch consolidates the unreleased Workshop migrations into one initial migration. A PocketBase volume that ran
|
|
an earlier v2 Workshop migration chain is unsupported. Back up anything you need, then use the intentional local reset
|
|
command near the end of this document and start the stack again. Startup never deletes a volume automatically. This
|
|
exception applies only to disposable pre-release volumes and must not be used for production data.
|
|
|
|
## Connect the Homarr application
|
|
|
|
Use the same URL contract in the repository-root `.env`, then restart Homarr:
|
|
|
|
```dotenv
|
|
HOMARR_WEBSITE_URL=http://127.0.0.1:3003
|
|
WORKSHOP_API_URL=http://127.0.0.1:8090
|
|
WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop
|
|
WORKSHOP_PUBLIC_ORIGIN=http://127.0.0.1:3003
|
|
PB_ALLOWED_ORIGINS=*
|
|
```
|
|
|
|
```sh
|
|
pnpm dev
|
|
```
|
|
|
|
`WORKSHOP_API_URL` controls API calls. `WORKSHOP_WEB_URL` controls links from Homarr. `HOMARR_WEBSITE_URL` is the
|
|
documentation/site base. All three must be absolute HTTP(S) URLs. The old Docusaurus-only `WORKSHOP_URL` variable
|
|
remains a deprecated alias for one release.
|
|
|
|
## Optional GitHub OAuth
|
|
|
|
Create a GitHub OAuth application with this callback:
|
|
|
|
```text
|
|
https://<workshop-host>/api/oauth2-redirect
|
|
```
|
|
|
|
Set both credentials in `.env`:
|
|
|
|
```dotenv
|
|
GITHUB_CLIENT_ID=...
|
|
GITHUB_CLIENT_SECRET=...
|
|
```
|
|
|
|
OAuth settings synchronize on every PocketBase startup. Restart after rotation. A production deployment must never
|
|
supply only one credential. `PB_ALLOWED_ORIGINS=*` is appropriate for the central community because arbitrary
|
|
self-hosted Homarr origins must open its OAuth popup; a restricted list must include every allowed browser origin.
|
|
Workshop uses the OAuth provider username as the public display name instead of the profile's full name.
|
|
|
|
## Promote a Workshop moderator
|
|
|
|
Sign in once through GitHub so the user record exists. In the PocketBase administration UI, open the `users` collection
|
|
and set `isAdmin` to `true`. The user must sign in again. Set it back to `false` to remove moderator access.
|
|
|
|
Regular API rules cannot set this field. Never describe a Workshop moderator as a Homarr administrator: they are
|
|
separate roles in separate services.
|
|
|
|
## Validation and tests
|
|
|
|
The official browser and shared client validate content with the canonical TypeScript schema and send the matching
|
|
schema identifier. PocketBase stores submission content without parsing, normalizing, or validating it; it only owns
|
|
revision fields and rejects stale compare-and-swap updates. Homarr validates downloaded content again before
|
|
installation, so direct API submissions that bypass the official client can be rejected there.
|
|
|
|
Run the disposable integration suite:
|
|
|
|
```sh
|
|
pnpm test:workshop
|
|
pnpm --filter @homarr/workshop typecheck
|
|
pnpm --filter @homarr/docs typecheck
|
|
```
|
|
|
|
The integration suite uses a separate Compose project and deletes only its disposable volume.
|
|
|
|
## Backup and restore
|
|
|
|
PocketBase data lives in `/pb_data`. Stop writes and copy it into a new, empty host directory for each
|
|
filesystem-level backup:
|
|
|
|
```sh
|
|
docker compose -f apps/workshop/docker-compose.yml stop workshop
|
|
docker compose -f apps/workshop/docker-compose.yml cp workshop:/pb_data/. ./workshop-backup
|
|
docker compose -f apps/workshop/docker-compose.yml start workshop
|
|
```
|
|
|
|
Never copy a backup over a non-empty PocketBase volume: files created after the backup could survive and make the
|
|
restore inconsistent. For a development restore, first verify that the selected backup contains the database, then
|
|
empty the stopped volume before copying it:
|
|
|
|
```sh
|
|
test -f ./workshop-backup/data.db
|
|
docker compose -f apps/workshop/docker-compose.yml stop workshop
|
|
docker compose -f apps/workshop/docker-compose.yml run --rm --no-deps --entrypoint sh workshop \
|
|
-c 'find /pb_data -mindepth 1 -maxdepth 1 -exec rm -rf {} +'
|
|
docker compose -f apps/workshop/docker-compose.yml cp ./workshop-backup/. workshop:/pb_data
|
|
docker compose -f apps/workshop/docker-compose.yml start workshop
|
|
curl --fail http://127.0.0.1:8090/api/health
|
|
curl --fail 'http://127.0.0.1:8090/api/collections/workshop_listings/records?perPage=1'
|
|
```
|
|
|
|
The cleanup command irreversibly empties the selected Compose volume, so confirm the backup path and preserve the
|
|
current volume separately before running it. After the HTTP checks, verify sign-in, reports, and a test installation.
|
|
Production backups must be versioned, access-controlled, restored into a clean staging volume, and validated before a
|
|
release.
|
|
|
|
## Production image and operations
|
|
|
|
The production target serves the built documentation from `/pb_public` and PocketBase from the same image. Required
|
|
production configuration:
|
|
|
|
```dotenv
|
|
HOMARR_WEBSITE_URL=https://homarr.dev
|
|
WORKSHOP_API_URL=https://homarr.dev
|
|
WORKSHOP_WEB_URL=https://homarr.dev/workshop
|
|
WORKSHOP_PUBLIC_ORIGIN=https://homarr.dev
|
|
PB_ALLOWED_ORIGINS=*
|
|
```
|
|
|
|
Build and smoke-test the combined image locally:
|
|
|
|
```sh
|
|
docker compose -f apps/workshop/docker-compose.yml --profile docs up --build --detach docs
|
|
curl --fail http://127.0.0.1:3003/api/health
|
|
curl --fail http://127.0.0.1:3003/
|
|
curl --fail http://127.0.0.1:3003/workshop
|
|
docker compose -f apps/workshop/docker-compose.yml --profile docs stop docs
|
|
```
|
|
|
|
Normal `up`, `start`, `stop`, and `down` operations retain the named volume. The following command is an intentional,
|
|
irreversible local reset:
|
|
|
|
```sh
|
|
docker compose -f apps/workshop/docker-compose.yml down --volumes
|
|
```
|
|
|
|
Never run the reset command against production. Release by immutable image digest, back up application and PocketBase
|
|
data first, rehearse restore and rollback in staging, and promote the exact tested digest. The Algolia recrawl workflow
|
|
remains manual until the external Homarr website host can dispatch it after promotion; publishing the image to GHCR does
|
|
not make that image live.
|