cleanup: remove broken patch script ref + unused collab props
Docker Build (ARM64) / Build & Push (push) Successful in 2m1s
Docker Build (ARM64) / Build & Push (push) Successful in 2m1s
This commit is contained in:
+94
-67
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Gitea server at `gitea.forkless.com` (RPi 5, native ARM64) hosts the repo
|
Gitea server at `gitea.forkless.com` (RPi 5, native ARM64) hosts the repo
|
||||||
`forkless/excalidraw-mcp-sentinel`. An ARM64 Gitea Actions runner is registered
|
`forkless/excalidraw-mcp-sentinel`. An ARM64 Gitea Actions runner is registered
|
||||||
on the Gitea server itself (labels: `arm64`, `ubuntu-latest`).
|
on the Gitea server itself (labels: `arm64`, `ubuntu-latest`). Runner #1.
|
||||||
|
|
||||||
Local workspace: `/home/pe1085/development/excalidraw-mcp-sentinel`
|
Local workspace: `/home/pe1085/development/excalidraw-mcp-sentinel`
|
||||||
Remote: `https://gitea.forkless.com/forkless/excalidraw-mcp-sentinel`
|
Remote: `https://gitea.forkless.com/forkless/excalidraw-mcp-sentinel`
|
||||||
@@ -19,108 +19,135 @@ Remote: `https://gitea.forkless.com/forkless/excalidraw-mcp-sentinel`
|
|||||||
- `gitea.forkless.com/forkless/excalidraw-mcp-sentinel-canvas:latest`
|
- `gitea.forkless.com/forkless/excalidraw-mcp-sentinel-canvas:latest`
|
||||||
|
|
||||||
Triggers on push to `main` (paths: Dockerfile, src/**, frontend/**, .gitea/**),
|
Triggers on push to `main` (paths: Dockerfile, src/**, frontend/**, .gitea/**),
|
||||||
PRs, and `workflow_dispatch`.
|
PRs, and `workflow_dispatch`. **Avoid empty commits** — they don't match the
|
||||||
|
`paths` filter and won't trigger a build.
|
||||||
|
|
||||||
### 2. Alpine Base Images
|
### 2. Alpine Base Images
|
||||||
Both `Dockerfile` and `Dockerfile.canvas` changed from `node:20-slim` to
|
Both `Dockerfile` and `Dockerfile.canvas` changed from `node:20-slim` to
|
||||||
`node:20-alpine`. Build tools via `apk add --no-cache python3 make g++`.
|
`node:20-alpine`. Build tools via `apk add --no-cache python3 make g++`.
|
||||||
|
|
||||||
### 3. AMD64 Runner Removed
|
### 3. AMD64 Runner Removed
|
||||||
There was a local x86_64 Gitea Actions runner (`wsl-runner`) on this machine
|
There was a local x86_64 Gitea Actions runner (`wsl-runner`, id=1) on this
|
||||||
that competed with the ARM64 runner. It was stopped and its `.runner` file
|
machine that competed with the ARM64 runner. It was stopped and its `.runner`
|
||||||
deleted from `/home/pe1085/development/gitea-test/`. It had label
|
file deleted from `/home/pe1085/development/gitea-test/`. It had
|
||||||
`ubuntu-latest:docker://catthehacker/ubuntu:full-latest` — would build amd64
|
`ubuntu-latest:docker://catthehacker/ubuntu:full-latest` — would build amd64
|
||||||
images that don't run on ARM64.
|
images that can't run on ARM64. **Do not re-register it.** Only the RPi 5
|
||||||
|
ARM64 runner should exist.
|
||||||
|
|
||||||
### 4. Frontend WebSocket URL
|
### 4. Frontend WebSocket URL
|
||||||
`frontend/src/App.tsx` line 438:
|
`frontend/src/App.tsx` line 438:
|
||||||
```ts
|
```ts
|
||||||
const wsUrl = 'wss://excalidraw.forkless.com'
|
const wsUrl = 'wss://excalidraw.forkless.com'
|
||||||
```
|
```
|
||||||
Previously used `window.location.host` — hardcoded to the forkless domain now.
|
Previously used `window.location.host` — hardcoded to the forkless domain.
|
||||||
|
This is for the app's custom sync protocol (JSON messages via `ws` library),
|
||||||
|
not the Excalidraw collaboration Socket.IO.
|
||||||
|
|
||||||
### 5. Reverse Proxy Config (NPM / OpenResty)
|
### 5. Reverse Proxy Config (NPM v2.15.1 / OpenResty)
|
||||||
The server at `excalidraw.forkless.com` runs behind Nginx Proxy Manager v2.15.1.
|
The server at `excalidraw.forkless.com` runs behind Nginx Proxy Manager.
|
||||||
Two settings fixed WebSocket connectivity:
|
**Critical settings for WebSocket to work:**
|
||||||
- **WebSockets Support** toggle enabled in NPM (or advanced config:
|
- **WebSockets Support** toggle enabled in NPM
|
||||||
`proxy_set_header Upgrade $http_upgrade;`)
|
- The WebSocket upgrade headers (`Upgrade`, `Connection`) are forwarded
|
||||||
- **ALLOWED_ORIGINS** env var must include `https://excalidraw.forkless.com`
|
- **Do NOT** add custom nginx proxy config in the Advanced tab unless the
|
||||||
|
toggle alone doesn't work — the toggle handles it. Custom config can
|
||||||
|
conflict and break NPM entirely (requires container restart to recover).
|
||||||
|
|
||||||
### 6. trust proxy
|
### 6. `trust proxy` in Express
|
||||||
`src/server.ts` line 43:
|
`src/server.ts` line 43:
|
||||||
```ts
|
```ts
|
||||||
app.set('trust proxy', 1);
|
app.set('trust proxy', 1);
|
||||||
```
|
```
|
||||||
Required because NPM sets `X-Forwarded-For` headers and express-rate-limit
|
Required because NPM sets `X-Forwarded-For` and express-rate-limit rejects
|
||||||
throws an error without it.
|
requests without this setting.
|
||||||
|
|
||||||
### 7. Gitea Actions Secrets
|
### 7. `ALLOWED_ORIGINS` Env Var
|
||||||
Create these in the Gitea repo settings → Actions → Secrets:
|
The `verifyWsClient()` in `src/security.ts` checks `Origin` header against
|
||||||
|
`ALLOWED_ORIGINS`. Default: `http://localhost:3000`, `http://127.0.0.1:3000`.
|
||||||
|
**Must include `https://excalidraw.forkless.com` for production.**
|
||||||
|
|
||||||
|
### 8. Gitea Actions Secrets
|
||||||
|
Set in Gitea repo → Settings → Actions → Secrets:
|
||||||
- `GITEAUSER` = `forkless`
|
- `GITEAUSER` = `forkless`
|
||||||
- `GITEATOKEN` = Gitea personal access token (needs `write:repository` scope)
|
- `GITEATOKEN` = Gitea PAT with `write:repository` scope
|
||||||
|
|
||||||
|
### 9. `collabServerUrl` Prop (Unused / Placeholder)
|
||||||
|
`frontend/src/App.tsx` line 1864-1865 still has:
|
||||||
|
```tsx
|
||||||
|
collabServerUrl="wss://excalidraw.forkless.com/socket.io"
|
||||||
|
onCollabDialogOpen={() => {}}
|
||||||
|
```
|
||||||
|
These are **not documented/working props** in `@excalidraw/excalidraw` v0.18.x
|
||||||
|
and are likely ignored by the library. They can be safely removed. The real
|
||||||
|
collaboration URL is hardcoded in the Excalidraw npm bundle and connects to
|
||||||
|
`wss://oss-collab.excalidraw.com` by default.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What Still Needs Work
|
## What Still Needs Work
|
||||||
|
|
||||||
### 1. Collab URL Hardcoded in Excalidraw Bundle
|
### 1. Build-Time URL Patch (Broken / Remove)
|
||||||
The Excalidraw npm library has `wss://oss-collab.excalidraw.com` hardcoded in
|
Commit `6ed842d` added `scripts/patch-collab-url.mjs` and modified `package.json`
|
||||||
its built JS. This is NOT configurable via component props — the `collabServerUrl`
|
to run it after `vite build`. The script replaces `wss://oss-collab.excalidraw.com`
|
||||||
prop is not a documented/working prop in v0.18.x.
|
with a custom URL in the built bundle.
|
||||||
|
|
||||||
Two options:
|
Current state:
|
||||||
|
- `package.json` still has `"build:frontend": "vite build && node scripts/patch-collab-url.mjs"`
|
||||||
|
- The script file was deleted from disk but exists in git history
|
||||||
|
- **Next build will fail** because the script file is missing
|
||||||
|
|
||||||
**A) Build-time patch (currently reverted but script exists):**
|
**Remove it:**
|
||||||
`scripts/patch-collab-url.mjs` replaces the URL in `dist/frontend/assets/`
|
```bash
|
||||||
after Vite builds. Enable it by restoring:
|
cd /home/pe1085/development/excalidraw-mcp-sentinel
|
||||||
```json
|
# Option A: Remove the script reference from package.json
|
||||||
"build:frontend": "vite build && node scripts/patch-collab-url.mjs"
|
sed -i 's/ && node scripts\/patch-collab-url.mjs//' package.json
|
||||||
|
# Option B: Restore the script file from git
|
||||||
|
git restore scripts/patch-collab-url.mjs
|
||||||
```
|
```
|
||||||
|
|
||||||
**B) Keep default (current):** Let the Excalidraw library connect to the
|
The official collab server at `oss-collab.excalidraw.com` works fine with the
|
||||||
official collab server. Requires NPM/OpenResty to allow egress to
|
trust proxy + CORS + cache-flush fix. Only self-host this if the official
|
||||||
`oss-collab.excalidraw.com`. Currently works after cache flush + trust proxy fix.
|
server is blocked.
|
||||||
|
|
||||||
### 2. ALLOWED_ORIGINS Env Var
|
### 2. Docker Build Fails (Missing Patch Script)
|
||||||
The `verifyWsClient` in `src/security.ts` checks `Origin` header against
|
Build 783 failed because of the missing script file. Fix above first, then
|
||||||
`ALLOWED_ORIGINS`. Defaults to `http://localhost:3000` and `http://127.0.0.1:3000`.
|
trigger a clean build (must modify a file matching the paths filter —
|
||||||
For production, set:
|
`Dockerfile`, `src/**`, `frontend/**`, `.gitea/**`).
|
||||||
```
|
|
||||||
ALLOWED_ORIGINS=https://excalidraw.forkless.com
|
|
||||||
```
|
|
||||||
Otherwise WebSocket connections from the real domain are rejected.
|
|
||||||
|
|
||||||
### 3. Unused Props in App.tsx
|
### 3. LocalStorage Cache
|
||||||
Line 1864-1867 of `frontend/src/App.tsx`:
|
Browser caches the Excalidraw bundle aggressively. When switching between
|
||||||
```tsx
|
collab URLs, users must clear localStorage + hard refresh (Ctrl+F5) or
|
||||||
collabServerUrl="wss://excalidraw.forkless.com/socket.io"
|
the old URL persists in their browser.
|
||||||
onCollabDialogOpen={() => {}}
|
|
||||||
```
|
|
||||||
These are likely ignored by the Excalidraw library (not documented props).
|
|
||||||
Can be removed — the real collab URL is controlled by the build-time patch
|
|
||||||
or the default in the npm bundle.
|
|
||||||
|
|
||||||
### 4. WebSocket Auth
|
### 4. WebSocket Auth Hardening
|
||||||
When `EXCALIDRAW_API_KEY` is set, the WS server sends `auth_required` on
|
When `EXCALIDRAW_API_KEY` is set, the WS requires auth. The frontend sends
|
||||||
connect, waits for `hello` with the API key, and closes if not received.
|
the key in a `hello` message after receiving `auth_required`. Without the
|
||||||
The frontend handles this in `connectWebSocket()` → `sendHello()`.
|
key, the connection drops. Consider:
|
||||||
This works but could be strengthened:
|
- Rate-limit WS connections by IP
|
||||||
- Add IP-based rate limiting for WS connections
|
- Reject no-origin connections in `verifyWsClient` (currently allows)
|
||||||
- Add `verifyWsClient` to reject missing origins (currently allows no-origin)
|
|
||||||
|
|
||||||
### 5. Docker Image Tags
|
### 5. Docker Image Tags
|
||||||
Currently only `:latest` and `:$sha` tags are pushed. For versioned releases,
|
Only `:latest` and `:$sha` are pushed. Version tags (`v*`) trigger is in
|
||||||
trigger on `v*` tags is in the workflow but untested.
|
the workflow but untested.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Credentials & Access
|
## Credentials & Access
|
||||||
|
|
||||||
- **Gitea API token**: stored in `~/.profile` as `GITEA_TOKEN` (not `GITEATOKEN`)
|
- **Gitea API token**: stored in `~/.profile` as `GITEA_TOKEN`
|
||||||
- **Git credentials**: stored via `git credential-store` for
|
- **Git credentials**: stored via `git credential-store` for
|
||||||
`gitea.forkless.com` — username `forkless`, password is the token
|
`gitea.forkless.com` — username `forkless`, password = Gitea PAT
|
||||||
- To use the token: `source ~/.profile` then `$GITEA_TOKEN`
|
- To use the token in exec_shell:
|
||||||
- Or via git: `TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2)`
|
`source ~/.profile && echo "$GITEA_TOKEN"` (DO NOT source `~/.profile`
|
||||||
|
if the user objects — but the token is stored there)
|
||||||
|
- Alternative: read via git credential helper:
|
||||||
|
`TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2)`
|
||||||
|
|
||||||
|
### Git Credential Helper Fix
|
||||||
|
The system had a broken `credential.helper` for `gitea.forkless.com` pointing
|
||||||
|
to `!tea login helper` — but the binary is `tea-cli`, not `tea`. Fixed with:
|
||||||
|
```bash
|
||||||
|
git config --global --replace-all credential.https://gitea.forkless.com.helper '!tea-cli login helper'
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -128,14 +155,14 @@ trigger on `v*` tags is in the workflow but untested.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check latest build
|
# Check latest build
|
||||||
TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2) && curl -s -H "Authorization: token $TOKEN" "https://gitea.forkless.com/api/v1/repos/forkless/excalidraw-mcp-sentinel/actions/runs?limit=3" | python3 -c "import sys,json;d=json.load(sys.stdin);[print(f'Run {r[\"id\"]}: {r[\"status\"]:>10} {r.get(\"conclusion\",\"-\"):>10} {r[\"head_sha\"][:12]}') for r in d.get('workflow_runs',[])]"
|
TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2) && curl -s -H "Authorization: token $TOKEN" "https://gitea.forkless.com/api/v1/repos/forkless/excalidraw-mcp-sentinel/actions/runs?limit=5" | python3 -c "import sys,json;d=json.load(sys.stdin);[print(f'Run {r[\"id\"]}: {r[\"status\"]:>10} {r.get(\"conclusion\",\"-\"):>10} {r[\"head_sha\"][:12]}') for r in d.get('workflow_runs',[])]"
|
||||||
|
|
||||||
# Check Docker image manifest
|
# Check Docker image manifest
|
||||||
docker manifest inspect gitea.forkless.com/forkless/excalidraw-mcp-sentinel:latest
|
docker manifest inspect gitea.forkless.com/forkless/excalidraw-mcp-sentinel:latest
|
||||||
|
|
||||||
# Git credential helper (broken 'tea' → fixed 'tea-cli')
|
|
||||||
git config --global credential.https://gitea.forkless.com.helper '!tea-cli login helper'
|
|
||||||
|
|
||||||
# Push without credential helper issues
|
# Push without credential helper issues
|
||||||
git -c credential.https://gitea.forkless.com.helper='!tea-cli login helper' push origin main
|
git push origin main
|
||||||
|
|
||||||
|
# Trigger dispatch (if paths filter blocks push trigger)
|
||||||
|
TOKEN=$(git credential fill <<< $'protocol=https\nhost=gitea.forkless.com\npath=/forkless/excalidraw-mcp-sentinel.git\n' 2>/dev/null | grep "^password=" | cut -d= -f2) && curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" "https://gitea.forkless.com/api/v1/repos/forkless/excalidraw-mcp-sentinel/actions/workflows/docker.yml/dispatches" -d '{"ref":"main"}'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1861,8 +1861,6 @@ function App(): JSX.Element {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onChange={handleCanvasChange}
|
onChange={handleCanvasChange}
|
||||||
collabServerUrl="wss://excalidraw.forkless.com/socket.io"
|
|
||||||
onCollabDialogOpen={() => {}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
"start": "npm run build:server && node dist/index.js",
|
"start": "npm run build:server && node dist/index.js",
|
||||||
"canvas": "npm run build:server && node dist/server.js",
|
"canvas": "npm run build:server && node dist/server.js",
|
||||||
"build": "npm run build:frontend && npm run build:server",
|
"build": "npm run build:frontend && npm run build:server",
|
||||||
"build:frontend": "vite build && node scripts/patch-collab-url.mjs",
|
"build:frontend": "vite build",
|
||||||
"build:server": "npx tsc",
|
"build:server": "npx tsc",
|
||||||
"build:types": "npx tsc --emitDeclarationOnly",
|
"build:types": "npx tsc --emitDeclarationOnly",
|
||||||
"dev": "concurrently \"npm run dev:server\" \"vite\"",
|
"dev": "concurrently \"npm run dev:server\" \"vite\"",
|
||||||
|
|||||||
Reference in New Issue
Block a user