A deployable React workspace providing authenticated Univer collaboration, Node hierarchy management, permissions, Trash, recent Resources, and Worktrees.
Requirements:
- Node.js 24 or newer
- pnpm 10
- a Univer license for capabilities that require one
pnpm install
pnpm workspace:dev:server
pnpm workspace:dev:webworkspace:dev:server watches the backend and listens at
http://127.0.0.1:3020. If dist/public exists, it also serves that last-built
static web application; web source changes are not rebuilt or hot-reloaded there.
workspace:dev:web starts Vite at http://127.0.0.1:5173, enables web
hot module replacement, and proxies product API and WebSocket requests to port
3020. Run both commands and open port 5173 for web development. Port 3020
alone is sufficient for backend work or viewing the latest built web application.
API documentation is available at:
http://127.0.0.1:3020/api-docshttp://127.0.0.1:3020/openapi.yaml
Product data is stored in .data/univer-workspace.sqlite. Univer unit data is
stored separately in .data/univer-collaboration.sqlite, and uploaded Blob
bytes default to .data/univer-workspace-blobs.
The Univer editors import and export XLSX/CSV/TSV, DOCX, and PPTX through the
server-side @univerjs-pro/exchange-node runtime. These endpoints follow the
Universer Exchange shape under /universer-api/exchange/**; they are not part
of the product OpenAPI. The Workspace file action automatically imports
XLS/XLSX/CSV/TSV, DOC/DOCX, and PPT/PPTX as normal Univer Resources in the
selected Space and folder; other file types remain downloadable Blob
Resources. Editor Ribbon imports default to the root of the signed-in user's
Personal Space. Uploaded source files, converted JSON snapshots, and export
files are temporary BlobStore objects with process-local task metadata and a
two-hour lifetime. Exchange actions are not shown for Worktree or merge-preview
editors because those scopes do not yet have scope-aware Office conversion.
Copy .env.example to .env. Development, database, and production start
commands load this file automatically. GitHub OAuth is enabled only when
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and GITHUB_CALLBACK_URL are all
configured.
Create a GitHub OAuth App for each environment. GitHub OAuth Apps accept only one callback URL, so local development and production should not share the same app. For local development, use:
Homepage URL: http://127.0.0.1:5173
Authorization callback URL: http://127.0.0.1:5173/api/auth/github/callback
For the production deployment, create a separate production OAuth App with:
Homepage URL: https://workspace.univer.plus/
Authorization callback URL: https://workspace.univer.plus/api/auth/github/callback
The callback URL configured in GitHub must exactly match
GITHUB_CALLBACK_URL.
Once configured, the sign-in page shows Continue with GitHub. A first-time GitHub login creates the product User and Personal Space; an existing signed-in User can link GitHub from the account menu. Access tokens are used only to load the GitHub profile during sign-in and are not persisted.
Workspace CLI uses browser approval by default. univer-workspace-cli login
creates a ten-minute, one-time authorization request and prints a /cli-login
URL plus verification code, persists the pending request locally, and exits.
After the user approves the matching code in their own browser, the Agent runs
univer-workspace-cli login --complete to exchange it once; neither command
waits or polls. If necessary, the user can first sign in with GitHub, Discord,
or a password and return to the approval page. The CLI receives a separate
normal workspace_session; it never
receives the browser cookie, Workspace password, or provider access token.
Pending authorization requests are process-local and intentionally disappear
on server restart; completed CLI sessions remain normal persisted login sessions.
Discord OAuth login and account linking are enabled when
DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, and DISCORD_CALLBACK_URL are
all configured. Add this redirect in the Discord Developer Portal for local
development:
http://127.0.0.1:5173/api/auth/discord/callback
The redirect must exactly match DISCORD_CALLBACK_URL. Workspace requests
only the identify scope, stores the stable Discord User ID and username, and
does not persist the access token. A separate Discord App integration can use
that stable ID to map Discord users to Workspace users.
A trusted Discord Bot server can log a Discord user into Workspace through
POST /api/auth/discord/bot-login. Configure the same random secret of at
least 32 characters as DISCORD_BOT_API_KEY in Workspace and send it from the
Bot server as the x-api-key header. The request must contain the stable
discordUserId; username, displayName, and avatarUrl are optional. The
endpoint resolves or creates the Workspace User and Personal Space, then
returns the normal authenticated session response and workspace_session
cookie. Only the trusted Bot server may call this endpoint; never expose the
shared key to a Discord client or browser. If the Bot initially supplies only
discordUserId, Workspace creates placeholder profile fields; a later Discord
OAuth login fills those placeholders from the verified Discord profile without
replacing profile fields that the User has already customized.
Workspace exposes a generic OAuth-style authorization capability. A registered
external client starts GET /api/auth/authorize; the authorize endpoint reuses
workspace_session, redirecting through the existing login page only when the
session is absent, then returns a one-time short-lived code to the registered
redirect URI. POST /api/auth/token validates the client secret, the registered
redirect URI, the PKCE verifier, expiry, and one-time use before returning the
Workspace identity. Registration is deployment-supplied via OAUTH_CLIENTS_JSON.
Existing Workspace login, OAuth callbacks, Cookie behavior, and product APIs
remain unchanged. The capability is additive and does not add a proxy or
deployment component.
The browser uses the same built-in runtime development license as Workspace
CLI. Both copies are rotated every 90 days and are application credentials, not
the repository software license. The built-in credential is for localhost;
set VITE_UNIVER_LICENSE at build time for any non-local deployment or to
override it locally. Server, database, GitHub, and Discord settings are runtime
values.
An authenticated Browser keeps one /api/worktree-events WebSocket open. AI or
CLI Worktree writes publish a cache-invalidation signal only after the combined
Collaboration and product operation completes, so active/processed task lists,
details, sidebar counts, and Worktree-driven Node/Resource lists refresh without
a page reload. The connection uses a one-time session ticket and carries no
Worktree metadata or content.
Build the image from the repository root:
docker build \
--build-arg VITE_UNIVER_LICENSE="$VITE_UNIVER_LICENSE" \
-f apps/workspace/Dockerfile \
-t univer-workspace .Run it with a persistent data volume:
docker run --name univer-workspace \
-p 3020:3020 \
-v univer-workspace-data:/app/univer-workspace/.data \
-e GITHUB_CLIENT_ID \
-e GITHUB_CLIENT_SECRET \
-e GITHUB_CALLBACK_URL=https://workspace.univer.plus/api/auth/github/callback \
-e DISCORD_CLIENT_ID \
-e DISCORD_CLIENT_SECRET \
-e DISCORD_CALLBACK_URL=https://workspace.univer.plus/api/auth/discord/callback \
-e OAUTH_CLIENTS_JSON \
-e SECURE_COOKIES=true \
univer-workspaceFor a plain HTTP environment, use -e SECURE_COOKIES=false. Keep secure
cookies enabled for https://workspace.univer.plus.
To intentionally erase all product and collaboration data in a disposable environment, run the reset command against the volume:
docker run --rm \
-v univer-workspace-data:/app/univer-workspace/.data \
univer-workspace node dist/server/db/reset.jsStarting or restarting the application does not recreate the database. Do not run the reset command during a normal deployment; application startup backs up and migrates supported V0 through V5 product databases to V6 automatically. For a V6 rollout, stop every old Workspace instance, start one V6 instance and wait for migration and health checks to succeed, then restore normal service; do not let V5 and V6 processes write the same SQLite file concurrently.
The manual Deploy Workspace workflow accepts an optional existing stable vX.Y.Z
repository tag. When provided, it checks out that tag and uses it for the container
image. When omitted, it builds the workflow dispatch commit and tags the image as
sha-<commit>. It then hands the image to the selected environment. A tag push does
not deploy Workspace automatically, and the deployment workflow does not publish the
CLI.
pnpm typecheck
pnpm test
pnpm build
pnpm db:resetSee architecture.md, data-model.md, and application-design.md.