excalidraw: native export menu + drawing rename
Users couldn't see Excalidraw's built-in Save as / Export image options:
the app's custom toolbar was drawn exactly on top of the native hamburger
menu button, hiding it. Removed the overlay and integrated Back to
Library / Save now / Rename into the native menu, so the native export
formats (.excalidraw file, PNG, SVG, clipboard) are now reachable.
Viktor asked for exports to work via the native Excalidraw feature and
for drawings to be renameable by clicking their name.
Rename: new PATCH /api/drawings/{id} endpoint (server-side name
sanitization, 409 on conflict) + click-to-rename title pill in the
editor (updates URL in place) + Rename button/modal in the dashboard.
Existing GET/PUT/DELETE semantics unchanged for API compatibility
(emo's upload pipeline). Added main_test.go (httptest) covering rename
+ existing handler behavior; dashboard rows now DOM-built (XSS-safe).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6f03ccd1aa
commit
1cbc1e962b
4 changed files with 577 additions and 133 deletions
|
|
@ -4,18 +4,28 @@ A self-hosted Excalidraw library with per-user drawing storage and management.
|
|||
|
||||
## Features
|
||||
|
||||
- Dashboard to manage all your drawings
|
||||
- Dashboard to manage all your drawings (create, open, rename, delete)
|
||||
- Per-user storage (via Authentik SSO headers)
|
||||
- Create, edit, and delete drawings
|
||||
- Rename drawings from the dashboard or by clicking the drawing name in the editor
|
||||
- Native Excalidraw export via the editor's hamburger menu: "Save to..."
|
||||
(.excalidraw file) and "Export image..." (PNG / SVG / clipboard)
|
||||
- Autosave (2s debounce) + manual save (Ctrl+S or menu "Save now")
|
||||
- Persistent storage via NFS
|
||||
|
||||
## Docker Image
|
||||
|
||||
```
|
||||
viktorbarzin/excalidraw-library:v4
|
||||
ghcr.io/viktorbarzin/excalidraw-library:latest
|
||||
```
|
||||
|
||||
Available on Docker Hub: https://hub.docker.com/r/viktorbarzin/excalidraw-library
|
||||
Built by GitHub Actions (`.github/workflows/build-excalidraw.yml` in the infra
|
||||
repo, ADR-0002) on every master push touching `stacks/excalidraw/project/**`;
|
||||
tags `:latest` + `:<git-sha>`. The package is PRIVATE — cluster pulls use the
|
||||
Kyverno-synced `ghcr-credentials` secret. Keel polls `:latest` and rolls the
|
||||
deployment on digest change.
|
||||
|
||||
The legacy manually-built DockerHub image `viktorbarzin/excalidraw-library:v4`
|
||||
is frozen as the rollback target; nothing pushes to it anymore.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
|
@ -39,54 +49,13 @@ Mount a persistent volume to the `DATA_DIR` path. Drawings are stored as `.excal
|
|||
└── my-diagram.excalidraw
|
||||
```
|
||||
|
||||
The filename (without extension) is both the drawing ID and its display name;
|
||||
renaming a drawing renames the file (`os.Rename`, mtime preserved).
|
||||
|
||||
## Deployment
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name excalidraw-rooms \
|
||||
-p 8080:8080 \
|
||||
-v /path/to/storage:/data \
|
||||
viktorbarzin/excalidraw-library:v4
|
||||
```
|
||||
|
||||
### Kubernetes
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: excalidraw
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: excalidraw
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: excalidraw
|
||||
spec:
|
||||
containers:
|
||||
- name: excalidraw
|
||||
image: viktorbarzin/excalidraw-library:v4
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: DATA_DIR
|
||||
value: /data
|
||||
- name: PORT
|
||||
value: "8080"
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: data
|
||||
nfs:
|
||||
server: 192.168.1.127
|
||||
path: /srv/nfs/excalidraw
|
||||
```
|
||||
Deployed by the `stacks/excalidraw` Terraform stack (namespace `excalidraw`,
|
||||
service `draw`, ingress `draw.viktorbarzin.me` with `auth = "required"`).
|
||||
|
||||
### With Authentik SSO
|
||||
|
||||
|
|
@ -96,23 +65,7 @@ The application reads user identity from Authentik headers:
|
|||
- `X-Authentik-Email` - Displayed in UI
|
||||
- `X-Authentik-Name` - Displayed in UI
|
||||
|
||||
Configure your ingress to pass these headers:
|
||||
|
||||
```yaml
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "X-authentik-username,X-authentik-email,X-authentik-name"
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
# Build the Docker image
|
||||
docker build -t excalidraw-library .
|
||||
|
||||
# Or build locally
|
||||
go build -o excalidraw-library .
|
||||
./excalidraw-library
|
||||
```
|
||||
Requests without `X-Authentik-Username` fall back to the `anonymous` user.
|
||||
|
||||
## API Endpoints
|
||||
|
||||
|
|
@ -122,10 +75,25 @@ go build -o excalidraw-library .
|
|||
| GET | `/api/drawings` | List all drawings for current user |
|
||||
| GET | `/api/drawings/:id` | Get drawing data |
|
||||
| PUT | `/api/drawings/:id` | Save drawing |
|
||||
| PATCH | `/api/drawings/:id` | Rename drawing — body `{"name": "<new-name>"}`; returns `{"status":"renamed","id":"<new-id>"}`; 409 if the target name exists |
|
||||
| DELETE | `/api/drawings/:id` | Delete drawing |
|
||||
| GET | `/api/user` | Get current user info |
|
||||
| GET | `/draw/:id` | Open drawing in editor |
|
||||
|
||||
Rename names are sanitized server-side to `[a-zA-Z0-9-_]` (other characters
|
||||
become `-`; a trailing `.excalidraw` is stripped). Existing IDs are accepted
|
||||
as-is for backward compatibility with API clients.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
go test ./...
|
||||
|
||||
# Run locally
|
||||
DATA_DIR=/tmp/excalidraw-data go run .
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue