medusa-live-chat
v0.0.2
Published
A starter for Medusa plugins.
Maintainers
Readme
Medusa Live Chat Plugin
medusa-live-chat is a Medusa v2 plugin that provides customer-admin chat with:
- direct channels between customer and admin users
- REST APIs for store and admin scopes
- realtime event gateway abstraction (Socket.IO-ready)
- unread tracking based on
last_read_at - offline notification integration via Medusa notification module
- admin extension page for channel/thread/composer
Compatibility
@medusajs/medusa2.12.x- Node
>=20
Feature Overview
- Channel model
- deterministic direct channel hash (
direct:<idA>:<idB>) - single channel reused per customer-admin pair
- deterministic direct channel hash (
- Message model
- supports
text,meeting,systemtypes - create/read/edit/delete endpoints
- supports
- Read state
- per-member
last_read_at - unread counts derived from message timestamps
- per-member
- Realtime
- user room pattern:
user:{id} - event envelope supports
event_id,channel_id,message_id,ts
- user room pattern:
- Fallback notifications
- when recipient is offline, plugin triggers notification module calls
- Digest job
- periodic unread summary sender (
chat-unread-digest)
- periodic unread summary sender (
Configuration
Use a central src/config.ts and read runtime values from there.
1) Create src/config.ts
export type ChatConfig = {
chat_v1_enabled: boolean
}
export const chatConfig: ChatConfig = {
chat_v1_enabled: true,
}
export function setChatConfig(input: Partial<ChatConfig>): void {
if ("chat_v1_enabled" in input) {
chatConfig.chat_v1_enabled = input.chat_v1_enabled === false ? false : true
}
}2) Register plugin in your Medusa app medusa-config.ts
import { defineConfig } from "@medusajs/framework/utils"
export default defineConfig({
plugins: [
{
resolve: "medusa-live-chat",
},
],
})3) Update chat config in src/config.ts
Set chat_v1_enabled directly in src/config.ts or call setChatConfig(...).
Behavior:
- default is
true - it becomes
falseonly whenchat_v1_enabled === false - all other values keep it enabled
When chat_v1_enabled is false, chat routes return Chat is disabled.
Data Model
Migration creates:
chat_channelid,channel_hash(unique),type,last_message_at, timestamps
chat_channel_memberid,channel_id,user_id,role,last_read_at,notified_at, timestamps- unique
(channel_id, user_id) - membership tracks customer/guest participants (admins are global operators and are not added by default)
chat_messageid,channel_id,sender_id,body,message_type,edited_at,deleted_at, timestamps- indexes on
(channel_id, created_at)and(sender_id, created_at)
API Endpoints
Admin (/admin/chat/*)
GET /admin/chat/channelsPOST /admin/chat/channelsPUT /admin/chat/channels/:channelHash/readGET /admin/chat/messages/:channelHashPOST /admin/chat/messages/:channelHashPUT /admin/chat/messages/id/:messageIdDELETE /admin/chat/messages/id/:messageIdGET /admin/chat/messages/unread-count
Auth rule: actor_type must be user.
Admin behavior:
- any authenticated admin can list all channels from
GET /admin/chat/channels - any authenticated admin can open any channel thread
- any authenticated admin can send messages in any channel from
POST /admin/chat/messages/:channelHash PUT /admin/chat/channels/:channelHash/readworks in admin scope without requiring channel membershipGET /admin/chat/messages/unread-countuses shop_staff scope: counts customer/guest messages in all direct channels for the logged-in admin user, usinglast_read_aton that user’s channel membership when present (membership is created when the admin marks a channel read)
Store (/store/chat/*)
GET /store/chat/channelsPOST /store/chat/channelsPUT /store/chat/channels/:channelHash/readGET /store/chat/messages/:channelHashPOST /store/chat/messages/:channelHashPUT /store/chat/messages/id/:messageIdDELETE /store/chat/messages/id/:messageIdGET /store/chat/messages/unread-count
Auth rule:
- authenticated requests use
auth_context.actor_id(customer session) - logged-out requests must send
guest_id_for_live_chatheader - if both are absent, route returns unauthorized
POST /store/chat/channelsaccepts optionaladmin_id; when omitted, a default admin key is usedGET /store/chat/channelsenriches each channel withadmin_idandadmin(when user data can be resolved)
Core Service Contracts
Implemented in src/modules/chat/service.ts:
getOrCreateDirectChannel(customerId, adminId)sendMessage({ channelId, senderId, body, type })sendMessageAsAdmin({ channelId, senderId, body, type })listUserChannels(userId, { search, limit, offset })listAllChannels({ search, limit, offset })getMessages(channelId, { cursor, direction, limit })markChannelRead(channelId, userId, at?)markChannelReadAsAdmin(channelId, userId, at?)editMessage(messageId, userId, body)deleteMessage(messageId, userId)getUnreadCount(userId, mode?)—participant(default, storefront) orshop_staff(admin + digest job)
Business checks:
- sender must be channel member
- admin send in admin scope can bypass channel membership
- admin mark-read creates an admin membership row if needed and persists
last_read_at - edit/delete allowed only to message author
- channel search must be at least 3 characters
Realtime Events
Gateway lives at src/modules/chat/realtime/gateway.ts.
Event names:
chat.message.createdchat.message.updatedchat.message.deletedchat.channel.read
Envelope shape:
{
"event_id": "evt_or_message_id",
"channel_id": "chat_channel_id",
"message_id": "optional_message_id",
"ts": "2026-03-26T12:00:00.000Z",
"payload": {}
}Admin UI
Admin extension route:
/app/chat
Includes:
- channel search/list
- create/open channel by customer ID
- unread counter
- thread view
- mark selected channel as read
- message composer (send)
- message edit/delete actions
Jobs
src/jobs/chat-unread-digest.ts- iterates users
- checks unread count
- sends digest via notification module
Development
Install dependencies:
npm installBuild plugin:
npm run buildRun tests:
npm run testTests
Table-driven tests are included:
src/modules/chat/__tests__/utils.test.tssrc/modules/chat/__tests__/service.test.tssrc/modules/chat/realtime/__tests__/gateway.test.tssrc/api/_shared/__tests__/chat.test.tssrc/api/store/chat/channels/__tests__/route.test.tssrc/api/store/chat/messages/[channelHash]/__tests__/route.test.tssrc/api/admin/chat/channels/__tests__/route.test.tssrc/api/admin/chat/channels/[channelHash]/read/__tests__/route.test.tssrc/api/admin/chat/messages/[channelHash]/__tests__/route.test.tssrc/api/admin/chat/messages/unread-count/__tests__/route.test.ts
Notes
- Routes are intentionally thin and delegate business logic to module service.
- Notification sending uses Medusa notification module integration (
create/createNotifications). - For production rollout, add provider-specific email templates for:
chat_unread_messagechat_daily_digest
