@amogads/ui 1.0.2 → 1.1.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/dist/server.js ADDED
@@ -0,0 +1,2713 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/server/index.ts
30
+ var server_exports = {};
31
+ __export(server_exports, {
32
+ UI_RENDER_SYSTEM_PROMPT: () => UI_RENDER_SYSTEM_PROMPT,
33
+ handleChatPost: () => handleChatPost,
34
+ handleContactsGet: () => handleContactsGet,
35
+ handleContactsPost: () => handleContactsPost,
36
+ handleConversationsGet: () => handleConversationsGet,
37
+ handleGeocodeRequest: () => handleGeocodeRequest,
38
+ handleGroupsGet: () => handleGroupsGet,
39
+ handleGroupsPost: () => handleGroupsPost,
40
+ handleMailInboxGet: () => handleMailInboxGet,
41
+ handleMailSendPost: () => handleMailSendPost,
42
+ handleMailSentGet: () => handleMailSentGet,
43
+ handleMailTestGet: () => handleMailTestGet,
44
+ handleMessagesGet: () => handleMessagesGet,
45
+ handleMessagesPost: () => handleMessagesPost,
46
+ handleNotificationsGet: () => handleNotificationsGet,
47
+ handleNotificationsPatch: () => handleNotificationsPatch,
48
+ handleNotificationsPost: () => handleNotificationsPost,
49
+ handleProfilesGet: () => handleProfilesGet,
50
+ handleSearchPost: () => handleSearchPost,
51
+ handleShortenOptions: () => handleShortenOptions,
52
+ handleShortenPost: () => handleShortenPost,
53
+ handleVouchersGet: () => handleVouchersGet,
54
+ handleVouchersPost: () => handleVouchersPost
55
+ });
56
+ module.exports = __toCommonJS(server_exports);
57
+
58
+ // src/server/chat.handler.ts
59
+ var import_ai_sdk_provider = require("@openrouter/ai-sdk-provider");
60
+ var import_ai = require("ai");
61
+ var import_server = require("next/server");
62
+ var UI_RENDER_SYSTEM_PROMPT = `
63
+ You are a UI Schema Generator. Your task is to generate a valid UI schema in JSON format based on the user's request.
64
+ You MUST output ONLY valid JSON. Do not write any explanations, do not wrap it in markdown code blocks, do not write anything else.
65
+
66
+ If the user provides an OCR text extraction payload (containing fields like invoice, date, total, business details, customer name, lines, etc.), you MUST analyze the text, extract the key values, and construct an editable Form containing corresponding inputs (e.g. Input with defaultValue, Textarea) so the user can verify, edit, and submit the extracted details.
67
+
68
+ The schema MUST follow this exact TypeScript interface:
69
+ interface UiSchema {
70
+ root: string; // The ID of the root element (usually "root")
71
+ elements: {
72
+ [elementId: string]: {
73
+ type: 'Stack' | 'Card' | 'Form' | 'Input' | 'Textarea' | 'Button' | 'Checkbox' | 'Badge' | 'Alert' | 'Separator' | 'Progress' | 'Heading' | 'Text' | 'Price' | 'FeatureList' | 'Tabs' | 'Calendar' | 'Switch' | 'RadioGroup' | 'PremiumStats';
74
+ props?: Record<string, any>;
75
+ children?: string[]; // Array of element IDs that are children of this element
76
+ }
77
+ }
78
+ }
79
+
80
+ Common Components & Props:
81
+ 1. Stack: props: { direction: 'vertical' | 'horizontal', gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl', align: 'start' | 'center' | 'end' }
82
+ 2. Form: props: { onSubmit?: string }
83
+ 3. Card: props: { title?: string, description?: string, className?: string, maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | 'full', centered?: boolean }
84
+ 4. Input: props: { label?: string, name?: string, placeholder?: string, required?: boolean, type?: string, defaultValue?: any }
85
+ 5. Textarea: props: { label?: string, name?: string, placeholder?: string, required?: boolean, defaultValue?: any }
86
+ 6. Button: props: { label: string, type?: 'button' | 'submit', variant?: 'default' | 'outline' | 'destructive' | 'ghost', className?: string }
87
+ 7. Heading: props: { level: '1' | '2' | '3' | '4' | '5' | '6', children: string }
88
+ 8. Text: props: { children: string, size?: 'sm' | 'base' | 'lg' | 'xl', className?: string }
89
+ `;
90
+ async function handleChatPost(request) {
91
+ try {
92
+ const { message, model, tool } = await request.json();
93
+ if (!message) {
94
+ return import_server.NextResponse.json({ error: "Message is required" }, { status: 400 });
95
+ }
96
+ const openRouterApiKey = process.env.OPENROUTER_API_KEY;
97
+ if (!openRouterApiKey) {
98
+ return import_server.NextResponse.json(
99
+ { error: "OpenRouter API key is not configured" },
100
+ { status: 500 }
101
+ );
102
+ }
103
+ const openrouter = (0, import_ai_sdk_provider.createOpenRouter)({
104
+ apiKey: openRouterApiKey
105
+ });
106
+ const isUiRender = tool === "ui-render";
107
+ const { text } = await (0, import_ai.generateText)({
108
+ model: openrouter.chat(model || "google/gemini-2.5-flash"),
109
+ system: isUiRender ? UI_RENDER_SYSTEM_PROMPT : void 0,
110
+ prompt: message
111
+ });
112
+ return import_server.NextResponse.json({ text });
113
+ } catch (error) {
114
+ console.error("Error in handleChatPost:", error);
115
+ return import_server.NextResponse.json(
116
+ { error: error?.message || "Failed to generate response" },
117
+ { status: 500 }
118
+ );
119
+ }
120
+ }
121
+
122
+ // src/server/geocode.handler.ts
123
+ var import_server2 = require("next/server");
124
+ async function handleGeocodeRequest(req) {
125
+ const { searchParams } = new URL(req.url);
126
+ const lat = searchParams.get("lat");
127
+ const lon = searchParams.get("lon");
128
+ if (!lat || !lon) {
129
+ return import_server2.NextResponse.json({ error: "Latitude and Longitude are required" }, { status: 400 });
130
+ }
131
+ try {
132
+ const response = await fetch(
133
+ `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}`,
134
+ {
135
+ headers: {
136
+ "User-Agent": "AmogaDS/1.0 (contact@amoga.io)",
137
+ "Accept-Language": "en"
138
+ },
139
+ next: { revalidate: 3600 }
140
+ }
141
+ );
142
+ if (!response.ok) {
143
+ throw new Error(`Nominatim returned status ${response.status}`);
144
+ }
145
+ const data = await response.json();
146
+ return import_server2.NextResponse.json(data);
147
+ } catch (error) {
148
+ console.error("Server-side geocoding failed:", error);
149
+ return import_server2.NextResponse.json({ error: error.message || "Geocoding failed" }, { status: 500 });
150
+ }
151
+ }
152
+
153
+ // src/server/shorten.handler.ts
154
+ var import_server3 = require("next/server");
155
+
156
+ // src/lib/short-url-store.ts
157
+ var import_fs = require("fs");
158
+ var import_path = __toESM(require("path"));
159
+ var import_supabase_js = require("@supabase/supabase-js");
160
+ var supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
161
+ var supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;
162
+ var supabase = supabaseUrl && supabaseAnonKey ? (0, import_supabase_js.createClient)(supabaseUrl, supabaseAnonKey) : null;
163
+ var URLS_FILE = import_path.default.join(process.cwd(), "src/features/link-builder/data/urls.json");
164
+ var ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
165
+ function getMemoryStore() {
166
+ const g = globalThis;
167
+ if (!g.__shortUrlStore) {
168
+ g.__shortUrlStore = /* @__PURE__ */ new Map();
169
+ }
170
+ return g.__shortUrlStore;
171
+ }
172
+ function generateShortId(length = 6) {
173
+ let result = "";
174
+ for (let i = 0; i < length; i++) {
175
+ result += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length));
176
+ }
177
+ return result;
178
+ }
179
+ function isKvConfigured() {
180
+ return Boolean(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN);
181
+ }
182
+ async function kvSet(key, entry, ttlSeconds) {
183
+ if (!isKvConfigured()) return false;
184
+ try {
185
+ const res = await fetch(process.env.KV_REST_API_URL, {
186
+ method: "POST",
187
+ headers: {
188
+ Authorization: `Bearer ${process.env.KV_REST_API_TOKEN}`,
189
+ "Content-Type": "application/json"
190
+ },
191
+ body: JSON.stringify(["SET", key, JSON.stringify(entry), "EX", ttlSeconds]),
192
+ cache: "no-store"
193
+ });
194
+ return res.ok;
195
+ } catch {
196
+ return false;
197
+ }
198
+ }
199
+ async function readFileStore() {
200
+ try {
201
+ const raw = await import_fs.promises.readFile(URLS_FILE, "utf-8");
202
+ return JSON.parse(raw);
203
+ } catch {
204
+ return [];
205
+ }
206
+ }
207
+ async function writeFileStore(entries) {
208
+ try {
209
+ await import_fs.promises.writeFile(URLS_FILE, JSON.stringify(entries, null, 2), "utf-8");
210
+ return true;
211
+ } catch {
212
+ return false;
213
+ }
214
+ }
215
+ async function saveShortUrl(targetUrl, expiresAtMs) {
216
+ const id = generateShortId();
217
+ const entry = {
218
+ id,
219
+ targetUrl,
220
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
221
+ expiresAt: new Date(expiresAtMs).toISOString()
222
+ };
223
+ const memory = getMemoryStore();
224
+ memory.set(id, entry);
225
+ const ttlSeconds = Math.max(60, Math.ceil((expiresAtMs - Date.now()) / 1e3));
226
+ const kvSaved = await kvSet(`short:${id}`, entry, ttlSeconds);
227
+ const existing = await readFileStore();
228
+ existing.push(entry);
229
+ const fileSaved = await writeFileStore(existing);
230
+ let supabaseSaved = false;
231
+ if (supabase) {
232
+ try {
233
+ const { error } = await supabase.from("short_urls").insert({
234
+ id,
235
+ target_url: targetUrl,
236
+ expires_at: new Date(expiresAtMs).toISOString()
237
+ });
238
+ if (error) {
239
+ console.error("Supabase saveShortUrl error:", error);
240
+ } else {
241
+ supabaseSaved = true;
242
+ console.log("Supabase saveShortUrl success:", id);
243
+ }
244
+ } catch (e) {
245
+ console.error("Supabase saveShortUrl exception:", e);
246
+ }
247
+ }
248
+ const needsFallback = !kvSaved && !fileSaved && !supabaseSaved;
249
+ const shortUrlSuffix = needsFallback ? `${id}?r=${Buffer.from(targetUrl).toString("base64url")}` : id;
250
+ return { id, entry, shortUrlSuffix };
251
+ }
252
+
253
+ // src/server/shorten.handler.ts
254
+ function getOrigin(request) {
255
+ const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host");
256
+ const proto = request.headers.get("x-forwarded-proto") ?? "https";
257
+ if (host) return `${proto}://${host}`;
258
+ if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
259
+ return request.nextUrl.origin;
260
+ }
261
+ async function handleShortenPost(request) {
262
+ try {
263
+ const body = await request.json();
264
+ const { url, durationHours = 1 } = body;
265
+ if (!url || typeof url !== "string") {
266
+ return import_server3.NextResponse.json({ error: "URL is required" }, { status: 400 });
267
+ }
268
+ const expiresAtMs = Date.now() + durationHours * 60 * 60 * 1e3;
269
+ const expiresAt = new Date(expiresAtMs).toISOString();
270
+ let urlWithExpiration = url;
271
+ if (url.includes("/l/")) {
272
+ const parts = url.split("/l/");
273
+ const domain = parts[0];
274
+ const config = parts[1];
275
+ urlWithExpiration = `${domain}/l?c=${config}&exp=${expiresAtMs}`;
276
+ } else if (url.includes("/l?")) {
277
+ const parsed = new URL(url);
278
+ parsed.searchParams.set("exp", String(expiresAtMs));
279
+ urlWithExpiration = parsed.toString();
280
+ } else {
281
+ const separator = url.includes("?") ? "&" : "?";
282
+ urlWithExpiration = `${url}${separator}exp=${expiresAtMs}`;
283
+ }
284
+ const origin = getOrigin(request);
285
+ const { shortUrlSuffix } = await saveShortUrl(urlWithExpiration, expiresAtMs);
286
+ const shortUrl = `${origin}/go/${shortUrlSuffix}`;
287
+ return import_server3.NextResponse.json(
288
+ { shortUrl, expiresAt },
289
+ {
290
+ status: 200,
291
+ headers: {
292
+ "Access-Control-Allow-Origin": "*",
293
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
294
+ "Access-Control-Allow-Headers": "Content-Type"
295
+ }
296
+ }
297
+ );
298
+ } catch (err) {
299
+ console.error("URL shortening API exception:", err);
300
+ return import_server3.NextResponse.json(
301
+ { error: err instanceof Error ? err.message : "Internal Server Error" },
302
+ { status: 500 }
303
+ );
304
+ }
305
+ }
306
+ async function handleShortenOptions() {
307
+ return new import_server3.NextResponse(null, {
308
+ status: 204,
309
+ headers: {
310
+ "Access-Control-Allow-Origin": "*",
311
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
312
+ "Access-Control-Allow-Headers": "Content-Type"
313
+ }
314
+ });
315
+ }
316
+
317
+ // src/server/vouchers.handler.ts
318
+ var import_server4 = require("next/server");
319
+
320
+ // src/lib/supabase/server.ts
321
+ var import_ssr = require("@supabase/ssr");
322
+ var import_headers = require("next/headers");
323
+ async function createClient2() {
324
+ const cookieStore = await (0, import_headers.cookies)();
325
+ return (0, import_ssr.createServerClient)(
326
+ process.env.NEXT_PUBLIC_SUPABASE_URL,
327
+ process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,
328
+ {
329
+ cookies: {
330
+ getAll() {
331
+ return cookieStore.getAll();
332
+ },
333
+ setAll(cookiesToSet) {
334
+ try {
335
+ cookiesToSet.forEach(
336
+ ({ name, value, options }) => cookieStore.set(name, value, options)
337
+ );
338
+ } catch {
339
+ }
340
+ }
341
+ }
342
+ }
343
+ );
344
+ }
345
+
346
+ // src/server/vouchers.handler.ts
347
+ var import_next_auth = require("next-auth");
348
+
349
+ // src/lib/auth.ts
350
+ var import_google = __toESM(require("next-auth/providers/google"));
351
+ var import_supabase_js2 = require("@supabase/supabase-js");
352
+ var import_crypto = __toESM(require("crypto"));
353
+ var SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
354
+ var SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || "";
355
+ function stringToUuid(str) {
356
+ if (!str) return import_crypto.default.randomUUID();
357
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
358
+ if (uuidRegex.test(str)) {
359
+ return str;
360
+ }
361
+ const hash = import_crypto.default.createHash("md5").update(str).digest("hex");
362
+ return `${hash.substring(0, 8)}-${hash.substring(8, 12)}-4${hash.substring(13, 16)}-a${hash.substring(17, 20)}-${hash.substring(20, 32)}`;
363
+ }
364
+ var authOptions = {
365
+ providers: [
366
+ (0, import_google.default)({
367
+ clientId: process.env.GOOGLE_CLIENT_ID || process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "",
368
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET || process.env.NEXT_PUBLIC_GOOGLE_SECRET_ID || "",
369
+ authorization: {
370
+ params: {
371
+ prompt: "select_account",
372
+ access_type: "offline",
373
+ response_type: "code"
374
+ }
375
+ },
376
+ checks: ["none"]
377
+ })
378
+ ],
379
+ secret: process.env.NEXTAUTH_SECRET || "secret_next_auth_shadcn_admin_key_2026_super_secure",
380
+ session: {
381
+ strategy: "jwt",
382
+ maxAge: 30 * 24 * 60 * 60
383
+ // 30 days
384
+ },
385
+ callbacks: {
386
+ async redirect({ url, baseUrl }) {
387
+ try {
388
+ const { cookies: cookies2 } = await import("next/headers");
389
+ const cookieStore = await cookies2();
390
+ const isMobileAuth = cookieStore.get("mobile_auth")?.value === "true" || url.includes("is_mobile=true");
391
+ if (isMobileAuth) {
392
+ console.log("\u{1F4F1} [NextAuth Redirect Callback] Mobile auth detected. Redirecting to /auth/callback?is_mobile=true");
393
+ return `${baseUrl}/auth/callback?is_mobile=true&next=/`;
394
+ }
395
+ } catch (err) {
396
+ console.error("\u274C [NextAuth Redirect Callback] Error inspecting cookies:", err);
397
+ }
398
+ if (url.includes("/auth/callback")) return url;
399
+ if (url.startsWith("/")) return `${baseUrl}${url}`;
400
+ else if (new URL(url).origin === baseUrl) return url;
401
+ return baseUrl;
402
+ },
403
+ async signIn({ user }) {
404
+ if (!user.email) return false;
405
+ try {
406
+ if (SUPABASE_URL && SUPABASE_KEY) {
407
+ const supabase2 = (0, import_supabase_js2.createClient)(SUPABASE_URL, SUPABASE_KEY);
408
+ const fallbackUuid = stringToUuid(user.id || user.email);
409
+ const { data: existingProfile } = await supabase2.from("profiles").select("id, auth_user_id").eq("email", user.email.toLowerCase()).maybeSingle();
410
+ const profileId = existingProfile?.id || fallbackUuid;
411
+ const profileData = {
412
+ id: profileId,
413
+ name: user.name || user.email.split("@")[0],
414
+ email: user.email.toLowerCase(),
415
+ avatar: user.image || null,
416
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
417
+ };
418
+ profileData.auth_user_id = existingProfile?.auth_user_id || profileId;
419
+ if (existingProfile) {
420
+ await supabase2.from("profiles").update(profileData).eq("id", existingProfile.id);
421
+ } else {
422
+ await supabase2.from("profiles").insert(profileData);
423
+ }
424
+ user.id = profileId;
425
+ }
426
+ } catch (err) {
427
+ console.error("[NextAuth] Error syncing user to profiles table:", err);
428
+ }
429
+ return true;
430
+ },
431
+ async jwt({ token, user }) {
432
+ if (user) {
433
+ token.id = stringToUuid(user.id || user.email);
434
+ token.email = user.email;
435
+ token.name = user.name;
436
+ token.picture = user.image;
437
+ } else if (token.sub && !stringToUuid(token.sub)) {
438
+ token.id = stringToUuid(token.sub);
439
+ }
440
+ return token;
441
+ },
442
+ async session({ session, token }) {
443
+ if (session.user) {
444
+ const canonicalId = stringToUuid(token.id || token.sub || session.user.email);
445
+ session.user.id = canonicalId;
446
+ session.user.picture = token.picture || token.image;
447
+ }
448
+ return session;
449
+ }
450
+ },
451
+ pages: {
452
+ signIn: "/sign-in",
453
+ error: "/sign-in"
454
+ }
455
+ };
456
+
457
+ // src/server/vouchers.handler.ts
458
+ async function handleVouchersGet() {
459
+ try {
460
+ const session = await (0, import_next_auth.getServerSession)(authOptions);
461
+ const supabase2 = await createClient2();
462
+ let userId = null;
463
+ let userEmail = null;
464
+ if (session?.user) {
465
+ const user = session.user;
466
+ userEmail = user.email ? user.email.toLowerCase() : null;
467
+ userId = stringToUuid(user.id || user.email);
468
+ } else {
469
+ const { data: { user } } = await supabase2.auth.getUser();
470
+ if (user?.id) {
471
+ userId = user.id;
472
+ userEmail = user.email ? user.email.toLowerCase() : null;
473
+ }
474
+ }
475
+ let voucherRows = [];
476
+ try {
477
+ if (userId) {
478
+ const { data: vData } = await supabase2.from("vouchers").select("*").eq("user_id", userId).order("created_at", { ascending: false }).limit(100);
479
+ if (vData && vData.length > 0) {
480
+ voucherRows = vData;
481
+ } else if (userEmail) {
482
+ const { data: profileRow } = await supabase2.from("profiles").select("id").eq("email", userEmail).maybeSingle();
483
+ if (profileRow?.id) {
484
+ const { data: vByProfile } = await supabase2.from("vouchers").select("*").eq("user_id", profileRow.id).order("created_at", { ascending: false }).limit(100);
485
+ if (vByProfile && vByProfile.length > 0) {
486
+ voucherRows = vByProfile;
487
+ }
488
+ }
489
+ }
490
+ }
491
+ if (voucherRows.length === 0) {
492
+ const { data: fallbackVData } = await supabase2.from("vouchers").select("*").order("created_at", { ascending: false }).limit(100);
493
+ if (fallbackVData) voucherRows = fallbackVData;
494
+ }
495
+ } catch (e) {
496
+ console.warn("[GET /api/vouchers] Vouchers table fetch warning:", e);
497
+ }
498
+ let chatFileRows = [];
499
+ try {
500
+ if (userId) {
501
+ const [rOwner, rSender] = await Promise.all([
502
+ supabase2.from("chat_messages").select("*").eq("owner_user_id", userId).not("file_url", "is", null).order("created_at", { ascending: false }).limit(100),
503
+ supabase2.from("chat_messages").select("*").eq("sender_user_id", userId).not("file_url", "is", null).order("created_at", { ascending: false }).limit(100)
504
+ ]);
505
+ const userMsgs = [...rOwner.data ?? [], ...rSender.data ?? []];
506
+ if (userMsgs.length > 0) {
507
+ chatFileRows = userMsgs.map((msg) => ({
508
+ id: `chat-file-${msg.id}`,
509
+ voucher_no: msg.id ? String(msg.id).slice(0, 8) : "file",
510
+ file_name: msg.file_name || "Attached File",
511
+ original_file_url: msg.file_url ?? void 0,
512
+ edited_file_url: msg.file_url ?? void 0,
513
+ vendor_name: msg.sender_name || "Uploaded Document",
514
+ customer_name: userEmail ? userEmail.split("@")[0] : "User",
515
+ user_name: userEmail ? userEmail.split("@")[0] : "User",
516
+ created_at: msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
517
+ status: msg.processing_status || "Active",
518
+ edited_json: msg.file_content_json || null
519
+ }));
520
+ }
521
+ }
522
+ if (chatFileRows.length === 0) {
523
+ const { data: fallbackMsgs } = await supabase2.from("chat_messages").select("*").not("file_url", "is", null).order("created_at", { ascending: false }).limit(100);
524
+ if (fallbackMsgs && fallbackMsgs.length > 0) {
525
+ chatFileRows = fallbackMsgs.map((msg) => ({
526
+ id: `chat-file-${msg.id}`,
527
+ voucher_no: msg.id ? String(msg.id).slice(0, 8) : "file",
528
+ file_name: msg.file_name || "Attached File",
529
+ original_file_url: msg.file_url ?? void 0,
530
+ edited_file_url: msg.file_url ?? void 0,
531
+ vendor_name: msg.sender_name || "Uploaded Document",
532
+ customer_name: userEmail ? userEmail.split("@")[0] : "User",
533
+ user_name: userEmail ? userEmail.split("@")[0] : "User",
534
+ created_at: msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
535
+ status: msg.processing_status || "Active",
536
+ edited_json: msg.file_content_json || null
537
+ }));
538
+ }
539
+ }
540
+ } catch (e) {
541
+ console.warn("[GET /api/vouchers] Chat files fetch warning:", e);
542
+ }
543
+ const allFiles = [...voucherRows, ...chatFileRows];
544
+ allFiles.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
545
+ const seen = /* @__PURE__ */ new Set();
546
+ const uniqueFiles = [];
547
+ for (const f of allFiles) {
548
+ const key = `${f.id}|${f.file_name ?? ""}|${f.original_file_url ?? ""}`;
549
+ if (!seen.has(key)) {
550
+ seen.add(key);
551
+ uniqueFiles.push(f);
552
+ }
553
+ }
554
+ return import_server4.NextResponse.json({ success: true, data: uniqueFiles });
555
+ } catch (err) {
556
+ console.error("[GET /api/vouchers] Internal error:", err);
557
+ return import_server4.NextResponse.json({ error: err.message || "Internal error" }, { status: 500 });
558
+ }
559
+ }
560
+ async function handleVouchersPost(request) {
561
+ try {
562
+ const session = await (0, import_next_auth.getServerSession)(authOptions);
563
+ const supabase2 = await createClient2();
564
+ let userId = null;
565
+ let userEmail = null;
566
+ if (session?.user) {
567
+ const user = session.user;
568
+ userEmail = user.email ? user.email.toLowerCase() : null;
569
+ userId = stringToUuid(user.id || user.email);
570
+ } else {
571
+ const { data: { user } } = await supabase2.auth.getUser();
572
+ if (user?.id) {
573
+ userId = user.id;
574
+ userEmail = user.email?.toLowerCase() ?? null;
575
+ }
576
+ }
577
+ if (!userId) {
578
+ return import_server4.NextResponse.json({ error: "Unauthorized" }, { status: 401 });
579
+ }
580
+ if (userEmail) {
581
+ const { data: profileRow } = await supabase2.from("profiles").select("id").eq("email", userEmail).maybeSingle();
582
+ if (profileRow?.id) userId = profileRow.id;
583
+ }
584
+ const body = await request.json();
585
+ const {
586
+ voucher_no,
587
+ file_name,
588
+ original_file_url,
589
+ edited_file_url,
590
+ edited_json,
591
+ vendor_name,
592
+ customer_name,
593
+ invoice_date,
594
+ total,
595
+ currency
596
+ } = body;
597
+ if (!voucher_no || !file_name) {
598
+ return import_server4.NextResponse.json({ error: "voucher_no and file_name are required" }, { status: 400 });
599
+ }
600
+ const { data: row, error } = await supabase2.from("vouchers").insert({
601
+ user_id: userId,
602
+ voucher_no,
603
+ file_name,
604
+ original_file_url: original_file_url || null,
605
+ edited_file_url: edited_file_url || null,
606
+ edited_json: edited_json || null,
607
+ vendor_name: vendor_name || null,
608
+ customer_name: customer_name || null,
609
+ invoice_date: invoice_date || null,
610
+ total: total || null,
611
+ currency: currency || "USD",
612
+ status: "Active"
613
+ }).select("*").single();
614
+ if (error) {
615
+ return import_server4.NextResponse.json({ error: error.message }, { status: 500 });
616
+ }
617
+ return import_server4.NextResponse.json({ success: true, data: row }, { status: 201 });
618
+ } catch (err) {
619
+ console.error("[POST /api/vouchers] Internal error:", err);
620
+ return import_server4.NextResponse.json({ error: err.message || "Internal error" }, { status: 500 });
621
+ }
622
+ }
623
+
624
+ // src/server/search.handler.ts
625
+ var import_server6 = require("next/server");
626
+
627
+ // src/services/ai-search.service.ts
628
+ var import_axios = __toESM(require("axios"));
629
+ var AiSearchService = class {
630
+ /**
631
+ * Executes web search through Tavily API.
632
+ */
633
+ static async searchWeb(query, apiKey) {
634
+ if (!apiKey) {
635
+ throw new Error("Tavily API key is not configured.");
636
+ }
637
+ const res = await import_axios.default.post(
638
+ "https://api.tavily.com/search",
639
+ {
640
+ api_key: apiKey,
641
+ query,
642
+ search_depth: "advanced",
643
+ max_results: 10,
644
+ include_images: true
645
+ },
646
+ { timeout: 15e3 }
647
+ );
648
+ return {
649
+ results: res.data?.results || [],
650
+ images: res.data?.images || []
651
+ };
652
+ }
653
+ /**
654
+ * Generates answer analysis using Gemini API.
655
+ */
656
+ static async generateAnswer(prompt, apiKey) {
657
+ if (!apiKey) {
658
+ throw new Error("Gemini API key is not configured.");
659
+ }
660
+ const res = await import_axios.default.post(
661
+ `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`,
662
+ {
663
+ contents: [
664
+ {
665
+ parts: [{ text: prompt }]
666
+ }
667
+ ]
668
+ },
669
+ { timeout: 3e4 }
670
+ );
671
+ return res.data?.candidates?.[0]?.content?.parts?.[0]?.text || "";
672
+ }
673
+ };
674
+
675
+ // src/server/search.handler.ts
676
+ async function handleSearchPost(request) {
677
+ try {
678
+ const body = await request.json();
679
+ const { query, toolPrompt, toolId } = body;
680
+ if (!query || typeof query !== "string") {
681
+ return import_server6.NextResponse.json({ error: "Query is required" }, { status: 400 });
682
+ }
683
+ const tavilyKey = process.env.TAVILY_API_KEY || process.env.NEXT_PUBLIC_TAVILY_API_KEY || "";
684
+ const geminiKey = process.env.GEMINI_API_KEY || process.env.NEXT_PUBLIC_GEMINI_API_KEY || "";
685
+ if (!tavilyKey) {
686
+ return import_server6.NextResponse.json({ error: "Tavily API key is not configured" }, { status: 500 });
687
+ }
688
+ const { results, images } = await AiSearchService.searchWeb(query, tavilyKey);
689
+ if (results.length === 0) {
690
+ return import_server6.NextResponse.json({
691
+ answer: "No search results found for your query. Please try a different search term.",
692
+ sources: [],
693
+ images: []
694
+ });
695
+ }
696
+ const context = results.map((item) => `Title: ${item.title}
697
+ Content: ${item.content}
698
+ URL: ${item.url}`).join("\n\n");
699
+ const systemPrompt = toolPrompt || "You are an AI Search Assistant. Give comprehensive answers using the search results provided. Use headings and bullet points when useful, and always cite your sources.";
700
+ const prompt = `
701
+ ${systemPrompt}
702
+
703
+ Question:
704
+ ${query}
705
+
706
+ Search Results:
707
+ ${context}
708
+
709
+ Instructions:
710
+ - Give a comprehensive answer based on search results.
711
+ - Use headings and bullet points for readability.
712
+ - Cite sources accurately.
713
+ `;
714
+ let answer = "";
715
+ if (geminiKey) {
716
+ answer = await AiSearchService.generateAnswer(prompt, geminiKey);
717
+ }
718
+ return import_server6.NextResponse.json({
719
+ answer,
720
+ sources: results,
721
+ images
722
+ });
723
+ } catch (err) {
724
+ console.error("Error in handleSearchPost:", err);
725
+ return import_server6.NextResponse.json(
726
+ { error: err.message || "Failed to process AI search request" },
727
+ { status: 500 }
728
+ );
729
+ }
730
+ }
731
+
732
+ // src/server/messages.handler.ts
733
+ var import_server7 = require("next/server");
734
+
735
+ // src/lib/supabase/client.ts
736
+ var import_ssr2 = require("@supabase/ssr");
737
+ var clientSingleton = null;
738
+ function createClient3() {
739
+ if (typeof window === "undefined") {
740
+ return (0, import_ssr2.createBrowserClient)(
741
+ process.env.NEXT_PUBLIC_SUPABASE_URL,
742
+ process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
743
+ );
744
+ }
745
+ if (!clientSingleton) {
746
+ clientSingleton = (0, import_ssr2.createBrowserClient)(
747
+ process.env.NEXT_PUBLIC_SUPABASE_URL,
748
+ process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
749
+ );
750
+ }
751
+ return clientSingleton;
752
+ }
753
+
754
+ // src/stores/auth-store.ts
755
+ var import_zustand = require("zustand");
756
+
757
+ // src/lib/cookies.ts
758
+ var DEFAULT_MAX_AGE = 60 * 60 * 24 * 7;
759
+ function getCookie(name) {
760
+ if (typeof document === "undefined") return void 0;
761
+ const value = `; ${document.cookie}`;
762
+ const parts = value.split(`; ${name}=`);
763
+ if (parts.length === 2) {
764
+ const cookieValue = parts.pop()?.split(";").shift();
765
+ return cookieValue;
766
+ }
767
+ return void 0;
768
+ }
769
+ function setCookie(name, value, maxAge = DEFAULT_MAX_AGE) {
770
+ if (typeof document === "undefined") return;
771
+ document.cookie = `${name}=${value}; path=/; max-age=${maxAge}`;
772
+ }
773
+ function removeCookie(name) {
774
+ if (typeof document === "undefined") return;
775
+ document.cookie = `${name}=; path=/; max-age=0`;
776
+ }
777
+
778
+ // src/stores/auth-store.ts
779
+ var ACCESS_TOKEN = "thisisjustarandomstring";
780
+ var USER_DATA = "auth_user_data";
781
+ var useAuthStore = (0, import_zustand.create)()((set) => {
782
+ const cookieState = getCookie(ACCESS_TOKEN);
783
+ let initToken = "";
784
+ if (cookieState) {
785
+ try {
786
+ initToken = JSON.parse(cookieState);
787
+ } catch {
788
+ removeCookie(ACCESS_TOKEN);
789
+ }
790
+ }
791
+ const userCookie = getCookie(USER_DATA);
792
+ let initUser = null;
793
+ if (userCookie) {
794
+ try {
795
+ const parsed = JSON.parse(decodeURIComponent(userCookie));
796
+ if (parsed.exp && parsed.exp > Date.now()) {
797
+ initUser = parsed;
798
+ } else {
799
+ removeCookie(ACCESS_TOKEN);
800
+ removeCookie(USER_DATA);
801
+ }
802
+ } catch {
803
+ initUser = null;
804
+ removeCookie(USER_DATA);
805
+ }
806
+ }
807
+ return {
808
+ auth: {
809
+ user: initUser,
810
+ setUser: (user) => set((state) => {
811
+ if (user) {
812
+ setCookie(USER_DATA, encodeURIComponent(JSON.stringify(user)));
813
+ } else {
814
+ removeCookie(USER_DATA);
815
+ }
816
+ return { ...state, auth: { ...state.auth, user } };
817
+ }),
818
+ accessToken: initUser ? initToken : "",
819
+ setAccessToken: (accessToken) => set((state) => {
820
+ setCookie(ACCESS_TOKEN, JSON.stringify(accessToken));
821
+ return { ...state, auth: { ...state.auth, accessToken } };
822
+ }),
823
+ resetAccessToken: () => set((state) => {
824
+ removeCookie(ACCESS_TOKEN);
825
+ return { ...state, auth: { ...state.auth, accessToken: "" } };
826
+ }),
827
+ reset: () => set((state) => {
828
+ removeCookie(ACCESS_TOKEN);
829
+ removeCookie(USER_DATA);
830
+ return {
831
+ ...state,
832
+ auth: { ...state.auth, user: null, accessToken: "" }
833
+ };
834
+ })
835
+ }
836
+ };
837
+ });
838
+
839
+ // src/features/chattemplate/shared/api/auth.ts
840
+ async function getAccessToken() {
841
+ const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || "";
842
+ try {
843
+ const storeToken = useAuthStore.getState().auth.accessToken;
844
+ if (storeToken && typeof storeToken === "string" && storeToken.split(".").length === 3) {
845
+ return storeToken;
846
+ }
847
+ const supabase2 = createClient3();
848
+ const { data: { session } } = await supabase2.auth.getSession();
849
+ if (session?.access_token && session.access_token.split(".").length === 3) {
850
+ return session.access_token;
851
+ }
852
+ return supabaseKey;
853
+ } catch (error) {
854
+ return supabaseKey;
855
+ }
856
+ }
857
+
858
+ // src/features/chattemplate/shared/api/headers.ts
859
+ async function getHeaders(customHeaders = {}) {
860
+ const token = await getAccessToken();
861
+ const apiKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || "";
862
+ const is3PartJwt = typeof token === "string" && token.split(".").length === 3;
863
+ const validBearerToken = is3PartJwt ? token : apiKey;
864
+ return {
865
+ "apikey": apiKey,
866
+ "Authorization": `Bearer ${validBearerToken}`,
867
+ "Content-Type": "application/json",
868
+ ...customHeaders
869
+ };
870
+ }
871
+
872
+ // src/features/chattemplate/shared/api/errorHandler.ts
873
+ var ApiError = class extends Error {
874
+ constructor(message, code, details, hint) {
875
+ super(message);
876
+ this.name = "ApiError";
877
+ this.code = code;
878
+ this.details = details;
879
+ this.hint = hint;
880
+ }
881
+ };
882
+ async function handleError(response) {
883
+ let errorData;
884
+ try {
885
+ errorData = await response.json();
886
+ } catch {
887
+ throw new ApiError(response.statusText || "An unknown network error occurred");
888
+ }
889
+ const message = errorData?.message || errorData?.error_description || "API request failed";
890
+ const code = errorData?.code;
891
+ const details = errorData?.details;
892
+ const hint = errorData?.hint;
893
+ throw new ApiError(message, code, details, hint);
894
+ }
895
+
896
+ // src/features/chattemplate/shared/api/apiClient.ts
897
+ var SUPABASE_URL2 = process.env.NEXT_PUBLIC_SUPABASE_URL;
898
+ if (!SUPABASE_URL2) {
899
+ throw new Error("Missing environment variable: NEXT_PUBLIC_SUPABASE_URL");
900
+ }
901
+ var BASE_URL = SUPABASE_URL2.endsWith("/") ? SUPABASE_URL2.slice(0, -1) : SUPABASE_URL2;
902
+ async function parseResponse(response) {
903
+ const text = await response.text();
904
+ return text ? JSON.parse(text) : {};
905
+ }
906
+ var apiClient = {
907
+ async get(path3, options) {
908
+ const headers = await getHeaders(options?.headers);
909
+ const response = await fetch(`${BASE_URL}${path3}`, {
910
+ method: "GET",
911
+ headers
912
+ });
913
+ if (!response.ok) {
914
+ await handleError(response);
915
+ }
916
+ return parseResponse(response);
917
+ },
918
+ async post(path3, body, options) {
919
+ const defaultHeaders = {
920
+ "Prefer": "return=representation"
921
+ };
922
+ const headers = await getHeaders({ ...defaultHeaders, ...options?.headers });
923
+ const response = await fetch(`${BASE_URL}${path3}`, {
924
+ method: "POST",
925
+ headers,
926
+ body: JSON.stringify(body)
927
+ });
928
+ if (!response.ok) {
929
+ await handleError(response);
930
+ }
931
+ return parseResponse(response);
932
+ },
933
+ async patch(path3, body, options) {
934
+ const defaultHeaders = {
935
+ "Prefer": "return=representation"
936
+ };
937
+ const headers = await getHeaders({ ...defaultHeaders, ...options?.headers });
938
+ const response = await fetch(`${BASE_URL}${path3}`, {
939
+ method: "PATCH",
940
+ headers,
941
+ body: JSON.stringify(body)
942
+ });
943
+ if (!response.ok) {
944
+ await handleError(response);
945
+ }
946
+ return parseResponse(response);
947
+ },
948
+ async delete(path3, options) {
949
+ const defaultHeaders = {
950
+ "Prefer": "return=representation"
951
+ };
952
+ const headers = await getHeaders({ ...defaultHeaders, ...options?.headers });
953
+ const response = await fetch(`${BASE_URL}${path3}`, {
954
+ method: "DELETE",
955
+ headers
956
+ });
957
+ if (!response.ok) {
958
+ await handleError(response);
959
+ }
960
+ return parseResponse(response);
961
+ }
962
+ };
963
+
964
+ // src/features/chattemplate/shared/api/queryBuilder.ts
965
+ var QueryBuilder = class {
966
+ constructor() {
967
+ this.params = new URLSearchParams();
968
+ }
969
+ /**
970
+ * Embeds resource relationships or filters output columns.
971
+ */
972
+ select(fields) {
973
+ const cleaned = fields.replace(/\s+/g, "");
974
+ this.params.set("select", cleaned);
975
+ return this;
976
+ }
977
+ /**
978
+ * Equality filter.
979
+ */
980
+ eq(column, value) {
981
+ this.params.append(column, `eq.${value}`);
982
+ return this;
983
+ }
984
+ lt(column, value) {
985
+ this.params.append(column, `lt.${value}`);
986
+ return this;
987
+ }
988
+ /**
989
+ * Case-insensitive pattern matching filter.
990
+ */
991
+ ilike(column, value) {
992
+ this.params.append(column, `ilike.${value}`);
993
+ return this;
994
+ }
995
+ /**
996
+ * IN filter matching array of values.
997
+ */
998
+ in(column, values) {
999
+ const formatted = values.map((v) => `${v}`).join(",");
1000
+ this.params.append(column, `in.(${formatted})`);
1001
+ return this;
1002
+ }
1003
+ /**
1004
+ * Logical OR filter matching multiple column criteria.
1005
+ * Example string: "email.ilike.test@gmail.com,contact_user_id.eq.some-uuid"
1006
+ */
1007
+ or(filterString) {
1008
+ this.params.set("or", `(${filterString})`);
1009
+ return this;
1010
+ }
1011
+ /**
1012
+ * Sorting filter.
1013
+ */
1014
+ order(column, options) {
1015
+ const dir = options?.ascending !== false ? "asc" : "desc";
1016
+ this.params.set("order", `${column}.${dir}`);
1017
+ return this;
1018
+ }
1019
+ /**
1020
+ * Limit result size.
1021
+ */
1022
+ limit(n) {
1023
+ this.params.set("limit", n.toString());
1024
+ return this;
1025
+ }
1026
+ /**
1027
+ * Offset result index.
1028
+ */
1029
+ offset(n) {
1030
+ this.params.set("offset", n.toString());
1031
+ return this;
1032
+ }
1033
+ /**
1034
+ * Generates the final query parameter string (e.g. "?owner_id=eq.123").
1035
+ */
1036
+ toString() {
1037
+ const q = this.params.toString();
1038
+ return q ? `?${q}` : "";
1039
+ }
1040
+ };
1041
+ function createQuery() {
1042
+ return new QueryBuilder();
1043
+ }
1044
+
1045
+ // src/features/chattemplate/chat/api/messages.api.ts
1046
+ async function getConversationMessages(conversationId, userId, options) {
1047
+ try {
1048
+ const query = createQuery().select(
1049
+ `
1050
+ *,
1051
+ sender:profiles!sender_user_id (
1052
+ id,
1053
+ name,
1054
+ email,
1055
+ avatar
1056
+ )
1057
+ `
1058
+ ).eq("conversation_id", conversationId).eq("owner_user_id", userId).or("deleted.eq.false,deleted_by.not.is.null");
1059
+ if (options?.before) query.lt("created_at", options.before);
1060
+ query.order("created_at", { ascending: !options?.limit });
1061
+ if (options?.limit) query.limit(options.limit);
1062
+ const response = await apiClient.get(
1063
+ `/rest/v1/chat_messages${query.toString()}`
1064
+ );
1065
+ const data = options?.limit ? [...response].reverse() : response;
1066
+ if (!data) return [];
1067
+ const messages = data.map((d) => ({
1068
+ id: d.id,
1069
+ conversation_id: d.conversation_id,
1070
+ owner_user_id: d.owner_user_id,
1071
+ sender_user_id: d.sender_user_id,
1072
+ message: d.message,
1073
+ message_type: d.message_type,
1074
+ direction: d.direction,
1075
+ sent: d.sent,
1076
+ received: d.received,
1077
+ created_at: d.created_at,
1078
+ message_status: d.message_status || void 0,
1079
+ client_message_id: d.client_message_id || void 0,
1080
+ queued_at: d.queued_at || void 0,
1081
+ delivered_at: d.delivered_at || void 0,
1082
+ read_at: d.read_at || void 0,
1083
+ retry_count: d.retry_count ? Number(d.retry_count) : void 0,
1084
+ file_url: d.file_url || void 0,
1085
+ file_name: d.file_name || void 0,
1086
+ file_size: d.file_size ? Number(d.file_size) : void 0,
1087
+ mime_type: d.mime_type || void 0,
1088
+ duration: d.duration ? Number(d.duration) : void 0,
1089
+ thumbnail: d.thumbnail || void 0,
1090
+ file_content_text: d.file_content_text || void 0,
1091
+ file_content_json: d.file_content_json || void 0,
1092
+ processing_status: d.processing_status || void 0,
1093
+ thumb: !!d.thumb,
1094
+ favorite: !!d.favorite,
1095
+ flag: !!d.flag,
1096
+ star: !!d.star,
1097
+ pin: !!d.pin,
1098
+ archive: !!d.archive,
1099
+ deleted: !!d.deleted,
1100
+ action_this: !!d.action_this,
1101
+ reply: !!d.reply,
1102
+ forward: !!d.forward,
1103
+ deleted_at: d.deleted_at || void 0,
1104
+ deleted_by: d.deleted_by || void 0,
1105
+ replyemoji: d.replyemoji || void 0,
1106
+ replyto_message_id: d.replyto_message_id || void 0,
1107
+ replyto_user_id: d.replyto_user_id || void 0,
1108
+ parent_message_id: d.parent_message_id || void 0,
1109
+ forwardemoji: d.forwardemoji || void 0,
1110
+ forwardto_message_id: d.forwardto_message_id || void 0,
1111
+ forwardto_user_id: d.forwardto_user_id || void 0,
1112
+ sender_message_id: d.sender_message_id || void 0,
1113
+ sender: d.sender ? {
1114
+ id: d.sender.id,
1115
+ name: d.sender.name,
1116
+ email: d.sender.email,
1117
+ avatar_url: d.sender.avatar || void 0
1118
+ } : void 0,
1119
+ location_data: d.location_data || void 0,
1120
+ location_type: d.location_type || void 0
1121
+ }));
1122
+ for (const msg of messages) {
1123
+ if (msg.reply && msg.replyto_message_id) {
1124
+ const localReply = messages.find(
1125
+ (candidate) => candidate.id === msg.replyto_message_id || candidate.sender_message_id === msg.replyto_message_id
1126
+ );
1127
+ if (localReply) {
1128
+ msg.replyto_message = localReply;
1129
+ msg.replyMetadata = {
1130
+ replyemoji: msg.replyemoji || null,
1131
+ replyto_message_id: msg.replyto_message_id,
1132
+ replyto_user_id: msg.replyto_user_id || null,
1133
+ parent_message_id: msg.parent_message_id || null,
1134
+ replyMessageText: localReply.deleted ? "Original message unavailable" : localReply.message_type === "text" ? localReply.message || "" : `Attachment: ${localReply.file_name || "File"}`,
1135
+ replySenderName: localReply.sender?.name || "User"
1136
+ };
1137
+ continue;
1138
+ }
1139
+ const replyQuery = createQuery().select(
1140
+ `
1141
+ id,
1142
+ sender_user_id,
1143
+ created_at,
1144
+ message,
1145
+ message_type,
1146
+ file_name,
1147
+ deleted,
1148
+ sender:profiles!sender_user_id(name)
1149
+ `
1150
+ ).eq("id", msg.replyto_message_id).limit(1);
1151
+ try {
1152
+ const replyMsgs = await apiClient.get(
1153
+ `/rest/v1/chat_messages${replyQuery.toString()}`
1154
+ );
1155
+ const replyMsg = replyMsgs[0] || null;
1156
+ if (replyMsg) {
1157
+ msg.replyto_message = {
1158
+ id: replyMsg.id,
1159
+ conversation_id: msg.conversation_id,
1160
+ owner_user_id: msg.owner_user_id,
1161
+ sender_user_id: replyMsg.sender_user_id,
1162
+ message: replyMsg.deleted ? "Original message unavailable" : replyMsg.message,
1163
+ message_type: replyMsg.message_type || "text",
1164
+ direction: "Received",
1165
+ sent: true,
1166
+ received: true,
1167
+ created_at: replyMsg.created_at || msg.created_at,
1168
+ file_name: replyMsg.file_name || void 0,
1169
+ thumb: false,
1170
+ favorite: false,
1171
+ flag: false,
1172
+ star: false,
1173
+ pin: false,
1174
+ archive: false,
1175
+ deleted: !!replyMsg.deleted,
1176
+ action_this: false,
1177
+ reply: false,
1178
+ forward: false,
1179
+ sender: replyMsg.sender ? {
1180
+ id: replyMsg.sender_user_id,
1181
+ name: replyMsg.sender.name || "User",
1182
+ email: ""
1183
+ } : void 0
1184
+ };
1185
+ msg.replyMetadata = {
1186
+ replyemoji: msg.replyemoji || null,
1187
+ replyto_message_id: msg.replyto_message_id,
1188
+ replyto_user_id: msg.replyto_user_id || null,
1189
+ parent_message_id: msg.parent_message_id || null,
1190
+ replyMessageText: replyMsg.deleted ? "Original message unavailable" : replyMsg.message_type === "text" ? replyMsg.message : `Attachment: ${replyMsg.file_name || "File"}`,
1191
+ replySenderName: replyMsg.sender?.name || "User"
1192
+ };
1193
+ } else {
1194
+ msg.replyMetadata = {
1195
+ replyemoji: msg.replyemoji || null,
1196
+ replyto_message_id: msg.replyto_message_id,
1197
+ replyto_user_id: msg.replyto_user_id || null,
1198
+ parent_message_id: msg.parent_message_id || null,
1199
+ replyMessageText: "Original message unavailable",
1200
+ replySenderName: "User"
1201
+ };
1202
+ }
1203
+ } catch (e) {
1204
+ console.warn(
1205
+ "[Messages API] Failed to fetch reply message details:",
1206
+ e
1207
+ );
1208
+ }
1209
+ }
1210
+ }
1211
+ return messages;
1212
+ } catch (e) {
1213
+ console.error("[Messages API] Failed to get conversation messages:", e);
1214
+ return [];
1215
+ }
1216
+ }
1217
+ async function createMessage(msg) {
1218
+ try {
1219
+ const memberQuery = createQuery().select("user_id").eq("conversation_id", msg.conversationId);
1220
+ const members = await apiClient.get(
1221
+ `/rest/v1/conversation_members${memberQuery.toString()}`
1222
+ );
1223
+ if (!members || members.length === 0) {
1224
+ throw new Error("No members found in conversation");
1225
+ }
1226
+ const senderMsgId = msg.id || crypto.randomUUID();
1227
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1228
+ let resolvedReplyToId = null;
1229
+ if (msg.replyMetadata?.replyto_message_id) {
1230
+ const parentMsgQuery = createQuery().select("id, sender_message_id").eq("id", msg.replyMetadata.replyto_message_id).limit(1);
1231
+ const replyMsgs = await apiClient.get(
1232
+ `/rest/v1/chat_messages${parentMsgQuery.toString()}`
1233
+ );
1234
+ const replyMsg = replyMsgs[0] || null;
1235
+ resolvedReplyToId = replyMsg ? replyMsg.sender_message_id || replyMsg.id : msg.replyMetadata.replyto_message_id;
1236
+ }
1237
+ const records = [];
1238
+ for (const member of members) {
1239
+ const isSender = member.user_id === msg.senderId;
1240
+ const msgId = isSender ? senderMsgId : crypto.randomUUID();
1241
+ let finalMessage = msg.message;
1242
+ if (msg.messageType === "system" && msg.systemMetadata) {
1243
+ const { type: sysType, groupName, creatorName } = msg.systemMetadata;
1244
+ if (sysType === "group_created") {
1245
+ finalMessage = isSender ? `You created group "${groupName}"` : `${creatorName} created group "${groupName}"`;
1246
+ } else if (sysType === "members_added") {
1247
+ if (isSender) {
1248
+ continue;
1249
+ }
1250
+ finalMessage = `${creatorName} added you`;
1251
+ }
1252
+ }
1253
+ const hasPreparsedData = !!(msg.fileContentText || msg.fileContentJson);
1254
+ records.push({
1255
+ id: msgId,
1256
+ conversation_id: msg.conversationId,
1257
+ owner_user_id: member.user_id,
1258
+ sender_user_id: msg.senderId,
1259
+ message: finalMessage,
1260
+ message_type: msg.messageType,
1261
+ direction: isSender ? "Sent" : "Received",
1262
+ sent: true,
1263
+ received: isSender,
1264
+ created_at: now,
1265
+ file_url: msg.fileUrl || null,
1266
+ file_name: msg.fileName || null,
1267
+ file_size: msg.fileSize || null,
1268
+ mime_type: msg.mimeType || null,
1269
+ duration: msg.duration || null,
1270
+ thumbnail: msg.thumbnail || null,
1271
+ thumb: false,
1272
+ favorite: false,
1273
+ flag: false,
1274
+ star: false,
1275
+ pin: false,
1276
+ archive: false,
1277
+ deleted: false,
1278
+ action_this: false,
1279
+ reply: !!msg.replyMetadata,
1280
+ forward: false,
1281
+ replyemoji: msg.replyMetadata?.replyemoji || null,
1282
+ replyto_message_id: resolvedReplyToId,
1283
+ replyto_user_id: msg.replyMetadata?.replyto_user_id || null,
1284
+ parent_message_id: msg.replyMetadata?.parent_message_id || null,
1285
+ sender_message_id: isSender ? null : senderMsgId,
1286
+ client_message_id: msg.clientMessageId || null,
1287
+ message_status: "sent",
1288
+ location_data: msg.locationData || null,
1289
+ location_type: msg.locationType || null,
1290
+ file_content_text: msg.fileContentText || null,
1291
+ file_content_json: msg.fileContentJson || null,
1292
+ processing_status: hasPreparsedData ? "completed" : msg.messageType === "document" && (msg.fileName?.toLowerCase().endsWith(".pdf") || msg.mimeType === "application/pdf") ? "pending" : null
1293
+ });
1294
+ }
1295
+ if (records.length > 0) {
1296
+ await apiClient.post("/rest/v1/chat_messages", records);
1297
+ const isPdf = msg.messageType === "document" && (msg.fileName?.toLowerCase().endsWith(".pdf") || msg.mimeType === "application/pdf");
1298
+ if (isPdf && typeof window !== "undefined") {
1299
+ fetch("/api/process-pdf", {
1300
+ method: "POST",
1301
+ headers: { "Content-Type": "application/json" },
1302
+ body: JSON.stringify({
1303
+ messageId: senderMsgId
1304
+ })
1305
+ }).catch((err) => console.warn("[Messages API] Asynchronous PDF processing dispatch error:", err));
1306
+ }
1307
+ if (typeof window !== "undefined") {
1308
+ fetch("/api/notifications/push", {
1309
+ method: "POST",
1310
+ headers: { "Content-Type": "application/json" },
1311
+ body: JSON.stringify({
1312
+ senderId: msg.senderId,
1313
+ conversationId: msg.conversationId,
1314
+ message: msg.message,
1315
+ messageType: msg.messageType,
1316
+ fileName: msg.fileName
1317
+ })
1318
+ }).catch((err) => console.warn("[Messages API] Asynchronous FCM push dispatch error:", err));
1319
+ }
1320
+ }
1321
+ const senderRecord = records.find(
1322
+ (r) => r.owner_user_id === msg.senderId
1323
+ );
1324
+ if (!senderRecord) return null;
1325
+ const profileQuery = createQuery().select("id, name, email, avatar").eq("id", msg.senderId).limit(1);
1326
+ const profiles = await apiClient.get(
1327
+ `/rest/v1/profiles${profileQuery.toString()}`
1328
+ );
1329
+ const profile = profiles[0] || null;
1330
+ return {
1331
+ id: senderRecord.id,
1332
+ conversation_id: senderRecord.conversation_id,
1333
+ owner_user_id: senderRecord.owner_user_id,
1334
+ sender_user_id: senderRecord.sender_user_id,
1335
+ message: senderRecord.message,
1336
+ message_type: senderRecord.message_type,
1337
+ direction: senderRecord.direction,
1338
+ sent: senderRecord.sent,
1339
+ received: senderRecord.received,
1340
+ created_at: senderRecord.created_at,
1341
+ file_url: senderRecord.file_url || void 0,
1342
+ file_name: senderRecord.file_name || void 0,
1343
+ file_size: senderRecord.file_size ? Number(senderRecord.file_size) : void 0,
1344
+ mime_type: senderRecord.mime_type || void 0,
1345
+ duration: senderRecord.duration ? Number(senderRecord.duration) : void 0,
1346
+ thumbnail: senderRecord.thumbnail || void 0,
1347
+ thumb: senderRecord.thumb,
1348
+ favorite: senderRecord.favorite,
1349
+ flag: senderRecord.flag,
1350
+ star: senderRecord.star,
1351
+ pin: senderRecord.pin,
1352
+ archive: senderRecord.archive,
1353
+ deleted: senderRecord.deleted,
1354
+ action_this: senderRecord.action_this,
1355
+ reply: senderRecord.reply,
1356
+ forward: senderRecord.forward,
1357
+ replyemoji: senderRecord.replyemoji || void 0,
1358
+ replyto_message_id: senderRecord.replyto_message_id || void 0,
1359
+ replyto_user_id: senderRecord.replyto_user_id || void 0,
1360
+ parent_message_id: senderRecord.parent_message_id || void 0,
1361
+ message_status: senderRecord.message_status,
1362
+ client_message_id: senderRecord.client_message_id || void 0,
1363
+ location_data: senderRecord.location_data || void 0,
1364
+ location_type: senderRecord.location_type || void 0,
1365
+ file_content_text: senderRecord.file_content_text || void 0,
1366
+ file_content_json: senderRecord.file_content_json || void 0,
1367
+ processing_status: senderRecord.processing_status || void 0,
1368
+ sender: profile ? {
1369
+ id: profile.id,
1370
+ name: profile.name || profile.email.split("@")[0],
1371
+ email: profile.email,
1372
+ avatar_url: profile.avatar || void 0
1373
+ } : void 0
1374
+ };
1375
+ } catch (err) {
1376
+ console.error("[Messages API] Failed to create message copies:", err);
1377
+ return null;
1378
+ }
1379
+ }
1380
+
1381
+ // src/features/chattemplate/chat/repositories/message-repository.ts
1382
+ async function getConversationMessages2(conversationId, userId, options) {
1383
+ return getConversationMessages(conversationId, userId, options);
1384
+ }
1385
+ async function createMessage2(msg) {
1386
+ return createMessage(msg);
1387
+ }
1388
+
1389
+ // src/features/chattemplate/chat/api/conversations.api.ts
1390
+ async function getUserConversations(userId) {
1391
+ try {
1392
+ const businessUserId = stringToUuid(userId);
1393
+ const memberQuery = createQuery().select("conversation_id").eq("user_id", businessUserId);
1394
+ const memberOf = await apiClient.get(`/rest/v1/conversation_members${memberQuery.toString()}`);
1395
+ if (!memberOf || memberOf.length === 0) return [];
1396
+ const convoIds = memberOf.map((m) => m.conversation_id);
1397
+ const convosQuery = createQuery().select(`
1398
+ *,
1399
+ conversation_members (
1400
+ role,
1401
+ joined_at,
1402
+ user_id,
1403
+ unread_count,
1404
+ profiles (
1405
+ id,
1406
+ name,
1407
+ email,
1408
+ avatar,
1409
+ last_seen
1410
+ )
1411
+ ),
1412
+ chat_messages (
1413
+ id,
1414
+ conversation_id,
1415
+ sender_user_id,
1416
+ message,
1417
+ message_type,
1418
+ file_url,
1419
+ file_name,
1420
+ file_size,
1421
+ mime_type,
1422
+ duration,
1423
+ created_at,
1424
+ deleted,
1425
+ deleted_by,
1426
+ message_status
1427
+ )
1428
+ `).in("id", convoIds).eq("chat_messages.owner_user_id", businessUserId);
1429
+ const convos = await apiClient.get(`/rest/v1/conversations${convosQuery.toString()}`);
1430
+ if (!convos) return [];
1431
+ const mapped = convos.map((c) => {
1432
+ const members = (c.conversation_members || []).map((cm) => {
1433
+ if (!cm.profiles) return null;
1434
+ return {
1435
+ id: cm.profiles.id,
1436
+ name: cm.profiles.name,
1437
+ email: cm.profiles.email,
1438
+ avatar_url: cm.profiles.avatar || void 0,
1439
+ last_seen: cm.profiles.last_seen || void 0
1440
+ };
1441
+ }).filter(Boolean);
1442
+ const messageCopies = [...c.chat_messages || []].filter((m) => !m.deleted || m.deleted_by !== null).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
1443
+ const lastMessage = messageCopies[0] || void 0;
1444
+ let displayName = c.name || "";
1445
+ let displayImage = c.image || "";
1446
+ if (c.type === "direct") {
1447
+ const otherMember = members.find((m) => m.id !== businessUserId);
1448
+ if (otherMember) {
1449
+ displayName = otherMember.name;
1450
+ displayImage = otherMember.avatar_url || "";
1451
+ } else {
1452
+ displayName = "Chat Note (You)";
1453
+ const selfMember = members.find((m) => m.id === businessUserId);
1454
+ displayImage = selfMember?.avatar_url || "";
1455
+ }
1456
+ }
1457
+ const selfMemberRecord = (c.conversation_members || []).find((cm) => cm.user_id === businessUserId);
1458
+ const unreadCount = selfMemberRecord?.unread_count || 0;
1459
+ return {
1460
+ id: c.id,
1461
+ type: c.type,
1462
+ name: displayName,
1463
+ image: displayImage,
1464
+ created_by: c.created_by || void 0,
1465
+ created_at: c.created_at,
1466
+ lastMessage: lastMessage ? {
1467
+ id: lastMessage.id,
1468
+ conversation_id: lastMessage.conversation_id,
1469
+ sender_user_id: lastMessage.sender_user_id || "system",
1470
+ message: lastMessage.message || "",
1471
+ message_type: lastMessage.message_type,
1472
+ file_url: lastMessage.file_url || void 0,
1473
+ file_name: lastMessage.file_name || void 0,
1474
+ file_size: lastMessage.file_size || void 0,
1475
+ mime_type: lastMessage.mime_type || void 0,
1476
+ duration: lastMessage.duration || void 0,
1477
+ created_at: lastMessage.created_at,
1478
+ deleted: !!lastMessage.deleted
1479
+ } : void 0,
1480
+ unreadCount,
1481
+ members
1482
+ };
1483
+ });
1484
+ const seenDirectRecipients = /* @__PURE__ */ new Set();
1485
+ const deduplicated = [];
1486
+ const sorted = [...mapped].sort((a, b) => {
1487
+ const timeA = a.lastMessage ? new Date(a.lastMessage.created_at).getTime() : new Date(a.created_at).getTime();
1488
+ const timeB = b.lastMessage ? new Date(b.lastMessage.created_at).getTime() : new Date(b.created_at).getTime();
1489
+ return timeB - timeA;
1490
+ });
1491
+ for (const convo of sorted) {
1492
+ if (convo.type === "direct") {
1493
+ const otherMember = convo.members.find((m) => m.id !== businessUserId);
1494
+ const recipientId = otherMember ? otherMember.id : businessUserId;
1495
+ if (seenDirectRecipients.has(recipientId)) {
1496
+ continue;
1497
+ }
1498
+ seenDirectRecipients.add(recipientId);
1499
+ }
1500
+ deduplicated.push(convo);
1501
+ }
1502
+ return deduplicated;
1503
+ } catch (err) {
1504
+ console.error("[Conversations API] Failed to get user conversations:", err);
1505
+ return [];
1506
+ }
1507
+ }
1508
+ async function getOrCreateDirectConversation(userAId, userBId) {
1509
+ try {
1510
+ const businessUserAId = userAId;
1511
+ const businessUserBId = userBId;
1512
+ const queryA = createQuery().select("conversation_id, conversations!inner(*)").eq("user_id", businessUserAId).eq("conversations.type", "direct");
1513
+ const membersA = await apiClient.get(`/rest/v1/conversation_members${queryA.toString()}`);
1514
+ if (membersA && membersA.length > 0) {
1515
+ const convoIds = membersA.map((m) => m.conversation_id);
1516
+ const queryB = createQuery().select("conversation_id").in("conversation_id", convoIds).eq("user_id", businessUserBId);
1517
+ const membersB = await apiClient.get(`/rest/v1/conversation_members${queryB.toString()}`);
1518
+ if (membersB && membersB.length > 0) {
1519
+ return membersB[0].conversation_id;
1520
+ }
1521
+ }
1522
+ const insertedConvos = await apiClient.post("/rest/v1/conversations", {
1523
+ type: "direct",
1524
+ created_by: businessUserAId
1525
+ });
1526
+ const newConvo = insertedConvos[0] || null;
1527
+ if (!newConvo) {
1528
+ throw new Error("Failed to create conversation.");
1529
+ }
1530
+ const membersToInsert = [
1531
+ { conversation_id: newConvo.id, user_id: businessUserAId }
1532
+ ];
1533
+ if (businessUserAId !== businessUserBId) {
1534
+ membersToInsert.push({ conversation_id: newConvo.id, user_id: businessUserBId });
1535
+ }
1536
+ await apiClient.post("/rest/v1/conversation_members", membersToInsert);
1537
+ return newConvo.id;
1538
+ } catch (err) {
1539
+ console.error("[Conversations API] Failed to get or create direct conversation:", err);
1540
+ return null;
1541
+ }
1542
+ }
1543
+
1544
+ // src/features/chattemplate/chat/repositories/conversation-repository.ts
1545
+ async function getUserConversations2(userId) {
1546
+ return getUserConversations(userId);
1547
+ }
1548
+ async function getOrCreateDirectConversation2(userAId, userBId) {
1549
+ return getOrCreateDirectConversation(userAId, userBId);
1550
+ }
1551
+
1552
+ // src/server/messages.handler.ts
1553
+ async function handleMessagesGet(request) {
1554
+ try {
1555
+ const { searchParams } = new URL(request.url);
1556
+ const conversationId = searchParams.get("conversationId");
1557
+ const recipientId = searchParams.get("recipientId");
1558
+ const senderId = searchParams.get("senderId");
1559
+ if (!conversationId && !recipientId) {
1560
+ return import_server7.NextResponse.json({ error: "conversationId or recipientId is required" }, { status: 400 });
1561
+ }
1562
+ if (!senderId) {
1563
+ return import_server7.NextResponse.json({ error: "senderId (owner_user_id) is required" }, { status: 400 });
1564
+ }
1565
+ let targetConvoId = conversationId;
1566
+ if (!targetConvoId && recipientId) {
1567
+ targetConvoId = await getOrCreateDirectConversation2(senderId, recipientId);
1568
+ }
1569
+ if (!targetConvoId) {
1570
+ return import_server7.NextResponse.json([]);
1571
+ }
1572
+ const messages = await getConversationMessages2(targetConvoId, senderId);
1573
+ return import_server7.NextResponse.json(messages);
1574
+ } catch (err) {
1575
+ console.error("GET messages error:", err);
1576
+ return import_server7.NextResponse.json(
1577
+ { error: err instanceof Error ? err.message : "Failed to fetch messages" },
1578
+ { status: 500 }
1579
+ );
1580
+ }
1581
+ }
1582
+ async function handleMessagesPost(request) {
1583
+ try {
1584
+ const body = await request.json();
1585
+ const isAttachment = body.messageType && body.messageType !== "text";
1586
+ const hasContent = body.message?.trim() || isAttachment && body.fileUrl;
1587
+ if (!hasContent) {
1588
+ return import_server7.NextResponse.json(
1589
+ { error: "message (or fileUrl for attachments) is required" },
1590
+ { status: 400 }
1591
+ );
1592
+ }
1593
+ let targetConvoId = body.conversationId;
1594
+ if (!targetConvoId && body.recipientId) {
1595
+ const senderId = body.senderId;
1596
+ if (!senderId) {
1597
+ return import_server7.NextResponse.json(
1598
+ { error: "senderId is required to resolve conversation" },
1599
+ { status: 400 }
1600
+ );
1601
+ }
1602
+ targetConvoId = await getOrCreateDirectConversation2(senderId, body.recipientId);
1603
+ }
1604
+ if (!targetConvoId) {
1605
+ return import_server7.NextResponse.json(
1606
+ { error: "Could not resolve or create conversation" },
1607
+ { status: 400 }
1608
+ );
1609
+ }
1610
+ const msg = await createMessage2({
1611
+ conversationId: targetConvoId,
1612
+ senderId: body.senderId,
1613
+ message: body.message,
1614
+ messageType: body.messageType || "text",
1615
+ fileUrl: body.fileUrl,
1616
+ fileName: body.fileName,
1617
+ replyToMessageId: body.replyToMessageId,
1618
+ forwardedFromMessageId: body.forwardedFromMessageId,
1619
+ locationData: body.location
1620
+ });
1621
+ if (!msg) {
1622
+ return import_server7.NextResponse.json(
1623
+ { error: "Failed to create message" },
1624
+ { status: 500 }
1625
+ );
1626
+ }
1627
+ return import_server7.NextResponse.json(msg, { status: 201 });
1628
+ } catch (err) {
1629
+ console.error("POST message error:", err);
1630
+ return import_server7.NextResponse.json(
1631
+ { error: err instanceof Error ? err.message : "Failed to create message" },
1632
+ { status: 500 }
1633
+ );
1634
+ }
1635
+ }
1636
+
1637
+ // src/server/contacts.handler.ts
1638
+ var import_server8 = require("next/server");
1639
+
1640
+ // src/features/chattemplate/contacts/api/contacts.mapper.ts
1641
+ function mapToContact(dbRecord) {
1642
+ const u = dbRecord.contact_user || {};
1643
+ const contactEmail = dbRecord.email || u.email || "";
1644
+ return {
1645
+ id: dbRecord.id,
1646
+ ownerId: dbRecord.owner_id,
1647
+ contactUserId: dbRecord.contact_user_id,
1648
+ fullName: dbRecord.nickname || u.name || contactEmail.split("@")[0] || "Unknown",
1649
+ email: contactEmail,
1650
+ avatarUrl: u.avatar || void 0,
1651
+ company: u.company || void 0,
1652
+ mobile: u.mobile || void 0,
1653
+ status: "Active",
1654
+ nickname: dbRecord.nickname || void 0,
1655
+ createdAt: dbRecord.created_at
1656
+ };
1657
+ }
1658
+
1659
+ // src/lib/db-alerts/types/db-alert.ts
1660
+ var DB_ALERTS_CONFIG = {
1661
+ groupName: "DB Alerts",
1662
+ groupImage: "https://images.unsplash.com/photo-1598257006458-087169a1f08d?w=128&h=128&fit=crop&auto=format&q=80",
1663
+ adminEmails: [
1664
+ "itsaman00786@gmail.com",
1665
+ "amanmicropay@gmail.com",
1666
+ "n.rajukrishna@gmail.com"
1667
+ ]
1668
+ };
1669
+
1670
+ // src/services/db-alert.service.ts
1671
+ function formatAlertTime(date) {
1672
+ const day = date.getDate();
1673
+ const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1674
+ const month = months[date.getMonth()];
1675
+ const year = date.getFullYear();
1676
+ let hours = date.getHours();
1677
+ const minutes = date.getMinutes().toString().padStart(2, "0");
1678
+ const ampm = hours >= 12 ? "AM" : "PM";
1679
+ hours = hours % 12;
1680
+ hours = hours ? hours : 12;
1681
+ return `${day} ${month} ${year} ${hours}:${minutes} ${ampm}`;
1682
+ }
1683
+ async function getOrCreateAlertConversation() {
1684
+ const supabase2 = createClient3();
1685
+ try {
1686
+ const { data: existing, error } = await supabase2.from("conversations").select("id").eq("name", DB_ALERTS_CONFIG.groupName).eq("type", "group").maybeSingle();
1687
+ if (error) throw error;
1688
+ if (existing) return existing.id;
1689
+ const { data: newConvo, error: createError } = await supabase2.from("conversations").insert({
1690
+ type: "group",
1691
+ name: DB_ALERTS_CONFIG.groupName,
1692
+ image: DB_ALERTS_CONFIG.groupImage || null
1693
+ }).select("id").single();
1694
+ if (createError) throw createError;
1695
+ if (!newConvo) return null;
1696
+ const { data: adminProfiles, error: profilesError } = await supabase2.from("profiles").select("id, email").in("email", DB_ALERTS_CONFIG.adminEmails);
1697
+ if (profilesError) throw profilesError;
1698
+ if (adminProfiles && adminProfiles.length > 0) {
1699
+ const membersToInsert = adminProfiles.map((p) => ({
1700
+ conversation_id: newConvo.id,
1701
+ user_id: p.id,
1702
+ role: "member"
1703
+ }));
1704
+ const { error: insertError } = await supabase2.from("conversation_members").insert(membersToInsert);
1705
+ if (insertError) {
1706
+ console.error("[DB Alerts] Failed to insert initial admin members:", insertError);
1707
+ } else {
1708
+ console.log(`[DB Alerts] Created group with ${adminProfiles.length} configured admin members`);
1709
+ }
1710
+ }
1711
+ return newConvo.id;
1712
+ } catch (err) {
1713
+ console.error("[DB Alerts] Error in getOrCreateAlertConversation:", err);
1714
+ return null;
1715
+ }
1716
+ }
1717
+ async function triggerContactAlert(action, ownerId, contactUserId) {
1718
+ const supabase2 = createClient3();
1719
+ try {
1720
+ const convoId = await getOrCreateAlertConversation();
1721
+ if (!convoId) return;
1722
+ const { data: ownerProfile } = await supabase2.from("profiles").select("name, email").eq("id", ownerId).maybeSingle();
1723
+ const ownerName = ownerProfile?.name || ownerProfile?.email?.split("@")[0] || "System";
1724
+ const { data: contactProfile } = await supabase2.from("profiles").select("name, email").eq("id", contactUserId).maybeSingle();
1725
+ const contactName = contactProfile?.name || contactProfile?.email?.split("@")[0] || "System";
1726
+ const timeStr = formatAlertTime(/* @__PURE__ */ new Date());
1727
+ let formattedMessage = "";
1728
+ if (action === "create") {
1729
+ formattedMessage = `Contact Created
1730
+ \u{1F7E2} Contact Added
1731
+ By: ${ownerName}
1732
+ Contact: ${contactName}
1733
+ Time: ${timeStr}`;
1734
+ } else if (action === "delete") {
1735
+ formattedMessage = `Contact Deleted
1736
+ \u{1F534} Contact Deleted
1737
+ By: ${ownerName}
1738
+ Contact: ${contactName}`;
1739
+ } else if (action === "update") {
1740
+ formattedMessage = `Contact Updated
1741
+ \u{1F7E1} Contact Updated
1742
+ By: ${ownerName}
1743
+ Contact: ${contactName}
1744
+ Time: ${timeStr}`;
1745
+ }
1746
+ await createMessage2({
1747
+ conversationId: convoId,
1748
+ senderId: ownerId,
1749
+ message: formattedMessage,
1750
+ messageType: "system"
1751
+ });
1752
+ } catch (err) {
1753
+ console.error("[DB Alerts] Failed to trigger contact alert:", err);
1754
+ }
1755
+ }
1756
+ async function triggerGroupAlert(action, actorId, groupName) {
1757
+ const supabase2 = createClient3();
1758
+ try {
1759
+ const convoId = await getOrCreateAlertConversation();
1760
+ if (!convoId) return;
1761
+ let actorName = "System";
1762
+ if (actorId) {
1763
+ const { data: actorProfile } = await supabase2.from("profiles").select("name, email").eq("id", actorId).maybeSingle();
1764
+ actorName = actorProfile?.name || actorProfile?.email?.split("@")[0] || "System";
1765
+ }
1766
+ let formattedMessage = "";
1767
+ if (action === "create") {
1768
+ formattedMessage = `Group Created
1769
+ \u{1F7E2} Group Created
1770
+ By: ${actorName}
1771
+ Group: ${groupName}`;
1772
+ } else if (action === "delete") {
1773
+ formattedMessage = `Group Deleted
1774
+ \u{1F534} Group Deleted
1775
+ By: ${actorName}
1776
+ Group: ${groupName}`;
1777
+ } else if (action === "update") {
1778
+ formattedMessage = `Group Updated
1779
+ \u{1F7E1} Group Updated
1780
+ By: ${actorName}
1781
+ Group: ${groupName}`;
1782
+ }
1783
+ let senderId = actorId;
1784
+ if (!senderId) {
1785
+ const { data: firstMember } = await supabase2.from("conversation_members").select("user_id").eq("conversation_id", convoId).limit(1).maybeSingle();
1786
+ senderId = firstMember?.user_id || "";
1787
+ }
1788
+ if (!senderId) {
1789
+ console.warn("[DB Alerts] Cannot send alert because no valid sender ID is available.");
1790
+ return;
1791
+ }
1792
+ await createMessage2({
1793
+ conversationId: convoId,
1794
+ senderId,
1795
+ message: formattedMessage,
1796
+ messageType: "system"
1797
+ });
1798
+ } catch (err) {
1799
+ console.error("[DB Alerts] Failed to trigger group alert:", err);
1800
+ }
1801
+ }
1802
+
1803
+ // src/features/chattemplate/chat/services/chat-storage.service.ts
1804
+ async function initializeContactStorage(contactEmail) {
1805
+ return Promise.resolve();
1806
+ }
1807
+
1808
+ // src/features/chattemplate/contacts/api/contacts.api.ts
1809
+ async function getContacts(userId) {
1810
+ try {
1811
+ const validUserId = stringToUuid(userId);
1812
+ const query = createQuery().select(`
1813
+ id,
1814
+ owner_id,
1815
+ contact_user_id,
1816
+ nickname,
1817
+ email,
1818
+ created_at,
1819
+ contact_user:profiles!contacts_contact_user_id_fkey (
1820
+ id,
1821
+ name,
1822
+ email,
1823
+ avatar,
1824
+ company,
1825
+ mobile
1826
+ )
1827
+ `).eq("owner_id", validUserId).order("created_at", { ascending: false });
1828
+ const data = await apiClient.get(`/rest/v1/contacts${query.toString()}`);
1829
+ if (!data) return [];
1830
+ return data.map(mapToContact);
1831
+ } catch (error) {
1832
+ console.error("[Contacts API] Failed to get user contacts:", error);
1833
+ return [];
1834
+ }
1835
+ }
1836
+ async function createContact(ownerId, contactEmail, nickname, ownerEmail) {
1837
+ try {
1838
+ const validOwnerId = stringToUuid(ownerId);
1839
+ const emailLower = contactEmail.trim().toLowerCase();
1840
+ const profileQuery = createQuery().select("id, name, email, avatar, company, mobile").eq("email", emailLower).limit(1);
1841
+ const profiles = await apiClient.get(`/rest/v1/profiles${profileQuery.toString()}`);
1842
+ const profile = profiles[0] || null;
1843
+ if (profile && profile.id === validOwnerId) {
1844
+ return { success: false, error: "You cannot add yourself as a contact." };
1845
+ }
1846
+ if (profile) {
1847
+ const existingQuery = createQuery().select("id").eq("owner_id", validOwnerId).or(`email.ilike.${emailLower},contact_user_id.eq.${profile.id}`).limit(1);
1848
+ const existing = await apiClient.get(`/rest/v1/contacts${existingQuery.toString()}`);
1849
+ if (existing.length > 0) {
1850
+ return { success: false, error: "This contact is already in your list." };
1851
+ }
1852
+ } else {
1853
+ const existingQuery = createQuery().select("id").eq("owner_id", validOwnerId).ilike("email", emailLower).limit(1);
1854
+ const existing = await apiClient.get(`/rest/v1/contacts${existingQuery.toString()}`);
1855
+ if (existing.length > 0) {
1856
+ return { success: false, error: "This contact is already in your list." };
1857
+ }
1858
+ }
1859
+ let contactUserId;
1860
+ if (profile) {
1861
+ contactUserId = profile.id;
1862
+ } else {
1863
+ const displayName = nickname || emailLower.split("@")[0];
1864
+ const newUserId = crypto.randomUUID ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
1865
+ const r = Math.random() * 16 | 0;
1866
+ const v = c === "x" ? r : r & 3 | 8;
1867
+ return v.toString(16);
1868
+ });
1869
+ const newProfileData = {
1870
+ id: newUserId,
1871
+ email: emailLower,
1872
+ name: displayName,
1873
+ avatar: null,
1874
+ company: null,
1875
+ mobile: null,
1876
+ status: "offline",
1877
+ online: false,
1878
+ offline: true,
1879
+ last_seen: (/* @__PURE__ */ new Date()).toISOString(),
1880
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1881
+ };
1882
+ const insertedProfiles = await apiClient.post("/rest/v1/profiles", newProfileData);
1883
+ const newProfile = insertedProfiles[0] || null;
1884
+ if (!newProfile) {
1885
+ return { success: false, error: "Failed to create contact profile." };
1886
+ }
1887
+ contactUserId = newProfile.id;
1888
+ }
1889
+ const contactData = {
1890
+ owner_id: validOwnerId,
1891
+ contact_user_id: contactUserId,
1892
+ nickname: nickname?.trim() || null,
1893
+ email: emailLower,
1894
+ user_uuid: validOwnerId
1895
+ };
1896
+ await apiClient.post("/rest/v1/contacts", contactData);
1897
+ triggerContactAlert("create", ownerId, contactUserId).catch(
1898
+ (err) => console.error("[DB Alerts] Error sending contact created alert:", err)
1899
+ );
1900
+ if (emailLower) {
1901
+ void initializeContactStorage(emailLower).catch(
1902
+ (err) => console.error("[Contact Storage] Error initializing contact file space:", err)
1903
+ );
1904
+ }
1905
+ if (ownerEmail) {
1906
+ void initializeContactStorage(ownerEmail).catch(
1907
+ (err) => console.error("[Contact Storage] Error initializing owner file space:", err)
1908
+ );
1909
+ }
1910
+ return { success: true };
1911
+ } catch (error) {
1912
+ console.error("[Contacts API] Failed to create contact:", error);
1913
+ return { success: false, error: error instanceof Error ? error.message : "Failed to create contact" };
1914
+ }
1915
+ }
1916
+
1917
+ // src/features/chattemplate/contacts/repositories/contact-repository.ts
1918
+ async function getUserContacts(userId) {
1919
+ return getContacts(userId);
1920
+ }
1921
+ async function createContact2(ownerId, contactEmail, nickname, ownerEmail) {
1922
+ return createContact(ownerId, contactEmail, nickname, ownerEmail);
1923
+ }
1924
+
1925
+ // src/server/contacts.handler.ts
1926
+ async function handleContactsGet(request) {
1927
+ try {
1928
+ const { searchParams } = new URL(request.url);
1929
+ const userId = searchParams.get("userId");
1930
+ if (!userId) {
1931
+ return import_server8.NextResponse.json({ error: "userId query parameter is required" }, { status: 400 });
1932
+ }
1933
+ const contacts = await getUserContacts(userId);
1934
+ return import_server8.NextResponse.json(contacts);
1935
+ } catch (err) {
1936
+ console.error("GET contacts error:", err);
1937
+ return import_server8.NextResponse.json(
1938
+ { error: err instanceof Error ? err.message : "Failed to fetch contacts" },
1939
+ { status: 500 }
1940
+ );
1941
+ }
1942
+ }
1943
+ async function handleContactsPost(request) {
1944
+ try {
1945
+ const body = await request.json();
1946
+ if (!body.ownerId) {
1947
+ return import_server8.NextResponse.json({ error: "ownerId is required" }, { status: 400 });
1948
+ }
1949
+ if (!body.email) {
1950
+ return import_server8.NextResponse.json({ error: "email is required" }, { status: 400 });
1951
+ }
1952
+ const result = await createContact2(body.ownerId, body.email, body.nickname);
1953
+ if (!result.success) {
1954
+ return import_server8.NextResponse.json({ error: result.error || "Failed to create contact" }, { status: 409 });
1955
+ }
1956
+ return import_server8.NextResponse.json({ success: true, message: "Contact added successfully" }, { status: 201 });
1957
+ } catch (err) {
1958
+ console.error("POST contact error:", err);
1959
+ return import_server8.NextResponse.json(
1960
+ { error: err instanceof Error ? err.message : "Failed to create contact" },
1961
+ { status: 500 }
1962
+ );
1963
+ }
1964
+ }
1965
+
1966
+ // src/server/conversations.handler.ts
1967
+ var import_server9 = require("next/server");
1968
+ async function handleConversationsGet(request) {
1969
+ try {
1970
+ const { searchParams } = new URL(request.url);
1971
+ const userId = searchParams.get("userId");
1972
+ if (!userId) {
1973
+ return import_server9.NextResponse.json({ error: "userId query parameter is required" }, { status: 400 });
1974
+ }
1975
+ const conversations = await getUserConversations2(userId);
1976
+ return import_server9.NextResponse.json(conversations);
1977
+ } catch (err) {
1978
+ console.error("GET conversations error:", err);
1979
+ return import_server9.NextResponse.json(
1980
+ { error: err instanceof Error ? err.message : "Failed to fetch conversations" },
1981
+ { status: 500 }
1982
+ );
1983
+ }
1984
+ }
1985
+
1986
+ // src/server/groups.handler.ts
1987
+ var import_server10 = require("next/server");
1988
+
1989
+ // src/features/chattemplate/groups/api/groups.mapper.ts
1990
+ function mapToGroup(dbRecord) {
1991
+ return {
1992
+ id: dbRecord.id,
1993
+ groupName: dbRecord.name,
1994
+ description: dbRecord.description || "",
1995
+ groupImage: dbRecord.image_url || "",
1996
+ users: Array.isArray(dbRecord.users) ? dbRecord.users : [],
1997
+ status: dbRecord.status,
1998
+ email: dbRecord.email || void 0,
1999
+ userUuid: dbRecord.user_uuid || void 0,
2000
+ createdAt: dbRecord.created_at,
2001
+ updatedAt: dbRecord.updated_at
2002
+ };
2003
+ }
2004
+
2005
+ // src/features/chattemplate/groups/api/groups.api.ts
2006
+ async function saveGroup(group) {
2007
+ const id = group.id || crypto.randomUUID();
2008
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2009
+ let userUuid = null;
2010
+ const groupEmail = group.email?.trim().toLowerCase();
2011
+ if (group.userUuid) {
2012
+ userUuid = group.userUuid;
2013
+ } else if (groupEmail) {
2014
+ try {
2015
+ const profileQuery = createQuery().select("id").eq("email", groupEmail).limit(1);
2016
+ const profiles = await apiClient.get(`/rest/v1/profiles${profileQuery.toString()}`);
2017
+ const existing = profiles[0] || null;
2018
+ if (existing) {
2019
+ userUuid = existing.id;
2020
+ } else {
2021
+ const newUserId = crypto.randomUUID ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
2022
+ const r = Math.random() * 16 | 0;
2023
+ const v = c === "x" ? r : r & 3 | 8;
2024
+ return v.toString(16);
2025
+ });
2026
+ const newProfile = {
2027
+ id: newUserId,
2028
+ email: groupEmail,
2029
+ name: group.groupName || groupEmail.split("@")[0],
2030
+ avatar: group.groupImage || null,
2031
+ status: "offline",
2032
+ online: false,
2033
+ offline: true,
2034
+ last_seen: now,
2035
+ updated_at: now
2036
+ };
2037
+ const inserted = await apiClient.post("/rest/v1/profiles", newProfile);
2038
+ if (inserted && inserted[0]) {
2039
+ userUuid = inserted[0].id;
2040
+ }
2041
+ }
2042
+ } catch (err) {
2043
+ console.error("[Groups API] Failed to get or create group user:", err);
2044
+ }
2045
+ }
2046
+ console.log("[Groups API] Saving group with userUuid:", userUuid);
2047
+ const record = {
2048
+ id,
2049
+ name: group.groupName,
2050
+ description: group.description || null,
2051
+ image_url: group.groupImage || null,
2052
+ users: group.users || [],
2053
+ status: group.status || "Active",
2054
+ email: groupEmail || null,
2055
+ user_uuid: userUuid,
2056
+ created_at: group.createdAt || now,
2057
+ updated_at: now
2058
+ };
2059
+ let isNew = true;
2060
+ try {
2061
+ const checkQuery = createQuery().select("id").eq("id", id).limit(1);
2062
+ const existing = await apiClient.get(`/rest/v1/chat_group${checkQuery.toString()}`);
2063
+ if (existing && existing.length > 0) {
2064
+ isNew = false;
2065
+ }
2066
+ } catch (err) {
2067
+ }
2068
+ try {
2069
+ const data = await apiClient.post("/rest/v1/chat_group", record, {
2070
+ headers: {
2071
+ "Prefer": "resolution=merge-duplicates,return=representation"
2072
+ }
2073
+ });
2074
+ const savedRecord = data && data[0] ? data[0] : null;
2075
+ if (!savedRecord) {
2076
+ return null;
2077
+ }
2078
+ triggerGroupAlert(isNew ? "create" : "update", userUuid || "", group.groupName).catch(
2079
+ (err) => console.error("[DB Alerts] Error sending group saved alert:", err)
2080
+ );
2081
+ return mapToGroup(savedRecord);
2082
+ } catch (error) {
2083
+ console.error("[Groups API] Supabase saveGroup error:", error);
2084
+ return null;
2085
+ }
2086
+ }
2087
+
2088
+ // src/features/chattemplate/groups/repositories/group-repository.ts
2089
+ async function saveGroup2(group) {
2090
+ return saveGroup(group);
2091
+ }
2092
+
2093
+ // src/server/groups.handler.ts
2094
+ var import_supabase_js3 = require("@supabase/supabase-js");
2095
+ async function handleGroupsGet(request) {
2096
+ try {
2097
+ const { searchParams } = new URL(request.url);
2098
+ const email = searchParams.get("email");
2099
+ if (!email) {
2100
+ return import_server10.NextResponse.json([]);
2101
+ }
2102
+ const supabaseUrl2 = process.env.NEXT_PUBLIC_SUPABASE_URL;
2103
+ const supabaseAnonKey2 = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;
2104
+ if (!supabaseUrl2 || !supabaseAnonKey2) {
2105
+ return import_server10.NextResponse.json([]);
2106
+ }
2107
+ const supabase2 = (0, import_supabase_js3.createClient)(supabaseUrl2, supabaseAnonKey2);
2108
+ const { data, error } = await supabase2.from("chat_group").select("*").contains("users", JSON.stringify([email])).order("created_at", { ascending: false });
2109
+ if (error) throw error;
2110
+ const mapped = (data || []).map((d) => ({
2111
+ id: d.id,
2112
+ groupName: d.name,
2113
+ description: d.description || "",
2114
+ groupImage: d.image_url || "",
2115
+ users: Array.isArray(d.users) ? d.users : [],
2116
+ status: d.status,
2117
+ email: d.email || void 0,
2118
+ userUuid: d.user_uuid || void 0,
2119
+ createdAt: d.created_at,
2120
+ updatedAt: d.updated_at
2121
+ }));
2122
+ return import_server10.NextResponse.json(mapped);
2123
+ } catch (err) {
2124
+ console.error("GET groups error:", err);
2125
+ return import_server10.NextResponse.json(
2126
+ { error: err instanceof Error ? err.message : "Failed to fetch groups" },
2127
+ { status: 500 }
2128
+ );
2129
+ }
2130
+ }
2131
+ async function handleGroupsPost(request) {
2132
+ try {
2133
+ const body = await request.json();
2134
+ if (!body.groupName || !body.users || !body.status || !body.email) {
2135
+ return import_server10.NextResponse.json(
2136
+ { error: "groupName, users, status, and email are required fields" },
2137
+ { status: 400 }
2138
+ );
2139
+ }
2140
+ const saved = await saveGroup2({
2141
+ groupName: body.groupName,
2142
+ description: body.description,
2143
+ groupImage: body.groupImage,
2144
+ users: body.users,
2145
+ status: body.status,
2146
+ email: body.email,
2147
+ userUuid: body.userUuid
2148
+ });
2149
+ if (!saved) {
2150
+ return import_server10.NextResponse.json({ error: "Failed to create group in database" }, { status: 500 });
2151
+ }
2152
+ return import_server10.NextResponse.json(saved, { status: 201 });
2153
+ } catch (err) {
2154
+ console.error("POST group error:", err);
2155
+ return import_server10.NextResponse.json(
2156
+ { error: err instanceof Error ? err.message : "Internal server error while creating group" },
2157
+ { status: 500 }
2158
+ );
2159
+ }
2160
+ }
2161
+
2162
+ // src/server/profiles.handler.ts
2163
+ var import_server11 = require("next/server");
2164
+
2165
+ // src/features/chattemplate/chat/api/profiles.api.ts
2166
+ async function getProfileByEmail(email) {
2167
+ try {
2168
+ const query = createQuery().select("*").eq("email", email).limit(1);
2169
+ const profiles = await apiClient.get(`/rest/v1/profiles${query.toString()}`);
2170
+ const data = profiles[0] || null;
2171
+ if (!data) return null;
2172
+ return {
2173
+ id: data.id,
2174
+ name: data.name,
2175
+ email: data.email,
2176
+ avatar_url: data.avatar || void 0
2177
+ };
2178
+ } catch (err) {
2179
+ console.error("[profiles.api] Failed to get profile by email:", err);
2180
+ return null;
2181
+ }
2182
+ }
2183
+ async function getAllProfiles() {
2184
+ try {
2185
+ const query = createQuery().select("*").order("name", { ascending: true });
2186
+ const data = await apiClient.get(`/rest/v1/profiles${query.toString()}`);
2187
+ if (!data) return [];
2188
+ return data.map((d) => ({
2189
+ id: d.id,
2190
+ name: d.name,
2191
+ email: d.email,
2192
+ avatar_url: d.avatar || void 0
2193
+ }));
2194
+ } catch (err) {
2195
+ console.error("[profiles.api] Failed to get all profiles:", err);
2196
+ return [];
2197
+ }
2198
+ }
2199
+
2200
+ // src/features/chattemplate/chat/repositories/profile-repository.ts
2201
+ async function getProfileByEmail2(email) {
2202
+ return getProfileByEmail(email);
2203
+ }
2204
+ async function getAllProfiles2() {
2205
+ return getAllProfiles();
2206
+ }
2207
+
2208
+ // src/server/profiles.handler.ts
2209
+ async function handleProfilesGet(request) {
2210
+ try {
2211
+ const { searchParams } = new URL(request.url);
2212
+ const email = searchParams.get("email");
2213
+ if (email) {
2214
+ const profile = await getProfileByEmail2(email);
2215
+ if (!profile) {
2216
+ return import_server11.NextResponse.json({ error: "Profile not found" }, { status: 404 });
2217
+ }
2218
+ return import_server11.NextResponse.json(profile);
2219
+ }
2220
+ const profiles = await getAllProfiles2();
2221
+ return import_server11.NextResponse.json(profiles);
2222
+ } catch (err) {
2223
+ console.error("GET profiles error:", err);
2224
+ return import_server11.NextResponse.json(
2225
+ { error: err instanceof Error ? err.message : "Failed to fetch profiles" },
2226
+ { status: 500 }
2227
+ );
2228
+ }
2229
+ }
2230
+
2231
+ // src/server/notifications.handler.ts
2232
+ var import_server12 = require("next/server");
2233
+ async function handleNotificationsGet(request) {
2234
+ try {
2235
+ const { searchParams } = new URL(request.url);
2236
+ const userId = searchParams.get("userId");
2237
+ const readFilter = searchParams.get("read");
2238
+ const limit = searchParams.get("limit");
2239
+ if (!userId) {
2240
+ return import_server12.NextResponse.json({ error: "userId query parameter is required" }, { status: 400 });
2241
+ }
2242
+ const query = createQuery().select("*").eq("user_id", userId).order("created_at", { ascending: false });
2243
+ if (readFilter !== null) {
2244
+ query.eq("read", readFilter);
2245
+ }
2246
+ if (limit) {
2247
+ query.limit(parseInt(limit, 10));
2248
+ }
2249
+ const notifications = await apiClient.get(`/rest/v1/notifications${query.toString()}`);
2250
+ return import_server12.NextResponse.json(notifications);
2251
+ } catch (err) {
2252
+ console.error("GET notifications error:", err);
2253
+ return import_server12.NextResponse.json(
2254
+ { error: err instanceof Error ? err.message : "Failed to fetch notifications" },
2255
+ { status: 500 }
2256
+ );
2257
+ }
2258
+ }
2259
+ async function handleNotificationsPost(request) {
2260
+ try {
2261
+ const body = await request.json();
2262
+ if (!body.userId || !body.messageText) {
2263
+ return import_server12.NextResponse.json(
2264
+ { error: "userId and messageText are required" },
2265
+ { status: 400 }
2266
+ );
2267
+ }
2268
+ const newNotification = await apiClient.post("/rest/v1/notifications", {
2269
+ user_id: body.userId,
2270
+ sender_id: body.senderId || null,
2271
+ message_id: body.messageId || null,
2272
+ message_text: body.messageText,
2273
+ read: false
2274
+ });
2275
+ return import_server12.NextResponse.json(newNotification, { status: 201 });
2276
+ } catch (err) {
2277
+ console.error("POST notification error:", err);
2278
+ return import_server12.NextResponse.json(
2279
+ { error: err instanceof Error ? err.message : "Failed to create notification" },
2280
+ { status: 500 }
2281
+ );
2282
+ }
2283
+ }
2284
+ async function handleNotificationsPatch(request) {
2285
+ try {
2286
+ const body = await request.json();
2287
+ if (!body.id) {
2288
+ return import_server12.NextResponse.json({ error: "id is required" }, { status: 400 });
2289
+ }
2290
+ const query = createQuery().eq("id", body.id);
2291
+ const updated = await apiClient.patch(`/rest/v1/notifications${query.toString()}`, {
2292
+ read: body.read !== void 0 ? body.read : true
2293
+ });
2294
+ return import_server12.NextResponse.json(updated);
2295
+ } catch (err) {
2296
+ console.error("PATCH notification error:", err);
2297
+ return import_server12.NextResponse.json(
2298
+ { error: err instanceof Error ? err.message : "Failed to update notification" },
2299
+ { status: 500 }
2300
+ );
2301
+ }
2302
+ }
2303
+
2304
+ // src/server/mail.handler.ts
2305
+ var import_server13 = require("next/server");
2306
+
2307
+ // src/lib/email/imap.ts
2308
+ var import_imapflow = require("imapflow");
2309
+
2310
+ // config/mail.json
2311
+ var mail_default = {
2312
+ email: "ask@morrai.com",
2313
+ password: "0un:ZX3JOs&E",
2314
+ smtp: {
2315
+ host: "smtp.hostinger.com",
2316
+ port: 587,
2317
+ secure: false,
2318
+ requireTLS: true
2319
+ },
2320
+ imap: {
2321
+ host: "imap.hostinger.com",
2322
+ port: 993,
2323
+ secure: true
2324
+ }
2325
+ };
2326
+
2327
+ // src/lib/email/imap.ts
2328
+ function createImapClient() {
2329
+ return new import_imapflow.ImapFlow({
2330
+ host: mail_default.imap.host,
2331
+ port: mail_default.imap.port,
2332
+ secure: mail_default.imap.secure,
2333
+ auth: {
2334
+ user: mail_default.email,
2335
+ pass: mail_default.password
2336
+ },
2337
+ // Hostinger and some other providers benefit from lower concurrency/logger configurations
2338
+ logger: false
2339
+ });
2340
+ }
2341
+
2342
+ // src/lib/email/email-parser.ts
2343
+ var import_mailparser = require("mailparser");
2344
+ async function parseEmail(source, seq, isRead) {
2345
+ const parsed = await (0, import_mailparser.simpleParser)(source);
2346
+ let fromAddress = "";
2347
+ const fromObj = parsed.from;
2348
+ if (fromObj && fromObj.value && fromObj.value.length > 0) {
2349
+ const fromVal = fromObj.value[0];
2350
+ fromAddress = fromVal.address || fromVal.name || "";
2351
+ }
2352
+ let toAddress = "";
2353
+ const toObj = parsed.to;
2354
+ if (toObj && toObj.value && toObj.value.length > 0) {
2355
+ const toVal = toObj.value[0];
2356
+ toAddress = toVal.address || toVal.name || "";
2357
+ }
2358
+ const formatSize2 = (bytes) => {
2359
+ if (!bytes || bytes === 0) return "0 B";
2360
+ const k = 1024;
2361
+ const sizes = ["B", "KB", "MB", "GB"];
2362
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2363
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
2364
+ };
2365
+ const attachments = (parsed.attachments || []).map((att, idx) => {
2366
+ const mimeType = att.contentType || "application/octet-stream";
2367
+ let fileUrl = "";
2368
+ let formattedSize = formatSize2(att.size || (att.content ? att.content.length : 0));
2369
+ if (att.content) {
2370
+ try {
2371
+ const buf = Buffer.isBuffer(att.content) ? att.content : Buffer.from(att.content);
2372
+ fileUrl = `data:${mimeType};base64,${buf.toString("base64")}`;
2373
+ } catch (err) {
2374
+ console.error("Error processing attachment:", err);
2375
+ }
2376
+ }
2377
+ return {
2378
+ id: `att-${seq}-${idx}`,
2379
+ name: att.filename || `attachment-${idx + 1}`,
2380
+ type: mimeType,
2381
+ size: formattedSize,
2382
+ url: fileUrl
2383
+ };
2384
+ });
2385
+ return {
2386
+ id: String(seq),
2387
+ from: fromAddress,
2388
+ fromName: parsed.from?.text || fromAddress,
2389
+ to: toAddress,
2390
+ subject: parsed.subject || "(No Subject)",
2391
+ date: parsed.date ? parsed.date.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
2392
+ text: parsed.text || "",
2393
+ html: parsed.html || parsed.textAsHtml || "",
2394
+ isRead,
2395
+ attachments
2396
+ };
2397
+ }
2398
+
2399
+ // src/lib/email/mailer.ts
2400
+ var import_nodemailer = __toESM(require("nodemailer"));
2401
+ var transporter = import_nodemailer.default.createTransport({
2402
+ host: mail_default.smtp.host,
2403
+ port: mail_default.smtp.port,
2404
+ secure: mail_default.smtp.secure,
2405
+ requireTLS: mail_default.smtp.requireTLS,
2406
+ auth: {
2407
+ user: mail_default.email,
2408
+ pass: mail_default.password
2409
+ }
2410
+ });
2411
+
2412
+ // src/lib/email/attachment-storage.ts
2413
+ var import_fs2 = __toESM(require("fs"));
2414
+ var import_path2 = __toESM(require("path"));
2415
+ var UPLOAD_DIR = import_path2.default.join(process.cwd(), "public", "uploads", "mail-attachments");
2416
+ function ensureDirectoryExists() {
2417
+ if (!import_fs2.default.existsSync(UPLOAD_DIR)) {
2418
+ import_fs2.default.mkdirSync(UPLOAD_DIR, { recursive: true });
2419
+ }
2420
+ }
2421
+ function formatSize(bytes) {
2422
+ if (!bytes || bytes === 0) return "0 B";
2423
+ const k = 1024;
2424
+ const sizes = ["B", "KB", "MB", "GB"];
2425
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2426
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
2427
+ }
2428
+ function saveAttachmentLocally(originalFilename, content) {
2429
+ try {
2430
+ ensureDirectoryExists();
2431
+ const sanitizeName = (originalFilename || "attachment").replace(/[^a-zA-Z0-9._-]/g, "_").substring(0, 100);
2432
+ const timestamp = Date.now();
2433
+ const safeFilename = `${timestamp}_${sanitizeName}`;
2434
+ const filePath = import_path2.default.join(UPLOAD_DIR, safeFilename);
2435
+ let buffer;
2436
+ if (Buffer.isBuffer(content)) {
2437
+ buffer = content;
2438
+ } else if (typeof content === "string") {
2439
+ let raw = content;
2440
+ if (raw.includes(";base64,")) {
2441
+ raw = raw.split(";base64,").pop() || "";
2442
+ }
2443
+ buffer = Buffer.from(raw, "base64");
2444
+ } else {
2445
+ buffer = Buffer.from(content);
2446
+ }
2447
+ import_fs2.default.writeFileSync(filePath, buffer);
2448
+ const publicUrl = `/uploads/mail-attachments/${safeFilename}`;
2449
+ const formattedSize = formatSize(buffer.length);
2450
+ return {
2451
+ url: publicUrl,
2452
+ size: formattedSize
2453
+ };
2454
+ } catch (err) {
2455
+ console.error("Failed to save attachment locally:", err);
2456
+ return {
2457
+ url: "",
2458
+ size: "0 B"
2459
+ };
2460
+ }
2461
+ }
2462
+
2463
+ // src/server/mail.handler.ts
2464
+ async function handleMailInboxGet(request) {
2465
+ const url = new URL(request.url);
2466
+ const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
2467
+ const limit = Math.max(1, parseInt(url.searchParams.get("limit") || "20", 10));
2468
+ const client = createImapClient();
2469
+ let hasMore = false;
2470
+ let totalMessages = 0;
2471
+ try {
2472
+ await client.connect();
2473
+ const lock = await client.getMailboxLock("INBOX");
2474
+ const emailsList = [];
2475
+ try {
2476
+ const status = await client.status("INBOX", { messages: true });
2477
+ totalMessages = status.messages || 0;
2478
+ if (totalMessages > 0) {
2479
+ const offset = (page - 1) * limit;
2480
+ const endSeq = Math.max(0, totalMessages - offset);
2481
+ const startSeq = Math.max(1, endSeq - limit + 1);
2482
+ if (endSeq >= 1) {
2483
+ const range = `${startSeq}:${endSeq}`;
2484
+ hasMore = startSeq > 1;
2485
+ for await (const message of client.fetch(range, { source: true, flags: true })) {
2486
+ const isRead = message.flags && message.flags.has("\\Seen");
2487
+ try {
2488
+ const parsed = await parseEmail(message.source, message.seq, !!isRead);
2489
+ emailsList.push(parsed);
2490
+ } catch (parseErr) {
2491
+ console.error(`Failed to parse email sequence ${message.seq}:`, parseErr);
2492
+ }
2493
+ }
2494
+ emailsList.reverse();
2495
+ }
2496
+ }
2497
+ } finally {
2498
+ lock.release();
2499
+ }
2500
+ await client.logout();
2501
+ return import_server13.NextResponse.json({
2502
+ success: true,
2503
+ emails: emailsList,
2504
+ hasMore,
2505
+ total: totalMessages,
2506
+ page,
2507
+ limit
2508
+ });
2509
+ } catch (error) {
2510
+ console.error("Error reading mailbox via IMAP:", error);
2511
+ try {
2512
+ await client.logout();
2513
+ } catch (_) {
2514
+ }
2515
+ return import_server13.NextResponse.json(
2516
+ {
2517
+ success: false,
2518
+ message: `Failed to load inbox emails: ${error.message || error}`
2519
+ },
2520
+ { status: 500 }
2521
+ );
2522
+ }
2523
+ }
2524
+ async function handleMailSentGet(request) {
2525
+ const url = new URL(request.url);
2526
+ const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
2527
+ const limit = Math.max(1, parseInt(url.searchParams.get("limit") || "20", 10));
2528
+ const client = createImapClient();
2529
+ let hasMore = false;
2530
+ let totalMessages = 0;
2531
+ try {
2532
+ await client.connect();
2533
+ let mailboxName = "INBOX.Sent";
2534
+ let lock;
2535
+ try {
2536
+ lock = await client.getMailboxLock(mailboxName);
2537
+ } catch (_) {
2538
+ mailboxName = "Sent";
2539
+ lock = await client.getMailboxLock(mailboxName);
2540
+ }
2541
+ const emailsList = [];
2542
+ try {
2543
+ const status = await client.status(mailboxName, { messages: true });
2544
+ totalMessages = status.messages || 0;
2545
+ if (totalMessages > 0) {
2546
+ const offset = (page - 1) * limit;
2547
+ const endSeq = Math.max(0, totalMessages - offset);
2548
+ const startSeq = Math.max(1, endSeq - limit + 1);
2549
+ if (endSeq >= 1) {
2550
+ const range = `${startSeq}:${endSeq}`;
2551
+ hasMore = startSeq > 1;
2552
+ for await (const message of client.fetch(range, { source: true, flags: true })) {
2553
+ try {
2554
+ const parsed = await parseEmail(message.source, message.seq, true);
2555
+ emailsList.push({
2556
+ ...parsed,
2557
+ isSent: true
2558
+ });
2559
+ } catch (parseErr) {
2560
+ console.error(`Failed to parse sent email sequence ${message.seq}:`, parseErr);
2561
+ }
2562
+ }
2563
+ emailsList.reverse();
2564
+ }
2565
+ }
2566
+ } finally {
2567
+ if (lock) {
2568
+ lock.release();
2569
+ }
2570
+ }
2571
+ await client.logout();
2572
+ return import_server13.NextResponse.json({
2573
+ success: true,
2574
+ emails: emailsList,
2575
+ hasMore,
2576
+ total: totalMessages,
2577
+ page,
2578
+ limit
2579
+ });
2580
+ } catch (error) {
2581
+ console.error("Error reading sent mailbox via IMAP:", error);
2582
+ try {
2583
+ await client.logout();
2584
+ } catch (_) {
2585
+ }
2586
+ return import_server13.NextResponse.json(
2587
+ {
2588
+ success: false,
2589
+ message: `Failed to load sent emails: ${error.message || error}`
2590
+ },
2591
+ { status: 500 }
2592
+ );
2593
+ }
2594
+ }
2595
+ async function handleMailSendPost(request) {
2596
+ try {
2597
+ const body = await request.json();
2598
+ const { to, subject, html } = body;
2599
+ if (!to || !subject || !html) {
2600
+ return import_server13.NextResponse.json(
2601
+ {
2602
+ success: false,
2603
+ message: "Validation failed: 'to', 'subject', and 'html' are required fields."
2604
+ },
2605
+ { status: 400 }
2606
+ );
2607
+ }
2608
+ const attachments = (body.attachments || []).map((att) => {
2609
+ let rawContent = att.content || att.url || "";
2610
+ const filename = att.filename || att.name || "attachment";
2611
+ const contentType = att.contentType || att.type || "application/octet-stream";
2612
+ const saved = saveAttachmentLocally(filename, rawContent);
2613
+ if (rawContent.includes(";base64,")) {
2614
+ rawContent = rawContent.split(";base64,").pop() || "";
2615
+ }
2616
+ return {
2617
+ filename,
2618
+ contentType,
2619
+ content: Buffer.from(rawContent, "base64"),
2620
+ url: saved.url,
2621
+ size: saved.size
2622
+ };
2623
+ });
2624
+ const mailOptions = {
2625
+ from: body.from || `"${mail_default.email.split("@")[0]}" <${mail_default.email}>`,
2626
+ to: Array.isArray(to) ? to.join(", ") : to,
2627
+ subject,
2628
+ html
2629
+ };
2630
+ if (attachments.length > 0) {
2631
+ mailOptions.attachments = attachments;
2632
+ }
2633
+ const info = await transporter.sendMail(mailOptions);
2634
+ try {
2635
+ const MailComposer = require("nodemailer/lib/mail-composer");
2636
+ const composer = new MailComposer(mailOptions);
2637
+ const rawMimeBuffer = await composer.compile().build();
2638
+ const client = createImapClient();
2639
+ await client.connect();
2640
+ await client.append("INBOX.Sent", rawMimeBuffer, ["\\Seen"]);
2641
+ await client.logout();
2642
+ } catch (imapErr) {
2643
+ console.warn("Could not save to IMAP INBOX.Sent:", imapErr);
2644
+ }
2645
+ const returnedAttachments = attachments.map((att, idx) => ({
2646
+ id: `sent-att-${Date.now()}-${idx}`,
2647
+ name: att.filename,
2648
+ type: att.contentType,
2649
+ size: att.size,
2650
+ url: att.url
2651
+ }));
2652
+ return import_server13.NextResponse.json({
2653
+ success: true,
2654
+ message: "Email sent successfully",
2655
+ messageId: info.messageId,
2656
+ attachments: returnedAttachments
2657
+ });
2658
+ } catch (error) {
2659
+ console.error("Error sending email via Nodemailer:", error);
2660
+ return import_server13.NextResponse.json(
2661
+ {
2662
+ success: false,
2663
+ message: `Failed to send email: ${error.message || error}`
2664
+ },
2665
+ { status: 500 }
2666
+ );
2667
+ }
2668
+ }
2669
+ async function handleMailTestGet() {
2670
+ try {
2671
+ await transporter.verify();
2672
+ return import_server13.NextResponse.json({
2673
+ success: true,
2674
+ message: "SMTP connection successful"
2675
+ });
2676
+ } catch (error) {
2677
+ console.error("SMTP verification error:", error);
2678
+ return import_server13.NextResponse.json(
2679
+ {
2680
+ success: false,
2681
+ message: `SMTP connection failed: ${error.message || error}`
2682
+ },
2683
+ { status: 500 }
2684
+ );
2685
+ }
2686
+ }
2687
+ // Annotate the CommonJS export names for ESM import in node:
2688
+ 0 && (module.exports = {
2689
+ UI_RENDER_SYSTEM_PROMPT,
2690
+ handleChatPost,
2691
+ handleContactsGet,
2692
+ handleContactsPost,
2693
+ handleConversationsGet,
2694
+ handleGeocodeRequest,
2695
+ handleGroupsGet,
2696
+ handleGroupsPost,
2697
+ handleMailInboxGet,
2698
+ handleMailSendPost,
2699
+ handleMailSentGet,
2700
+ handleMailTestGet,
2701
+ handleMessagesGet,
2702
+ handleMessagesPost,
2703
+ handleNotificationsGet,
2704
+ handleNotificationsPatch,
2705
+ handleNotificationsPost,
2706
+ handleProfilesGet,
2707
+ handleSearchPost,
2708
+ handleShortenOptions,
2709
+ handleShortenPost,
2710
+ handleVouchersGet,
2711
+ handleVouchersPost
2712
+ });
2713
+ //# sourceMappingURL=server.js.map