@xk2800/nextjs-template 0.7.1-beta.1 → 0.7.1-beta.10

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 (131) hide show
  1. package/README.md +32 -30
  2. package/components/auth/deviceCheck.tsx +47 -6
  3. package/components/auth/oneTap.tsx +5 -5
  4. package/components/dashboard/admin/adminUserConsentCard.tsx +79 -0
  5. package/components/dashboard/admin/adminUserDevicesCard.tsx +80 -0
  6. package/components/dashboard/admin/broadcastActions.tsx +148 -0
  7. package/components/dashboard/admin/confirmSendDialog.tsx +44 -0
  8. package/components/dashboard/admin/marketingEmailForm.tsx +115 -0
  9. package/components/dashboard/admin/marketingSection.tsx +67 -0
  10. package/components/dashboard/admin/subscribersCard.tsx +62 -0
  11. package/components/dashboard/admin/systemSettingsForm.tsx +33 -0
  12. package/components/dashboard/admin/systemSettingsSection.tsx +8 -2
  13. package/components/dashboard/errorFeedback.tsx +2 -4
  14. package/components/dashboard/feedbackDialog.tsx +4 -4
  15. package/components/dashboard/marketingUpdatesCard.tsx +62 -0
  16. package/components/dashboard/marketingUpdatesSection.tsx +10 -0
  17. package/components/site/cookie-banner.tsx +109 -7
  18. package/components/ui/alert-dialog.tsx +1 -1
  19. package/components/ui/badge.tsx +1 -1
  20. package/components/ui/card.tsx +1 -1
  21. package/components/ui/data-table.tsx +4 -1
  22. package/components/ui/dialog.tsx +1 -1
  23. package/components/ui/dropdown-menu.tsx +2 -2
  24. package/components/ui/file-upload.tsx +120 -2
  25. package/components/ui/select.tsx +189 -0
  26. package/components/ui/switch.tsx +2 -2
  27. package/dist/activity/logger.js +36 -0
  28. package/dist/activity/logger.js.map +1 -1
  29. package/dist/activity/logger.mjs +4 -4
  30. package/dist/activity/queries.d.mts +1 -1
  31. package/dist/activity/queries.d.ts +1 -1
  32. package/dist/activity/queries.js +36 -0
  33. package/dist/activity/queries.js.map +1 -1
  34. package/dist/activity/queries.mjs +3 -3
  35. package/dist/admin/queries.d.mts +19 -1
  36. package/dist/admin/queries.d.ts +19 -1
  37. package/dist/admin/queries.js +64 -0
  38. package/dist/admin/queries.js.map +1 -1
  39. package/dist/admin/queries.mjs +26 -5
  40. package/dist/admin/queries.mjs.map +1 -1
  41. package/dist/api/feedback.js +53 -2
  42. package/dist/api/feedback.js.map +1 -1
  43. package/dist/api/feedback.mjs +9 -8
  44. package/dist/api/feedback.mjs.map +1 -1
  45. package/dist/api/marketing.d.mts +36 -0
  46. package/dist/api/marketing.d.ts +36 -0
  47. package/dist/api/marketing.js +1357 -0
  48. package/dist/api/marketing.js.map +1 -0
  49. package/dist/api/marketing.mjs +170 -0
  50. package/dist/api/marketing.mjs.map +1 -0
  51. package/dist/auth/helpers.d.mts +3 -3
  52. package/dist/auth/helpers.d.ts +3 -3
  53. package/dist/auth/helpers.js +51 -0
  54. package/dist/auth/helpers.js.map +1 -1
  55. package/dist/auth/helpers.mjs +9 -64
  56. package/dist/auth/helpers.mjs.map +1 -1
  57. package/dist/auth/index.js +51 -0
  58. package/dist/auth/index.js.map +1 -1
  59. package/dist/auth/index.mjs +8 -7
  60. package/dist/auth-client/index.d.mts +16 -16
  61. package/dist/auth-client/index.d.ts +16 -16
  62. package/dist/{chunk-KAEA35OC.mjs → chunk-6MDQRT6U.mjs} +4 -4
  63. package/dist/{chunk-KAEA35OC.mjs.map → chunk-6MDQRT6U.mjs.map} +1 -1
  64. package/dist/{chunk-N6ECRCHY.mjs → chunk-6OAHCEDJ.mjs} +4 -3
  65. package/dist/chunk-6OAHCEDJ.mjs.map +1 -0
  66. package/dist/chunk-6Z3WUSDJ.mjs +62 -0
  67. package/dist/chunk-6Z3WUSDJ.mjs.map +1 -0
  68. package/dist/{chunk-VQ5B4MA7.mjs → chunk-7F2POZM6.mjs} +4 -4
  69. package/dist/{chunk-VQ5B4MA7.mjs.map → chunk-7F2POZM6.mjs.map} +1 -1
  70. package/dist/{chunk-XWVAJXAG.mjs → chunk-7QVIOQ47.mjs} +23 -19
  71. package/dist/chunk-7QVIOQ47.mjs.map +1 -0
  72. package/dist/chunk-C6RGN6QA.mjs +60 -0
  73. package/dist/chunk-C6RGN6QA.mjs.map +1 -0
  74. package/dist/chunk-OVKVD2HH.mjs +25 -0
  75. package/dist/chunk-OVKVD2HH.mjs.map +1 -0
  76. package/dist/{chunk-3MGEAGHU.mjs → chunk-P2G23JMY.mjs} +11 -6
  77. package/dist/chunk-P2G23JMY.mjs.map +1 -0
  78. package/dist/{chunk-TMJYAOC2.mjs → chunk-TKWNEKK6.mjs} +8 -2
  79. package/dist/chunk-TKWNEKK6.mjs.map +1 -0
  80. package/dist/{chunk-ZQCALTKB.mjs → chunk-TR5XFBTD.mjs} +34 -4
  81. package/dist/chunk-TR5XFBTD.mjs.map +1 -0
  82. package/dist/{chunk-WZDAMCUX.mjs → chunk-ZJMFE5B2.mjs} +4 -4
  83. package/dist/{chunk-WZDAMCUX.mjs.map → chunk-ZJMFE5B2.mjs.map} +1 -1
  84. package/dist/config/env.d.mts +2 -0
  85. package/dist/config/env.d.ts +2 -0
  86. package/dist/config/env.js +6 -0
  87. package/dist/config/env.js.map +1 -1
  88. package/dist/config/env.mjs +1 -1
  89. package/dist/db/index.d.mts +1 -1
  90. package/dist/db/index.d.ts +1 -1
  91. package/dist/db/index.js +36 -0
  92. package/dist/db/index.js.map +1 -1
  93. package/dist/db/index.mjs +3 -3
  94. package/dist/db/schema.d.mts +1 -1
  95. package/dist/db/schema.d.ts +1 -1
  96. package/dist/db/schema.js +30 -0
  97. package/dist/db/schema.js.map +1 -1
  98. package/dist/db/schema.mjs +1 -1
  99. package/dist/errors.js +36 -0
  100. package/dist/errors.js.map +1 -1
  101. package/dist/errors.mjs +4 -4
  102. package/dist/marketing/index.d.mts +35 -0
  103. package/dist/marketing/index.d.ts +35 -0
  104. package/dist/marketing/index.js +125 -0
  105. package/dist/marketing/index.js.map +1 -0
  106. package/dist/marketing/index.mjs +6 -0
  107. package/dist/marketing/index.mjs.map +1 -0
  108. package/dist/{schema-UuqxoOzS.d.mts → schema-CN_Y6EXv.d.mts} +148 -2
  109. package/dist/{schema-UuqxoOzS.d.ts → schema-CN_Y6EXv.d.ts} +148 -2
  110. package/dist/sessions/queries.js +36 -0
  111. package/dist/sessions/queries.js.map +1 -1
  112. package/dist/sessions/queries.mjs +3 -3
  113. package/dist/settings/queries.d.mts +4 -3
  114. package/dist/settings/queries.d.ts +4 -3
  115. package/dist/settings/queries.js +42 -0
  116. package/dist/settings/queries.js.map +1 -1
  117. package/dist/settings/queries.mjs +4 -4
  118. package/dist/users/queries.js +36 -0
  119. package/dist/users/queries.js.map +1 -1
  120. package/dist/users/queries.mjs +3 -3
  121. package/lib/cookie-consent.ts +72 -20
  122. package/lib/cookie-names.ts +47 -0
  123. package/lib/device-fingerprint.ts +66 -10
  124. package/lib/recalculate-columns.ts +2 -0
  125. package/package.json +28 -3
  126. package/styles/theme.css +17 -3
  127. package/dist/chunk-3MGEAGHU.mjs.map +0 -1
  128. package/dist/chunk-N6ECRCHY.mjs.map +0 -1
  129. package/dist/chunk-TMJYAOC2.mjs.map +0 -1
  130. package/dist/chunk-XWVAJXAG.mjs.map +0 -1
  131. package/dist/chunk-ZQCALTKB.mjs.map +0 -1
