Tomas Dvorak 910546230d
Docker Images / Build and push (push) Failing after 21s
refactor(editor): improve type safety and optimize build chunks
Refactor the Editor component to replace `any` types with explicit interfaces for Excalidraw props, library items, and elements, improving type safety and developer experience.

Additionally, update the Vite configuration to implement manual chunking for large dependencies like Excalidraw, React, and Zustand to optimize bundle loading and improve build performance.
2026-05-09 19:27:36 +02:00
2026-04-28 10:12:30 +02:00
2026-04-29 11:31:56 +02:00
2026-04-27 11:23:56 +02:00
2024-03-29 09:55:57 +00:00
2026-04-27 11:23:56 +02:00
2026-04-27 11:23:56 +02:00
2024-05-24 09:24:13 +00:00
2026-04-27 11:23:56 +02:00
2026-04-29 11:31:56 +02:00
2026-04-29 11:31:56 +02:00

Excalidraw FULL

A self-hosted visual workspace for drawing, planning, mapping, charting, and lightweight project management built around a fast collaborative canvas.

What It Is

Excalidraw FULL is a production-grade visual workspace platform. It is no longer a simple drawing wrapper — it is a full product with backend-owned persistence, team collaboration, permissions, and structured file management.

  • Visual canvas first — freeform drawing remains central
  • Backend-owned persistence only — no browser/local save modes
  • Team collaboration native — real-time editing with presence
  • File/folder/project management built in — hierarchical organization via /folder/:folderId/drawing/:drawingId
  • Secure sharing and permissioning — explicit grants with inheritance
  • Activity history and auditability — every action is tracked
  • Templates and structured productivity — system + team + personal templates
  • Rich linking between canvases — embeds, references, knowledge graph
  • Command palette — global Cmd/Ctrl+K for power users
  • Fulltext search — find drawings from anywhere
  • Revision browser — time-travel through drawing history with one-click restore
  • Dark mode — persistent theme preference across sessions
  • Presenter notes — add notes to any drawing for presentations
  • Responsive layout — mobile sidebar toggle, adaptive grids
  • Accessibility — ARIA labels, roles, keyboard navigation
  • Self-hosting — single Docker image, healthchecks, volume mounts

Quick Start

git clone https://github.com/BetterAndBetterII/excalidraw-full.git
cd excalidraw-full
cp .env.example .env
# Edit .env and set JWT_SECRET (required)
# openssl rand -base64 32
make build     # Build frontend + Go binary
make test      # Run all tests
make docker-up # Or run via Docker Compose

The application will be available at http://localhost:3002.

Quick Start with Docker (Pre-built Image)

Run the latest pre-built image without cloning or building:

1. Create the Docker Compose file

services:
  postgres:
    image: postgres:16-alpine
    container_name: excalidraw-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-excalidraw}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-excalidraw}
      POSTGRES_DB: ${POSTGRES_DB:-excalidraw}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5

  excalidraw:
    image: ghcr.io/dvorinka/excalidraw-full:latest
    container_name: excalidraw-app
    restart: unless-stopped
    ports:
      - "${PORT:-3002}:3002"
    environment:
      - LISTEN_ADDR=:3002
      - STORAGE_TYPE=postgres
      - DATABASE_URL=postgres://${POSTGRES_USER:-excalidraw}:${POSTGRES_PASSWORD:-excalidraw}@postgres:5432/${POSTGRES_DB:-excalidraw}?sslmode=disable
      - JWT_SECRET=${JWT_SECRET}
      # Optional: GitHub OAuth
      - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID:-}
      - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-}
      # Optional: Generic OIDC
      - OIDC_ISSUER_URL=${OIDC_ISSUER_URL:-}
      - OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-}
      - OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET:-}
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:

2. Set Environment Variables

For Dokploy/CasaOS: Configure these in the UI under Environment Variables.

For CLI/Terminal: Create a .env file:

cat > .env << EOF
# Required: Generate with: openssl rand -base64 32
JWT_SECRET=your-secure-random-string-min-32-chars

# Optional: Change defaults or leave as-is
POSTGRES_USER=excalidraw
POSTGRES_PASSWORD=excalidraw
POSTGRES_DB=excalidraw
PORT=3002

# Optional: GitHub OAuth (for social login)
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# Optional: Generic OIDC (for SSO)
OIDC_ISSUER_URL=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=

EOF

3. Start the services

docker compose up -d

The app will be available at http://localhost:3002 (or your configured PORT).

Docker Images

Pushing to GitHub automatically builds and publishes the production image to GitHub Container Registry:

ghcr.io/<owner>/<repo>:latest
ghcr.io/<owner>/<repo>:sha-<commit>
ghcr.io/<owner>/<repo>:<branch>

No registry secrets are required. The workflow uses GitHub's built-in GITHUB_TOKEN.

Requirements

  • Go 1.26.2+
  • Node.js 20+ (for frontend build)
  • Make (optional, for convenience commands)
  • Docker (optional, for containerized deployment)

Configuration

All configuration is via environment variables. See .env.example for the full reference.

