@xk2800/nextjs-template 0.5.3

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 (199) hide show
  1. package/README.md +339 -0
  2. package/components/auth/authCard.tsx +92 -0
  3. package/components/auth/authShell.tsx +46 -0
  4. package/components/auth/deviceCheck.tsx +52 -0
  5. package/components/auth/emailPasswordLogin.tsx +136 -0
  6. package/components/auth/emailPasswordSignup.tsx +178 -0
  7. package/components/auth/forgotPasswordForm.tsx +66 -0
  8. package/components/auth/loginErrorRecovery.tsx +117 -0
  9. package/components/auth/logoutButtons.tsx +26 -0
  10. package/components/auth/oneTap.tsx +50 -0
  11. package/components/auth/passkeyLogin.tsx +50 -0
  12. package/components/auth/resetPasswordForm.tsx +112 -0
  13. package/components/auth/socialLogin.tsx +51 -0
  14. package/components/auth/twoFactorVerifyForm.tsx +100 -0
  15. package/components/dashboard/accountDetailsCard.tsx +58 -0
  16. package/components/dashboard/activityLogsCard.tsx +141 -0
  17. package/components/dashboard/activityLogsCardSkeleton.tsx +20 -0
  18. package/components/dashboard/activityLogsSection.tsx +16 -0
  19. package/components/dashboard/admin/activityLogsSection.tsx +13 -0
  20. package/components/dashboard/admin/adminActivityLogsSkeleton.tsx +34 -0
  21. package/components/dashboard/admin/adminActivityLogsTable.tsx +319 -0
  22. package/components/dashboard/admin/adminStatsGrid.tsx +30 -0
  23. package/components/dashboard/admin/adminStatsGridSkeleton.tsx +16 -0
  24. package/components/dashboard/admin/adminUserActivityLogCard.tsx +53 -0
  25. package/components/dashboard/admin/adminUserActivityLogTable.tsx +69 -0
  26. package/components/dashboard/admin/adminUserLoginInfoCard.tsx +50 -0
  27. package/components/dashboard/admin/adminUserLoginInfoTable.tsx +63 -0
  28. package/components/dashboard/admin/adminUserSessionsCard.tsx +225 -0
  29. package/components/dashboard/admin/adminUserStatusCard.tsx +57 -0
  30. package/components/dashboard/admin/adminUsersSkeleton.tsx +30 -0
  31. package/components/dashboard/admin/adminUsersTable.tsx +596 -0
  32. package/components/dashboard/admin/deviceThrottleCard.tsx +59 -0
  33. package/components/dashboard/admin/deviceThrottleTable.tsx +93 -0
  34. package/components/dashboard/admin/impersonationLogCard.tsx +73 -0
  35. package/components/dashboard/admin/newDeviceCard.tsx +35 -0
  36. package/components/dashboard/admin/newDeviceTable.tsx +71 -0
  37. package/components/dashboard/admin/revokeAllSessionsButton.tsx +77 -0
  38. package/components/dashboard/admin/sharedDeviceCard.tsx +42 -0
  39. package/components/dashboard/admin/sharedDeviceTable.tsx +75 -0
  40. package/components/dashboard/admin/systemSettingsForm.tsx +200 -0
  41. package/components/dashboard/admin/systemSettingsSection.tsx +9 -0
  42. package/components/dashboard/admin/systemSettingsSkeleton.tsx +29 -0
  43. package/components/dashboard/admin/usersSection.tsx +13 -0
  44. package/components/dashboard/adminSection.tsx +31 -0
  45. package/components/dashboard/appSidebar.tsx +170 -0
  46. package/components/dashboard/dashboardHeader.tsx +85 -0
  47. package/components/dashboard/heartbeat.tsx +22 -0
  48. package/components/dashboard/impersonationBanner.tsx +50 -0
  49. package/components/dashboard/passkeysCard.tsx +187 -0
  50. package/components/dashboard/profileCard.tsx +42 -0
  51. package/components/dashboard/sectionErrorFallback.tsx +18 -0
  52. package/components/dashboard/sessionsCard.tsx +194 -0
  53. package/components/dashboard/sessionsCardSkeleton.tsx +20 -0
  54. package/components/dashboard/sessionsSection.tsx +22 -0
  55. package/components/dashboard/twoFactorCard.tsx +344 -0
  56. package/components/dashboard/verifyEmailButton.tsx +32 -0
  57. package/components/email/email-template-2.tsx +44 -0
  58. package/components/email/email-template-new-device.tsx +80 -0
  59. package/components/email/email-template-passkey-change.tsx +81 -0
  60. package/components/email/email-template-reset-password.tsx +56 -0
  61. package/components/email/email-template-verify-email.tsx +55 -0
  62. package/components/email/email-template-welcome.tsx +53 -0
  63. package/components/email/email-template.tsx +33 -0
  64. package/components/email/send-email-button.tsx +61 -0
  65. package/components/providers/theme-provider.tsx +9 -0
  66. package/components/theme/theme-toggle.tsx +53 -0
  67. package/components/ui/alert-dialog.tsx +141 -0
  68. package/components/ui/avatar.tsx +53 -0
  69. package/components/ui/badge.tsx +46 -0
  70. package/components/ui/button.tsx +64 -0
  71. package/components/ui/card.tsx +92 -0
  72. package/components/ui/checkbox.tsx +32 -0
  73. package/components/ui/data-table.tsx +302 -0
  74. package/components/ui/dialog.tsx +143 -0
  75. package/components/ui/dropdown-menu.tsx +203 -0
  76. package/components/ui/input.tsx +21 -0
  77. package/components/ui/label.tsx +26 -0
  78. package/components/ui/password-input.tsx +38 -0
  79. package/components/ui/separator.tsx +28 -0
  80. package/components/ui/sheet.tsx +143 -0
  81. package/components/ui/sidebar.tsx +726 -0
  82. package/components/ui/skeleton.tsx +13 -0
  83. package/components/ui/switch.tsx +35 -0
  84. package/components/ui/table.tsx +116 -0
  85. package/components/ui/textarea.tsx +18 -0
  86. package/components/ui/tooltip.tsx +57 -0
  87. package/dist/activity/logger.d.mts +18 -0
  88. package/dist/activity/logger.d.ts +18 -0
  89. package/dist/activity/logger.js +289 -0
  90. package/dist/activity/logger.js.map +1 -0
  91. package/dist/activity/logger.mjs +7 -0
  92. package/dist/activity/logger.mjs.map +1 -0
  93. package/dist/activity/queries.d.mts +98 -0
  94. package/dist/activity/queries.d.ts +98 -0
  95. package/dist/activity/queries.js +372 -0
  96. package/dist/activity/queries.js.map +1 -0
  97. package/dist/activity/queries.mjs +124 -0
  98. package/dist/activity/queries.mjs.map +1 -0
  99. package/dist/admin/queries.d.mts +68 -0
  100. package/dist/admin/queries.d.ts +68 -0
  101. package/dist/admin/queries.js +450 -0
  102. package/dist/admin/queries.js.map +1 -0
  103. package/dist/admin/queries.mjs +143 -0
  104. package/dist/admin/queries.mjs.map +1 -0
  105. package/dist/auth/helpers.d.mts +111 -0
  106. package/dist/auth/helpers.d.ts +111 -0
  107. package/dist/auth/helpers.js +1023 -0
  108. package/dist/auth/helpers.js.map +1 -0
  109. package/dist/auth/helpers.mjs +67 -0
  110. package/dist/auth/helpers.mjs.map +1 -0
  111. package/dist/auth/index.d.mts +6589 -0
  112. package/dist/auth/index.d.ts +6589 -0
  113. package/dist/auth/index.js +968 -0
  114. package/dist/auth/index.js.map +1 -0
  115. package/dist/auth/index.mjs +10 -0
  116. package/dist/auth/index.mjs.map +1 -0
  117. package/dist/auth-client/index.d.mts +4864 -0
  118. package/dist/auth-client/index.d.ts +4864 -0
  119. package/dist/auth-client/index.js +55 -0
  120. package/dist/auth-client/index.js.map +1 -0
  121. package/dist/auth-client/index.mjs +49 -0
  122. package/dist/auth-client/index.mjs.map +1 -0
  123. package/dist/chunk-2FW7C3KS.mjs +556 -0
  124. package/dist/chunk-2FW7C3KS.mjs.map +1 -0
  125. package/dist/chunk-ENGYO4K7.mjs +189 -0
  126. package/dist/chunk-ENGYO4K7.mjs.map +1 -0
  127. package/dist/chunk-GTPTVNY2.mjs +40 -0
  128. package/dist/chunk-GTPTVNY2.mjs.map +1 -0
  129. package/dist/chunk-HYKUB3A2.mjs +30 -0
  130. package/dist/chunk-HYKUB3A2.mjs.map +1 -0
  131. package/dist/chunk-JIYHBWAU.mjs +79 -0
  132. package/dist/chunk-JIYHBWAU.mjs.map +1 -0
  133. package/dist/chunk-K6OJCSJS.mjs +41 -0
  134. package/dist/chunk-K6OJCSJS.mjs.map +1 -0
  135. package/dist/chunk-N6ECRCHY.mjs +61 -0
  136. package/dist/chunk-N6ECRCHY.mjs.map +1 -0
  137. package/dist/chunk-YN4436PK.mjs +33 -0
  138. package/dist/chunk-YN4436PK.mjs.map +1 -0
  139. package/dist/config/env.d.mts +15 -0
  140. package/dist/config/env.d.ts +15 -0
  141. package/dist/config/env.js +42 -0
  142. package/dist/config/env.js.map +1 -0
  143. package/dist/config/env.mjs +4 -0
  144. package/dist/config/env.mjs.map +1 -0
  145. package/dist/db/index.d.mts +10 -0
  146. package/dist/db/index.d.ts +10 -0
  147. package/dist/db/index.js +252 -0
  148. package/dist/db/index.js.map +1 -0
  149. package/dist/db/index.mjs +6 -0
  150. package/dist/db/index.mjs.map +1 -0
  151. package/dist/db/schema.d.mts +2 -0
  152. package/dist/db/schema.d.ts +2 -0
  153. package/dist/db/schema.js +186 -0
  154. package/dist/db/schema.js.map +1 -0
  155. package/dist/db/schema.mjs +4 -0
  156. package/dist/db/schema.mjs.map +1 -0
  157. package/dist/schema-CgkDv4wJ.d.mts +1758 -0
  158. package/dist/schema-CgkDv4wJ.d.ts +1758 -0
  159. package/dist/sessions/queries.d.mts +34 -0
  160. package/dist/sessions/queries.d.ts +34 -0
  161. package/dist/sessions/queries.js +268 -0
  162. package/dist/sessions/queries.js.map +1 -0
  163. package/dist/sessions/queries.mjs +23 -0
  164. package/dist/sessions/queries.mjs.map +1 -0
  165. package/dist/settings/queries.d.mts +20 -0
  166. package/dist/settings/queries.d.ts +20 -0
  167. package/dist/settings/queries.js +344 -0
  168. package/dist/settings/queries.js.map +1 -0
  169. package/dist/settings/queries.mjs +7 -0
  170. package/dist/settings/queries.mjs.map +1 -0
  171. package/dist/types/auth/loginSchema.d.mts +8 -0
  172. package/dist/types/auth/loginSchema.d.ts +8 -0
  173. package/dist/types/auth/loginSchema.js +18 -0
  174. package/dist/types/auth/loginSchema.js.map +1 -0
  175. package/dist/types/auth/loginSchema.mjs +16 -0
  176. package/dist/types/auth/loginSchema.mjs.map +1 -0
  177. package/dist/types/auth/signupSchema.d.mts +9 -0
  178. package/dist/types/auth/signupSchema.d.ts +9 -0
  179. package/dist/types/auth/signupSchema.js +21 -0
  180. package/dist/types/auth/signupSchema.js.map +1 -0
  181. package/dist/types/auth/signupSchema.mjs +19 -0
  182. package/dist/types/auth/signupSchema.mjs.map +1 -0
  183. package/dist/users/queries.d.mts +71 -0
  184. package/dist/users/queries.d.ts +71 -0
  185. package/dist/users/queries.js +293 -0
  186. package/dist/users/queries.js.map +1 -0
  187. package/dist/users/queries.mjs +47 -0
  188. package/dist/users/queries.mjs.map +1 -0
  189. package/lib/auth-client.ts +58 -0
  190. package/lib/cookie-names.ts +13 -0
  191. package/lib/device-fingerprint.ts +21 -0
  192. package/lib/formatters.ts +53 -0
  193. package/lib/recalculate-columns.ts +75 -0
  194. package/lib/request-info.ts +78 -0
  195. package/lib/utils.ts +6 -0
  196. package/package.json +301 -0
  197. package/styles/theme.css +124 -0
  198. package/types/auth/loginSchema.ts +11 -0
  199. package/types/auth/signupSchema.ts +14 -0
