@cometchat/skills-cli 2.4.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -37
- package/dist/index.js +1821 -13090
- package/dist/utils/error-dictionary.js +0 -69
- package/package.json +10 -12
- package/dist/registry/schemas/config.schema.json +0 -119
- package/dist/registry/schemas/template.schema.json +0 -142
- package/dist/registry/v6/experiences/1-conversation-list/astro.template.json +0 -89
- package/dist/registry/v6/experiences/1-conversation-list/nextjs-pages.template.json +0 -83
- package/dist/registry/v6/experiences/1-conversation-list/nextjs.template.json +0 -83
- package/dist/registry/v6/experiences/1-conversation-list/react-router.template.json +0 -102
- package/dist/registry/v6/experiences/1-conversation-list/reactjs-dedicated-route.template.json +0 -81
- package/dist/registry/v6/experiences/1-conversation-list/reactjs.template.json +0 -86
- package/dist/registry/v6/experiences/1-conversation-list/templates/astro/ChatApp.css.tpl +0 -43
- package/dist/registry/v6/experiences/1-conversation-list/templates/astro/ChatApp.tsx.tpl +0 -86
- package/dist/registry/v6/experiences/1-conversation-list/templates/astro/CometChatSelector.tsx.tpl +0 -40
- package/dist/registry/v6/experiences/1-conversation-list/templates/astro/chat.astro.tpl +0 -17
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs/CometChatNoSSR.css.tpl +0 -43
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs/CometChatNoSSR.tsx.tpl +0 -86
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs/CometChatSelector.tsx.tpl +0 -41
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs/chat-page.tsx.tpl +0 -11
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs-pages/CometChatNoSSR.css.tpl +0 -43
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs-pages/CometChatNoSSR.tsx.tpl +0 -88
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs-pages/CometChatSelector.tsx.tpl +0 -41
- package/dist/registry/v6/experiences/1-conversation-list/templates/nextjs-pages/chat-page.tsx.tpl +0 -15
- package/dist/registry/v6/experiences/1-conversation-list/templates/react-router/CometChat-route.tsx.tpl +0 -22
- package/dist/registry/v6/experiences/1-conversation-list/templates/react-router/CometChatNoSSR.css.tpl +0 -43
- package/dist/registry/v6/experiences/1-conversation-list/templates/react-router/CometChatNoSSR.tsx.tpl +0 -87
- package/dist/registry/v6/experiences/1-conversation-list/templates/react-router/CometChatSelector.tsx.tpl +0 -40
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs/App.css.tpl +0 -40
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs/App.tsx.tpl +0 -55
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs/CometChatSelector.tsx.tpl +0 -40
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs/main.tsx.tpl +0 -46
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +0 -87
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +0 -20
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +0 -54
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatSelector.tsx.tpl +0 -40
- package/dist/registry/v6/experiences/2-one-to-one/astro.template.json +0 -81
- package/dist/registry/v6/experiences/2-one-to-one/nextjs.template.json +0 -77
- package/dist/registry/v6/experiences/2-one-to-one/react-router.template.json +0 -93
- package/dist/registry/v6/experiences/2-one-to-one/reactjs-dedicated-route.template.json +0 -75
- package/dist/registry/v6/experiences/2-one-to-one/reactjs.template.json +0 -80
- package/dist/registry/v6/experiences/2-one-to-one/templates/astro/ChatApp.css.tpl +0 -21
- package/dist/registry/v6/experiences/2-one-to-one/templates/astro/ChatApp.tsx.tpl +0 -66
- package/dist/registry/v6/experiences/2-one-to-one/templates/astro/chat.astro.tpl +0 -17
- package/dist/registry/v6/experiences/2-one-to-one/templates/nextjs/CometChatNoSSR.css.tpl +0 -21
- package/dist/registry/v6/experiences/2-one-to-one/templates/nextjs/CometChatNoSSR.tsx.tpl +0 -66
- package/dist/registry/v6/experiences/2-one-to-one/templates/nextjs/chat-page.tsx.tpl +0 -11
- package/dist/registry/v6/experiences/2-one-to-one/templates/react-router/CometChat-route.tsx.tpl +0 -21
- package/dist/registry/v6/experiences/2-one-to-one/templates/react-router/CometChatNoSSR.css.tpl +0 -21
- package/dist/registry/v6/experiences/2-one-to-one/templates/react-router/CometChatNoSSR.tsx.tpl +0 -67
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs/App.css.tpl +0 -4
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs/App.tsx.tpl +0 -44
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs/main.tsx.tpl +0 -46
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +0 -84
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +0 -9
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/astro.template.json +0 -93
- package/dist/registry/v6/experiences/3-tab-based/nextjs.template.json +0 -90
- package/dist/registry/v6/experiences/3-tab-based/react-router.template.json +0 -105
- package/dist/registry/v6/experiences/3-tab-based/reactjs-dedicated-route.template.json +0 -89
- package/dist/registry/v6/experiences/3-tab-based/reactjs.template.json +0 -93
- package/dist/registry/v6/experiences/3-tab-based/templates/astro/ChatApp.css.tpl +0 -39
- package/dist/registry/v6/experiences/3-tab-based/templates/astro/ChatApp.tsx.tpl +0 -93
- package/dist/registry/v6/experiences/3-tab-based/templates/astro/CometChatTabs.css.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/templates/astro/CometChatTabs.tsx.tpl +0 -72
- package/dist/registry/v6/experiences/3-tab-based/templates/astro/chat.astro.tpl +0 -17
- package/dist/registry/v6/experiences/3-tab-based/templates/nextjs/CometChatNoSSR.css.tpl +0 -39
- package/dist/registry/v6/experiences/3-tab-based/templates/nextjs/CometChatNoSSR.tsx.tpl +0 -93
- package/dist/registry/v6/experiences/3-tab-based/templates/nextjs/CometChatTabs.css.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/templates/nextjs/CometChatTabs.tsx.tpl +0 -73
- package/dist/registry/v6/experiences/3-tab-based/templates/nextjs/chat-page.tsx.tpl +0 -11
- package/dist/registry/v6/experiences/3-tab-based/templates/react-router/CometChat-route.tsx.tpl +0 -21
- package/dist/registry/v6/experiences/3-tab-based/templates/react-router/CometChatNoSSR.css.tpl +0 -39
- package/dist/registry/v6/experiences/3-tab-based/templates/react-router/CometChatNoSSR.tsx.tpl +0 -94
- package/dist/registry/v6/experiences/3-tab-based/templates/react-router/CometChatTabs.css.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/templates/react-router/CometChatTabs.tsx.tpl +0 -72
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs/App.css.tpl +0 -7
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs/App.tsx.tpl +0 -62
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs/CometChatTabs.css.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs/CometChatTabs.tsx.tpl +0 -72
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs/main.tsx.tpl +0 -46
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +0 -84
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +0 -11
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +0 -61
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.css.tpl +0 -43
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.tsx.tpl +0 -72
- package/dist/registry/v6/features/catalog.json +0 -990
- package/dist/registry/v6/production/astro/cometchat-token.ts.tpl +0 -86
- package/dist/registry/v6/production/astro.template.json +0 -51
- package/dist/registry/v6/production/nextjs/route.ts.tpl +0 -79
- package/dist/registry/v6/production/nextjs.template.json +0 -50
- package/dist/registry/v6/production/react-router/api.cometchat-token.ts.tpl +0 -73
- package/dist/registry/v6/production/react-router.template.json +0 -58
- package/dist/registry/v6/user-mgmt/astro/cometchat-user.ts.tpl +0 -164
- package/dist/registry/v6/user-mgmt/astro.template.json +0 -33
- package/dist/registry/v6/user-mgmt/nextjs/route.ts.tpl +0 -212
- package/dist/registry/v6/user-mgmt/nextjs.template.json +0 -57
- package/dist/registry/v6/user-mgmt/react-router/api.cometchat-user.ts.tpl +0 -176
- package/dist/registry/v6/user-mgmt/react-router.template.json +0 -40
- package/dist/registry/v6/widget/astro/CometChatWidget.css.tpl +0 -105
- package/dist/registry/v6/widget/astro/CometChatWidget.tsx.tpl +0 -88
- package/dist/registry/v6/widget/astro.template.json +0 -38
- package/dist/registry/v6/widget/nextjs/CometChatWidget.css.tpl +0 -105
- package/dist/registry/v6/widget/nextjs/CometChatWidget.tsx.tpl +0 -88
- package/dist/registry/v6/widget/nextjs.template.json +0 -40
- package/dist/registry/v6/widget/react-router/CometChatWidget.css.tpl +0 -105
- package/dist/registry/v6/widget/react-router/CometChatWidget.tsx.tpl +0 -98
- package/dist/registry/v6/widget/react-router.template.json +0 -36
- package/dist/registry/v6/widget/reactjs/CometChatWidget.css.tpl +0 -105
- package/dist/registry/v6/widget/reactjs/CometChatWidget.tsx.tpl +0 -89
- package/dist/registry/v6/widget/reactjs.template.json +0 -36
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* /api/cometchat-user — server-side CometChat user management.
|
|
3
|
-
*
|
|
4
|
-
* Handles three operations:
|
|
5
|
-
* POST — create a new CometChat user (call when an app user signs up)
|
|
6
|
-
* PATCH — update an existing user (call when profile changes)
|
|
7
|
-
* DELETE — delete a user (call when app user is deleted)
|
|
8
|
-
*
|
|
9
|
-
* All operations use the CometChat REST API with the server-only AUTH_KEY.
|
|
10
|
-
* The auth key NEVER leaves the server.
|
|
11
|
-
*
|
|
12
|
-
* Generated by `cometchat add-user-mgmt`.
|
|
13
|
-
*
|
|
14
|
-
* SECURITY: this template is unauthenticated by design — add your own
|
|
15
|
-
* session check (NextAuth, Clerk, custom JWT) before exposing it publicly.
|
|
16
|
-
* Otherwise anyone with the URL can create/delete users in your app.
|
|
17
|
-
*/
|
|
18
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
19
|
-
|
|
20
|
-
const COMETCHAT_APP_ID = process.env.NEXT_PUBLIC_COMETCHAT_APP_ID!;
|
|
21
|
-
const COMETCHAT_REGION = process.env.NEXT_PUBLIC_COMETCHAT_REGION!;
|
|
22
|
-
const COMETCHAT_AUTH_KEY = process.env.COMETCHAT_AUTH_KEY!;
|
|
23
|
-
|
|
24
|
-
interface CreateUserBody {
|
|
25
|
-
uid: string;
|
|
26
|
-
name: string;
|
|
27
|
-
avatar?: string;
|
|
28
|
-
link?: string;
|
|
29
|
-
role?: string;
|
|
30
|
-
metadata?: Record<string, unknown>;
|
|
31
|
-
tags?: string[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface UpdateUserBody {
|
|
35
|
-
uid: string;
|
|
36
|
-
name?: string;
|
|
37
|
-
avatar?: string;
|
|
38
|
-
link?: string;
|
|
39
|
-
role?: string;
|
|
40
|
-
metadata?: Record<string, unknown>;
|
|
41
|
-
tags?: string[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const COMETCHAT_API_BASE = `https://${COMETCHAT_APP_ID}.api-${COMETCHAT_REGION}.cometchat.io/v3`;
|
|
45
|
-
|
|
46
|
-
const baseHeaders = {
|
|
47
|
-
"Content-Type": "application/json",
|
|
48
|
-
appid: COMETCHAT_APP_ID,
|
|
49
|
-
apikey: COMETCHAT_AUTH_KEY,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function validateConfig(): NextResponse | null {
|
|
53
|
-
if (!COMETCHAT_APP_ID || !COMETCHAT_REGION || !COMETCHAT_AUTH_KEY) {
|
|
54
|
-
return NextResponse.json(
|
|
55
|
-
{ error: "CometChat credentials not configured on the server" },
|
|
56
|
-
{ status: 500 },
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// CometChat UIDs must be alphanumeric + hyphens + underscores. Validate before
|
|
63
|
-
// passing to the API to give better error messages than the upstream 400.
|
|
64
|
-
function validateUid(uid: unknown): string | null {
|
|
65
|
-
if (typeof uid !== "string" || uid.length === 0) return "uid is required";
|
|
66
|
-
if (uid.length > 100) return "uid must be ≤100 characters";
|
|
67
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(uid)) {
|
|
68
|
-
return "uid must contain only alphanumeric characters, hyphens, and underscores";
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** POST — create a new user. Body: { uid, name, ...optional }. */
|
|
74
|
-
export async function POST(request: NextRequest) {
|
|
75
|
-
const configError = validateConfig();
|
|
76
|
-
if (configError) return configError;
|
|
77
|
-
|
|
78
|
-
let body: CreateUserBody;
|
|
79
|
-
try {
|
|
80
|
-
body = await request.json();
|
|
81
|
-
} catch {
|
|
82
|
-
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const uidError = validateUid(body.uid);
|
|
86
|
-
if (uidError) return NextResponse.json({ error: uidError }, { status: 400 });
|
|
87
|
-
if (!body.name || typeof body.name !== "string") {
|
|
88
|
-
return NextResponse.json({ error: "name is required" }, { status: 400 });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
const res = await fetch(`${COMETCHAT_API_BASE}/users`, {
|
|
93
|
-
method: "POST",
|
|
94
|
-
headers: baseHeaders,
|
|
95
|
-
body: JSON.stringify({
|
|
96
|
-
uid: body.uid,
|
|
97
|
-
name: body.name,
|
|
98
|
-
...(body.avatar && { avatar: body.avatar }),
|
|
99
|
-
...(body.link && { link: body.link }),
|
|
100
|
-
...(body.role && { role: body.role }),
|
|
101
|
-
...(body.metadata && { metadata: body.metadata }),
|
|
102
|
-
...(body.tags && { tags: body.tags }),
|
|
103
|
-
}),
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (!res.ok) {
|
|
107
|
-
const detail = await res.text();
|
|
108
|
-
return NextResponse.json(
|
|
109
|
-
{ error: `CometChat user create failed (${res.status})`, detail },
|
|
110
|
-
{ status: res.status === 409 ? 409 : 502 },
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const data = (await res.json()) as { data?: unknown };
|
|
115
|
-
return NextResponse.json({ user: data.data }, { status: 201 });
|
|
116
|
-
} catch (err) {
|
|
117
|
-
return NextResponse.json(
|
|
118
|
-
{ error: "Create failed", detail: err instanceof Error ? err.message : String(err) },
|
|
119
|
-
{ status: 500 },
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** PATCH — update an existing user. Body: { uid, ...fields-to-update }. */
|
|
125
|
-
export async function PATCH(request: NextRequest) {
|
|
126
|
-
const configError = validateConfig();
|
|
127
|
-
if (configError) return configError;
|
|
128
|
-
|
|
129
|
-
let body: UpdateUserBody;
|
|
130
|
-
try {
|
|
131
|
-
body = await request.json();
|
|
132
|
-
} catch {
|
|
133
|
-
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const uidError = validateUid(body.uid);
|
|
137
|
-
if (uidError) return NextResponse.json({ error: uidError }, { status: 400 });
|
|
138
|
-
|
|
139
|
-
const updates: Record<string, unknown> = {};
|
|
140
|
-
if (body.name !== undefined) updates.name = body.name;
|
|
141
|
-
if (body.avatar !== undefined) updates.avatar = body.avatar;
|
|
142
|
-
if (body.link !== undefined) updates.link = body.link;
|
|
143
|
-
if (body.role !== undefined) updates.role = body.role;
|
|
144
|
-
if (body.metadata !== undefined) updates.metadata = body.metadata;
|
|
145
|
-
if (body.tags !== undefined) updates.tags = body.tags;
|
|
146
|
-
|
|
147
|
-
if (Object.keys(updates).length === 0) {
|
|
148
|
-
return NextResponse.json({ error: "No fields to update" }, { status: 400 });
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const res = await fetch(`${COMETCHAT_API_BASE}/users/${encodeURIComponent(body.uid)}`, {
|
|
153
|
-
method: "PUT",
|
|
154
|
-
headers: baseHeaders,
|
|
155
|
-
body: JSON.stringify(updates),
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
if (!res.ok) {
|
|
159
|
-
const detail = await res.text();
|
|
160
|
-
return NextResponse.json(
|
|
161
|
-
{ error: `CometChat user update failed (${res.status})`, detail },
|
|
162
|
-
{ status: 502 },
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const data = (await res.json()) as { data?: unknown };
|
|
167
|
-
return NextResponse.json({ user: data.data });
|
|
168
|
-
} catch (err) {
|
|
169
|
-
return NextResponse.json(
|
|
170
|
-
{ error: "Update failed", detail: err instanceof Error ? err.message : String(err) },
|
|
171
|
-
{ status: 500 },
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** DELETE — delete a user. Query: ?uid=<uid>. */
|
|
177
|
-
export async function DELETE(request: NextRequest) {
|
|
178
|
-
const configError = validateConfig();
|
|
179
|
-
if (configError) return configError;
|
|
180
|
-
|
|
181
|
-
const uid = request.nextUrl.searchParams.get("uid");
|
|
182
|
-
const uidError = validateUid(uid);
|
|
183
|
-
if (uidError) return NextResponse.json({ error: uidError }, { status: 400 });
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
const res = await fetch(
|
|
187
|
-
`${COMETCHAT_API_BASE}/users/${encodeURIComponent(uid as string)}`,
|
|
188
|
-
{
|
|
189
|
-
method: "DELETE",
|
|
190
|
-
headers: baseHeaders,
|
|
191
|
-
body: JSON.stringify({ permanent: true }),
|
|
192
|
-
},
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
if (!res.ok) {
|
|
196
|
-
const detail = await res.text();
|
|
197
|
-
return NextResponse.json(
|
|
198
|
-
{ error: `CometChat user delete failed (${res.status})`, detail },
|
|
199
|
-
{ status: 502 },
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return NextResponse.json({ deleted: uid });
|
|
204
|
-
} catch (err) {
|
|
205
|
-
return NextResponse.json(
|
|
206
|
-
{ error: "Delete failed", detail: err instanceof Error ? err.message : String(err) },
|
|
207
|
-
{ status: 500 },
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export const dynamic = "force-dynamic";
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../../schemas/template.schema.json",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"experience_id": 1,
|
|
5
|
-
"framework": "nextjs",
|
|
6
|
-
"sdk_compatibility": ">=6.0.0 <7.0.0",
|
|
7
|
-
"default_placement": "dedicated-route",
|
|
8
|
-
"deps": [],
|
|
9
|
-
"env_vars": [
|
|
10
|
-
{
|
|
11
|
-
"name": "COMETCHAT_AUTH_KEY",
|
|
12
|
-
"required": true,
|
|
13
|
-
"secret": true,
|
|
14
|
-
"description": "Server-side only Auth Key (no NEXT_PUBLIC_ prefix). Used by both /api/cometchat-token AND /api/cometchat-user."
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"name": "NEXT_PUBLIC_COMETCHAT_APP_ID",
|
|
18
|
-
"required": true
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"name": "NEXT_PUBLIC_COMETCHAT_REGION",
|
|
22
|
-
"required": true
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"files": [
|
|
26
|
-
{
|
|
27
|
-
"path": "src/app/api/cometchat-user/route.ts",
|
|
28
|
-
"action": "create",
|
|
29
|
-
"template_ref": "route.ts.tpl",
|
|
30
|
-
"owned": true
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
"verify_rules": [],
|
|
34
|
-
"next_steps": [
|
|
35
|
-
"1. The endpoint at /api/cometchat-user supports POST (create), PATCH (update), and DELETE.",
|
|
36
|
-
"2. Wire it into your sign-up flow. When a new user signs up, call POST /api/cometchat-user with { uid, name, avatar?, metadata? } to create their CometChat counterpart.",
|
|
37
|
-
"",
|
|
38
|
-
" Example with NextAuth (in your sign-up callback):",
|
|
39
|
-
" const res = await fetch('/api/cometchat-user', {",
|
|
40
|
-
" method: 'POST',",
|
|
41
|
-
" headers: { 'Content-Type': 'application/json' },",
|
|
42
|
-
" body: JSON.stringify({ uid: session.user.id, name: session.user.name }),",
|
|
43
|
-
" });",
|
|
44
|
-
"",
|
|
45
|
-
" Example with Clerk (in a Clerk webhook handler):",
|
|
46
|
-
" export async function POST(req: Request) {",
|
|
47
|
-
" const event = await req.json();",
|
|
48
|
-
" if (event.type === 'user.created') {",
|
|
49
|
-
" await fetch(`${process.env.NEXTAUTH_URL}/api/cometchat-user`, { ... });",
|
|
50
|
-
" }",
|
|
51
|
-
" }",
|
|
52
|
-
"",
|
|
53
|
-
"3. The endpoint validates uid is alphanumeric + hyphens only (CometChat constraint).",
|
|
54
|
-
"4. Errors are returned as { error, detail } with HTTP status codes.",
|
|
55
|
-
"5. SECURITY: in production, this endpoint should be authenticated. The template uses no auth — add session checks before deploying."
|
|
56
|
-
]
|
|
57
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* /api/cometchat-user — server-side CometChat user management.
|
|
3
|
-
*
|
|
4
|
-
* React Router v7 framework mode action+loader pattern.
|
|
5
|
-
* POST via action → create user
|
|
6
|
-
* PUT via action → update user
|
|
7
|
-
* DELETE via action → delete user
|
|
8
|
-
*
|
|
9
|
-
* The Auth Key NEVER leaves the server.
|
|
10
|
-
*
|
|
11
|
-
* Generated by `cometchat add-user-mgmt`.
|
|
12
|
-
*
|
|
13
|
-
* SECURITY: this template is unauthenticated by design — add your own
|
|
14
|
-
* session check before exposing it publicly.
|
|
15
|
-
*/
|
|
16
|
-
import type { ActionFunctionArgs } from "react-router";
|
|
17
|
-
|
|
18
|
-
const COMETCHAT_APP_ID = process.env.VITE_COMETCHAT_APP_ID!;
|
|
19
|
-
const COMETCHAT_REGION = process.env.VITE_COMETCHAT_REGION!;
|
|
20
|
-
const COMETCHAT_AUTH_KEY = process.env.COMETCHAT_AUTH_KEY!;
|
|
21
|
-
|
|
22
|
-
const COMETCHAT_API_BASE = `https://${COMETCHAT_APP_ID}.api-${COMETCHAT_REGION}.cometchat.io/v3`;
|
|
23
|
-
|
|
24
|
-
const baseHeaders = {
|
|
25
|
-
"Content-Type": "application/json",
|
|
26
|
-
appid: COMETCHAT_APP_ID,
|
|
27
|
-
apikey: COMETCHAT_AUTH_KEY,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
function jsonResponse(data: unknown, status = 200) {
|
|
31
|
-
return new Response(JSON.stringify(data), {
|
|
32
|
-
status,
|
|
33
|
-
headers: { "Content-Type": "application/json" },
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function validateUid(uid: unknown): string | null {
|
|
38
|
-
if (typeof uid !== "string" || uid.length === 0) return "uid is required";
|
|
39
|
-
if (uid.length > 100) return "uid must be ≤100 characters";
|
|
40
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(uid)) {
|
|
41
|
-
return "uid must contain only alphanumeric characters, hyphens, and underscores";
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function validateConfig() {
|
|
47
|
-
if (!COMETCHAT_APP_ID || !COMETCHAT_REGION || !COMETCHAT_AUTH_KEY) {
|
|
48
|
-
return jsonResponse({ error: "CometChat credentials not configured on the server" }, 500);
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function action({ request }: ActionFunctionArgs) {
|
|
54
|
-
const configError = validateConfig();
|
|
55
|
-
if (configError) return configError;
|
|
56
|
-
|
|
57
|
-
const method = request.method.toUpperCase();
|
|
58
|
-
|
|
59
|
-
if (method === "POST") {
|
|
60
|
-
let body;
|
|
61
|
-
try {
|
|
62
|
-
body = await request.json();
|
|
63
|
-
} catch {
|
|
64
|
-
return jsonResponse({ error: "Invalid JSON body" }, 400);
|
|
65
|
-
}
|
|
66
|
-
const uidError = validateUid(body.uid);
|
|
67
|
-
if (uidError) return jsonResponse({ error: uidError }, 400);
|
|
68
|
-
if (!body.name) return jsonResponse({ error: "name is required" }, 400);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const res = await fetch(`${COMETCHAT_API_BASE}/users`, {
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers: baseHeaders,
|
|
74
|
-
body: JSON.stringify({
|
|
75
|
-
uid: body.uid,
|
|
76
|
-
name: body.name,
|
|
77
|
-
...(body.avatar && { avatar: body.avatar }),
|
|
78
|
-
...(body.metadata && { metadata: body.metadata }),
|
|
79
|
-
...(body.tags && { tags: body.tags }),
|
|
80
|
-
}),
|
|
81
|
-
});
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
const detail = await res.text();
|
|
84
|
-
return jsonResponse(
|
|
85
|
-
{ error: `CometChat user create failed (${res.status})`, detail },
|
|
86
|
-
res.status === 409 ? 409 : 502,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
const data = await res.json();
|
|
90
|
-
return jsonResponse({ user: data.data }, 201);
|
|
91
|
-
} catch (err) {
|
|
92
|
-
return jsonResponse(
|
|
93
|
-
{ error: "Create failed", detail: err instanceof Error ? err.message : String(err) },
|
|
94
|
-
500,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (method === "PUT" || method === "PATCH") {
|
|
100
|
-
let body;
|
|
101
|
-
try {
|
|
102
|
-
body = await request.json();
|
|
103
|
-
} catch {
|
|
104
|
-
return jsonResponse({ error: "Invalid JSON body" }, 400);
|
|
105
|
-
}
|
|
106
|
-
const uidError = validateUid(body.uid);
|
|
107
|
-
if (uidError) return jsonResponse({ error: uidError }, 400);
|
|
108
|
-
|
|
109
|
-
const updates: Record<string, unknown> = {};
|
|
110
|
-
if (body.name !== undefined) updates.name = body.name;
|
|
111
|
-
if (body.avatar !== undefined) updates.avatar = body.avatar;
|
|
112
|
-
if (body.metadata !== undefined) updates.metadata = body.metadata;
|
|
113
|
-
if (body.tags !== undefined) updates.tags = body.tags;
|
|
114
|
-
if (Object.keys(updates).length === 0) {
|
|
115
|
-
return jsonResponse({ error: "No fields to update" }, 400);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
const res = await fetch(
|
|
120
|
-
`${COMETCHAT_API_BASE}/users/${encodeURIComponent(body.uid)}`,
|
|
121
|
-
{
|
|
122
|
-
method: "PUT",
|
|
123
|
-
headers: baseHeaders,
|
|
124
|
-
body: JSON.stringify(updates),
|
|
125
|
-
},
|
|
126
|
-
);
|
|
127
|
-
if (!res.ok) {
|
|
128
|
-
const detail = await res.text();
|
|
129
|
-
return jsonResponse(
|
|
130
|
-
{ error: `CometChat user update failed (${res.status})`, detail },
|
|
131
|
-
502,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
const data = await res.json();
|
|
135
|
-
return jsonResponse({ user: data.data });
|
|
136
|
-
} catch (err) {
|
|
137
|
-
return jsonResponse(
|
|
138
|
-
{ error: "Update failed", detail: err instanceof Error ? err.message : String(err) },
|
|
139
|
-
500,
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (method === "DELETE") {
|
|
145
|
-
const url = new URL(request.url);
|
|
146
|
-
const uid = url.searchParams.get("uid");
|
|
147
|
-
const uidError = validateUid(uid);
|
|
148
|
-
if (uidError) return jsonResponse({ error: uidError }, 400);
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
const res = await fetch(
|
|
152
|
-
`${COMETCHAT_API_BASE}/users/${encodeURIComponent(uid as string)}`,
|
|
153
|
-
{
|
|
154
|
-
method: "DELETE",
|
|
155
|
-
headers: baseHeaders,
|
|
156
|
-
body: JSON.stringify({ permanent: true }),
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
if (!res.ok) {
|
|
160
|
-
const detail = await res.text();
|
|
161
|
-
return jsonResponse(
|
|
162
|
-
{ error: `CometChat user delete failed (${res.status})`, detail },
|
|
163
|
-
502,
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return jsonResponse({ deleted: uid });
|
|
167
|
-
} catch (err) {
|
|
168
|
-
return jsonResponse(
|
|
169
|
-
{ error: "Delete failed", detail: err instanceof Error ? err.message : String(err) },
|
|
170
|
-
500,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return jsonResponse({ error: `Method ${method} not allowed` }, 405);
|
|
176
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../../schemas/template.schema.json",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"experience_id": 1,
|
|
5
|
-
"framework": "react-router",
|
|
6
|
-
"sdk_compatibility": ">=6.0.0 <7.0.0",
|
|
7
|
-
"default_placement": "dedicated-route",
|
|
8
|
-
"deps": [],
|
|
9
|
-
"env_vars": [
|
|
10
|
-
{
|
|
11
|
-
"name": "COMETCHAT_AUTH_KEY",
|
|
12
|
-
"required": true,
|
|
13
|
-
"secret": true,
|
|
14
|
-
"description": "Server-side only Auth Key (no VITE_ prefix). Used by the user management endpoints."
|
|
15
|
-
}
|
|
16
|
-
],
|
|
17
|
-
"files": [
|
|
18
|
-
{
|
|
19
|
-
"path": "app/routes/api.cometchat-user.ts",
|
|
20
|
-
"action": "create",
|
|
21
|
-
"template_ref": "api.cometchat-user.ts.tpl",
|
|
22
|
-
"owned": true
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"path": "app/routes.ts",
|
|
26
|
-
"action": "patch",
|
|
27
|
-
"patch_mode": "insert-before",
|
|
28
|
-
"anchor": "] satisfies RouteConfig",
|
|
29
|
-
"patch_content": " route(\"api/cometchat-user\", \"routes/api.cometchat-user.ts\"),\n",
|
|
30
|
-
"skip_if_exists": "routes/api.cometchat-user.ts"
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
"verify_rules": [],
|
|
34
|
-
"next_steps": [
|
|
35
|
-
"1. Endpoint at /api/cometchat-user supports POST (create), PATCH (update), DELETE.",
|
|
36
|
-
"2. Add COMETCHAT_AUTH_KEY (no VITE_ prefix) to .env. The auth key stays server-only.",
|
|
37
|
-
"3. Wire it into your sign-up flow — when an app user is created, call POST /api/cometchat-user with { uid, name, ... }.",
|
|
38
|
-
"4. SECURITY: this template is unauthenticated. Add session checks before deploying."
|
|
39
|
-
]
|
|
40
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CometChatWidget styles — floating button + drawer.
|
|
3
|
-
*
|
|
4
|
-
* Edit the .cometchat-widget-button selector to change the button position
|
|
5
|
-
* (default: bottom-right). Edit the .cometchat-widget-drawer selector to
|
|
6
|
-
* change the drawer size + position.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
.cometchat-widget-button {
|
|
10
|
-
position: fixed;
|
|
11
|
-
bottom: 24px;
|
|
12
|
-
right: 24px;
|
|
13
|
-
width: 56px;
|
|
14
|
-
height: 56px;
|
|
15
|
-
border-radius: 50%;
|
|
16
|
-
border: none;
|
|
17
|
-
background: var(--cometchat-primary-color, #6852D6);
|
|
18
|
-
color: white;
|
|
19
|
-
font-size: 24px;
|
|
20
|
-
cursor: pointer;
|
|
21
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
22
|
-
z-index: 9999;
|
|
23
|
-
display: flex;
|
|
24
|
-
align-items: center;
|
|
25
|
-
justify-content: center;
|
|
26
|
-
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.cometchat-widget-button:hover {
|
|
30
|
-
transform: scale(1.05);
|
|
31
|
-
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.cometchat-widget-button:active {
|
|
35
|
-
transform: scale(0.98);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.cometchat-widget-drawer {
|
|
39
|
-
position: fixed;
|
|
40
|
-
bottom: 96px;
|
|
41
|
-
right: 24px;
|
|
42
|
-
width: 720px;
|
|
43
|
-
max-width: calc(100vw - 48px);
|
|
44
|
-
height: 600px;
|
|
45
|
-
max-height: calc(100vh - 120px);
|
|
46
|
-
background: white;
|
|
47
|
-
border-radius: 12px;
|
|
48
|
-
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2);
|
|
49
|
-
z-index: 9998;
|
|
50
|
-
display: flex;
|
|
51
|
-
overflow: hidden;
|
|
52
|
-
border: 1px solid var(--cometchat-border-color-default, #e8e8e8);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.cometchat-widget-conversations {
|
|
56
|
-
width: 280px;
|
|
57
|
-
border-right: 1px solid var(--cometchat-border-color-default, #e8e8e8);
|
|
58
|
-
overflow: hidden;
|
|
59
|
-
display: flex;
|
|
60
|
-
flex-direction: column;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.cometchat-widget-conversations > .cometchat {
|
|
64
|
-
overflow: hidden;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.cometchat-widget-messages {
|
|
68
|
-
flex: 1;
|
|
69
|
-
display: flex;
|
|
70
|
-
flex-direction: column;
|
|
71
|
-
overflow: hidden;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.cometchat-widget-empty {
|
|
75
|
-
flex: 1;
|
|
76
|
-
display: flex;
|
|
77
|
-
align-items: center;
|
|
78
|
-
justify-content: center;
|
|
79
|
-
background: var(--cometchat-background-color-03, #f5f5f5);
|
|
80
|
-
color: var(--cometchat-text-color-secondary, #727272);
|
|
81
|
-
font: var(--cometchat-font-body-regular, 400 14px Roboto);
|
|
82
|
-
padding: 16px;
|
|
83
|
-
text-align: center;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@media (max-width: 600px) {
|
|
87
|
-
.cometchat-widget-drawer {
|
|
88
|
-
bottom: 16px;
|
|
89
|
-
right: 16px;
|
|
90
|
-
left: 16px;
|
|
91
|
-
width: auto;
|
|
92
|
-
height: calc(100vh - 100px);
|
|
93
|
-
flex-direction: column;
|
|
94
|
-
}
|
|
95
|
-
.cometchat-widget-conversations {
|
|
96
|
-
width: 100%;
|
|
97
|
-
height: 200px;
|
|
98
|
-
border-right: none;
|
|
99
|
-
border-bottom: 1px solid var(--cometchat-border-color-default, #e8e8e8);
|
|
100
|
-
}
|
|
101
|
-
.cometchat-widget-button {
|
|
102
|
-
bottom: 16px;
|
|
103
|
-
right: 16px;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CometChatWidget — floating chat overlay (Astro React island).
|
|
3
|
-
*
|
|
4
|
-
* Render this from any .astro file with `client:only="react"` so Astro
|
|
5
|
-
* skips SSR entirely:
|
|
6
|
-
* import CometChatWidget from "../cometchat/CometChatWidget";
|
|
7
|
-
* <CometChatWidget client:only="react" />
|
|
8
|
-
*
|
|
9
|
-
* Assumes CometChatUIKit.init() and CometChatUIKit.login() have already
|
|
10
|
-
* been called by your existing ChatApp island. The widget does NOT
|
|
11
|
-
* initialize CometChat — it just renders the existing logged-in user's
|
|
12
|
-
* chat. Mount the widget on a page that also mounts the ChatApp island,
|
|
13
|
-
* OR move init/login into a shared location.
|
|
14
|
-
*/
|
|
15
|
-
import { useEffect, useState } from "react";
|
|
16
|
-
import {
|
|
17
|
-
CometChatMessageComposer,
|
|
18
|
-
CometChatMessageHeader,
|
|
19
|
-
CometChatMessageList,
|
|
20
|
-
} from "@cometchat/chat-uikit-react";
|
|
21
|
-
import { CometChat } from "@cometchat/chat-sdk-javascript";
|
|
22
|
-
import { CometChatSelector } from "./CometChatSelector";
|
|
23
|
-
import "./CometChatWidget.css";
|
|
24
|
-
|
|
25
|
-
export default function CometChatWidget() {
|
|
26
|
-
const [open, setOpen] = useState(false);
|
|
27
|
-
const [selectedUser, setSelectedUser] = useState<CometChat.User | undefined>();
|
|
28
|
-
const [selectedGroup, setSelectedGroup] = useState<CometChat.Group | undefined>();
|
|
29
|
-
|
|
30
|
-
// Close on Escape key
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
if (!open) return;
|
|
33
|
-
const handler = (e: KeyboardEvent) => {
|
|
34
|
-
if (e.key === "Escape") setOpen(false);
|
|
35
|
-
};
|
|
36
|
-
window.addEventListener("keydown", handler);
|
|
37
|
-
return () => window.removeEventListener("keydown", handler);
|
|
38
|
-
}, [open]);
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<>
|
|
42
|
-
<button
|
|
43
|
-
type="button"
|
|
44
|
-
className="cometchat-widget-button"
|
|
45
|
-
onClick={() => setOpen((v) => !v)}
|
|
46
|
-
aria-label={open ? "Close chat" : "Open chat"}
|
|
47
|
-
>
|
|
48
|
-
{open ? "✕" : "💬"}
|
|
49
|
-
</button>
|
|
50
|
-
|
|
51
|
-
{open && (
|
|
52
|
-
<div className="cometchat-widget-drawer" role="dialog" aria-label="Chat">
|
|
53
|
-
<div className="cometchat-widget-conversations">
|
|
54
|
-
<CometChatSelector
|
|
55
|
-
onSelectorItemClicked={(activeItem) => {
|
|
56
|
-
const item =
|
|
57
|
-
activeItem instanceof CometChat.Conversation
|
|
58
|
-
? activeItem.getConversationWith()
|
|
59
|
-
: activeItem;
|
|
60
|
-
if (item instanceof CometChat.User) {
|
|
61
|
-
setSelectedUser(item);
|
|
62
|
-
setSelectedGroup(undefined);
|
|
63
|
-
} else if (item instanceof CometChat.Group) {
|
|
64
|
-
setSelectedUser(undefined);
|
|
65
|
-
setSelectedGroup(item);
|
|
66
|
-
}
|
|
67
|
-
}}
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
{selectedUser || selectedGroup ? (
|
|
71
|
-
<div className="cometchat-widget-messages">
|
|
72
|
-
{selectedUser && <CometChatMessageHeader user={selectedUser} />}
|
|
73
|
-
{selectedGroup && <CometChatMessageHeader group={selectedGroup} />}
|
|
74
|
-
{selectedUser && <CometChatMessageList user={selectedUser} />}
|
|
75
|
-
{selectedGroup && <CometChatMessageList group={selectedGroup} />}
|
|
76
|
-
{selectedUser && <CometChatMessageComposer user={selectedUser} />}
|
|
77
|
-
{selectedGroup && <CometChatMessageComposer group={selectedGroup} />}
|
|
78
|
-
</div>
|
|
79
|
-
) : (
|
|
80
|
-
<div className="cometchat-widget-empty">
|
|
81
|
-
Select a conversation to start chatting
|
|
82
|
-
</div>
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
85
|
-
)}
|
|
86
|
-
</>
|
|
87
|
-
);
|
|
88
|
-
}
|