@lastbrain/module-auth 2.0.19 → 2.0.31

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.
Files changed (178) hide show
  1. package/README.md +55 -7
  2. package/dist/api/admin/signup-stats.d.ts.map +1 -1
  3. package/dist/api/admin/signup-stats.js +2 -1
  4. package/dist/api/admin/storage/usage.d.ts +18 -0
  5. package/dist/api/admin/storage/usage.d.ts.map +1 -0
  6. package/dist/api/admin/storage/usage.js +100 -0
  7. package/dist/api/admin/users/[id]/notifications.d.ts.map +1 -1
  8. package/dist/api/admin/users/[id]/notifications.js +3 -2
  9. package/dist/api/admin/users/[id].d.ts.map +1 -1
  10. package/dist/api/admin/users/[id].js +3 -2
  11. package/dist/api/admin/users/reactivate/[id].d.ts +16 -0
  12. package/dist/api/admin/users/reactivate/[id].d.ts.map +1 -0
  13. package/dist/api/admin/users/reactivate/[id].js +59 -0
  14. package/dist/api/admin/users/suspend/[id].d.ts +16 -0
  15. package/dist/api/admin/users/suspend/[id].d.ts.map +1 -0
  16. package/dist/api/admin/users/suspend/[id].js +59 -0
  17. package/dist/api/admin/users-by-source.d.ts.map +1 -1
  18. package/dist/api/admin/users-by-source.js +2 -1
  19. package/dist/api/admin/users.d.ts.map +1 -1
  20. package/dist/api/admin/users.js +53 -2
  21. package/dist/api/auth/account/email-change.d.ts +7 -0
  22. package/dist/api/auth/account/email-change.d.ts.map +1 -0
  23. package/dist/api/auth/account/email-change.js +39 -0
  24. package/dist/api/auth/account/reset-password.d.ts +7 -0
  25. package/dist/api/auth/account/reset-password.d.ts.map +1 -0
  26. package/dist/api/auth/account/reset-password.js +36 -0
  27. package/dist/api/auth/check-username.d.ts +9 -0
  28. package/dist/api/auth/check-username.d.ts.map +1 -0
  29. package/dist/api/auth/check-username.js +35 -0
  30. package/dist/api/auth/establish-session.d.ts +2 -0
  31. package/dist/api/auth/establish-session.d.ts.map +1 -0
  32. package/dist/api/auth/establish-session.js +23 -0
  33. package/dist/api/auth/me.d.ts +4 -4
  34. package/dist/api/auth/me.d.ts.map +1 -1
  35. package/dist/api/auth/me.js +28 -6
  36. package/dist/api/auth/profile.d.ts.map +1 -1
  37. package/dist/api/auth/profile.js +6 -3
  38. package/dist/api/auth/storage/recalculate.d.ts +15 -0
  39. package/dist/api/auth/storage/recalculate.d.ts.map +1 -0
  40. package/dist/api/auth/storage/recalculate.js +68 -0
  41. package/dist/api/auth/storage/usage.d.ts +10 -0
  42. package/dist/api/auth/storage/usage.d.ts.map +1 -0
  43. package/dist/api/auth/storage/usage.js +86 -0
  44. package/dist/api/public/add-welcome-bonus.d.ts +16 -0
  45. package/dist/api/public/add-welcome-bonus.d.ts.map +1 -0
  46. package/dist/api/public/add-welcome-bonus.js +177 -0
  47. package/dist/api/public/callback.d.ts +3 -0
  48. package/dist/api/public/callback.d.ts.map +1 -0
  49. package/dist/api/public/callback.js +197 -0
  50. package/dist/api/public/reset-password.d.ts +3 -0
  51. package/dist/api/public/reset-password.d.ts.map +1 -0
  52. package/dist/api/public/reset-password.js +43 -0
  53. package/dist/api/public/set-session.d.ts +7 -0
  54. package/dist/api/public/set-session.d.ts.map +1 -0
  55. package/dist/api/public/set-session.js +55 -0
  56. package/dist/api/public/signin.d.ts.map +1 -1
  57. package/dist/api/public/signin.js +31 -0
  58. package/dist/api/public/signup.d.ts.map +1 -1
  59. package/dist/api/public/signup.js +38 -27
  60. package/dist/api/public/webhook/storage-addon.d.ts +9 -0
  61. package/dist/api/public/webhook/storage-addon.d.ts.map +1 -0
  62. package/dist/api/public/webhook/storage-addon.js +155 -0
  63. package/dist/api/storage.js +2 -2
  64. package/dist/auth.build.config.d.ts.map +1 -1
  65. package/dist/auth.build.config.js +134 -14
  66. package/dist/components/AccountButton.d.ts.map +1 -1
  67. package/dist/components/AccountButton.js +54 -28
  68. package/dist/components/Doc.d.ts.map +1 -1
  69. package/dist/components/Doc.js +1 -1
  70. package/dist/components/HasProfil.d.ts +4 -0
  71. package/dist/components/HasProfil.d.ts.map +1 -0
  72. package/dist/components/HasProfil.js +39 -0
  73. package/dist/{web → components}/auth/dashboard.d.ts +1 -1
  74. package/dist/components/auth/dashboard.d.ts.map +1 -0
  75. package/dist/components/auth/dashboard.js +74 -0
  76. package/dist/index.d.ts +3 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +3 -1
  79. package/dist/lib/app-branding-data.d.ts +22 -0
  80. package/dist/lib/app-branding-data.d.ts.map +1 -0
  81. package/dist/lib/app-branding-data.js +49 -0
  82. package/dist/lib/auth-email-service.d.ts +57 -0
  83. package/dist/lib/auth-email-service.d.ts.map +1 -0
  84. package/dist/lib/auth-email-service.js +382 -0
  85. package/dist/lib/auth-email-templates.d.ts +2 -0
  86. package/dist/lib/auth-email-templates.d.ts.map +1 -0
  87. package/dist/lib/auth-email-templates.js +1 -0
  88. package/dist/lib/site-url.d.ts +3 -0
  89. package/dist/lib/site-url.d.ts.map +1 -0
  90. package/dist/lib/site-url.js +11 -0
  91. package/dist/sitemap/manifest.d.ts +9 -0
  92. package/dist/sitemap/manifest.d.ts.map +1 -0
  93. package/dist/sitemap/manifest.js +14 -0
  94. package/dist/web/admin/signup-stats.js +3 -3
  95. package/dist/web/admin/user-detail.d.ts.map +1 -1
  96. package/dist/web/admin/user-detail.js +135 -14
  97. package/dist/web/admin/users-by-signup-source.js +2 -2
  98. package/dist/web/admin/users.d.ts.map +1 -1
  99. package/dist/web/admin/users.js +26 -7
  100. package/dist/web/auth/folder.d.ts.map +1 -1
  101. package/dist/web/auth/folder.js +4 -3
  102. package/dist/web/auth/profile.d.ts.map +1 -1
  103. package/dist/web/auth/profile.js +132 -13
  104. package/dist/web/auth/reglage.d.ts.map +1 -1
  105. package/dist/web/auth/reglage.js +15 -8
  106. package/dist/web/public/ResetPassword.d.ts.map +1 -1
  107. package/dist/web/public/ResetPassword.js +172 -2
  108. package/dist/web/public/SignInPage.d.ts.map +1 -1
  109. package/dist/web/public/SignInPage.js +39 -3
  110. package/dist/web/public/SignUpPage.d.ts.map +1 -1
  111. package/dist/web/public/SignUpPage.js +7 -2
  112. package/dist/web/public/auth-code-error.d.ts +2 -0
  113. package/dist/web/public/auth-code-error.d.ts.map +1 -0
  114. package/dist/web/public/auth-code-error.js +14 -0
  115. package/dist/web/public/confirm.d.ts +2 -0
  116. package/dist/web/public/confirm.d.ts.map +1 -0
  117. package/dist/web/public/confirm.js +157 -0
  118. package/package.json +10 -5
  119. package/src/api/admin/signup-stats.ts +2 -1
  120. package/src/api/admin/storage/usage.ts +141 -0
  121. package/src/api/admin/users/[id]/notifications.ts +3 -2
  122. package/src/api/admin/users/[id].ts +3 -2
  123. package/src/api/admin/users/reactivate/[id].ts +88 -0
  124. package/src/api/admin/users/suspend/[id].ts +85 -0
  125. package/src/api/admin/users-by-source.ts +2 -1
  126. package/src/api/admin/users.ts +59 -2
  127. package/src/api/auth/account/email-change.ts +54 -0
  128. package/src/api/auth/account/reset-password.ts +47 -0
  129. package/src/api/auth/check-username.ts +52 -0
  130. package/src/api/auth/establish-session.ts +32 -0
  131. package/src/api/auth/me.ts +29 -7
  132. package/src/api/auth/profile.ts +6 -2
  133. package/src/api/auth/storage/recalculate.ts +108 -0
  134. package/src/api/auth/storage/usage.ts +113 -0
  135. package/src/api/public/add-welcome-bonus.ts +229 -0
  136. package/src/api/public/callback.ts +307 -0
  137. package/src/api/public/reset-password.ts +52 -0
  138. package/src/api/public/set-session.ts +73 -0
  139. package/src/api/public/signin.ts +36 -0
  140. package/src/api/public/signup.ts +44 -37
  141. package/src/api/public/webhook/storage-addon.ts +267 -0
  142. package/src/api/storage.ts +2 -2
  143. package/src/auth.build.config.ts +134 -14
  144. package/src/components/AccountButton.tsx +114 -90
  145. package/src/components/Doc.tsx +47 -9
  146. package/src/components/HasProfil.tsx +63 -0
  147. package/src/{web → components}/auth/dashboard.tsx +64 -20
  148. package/src/i18n/en.json +78 -8
  149. package/src/i18n/es.json +330 -0
  150. package/src/i18n/fr.json +75 -8
  151. package/src/index.ts +3 -1
  152. package/src/lib/app-branding-data.ts +90 -0
  153. package/src/lib/auth-email-service.ts +508 -0
  154. package/src/lib/auth-email-templates.ts +5 -0
  155. package/src/lib/site-url.ts +17 -0
  156. package/src/sitemap/manifest.ts +26 -0
  157. package/src/web/admin/signup-stats.tsx +3 -3
  158. package/src/web/admin/user-detail.tsx +314 -15
  159. package/src/web/admin/users-by-signup-source.tsx +2 -2
  160. package/src/web/admin/users.tsx +50 -14
  161. package/src/web/auth/folder.tsx +23 -5
  162. package/src/web/auth/profile.tsx +227 -13
  163. package/src/web/auth/reglage.tsx +55 -24
  164. package/src/web/public/ResetPassword.tsx +301 -1
  165. package/src/web/public/SignInPage.tsx +43 -3
  166. package/src/web/public/SignUpPage.tsx +14 -5
  167. package/src/web/public/auth-code-error.tsx +49 -0
  168. package/src/web/public/confirm.tsx +195 -0
  169. package/supabase/migrations/20251112000001_auto_profile_and_admin_view.sql +3 -1
  170. package/supabase/migrations/20251127100000_rename_body_to_message.sql +8 -3
  171. package/supabase/migrations/20260120150001_add_user_storage.sql +105 -0
  172. package/supabase/migrations/20260122131200_add_global_addons_system.sql +305 -0
  173. package/supabase/migrations/20260123100000_enable_vector_extension.sql +9 -0
  174. package/supabase/migrations/20260123140000_module_auth_fix_get_user_limits_null.sql +93 -0
  175. package/supabase/migrations/20260123145000_module_auth_fix_circular_storage_base.sql +89 -0
  176. package/supabase/migrations/20260129400000_add_username_to_user_profil.sql +33 -0
  177. package/dist/web/auth/dashboard.d.ts.map +0 -1
  178. package/dist/web/auth/dashboard.js +0 -48
