Commit Graph
245 Commits
Author SHA1 Message Date
Niklas 3e3013045c fix(upload): sniff magic bytes when CDN serves generic binary/octet-stream
Publish container image / build-and-push (push) Canceled after 0s
2026-09-05 12:57:20 +02:00
Niklas 6587c3f865 fix(upload): send browser-like headers in from-url fetch (CDN 403s)
Publish container image / build-and-push (push) Canceled after 0s
2026-09-05 12:45:26 +02:00
Hermes c36ea81c29 Merge pull request 'AYON integration: ticket SSO + task boards (v1.0)' (#1) from ayon-integration into main
Publish container image / build-and-push (push) Canceled after 0s
2026-09-04 23:43:00 +00:00
Hermes c00cb94fe7 docs+deploy: production status, lessons learned (9 bugs), deploy compose, e2e test tools 2026-09-04 23:42:36 +00:00
Hermes f66a4e2d53 fix: treat 401/403 from exchange as invalid-ticket (single-use UX) 2026-09-04 15:01:01 +00:00
Hermes ed0c606aae docs: AYON setup + usage guide (verified against live stack) 2026-09-04 14:33:30 +00:00
Hermes ec9be5b05d fix: import uuidv4 in ayon helpers 2026-09-04 13:53:52 +00:00
Hermes 5a4cfcbf64 feat: AYON single-sign-on (ticket exchange, task boards, browser entry)
- POST /api/auth/ayon/exchange: redeem single-use ticket (issued by the
  AYON addon) via AYON_EXCHANGE_URL, mint session JWT, get-or-create
  internal user row and the <project>/<task> board in the AYON collection
- db: getOrCreateAyonUser / getOrCreateAyonBoard / grantAyonCollectionAccess
- frontend: /b route redeems ticket from URL and forwards to the board
- password login/register paths untouched (legacy instance support)
2026-09-04 13:40:33 +00:00
Hiren KangadandClaude Opus 4.7 33d3109136 docs: deployment templates for Cloudflare Tunnel, Caddy, Fly.io, Render
examples/compose/cloudflared-paired.yml pairs RefBoard with a cloudflared
sidecar reading a TUNNEL_TOKEN env var — no inbound ports needed, suitable
for home / studio servers.

examples/compose/behind-caddy.yml + Caddyfile fronts RefBoard with Caddy
for automatic Let's Encrypt TLS on a public hostname. Caddy 2's
reverse_proxy transparently upgrades Socket.IO WebSockets.

examples/fly.toml deploys to Fly.io using the GHCR image and a persistent
volume mounted at /app/data. STORAGE_BACKEND=fs so the whole stack is one
machine with one disk.

examples/render.yaml is a Render.com Blueprint targeting the same shape
(GHCR image + attached disk + FS storage).

examples/README.md indexes everything and adds notes for Coolify /
Dokploy / CapRover / Railway, which consume the existing compose file
directly without a dedicated template.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
upstream-v0.5.0
2026-05-21 09:51:52 +05:30
Hiren KangadandClaude Opus 4.7 548d0c753a feat: one-click setup scripts for macOS, Linux, and Windows
scripts/setup.sh (bash) and scripts/setup.ps1 (PowerShell) wrap the
canonical Docker Compose flow: detect Docker + Compose v2 (with legacy
docker-compose fallback), copy .env.example to .env if missing, pull the
GHCR image, bring the stack up, poll /health until it's ready (90s cap),
print a summary, and open the URL in the default browser. Idempotent —
safe to re-run.

README quick-start now points designers at the one-liner as the primary
path, with the raw docker compose commands kept underneath for reference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 09:51:39 +05:30
Hiren KangadandClaude Opus 4.7 76a6e462ee feat: zero-config first boot and pluggable storage backend
Two changes that drop the friction in self-hosting RefBoard so the install
story becomes "docker compose up, open the URL".

1. JWT_SECRET is now optional. On first boot the backend generates a
   64-byte random secret and persists it in the existing settings table.
   process.env.JWT_SECRET still wins when set, so ops setups that manage
   secrets out-of-band are unaffected. The prod-throws-without-env guard
   is gone (auto-generation is a strictly safer default than the previous
   hardcoded dev fallback).

2. STORAGE_BACKEND=fs|minio picks between MinIO (default, unchanged) and
   a new local-filesystem adapter. The FS adapter exposes a fake minioClient
   that mirrors the methods RefBoard calls (statObject, getObject,
   getPartialObject, listObjectsV2, putObject, removeObject(s), bucketExists,
   makeBucket), so consumers swap require('./minio') for require('./storage')
   and nothing else changes. Sidecar .mime files hold Content-Type so the
   range-aware media proxy still serves the right response headers.

examples/compose/minimal-fs.yml is the single-container variant that uses
the FS adapter. The default docker-compose.yml still spins up MinIO.

README quick-start collapses to one block (cp .env, docker compose pull,
docker compose up -d). The first user you register on the Login screen is
auto-promoted to admin, same as before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 09:51:20 +05:30
Hiren KangadandClaude Opus 4.7 9328713ae5 ci: publish multi-arch container image to GHCR
Build linux/amd64 + linux/arm64 from the existing Dockerfile on every push
to main, on git tags v*.*.*, and on manual workflow_dispatch. Pushes to
ghcr.io/metalfinger/refboard with tags :latest (default branch), :sha-XXX
(every push), and semver tags on releases. Uses GHA cache for speed.

Tightens .dockerignore so multi-arch builds don't ship docs/, examples/,
scripts/, .github/, or .docker-data into the image context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 09:50:59 +05:30
Vivek Shukla 9a02edd9dc fix: polyfill crypto.randomUUID for insecure-context origins (#1)
crypto.randomUUID is restricted to secure contexts (HTTPS or localhost).
When RefBoard is served over plain HTTP on a LAN IP (e.g.
http://192.168.x.x:8000), the function is undefined and the editor crashes
on first ID generation with:

  Uncaught TypeError: crypto.randomUUID is not a function

The frontend calls crypto.randomUUID in ~20 places (uploadManager,
SceneManager, Editor, canvas tools, grouping, scene-format, etc.), so a
single polyfill at the entry point is the smallest fix.

The polyfill uses crypto.getRandomValues — available on insecure origins —
to build an RFC 4122 v4 UUID with the correct version/variant bits. It is
a no-op when the native function exists, so HTTPS and localhost paths are
unchanged.

No crypto.subtle usage exists in the source, so randomUUID is the only
secure-context API the frontend depends on today.
2026-05-20 18:58:35 +05:30
Hiren Kangad 8d4068e220 docs: roadmap for designer-friendly install paths
Captures two install upgrades in dependency order so a future session can
pick them up without re-thinking:

  Tier 1 — scripts/setup.sh: one-liner installer for terminal-comfortable
  designers. Auto-generates JWT_SECRET, prompts for admin email/password,
  brings up docker compose, opens the browser. Floor: Docker Desktop must
  be installed (script exits with a friendly install link if not).

  Tier 2 — native installer: .dmg / .exe with no Docker, no terminal.
  Replaces MinIO with a local-filesystem storage adapter, bundles the
  Node backend + frontend dist into a single binary via pkg, wraps in a
  Tauri tray app. Designer double-clicks, gets a menu-bar icon, clicks
  "Open RefBoard."

Both unchecked in README roadmap and detailed in docs/install-roadmap.md
with file layout, estimated effort, and open questions.
2026-04-29 12:57:56 +05:30
Hiren Kangad eb0bd210ac feat: per-board activity log
Adds an audit trail per board, visible from a new clock-icon button on the
toolbar. Useful for review-style work where someone wants to see who
contributed which references and when.

Logged events (high-signal only — canvas-edit noise intentionally skipped):
- image / video / pdf added (whether dropped, pasted, or pulled from a URL)
- board created / renamed / deleted
- thread started, resolved, reopened
- comment posted on a thread

Backend:
- new activity_logs table (id, board_id, user_id, denormalised actor name +
  email, action, target_type/id/label, metadata JSON, created_at) with an
  index on (board_id, created_at DESC).
- logActivity helper resolves the user once at log time and stores their
  display name + email so entries survive deactivation/rename.
- recordActivity wraps logActivity + a Socket.IO emit to the board's room
  so the panel updates live without polling.
- GET /api/boards/:id/activity?limit=&before= for pagination
  (collection-membership gated, viewer+).

Frontend:
- ActivityPanel side-drawer: time-grouped feed (Today / Yesterday / older),
  per-action icons + tone colours (add/remove/edit/comment), pagination
  via "Load older", live append on Socket.IO 'activity:new'.
- Relative timestamps refresh every 30s.
- Wired into Editor + Toolbar.

README updated; roadmap entry checked off.
2026-04-28 21:29:48 +05:30
Hiren Kangad 2fb71b4ae1 feat: runtime self-registration toggle in admin dashboard
Self-registration is now controlled at runtime from the admin panel rather
than at build time via an env var. Default: off.

- New `settings` table (key/value/updated_at) plus getSetting/setSetting
  helpers. Idempotent first-boot migration seeds allow_self_registration
  from the ALLOW_SELF_REGISTRATION env var; after first boot the env var
  is ignored and admins control the toggle from the UI.
- New public GET /api/auth/config (no auth) — returns
  { allowSelfRegistration, hasUsers }. The Login page polls this on mount
  to decide whether to show a Register link, and to render
  "Create the first admin account" mode when the install is empty.
- New admin GET /api/admin/settings + PUT /api/admin/settings/:key for
  the dashboard. Constrained to a known-keys allowlist with type coercion
  so unrecognized keys can't be stored.
- POST /api/auth/register now reads the toggle from the database instead
  of process.env. The first user is still always allowed and is auto-
  promoted to admin.
- Admin.tsx grows a "Settings" card with a labelled toggle switch and
  toast feedback. The card sits above the user table.
- VITE_ALLOW_SELF_REGISTRATION dropped — runtime fetch replaces it.

Docs: README + .env.example clarify that ALLOW_SELF_REGISTRATION is now
an initial seed only, the going-public checklist points at the dashboard
toggle, and the features list calls out runtime control.
2026-04-28 20:56:52 +05:30
Hiren Kangad 782d6df9e0 feat: admin dashboard for user management
Adds an /admin route, visible only to users with role=admin, that lets an
operator manage the user base from the UI:
- list / search users (active + inactive)
- create new accounts (with role and optional display name)
- reset a user's password
- promote/demote between admin and member
- deactivate / reactivate (soft-delete via is_active flag)

Backend changes:
- New adminOrApiKeyMiddleware accepts EITHER a Bearer JWT belonging to a
  role=admin user (UI path) OR the existing X-API-Key (bot/server-to-server).
- Existing /api/admin/* routes switched to the hybrid middleware, so the same
  endpoints serve both the dashboard and any external scripts.
- Added PUT /api/admin/users/:id/role and PUT /api/admin/users/:id/reactivate.
- Self-deactivation and self-demotion are explicitly blocked so an admin can't
  lock themselves out.

Frontend changes:
- New Admin.tsx page (table view, modals for create + reset, toast feedback).
- Admin button in CollectionList header, only rendered for admin role.
- Wired into App.tsx routing.

Also: friendly error when poppler-utils is missing on the host (PDF uploads
return 501 POPPLER_MISSING with a one-line install hint instead of crashing
the request); README clarifies poppler is required for the manual install.
2026-04-28 20:34:49 +05:30
Hiren Kangad f2260a9e35 docs: add domain / tunnel deployment guide
Cover Cloudflare Tunnel (the path I run myself), Caddy + Let's Encrypt,
nginx with WebSocket headers, Tailscale for tailnet-private access, and
a 'going public' checklist (JWT_SECRET, CORS, registration, MinIO scope,
backups).
2026-04-28 20:10:01 +05:30
Hiren Kangad 69b58f73f8 chore: prepare standalone public repo
- Remove Mattermost integration (OAuth, channel bridge, file sync watcher,
  frontend import modal). RefBoard now ships as a self-contained app.
- Replace SSO Login screen with email/password form (+ optional register link
  gated by ALLOW_SELF_REGISTRATION).
- Add SEED_ADMIN_EMAIL / SEED_ADMIN_PASSWORD env-var bootstrap so a fresh
  install ships with an admin account on first boot (idempotent).
- ALLOW_SELF_REGISTRATION flag (default false) gates POST /api/auth/register.
  First user can always register (auto-promoted to admin).
- Drop mattermost_id and mm_file_id columns + board_channel_links table
  from the schema; remove related db helpers and exports.
- Add MIT LICENSE, comprehensive README, .env.example, docker-compose.yml
  (bundles MinIO so one command boots a working stack).
- Expand .gitignore for typical Node + Docker dev artefacts.
2026-04-28 19:58:53 +05:30
Hiren 24fa9d1252 fix(refboard): remove loading overlay that got stuck due to viewport culling
The asset progress overlay (e.g. "Loading assets 6/22") was incompatible
with the viewport culling system — culling only loads nearby textures and
unloads distant ones, so loaded count could never reach total. Simplified
to a brief spinner during scene data parsing only.
2026-03-16 16:23:57 +05:30
Hiren 7bcb0fd071 fix(refboard): fix WebGL null texture crash and add loading overlay
- Set texture to Texture.EMPTY before destroying sprites in
  AnimatedGifSprite and PdfPageSprite (matches VideoSprite pattern)
- Add full-screen loading overlay that blocks interaction until
  scene and assets finish loading
2026-03-16 11:10:19 +05:30
Hiren Kangad 019e17ae59 fix: allow spacebar in contentEditable elements (markdown editor)
Space-to-pan handler was intercepting spacebar globally but only
excluded INPUT/TEXTAREA — missed contentEditable (BlockNote editor).
2026-03-15 13:15:00 +05:30
Hiren Kangad 479feb991c fix: block flip/rotate in SelectionToolbar for sticky, text, markdown, pdf-page
The toolbar buttons called ops directly without type filtering.
Now filters NON_TRANSFORMABLE types before applying flip/rotate ops.
2026-03-14 16:51:02 +05:30
Hiren Kangad 94ea1e3123 docs: mark Phase 3 (PDF support) as completed in expansion plan 2026-03-14 12:08:41 +05:30
Hiren Kangad 6f01c0fa54 feat: PDF support — upload, rasterize, page picker, canvas placement
Upload PDFs → server-side page rasterization via pdftoppm (poppler-utils)
→ page picker modal for selecting pages → placed on canvas as pdf-page
objects with lazy texture loading and progressive high-res upgrades.

Key features:
- New pdf-page scene object type with source PDF metadata
- Page picker modal with lazy thumbnail loading (first 20 + on-scroll)
- Priority queue: hires jobs (priority 10) jump ahead of thumbnails (0)
- Single-page PDFs skip picker, place directly
- No crop/rotate/flip on PDF pages (enforced in TransformBox, context
  menu, and keyboard shortcuts)
- Per-page socket events for progressive texture upgrades
- 500-page cap, 50-page batch placement limit
- Early validation (before MinIO upload) prevents orphaned assets
2026-03-14 12:07:15 +05:30
Hiren Kangad 4dbd755c41 fix: PdfPickerModal thumbnail URL uses /api/images/ not /api/assets/ 2026-03-14 11:40:49 +05:30
Hiren Kangad 9c8b3cc5f5 feat: wire PdfPickerModal into Editor with hires texture upgrade via socket 2026-03-14 11:31:45 +05:30
Hiren Kangad 656b0a9875 feat: PdfPickerModal — page selection grid with lazy thumbnail loading 2026-03-14 11:29:31 +05:30
Hiren Kangad bbf9b9484a feat: PDF upload response forks to picker callback or direct placement 2026-03-14 11:28:29 +05:30
Hiren Kangad 3bd6270990 feat: frontend accepts PDF uploads, UploadManager detects PDF type 2026-03-14 11:26:29 +05:30
Hiren Kangad e118ad632a feat: block rotate/flip for pdf-page in context menu and shortcuts 2026-03-14 11:22:29 +05:30
Hiren Kangad 07953ab18d feat: SceneManager creates pdf-page items with culling support 2026-03-14 11:21:08 +05:30
Hiren Kangad 332f12cafe feat: add PdfPageSprite — placeholder, lazy texture, page badge 2026-03-14 11:19:55 +05:30
Hiren Kangad 3511c54658 feat: add PdfPageObject type to scene format 2026-03-14 11:19:04 +05:30
Hiren Kangad 247145f545 fix: early PDF validation, type-aware errors, exact job dedup
- Move PDF page count check before MinIO upload and DB record creation
  to prevent orphaned objects when >500 page PDFs are rejected
- Use job type label (PDF page / Video) in media-worker error messages
  instead of hardcoded "Video processing failed"
- Replace LIKE-based idempotency check with exact JSON match to prevent
  page 1 matching page 11/12/etc substring collisions
2026-03-14 11:16:58 +05:30
Hiren Kangad 4b68e337cf feat: media worker dispatches pdf-thumbnail and pdf-hires jobs 2026-03-14 11:12:41 +05:30
Hiren Kangad 1250ad1b72 feat: add PDF page selection and lazy thumbnail endpoints 2026-03-14 11:11:50 +05:30
Hiren Kangad 0b56ec7f6f feat: accept PDF uploads — pdfinfo extraction, pdf_pages rows, thumbnail job queuing 2026-03-14 11:10:45 +05:30
Hiren Kangad d28998e4d7 feat: add pdf-utils.js — pdfInfo, pdfRenderPage, bufferToTempFile 2026-03-14 11:10:01 +05:30
Hiren Kangad e1aedd721b feat(db): add pdf_pages table, priority column, and helper functions 2026-03-14 11:08:45 +05:30
Hiren Kangad 0e14790990 build: add poppler-utils to Dockerfile for PDF support 2026-03-14 11:07:03 +05:30
Hiren Kangad a901833b72 fix: rotated transform box, video texture crash, paste duplication, and loading UX
- TransformBox rotates with single-item selection (Figma-style), with
  handles and resize math projected into rotated coordinate space
- Fix VideoSprite alphaMode crash by swapping texture to EMPTY before
  destroying, preventing PixiJS render loop from reading null source
- Fix Ctrl+V double-paste: internal clipboard now always takes priority
  over system clipboard PNG, with wasRecentInternalPaste() guard
- Add asset loading progress bar and suppress "Drop images here" flash
  during initial scene load
2026-03-13 17:37:21 +05:30
Hiren Kangad e7e217d3d4 fix: restrict rotation to image/video selections only
Hide rotate handles and block rotation drag for sticky, markdown,
text, drawing, and group item types. Only images and videos support
rotation.
2026-03-13 17:07:26 +05:30
Hiren Kangad 05118330d6 feat: unified composition renderer for clipboard/export
Replace the split snapshot/native renderer paths with a single
composition pipeline (compositionRenderer.ts) that:

- Loads actual source images and uses naturalWidth/Height for
  correct full-resolution sampling (fixes top-left-corner-only bug
  caused by data.w/h being capped to 600px display dimensions)
- Routes image-only selections through native Canvas 2D composition
- Falls back to viewport snapshot for mixed/unsupported selections
  with explicit warnings instead of silent degradation
- Resolves group children via itemResolver for proper group export
- Rejects group children from native composition (local coords
  incompatible with world-space drawing)
- Adds canvas size safety limits with auto-downscale
- Guards VideoSprite._drawFrame against null texture source race
  condition during zoom-triggered culling

New files:
- compositionRenderer.ts — unified composition module
- compositionRenderer.test.ts — 16 tests for entry flattening,
  bounds, dimensions, group handling

Modified:
- clipboard.ts — uses composeSelection() instead of direct renderers
- export.ts — uses composeSelection() + getCompositionDimensions()
- Editor.tsx, useShortcutHandler.ts — pass scene for group resolution
- VideoSprite.ts — null guard on texture source in frame loop
2026-03-13 16:59:11 +05:30
Hiren Kangad 1ceafec67f refboard: fix native export pixel bounds rounding 2026-03-13 15:51:12 +05:30
Hiren Kangad 7b2fcfd6a8 refboard: add native image copy and export renderer 2026-03-13 15:48:12 +05:30
Hiren Kangad ae9b1ecefe refboard: avoid eager extract fallback for canvas snapshots 2026-03-13 15:36:12 +05:30
Hiren Kangad 80ec4808e8 refboard: await clipboard writes before copy success 2026-03-13 15:33:37 +05:30
Hiren Kangad 3dd110f491 refboard: restore clipboard copy and native paste flows 2026-03-13 15:30:23 +05:30
Hiren Kangad e9e0c25491 refboard: standardize clipboard copy on rendered canvas 2026-03-13 15:27:41 +05:30