@@ -826,8 +826,8 @@ declare const authClient: better_auth_react.ReactAuthClient<{
826
826
  $Infer: {
827
827
  body: {
828
828
  permissions: {
829
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
830
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
829
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
830
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
831
831
  };
832
832
  } & {
833
833
  userId?: string | undefined;
@@ -916,8 +916,8 @@ declare const authClient: better_auth_react.ReactAuthClient<{
916
916
  admin: {
917
917
  checkRolePermission: <R extends "user" | "admin">(data: {
918
918
  permissions: {
919
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
920
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
919
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
920
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
921
921
  };
922
922
  } & {
923
923
  role: R;
@@ -2423,8 +2423,8 @@ declare const useSession: () => {
2423
2423
  banned: boolean | null | undefined;
2424
2424
  } & {
2425
2425
  role?: string | null | undefined;
2426
- banExpires?: Date | null | undefined;
2427
2426
  banReason?: string | null | undefined;
2427
+ banExpires?: Date | null | undefined;
2428
2428
  } & {
2429
2429
  twoFactorEnabled: boolean | null | undefined;
2430
2430
  } & {}>;
@@ -2450,7 +2450,7 @@ declare const useSession: () => {
2450
2450
  };
2451
2451
  declare const signIn: {
2452
2452
  social: <FetchOptions extends better_auth.ClientFetchOption<Partial<{
2453
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2453
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2454
2454
  callbackURL?: string | undefined;
2455
2455
  newUserCallbackURL?: string | undefined;
2456
2456
  errorCallbackURL?: string | undefined;
@@ -2475,7 +2475,7 @@ declare const signIn: {
2475
2475
  additionalParams?: Record<string, string> | undefined;
2476
2476
  additionalData?: Record<string, any> | undefined;
2477
2477
  }> & Record<string, any>, Partial<Record<string, any>> & Record<string, any>, Record<string, string | undefined> | undefined>>(data_0: better_auth.Prettify<Omit<{
2478
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2478
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2479
2479
  callbackURL?: string | undefined;
2480
2480
  newUserCallbackURL?: string | undefined;
2481
2481
  errorCallbackURL?: string | undefined;
@@ -2502,7 +2502,7 @@ declare const signIn: {
2502
2502
  } & {
2503
2503
  fetchOptions?: FetchOptions | undefined;
2504
2504
  }, "provider"> & {
2505
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2505
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2506
2506
  fetchOptions?: FetchOptions | undefined;
2507
2507
  }>, data_1?: FetchOptions | undefined) => Promise<_better_fetch_fetch.BetterFetchResponse<{
2508
2508
  redirect: boolean;
@@ -2533,8 +2533,8 @@ declare const signIn: {
2533
2533
  banned: boolean | null | undefined;
2534
2534
  } & {
2535
2535
  role?: string | null | undefined;
2536
- banExpires?: Date | null | undefined;
2537
2536
  banReason?: string | null | undefined;
2537
+ banExpires?: Date | null | undefined;
2538
2538
  } & {
2539
2539
  twoFactorEnabled: boolean | null | undefined;
2540
2540
  } & {}>;
@@ -2581,8 +2581,8 @@ declare const signIn: {
2581
2581
  banned: boolean | null | undefined;
2582
2582
  } & {
2583
2583
  role?: string | null | undefined;
2584
- banExpires?: Date | null | undefined;
2585
2584
  banReason?: string | null | undefined;
2585
+ banExpires?: Date | null | undefined;
2586
2586
  } & {
2587
2587
  twoFactorEnabled: boolean | null | undefined;
2588
2588
  } & {}>;
@@ -3481,8 +3481,8 @@ declare const signUp: {
3481
3481
  $Infer: {
3482
3482
  body: {
3483
3483
  permissions: {
3484
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
3485
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3484
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3485
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
3486
3486
  };
3487
3487
  } & {
3488
3488
  userId?: string | undefined;
@@ -3571,8 +3571,8 @@ declare const signUp: {
3571
3571
  admin: {
3572
3572
  checkRolePermission: <R extends "user" | "admin">(data: {
3573
3573
  permissions: {
3574
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
3575
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3574
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3575
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
3576
3576
  };
3577
3577
  } & {
3578
3578
  role: R;
@@ -5155,8 +5155,8 @@ declare const signUp: {
5155
5155
  banned: boolean | null | undefined;
5156
5156
  } & {
5157
5157
  role?: string | null | undefined;
5158
- banExpires?: Date | null | undefined;
5159
5158
  banReason?: string | null | undefined;
5159
+ banExpires?: Date | null | undefined;
5160
5160
  } & {
5161
5161
  twoFactorEnabled: boolean | null | undefined;
5162
5162
  } & {}>;
@@ -5184,8 +5184,8 @@ declare const signUp: {
5184
5184
  banned: boolean | null | undefined;
5185
5185
  } & {
5186
5186
  role?: string | null | undefined;
5187
- banExpires?: Date | null | undefined;
5188
5187
  banReason?: string | null | undefined;
5188
+ banExpires?: Date | null | undefined;
5189
5189
  } & {
5190
5190
  twoFactorEnabled: boolean | null | undefined;
5191
5191
  } & {}>;
@@ -826,8 +826,8 @@ declare const authClient: better_auth_react.ReactAuthClient<{
826
826
  $Infer: {
827
827
  body: {
828
828
  permissions: {
829
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
830
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
829
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
830
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
831
831
  };
832
832
  } & {
833
833
  userId?: string | undefined;
@@ -916,8 +916,8 @@ declare const authClient: better_auth_react.ReactAuthClient<{
916
916
  admin: {
917
917
  checkRolePermission: <R extends "user" | "admin">(data: {
918
918
  permissions: {
919
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
920
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
919
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
920
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
921
921
  };
922
922
  } & {
923
923
  role: R;
@@ -2423,8 +2423,8 @@ declare const useSession: () => {
2423
2423
  banned: boolean | null | undefined;
2424
2424
  } & {
2425
2425
  role?: string | null | undefined;
2426
- banExpires?: Date | null | undefined;
2427
2426
  banReason?: string | null | undefined;
2427
+ banExpires?: Date | null | undefined;
2428
2428
  } & {
2429
2429
  twoFactorEnabled: boolean | null | undefined;
2430
2430
  } & {}>;
@@ -2450,7 +2450,7 @@ declare const useSession: () => {
2450
2450
  };
2451
2451
  declare const signIn: {
2452
2452
  social: <FetchOptions extends better_auth.ClientFetchOption<Partial<{
2453
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2453
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2454
2454
  callbackURL?: string | undefined;
2455
2455
  newUserCallbackURL?: string | undefined;
2456
2456
  errorCallbackURL?: string | undefined;
@@ -2475,7 +2475,7 @@ declare const signIn: {
2475
2475
  additionalParams?: Record<string, string> | undefined;
2476
2476
  additionalData?: Record<string, any> | undefined;
2477
2477
  }> & Record<string, any>, Partial<Record<string, any>> & Record<string, any>, Record<string, string | undefined> | undefined>>(data_0: better_auth.Prettify<Omit<{
2478
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2478
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2479
2479
  callbackURL?: string | undefined;
2480
2480
  newUserCallbackURL?: string | undefined;
2481
2481
  errorCallbackURL?: string | undefined;
@@ -2502,7 +2502,7 @@ declare const signIn: {
2502
2502
  } & {
2503
2503
  fetchOptions?: FetchOptions | undefined;
2504
2504
  }, "provider"> & {
2505
- provider: "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat" | (string & {});
2505
+ provider: (string & {}) | "github" | "apple" | "atlassian" | "cloudflare" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "huggingface" | "slack" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "linear" | "linkedin" | "gitlab" | "tiktok" | "reddit" | "roblox" | "salesforce" | "vk" | "zoom" | "notion" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
2506
2506
  fetchOptions?: FetchOptions | undefined;
2507
2507
  }>, data_1?: FetchOptions | undefined) => Promise<_better_fetch_fetch.BetterFetchResponse<{
2508
2508
  redirect: boolean;
@@ -2533,8 +2533,8 @@ declare const signIn: {
2533
2533
  banned: boolean | null | undefined;
2534
2534
  } & {
2535
2535
  role?: string | null | undefined;
2536
- banExpires?: Date | null | undefined;
2537
2536
  banReason?: string | null | undefined;
2537
+ banExpires?: Date | null | undefined;
2538
2538
  } & {
2539
2539
  twoFactorEnabled: boolean | null | undefined;
2540
2540
  } & {}>;
@@ -2581,8 +2581,8 @@ declare const signIn: {
2581
2581
  banned: boolean | null | undefined;
2582
2582
  } & {
2583
2583
  role?: string | null | undefined;
2584
- banExpires?: Date | null | undefined;
2585
2584
  banReason?: string | null | undefined;
2585
+ banExpires?: Date | null | undefined;
2586
2586
  } & {
2587
2587
  twoFactorEnabled: boolean | null | undefined;
2588
2588
  } & {}>;
@@ -3481,8 +3481,8 @@ declare const signUp: {
3481
3481
  $Infer: {
3482
3482
  body: {
3483
3483
  permissions: {
3484
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
3485
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3484
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3485
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
3486
3486
  };
3487
3487
  } & {
3488
3488
  userId?: string | undefined;
@@ -3571,8 +3571,8 @@ declare const signUp: {
3571
3571
  admin: {
3572
3572
  checkRolePermission: <R extends "user" | "admin">(data: {
3573
3573
  permissions: {
3574
- readonly user?: ("set-role" | "delete" | "update" | "list" | "create" | "ban" | "impersonate" | "impersonate-admins" | "set-password" | "set-email" | "get")[] | undefined;
3575
- readonly session?: ("delete" | "list" | "revoke")[] | undefined;
3574
+ readonly user?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3575
+ readonly session?: ("list" | "delete" | "revoke")[] | undefined;
3576
3576
  };
3577
3577
  } & {
3578
3578
  role: R;
@@ -5155,8 +5155,8 @@ declare const signUp: {
5155
5155
  banned: boolean | null | undefined;
5156
5156
  } & {
5157
5157
  role?: string | null | undefined;
5158
- banExpires?: Date | null | undefined;
5159
5158
  banReason?: string | null | undefined;
5159
+ banExpires?: Date | null | undefined;
5160
5160
  } & {
5161
5161
  twoFactorEnabled: boolean | null | undefined;
5162
5162
  } & {}>;
@@ -5184,8 +5184,8 @@ declare const signUp: {
5184
5184
  banned: boolean | null | undefined;
5185
5185
  } & {
5186
5186
  role?: string | null | undefined;
5187
- banExpires?: Date | null | undefined;
5188
5187
  banReason?: string | null | undefined;
5188
+ banExpires?: Date | null | undefined;
5189
5189
  } & {
5190
5190
  twoFactorEnabled: boolean | null | undefined;
5191
5191
  } & {}>;
@@ -1,5 +1,5 @@
1
- import { db } from './chunk-WZDAMCUX.mjs';
2
- import { errorRefs } from './chunk-ZQCALTKB.mjs';
1
+ import { db } from './chunk-ZJMFE5B2.mjs';
2
+ import { errorRefs } from './chunk-TR5XFBTD.mjs';
3
3
  import 'server-only';
4
4
  import { randomBytes } from 'crypto';
5
5
  import { after } from 'next/server';
@@ -72,5 +72,5 @@ function withActionErrors(action) {
72
72
  }
73
73
 
74
74
  export { ActionError, lookupErrorDetail, unexpectedErrorMessage, withActionErrors };
75
- //# sourceMappingURL=chunk-KAEA35OC.mjs.map
76
- //# sourceMappingURL=chunk-KAEA35OC.mjs.map
75
+ //# sourceMappingURL=chunk-6MDQRT6U.mjs.map
76
+ //# sourceMappingURL=chunk-6MDQRT6U.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/errors.ts"],"names":[],"mappings":"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","file":"chunk-KAEA35OC.mjs","sourcesContent":["import \"server-only\"\nimport { randomBytes } from \"node:crypto\"\nimport { after } from \"next/server\"\nimport { eq, lt } from \"drizzle-orm\"\nimport { db } from \"../server/db\"\nimport { errorRefs } from \"../server/db/schema\"\n\n// No 0/O/1/I so a user can read a ref back without mix-ups.\nconst REF_ALPHABET = \"23456789ABCDEFGHJKLMNPQRSTUVWXYZ\"\nconst REF_PATTERN = /\\(ref: ([2-9A-HJ-NP-Z]{6})\\)/\nconst DETAIL_MAX_LENGTH = 8000\nconst RETENTION_MS = 30 * 24 * 60 * 60 * 1000\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) return error.stack || `${error.name}: ${error.message}`\n try {\n return JSON.stringify(error, null, 2)\n } catch {\n return String(error)\n }\n}\n\n// Saves the detail so a later feedback report can carry it. Runs after the\n// response when inside a request (after()), and never fails the caller.\nfunction storeDetail(ref: string, message: string, error: unknown) {\n const save = async () => {\n try {\n await db.insert(errorRefs).values({\n ref,\n message,\n detail: describeError(error).slice(0, DETAIL_MAX_LENGTH),\n })\n // ponytail: prune on insert; a scheduled cleanup if error volume ever gets high.\n await db.delete(errorRefs).where(lt(errorRefs.createdAt, new Date(Date.now() - RETENTION_MS)))\n } catch (storeError) {\n console.error(`[error ref ${ref}] could not store error detail`, storeError)\n }\n }\n try {\n after(save)\n } catch {\n void save() // outside a request scope (e.g. a script)\n }\n}\n\n/** The stored technical detail for the ref inside a user-facing error message, if any. */\nexport async function lookupErrorDetail(text: string): Promise<string | null> {\n const ref = text.match(REF_PATTERN)?.[1]\n if (!ref) return null\n const [row] = await db\n .select({ detail: errorRefs.detail })\n .from(errorRefs)\n .where(eq(errorRefs.ref, ref))\n .limit(1)\n return row?.detail ?? null\n}\n\n/**\n * Logs an unexpected error under a short reference and returns the message to\n * show instead, e.g. \"Failed to update role (ref: K7Q2PX)\". The user, and any\n * feedback report they send, only ever get the ref. The error itself goes to\n * the server log (\"error ref K7Q2PX\") and the error_refs table, from which the\n * feedback route attaches it to reports sent to Discord/email.\n */\nexport function unexpectedErrorMessage(\n error: unknown,\n message = \"Something went wrong. Please try again.\"\n): string {\n const ref = Array.from(randomBytes(6), (b) => REF_ALPHABET[b % REF_ALPHABET.length]).join(\"\")\n console.error(`[error ref ${ref}] ${message}`, error)\n storeDetail(ref, message, error)\n return `${message} (ref: ${ref})`\n}\n\n/**\n * Throw this for messages that are safe to show to the end user (validation,\n * permission, and state-conflict errors). Anything else thrown inside a\n * `withActionErrors`-wrapped action is logged server-side and replaced with a\n * generic message carrying an error ref.\n */\nexport class ActionError extends Error {}\n\nexport type ActionResult<T> =\n | { ok: true; data: T }\n | { ok: false; message: string }\n\nfunction isFrameworkControlFlowError(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest\n return (\n typeof digest === \"string\" &&\n (digest.startsWith(\"NEXT_REDIRECT\") || digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK\"))\n )\n}\n\n/** Wraps a server action so it returns an ActionResult instead of throwing. */\nexport function withActionErrors<Args extends unknown[], T>(\n action: (...args: Args) => Promise<T>\n): (...args: Args) => Promise<ActionResult<T>> {\n return async (...args: Args): Promise<ActionResult<T>> => {\n try {\n return { ok: true, data: await action(...args) }\n } catch (error) {\n if (error instanceof ActionError) {\n return { ok: false, message: error.message }\n }\n if (isFrameworkControlFlowError(error)) {\n throw error\n }\n // Returned, not thrown: Next.js replaces thrown server-action messages\n // in production, which would drop the ref.\n return { ok: false, message: unexpectedErrorMessage(error) }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../lib/errors.ts"],"names":[],"mappings":"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","file":"chunk-6MDQRT6U.mjs","sourcesContent":["import \"server-only\"\nimport { randomBytes } from \"node:crypto\"\nimport { after } from \"next/server\"\nimport { eq, lt } from \"drizzle-orm\"\nimport { db } from \"../server/db\"\nimport { errorRefs } from \"../server/db/schema\"\n\n// No 0/O/1/I so a user can read a ref back without mix-ups.\nconst REF_ALPHABET = \"23456789ABCDEFGHJKLMNPQRSTUVWXYZ\"\nconst REF_PATTERN = /\\(ref: ([2-9A-HJ-NP-Z]{6})\\)/\nconst DETAIL_MAX_LENGTH = 8000\nconst RETENTION_MS = 30 * 24 * 60 * 60 * 1000\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) return error.stack || `${error.name}: ${error.message}`\n try {\n return JSON.stringify(error, null, 2)\n } catch {\n return String(error)\n }\n}\n\n// Saves the detail so a later feedback report can carry it. Runs after the\n// response when inside a request (after()), and never fails the caller.\nfunction storeDetail(ref: string, message: string, error: unknown) {\n const save = async () => {\n try {\n await db.insert(errorRefs).values({\n ref,\n message,\n detail: describeError(error).slice(0, DETAIL_MAX_LENGTH),\n })\n // ponytail: prune on insert; a scheduled cleanup if error volume ever gets high.\n await db.delete(errorRefs).where(lt(errorRefs.createdAt, new Date(Date.now() - RETENTION_MS)))\n } catch (storeError) {\n console.error(`[error ref ${ref}] could not store error detail`, storeError)\n }\n }\n try {\n after(save)\n } catch {\n void save() // outside a request scope (e.g. a script)\n }\n}\n\n/** The stored technical detail for the ref inside a user-facing error message, if any. */\nexport async function lookupErrorDetail(text: string): Promise<string | null> {\n const ref = text.match(REF_PATTERN)?.[1]\n if (!ref) return null\n const [row] = await db\n .select({ detail: errorRefs.detail })\n .from(errorRefs)\n .where(eq(errorRefs.ref, ref))\n .limit(1)\n return row?.detail ?? null\n}\n\n/**\n * Logs an unexpected error under a short reference and returns the message to\n * show instead, e.g. \"Failed to update role (ref: K7Q2PX)\". The user, and any\n * feedback report they send, only ever get the ref. The error itself goes to\n * the server log (\"error ref K7Q2PX\") and the error_refs table, from which the\n * feedback route attaches it to reports sent to Discord/email.\n */\nexport function unexpectedErrorMessage(\n error: unknown,\n message = \"Something went wrong. Please try again.\"\n): string {\n const ref = Array.from(randomBytes(6), (b) => REF_ALPHABET[b % REF_ALPHABET.length]).join(\"\")\n console.error(`[error ref ${ref}] ${message}`, error)\n storeDetail(ref, message, error)\n return `${message} (ref: ${ref})`\n}\n\n/**\n * Throw this for messages that are safe to show to the end user (validation,\n * permission, and state-conflict errors). Anything else thrown inside a\n * `withActionErrors`-wrapped action is logged server-side and replaced with a\n * generic message carrying an error ref.\n */\nexport class ActionError extends Error {}\n\nexport type ActionResult<T> =\n | { ok: true; data: T }\n | { ok: false; message: string }\n\nfunction isFrameworkControlFlowError(error: unknown): boolean {\n const digest = (error as { digest?: unknown } | null)?.digest\n return (\n typeof digest === \"string\" &&\n (digest.startsWith(\"NEXT_REDIRECT\") || digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK\"))\n )\n}\n\n/** Wraps a server action so it returns an ActionResult instead of throwing. */\nexport function withActionErrors<Args extends unknown[], T>(\n action: (...args: Args) => Promise<T>\n): (...args: Args) => Promise<ActionResult<T>> {\n return async (...args: Args): Promise<ActionResult<T>> => {\n try {\n return { ok: true, data: await action(...args) }\n } catch (error) {\n if (error instanceof ActionError) {\n return { ok: false, message: error.message }\n }\n if (isFrameworkControlFlowError(error)) {\n throw error\n }\n // Returned, not thrown: Next.js replaces thrown server-action messages\n // in production, which would drop the ref.\n return { ok: false, message: unexpectedErrorMessage(error) }\n }\n }\n}\n"]}
@@ -50,12 +50,13 @@ function formatLocation({ country, city }) {
50
50
  var WINDOW_MS = 15 * 60 * 1e3;
51
51
  var MAX = 10;
52
52
  var MAX_IP = MAX * 3;
53
+ var RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
53
54
  function isOverLimit(row, now, max = MAX) {
54
55
  if (!row) return false;
55
56
  if (now - row.windowStart.getTime() >= WINDOW_MS) return false;
56
57
  return row.count >= max;
57
58
  }
58
59
 
59
- export { MAX_IP, WINDOW_MS, formatDeviceInfo, formatLocation, getClientIp, isOverLimit, lookupGeoLocation, parseUserAgent };
60
- //# sourceMappingURL=chunk-N6ECRCHY.mjs.map
61
- //# sourceMappingURL=chunk-N6ECRCHY.mjs.map
60
+ export { MAX_IP, RETENTION_MS, WINDOW_MS, formatDeviceInfo, formatLocation, getClientIp, isOverLimit, lookupGeoLocation, parseUserAgent };
61
+ //# sourceMappingURL=chunk-6OAHCEDJ.mjs.map
62
+ //# sourceMappingURL=chunk-6OAHCEDJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/request-info.ts","../lib/auth-throttle-limits.ts"],"names":[],"mappings":"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","file":"chunk-6OAHCEDJ.mjs","sourcesContent":["import \"server-only\"\nimport { UAParser } from \"ua-parser-js\"\nimport geoip from \"geoip-lite\"\n\nconst countryNames = new Intl.DisplayNames([\"en\"], { type: \"region\" })\n\nexport type DeviceType = \"desktop\" | \"mobile\" | \"tablet\"\n\nexport interface DeviceInfo {\n os: string | null\n browser: string | null\n deviceType: DeviceType\n}\n\nexport interface GeoInfo {\n country: string | null\n city: string | null\n}\n\n/**\n * First address in X-Forwarded-For is the original client — everything after\n * is intermediate proxies. Falls back to null (e.g. local dev with no proxy).\n */\nexport function getClientIp(headers: Headers | undefined | null): string | null {\n const forwardedFor = headers?.get(\"x-forwarded-for\")\n if (forwardedFor) return forwardedFor.split(\",\")[0].trim()\n\n return headers?.get(\"x-real-ip\") ?? null\n}\n\nexport function parseUserAgent(userAgent: string | null | undefined): DeviceInfo {\n if (!userAgent) {\n return { os: null, browser: null, deviceType: \"desktop\" }\n }\n\n const { os, browser, device } = new UAParser(userAgent).getResult()\n\n return {\n os: os.name ? [os.name, os.version].filter(Boolean).join(\" \") : null,\n browser: browser.name ?? null,\n deviceType: device.type === \"mobile\" || device.type === \"tablet\" ? device.type : \"desktop\",\n }\n}\n\n/**\n * Offline lookup (geoip-lite ships its own IP-range database, no network\n * call) — resolves to nulls for private/local addresses like 127.0.0.1.\n */\nexport function lookupGeoLocation(ipAddress: string | null | undefined): GeoInfo {\n if (!ipAddress) return { country: null, city: null }\n\n const geo = geoip.lookup(ipAddress)\n if (!geo) return { country: null, city: null }\n\n let country: string | null = geo.country || null\n if (country) {\n try {\n country = countryNames.of(country) ?? country\n } catch {\n // Unrecognized region code — keep the raw code rather than losing it.\n }\n }\n\n return { country, city: geo.city || null }\n}\n\nexport function formatDeviceInfo({ os, browser, deviceType }: DeviceInfo): string {\n const label = [browser, os].filter(Boolean).join(\" on \")\n const typeLabel = deviceType === \"mobile\" ? \"Mobile\" : deviceType === \"tablet\" ? \"Tablet\" : \"Desktop\"\n\n return label ? `${label} · ${typeLabel}` : `Unknown device · ${typeLabel}`\n}\n\nexport function formatLocation({ country, city }: GeoInfo): string {\n if (!country && !city) return \"Unknown\"\n\n return [city, country].filter(Boolean).join(\", \")\n}\n","// Pure throttle policy — no DB, no `server-only`, so it stays unit-testable.\n// Consumed by lib/auth-throttle.ts (the DB plumbing) and its test.\n\n// Tune per deployment, or lift to env vars if you need it configurable.\nexport const WINDOW_MS = 15 * 60 * 1000 // how long attempts accumulate\nexport const MAX = 10 // failed sign-ins / sign-ups per window before a 429, per fingerprint\n\n// Backstop cap keyed by IP instead of fingerprint — the fingerprint header is\n// client-supplied and unsigned, so it's trivial to omit or randomize per\n// request (see server/auth.ts before-hook). Higher than MAX because an IP can\n// be shared behind NAT/CGNAT/VPN.\n// ponytail: flat multiplier, not a real per-deployment tuning — revisit if\n// legitimate shared-IP traffic ever gets caught in it.\nexport const MAX_IP = MAX * 3\n\n// Rows untouched this long are deleted (lib/auth-throttle.ts). They hold IP,\n// user agent and the last email typed — often for people with no account — so\n// GDPR storage limitation says don't keep them past their use.\nexport const RETENTION_MS = 30 * 24 * 60 * 60 * 1000\n\n// A row whose window has already elapsed counts as \"no row\" — it resets on\n// the next attempt (see recordDeviceAttempt), so the device isn't blocked in\n// the meantime.\nexport function isOverLimit(\n row: { count: number; windowStart: Date } | undefined,\n now: number,\n max: number = MAX,\n): boolean {\n if (!row) return false\n if (now - row.windowStart.getTime() >= WINDOW_MS) return false\n return row.count >= max\n}\n"]}
@@ -0,0 +1,62 @@
1
+ import { auth } from './chunk-7QVIOQ47.mjs';
2
+ import { db } from './chunk-ZJMFE5B2.mjs';
3
+ import { users } from './chunk-TR5XFBTD.mjs';
4
+ import 'server-only';
5
+ import { cache } from 'react';
6
+ import { headers } from 'next/headers';
7
+ import { redirect } from 'next/navigation';
8
+ import { eq } from 'drizzle-orm';
9
+
10
+ var getCurrentSession = cache(async () => {
11
+ const session = await auth.api.getSession({
12
+ headers: await headers()
13
+ });
14
+ return session;
15
+ });
16
+ var requireAuth = cache(async () => {
17
+ var _a;
18
+ const session = await getCurrentSession();
19
+ if (!(session == null ? void 0 : session.user)) {
20
+ redirect("/login");
21
+ }
22
+ const user = await db.select().from(users).where(eq(users.id, session.user.id)).limit(1);
23
+ if ((_a = user[0]) == null ? void 0 : _a.banned) {
24
+ await auth.api.signOut({
25
+ headers: await headers()
26
+ });
27
+ redirect("/login?error=account_banned");
28
+ }
29
+ return session;
30
+ });
31
+ function hasRole(userRole, requiredRole) {
32
+ if (!userRole) return false;
33
+ if (requiredRole === "admin") {
34
+ return userRole === "admin";
35
+ }
36
+ return userRole === "user" || userRole === "admin" || userRole === "tester";
37
+ }
38
+ async function requireRole(requiredRole) {
39
+ const session = await requireAuth();
40
+ if (!hasRole(session.user.role, requiredRole)) {
41
+ redirect("/dashboard");
42
+ }
43
+ return session;
44
+ }
45
+ function normalizeCallbackUrl(value) {
46
+ if (typeof value !== "string" || !value.trim()) return "/dashboard";
47
+ const candidate = value.trim();
48
+ try {
49
+ const appOrigin = new URL(process.env.NEXT_PUBLIC_APP_URL || process.env.BETTER_AUTH_URL || "http://localhost:3000").origin;
50
+ const callbackUrl = new URL(candidate, appOrigin);
51
+ if (callbackUrl.origin !== appOrigin) {
52
+ return "/dashboard";
53
+ }
54
+ return `${callbackUrl.pathname}${callbackUrl.search}${callbackUrl.hash}` || "/dashboard";
55
+ } catch (e) {
56
+ return "/dashboard";
57
+ }
58
+ }
59
+
60
+ export { getCurrentSession, hasRole, normalizeCallbackUrl, requireAuth, requireRole };
61
+ //# sourceMappingURL=chunk-6Z3WUSDJ.mjs.map
62
+ //# sourceMappingURL=chunk-6Z3WUSDJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/auth-helpers.ts"],"names":[],"mappings":";;;;;;;;;AAgBO,IAAM,iBAAA,GAAoB,MAAM,YAAY;AACjD,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA,CAAI,UAAA,CAAW;AAAA,IACxC,OAAA,EAAS,MAAM,OAAA;AAAQ,GACxB,CAAA;AAED,EAAA,OAAO,OAAA;AACT,CAAC;AASM,IAAM,WAAA,GAAc,MAAM,YAAY;AA/B7C,EAAA,IAAA,EAAA;AAgCE,EAAA,MAAM,OAAA,GAAU,MAAM,iBAAA,EAAkB;AAExC,EAAA,IAAI,EAAC,mCAAS,IAAA,CAAA,EAAM;AAClB,IAAA,QAAA,CAAS,QAAQ,CAAA;AAAA,EACnB;AAGA,EAAA,MAAM,OAAO,MAAM,EAAA,CAChB,QAAO,CACP,IAAA,CAAK,KAAK,CAAA,CACV,KAAA,CAAM,EAAA,CAAG,KAAA,CAAM,IAAI,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAC,CAAA,CACnC,MAAM,CAAC,CAAA;AAEV,EAAA,IAAA,CAAI,EAAA,GAAA,IAAA,CAAK,CAAC,CAAA,KAAN,IAAA,GAAA,MAAA,GAAA,EAAA,CAAS,MAAA,EAAQ;AAEnB,IAAA,MAAM,IAAA,CAAK,IAAI,OAAA,CAAQ;AAAA,MACrB,OAAA,EAAS,MAAM,OAAA;AAAQ,KACxB,CAAA;AACD,IAAA,QAAA,CAAS,6BAA6B,CAAA;AAAA,EACxC;AAEA,EAAA,OAAO,OAAA;AACT,CAAC;AAKM,SAAS,OAAA,CAAQ,UAAqC,YAAA,EAAyC;AACpG,EAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AAEtB,EAAA,IAAI,iBAAiB,OAAA,EAAS;AAC5B,IAAA,OAAO,QAAA,KAAa,OAAA;AAAA,EACtB;AAGA,EAAA,OAAO,QAAA,KAAa,MAAA,IAAU,QAAA,KAAa,OAAA,IAAW,QAAA,KAAa,QAAA;AACrE;AAKA,eAAsB,YAAY,YAAA,EAAgC;AAChE,EAAA,MAAM,OAAA,GAAU,MAAM,WAAA,EAAY;AAElC,EAAA,IAAI,CAAC,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,IAAA,EAAM,YAAY,CAAA,EAAG;AAC7C,IAAA,QAAA,CAAS,YAAY,CAAA;AAAA,EACvB;AAEA,EAAA,OAAO,OAAA;AACT;AAQO,SAAS,qBAAqB,KAAA,EAAmC;AACtE,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,IAAA,IAAQ,OAAO,YAAA;AAEvD,EAAA,MAAM,SAAA,GAAY,MAAM,IAAA,EAAK;AAE7B,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,uBAAuB,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,uBAAuB,CAAA,CAAE,MAAA;AACrH,IAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,SAAA,EAAW,SAAS,CAAA;AAEhD,IAAA,IAAI,WAAA,CAAY,WAAW,SAAA,EAAW;AACpC,MAAA,OAAO,YAAA;AAAA,IACT;AAEA,IAAA,OAAO,CAAA,EAAG,YAAY,QAAQ,CAAA,EAAG,YAAY,MAAM,CAAA,EAAG,WAAA,CAAY,IAAI,CAAA,CAAA,IAAM,YAAA;AAAA,EAC9E,CAAA,CAAA,OAAQ,CAAA,EAAA;AACN,IAAA,OAAO,YAAA;AAAA,EACT;AACF","file":"chunk-6Z3WUSDJ.mjs","sourcesContent":["import \"server-only\"\nimport { cache } from \"react\"\nimport { auth } from \"../server/auth\"\nimport { headers } from \"next/headers\"\nimport { redirect } from \"next/navigation\"\nimport { db } from \"../server/db\"\nimport { users } from \"../server/db/schema\"\nimport { eq } from \"drizzle-orm\"\n\n/**\n * Get current session (server-side only)\n * Returns session data or null if not authenticated\n *\n * Wrapped in React's cache() so the layout and page (both of which call\n * requireAuth per request) share one auth.api.getSession call instead of two.\n */\nexport const getCurrentSession = cache(async () => {\n const session = await auth.api.getSession({\n headers: await headers()\n })\n\n return session\n})\n\n/**\n * Require authentication - redirect to login if not authenticated\n * Use in server components/layouts that need protection\n *\n * Wrapped in cache() so the layout, page, and any admin layout that all call\n * requireAuth per request share one banned-status DB lookup instead of several.\n */\nexport const requireAuth = cache(async () => {\n const session = await getCurrentSession()\n\n if (!session?.user) {\n redirect('/login')\n }\n\n // Check if user is banned\n const user = await db\n .select()\n .from(users)\n .where(eq(users.id, session.user.id))\n .limit(1)\n\n if (user[0]?.banned) {\n // Sign out the user\n await auth.api.signOut({\n headers: await headers()\n })\n redirect('/login?error=account_banned')\n }\n\n return session\n})\n\n/**\n * Check if user has specific role\n */\nexport function hasRole(userRole: string | null | undefined, requiredRole: 'user' | 'admin'): boolean {\n if (!userRole) return false\n\n if (requiredRole === 'admin') {\n return userRole === 'admin'\n }\n\n // 'user' role check - every role ('user', 'admin', 'tester') has access\n return userRole === 'user' || userRole === 'admin' || userRole === 'tester'\n}\n\n/**\n * Require specific role - throw error if user doesn't have it\n */\nexport async function requireRole(requiredRole: 'user' | 'admin') {\n const session = await requireAuth()\n\n if (!hasRole(session.user.role, requiredRole)) {\n redirect('/dashboard')\n }\n\n return session\n}\n\n/**\n * Normalize a callbackUrl query param to a same-origin path before it's ever\n * passed to redirect() or an auth form's callbackURL. Guards against open\n * redirects (e.g. /login?callbackUrl=https://evil.com) — anything that\n * doesn't resolve to this app's own origin falls back to /dashboard.\n */\nexport function normalizeCallbackUrl(value?: string | string[]): string {\n if (typeof value !== 'string' || !value.trim()) return '/dashboard'\n\n const candidate = value.trim()\n\n try {\n const appOrigin = new URL(process.env.NEXT_PUBLIC_APP_URL || process.env.BETTER_AUTH_URL || 'http://localhost:3000').origin\n const callbackUrl = new URL(candidate, appOrigin)\n\n if (callbackUrl.origin !== appOrigin) {\n return '/dashboard'\n }\n\n return `${callbackUrl.pathname}${callbackUrl.search}${callbackUrl.hash}` || '/dashboard'\n } catch {\n return '/dashboard'\n }\n}\n\n"]}
@@ -1,5 +1,5 @@
1
- import { db } from './chunk-WZDAMCUX.mjs';
2
- import { activityLogs } from './chunk-ZQCALTKB.mjs';
1
+ import { db } from './chunk-ZJMFE5B2.mjs';
2
+ import { activityLogs } from './chunk-TR5XFBTD.mjs';
3
3
  import 'server-only';
4
4
 
5
5
  async function logActivity({
@@ -37,5 +37,5 @@ async function logActivity({
37
37
  }
38
38
 
39
39
  export { logActivity };
40
- //# sourceMappingURL=chunk-VQ5B4MA7.mjs.map
41
- //# sourceMappingURL=chunk-VQ5B4MA7.mjs.map
40
+ //# sourceMappingURL=chunk-7F2POZM6.mjs.map
41
+ //# sourceMappingURL=chunk-7F2POZM6.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/activity-logger.ts"],"names":[],"mappings":";;;;AAsCA,eAAsB,WAAA,CAAY;AAAA,EAChC,MAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,EAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAsB;AACpB,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,CAAG,MAAA,CAAO,YAAY,CAAA,CAAE,MAAA,CAAO;AAAA,MACnC,MAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,WAAW,SAAA,IAAa,IAAA;AAAA,MACxB,WAAW,SAAA,IAAa,IAAA;AAAA,MACxB,QAAA,EAAU,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA,GAAI,IAAA;AAAA,MAChD,aAAa,WAAA,IAAe,IAAA;AAAA,MAC5B,IAAI,EAAA,IAAM,IAAA;AAAA,MACV,SAAS,OAAA,IAAW,IAAA;AAAA,MACpB,YAAY,UAAA,IAAc,IAAA;AAAA,MAC1B,SAAS,OAAA,IAAW,IAAA;AAAA,MACpB,MAAM,IAAA,IAAQ;AAAA,KACf,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,KAAK,CAAA;AAAA,EAEhD;AACF","file":"chunk-VQ5B4MA7.mjs","sourcesContent":["import \"server-only\"\nimport { db } from \"../server/db\"\nimport { activityLogs } from \"../server/db/schema\"\n\nexport type ActivityAction =\n | 'login'\n | 'logout'\n | 'login_failed'\n | 'password_changed'\n | 'email_changed'\n | 'profile_updated'\n | 'session_revoked'\n | 'user_deleted'\n | 'user_banned'\n | 'user_unbanned'\n | 'role_changed'\n | 'impersonation_started'\n | 'impersonation_stopped'\n | 'settings_changed'\n | 'passkey_added'\n | 'passkey_removed'\n\ninterface LogActivityParams {\n userId: string\n action: ActivityAction\n description: string\n ipAddress?: string | null\n userAgent?: string | null\n metadata?: Record<string, any>\n // Populated on 'login' events only — see server/auth.ts's session.create hook.\n referrerUrl?: string | null\n os?: string | null\n browser?: string | null\n deviceType?: string | null\n country?: string | null\n city?: string | null\n}\n\nexport async function logActivity({\n userId,\n action,\n description,\n ipAddress,\n userAgent,\n metadata,\n referrerUrl,\n os,\n browser,\n deviceType,\n country,\n city,\n}: LogActivityParams) {\n try {\n await db.insert(activityLogs).values({\n userId,\n action,\n description,\n ipAddress: ipAddress || null,\n userAgent: userAgent || null,\n metadata: metadata ? JSON.stringify(metadata) : null,\n referrerUrl: referrerUrl || null,\n os: os || null,\n browser: browser || null,\n deviceType: deviceType || null,\n country: country || null,\n city: city || null,\n })\n } catch (error) {\n console.error('Failed to log activity:', error)\n // Don't throw - activity logging should not break the app\n }\n}\n"]}
1
+ {"version":3,"sources":["../lib/activity-logger.ts"],"names":[],"mappings":";;;;AAsCA,eAAsB,WAAA,CAAY;AAAA,EAChC,MAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,EAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAsB;AACpB,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,CAAG,MAAA,CAAO,YAAY,CAAA,CAAE,MAAA,CAAO;AAAA,MACnC,MAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,WAAW,SAAA,IAAa,IAAA;AAAA,MACxB,WAAW,SAAA,IAAa,IAAA;AAAA,MACxB,QAAA,EAAU,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA,GAAI,IAAA;AAAA,MAChD,aAAa,WAAA,IAAe,IAAA;AAAA,MAC5B,IAAI,EAAA,IAAM,IAAA;AAAA,MACV,SAAS,OAAA,IAAW,IAAA;AAAA,MACpB,YAAY,UAAA,IAAc,IAAA;AAAA,MAC1B,SAAS,OAAA,IAAW,IAAA;AAAA,MACpB,MAAM,IAAA,IAAQ;AAAA,KACf,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,KAAK,CAAA;AAAA,EAEhD;AACF","file":"chunk-7F2POZM6.mjs","sourcesContent":["import \"server-only\"\nimport { db } from \"../server/db\"\nimport { activityLogs } from \"../server/db/schema\"\n\nexport type ActivityAction =\n | 'login'\n | 'logout'\n | 'login_failed'\n | 'password_changed'\n | 'email_changed'\n | 'profile_updated'\n | 'session_revoked'\n | 'user_deleted'\n | 'user_banned'\n | 'user_unbanned'\n | 'role_changed'\n | 'impersonation_started'\n | 'impersonation_stopped'\n | 'settings_changed'\n | 'passkey_added'\n | 'passkey_removed'\n\ninterface LogActivityParams {\n userId: string\n action: ActivityAction\n description: string\n ipAddress?: string | null\n userAgent?: string | null\n metadata?: Record<string, any>\n // Populated on 'login' events only — see server/auth.ts's session.create hook.\n referrerUrl?: string | null\n os?: string | null\n browser?: string | null\n deviceType?: string | null\n country?: string | null\n city?: string | null\n}\n\nexport async function logActivity({\n userId,\n action,\n description,\n ipAddress,\n userAgent,\n metadata,\n referrerUrl,\n os,\n browser,\n deviceType,\n country,\n city,\n}: LogActivityParams) {\n try {\n await db.insert(activityLogs).values({\n userId,\n action,\n description,\n ipAddress: ipAddress || null,\n userAgent: userAgent || null,\n metadata: metadata ? JSON.stringify(metadata) : null,\n referrerUrl: referrerUrl || null,\n os: os || null,\n browser: browser || null,\n deviceType: deviceType || null,\n country: country || null,\n city: city || null,\n })\n } catch (error) {\n console.error('Failed to log activity:', error)\n // Don't throw - activity logging should not break the app\n }\n}\n"]}
@@ -1,15 +1,16 @@
1
- import { logActivity } from './chunk-VQ5B4MA7.mjs';
2
- import { getClientIp, parseUserAgent, lookupGeoLocation, formatLocation, formatDeviceInfo, WINDOW_MS, isOverLimit, MAX_IP } from './chunk-N6ECRCHY.mjs';
3
- import { getEffectiveAuthFlags } from './chunk-3MGEAGHU.mjs';
4
- import { db } from './chunk-WZDAMCUX.mjs';
5
- import { users, sessions, passkeys, twoFactors, verifications, accounts, authThrottle } from './chunk-ZQCALTKB.mjs';
6
- import { config } from './chunk-TMJYAOC2.mjs';
1
+ import { logActivity } from './chunk-7F2POZM6.mjs';
2
+ import { getClientIp, parseUserAgent, lookupGeoLocation, formatLocation, formatDeviceInfo, WINDOW_MS, isOverLimit, MAX_IP, RETENTION_MS } from './chunk-6OAHCEDJ.mjs';
3
+ import { getEffectiveAuthFlags } from './chunk-P2G23JMY.mjs';
4
+ import { getResend, EMAIL_FROM } from './chunk-OVKVD2HH.mjs';
5
+ import { db } from './chunk-ZJMFE5B2.mjs';
6
+ import { users, sessions, passkeys, twoFactors, verifications, accounts, authThrottle } from './chunk-TR5XFBTD.mjs';
7
+ import { config } from './chunk-TKWNEKK6.mjs';
7
8
  import { __spreadValues, __spreadProps } from './chunk-3465SI5N.mjs';
8
9
  import 'server-only';
9
10
  import { betterAuth } from 'better-auth';
10
11
  import { drizzleAdapter } from 'better-auth/adapters/drizzle';
11
12
  import { createId } from '@paralleldrive/cuid2';
12
- import { sql, eq } from 'drizzle-orm';
13
+ import { sql, eq, lt } from 'drizzle-orm';
13
14
  import { admin, twoFactor, oneTap } from 'better-auth/plugins';
14
15
  import { passkey } from '@better-auth/passkey';
15
16
  import { createAuthMiddleware, APIError, getSessionFromCtx } from 'better-auth/api';
@@ -47,6 +48,18 @@ async function recordDeviceAttempt(fingerprint, meta) {
47
48
  } catch (error) {
48
49
  console.error("auth-throttle write failed", error);
49
50
  }
51
+ await pruneStaleRows(now);
52
+ }
53
+ var PRUNE_EVERY_MS = 60 * 60 * 1e3;
54
+ var lastPrune = 0;
55
+ async function pruneStaleRows(now = /* @__PURE__ */ new Date()) {
56
+ if (now.getTime() - lastPrune < PRUNE_EVERY_MS) return;
57
+ lastPrune = now.getTime();
58
+ try {
59
+ await db.delete(authThrottle).where(lt(authThrottle.lastAttemptAt, new Date(now.getTime() - RETENTION_MS)));
60
+ } catch (error) {
61
+ console.error("auth-throttle prune failed", error);
62
+ }
50
63
  }
51
64
  async function clearDeviceThrottle(fingerprint) {
52
65
  try {
@@ -55,15 +68,6 @@ async function clearDeviceThrottle(fingerprint) {
55
68
  console.error("auth-throttle clear failed", error);
56
69
  }
57
70
  }
58
- var EMAIL_FROM = config.EMAIL_FROM;
59
- var resend;
60
- async function getResend() {
61
- if (!resend) {
62
- const { Resend } = await import('resend');
63
- resend = new Resend(config.RESEND_API_KEY);
64
- }
65
- return resend;
66
- }
67
71
 
68
72
  // server/auth.ts
69
73
  async function logFailedLogin(ctx) {
@@ -453,6 +457,6 @@ var auth = new Proxy({}, {
453
457
  }
454
458
  });
455
459
 
456
- export { EMAIL_FROM, auth, createAuth, getResend };
457
- //# sourceMappingURL=chunk-XWVAJXAG.mjs.map
458
- //# sourceMappingURL=chunk-XWVAJXAG.mjs.map
460
+ export { auth, createAuth };
461
+ //# sourceMappingURL=chunk-7QVIOQ47.mjs.map
462
+ //# sourceMappingURL=chunk-7QVIOQ47.mjs.map