Alichs Chat Stack Skill
Schwarz
Article
Alichs Chat Stack Skill
name: alichs-chat-stack
description: Implement and debug the Alichs chat stack across frontend and backend. Use when working on /chat, chat SSE, visitor fingerprint identity, ScrmOpenAPI.js, api-yuntun-main, api-chat-sub, service.rabi.ae, OSS deployment, ngrok exposure, or H5 chat layout issues.
Alichs Chat Stack
Scope
This skill covers the chat feature spanning three workspace projects:
- Frontend:
alichs-in-dubai - Main backend host app:
api-codes/api-yuntun-main - Chat sub-app:
api-codes/api-chat-sub
Use this skill for feature work, debugging, deployment, and environment bring-up of the chat page.
Architecture Snapshot
Frontend
- Stack: React 18 + Razzle + Ant Design + Zustand.
- Chat route:
/chat. - User identity: browser fingerprint from
@rajesh896/broprint.js. - API client:
src/services/abudhabi/ScrmOpenAPI.js, generated bypnpm openapi. - Chat state:
src/stores/chat.js. - Chat UI:
src/routes/Chat/index.jsandsrc/routes/Chat/index.less. - Frontend dev proxy:
src/server.jsproxies/webcoreto backend.
Backend
- Main service: Hapi app in
api-yuntun-main. - Chat plugin mount:
api-chat-subis registered under/webcore/chat. - Public backend domain:
https://service.rabi.ae. - Main runtime ports:
443HTTPS app7777secondary app
- Chat storage:
- MongoDB for message persistence
- Redis for online presence and Pub/Sub
- Chat routes and handlers live in
api-chat-sub/router.
Runtime Message Flow
- Frontend generates
visitorIdfrom browser fingerprint. - Frontend opens SSE stream for online status and realtime messages.
- Frontend fetches room list and message history through
ScrmOpenAPI. - Backend stores messages in MongoDB.
- Backend pushes realtime updates by SSE, using Redis Pub/Sub when available and in-process fallback when Redis is unstable.
Key Files
Frontend
alichs-in-dubai/config/routes.jsalichs-in-dubai/src/App.jsalichs-in-dubai/src/server.jsalichs-in-dubai/src/utils/interceptors.jsalichs-in-dubai/src/utils/luna-utils.jsalichs-in-dubai/src/stores/chat.jsalichs-in-dubai/src/routes/Chat/index.jsalichs-in-dubai/src/routes/Chat/index.less
Backend
api-codes/api-yuntun-main/configuration/config.jsonapi-codes/api-yuntun-main/server.jsapi-codes/api-chat-sub/router/messages/index.jsapi-codes/api-chat-sub/router/messages/handlers/index.jsapi-codes/api-chat-sub/router/rooms/handlers/index.jsapi-codes/api-chat-sub/router/chat-runtime.js
Important Design Decisions
Conversation Model
- Do not treat direct chat as a single-user room id.
- Use a stable conversation id:
- format:
dm:<smaller-id>:<larger-id>
- format:
- Messages should carry both:
senderIdreceiverId
This prevents the receiver UI from mislabeling the sender.
SSE Host Selection
- Local frontend on
localhostshould use relative/webcore/...SSE so local proxying works. - Online frontend on
www.rabi.aemust connect SSE tohttps://service.rabi.ae/webcore/chat/base/stream. - Do not rely on axios interceptors for SSE.
EventSourceneeds explicit host selection.
SSE Response Handling
- SSE handler uses
request.raw.res.writeHead(...). - Because this bypasses normal Hapi response processing, CORS headers must be set manually.
- Required SSE headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-aliveX-Accel-Buffering: noAccess-Control-Allow-OriginAccess-Control-Allow-CredentialsVary: Origin
Redis Failure Tolerance
Redis can be unstable in this environment. Chat must degrade gracefully:
- Rooms endpoint should not hang forever on Redis calls.
- SSE should connect immediately, even if Redis subscription is late or failing.
- Online visitor list should have an in-process fallback.
- Realtime message delivery should still work within the same backend instance if Redis Pub/Sub fails.
Frontend Workflow
When adding or changing chat UI
- Update route and whitelist if needed.
- Keep chat logic centralized in
src/stores/chat.js. - Keep UI components dumb; derive state from store.
- Normalize any dynamic text before rendering.
- For H5, ensure only the message area scrolls.
Chat store responsibilities
- generate or restore
visitorId - fetch rooms
- fetch history with pagination
- open and maintain SSE connection
- merge optimistic sends
- maintain unread counts
- handle reconnects
H5 layout requirements
- Use
100dvhrather than only100vh. - Parent flex containers must allow children to shrink with
min-height: 0. - Message list must be the scroll container.
- Composer must not shrink away.
- Add bottom safe-area padding for mobile input area.
Backend Workflow
When adding or changing chat API
- Edit
api-chat-sub. - If routes change, confirm swagger includes them through
api-yuntun-main. - Run
pnpm installinsideapi-codes/api-yuntun-main.- This is required because
api-chat-subis afile:dependency and may not refresh automatically.
- This is required because
- Restart
api-yuntun-main. - Re-run frontend
pnpm openapi.
Message endpoints
- history:
GET /webcore/chat/base/rooms/{roomId}/messages - send:
POST /webcore/chat/base/rooms/{roomId}/messages - stream:
GET /webcore/chat/base/stream - offline:
POST /webcore/chat/base/offline - rooms:
GET /webcore/chat/base/rooms
Main backend registration
api-yuntun-main/configuration/config.json mounts:
api-chat-subat/webcore/chat- CORS is enabled with
origin: ["*"]
Even with route CORS enabled, SSE still needs manual headers because raw response writing bypasses automatic handling.
Local Development Environment
Frontend
Project: alichs-in-dubai
- start dev server:
pnpm start - default local page:
http://localhost:8090/chat - regenerate API client:
pnpm openapi
Backend
Project: api-codes/api-yuntun-main
- start service:
pnpm start - runtime is usually kept in
tmux - HTTPS endpoint:
https://localhost:443 - swagger:
https://localhost:443/swagger.json
External exposure
- ngrok exposes local
443ashttps://service.rabi.ae - frontend local proxy targets
service.rabi.aeby default unless intentionally kept local
Deployment Workflow
Frontend deployment
Project: alichs-in-dubai
Run:
pnpm page-publish:mac
This builds static assets and uploads them to OSS.
Expected online entry:
https://www.rabi.ae/chat/
Note:
/chat/may work while/chatreturns404if OSS/CDN routing is not configured to rewrite tochat/index.html.
Backend deployment model
- backend stays on the local host app
api-yuntun-mainserves443- ngrok maps that HTTPS service to
https://service.rabi.ae
If backend code changed:
- update
api-chat-sub - run
pnpm installinapi-yuntun-main - restart backend in
tmux - confirm
https://service.rabi.ae/swagger.json
Verification Checklist
Frontend checks
/chator/chat/loads- route is whitelisted
- room list loads
- history loads
- sender label is correct
- H5 can scroll long histories
- input box stays visible on mobile
Backend checks
- swagger includes chat endpoints
GET /webcore/chat/base/roomsreturns quicklyGET /webcore/chat/base/streamsendsstream-openandconnected- SSE response includes CORS headers for
www.rabi.ae - history endpoint returns chronological message list
Useful probe commands
curl -I https://service.rabi.ae/swagger.json
curl -N "http://localhost:8090/webcore/chat/base/stream?visitorId=test&roomId=test"
curl -I -H "Origin: https://www.rabi.ae" "https://service.rabi.ae/webcore/chat/base/stream?visitorId=test&roomId=test"
curl "http://localhost:8090/webcore/chat/base/rooms"
Common Failure Modes
Objects are not valid as a React child
Cause:
- a message or room field contains
{ code, data, message }or another object and gets rendered directly
Fix:
- normalize renderable text in the store and component layer
History API added but frontend client missing method
Cause:
api-yuntun-mainstill uses stale localfile:dependency
Fix:
- run
pnpm installinapi-yuntun-main - restart backend
- run
pnpm openapiin frontend
Local /webcore/chat/base/rooms hangs
Cause:
- Redis calls stall
Fix:
- use timeout-wrapped Redis calls
- return in-memory online visitor fallback
Online SSE blocked by CORS
Cause:
- SSE switched to
service.rabi.aebut raw response omittedAccess-Control-Allow-Origin
Fix:
- add CORS headers in
handleSSEStream
Online page loads but SSE points to wrong host
Cause:
EventSourcestill uses relative/webcore/...
Fix:
- explicitly use
https://service.rabi.ae/webcore/chat/base/streamonline - keep relative path only for local
localhost
H5 cannot scroll and composer is pushed off-screen
Cause:
- stacked viewport heights and missing
min-height: 0in flex containers
Fix:
- use
100dvh - make message list the only scroll area
- keep composer
flex-shrink: 0
Default Execution Order For Future Agents
When asked to change chat behavior, use this order:
- Confirm whether the problem is frontend-only, backend-only, or both.
- Check
src/stores/chat.jsfirst for state, SSE, and host selection issues. - Check
api-chat-subnext for API behavior and SSE streaming. - If backend code changed, run
pnpm installinapi-yuntun-main. - Restart backend in
tmux. - If Swagger changed, run
pnpm openapi. - Verify locally.
- If requested, deploy frontend with
pnpm page-publish:mac.
Output Expectations
When using this skill, the agent should report:
- what changed in frontend
- what changed in backend
- whether
api-yuntun-maindependency sync was required - whether backend restart was performed
- what was verified locally
- what was verified online