This project is a small WOPI host for Collabora Online CODE. It lists Office files from a mounted folder, opens them in Collabora, and writes saves back into the same folder.
- reads supported Office files from a local or mounted folder
- shows a simple browser UI with nested paths
- opens the selected file inside an embedded Collabora iframe
- supports document open/save/rename workflows with lock handling via WOPI
- supports Open/Edit/View launch modes and public share links
- supports creating new text/spreadsheet/presentation files and optional template-based creation
- supports rename/move/copy/delete, favorites, recent files, and version history restore
- supports file uploads into the root folder or a selected folder, including drag-and-drop of files and folders
- supports Office thumbnails in the details panel via Collabora
convert-towith version-based cache - supports authentication with session cookies and admin user management
- exposes feature matrix, diagnostics, and supported-format endpoints
- runs together with a Collabora CODE container via Docker Compose
- keeps document access scoped to Docker mounts and per-user mount permissions
The app lists common Writer, Calc, and Impress formats such as:
- Writer:
odt,doc,docx,rtf,txt - Calc:
ods,xls,xlsx,csv,tsv - Impress:
odp,ppt,pptx
Additional extensions can be added in lib/documentStore.js.
All file access is organized around Docker mounts. The app scans the configured MOUNT_ROOT, turns each subdirectory into a mount, and displays only the mounts assigned to the current user. This keeps each mount isolated and prevents unauthorized access across users or WOPI state.
If Collabora is already running elsewhere, you can start the WOPI app as a standalone container with explicit mounts.
mkdir -p \
/srv/wopi-folder-browser/mounts/documents \
/srv/wopi-folder-browser/mounts/projects \
/srv/wopi-folder-browser/mounts/archive \
/srv/wopi-folder-browser/wopi-state
docker run -d \
--name wopi-folder-browser \
--restart unless-stopped \
--network my-collabora-network \
-p 3000:3000 \
-e PORT=3000 \
-e APP_BASE_URL=http://localhost:3000 \
-e COLLABORA_INTERNAL_URL=http://collabora:9980 \
-e COLLABORA_PUBLIC_URL=http://localhost:9980 \
-e ACCESS_TOKEN_SECRET=replace-with-a-long-random-secret \
-e SESSION_SECRET=replace-with-a-second-long-random-secret \
-v /srv/wopi-folder-browser/mounts:/mnt \
-v /srv/wopi-folder-browser/wopi-state:/var/lib/wopi-state \
ghcr.io/mini-maya/wopi-folder-browser:latestNotes:
- Each subdirectory under
MOUNT_ROOTbecomes a mount in the app. - If Collabora is not on the same Docker network, replace
http://collabora:9980with the host or service URL that the app can reach. APP_BASE_URLshould be the public URL the browser and Collabora need to reach the WOPI host.COLLABORA_PUBLIC_URLis the browser-visible Collabora URL used inside the iframe.
Minimal example (run Collabora + app with docker run on one network):
mkdir -p \
/srv/wopi-folder-browser/mounts/documents \
/srv/wopi-folder-browser/mounts/projects \
/srv/wopi-folder-browser/mounts/archive \
/srv/wopi-folder-browser/wopi-state
docker network create wopi-net
docker run -d \
--name collabora \
--restart unless-stopped \
--network wopi-net \
-p 9980:9980 \
-e aliasgroup1=http://wopi-folder-browser:3000 \
-e extra_params="--o:ssl.enable=false --o:ssl.termination=false --o:welcome.enable=false" \
collabora/code:latest
docker run -d \
--name wopi-folder-browser \
--restart unless-stopped \
--network wopi-net \
-p 3000:3000 \
-e APP_BASE_URL=http://localhost:3000 \
-e COLLABORA_INTERNAL_URL=http://collabora:9980 \
-e COLLABORA_PUBLIC_URL=http://localhost:9980 \
-e ACCESS_TOKEN_SECRET=replace-with-a-long-random-secret \
-e SESSION_SECRET=replace-with-a-second-long-random-secret \
-v /srv/wopi-folder-browser/mounts:/mnt \
-v /srv/wopi-folder-browser/wopi-state:/var/lib/wopi-state \
ghcr.io/mini-maya/wopi-folder-browser:latestIf your Collabora endpoint is HTTPS and signed by your own CA, mount the CA cert into the app container and set NODE_EXTRA_CA_CERTS:
mkdir -p \
/srv/wopi-folder-browser/mounts/documents \
/srv/wopi-folder-browser/mounts/projects \
/srv/wopi-folder-browser/mounts/archive \
/srv/wopi-folder-browser/wopi-state \
/srv/wopi-folder-browser/certs
docker run -d \
--name wopi-folder-browser \
--restart unless-stopped \
--network my-collabora-network \
-p 3000:3000 \
-e APP_BASE_URL=https://office.lan \
-e COLLABORA_INTERNAL_URL=https://collabora.lan \
-e COLLABORA_PUBLIC_URL=https://collabora.lan \
-e NODE_EXTRA_CA_CERTS=/run/certs/collabora-ca.crt \
-e ACCESS_TOKEN_SECRET=replace-with-a-long-random-secret \
-e SESSION_SECRET=replace-with-a-second-long-random-secret \
-v /srv/wopi-folder-browser/mounts:/mnt \
-v /srv/wopi-folder-browser/wopi-state:/var/lib/wopi-state \
-v /srv/wopi-folder-browser/certs/ca.crt:/run/certs/collabora-ca.crt:ro \
ghcr.io/mini-maya/wopi-folder-browser:latestThis setup runs both the WOPI app and a Collabora CODE container together.
The app uses the published image ghcr.io/mini-maya/wopi-folder-browser:latest.
Use this section with docker-compose.yml (no .env file required).
mkdir -p \
./wopi_dev_folder/mounts/documents \
./wopi_dev_folder/mounts/projects \
./wopi_dev_folder/mounts/archive \
./wopi_dev_folder/wopi-stateThe app stores its persistent WOPI state in WOPI_STATE_ROOT, which is set to /var/lib/wopi-state in Docker. That directory must be mounted into the container so the login state, user records, mounts, locks, and other runtime metadata stay consistent across restarts.
docker compose pull
docker compose up -dOpen http://localhost:3000.
docker compose exec app npm run setup:admin -- --username admin --password "ChangeThisNow123!"This command is one-time only. A second run returns an error because setup is already completed.
Each subdirectory under ./wopi_dev_folder/mounts becomes a mount inside the app. The browser talks to the app on localhost:3000, while Collabora talks to the same app over Docker's internal network using the service name app:3000.
This local Compose setup is intentionally plain HTTP end-to-end. Do not enable ssl.termination=true unless Collabora is actually behind an HTTPS reverse proxy that terminates TLS.
The app keeps its internal defaults in code. These are the canonical container paths used by the runtime and are applied automatically unless you explicitly override them in the environment.
| Variable | Default | Purpose |
|---|---|---|
MOUNT_ROOT |
/mnt |
Root directory inside the container that contains the Docker mount folders. |
WOPI_STATE_ROOT |
/var/lib/wopi-state |
Persistent root for runtime state, locks, user records, and per-mount metadata. |
The app scans MOUNT_ROOT and treats each subdirectory as one mount. Host-side bind mounts are configured directly in the Compose files, and WOPI_STATE_ROOT must point to the shared persisted state directory that both setup and runtime use. You can override either value via environment variables when needed, but the defaults are already set in the app so they do not have to be repeated in Compose files.
docker-compose.prod.yml uses a dedicated .env.production file.
Use this section for server-style deployments with explicit host paths and production secrets.
cp .env.production.example .env.productionThen start the stack with the prod compose file:
docker compose -f docker-compose.prod.yml up -dExample .env.production:
# Required runtime settings
APP_BASE_URL=https://office.lan
COLLABORA_INTERNAL_URL=https://collabora.lan
COLLABORA_PUBLIC_URL=https://collabora.lan
ACCESS_TOKEN_SECRET=replace-with-a-long-random-secret
SESSION_SECRET=replace-with-a-second-long-random-secret
PASSWORD_MIN_LENGTH=12
# Optional overrides; defaults are already defined in the app
MOUNT_ROOT=/mnt
WOPI_STATE_ROOT=/var/lib/wopi-state
# Collabora admin
COLLABORA_ADMIN_USER=admin
COLLABORA_ADMIN_PASSWORD=replace-with-a-strong-passwordThe production compose file mounts the host folders into the container at MOUNT_ROOT:
services:
app:
env_file:
- .env.production
restart: unless-stopped
volumes:
- /srv/wopi-folder-browser/mounts:/mnt
- /srv/wopi-folder-browser/wopi-state:/var/lib/wopi-state
collabora:
restart: unless-stoppedThe repository includes a local development layout under wopi_dev_folder and a dedicated development file: docker-compose.dev.yml.
Use this section when you need local image builds (code changes in the current checkout).
docker compose -f docker-compose.dev.yml up --buildThis version is meant for quick local testing and development. It keeps the runtime defaults in code and uses the local mirror folders for the host-side mounts.
The app discovers mounts by scanning MOUNT_ROOT and creating one mount for each subdirectory. Each user sees only the mount folders they are explicitly allowed to access, so access checks happen server-side and not in the browser.
These variables are used by docker-compose.prod.yml through env_file.
The .env.production block above is the quick bootstrap example; this table is the canonical reference for meaning and allowed values.
| Variable | Type | Topic | Purpose | Effect / persistence | Default / example |
|---|---|---|---|---|---|
APP_BASE_URL |
Variable | WOPI / network | Public base URL the browser and Collabora use to reach the WOPI host | Used for callback URLs and WOPI discovery at runtime. | https://office.lan |
COLLABORA_INTERNAL_URL |
Variable | WOPI / network | URL the app container uses to fetch discovery | Used by app-side discovery calls. | https://collabora.lan |
COLLABORA_PUBLIC_URL |
Variable | WOPI / network | Browser-visible Collabora URL used inside the iframe | Used in browser-facing URLs. | https://collabora.lan |
ACCESS_TOKEN_SECRET |
Initial | Auth | Secret used to sign WOPI access tokens | Rotate carefully; existing tokens become invalid. | replace-with-a-long-random-secret |
SESSION_SECRET |
Initial | Auth | Secret used to sign browser session cookies | Rotating invalidates active sessions. | replace-with-a-second-long-random-secret |
PASSWORD_MIN_LENGTH |
Variable | Auth | Minimum password length for setup/admin/user password flows | Runtime password policy. | 12 |
MOUNT_ROOT |
Variable | Mounts | Base directory inside the container that contains the Docker mount folders | Each subdirectory becomes one mount in the app. | /mnt |
WOPI_STATE_ROOT |
Variable | State | Persistent root for runtime state and lock metadata | Keeps WOPI state and mount-specific data separated. | /var/lib/wopi-state |
COLLABORA_ADMIN_USER |
Initial | Collabora auth | Admin username for Collabora-only admin tasks | Used for Collabora admin integration. | optional |
COLLABORA_ADMIN_PASSWORD |
Variable | Collabora auth | Admin password for Collabora-only admin tasks | Used for configured admin calls at runtime. | optional |
These optional variables are read directly by the app at runtime and are not already covered by the production .env.production table above.
| Variable | Type | Topic | Purpose | Effect / persistence | Default / example |
|---|---|---|---|---|---|
MAX_DOCUMENT_SIZE |
Variable | Uploads | Raw upload limit for PutFile |
Limits maximum incoming file size. | 100mb |
DEFAULT_EDITOR_MODE |
Variable | UI | Launch mode for Open action (edit or view) |
Runtime default for document open mode. | edit |
ALLOW_DOCUMENT_CREATION |
Variable | Features | Enables/disables create-document API endpoints (1 enabled, 0 disabled) |
1 by default. |
1 |
ALLOW_TEMPLATES |
Variable | Features | Enables/disables template endpoints (1 enabled, 0 disabled) |
1 by default. |
1 |
ALLOW_PDF_EXPORT |
Variable | Features | Feature flag for PDF export integration hooks (1 enabled, 0 disabled) |
1 by default. |
1 |
ALLOW_PUBLIC_EDITING |
Variable | Features | Enables/disables edit-capable public links (1 enabled, 0 disabled) |
1 by default. |
1 |
PREVIEW_GENERATION |
Variable | Features | Feature flag for preview generation hooks (1 enabled, 0 disabled) |
1 by default. |
1 |
THUMBNAIL_MAX_WIDTH |
Variable | Preview | Maximum thumbnail width in pixels | 1024 by default. |
1024 |
THUMBNAIL_MAX_HEIGHT |
Variable | Preview | Maximum thumbnail height in pixels | 1024 by default. |
1024 |
THUMBNAIL_RETRY_COUNT |
Variable | Preview | Conversion retry attempts | 3 by default. |
3 |
THUMBNAIL_RETRY_DELAY_MS |
Variable | Preview | Delay between conversion retries in ms | 300 by default. |
300 |
THUMBNAIL_REQUEST_TIMEOUT_MS |
Variable | Preview | Timeout for capabilities/convert requests in ms | 15000 by default. |
15000 |
THUMBNAIL_TOKEN_TTL_MS |
Variable | Preview | Read-only WOPI token lifetime for thumbnail requests | 60000 by default. |
60000 |
THUMBNAIL_DEBUG |
Variable | Preview | Enables detailed thumbnail debug logs (1 enabled, 0 disabled) |
0 by default. |
0 |
ACTIVITY_MAX_AGE_DAYS |
Variable | Retention | Maximum age of activity log entries in days | Older entries are pruned automatically; the newest entry is always kept. | 180 |
ACTIVITY_MAX_COUNT |
Variable | Retention | Maximum number of activity log entries kept per document context | Entries beyond the limit are pruned automatically; the newest entry is always kept. | 60 |
VERSION_MAX_AGE_DAYS |
Variable | Retention | Maximum age of document versions in days | Older, unlabeled versions (and their stored files) are pruned automatically; the newest and any labeled version are always kept. | 365 |
VERSION_MAX_COUNT |
Variable | Retention | Maximum number of versions kept per document | Unlabeled versions beyond the limit (and their stored files) are pruned automatically; the newest and any labeled version are always kept. | 36 |
Important: Environment changes become active only after container restart or redeploy.
Reference app: ref_richdocuments/richdocuments, version 12.0.0-dev.0 (appinfo/info.xml).
The current host exposes /api/feature-matrix with a mapped list of features and categories:
- already provided by Collabora
- provided by WOPI
- implemented in this application
- already provided by existing application components
- not meaningful / not supported in this sample host
npm install
npm startUseful environment variables:
PORT=3000
APP_BASE_URL=http://localhost:3000
COLLABORA_INTERNAL_URL=http://localhost:9980
COLLABORA_PUBLIC_URL=http://localhost:9980
ACCESS_TOKEN_SECRET=change-me
SESSION_SECRET=change-me-session
PASSWORD_MIN_LENGTH=12
THUMBNAIL_DEBUG=0