Files
plane/.env.example
Aaron 3284b5d7b0 [WEB-6646] fix(flux): resolve workspace identifier mismatch between frontend, server, and consumer (#6233)
* fix(flux): resolve workspace identifier mismatch between frontend, server, and consumer

The flux service had a fundamental identifier mismatch:
- Frontend connects to /events/{workspaceSlug} (slug)
- Consumer receives events with workspace_id (UUID from outbox)
- Auth middleware needs slug for Django API calls

Solution: Redis-backed WorkspaceRegistry for UUID↔slug mapping

Changes:
- Add WorkspaceRegistry service backed by Redis with bidirectional
  UUID↔slug mapping (24h TTL per key)
- Add getWorkspace method to AuthService (calls GET /api/workspaces/{slug}/)
- Update auth middleware to fetch workspace UUID on connection and
  register the mapping; store both workspaceSlug and workspaceId on socket
- Update consumer to resolve workspace_id→slug via registry before emitting
- Remove workspace_slug field from EventMessage schema (uses registry instead)
- Fix lint warnings: use Effect.async/Effect.promise idiomatically,
  wrap async middleware to avoid no-misused-promises
- Frontend already updated to use workspaceSlug throughout (prior work)

* refactor(web): improve socket client with useSyncExternalStore

- Use useSyncExternalStore for tear-safe status reads from SocketClient
- Replace single statusChangeHandler with Set for multi-listener support
- Simplify work-item-detail revalidation with inverse logic instead of
  fragile 15-item event type list
- Rename work-item.tsx → work-item.ts (pure type file, no JSX)

* fix(flux): update .env.example AMQP_URL with credentials and vhost

The consumer needs the RabbitMQ user, password, and vhost to connect
to the correct virtual host where plane.event_stream exchange exists.

* fix(flux): handle registry errors in consumer, remove redundant httpServer.close

- Wrap registry.getSlugById() with Effect.either in consumer to catch
  Redis errors, log them, and ack the message instead of crashing the
  stream and leaving messages stuck/unacked.
- Remove redundant httpServer.close() in server shutdown — io.close()
  already closes the underlying httpServer when one was passed to the
  constructor.

* fix: update lockfile after removing axios from flux

* fix(flux): sliding TTL on registry reads, isolate status listeners

- Add sliding TTL (expire refresh) on successful WorkspaceRegistry reads
  to prevent mappings expiring while actively used by the consumer.
- Wrap status change listener calls in try/catch to prevent one failing
  listener from blocking others.
- Remove void prefix on socket.join() calls (synchronous at runtime).

* Remove dead code from events handler and client types

- Remove unused workspace:/user: room joins (nothing emits to them)
- Remove unused connected event emit and client type
- Remove onAny logger (production noise)
- Add ALLOWED_ENTITY_TYPES validation on subscribe/unsubscribe
- Use namespace.sockets.size for connection stats

* fix(flux): refactor socket system to minimal signal architecture

Flux server:
- AMQP service with acquireRelease lifecycle and reconnect loop
- Consumer parses outbox events, builds minimal signals, emits via Redis
- SocketEmitter uses @socket.io/redis-emitter for cross-process delivery
- Room-based routing: only subscribed clients receive entity events
- WorkspaceRegistry with bidirectional slug/UUID mapping and sliding TTL
- RedisClient as shared Context.Tag singleton

Web client:
- Replace verbose type system with minimal TEntityEvent
- Replace useSocketEvent/useFilteredSocketEvent with useEntityEvent
- Room-based subscriptions via subscribe:entity/unsubscribe:entity
- Remove deleted work-item.ts types file
- Simplify provider context to status + subscribeEntity

* docs(flux): document TODO for excluding originating connection from broadcast

* fix(flux): update stale JSDoc and use register() for resilient registry refresh

- Fix JSDoc on setupEventsNamespace to reflect current room-based architecture
- Use register() instead of refreshTtl() for periodic refresh so mappings
  survive Redis eviction/restart instead of silently no-oping on EXPIRE
2026-03-20 01:48:26 +05:30

46 lines
1.1 KiB
Plaintext

# Database Settings
POSTGRES_USER="plane"
POSTGRES_PASSWORD="plane"
POSTGRES_DB="plane"
PGDATA="/var/lib/postgresql/data"
# Redis Settings
REDIS_HOST="plane-redis"
REDIS_PORT="6379"
# RabbitMQ Settings
RABBITMQ_HOST="plane-mq"
RABBITMQ_PORT="5672"
RABBITMQ_USER="plane"
RABBITMQ_PASSWORD="plane"
RABBITMQ_VHOST="plane"
LISTEN_HTTP_PORT=80
LISTEN_HTTPS_PORT=443
# AWS Settings
AWS_REGION=""
AWS_ACCESS_KEY_ID="access-key"
AWS_SECRET_ACCESS_KEY="secret-key"
AWS_S3_ENDPOINT_URL="http://plane-minio:9000"
# Changing this requires change in the proxy config for uploads if using minio setup
AWS_S3_BUCKET_NAME="uploads"
# Maximum file upload limit
FILE_SIZE_LIMIT=5242880
# Settings related to Docker
DOCKERIZED=1 # deprecated
# set to 1 If using the pre-configured minio setup
USE_MINIO=1
# If SSL Cert to be generated, set CERT_EMAIl="email <EMAIL_ADDRESS>"
CERT_ACME_CA=https://acme-v02.api.letsencrypt.org/directory
TRUSTED_PROXIES=0.0.0.0/0
SITE_ADDRESS=:80
CERT_EMAIL=
# For DNS Challenge based certificate generation, set the CERT_ACME_DNS, CERT_EMAIL
# CERT_ACME_DNS="acme_dns <CERT_DNS_PROVIDER> <CERT_DNS_PROVIDER_API_KEY>"
CERT_ACME_DNS=