@@ -0,0 +1,49 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ /**
4
+ * Charge les traductions de l'app depuis les fichiers i18n agrégés
5
+ */
6
+ function loadAppTranslations(lang) {
7
+ try {
8
+ const possiblePaths = [
9
+ // Production (Vercel)
10
+ path.join("/app", "i18n", `${lang}.json`),
11
+ // Development - apps/lastbrain
12
+ path.join(process.cwd(), "i18n", `${lang}.json`),
13
+ // Development - depuis packages vers apps/lastbrain
14
+ path.join(process.cwd(), "..", "..", "apps", "lastbrain", "i18n", `${lang}.json`),
15
+ // Development - apps/recipe
16
+ path.join(process.cwd(), "..", "..", "apps", "recipe", "i18n", `${lang}.json`),
17
+ ];
18
+ for (const filePath of possiblePaths) {
19
+ if (fs.existsSync(filePath)) {
20
+ const content = fs.readFileSync(filePath, "utf-8");
21
+ return JSON.parse(content);
22
+ }
23
+ }
24
+ }
25
+ catch (error) {
26
+ console.warn(`[getAppBranding] Could not load translations for ${lang}:`, error);
27
+ }
28
+ return {};
29
+ }
30
+ /**
31
+ * Récupère le branding depuis les clés i18n de l'app
32
+ * Utilise les clés : app.name, app.icon, app.description, app.tagline, etc.
33
+ */
34
+ export function getAppBranding(locale = "en") {
35
+ const loc = locale === "fr" ? "fr" : "en";
36
+ const translations = loadAppTranslations(loc);
37
+ return {
38
+ appName: translations["app.name"] || "LastBrain",
39
+ icon: translations["app.icon"] || "Brain",
40
+ logo: translations["app.icon"] || "Brain",
41
+ description: translations["app.description"] || "",
42
+ tagline: translations["app.tagline"] || "",
43
+ twitterCreator: translations["app.twitter_creator"] || "",
44
+ twitterSite: translations["app.twitter_site"] || "",
45
+ supportEmail: translations["app.email.contact"] || "support@lastbrain.io",
46
+ primaryColor: translations["app.email.color.primary"] || "#3b82f6",
47
+ secondaryColor: translations["app.email.color.secondary"] || "#8b5cf6",
48
+ };
49
+ }
@@ -0,0 +1,57 @@
1
+ interface BaseEmailParams {
2
+ email: string;
3
+ displayName?: string;
4
+ ownerId?: string;
5
+ redirectTo?: string;
6
+ }
7
+ export declare function sendSignupConfirmationEmail(params: {
8
+ email: string;
9
+ password: string;
10
+ fullName?: string;
11
+ signupSource?: string;
12
+ redirectTo?: string;
13
+ next?: string;
14
+ locale?: string;
15
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
16
+ export declare function sendPasswordResetEmail(params: BaseEmailParams & {
17
+ locale?: string;
18
+ next?: string;
19
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
20
+ export declare function sendMagicLinkEmail(params: BaseEmailParams & {
21
+ locale?: string;
22
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
23
+ export declare function sendInviteEmail(params: BaseEmailParams & {
24
+ password?: string;
25
+ locale?: string;
26
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
27
+ export declare function sendEmailChangeConfirmation(params: {
28
+ email: string;
29
+ newEmail: string;
30
+ displayName?: string;
31
+ ownerId?: string;
32
+ redirectTo?: string;
33
+ locale?: string;
34
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
35
+ export declare function sendReauthenticationEmail(params: BaseEmailParams & {
36
+ locale?: string;
37
+ }): Promise<import("@supabase/supabase-js").AuthUser>;
38
+ export declare function fetchCurrentUserOwnerId(): Promise<string | null>;
39
+ export declare function sendWelcomeOnboardingEmail(params: {
40
+ email: string;
41
+ displayName?: string;
42
+ locale?: string;
43
+ ownerId?: string;
44
+ bonusAmountUsd?: number;
45
+ }): Promise<void>;
46
+ /**
47
+ * Send notification to admin when new user signs up
48
+ */
49
+ export declare function sendNewUserNotificationToAdmin(params: {
50
+ userEmail: string;
51
+ displayName?: string;
52
+ userId: string;
53
+ locale?: string;
54
+ bonusAmountUsd?: number;
55
+ }): Promise<void>;
56
+ export {};
57
+ //# sourceMappingURL=auth-email-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-email-service.d.ts","sourceRoot":"","sources":["../../src/lib/auth-email-service.ts"],"names":[],"mappings":"AAqCA,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA0DD,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,qDA6CA;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,qDAwF7D;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,qDAqC9C;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,eAAe,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,qDAgCjE;AAED,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,qDAiBA;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,qDA+B9C;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMtE;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,iBA6BA;AAED;;GAEG;AACH,wBAAsB,8BAA8B,CAAC,MAAM,EAAE;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,iBAwEA"}
@@ -0,0 +1,382 @@
1
+ import { getSupabaseServiceClient, getSupabaseServerClient, } from "@lastbrain/core/server";
2
+ import { sendEmail, getAuthEmailSubject, renderAuthEmailHtml, } from "@lastbrain-labs/module-contact-pro";
3
+ import { getAppBranding } from "./app-branding-data";
4
+ import { logger } from "@lastbrain/core";
5
+ import { cookies } from "next/headers";
6
+ const FALLBACK_SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ||
7
+ process.env.SITE_URL ||
8
+ "http://localhost:3000";
9
+ const NORMALIZED_SITE_URL = FALLBACK_SITE_URL.replace(/\/$/, "");
10
+ const DEFAULT_LOCALE = "fr";
11
+ /**
12
+ * Récupère la langue depuis le cookie NEXT_LOCALE
13
+ */
14
+ async function getLocaleFromCookie() {
15
+ try {
16
+ const cookieStore = await cookies();
17
+ const localeCookie = cookieStore.get("NEXT_LOCALE");
18
+ if (localeCookie?.value && /^[a-z]{2}$/.test(localeCookie.value)) {
19
+ return localeCookie.value;
20
+ }
21
+ }
22
+ catch (error) {
23
+ logger.debug("[getLocaleFromCookie] Could not read cookie:", error);
24
+ }
25
+ return undefined;
26
+ }
27
+ async function generateLink(type, params) {
28
+ const supabase = await getSupabaseServiceClient();
29
+ return supabase.auth.admin.generateLink({ type, ...params });
30
+ }
31
+ async function sendAuthEmail(kind, to, actionLink, options) {
32
+ const branding = getAppBranding(options.locale);
33
+ logger.debug("📧 sendAuthEmail - branding:", {
34
+ locale: options.locale,
35
+ appName: branding.appName,
36
+ icon: branding.icon,
37
+ });
38
+ const html = renderAuthEmailHtml(kind, {
39
+ kind,
40
+ actionLink,
41
+ displayName: options.displayName,
42
+ locale: options.locale,
43
+ appName: branding.appName,
44
+ appIcon: branding.icon,
45
+ appTagline: branding.tagline,
46
+ supportEmail: branding.supportEmail,
47
+ primaryColor: branding.primaryColor,
48
+ secondaryColor: branding.secondaryColor,
49
+ });
50
+ try {
51
+ const result = await sendEmail({
52
+ to,
53
+ subject: getAuthEmailSubject(kind, options.locale),
54
+ html,
55
+ emailType: "transactional",
56
+ ownerId: options.ownerId,
57
+ });
58
+ logger.debug("📧 sendEmail result", { ok: true, result });
59
+ return result;
60
+ }
61
+ catch (err) {
62
+ logger.error("📧 sendEmail error", err);
63
+ throw err;
64
+ }
65
+ }
66
+ export async function sendSignupConfirmationEmail(params) {
67
+ // Récupérer la langue depuis le cookie si non fournie
68
+ const locale = params.locale || (await getLocaleFromCookie()) || DEFAULT_LOCALE;
69
+ // Use magic link for signup so client receives tokens in URL fragment
70
+ // Inclure la locale dans l'URL si pas déjà présente
71
+ let redirectTo = params.redirectTo || `${NORMALIZED_SITE_URL}/${locale}/confirm`;
72
+ // Si un redirectTo personnalisé est fourni sans locale, l'ajouter
73
+ if (params.redirectTo && !params.redirectTo.match(/\/(fr|en|es|de)\//)) {
74
+ const url = new URL(params.redirectTo, NORMALIZED_SITE_URL);
75
+ url.pathname = `/${locale}${url.pathname}`;
76
+ redirectTo = url.toString();
77
+ }
78
+ if (params.next) {
79
+ const sep = redirectTo.includes("?") ? "&" : "?";
80
+ redirectTo = `${redirectTo}${sep}next=${encodeURIComponent(params.next)}`;
81
+ }
82
+ const { data, error } = await generateLink("magiclink", {
83
+ email: params.email,
84
+ options: {
85
+ redirectTo,
86
+ data: {
87
+ full_name: params.fullName,
88
+ signup_source: params.signupSource,
89
+ },
90
+ },
91
+ });
92
+ if (error)
93
+ throw error;
94
+ const actionLink = data?.properties?.action_link || data?.action_link;
95
+ await sendAuthEmail("magic_link", params.email, actionLink, {
96
+ displayName: params.fullName,
97
+ ownerId: data.user?.id,
98
+ locale,
99
+ });
100
+ return data.user;
101
+ }
102
+ export async function sendPasswordResetEmail(params) {
103
+ // Récupérer la langue depuis le cookie si non fournie
104
+ const cookieLocale = await getLocaleFromCookie();
105
+ logger.debug("[sendPasswordResetEmail] locale detection", {
106
+ providedLocale: params.locale,
107
+ cookieLocale,
108
+ });
109
+ // Attempt to infer locale from redirectTo path or use provided locale
110
+ const inferredLocale = (() => {
111
+ // Priority: 1. provided locale, 2. cookie, 3. redirectTo path
112
+ if (params.locale && (params.locale === "fr" || params.locale === "en")) {
113
+ return params.locale;
114
+ }
115
+ if (cookieLocale) {
116
+ return cookieLocale;
117
+ }
118
+ // Otherwise, try to infer from redirectTo URL path
119
+ const url = params.redirectTo || `${FALLBACK_SITE_URL}/api/auth/callback`;
120
+ try {
121
+ const u = new URL(url);
122
+ const seg = u.pathname.split("/").filter(Boolean)[0];
123
+ return seg === "fr" || seg === "en" ? seg : undefined;
124
+ }
125
+ catch {
126
+ return undefined;
127
+ }
128
+ })();
129
+ // Default to the reset-password page so the client can handle tokens directly.
130
+ const targetLocale = inferredLocale || DEFAULT_LOCALE;
131
+ logger.debug("[sendPasswordResetEmail] final locale", {
132
+ inferredLocale,
133
+ targetLocale,
134
+ });
135
+ // Construire le redirectTo avec la locale
136
+ let redirectTo = params.redirectTo ||
137
+ `${NORMALIZED_SITE_URL}/${targetLocale}/reset-password`;
138
+ // Si un redirectTo personnalisé est fourni sans locale, l'ajouter
139
+ if (params.redirectTo && !params.redirectTo.match(/\/(fr|en|es|de)\//)) {
140
+ const url = new URL(params.redirectTo, NORMALIZED_SITE_URL);
141
+ url.pathname = `/${targetLocale}${url.pathname}`;
142
+ redirectTo = url.toString();
143
+ }
144
+ if (params.next) {
145
+ const next = params.next;
146
+ const sep = redirectTo.includes("?") ? "&" : "?";
147
+ redirectTo = `${redirectTo}${sep}next=${encodeURIComponent(next)}`;
148
+ }
149
+ logger.debug("[sendPasswordResetEmail] calling generateLink with redirectTo", {
150
+ redirectTo,
151
+ email: params.email,
152
+ });
153
+ const { data, error } = await generateLink("recovery", {
154
+ email: params.email,
155
+ options: { redirectTo },
156
+ });
157
+ logger.debug("[sendPasswordResetEmail] generateLink returned", {
158
+ action_link: data?.properties?.action_link || data?.action_link,
159
+ supabaseData: data,
160
+ error,
161
+ });
162
+ if (error)
163
+ throw error;
164
+ const actionLink = data?.properties?.action_link || data?.action_link;
165
+ await sendAuthEmail("magic_link", params.email, actionLink, {
166
+ displayName: params.displayName,
167
+ ownerId: params.ownerId,
168
+ locale: targetLocale,
169
+ });
170
+ return data.user;
171
+ }
172
+ export async function sendMagicLinkEmail(params) {
173
+ // Récupérer la langue depuis le cookie si non fournie
174
+ const cookieLocale = await getLocaleFromCookie();
175
+ const inferredLocale = (() => {
176
+ if (params.locale && (params.locale === "fr" || params.locale === "en")) {
177
+ return params.locale;
178
+ }
179
+ if (cookieLocale) {
180
+ return cookieLocale;
181
+ }
182
+ const url = params.redirectTo || `${FALLBACK_SITE_URL}/api/auth/callback`;
183
+ try {
184
+ const u = new URL(url);
185
+ const seg = u.pathname.split("/").filter(Boolean)[0];
186
+ return seg === "fr" || seg === "en" ? seg : undefined;
187
+ }
188
+ catch {
189
+ return undefined;
190
+ }
191
+ })();
192
+ const redirectTo = params.redirectTo || `${NORMALIZED_SITE_URL}/api/public/callback`;
193
+ const { data, error } = await generateLink("magiclink", {
194
+ email: params.email,
195
+ options: { redirectTo },
196
+ });
197
+ if (error)
198
+ throw error;
199
+ const actionLink = data?.properties?.action_link || data?.action_link;
200
+ await sendAuthEmail("magic_link", params.email, actionLink, {
201
+ displayName: params.displayName,
202
+ ownerId: params.ownerId,
203
+ locale: inferredLocale,
204
+ });
205
+ return data.user;
206
+ }
207
+ export async function sendInviteEmail(params) {
208
+ const inferredLocale = (() => {
209
+ if (params.locale && (params.locale === "fr" || params.locale === "en")) {
210
+ return params.locale;
211
+ }
212
+ const url = params.redirectTo || `${FALLBACK_SITE_URL}/api/auth/callback`;
213
+ try {
214
+ const u = new URL(url);
215
+ const seg = u.pathname.split("/").filter(Boolean)[0];
216
+ return seg === "fr" || seg === "en" ? seg : undefined;
217
+ }
218
+ catch {
219
+ return undefined;
220
+ }
221
+ })();
222
+ const redirectTo = params.redirectTo || `${NORMALIZED_SITE_URL}/api/public/callback`;
223
+ const { data, error } = await generateLink("invite", {
224
+ email: params.email,
225
+ password: params.password,
226
+ options: { redirectTo },
227
+ });
228
+ if (error)
229
+ throw error;
230
+ const actionLink = data?.properties?.action_link || data?.action_link;
231
+ await sendAuthEmail("invite", params.email, actionLink, {
232
+ displayName: params.displayName,
233
+ ownerId: params.ownerId ?? data.user?.id,
234
+ locale: inferredLocale,
235
+ });
236
+ return data.user;
237
+ }
238
+ export async function sendEmailChangeConfirmation(params) {
239
+ const redirectTo = params.redirectTo || `${NORMALIZED_SITE_URL}/api/auth/callback`;
240
+ const { data, error } = await generateLink("email_change", {
241
+ email: params.email,
242
+ newEmail: params.newEmail,
243
+ options: { redirectTo },
244
+ });
245
+ if (error)
246
+ throw error;
247
+ const actionLink = data?.properties?.action_link || data?.action_link;
248
+ await sendAuthEmail("confirm_email_change", params.newEmail, actionLink, {
249
+ displayName: params.displayName,
250
+ ownerId: params.ownerId,
251
+ locale: params.locale,
252
+ });
253
+ return data.user;
254
+ }
255
+ export async function sendReauthenticationEmail(params) {
256
+ const inferredLocale = (() => {
257
+ if (params.locale && (params.locale === "fr" || params.locale === "en")) {
258
+ return params.locale;
259
+ }
260
+ const url = params.redirectTo || `${FALLBACK_SITE_URL}/api/auth/callback`;
261
+ try {
262
+ const u = new URL(url);
263
+ const seg = u.pathname.split("/").filter(Boolean)[0];
264
+ return seg === "fr" || seg === "en" ? seg : undefined;
265
+ }
266
+ catch {
267
+ return undefined;
268
+ }
269
+ })();
270
+ const redirectTo = params.redirectTo || `${NORMALIZED_SITE_URL}/api/auth/callback`;
271
+ const { data, error } = await generateLink("reauthentication", {
272
+ email: params.email,
273
+ options: { redirectTo },
274
+ });
275
+ if (error)
276
+ throw error;
277
+ const actionLink = data?.properties?.action_link || data?.action_link;
278
+ await sendAuthEmail("reauthentication", params.email, actionLink, {
279
+ displayName: params.displayName,
280
+ ownerId: params.ownerId,
281
+ locale: inferredLocale,
282
+ });
283
+ return data.user;
284
+ }
285
+ export async function fetchCurrentUserOwnerId() {
286
+ const supabase = await getSupabaseServerClient();
287
+ const { data: { user }, } = await supabase.auth.getUser();
288
+ return user?.id || null;
289
+ }
290
+ export async function sendWelcomeOnboardingEmail(params) {
291
+ const branding = getAppBranding(params.locale);
292
+ // Render the welcome onboarding email using the template from module-contact-pro
293
+ const html = renderAuthEmailHtml("welcome_onboarding", {
294
+ kind: "welcome_onboarding",
295
+ actionLink: `${FALLBACK_SITE_URL}/`,
296
+ displayName: params.displayName,
297
+ locale: params.locale,
298
+ appName: branding.appName,
299
+ appIcon: branding.icon,
300
+ appTagline: branding.tagline,
301
+ supportEmail: branding.supportEmail,
302
+ bonusAmountUsd: params.bonusAmountUsd,
303
+ });
304
+ await sendEmail({
305
+ to: params.email,
306
+ subject: getAuthEmailSubject("welcome_onboarding", params.locale, params.bonusAmountUsd),
307
+ html,
308
+ emailType: "transactional",
309
+ ownerId: params.ownerId,
310
+ });
311
+ return;
312
+ }
313
+ /**
314
+ * Send notification to admin when new user signs up
315
+ */
316
+ export async function sendNewUserNotificationToAdmin(params) {
317
+ const ADMIN_EMAIL = "contact@lastbrain.io";
318
+ const branding = getAppBranding("fr");
319
+ const html = `
320
+ <!DOCTYPE html>
321
+ <html>
322
+ <head>
323
+ <meta charset="utf-8">
324
+ <style>
325
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
326
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
327
+ .header { background: #4F46E5; color: white; padding: 20px; border-radius: 8px 8px 0 0; }
328
+ .content { background: #f9fafb; padding: 30px; border-radius: 0 0 8px 8px; }
329
+ .info-row { margin: 10px 0; padding: 10px; background: white; border-radius: 4px; }
330
+ .label { font-weight: 600; color: #6B7280; }
331
+ .value { color: #111827; }
332
+ </style>
333
+ </head>
334
+ <body>
335
+ <div class="container">
336
+ <div class="header">
337
+ <h2>🎉 Nouvelle inscription - ${branding.appName}</h2>
338
+ </div>
339
+ <div class="content">
340
+ <p>Un nouvel utilisateur vient de s'inscrire sur la plateforme :</p>
341
+
342
+ <div class="info-row">
343
+ <span class="label">Email :</span>
344
+ <span class="value">${params.userEmail}</span>
345
+ </div>
346
+
347
+ ${params.displayName ? `<div class="info-row"><span class="label">Nom :</span><span class="value">${params.displayName}</span></div>` : ""}
348
+
349
+ <div class="info-row">
350
+ <span class="label">User ID :</span>
351
+ <span class="value">${params.userId}</span>
352
+ </div>
353
+
354
+ <div class="info-row">
355
+ <span class="label">Langue :</span>
356
+ <span class="value">${params.locale || "non définie"}</span>
357
+ </div>
358
+
359
+ ${params.bonusAmountUsd ? `<div class="info-row"><span class="label">Bonus accordé :</span><span class="value">$${params.bonusAmountUsd}</span></div>` : ""}
360
+
361
+ <div class="info-row">
362
+ <span class="label">Date :</span>
363
+ <span class="value">${new Date().toLocaleString("fr-FR")}</span>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </body>
368
+ </html>
369
+ `;
370
+ try {
371
+ await sendEmail({
372
+ to: ADMIN_EMAIL,
373
+ subject: `[${branding.appName}] Nouvelle inscription : ${params.userEmail}`,
374
+ html,
375
+ emailType: "transactional",
376
+ });
377
+ logger.info(`[sendNewUserNotificationToAdmin] Notification sent to ${ADMIN_EMAIL} for user ${params.userId}`);
378
+ }
379
+ catch (error) {
380
+ logger.error("[sendNewUserNotificationToAdmin] Failed to send admin notification:", error);
381
+ }
382
+ }
@@ -0,0 +1,2 @@
1
+ export { type AuthEmailKind, getAuthEmailSubject, renderAuthEmailHtml, } from "@lastbrain-labs/module-contact-pro";
2
+ //# sourceMappingURL=auth-email-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-email-templates.d.ts","sourceRoot":"","sources":["../../src/lib/auth-email-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,oCAAoC,CAAC"}
@@ -0,0 +1 @@
1
+ export { getAuthEmailSubject, renderAuthEmailHtml, } from "@lastbrain-labs/module-contact-pro";
@@ -0,0 +1,3 @@
1
+ import { NextRequest } from "next/server";
2
+ export declare function resolveSiteUrl(request: NextRequest): string;
3
+ //# sourceMappingURL=site-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-url.d.ts","sourceRoot":"","sources":["../../src/lib/site-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,UASlD"}
@@ -0,0 +1,11 @@
1
+ const FALLBACK_SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ||
2
+ process.env.SITE_URL ||
3
+ "http://localhost:3000";
4
+ export function resolveSiteUrl(request) {
5
+ const origin = process.env.NEXT_PUBLIC_SITE_URL ||
6
+ process.env.SITE_URL ||
7
+ request.headers.get("origin") ||
8
+ request.nextUrl.origin ||
9
+ FALLBACK_SITE_URL;
10
+ return origin.replace(/\/$/, "");
11
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Manifest des sitemaps du module auth
3
+ * Déclare tous les sitemaps enfants à générer
4
+ * Auth module only has static pages (signin, signup, etc.)
5
+ */
6
+ import type { SitemapManifest, SitemapChild, SitemapChildKind } from "@lastbrain/core";
7
+ export type { SitemapManifest, SitemapChild, SitemapChildKind };
8
+ export declare const sitemapManifest: SitemapManifest;
9
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/sitemap/manifest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAChE,eAAO,MAAM,eAAe,EAAE,eAapB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const sitemapManifest = {
2
+ module: "auth",
3
+ enabled: true,
4
+ includePublicPagesFromBuildConfig: true,
5
+ children: [
6
+ // Pages statiques (générées auto depuis build.config)
7
+ // Includes: /signin, /signup, /reset-password, /confirm, etc.
8
+ {
9
+ id: "static",
10
+ path: ":lang/static.xml",
11
+ kind: "static",
12
+ },
13
+ ],
14
+ };
@@ -30,16 +30,16 @@ export function SignupStatsPage() {
30
30
  }
31
31
  };
32
32
  if (loading) {
33
- return (_jsx("div", { className: "flex justify-center items-center min-h-screen", children: _jsx(Spinner, { size: "lg", label: t("signup_stats.loading") || "Chargement des statistiques..." }) }));
33
+ return (_jsx("div", { className: "mt-16 flex justify-center items-center min-h-screen", children: _jsx(Spinner, { size: "lg", label: t("signup_stats.loading") || "Chargement des statistiques..." }) }));
34
34
  }
35
35
  if (error || !stats) {
36
- return (_jsx("div", { className: "p-6", children: _jsx(Card, { className: "border border-danger-200 bg-danger-50/50", children: _jsx(CardBody, { children: _jsx("p", { className: "text-danger-600", children: error || "Erreur de chargement" }) }) }) }));
36
+ return (_jsx("div", { className: "mt-16 p-6", children: _jsx(Card, { className: "border border-danger-200 bg-danger-50/50", children: _jsx(CardBody, { children: _jsx("p", { className: "text-danger-600", children: error || "Erreur de chargement" }) }) }) }));
37
37
  }
38
38
  const lastbrainPercentage = ((stats.bySource.lastbrain / stats.total) *
39
39
  100).toFixed(1);
40
40
  const recipePercentage = ((stats.bySource.recipe / stats.total) *
41
41
  100).toFixed(1);
42
- return (_jsxs("div", { className: "space-y-6 px-2 md:p-6", children: [_jsxs("div", { className: "flex items-center gap-2 mb-8", children: [_jsx(BarChart3, { size: 28, className: "text-primary-600" }), _jsx("h1", { className: "text-3xl font-bold", children: t("signup_stats.title") || "Statistiques d'inscriptions" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-12", children: [_jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-medium text-primary-600 dark:text-primary-400", children: t("signup_stats.total") || "Total d'inscriptions" }), _jsx(TrendingUp, { size: 20, className: "text-primary-600" })] }), _jsx("p", { className: "text-4xl font-bold text-primary-700 dark:text-primary-300", children: stats.total })] }) }), _jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("span", { className: "text-sm font-medium text-secondary-600 dark:text-secondary-400", children: "Inscriptions LastBrain" }) }), _jsxs("div", { className: "flex items-end justify-between gap-2", children: [_jsx("p", { className: "text-4xl font-bold text-secondary-700 dark:text-secondary-300", children: stats.bySource.lastbrain }), _jsxs(Chip, { size: "sm", color: "secondary", variant: "flat", children: [lastbrainPercentage, "%"] })] })] }) }), _jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("span", { className: "text-sm font-medium text-success-600 dark:text-success-400", children: "Inscriptions Recipe" }) }), _jsxs("div", { className: "flex items-end justify-between gap-2", children: [_jsx("p", { className: "text-4xl font-bold text-success-700 dark:text-success-300", children: stats.bySource.recipe }), _jsxs(Chip, { size: "sm", color: "success", variant: "flat", children: [recipePercentage, "%"] })] })] }) })] }), _jsxs(Tabs, { "aria-label": "Vues des statistiques", color: "primary", variant: "bordered", children: [_jsx(Tab, { title: "Par Source", children: _jsxs(Card, { className: "mt-6", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: "R\u00E9sum\u00E9 par source" }) }), _jsx(CardBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "p-4 rounded-lg border border-secondary-800 dark:border-secondary-800", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("span", { className: "font-medium text-secondary-700 dark:text-secondary-300", children: "LastBrain" }), _jsxs(Chip, { size: "sm", color: "secondary", variant: "flat", children: [stats.bySource.lastbrain, " inscriptions"] })] }), _jsx("div", { className: "w-full bg-default-200 rounded-full h-2 dark:bg-default-700", children: _jsx("div", { className: "bg-secondary-500 h-2 rounded-full", style: {
42
+ return (_jsxs("div", { className: "mt-16 space-y-6 px-2 md:p-6", children: [_jsxs("div", { className: "flex items-center gap-2 mb-8", children: [_jsx(BarChart3, { size: 28, className: "text-primary-600" }), _jsx("h1", { className: "text-3xl font-bold", children: t("signup_stats.title") || "Statistiques d'inscriptions" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-12", children: [_jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-medium text-primary-600 dark:text-primary-400", children: t("signup_stats.total") || "Total d'inscriptions" }), _jsx(TrendingUp, { size: 20, className: "text-primary-600" })] }), _jsx("p", { className: "text-4xl font-bold text-primary-700 dark:text-primary-300", children: stats.total })] }) }), _jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("span", { className: "text-sm font-medium text-secondary-600 dark:text-secondary-400", children: "Inscriptions LastBrain" }) }), _jsxs("div", { className: "flex items-end justify-between gap-2", children: [_jsx("p", { className: "text-4xl font-bold text-secondary-700 dark:text-secondary-300", children: stats.bySource.lastbrain }), _jsxs(Chip, { size: "sm", color: "secondary", variant: "flat", children: [lastbrainPercentage, "%"] })] })] }) }), _jsx(Card, { className: " ", children: _jsxs(CardBody, { className: "gap-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("span", { className: "text-sm font-medium text-success-600 dark:text-success-400", children: "Inscriptions Recipe" }) }), _jsxs("div", { className: "flex items-end justify-between gap-2", children: [_jsx("p", { className: "text-4xl font-bold text-success-700 dark:text-success-300", children: stats.bySource.recipe }), _jsxs(Chip, { size: "sm", color: "success", variant: "flat", children: [recipePercentage, "%"] })] })] }) })] }), _jsxs(Tabs, { "aria-label": "Vues des statistiques", color: "primary", variant: "bordered", children: [_jsx(Tab, { title: "Par Source", children: _jsxs(Card, { className: "mt-6", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: "R\u00E9sum\u00E9 par source" }) }), _jsx(CardBody, { children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "p-4 rounded-lg border border-secondary-800 dark:border-secondary-800", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("span", { className: "font-medium text-secondary-700 dark:text-secondary-300", children: "LastBrain" }), _jsxs(Chip, { size: "sm", color: "secondary", variant: "flat", children: [stats.bySource.lastbrain, " inscriptions"] })] }), _jsx("div", { className: "w-full bg-default-200 rounded-full h-2 dark:bg-default-700", children: _jsx("div", { className: "bg-secondary-500 h-2 rounded-full", style: {
43
43
  width: `${(stats.bySource.lastbrain / stats.total) * 100}%`,
44
44
  } }) }), _jsxs("p", { className: "text-xs text-default-500 mt-2", children: [lastbrainPercentage, "% du total"] })] }), _jsxs("div", { className: "p-4 rounded-lg border border-success-200 dark:border-success-800", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("span", { className: "font-medium text-success-700 dark:text-success-300", children: "Recipe" }), _jsxs(Chip, { size: "sm", color: "success", variant: "flat", children: [stats.bySource.recipe, " inscriptions"] })] }), _jsx("div", { className: "w-full bg-default-200 rounded-full h-2 dark:bg-default-700", children: _jsx("div", { className: "bg-success-500 h-2 rounded-full", style: {
45
45
  width: `${(stats.bySource.recipe / stats.total) * 100}%`,
@@ -1 +1 @@
1
- {"version":3,"file":"user-detail.d.ts","sourceRoot":"","sources":["../../../src/web/admin/user-detail.tsx"],"names":[],"mappings":"AAyBA,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAkCD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,cAAmB,GACpB,EAAE,mBAAmB,2CAulBrB"}
1
+ {"version":3,"file":"user-detail.d.ts","sourceRoot":"","sources":["../../../src/web/admin/user-detail.tsx"],"names":[],"mappings":"AAgCA,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAkCD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,cAAmB,GACpB,EAAE,mBAAmB,2CA23BrB"}