Variable Required Description
JWT_SECRET Yes Secure random string for session signing (min 32 chars)
STORAGE_TYPE No postgres (default), memory, filesystem, s3
DATABASE_URL Yes for Postgres PostgreSQL connection string
GITHUB_CLIENT_ID No* GitHub OAuth app client ID
GITHUB_CLIENT_SECRET No* GitHub OAuth app client secret
OIDC_ISSUER_URL No* Generic OIDC issuer for SSO
OIDC_CLIENT_ID No* OIDC client ID
OIDC_CLIENT_SECRET No* OIDC client secret
ALLOWED_ORIGINS No Comma-separated CORS origins
LISTEN_ADDR No Server bind address (default :3002)

* At least one external auth provider (GitHub OAuth or OIDC) OR use built-in password authentication. Password auth works out of the box.

Architecture

  • Backend: Go 1.26.2, Chi router, PostgreSQL (pgx)
  • Frontend: React 18, Vite, TypeScript, Zustand, react-router-dom, react-i18next
  • Real-time: Socket.IO for collaborative canvas sync
  • Auth: Session cookies (httpOnly, SameSite=Lax) + bcrypt password hashing + OAuth/OIDC
  • Storage: PostgreSQL default, with legacy filesystem/S3 options for canvas storage
  • API spec: OpenAPI 3.0 in api/openapi.yaml; TypeScript client via make generate-api-client

API

The workspace API is mounted at /api and requires session authentication. Key endpoints:

  • POST /api/auth/signup / POST /api/auth/login / POST /api/auth/logout
  • GET /api/auth/me — current user
  • GET /api/teams, POST /api/teams, GET /api/teams/:id/members
  • GET /api/drawings, POST /api/drawings, GET /api/drawings/:id
  • PATCH /api/drawings/:id, DELETE /api/drawings/:id
  • GET /api/drawings/:id/revisions, POST /api/drawings/:id/revisions
  • GET /api/drawings/:id/permissions, POST /api/drawings/:id/permissions
  • GET /api/drawings/:id/share-links, POST /api/drawings/:id/share-links
  • GET /api/search?q= — fulltext search
  • GET /api/folders, POST /api/folders
  • GET /api/projects, POST /api/projects
  • GET /api/templates — system, team, and personal templates
  • GET /api/activity — audit trail with actor hydration
  • GET /api/stats — workspace statistics (counts + storage)
  • GET /api/health — readiness probe

Development

# Terminal 1: Go backend with auto-reload (requires air)
make dev-backend

# Terminal 2: Vite dev server
make dev-frontend

# Or run both manually:
go run main.go                  # backend on :3002
cd frontend && npm run dev      # frontend on :5173

Building

make build              # Full production build (frontend + Go binary)
make build-frontend     # React build only
make build-backend      # Go binary only
make build-docker       # Docker image locally
make test               # Run Go + frontend tests
make test-backend       # Go unit tests
make test-frontend      # Vitest unit tests
make test-e2e           # Playwright E2E tests
make lint               # Run all linters
make fmt                # Format all code
make generate-api-client # TS client from openapi.yaml
make clean              # Remove build artifacts
make docker-up          # Docker Compose local run
make docker-down        # Stop Docker Compose
make docker-logs        # Tail Docker logs
make help               # Show all targets

Project Structure

.
├── main.go                     # Go server entrypoint
├── workspace/                  # Core domain: models, store, HTTP handlers, tests
│   ├── models.go
│   ├── store.go                # PostgreSQL persistence + migrations
│   ├── store_sharing.go        # Permissions, share links, embeds
│   ├── http.go                 # API route handlers
│   ├── http_extra.go           # Search, stats, activity
│   ├── stats.go                # Workspace statistics
│   ├── rate_limiter.go         # Auth endpoint rate limiting
│   └── *_test.go               # Go unit tests
├── middleware/                 # Auth, security headers
├── handlers/                   # Legacy firebase, kv, auth
├── frontend/                   # React + Vite frontend
│   ├── src/
│   │   ├── pages/              # Dashboard, Editor, Auth, Settings, etc.
│   │   ├── components/         # Reusable UI (Button, Card, CommandPalette, etc.)
│   │   ├── stores/             # Zustand state management
│   │   ├── services/           # API client
│   │   ├── i18n/               # Translation files (en.json)
│   │   └── styles/             # Global SCSS + CSS variables
│   └── package.json
├── api/
│   └── openapi.yaml            # Full OpenAPI 3.0 spec
├── excalidraw-full.Dockerfile  # Multi-stage production build
├── docker-compose.yml          # Local Docker Compose
├── Makefile                    # Build, test, dev automation
└── .env.example                # Environment reference

Security

  • bcrypt(cost=12) password hashing
  • httpOnly, SameSite=Lax session cookies
  • CSRF-like same-origin mutation checks on state-changing requests
  • URL sanitization for embeds (blocks file://, javascript:, private IPs)
  • Content-Security-Policy headers with strict defaults
  • Rate limiting on auth endpoints (10 req / 15 min per IP)
  • Permission matrix with explicit grants + inheritance
  • All mutations require authenticated session

Internationalization

Frontend uses react-i18next with i18next-browser-languagedetector. All UI strings are externalized to frontend/src/i18n/locales/en.json. Add new keys there and reference via t('key').

License

MIT

S
Description
No description provided
Readme
87 MiB
Languages
JavaScript 57.7%
HTML 28.9%
TypeScript 5.7%
Go 5.3%
SCSS 2%
Other 0.2%