@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.
- package/README.md +339 -0
- package/components/auth/authCard.tsx +92 -0
- package/components/auth/authShell.tsx +46 -0
- package/components/auth/deviceCheck.tsx +52 -0
- package/components/auth/emailPasswordLogin.tsx +136 -0
- package/components/auth/emailPasswordSignup.tsx +178 -0
- package/components/auth/forgotPasswordForm.tsx +66 -0
- package/components/auth/loginErrorRecovery.tsx +117 -0
- package/components/auth/logoutButtons.tsx +26 -0
- package/components/auth/oneTap.tsx +50 -0
- package/components/auth/passkeyLogin.tsx +50 -0
- package/components/auth/resetPasswordForm.tsx +112 -0
- package/components/auth/socialLogin.tsx +51 -0
- package/components/auth/twoFactorVerifyForm.tsx +100 -0
- package/components/dashboard/accountDetailsCard.tsx +58 -0
- package/components/dashboard/activityLogsCard.tsx +141 -0
- package/components/dashboard/activityLogsCardSkeleton.tsx +20 -0
- package/components/dashboard/activityLogsSection.tsx +16 -0
- package/components/dashboard/admin/activityLogsSection.tsx +13 -0
- package/components/dashboard/admin/adminActivityLogsSkeleton.tsx +34 -0
- package/components/dashboard/admin/adminActivityLogsTable.tsx +319 -0
- package/components/dashboard/admin/adminStatsGrid.tsx +30 -0
- package/components/dashboard/admin/adminStatsGridSkeleton.tsx +16 -0
- package/components/dashboard/admin/adminUserActivityLogCard.tsx +53 -0
- package/components/dashboard/admin/adminUserActivityLogTable.tsx +69 -0
- package/components/dashboard/admin/adminUserLoginInfoCard.tsx +50 -0
- package/components/dashboard/admin/adminUserLoginInfoTable.tsx +63 -0
- package/components/dashboard/admin/adminUserSessionsCard.tsx +225 -0
- package/components/dashboard/admin/adminUserStatusCard.tsx +57 -0
- package/components/dashboard/admin/adminUsersSkeleton.tsx +30 -0
- package/components/dashboard/admin/adminUsersTable.tsx +596 -0
- package/components/dashboard/admin/deviceThrottleCard.tsx +59 -0
- package/components/dashboard/admin/deviceThrottleTable.tsx +93 -0
- package/components/dashboard/admin/impersonationLogCard.tsx +73 -0
- package/components/dashboard/admin/newDeviceCard.tsx +35 -0
- package/components/dashboard/admin/newDeviceTable.tsx +71 -0
- package/components/dashboard/admin/revokeAllSessionsButton.tsx +77 -0
- package/components/dashboard/admin/sharedDeviceCard.tsx +42 -0
- package/components/dashboard/admin/sharedDeviceTable.tsx +75 -0
- package/components/dashboard/admin/systemSettingsForm.tsx +200 -0
- package/components/dashboard/admin/systemSettingsSection.tsx +9 -0
- package/components/dashboard/admin/systemSettingsSkeleton.tsx +29 -0
- package/components/dashboard/admin/usersSection.tsx +13 -0
- package/components/dashboard/adminSection.tsx +31 -0
- package/components/dashboard/appSidebar.tsx +170 -0
- package/components/dashboard/dashboardHeader.tsx +85 -0
- package/components/dashboard/heartbeat.tsx +22 -0
- package/components/dashboard/impersonationBanner.tsx +50 -0
- package/components/dashboard/passkeysCard.tsx +187 -0
- package/components/dashboard/profileCard.tsx +42 -0
- package/components/dashboard/sectionErrorFallback.tsx +18 -0
- package/components/dashboard/sessionsCard.tsx +194 -0
- package/components/dashboard/sessionsCardSkeleton.tsx +20 -0
- package/components/dashboard/sessionsSection.tsx +22 -0
- package/components/dashboard/twoFactorCard.tsx +344 -0
- package/components/dashboard/verifyEmailButton.tsx +32 -0
- package/components/email/email-template-2.tsx +44 -0
- package/components/email/email-template-new-device.tsx +80 -0
- package/components/email/email-template-passkey-change.tsx +81 -0
- package/components/email/email-template-reset-password.tsx +56 -0
- package/components/email/email-template-verify-email.tsx +55 -0
- package/components/email/email-template-welcome.tsx +53 -0
- package/components/email/email-template.tsx +33 -0
- package/components/email/send-email-button.tsx +61 -0
- package/components/providers/theme-provider.tsx +9 -0
- package/components/theme/theme-toggle.tsx +53 -0
- package/components/ui/alert-dialog.tsx +141 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/button.tsx +64 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/data-table.tsx +302 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/dropdown-menu.tsx +203 -0
- package/components/ui/input.tsx +21 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/password-input.tsx +38 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +143 -0
- package/components/ui/sidebar.tsx +726 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/switch.tsx +35 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/tooltip.tsx +57 -0
- package/dist/activity/logger.d.mts +18 -0
- package/dist/activity/logger.d.ts +18 -0
- package/dist/activity/logger.js +289 -0
- package/dist/activity/logger.js.map +1 -0
- package/dist/activity/logger.mjs +7 -0
- package/dist/activity/logger.mjs.map +1 -0
- package/dist/activity/queries.d.mts +98 -0
- package/dist/activity/queries.d.ts +98 -0
- package/dist/activity/queries.js +372 -0
- package/dist/activity/queries.js.map +1 -0
- package/dist/activity/queries.mjs +124 -0
- package/dist/activity/queries.mjs.map +1 -0
- package/dist/admin/queries.d.mts +68 -0
- package/dist/admin/queries.d.ts +68 -0
- package/dist/admin/queries.js +450 -0
- package/dist/admin/queries.js.map +1 -0
- package/dist/admin/queries.mjs +143 -0
- package/dist/admin/queries.mjs.map +1 -0
- package/dist/auth/helpers.d.mts +111 -0
- package/dist/auth/helpers.d.ts +111 -0
- package/dist/auth/helpers.js +1023 -0
- package/dist/auth/helpers.js.map +1 -0
- package/dist/auth/helpers.mjs +67 -0
- package/dist/auth/helpers.mjs.map +1 -0
- package/dist/auth/index.d.mts +6589 -0
- package/dist/auth/index.d.ts +6589 -0
- package/dist/auth/index.js +968 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +10 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth-client/index.d.mts +4864 -0
- package/dist/auth-client/index.d.ts +4864 -0
- package/dist/auth-client/index.js +55 -0
- package/dist/auth-client/index.js.map +1 -0
- package/dist/auth-client/index.mjs +49 -0
- package/dist/auth-client/index.mjs.map +1 -0
- package/dist/chunk-2FW7C3KS.mjs +556 -0
- package/dist/chunk-2FW7C3KS.mjs.map +1 -0
- package/dist/chunk-ENGYO4K7.mjs +189 -0
- package/dist/chunk-ENGYO4K7.mjs.map +1 -0
- package/dist/chunk-GTPTVNY2.mjs +40 -0
- package/dist/chunk-GTPTVNY2.mjs.map +1 -0
- package/dist/chunk-HYKUB3A2.mjs +30 -0
- package/dist/chunk-HYKUB3A2.mjs.map +1 -0
- package/dist/chunk-JIYHBWAU.mjs +79 -0
- package/dist/chunk-JIYHBWAU.mjs.map +1 -0
- package/dist/chunk-K6OJCSJS.mjs +41 -0
- package/dist/chunk-K6OJCSJS.mjs.map +1 -0
- package/dist/chunk-N6ECRCHY.mjs +61 -0
- package/dist/chunk-N6ECRCHY.mjs.map +1 -0
- package/dist/chunk-YN4436PK.mjs +33 -0
- package/dist/chunk-YN4436PK.mjs.map +1 -0
- package/dist/config/env.d.mts +15 -0
- package/dist/config/env.d.ts +15 -0
- package/dist/config/env.js +42 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/env.mjs +4 -0
- package/dist/config/env.mjs.map +1 -0
- package/dist/db/index.d.mts +10 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.js +252 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/index.mjs +6 -0
- package/dist/db/index.mjs.map +1 -0
- package/dist/db/schema.d.mts +2 -0
- package/dist/db/schema.d.ts +2 -0
- package/dist/db/schema.js +186 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/schema.mjs +4 -0
- package/dist/db/schema.mjs.map +1 -0
- package/dist/schema-CgkDv4wJ.d.mts +1758 -0
- package/dist/schema-CgkDv4wJ.d.ts +1758 -0
- package/dist/sessions/queries.d.mts +34 -0
- package/dist/sessions/queries.d.ts +34 -0
- package/dist/sessions/queries.js +268 -0
- package/dist/sessions/queries.js.map +1 -0
- package/dist/sessions/queries.mjs +23 -0
- package/dist/sessions/queries.mjs.map +1 -0
- package/dist/settings/queries.d.mts +20 -0
- package/dist/settings/queries.d.ts +20 -0
- package/dist/settings/queries.js +344 -0
- package/dist/settings/queries.js.map +1 -0
- package/dist/settings/queries.mjs +7 -0
- package/dist/settings/queries.mjs.map +1 -0
- package/dist/types/auth/loginSchema.d.mts +8 -0
- package/dist/types/auth/loginSchema.d.ts +8 -0
- package/dist/types/auth/loginSchema.js +18 -0
- package/dist/types/auth/loginSchema.js.map +1 -0
- package/dist/types/auth/loginSchema.mjs +16 -0
- package/dist/types/auth/loginSchema.mjs.map +1 -0
- package/dist/types/auth/signupSchema.d.mts +9 -0
- package/dist/types/auth/signupSchema.d.ts +9 -0
- package/dist/types/auth/signupSchema.js +21 -0
- package/dist/types/auth/signupSchema.js.map +1 -0
- package/dist/types/auth/signupSchema.mjs +19 -0
- package/dist/types/auth/signupSchema.mjs.map +1 -0
- package/dist/users/queries.d.mts +71 -0
- package/dist/users/queries.d.ts +71 -0
- package/dist/users/queries.js +293 -0
- package/dist/users/queries.js.map +1 -0
- package/dist/users/queries.mjs +47 -0
- package/dist/users/queries.mjs.map +1 -0
- package/lib/auth-client.ts +58 -0
- package/lib/cookie-names.ts +13 -0
- package/lib/device-fingerprint.ts +21 -0
- package/lib/formatters.ts +53 -0
- package/lib/recalculate-columns.ts +75 -0
- package/lib/request-info.ts +78 -0
- package/lib/utils.ts +6 -0
- package/package.json +301 -0
- package/styles/theme.css +124 -0
- package/types/auth/loginSchema.ts +11 -0
- 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
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
|
+
}
|
package/styles/theme.css
ADDED
|
@@ -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
|
+
})
|