@@ -0,0 +1,75 @@
1
+ import type { ReactNode } from "react"
2
+
3
+ /**
4
+ * Container-width-based column collapsing. Pure — no DOM or React runtime dependency,
5
+ * so it is trivially unit-testable. The `<DataTable>` component feeds it the wrapper's
6
+ * measured width on every resize.
7
+ */
8
+
9
+ /** Fallback width for a column that does not declare its own `minWidth`. */
10
+ export const MIN_CELL_WIDTH = 150
11
+
12
+ /** Below this width, `sticky` columns stop being pinned and can be auto-hidden too. */
13
+ export const STICKY_BREAKPOINT = 577
14
+
15
+ /** Rendered width of the row-toggle column (w-8 + cell padding) shown once collapsed. */
16
+ export const EXPANDER_WIDTH = 48
17
+
18
+ export type Column<TRow = Record<string, unknown>> = {
19
+ name: string
20
+ title: ReactNode
21
+ minWidth?: number
22
+ maxWidth?: number
23
+ /** Pinned column — only kept from auto-hiding while width >= the sticky breakpoint. */
24
+ sticky?: boolean
25
+ /** Set by `recalculateColumns` — the column did not fit and moved into the detail row. */
26
+ autoHide?: boolean
27
+ renderer?: (row: TRow) => ReactNode
28
+ }
29
+
30
+ export type RecalcOptions = {
31
+ /** Fallback when a column has no `minWidth`. Defaults to {@link MIN_CELL_WIDTH}. */
32
+ minCellWidth?: number
33
+ /** `sticky` columns are pinned only at width >= this. Defaults to {@link STICKY_BREAKPOINT}. */
34
+ stickyBreakpoint?: number
35
+ }
36
+
37
+ export type RecalcResult<TRow = Record<string, unknown>> = {
38
+ /** Same order as the input, with `autoHide` set on the columns that did not fit. */
39
+ columns: Column<TRow>[]
40
+ /** True when at least one column was auto-hidden. */
41
+ isCollapsed: boolean
42
+ }
43
+
44
+ /**
45
+ * Decide which columns fit in `width`. Sums each visible column's `minWidth`
46
+ * (or `minCellWidth`); if the total exceeds `width`, walks right-to-left marking
47
+ * columns `autoHide` until the rest fit. Column 0 is always kept as an anchor, and
48
+ * active `sticky` columns are skipped.
49
+ */
50
+ export function recalculateColumns<TRow>(
51
+ columns: Column<TRow>[],
52
+ width: number,
53
+ opts: RecalcOptions = {},
54
+ ): RecalcResult<TRow> {
55
+ const minCell = opts.minCellWidth ?? MIN_CELL_WIDTH
56
+ const stickyActive = width >= (opts.stickyBreakpoint ?? STICKY_BREAKPOINT)
57
+ const cellW = (c: Column<TRow>) => c.minWidth ?? minCell
58
+
59
+ const next = columns.map((c) => ({ ...c, autoHide: false }))
60
+ if (!(width > 0)) return { columns: next, isCollapsed: false } // unmeasured (SSR / first paint)
61
+
62
+ let predicted = next.reduce((sum, c) => sum + cellW(c), 0)
63
+ if (predicted <= width) return { columns: next, isCollapsed: false }
64
+ predicted += EXPANDER_WIDTH // collapsing adds the row-toggle column
65
+
66
+ // Walk right -> left; stop at index 1 so column 0 always stays as an anchor.
67
+ for (let i = next.length - 1; i >= 1 && predicted > width; i--) {
68
+ const c = next[i]
69
+ if (c.sticky && stickyActive) continue // pinned & active -> keep
70
+ c.autoHide = true
71
+ predicted -= cellW(c)
72
+ }
73
+
74
+ return { columns: next, isCollapsed: next.some((c) => c.autoHide) }
75
+ }
@@ -0,0 +1,78 @@
1
+ import "server-only"
2
+ import { UAParser } from "ua-parser-js"
3
+ import geoip from "geoip-lite"
4
+
5
+ const countryNames = new Intl.DisplayNames(["en"], { type: "region" })
6
+
7
+ export type DeviceType = "desktop" | "mobile" | "tablet"
8
+
9
+ export interface DeviceInfo {
10
+ os: string | null
11
+ browser: string | null
12
+ deviceType: DeviceType
13
+ }
14
+
15
+ export interface GeoInfo {
16
+ country: string | null
17
+ city: string | null
18
+ }
19
+
20
+ /**
21
+ * First address in X-Forwarded-For is the original client — everything after
22
+ * is intermediate proxies. Falls back to null (e.g. local dev with no proxy).
23
+ */
24
+ export function getClientIp(headers: Headers | undefined | null): string | null {
25
+ const forwardedFor = headers?.get("x-forwarded-for")
26
+ if (forwardedFor) return forwardedFor.split(",")[0].trim()
27
+
28
+ return headers?.get("x-real-ip") ?? null
29
+ }
30
+
31
+ export function parseUserAgent(userAgent: string | null | undefined): DeviceInfo {
32
+ if (!userAgent) {
33
+ return { os: null, browser: null, deviceType: "desktop" }
34
+ }
35
+
36
+ const { os, browser, device } = new UAParser(userAgent).getResult()
37
+
38
+ return {
39
+ os: os.name ? [os.name, os.version].filter(Boolean).join(" ") : null,
40
+ browser: browser.name ?? null,
41
+ deviceType: device.type === "mobile" || device.type === "tablet" ? device.type : "desktop",
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Offline lookup (geoip-lite ships its own IP-range database, no network
47
+ * call) — resolves to nulls for private/local addresses like 127.0.0.1.
48
+ */
49
+ export function lookupGeoLocation(ipAddress: string | null | undefined): GeoInfo {
50
+ if (!ipAddress) return { country: null, city: null }
51
+
52
+ const geo = geoip.lookup(ipAddress)
53
+ if (!geo) return { country: null, city: null }
54
+
55
+ let country: string | null = geo.country || null
56
+ if (country) {
57
+ try {
58
+ country = countryNames.of(country) ?? country
59
+ } catch {
60
+ // Unrecognized region code — keep the raw code rather than losing it.
61
+ }
62
+ }
63
+
64
+ return { country, city: geo.city || null }
65
+ }
66
+
67
+ export function formatDeviceInfo({ os, browser, deviceType }: DeviceInfo): string {
68
+ const label = [browser, os].filter(Boolean).join(" on ")
69
+ const typeLabel = deviceType === "mobile" ? "Mobile" : deviceType === "tablet" ? "Tablet" : "Desktop"
70
+
71
+ return label ? `${label} · ${typeLabel}` : `Unknown device · ${typeLabel}`
72
+ }
73
+
74
+ export function formatLocation({ country, city }: GeoInfo): string {
75
+ if (!country && !city) return "Unknown"
76
+
77
+ return [city, country].filter(Boolean).join(", ")
78
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
package/package.json ADDED
@@ -0,0 +1,301 @@
1
+ {
2
+ "name": "@xk2800/nextjs-template",
3
+ "version": "0.5.3",
4
+ "private": false,
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/xk2800/nextjs-template.git"
8
+ },
9
+ "publishConfig": {
10
+ "registry": "https://registry.npmjs.org",
11
+ "access": "public"
12
+ },
13
+ "sideEffects": [
14
+ "./dist/config/env.js",
15
+ "./dist/config/env.mjs",
16
+ "./dist/db/index.js",
17
+ "./dist/db/index.mjs",
18
+ "./dist/auth/index.js",
19
+ "./dist/auth/index.mjs",
20
+ "./dist/activity/logger.js",
21
+ "./dist/activity/logger.mjs",
22
+ "./dist/activity/queries.js",
23
+ "./dist/activity/queries.mjs",
24
+ "./dist/sessions/queries.js",
25
+ "./dist/sessions/queries.mjs",
26
+ "./dist/admin/queries.js",
27
+ "./dist/admin/queries.mjs",
28
+ "./dist/users/queries.js",
29
+ "./dist/users/queries.mjs",
30
+ "./dist/settings/queries.js",
31
+ "./dist/settings/queries.mjs"
32
+ ],
33
+ "files": [
34
+ "dist",
35
+ "components/ui",
36
+ "components/auth",
37
+ "components/theme",
38
+ "components/providers",
39
+ "components/email",
40
+ "components/dashboard",
41
+ "lib/utils.ts",
42
+ "lib/auth-client.ts",
43
+ "lib/formatters.ts",
44
+ "lib/recalculate-columns.ts",
45
+ "lib/cookie-names.ts",
46
+ "lib/device-fingerprint.ts",
47
+ "lib/request-info.ts",
48
+ "types/auth",
49
+ "styles/theme.css",
50
+ "README.md"
51
+ ],
52
+ "exports": {
53
+ "./package.json": "./package.json",
54
+ "./auth": {
55
+ "types": "./dist/auth/index.d.ts",
56
+ "import": "./dist/auth/index.mjs",
57
+ "require": "./dist/auth/index.js"
58
+ },
59
+ "./auth/helpers": {
60
+ "types": "./dist/auth/helpers.d.ts",
61
+ "import": "./dist/auth/helpers.mjs",
62
+ "require": "./dist/auth/helpers.js"
63
+ },
64
+ "./auth-client": {
65
+ "types": "./dist/auth-client/index.d.ts",
66
+ "import": "./dist/auth-client/index.mjs",
67
+ "require": "./dist/auth-client/index.js"
68
+ },
69
+ "./db": {
70
+ "types": "./dist/db/index.d.ts",
71
+ "import": "./dist/db/index.mjs",
72
+ "require": "./dist/db/index.js"
73
+ },
74
+ "./db/schema": {
75
+ "types": "./dist/db/schema.d.ts",
76
+ "import": "./dist/db/schema.mjs",
77
+ "require": "./dist/db/schema.js"
78
+ },
79
+ "./config/env": {
80
+ "types": "./dist/config/env.d.ts",
81
+ "import": "./dist/config/env.mjs",
82
+ "require": "./dist/config/env.js"
83
+ },
84
+ "./types/auth/loginSchema": {
85
+ "types": "./dist/types/auth/loginSchema.d.ts",
86
+ "import": "./dist/types/auth/loginSchema.mjs",
87
+ "require": "./dist/types/auth/loginSchema.js"
88
+ },
89
+ "./types/auth/signupSchema": {
90
+ "types": "./dist/types/auth/signupSchema.d.ts",
91
+ "import": "./dist/types/auth/signupSchema.mjs",
92
+ "require": "./dist/types/auth/signupSchema.js"
93
+ },
94
+ "./activity/logger": {
95
+ "types": "./dist/activity/logger.d.ts",
96
+ "import": "./dist/activity/logger.mjs",
97
+ "require": "./dist/activity/logger.js"
98
+ },
99
+ "./activity/queries": {
100
+ "types": "./dist/activity/queries.d.ts",
101
+ "import": "./dist/activity/queries.mjs",
102
+ "require": "./dist/activity/queries.js"
103
+ },
104
+ "./sessions/queries": {
105
+ "types": "./dist/sessions/queries.d.ts",
106
+ "import": "./dist/sessions/queries.mjs",
107
+ "require": "./dist/sessions/queries.js"
108
+ },
109
+ "./admin/queries": {
110
+ "types": "./dist/admin/queries.d.ts",
111
+ "import": "./dist/admin/queries.mjs",
112
+ "require": "./dist/admin/queries.js"
113
+ },
114
+ "./users/queries": {
115
+ "types": "./dist/users/queries.d.ts",
116
+ "import": "./dist/users/queries.mjs",
117
+ "require": "./dist/users/queries.js"
118
+ },
119
+ "./settings/queries": {
120
+ "types": "./dist/settings/queries.d.ts",
121
+ "import": "./dist/settings/queries.mjs",
122
+ "require": "./dist/settings/queries.js"
123
+ },
124
+ "./lib/utils": "./lib/utils.ts",
125
+ "./lib/formatters": "./lib/formatters.ts",
126
+ "./lib/recalculate-columns": "./lib/recalculate-columns.ts",
127
+ "./styles/theme.css": "./styles/theme.css",
128
+ "./components/*": "./components/*.tsx"
129
+ },
130
+ "typesVersions": {
131
+ "*": {
132
+ "auth": [
133
+ "dist/auth/index.d.ts"
134
+ ],
135
+ "auth/helpers": [
136
+ "dist/auth/helpers.d.ts"
137
+ ],
138
+ "auth-client": [
139
+ "dist/auth-client/index.d.ts"
140
+ ],
141
+ "db": [
142
+ "dist/db/index.d.ts"
143
+ ],
144
+ "db/schema": [
145
+ "dist/db/schema.d.ts"
146
+ ],
147
+ "config/env": [
148
+ "dist/config/env.d.ts"
149
+ ],
150
+ "types/auth/loginSchema": [
151
+ "dist/types/auth/loginSchema.d.ts"
152
+ ],
153
+ "types/auth/signupSchema": [
154
+ "dist/types/auth/signupSchema.d.ts"
155
+ ],
156
+ "activity/logger": [
157
+ "dist/activity/logger.d.ts"
158
+ ],
159
+ "activity/queries": [
160
+ "dist/activity/queries.d.ts"
161
+ ],
162
+ "sessions/queries": [
163
+ "dist/sessions/queries.d.ts"
164
+ ],
165
+ "admin/queries": [
166
+ "dist/admin/queries.d.ts"
167
+ ],
168
+ "users/queries": [
169
+ "dist/users/queries.d.ts"
170
+ ],
171
+ "settings/queries": [
172
+ "dist/settings/queries.d.ts"
173
+ ]
174
+ }
175
+ },
176
+ "scripts": {
177
+ "dev": "next dev --turbopack",
178
+ "build": "npm run build:lib && next build",
179
+ "start": "next start",
180
+ "lint": "next lint",
181
+ "test": "bun lib/recalculate-columns.test.ts && bun lib/auth-throttle-limits.test.ts && bun lib/shared-devices.test.ts",
182
+ "build:lib": "tsup",
183
+ "typecheck": "tsc --noEmit -p tsconfig.build.json",
184
+ "doctor": "bun --env-file=.env.development scripts/doctor.ts",
185
+ "prepublishOnly": "bun run typecheck && bun run build:lib",
186
+ "bump-version": "bun scripts/bump-version.ts",
187
+ "generate": "bun drizzle-kit generate",
188
+ "migrate:dev": "bun --env-file=.env.development drizzle-kit migrate",
189
+ "migrate:prod": "bun --env-file=.env.production drizzle-kit migrate",
190
+ "migrate:test": "bun --env-file=.env.test drizzle-kit migrate",
191
+ "studio:dev": "bun --env-file=.env.development drizzle-kit studio",
192
+ "studio:prod": "bun --env-file=.env.production drizzle-kit studio",
193
+ "dev:doppler": "doppler run -- next dev --turbopack",
194
+ "build:doppler": "doppler run -- next build",
195
+ "start:doppler": "doppler run -- next start",
196
+ "generate:doppler": "doppler run -- drizzle-kit generate",
197
+ "migrate:doppler": "doppler run -- drizzle-kit migrate",
198
+ "studio:doppler": "doppler run -- drizzle-kit studio",
199
+ "docs:dev": "cd docs && bun run dev"
200
+ },
201
+ "dependencies": {
202
+ "@neondatabase/serverless": "^1.0.0",
203
+ "@paralleldrive/cuid2": "^3.0.4",
204
+ "bcrypt": "^6.0.0",
205
+ "better-auth": "^1.3.34",
206
+ "drizzle-orm": "^0.45.0",
207
+ "geoip-lite": "^2.0.3",
208
+ "pg": "^8.16.0",
209
+ "radix-ui": "^1.6.7",
210
+ "server-only": "^0.0.1",
211
+ "ua-parser-js": "2.0.10",
212
+ "zod": "^4.1.12"
213
+ },
214
+ "peerDependencies": {
215
+ "next": "^15.0.0 || ^16.0.0",
216
+ "react": "^19.0.0",
217
+ "react-dom": "^19.0.0",
218
+ "@radix-ui/react-alert-dialog": "^1.1.0",
219
+ "@radix-ui/react-avatar": "^1.1.0",
220
+ "@radix-ui/react-dialog": "^1.1.0",
221
+ "@radix-ui/react-dropdown-menu": "^2.1.0",
222
+ "@radix-ui/react-label": "^2.1.0",
223
+ "@radix-ui/react-separator": "^1.1.0",
224
+ "@radix-ui/react-slot": "^1.2.0",
225
+ "class-variance-authority": "^0.7.0",
226
+ "clsx": "^2.1.0",
227
+ "tailwind-merge": "^3.3.0",
228
+ "lucide-react": "^1.16.0",
229
+ "react-icons": "^5.5.0",
230
+ "next-themes": "^0.4.0",
231
+ "@react-email/components": "^1.0.0",
232
+ "resend": "^6.4.2",
233
+ "sonner": "^2.0.0",
234
+ "@fingerprintjs/fingerprintjs": "^5.0.0",
235
+ "react-qr-code": "^2.0.0"
236
+ },
237
+ "peerDependenciesMeta": {
238
+ "@react-email/components": {
239
+ "optional": true
240
+ },
241
+ "resend": {
242
+ "optional": true
243
+ },
244
+ "next-themes": {
245
+ "optional": true
246
+ },
247
+ "sonner": {
248
+ "optional": true
249
+ },
250
+ "@fingerprintjs/fingerprintjs": {
251
+ "optional": true
252
+ },
253
+ "react-qr-code": {
254
+ "optional": true
255
+ }
256
+ },
257
+ "devDependencies": {
258
+ "@eslint/eslintrc": "^3",
259
+ "@fingerprintjs/fingerprintjs": "^5.2.0",
260
+ "@inquirer/prompts": "^8.5.2",
261
+ "@radix-ui/react-alert-dialog": "^1.1.15",
262
+ "@radix-ui/react-avatar": "^1.1.11",
263
+ "@radix-ui/react-dialog": "^1.1.15",
264
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
265
+ "@radix-ui/react-label": "^2.1.8",
266
+ "@radix-ui/react-separator": "^1.1.8",
267
+ "@radix-ui/react-slot": "^1.2.4",
268
+ "@react-email/components": "^1.0.1",
269
+ "@tailwindcss/postcss": "^4",
270
+ "@types/bcrypt": "^6.0.0",
271
+ "@types/geoip-lite": "^1.4.4",
272
+ "@types/node": "^26.1.0",
273
+ "@types/pg": "^8.15.2",
274
+ "@types/react": "^19.2.17",
275
+ "@types/react-dom": "^19",
276
+ "@types/ua-parser-js": "^0.7.39",
277
+ "bun-types": "^1.4.2",
278
+ "class-variance-authority": "^0.7.1",
279
+ "clsx": "^2.1.1",
280
+ "dotenv": "^17.2.3",
281
+ "drizzle-kit": "^0.31.1",
282
+ "eslint": "^10",
283
+ "eslint-config-next": "16.3.0",
284
+ "lucide-react": "^1.16.0",
285
+ "next": "16.3.0",
286
+ "next-themes": "^0.4.6",
287
+ "playwright": "^1.62.0",
288
+ "react": "^19.0.0",
289
+ "react-dom": "^19.0.0",
290
+ "react-icons": "^5.5.0",
291
+ "react-qr-code": "^2.2.0",
292
+ "resend": "^6.4.2",
293
+ "sonner": "^2.0.7",
294
+ "tailwind-merge": "^3.3.1",
295
+ "tailwindcss": "^4",
296
+ "tsup": "^8.5.1",
297
+ "tsx": "^4.19.4",
298
+ "tw-animate-css": "^1.3.6",
299
+ "typescript": "^6.0.3"
300
+ }
301
+ }
@@ -0,0 +1,124 @@
1
+ /*
2
+ * shadcn/ui theme variables required by components/ui/* and components/auth/*.
3
+ * Import this after "tailwindcss" in your project's globals.css:
4
+ * @import "tailwindcss";
5
+ * @import "@xk2800/nextjs-template/styles/theme.css";
6
+ * Also add tw-animate-css if you use Dialog/AlertDialog/DropdownMenu transitions:
7
+ * @import "tw-animate-css";
8
+ */
9
+
10
+ @theme inline {
11
+ --color-background: var(--background);
12
+ --color-foreground: var(--foreground);
13
+ --color-sidebar-ring: var(--sidebar-ring);
14
+ --color-sidebar-border: var(--sidebar-border);
15
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16
+ --color-sidebar-accent: var(--sidebar-accent);
17
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18
+ --color-sidebar-primary: var(--sidebar-primary);
19
+ --color-sidebar-foreground: var(--sidebar-foreground);
20
+ --color-sidebar: var(--sidebar);
21
+ --color-chart-5: var(--chart-5);
22
+ --color-chart-4: var(--chart-4);
23
+ --color-chart-3: var(--chart-3);
24
+ --color-chart-2: var(--chart-2);
25
+ --color-chart-1: var(--chart-1);
26
+ --color-ring: var(--ring);
27
+ --color-input: var(--input);
28
+ --color-border: var(--border);
29
+ --color-destructive: var(--destructive);
30
+ --color-accent-foreground: var(--accent-foreground);
31
+ --color-accent: var(--accent);
32
+ --color-muted-foreground: var(--muted-foreground);
33
+ --color-muted: var(--muted);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-secondary: var(--secondary);
36
+ --color-primary-foreground: var(--primary-foreground);
37
+ --color-primary: var(--primary);
38
+ --color-popover-foreground: var(--popover-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-card-foreground: var(--card-foreground);
41
+ --color-card: var(--card);
42
+ --radius-sm: calc(var(--radius) - 4px);
43
+ --radius-md: calc(var(--radius) - 2px);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) + 4px);
46
+ }
47
+
48
+ :root {
49
+ --radius: 0.625rem;
50
+ --background: oklch(1 0 0);
51
+ --foreground: oklch(0.145 0 0);
52
+ --card: oklch(1 0 0);
53
+ --card-foreground: oklch(0.145 0 0);
54
+ --popover: oklch(1 0 0);
55
+ --popover-foreground: oklch(0.145 0 0);
56
+ --primary: oklch(0.205 0 0);
57
+ --primary-foreground: oklch(0.985 0 0);
58
+ --secondary: oklch(0.97 0 0);
59
+ --secondary-foreground: oklch(0.205 0 0);
60
+ --muted: oklch(0.97 0 0);
61
+ --muted-foreground: oklch(0.556 0 0);
62
+ --accent: oklch(0.97 0 0);
63
+ --accent-foreground: oklch(0.205 0 0);
64
+ --destructive: oklch(0.577 0.245 27.325);
65
+ --border: oklch(0.922 0 0);
66
+ --input: oklch(0.922 0 0);
67
+ --ring: oklch(0.708 0 0);
68
+ --chart-1: oklch(0.646 0.222 41.116);
69
+ --chart-2: oklch(0.6 0.118 184.704);
70
+ --chart-3: oklch(0.398 0.07 227.392);
71
+ --chart-4: oklch(0.828 0.189 84.429);
72
+ --chart-5: oklch(0.769 0.188 70.08);
73
+ --sidebar: oklch(0.985 0 0);
74
+ --sidebar-foreground: oklch(0.145 0 0);
75
+ --sidebar-primary: oklch(0.205 0 0);
76
+ --sidebar-primary-foreground: oklch(0.985 0 0);
77
+ --sidebar-accent: oklch(0.97 0 0);
78
+ --sidebar-accent-foreground: oklch(0.205 0 0);
79
+ --sidebar-border: oklch(0.922 0 0);
80
+ --sidebar-ring: oklch(0.708 0 0);
81
+ }
82
+
83
+ .dark {
84
+ --background: oklch(0.145 0 0);
85
+ --foreground: oklch(0.985 0 0);
86
+ --card: oklch(0.205 0 0);
87
+ --card-foreground: oklch(0.985 0 0);
88
+ --popover: oklch(0.205 0 0);
89
+ --popover-foreground: oklch(0.985 0 0);
90
+ --primary: oklch(0.922 0 0);
91
+ --primary-foreground: oklch(0.205 0 0);
92
+ --secondary: oklch(0.269 0 0);
93
+ --secondary-foreground: oklch(0.985 0 0);
94
+ --muted: oklch(0.269 0 0);
95
+ --muted-foreground: oklch(0.708 0 0);
96
+ --accent: oklch(0.269 0 0);
97
+ --accent-foreground: oklch(0.985 0 0);
98
+ --destructive: oklch(0.704 0.191 22.216);
99
+ --border: oklch(1 0 0 / 10%);
100
+ --input: oklch(1 0 0 / 15%);
101
+ --ring: oklch(0.556 0 0);
102
+ --chart-1: oklch(0.488 0.243 264.376);
103
+ --chart-2: oklch(0.696 0.17 162.48);
104
+ --chart-3: oklch(0.769 0.188 70.08);
105
+ --chart-4: oklch(0.627 0.265 303.9);
106
+ --chart-5: oklch(0.645 0.246 16.439);
107
+ --sidebar: oklch(0.205 0 0);
108
+ --sidebar-foreground: oklch(0.985 0 0);
109
+ --sidebar-primary: oklch(0.488 0.243 264.376);
110
+ --sidebar-primary-foreground: oklch(0.985 0 0);
111
+ --sidebar-accent: oklch(0.269 0 0);
112
+ --sidebar-accent-foreground: oklch(0.985 0 0);
113
+ --sidebar-border: oklch(1 0 0 / 10%);
114
+ --sidebar-ring: oklch(0.556 0 0);
115
+ }
116
+
117
+ @layer base {
118
+ * {
119
+ @apply border-border outline-ring/50;
120
+ }
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ }
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+
3
+ export const LoginSchema = z.object({
4
+ email: z.string().email(),
5
+ password: z.string().min(6, {
6
+ message: 'Password must be at least 6 characters long.',
7
+ }),
8
+ // name: z.string().min(1, {
9
+ // message: 'Name should be at least 1 character long.'
10
+ // })
11
+ })
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+
3
+ export const SignupSchema = z.object({
4
+ name: z.string().min(1, {
5
+ message: 'Name is required.',
6
+ }),
7
+ email: z.string().email(),
8
+ // Matches better-auth's default minPasswordLength (server/auth.ts doesn't
9
+ // override it) — was 6 here, which let the client accept passwords the
10
+ // server would then reject.
11
+ password: z.string().min(8, {
12
+ message: 'Password must be at least 8 characters long.',
13
+ }),
14
+ })