@xk2800/nextjs-template 0.8.0-beta.3 → 0.8.0-beta.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../components/email/email-template-passkey-change.tsx","../../components/email/email-template-reset-password.tsx","../../components/email/email-template-verify-email.tsx","../../server/db/schema.ts","../../lib/maintenance.ts","../../config/env-schema.ts","../../config/env.ts","../../server/db/index.ts","../../lib/activity-logger.ts","../../lib/request-info.ts","../../lib/cookie-names.ts","../../lib/auth-throttle-limits.ts","../../lib/auth-throttle.ts","../../lib/settings-queries.ts","../../lib/resend.ts","../../server/auth.ts","../../lib/feedback-route.ts"],"names":["Html","jsx","Head","Tailwind","Body","Container","jsxs","Section","Heading","Text","Button","Link","pgEnum","pgTable","text","createId","boolean","timestamp","integer","unique","check","sql","z","Pool","drizzlePg","neon","drizzle","headers","UAParser","geoip","eq","getSessionFromCtx","EmailTemplatePasskeyChange","betterAuth","drizzleAdapter","EmailTemplateResetPassword","EmailTemplateVerifyEmail","createAuthMiddleware","APIError","current","adminPlugin","twoFactor","passkey","oneTap","NextResponse","resend","results"],"mappings":"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","file":"feedback.js","sourcesContent":["import {\n Html,\n Head,\n Body,\n Container,\n Section,\n Text,\n Heading,\n Button,\n Tailwind,\n} from '@react-email/components';\n\ninterface EmailTemplatePasskeyChangeProps {\n firstName: string;\n action: 'added' | 'removed';\n device: string;\n location: string;\n ipAddress: string;\n when: string;\n manageUrl?: string;\n}\n\nexport function EmailTemplatePasskeyChange({\n firstName,\n action,\n device,\n location,\n ipAddress,\n when,\n manageUrl,\n}: EmailTemplatePasskeyChangeProps) {\n const verb = action === 'added' ? 'added to' : 'removed from';\n\n return (\n <Html>\n <Head />\n <Tailwind>\n <Body className=\"bg-white font-sans\">\n <Container className=\"mx-auto max-w-[560px] px-0 pt-5 pb-12\">\n <Section className=\"rounded-xl bg-[#f8f9fa] p-10\">\n <Heading className=\"mb-3 text-center text-2xl font-bold text-[#1a1a1a]\">\n A passkey was {action}\n </Heading>\n <Text className=\"mb-6 text-base leading-6 text-[#666666]\">\n Hi {firstName}, a passkey was just {verb} your account.\n </Text>\n <Section className=\"mb-6 rounded-lg bg-white p-5\">\n <Text className=\"m-0 mb-2 text-sm leading-5 text-[#1a1a1a]\">\n <strong>Device:</strong> {device}\n </Text>\n <Text className=\"m-0 mb-2 text-sm leading-5 text-[#1a1a1a]\">\n <strong>Approx. location:</strong> {location}\n </Text>\n <Text className=\"m-0 mb-2 text-sm leading-5 text-[#1a1a1a]\">\n <strong>IP address:</strong> {ipAddress}\n </Text>\n <Text className=\"m-0 text-sm leading-5 text-[#1a1a1a]\">\n <strong>When:</strong> {when}\n </Text>\n </Section>\n {manageUrl && (\n <Section className=\"mb-6 text-center\">\n <Button\n href={manageUrl}\n className=\"rounded-lg bg-[#1a1a1a] px-6 py-3 text-sm font-bold text-white no-underline\"\n >\n Review your passkeys\n </Button>\n </Section>\n )}\n <Text className=\"text-sm leading-[22px] text-[#666666]\">\n If this was you, no action is needed. If you don&apos;t recognize it, change\n your password now and review your passkeys and active sessions.\n </Text>\n </Section>\n </Container>\n </Body>\n </Tailwind>\n </Html>\n );\n}\n","import {\n Html,\n Head,\n Body,\n Container,\n Section,\n Text,\n Heading,\n Button,\n} from '@react-email/components';\n\ninterface EmailTemplateResetPasswordProps {\n firstName: string;\n url: string;\n}\n\nexport function EmailTemplateResetPassword({ firstName, url }: EmailTemplateResetPasswordProps) {\n return (\n <Html>\n <Head />\n <Body style={{ backgroundColor: '#ffffff', fontFamily: 'Arial, sans-serif' }}>\n <Container style={{ margin: '0 auto', padding: '20px 0 48px', maxWidth: '560px' }}>\n <Section style={{ backgroundColor: '#f8f9fa', borderRadius: '12px', padding: '40px', textAlign: 'center' as const }}>\n <Heading style={{ fontSize: '28px', fontWeight: 'bold', marginBottom: '12px', color: '#1a1a1a' }}>\n Reset your password\n </Heading>\n <Text style={{ fontSize: '16px', color: '#666666', marginBottom: '32px', lineHeight: '24px' }}>\n Hi {firstName}, we got a request to reset your password. This link expires in an hour\n — if you didn&apos;t ask for this, you can safely ignore this email.\n </Text>\n <Section style={{ marginBottom: '32px' }}>\n <Button\n href={url}\n style={{\n backgroundColor: '#000000',\n color: '#ffffff',\n padding: '12px 32px',\n borderRadius: '6px',\n textDecoration: 'none',\n fontSize: '16px',\n fontWeight: 'bold',\n }}\n >\n Reset password\n </Button>\n </Section>\n <Text style={{ fontSize: '13px', color: '#999999', lineHeight: '20px', wordBreak: 'break-all' as const }}>\n Or paste this link into your browser:<br />\n <a href={url} style={{ color: '#000000', textDecoration: 'underline' }}>{url}</a>\n </Text>\n </Section>\n </Container>\n </Body>\n </Html>\n );\n}\n","import {\n Html,\n Head,\n Body,\n Container,\n Section,\n Text,\n Heading,\n Button,\n Link,\n Tailwind,\n} from '@react-email/components';\n\ninterface EmailTemplateVerifyEmailProps {\n firstName: string;\n url: string;\n}\n\nexport function EmailTemplateVerifyEmail({ firstName, url }: EmailTemplateVerifyEmailProps) {\n return (\n <Html>\n <Head />\n <Tailwind>\n <Body className=\"bg-white font-sans\">\n <Container className=\"mx-auto max-w-[560px] px-0 pt-5 pb-12\">\n <Section className=\"rounded-xl bg-[#f8f9fa] p-10 text-center\">\n <Heading className=\"mb-3 text-[28px] font-bold text-[#1a1a1a]\">\n Confirm your email\n </Heading>\n <Text className=\"mb-8 text-base leading-6 text-[#666666]\">\n Hi {firstName}, click the button below to verify this email address. If you\n didn&apos;t create an account, you can safely ignore this email.\n </Text>\n <Section className=\"mb-8\">\n <Button\n href={url}\n className=\"rounded-md bg-black px-8 py-3 text-base font-bold text-white no-underline\"\n >\n Verify email\n </Button>\n </Section>\n <Text className=\"text-[13px] leading-5 break-all text-[#999999]\">\n Or paste this link into your browser:\n <br />\n <Link href={url} className=\"text-black underline\">\n {url}\n </Link>\n </Text>\n </Section>\n </Container>\n </Body>\n </Tailwind>\n </Html>\n );\n}\n","import { boolean, check, integer, pgEnum, pgTable, text, timestamp, unique } from \"drizzle-orm/pg-core\";\nimport { sql } from \"drizzle-orm\";\nimport { createId } from '@paralleldrive/cuid2'\nimport { DEFAULT_MAINTENANCE_EXEMPT_PATHS } from \"../../lib/maintenance\";\n\n// 'tester' is a regular user account flagged for testing; apps can use it to\n// keep test accounts apart from real users. It has no admin access.\nexport const RoleEnum = pgEnum('roles', ['user', 'admin', 'tester'])\n\nexport const ActivityActionEnum = pgEnum('activity_actions', [\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])\n\nexport const users = pgTable(\"user\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text('name').notNull(),\n email: text('email').notNull().unique(),\n password: text('password'),\n emailVerified: boolean(\"emailVerified\").notNull().default(false),\n image: text(\"image\"),\n role: RoleEnum('roles').default('user').notNull(),\n banned: boolean('banned').default(false).notNull(),\n bannedAt: timestamp('bannedAt'),\n bannedReason: text('bannedReason'),\n // Required by better-auth's admin plugin schema (auto-expiring bans). We\n // never set this ourselves, so it stays null and bans behave as permanent,\n // same as before the plugin was added.\n banExpires: timestamp('banExpires'),\n lastLoginAt: timestamp('lastLoginAt'),\n lastActiveAt: timestamp('lastActiveAt'),\n // Set by better-auth's twoFactor plugin — true once the user has\n // completed TOTP setup (see twoFactor pgTable below for the secret /\n // backup codes themselves).\n twoFactorEnabled: boolean('twoFactorEnabled').notNull().default(false),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n updatedAt: timestamp('updatedAt').notNull().defaultNow(),\n});\n\nexport const sessions = pgTable(\"session\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n token: text(\"token\").notNull().unique(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Set by better-auth's admin plugin while this session is an admin\n // impersonating another user; holds the impersonating admin's user id.\n impersonatedBy: text(\"impersonatedBy\"),\n});\n\nexport const accounts = pgTable(\"account\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n accountId: text(\"accountId\").notNull(),\n providerId: text(\"providerId\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n accessToken: text(\"accessToken\"),\n refreshToken: text(\"refreshToken\"),\n idToken: text(\"idToken\"),\n accessTokenExpiresAt: timestamp(\"accessTokenExpiresAt\"),\n refreshTokenExpiresAt: timestamp(\"refreshTokenExpiresAt\"),\n scope: text(\"scope\"),\n password: text(\"password\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\n// Better-auth's twoFactor plugin. One row per user (created on first\n// `enable`); secret and backupCodes are its own encrypted-at-rest strings,\n// field names/types must match its expectations exactly (drizzleAdapter in\n// server/auth.ts maps this table to the plugin's \"twoFactor\" model as-is).\nexport const twoFactors = pgTable(\"twoFactor\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n secret: text(\"secret\").notNull(),\n backupCodes: text(\"backupCodes\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n // Added in better-auth 1.7 (TOTP lockout). Defaults match the plugin's.\n verified: boolean(\"verified\").default(true),\n failedVerificationCount: integer(\"failedVerificationCount\").default(0),\n lockedUntil: timestamp(\"lockedUntil\"),\n});\n\n// Better-auth's passkey plugin — one row per registered WebAuthn credential.\nexport const passkeys = pgTable(\"passkey\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n name: text(\"name\"),\n publicKey: text(\"publicKey\").notNull(),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n credentialID: text(\"credentialID\").notNull().unique(),\n counter: integer(\"counter\").notNull(),\n deviceType: text(\"deviceType\").notNull(),\n backedUp: boolean(\"backedUp\").notNull(),\n transports: text(\"transports\"),\n createdAt: timestamp(\"createdAt\"),\n aaguid: text(\"aaguid\"),\n});\n\nexport const verifications = pgTable(\"verification\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n identifier: text(\"identifier\").notNull(),\n value: text(\"value\").notNull(),\n expiresAt: timestamp(\"expiresAt\").notNull(),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n});\n\nexport const activityLogs = pgTable(\"activity_log\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n action: ActivityActionEnum('action').notNull(),\n description: text('description').notNull(),\n ipAddress: text('ipAddress'),\n userAgent: text('userAgent'),\n metadata: text('metadata'),\n // Populated on 'login' events only (registration + sign-in) — parsed from\n // userAgent/ipAddress at write time so the record stays stable even if\n // the parsing library or geoip database changes later.\n referrerUrl: text('referrerUrl'),\n os: text('os'),\n browser: text('browser'),\n deviceType: text('deviceType'),\n country: text('country'),\n city: text('city'),\n createdAt: timestamp('createdAt').notNull().defaultNow(),\n});\n\n// One row per (user, device fingerprint) pair. Its only job is to answer\n// \"has this account signed in from this device before?\" — no matching row ⇒\n// send a new-device alert (see app/api/device-check/route.ts). visitorId is\n// FingerprintJS's identifier, computed client-side in\n// components/auth/deviceCheck.tsx. UA+IP are deliberately NOT the signal here:\n// they churn constantly on mobile and would alert on every other login.\nexport const deviceFingerprints = pgTable(\"device_fingerprint\", {\n id: text(\"id\").notNull().primaryKey().$defaultFn(() => createId()),\n userId: text(\"userId\")\n .notNull()\n .references(() => users.id, { onDelete: \"cascade\" }),\n visitorId: text(\"visitorId\").notNull(),\n // Context captured at first-seen time — for the admin panel's \"New devices\"\n // card and the alert email. Not part of the new-vs-known decision (that's\n // visitorId only), just so an admin can eyeball whether a sign-in looks like\n // a hijack. Nullable: an old row predates these columns, or geoip missed.\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n country: text(\"country\"),\n city: text(\"city\"),\n createdAt: timestamp(\"createdAt\").notNull().defaultNow(),\n}, (table) => [\n unique(\"device_fingerprint_user_visitor\").on(table.userId, table.visitorId),\n]);\n\n// Per-device abuse throttle for /sign-in/email and /sign-up/email — one\n// fixed-window counter row per FingerprintJS visitorId (sent by the auth\n// forms as the x-device-fingerprint header; see lib/auth-throttle.ts).\n// Keyed by device, not IP: IP is shared behind corporate NAT / CGNAT / VPN,\n// so an IP counter throttles coworkers together while missing an abuser who\n// rotates IPs. Rows whose window has elapsed are dead weight but harmless\n// (reset on the next attempt) — prune on a cron if the table ever grows.\nexport const authThrottle = pgTable(\"auth_throttle\", {\n fingerprint: text(\"fingerprint\").notNull().primaryKey(),\n count: integer(\"count\").notNull().default(0),\n windowStart: timestamp(\"windowStart\").notNull().defaultNow(),\n // Blockable data points, overwritten on every attempt (last-write-wins) so\n // the admin panel can show who to block — by IP or by fingerprint — with no\n // join and no per-attempt history table. windowStart anchors the counter's\n // window; lastAttemptAt is just \"most recently seen\".\n lastAttemptAt: timestamp(\"lastAttemptAt\").notNull().defaultNow(),\n ipAddress: text(\"ipAddress\"),\n userAgent: text(\"userAgent\"),\n lastEmail: text(\"lastEmail\"),\n lastKind: text(\"lastKind\"), // 'signin' | 'signup'\n});\n\n// Singleton row (id is always 'default') holding admin-editable settings for\n// config that used to be env-var-only. See lib/settings-queries.ts for the\n// cached read path and lazy-seed-from-env behavior.\nexport const systemSettings = pgTable(\"system_settings\", {\n id: text(\"id\").primaryKey().default(\"default\"),\n maintenanceMode: boolean(\"maintenanceMode\").notNull().default(false),\n maintenanceMessage: text(\"maintenanceMessage\"),\n // Path prefixes that stay reachable while maintenance mode is on (see\n // lib/maintenance.ts — a few auth paths are always open on top of these).\n maintenanceExemptPaths: text(\"maintenanceExemptPaths\").array().notNull().default(DEFAULT_MAINTENANCE_EXEMPT_PATHS),\n authEnableGoogle: boolean(\"authEnableGoogle\").notNull().default(true),\n authEnableEmailPassword: boolean(\"authEnableEmailPassword\").notNull().default(true),\n authEnableOneTap: boolean(\"authEnableOneTap\").notNull().default(false),\n enableSessionRevocation: boolean(\"enableSessionRevocation\").notNull().default(true),\n updatedAt: timestamp(\"updatedAt\").notNull().defaultNow(),\n // Who last changed a setting. This row doesn't \"belong\" to that admin the\n // way sessions/activityLogs belong to their user, so deleting that admin's\n // account should null this out rather than cascade-delete the singleton row.\n updatedBy: text(\"updatedBy\").references(() => users.id, { onDelete: \"set null\" }),\n}, (table) => [\n check(\"system_settings_singleton\", sql`${table.id} = 'default'`),\n]);","// Pure path logic for maintenance mode — no DB / server-only imports, so it\n// can run in proxy.ts and in the admin form alike.\n\n// Default for the admin-editable exempt list: the public marketing pages.\nexport const DEFAULT_MAINTENANCE_EXEMPT_PATHS = [\n \"/\",\n \"/features\",\n \"/changelog\",\n \"/changelog-template\",\n \"/privacy\",\n \"/terms\",\n]\n\n// Always reachable, whatever the admin saves — otherwise an admin could\n// lock themselves out of the sign-in flow needed to turn maintenance off.\nexport const ALWAYS_OPEN_PATHS = [\n \"/maintenance\",\n \"/login\", // includes /login/2fa\n \"/forgot-password\",\n \"/reset-password\",\n \"/api/auth\",\n \"/api/settings/public\", // login page reads the One Tap flag from here\n]\n\n// `/foo` matches `/foo` and `/foo/...` but not `/foobar`; `/` matches only `/`.\nfunction matches(pathname: string, prefix: string) {\n const p = prefix.length > 1 ? prefix.replace(/\\/+$/, \"\") : prefix\n return pathname === p || (p !== \"/\" && pathname.startsWith(p + \"/\"))\n}\n\nexport function isMaintenanceExempt(pathname: string, exemptPaths: string[]) {\n return [...ALWAYS_OPEN_PATHS, ...exemptPaths].some((p) => matches(pathname, p))\n}\n","// No \"server-only\" guard here — this schema is also imported by scripts that\n// run outside Next.js's bundler (e.g. scripts/doctor.ts), where that guard\n// throws unconditionally. config/env.ts re-exports the parsed, guarded result\n// for app code; import this file directly only from non-Next scripts.\nimport { z } from 'zod';\n\nexport const envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().default('3000').transform(Number),\n DATABASE_URL: z.string().default(''),\n RESEND_API_KEY: z.string().default(''),\n // Sender for every transactional email. Must be on a domain verified in\n // your Resend account.\n EMAIL_FROM: z.string().default('Acme <onboarding@xavierkhew.xyz>'),\n // Display name for this deployment — shown as the authenticator-app label\n // (2FA issuer) and the passkey relying-party name. Consuming projects set\n // this in their own .env; defaults to the template name.\n APP_NAME: z.string().default('Next.js Template'),\n // Where the dashboard feedback form delivers (app/api/feedback). Set either\n // or both; with neither, the Feedback button is hidden. FEEDBACK_EMAIL takes\n // a comma-separated list and sends through Resend (needs RESEND_API_KEY).\n FEEDBACK_DISCORD_WEBHOOK_URL: z.string().default(''),\n FEEDBACK_EMAIL: z.string().default(''),\n // Seed-only: the live value lives in the system_settings DB row (see\n // lib/settings-queries.ts) once it's been seeded on first read, and is\n // changed from the admin System Settings page, not by editing this var.\n ENABLE_SESSION_REVOCATION: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_GOOGLE: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_EMAIL_PASSWORD: z.string().default('true').transform(val => val === 'true'),\n AUTH_ENABLE_ONE_TAP: z.string().default('false').transform(val => val === 'true'),\n DB_DRIVER: z.enum(['pg', 'neon']).default('pg'),\n // Only relevant when DB_DRIVER=pg — whether the target Postgres server has SSL\n // enabled. Self-hosted Postgres (e.g. via Docker/Dokploy) typically does not.\n DATABASE_SSL: z.string().default('false').transform(val => val === 'true'),\n});\n","import \"server-only\";\nimport { envSchema } from './env-schema';\n\nconst results = envSchema.safeParse(process.env);\n\nif (results.success && (results.data.NODE_ENV === 'development')) {\n console.log('Environment variables loaded:', Object.keys(results.data));\n}\n\nif (!results.success) {\n console.error('Invalid environment variables:', results.error.format());\n throw new Error('Invalid environment variables');\n}\n\nexport const config = results.data;\n","import 'server-only';\n\n// neon-http driver for production\nimport { neon } from '@neondatabase/serverless';\nimport { drizzle } from 'drizzle-orm/neon-http';\n\n// postgres driver for local development\nimport { drizzle as drizzlePg } from 'drizzle-orm/node-postgres';\nimport { Pool } from 'pg';\n\nimport * as schema from './schema';\nimport { config } from '../../config/env';\n\n// Import the query builder\nimport { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport { NeonHttpDatabase } from 'drizzle-orm/neon-http';\n\ntype DrizzleNeon = NeonHttpDatabase<typeof schema>;\ntype DrizzlePg = PostgresJsDatabase<typeof schema>;\n\nlet db: DrizzleNeon | DrizzlePg\n\n\nif (config.DB_DRIVER === 'pg') {\n const pool = new Pool({\n connectionString: config.DATABASE_URL!,\n ssl: config.DATABASE_SSL ? { rejectUnauthorized: false } : undefined,\n });\n db = drizzlePg(pool, {\n schema,\n logger: true,\n });\n\n console.log('🟡 Using PostgreSQL (pg) driver');\n\n} else if (config.DB_DRIVER === 'neon') {\n const sql = neon(config.DATABASE_URL!);\n db = drizzle(sql, { schema, logger: true });\n\n console.log('🟢 Using Neon (HTTP) driver');\n\n} else {\n console.log('Database connection not established, unknown DB_DRIVER:', config.DB_DRIVER);\n}\n// Note: In production, you might want to disable logging or use a more sophisticated logger.\n\nexport { db }","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","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","// Shared cookie/header names used by both client components and server/auth.ts\n// — no \"server-only\" guard, since both sides need them.\n\n// Stashes the page a social sign-in was initiated from, so the login-activity\n// log shows the real referrer instead of the OAuth provider's own domain\n// (e.g. accounts.google.com) once the browser redirects back.\nexport const LOGIN_REFERRER_COOKIE = 'login_referrer'\n\n// Carries the client's FingerprintJS visitorId on /sign-in/email and\n// /sign-up/email requests — the key for the per-device abuse throttle\n// (see lib/auth-throttle.ts). A header, not a cookie, so it's only sent on\n// the auth calls that need it, not every request.\nexport const DEVICE_FINGERPRINT_HEADER = 'x-device-fingerprint'\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// 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","import \"server-only\"\nimport { eq, sql } from \"drizzle-orm\"\nimport { db } from \"../server/db\"\nimport { authThrottle } from \"../server/db/schema\"\nimport { WINDOW_MS, isOverLimit } from \"./auth-throttle-limits\"\n\n// Is this key (device fingerprint, or \"ip:<address>\" backstop) currently over\n// the cap? Read-only — the caller (server/auth.ts before-hook) decides\n// whether to 429.\nexport async function isDeviceThrottled(fingerprint: string, max?: number): Promise<boolean> {\n try {\n const [row] = await db\n .select({ count: authThrottle.count, windowStart: authThrottle.windowStart })\n .from(authThrottle)\n .where(eq(authThrottle.fingerprint, fingerprint))\n .limit(1)\n return isOverLimit(row, Date.now(), max)\n } catch (error) {\n // Fail open — a throttle-store hiccup must never lock everyone out of sign-in.\n console.error(\"auth-throttle read failed, allowing request\", error)\n return false\n }\n}\n\nexport type AttemptMeta = {\n ipAddress: string | null\n userAgent: string | null\n email: string | null\n kind: \"signin\" | \"signup\"\n}\n\n// Atomic upsert: new row at 1, +1 within the live window, or reset to 1 when\n// the previous window has elapsed. One statement, so a device hammering the\n// endpoint (the whole point) can't race its own counter. The meta fields are\n// overwritten every time — they're \"last seen from this device\", for the\n// admin panel's block-list view (lib/admin-queries.ts).\nexport async function recordDeviceAttempt(fingerprint: string, meta: AttemptMeta): Promise<void> {\n const now = new Date()\n const cutoff = new Date(now.getTime() - WINDOW_MS)\n const seen = {\n lastAttemptAt: now,\n ipAddress: meta.ipAddress,\n userAgent: meta.userAgent,\n lastEmail: meta.email,\n lastKind: meta.kind,\n }\n try {\n await db\n .insert(authThrottle)\n .values({ fingerprint, count: 1, windowStart: now, ...seen })\n .onConflictDoUpdate({\n target: authThrottle.fingerprint,\n set: {\n count: sql`case when ${authThrottle.windowStart} < ${cutoff} then 1 else ${authThrottle.count} + 1 end`,\n windowStart: sql`case when ${authThrottle.windowStart} < ${cutoff} then ${now} else ${authThrottle.windowStart} end`,\n ...seen,\n },\n })\n } catch (error) {\n console.error(\"auth-throttle write failed\", error)\n }\n}\n\nexport async function clearDeviceThrottle(fingerprint: string): Promise<void> {\n try {\n await db.delete(authThrottle).where(eq(authThrottle.fingerprint, fingerprint))\n } catch (error) {\n console.error(\"auth-throttle clear failed\", error)\n }\n}\n","import \"server-only\"\nimport { db } from \"../server/db\"\nimport { systemSettings } from \"../server/db/schema\"\nimport { config } from \"../config/env\"\nimport { eq } from \"drizzle-orm\"\nimport { DEFAULT_MAINTENANCE_EXEMPT_PATHS } from \"./maintenance\"\n\nconst SETTINGS_ROW_ID = \"default\"\n// Short TTL: cheap reads (no DB round trip on every request), while an\n// admin's change is guaranteed live within this window even for a reader\n// that isn't the one who just saved (invalidateSystemSettingsCache() makes\n// it instant for that request's own process).\nconst CACHE_TTL_MS = 10_000\n\nexport type SystemSettings = typeof systemSettings.$inferSelect\n\nlet cached: { value: SystemSettings; expiresAt: number } | null = null\n\n// Fail-open shape if the DB is unreachable: auth flags fall back to their\n// config/env.ts defaults (unchanged behavior), maintenance mode is always\n// off — a DB hiccup must never lock out the whole site, including admins.\nfunction fallbackSettings(): SystemSettings {\n return {\n id: SETTINGS_ROW_ID,\n maintenanceMode: false,\n maintenanceMessage: null,\n maintenanceExemptPaths: DEFAULT_MAINTENANCE_EXEMPT_PATHS,\n authEnableGoogle: config.AUTH_ENABLE_GOOGLE,\n authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,\n enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,\n updatedAt: new Date(0),\n updatedBy: null,\n }\n}\n\nasync function loadOrSeed(): Promise<SystemSettings> {\n const existing = await db\n .select()\n .from(systemSettings)\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .limit(1)\n if (existing[0]) return existing[0]\n\n // Lazy-seed on first read, from the *current* env config — not the\n // schema's hardcoded column defaults — so turning this feature on never\n // silently flips an already-deployed flag (e.g. a prod deploy that\n // already set AUTH_ENABLE_GOOGLE=false).\n const seeded = await db\n .insert(systemSettings)\n .values({\n id: SETTINGS_ROW_ID,\n maintenanceMode: false,\n authEnableGoogle: config.AUTH_ENABLE_GOOGLE,\n authEnableEmailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n authEnableOneTap: config.AUTH_ENABLE_ONE_TAP,\n enableSessionRevocation: config.ENABLE_SESSION_REVOCATION,\n })\n .onConflictDoNothing()\n .returning()\n\n if (seeded[0]) return seeded[0]\n\n // Lost the seed race to a concurrent first-reader.\n const retry = await db\n .select()\n .from(systemSettings)\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .limit(1)\n return retry[0] ?? fallbackSettings()\n}\n\nexport async function getSystemSettings(): Promise<SystemSettings> {\n const now = Date.now()\n if (cached && cached.expiresAt > now) return cached.value\n\n try {\n const value = await loadOrSeed()\n cached = { value, expiresAt: now + CACHE_TTL_MS }\n return value\n } catch (error) {\n console.error(\"Failed to load system settings, failing open:\", error)\n return fallbackSettings() // not cached — retry next request\n }\n}\n\nexport function invalidateSystemSettingsCache() {\n cached = null\n}\n\nexport type SystemSettingsPatch = Partial<Pick<SystemSettings,\n | 'maintenanceMode' | 'maintenanceMessage' | 'maintenanceExemptPaths'\n | 'authEnableGoogle' | 'authEnableEmailPassword' | 'authEnableOneTap'\n | 'enableSessionRevocation'\n>>\n\nexport async function updateSystemSettings(\n patch: SystemSettingsPatch,\n updatedBy: string\n): Promise<SystemSettings> {\n await loadOrSeed() // ensure the row exists even if nobody's read it yet\n\n const [updated] = await db\n .update(systemSettings)\n .set({ ...patch, updatedAt: new Date(), updatedBy })\n .where(eq(systemSettings.id, SETTINGS_ROW_ID))\n .returning()\n\n invalidateSystemSettingsCache()\n return updated\n}\n\n// Effective auth-method visibility = deploy-time \"configured\" (env, decides\n// whether the provider is even registered with better-auth — see\n// server/auth.ts's createAuth()) AND runtime \"offered\" (this DB flag).\nexport async function getEffectiveAuthFlags() {\n const settings = await getSystemSettings()\n return {\n google: config.AUTH_ENABLE_GOOGLE && settings.authEnableGoogle,\n emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD && settings.authEnableEmailPassword,\n oneTap: config.AUTH_ENABLE_ONE_TAP && settings.authEnableOneTap,\n }\n}\n\n// The env-only half of the AND above. The System Settings page needs this\n// to explain *why* a toggle it controls isn't taking effect — flipping the\n// DB flag on can't do anything if the env var for that method is off, so\n// the UI should say so instead of looking like a silent no-op.\nexport function getConfiguredAuthFlags() {\n return {\n google: config.AUTH_ENABLE_GOOGLE,\n emailPassword: config.AUTH_ENABLE_EMAIL_PASSWORD,\n oneTap: config.AUTH_ENABLE_ONE_TAP,\n }\n}\n","import \"server-only\"\nimport type { Resend } from \"resend\"\nimport { config } from \"@/config/env\"\n\n// From env, not hardcoded: scaffolded apps run this module from the package's\n// bundled auth, so a local edit here would never reach their auth emails.\nexport const EMAIL_FROM = config.EMAIL_FROM\n\n// Loaded on first send, so projects that never send email don't pay for it\n// at startup.\nlet resend: Resend | undefined\nexport async function getResend() {\n if (!resend) {\n const { Resend } = await import(\"resend\")\n resend = new Resend(config.RESEND_API_KEY)\n }\n return resend\n}\n","import \"server-only\"\nimport { betterAuth, type BetterAuthOptions } from \"better-auth\"\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\"\nimport { createId } from \"@paralleldrive/cuid2\"\nimport { eq, sql } from \"drizzle-orm\"\nimport { db } from \"./db\"\nimport { users, accounts, sessions, verifications, twoFactors, passkeys } from \"./db/schema\"\nimport { config } from \"@/config/env\"\nimport { oneTap, admin as adminPlugin, twoFactor } from \"better-auth/plugins\";\nimport { passkey } from \"@better-auth/passkey\";\nimport { createAuthMiddleware, getSessionFromCtx, APIError } from \"better-auth/api\"\nimport { logActivity } from \"@/lib/activity-logger\"\nimport { getClientIp, parseUserAgent, lookupGeoLocation, formatDeviceInfo, formatLocation } from \"@/lib/request-info\"\nimport { LOGIN_REFERRER_COOKIE, DEVICE_FINGERPRINT_HEADER } from \"@/lib/cookie-names\"\nimport { isDeviceThrottled, recordDeviceAttempt, clearDeviceThrottle } from \"@/lib/auth-throttle\"\nimport { MAX_IP } from \"@/lib/auth-throttle-limits\"\nimport { getEffectiveAuthFlags } from \"@/lib/settings-queries\"\nimport { getResend, EMAIL_FROM } from \"@/lib/resend\"\n// Email templates are imported lazily where they're sent, so\n// @react-email/components only loads when an email goes out.\n\ntype AuthOverrides = {\n // Merged with (not replacing) the default `google` provider below, so\n // consuming projects can add e.g. `apple` without forking this file.\n socialProviders?: BetterAuthOptions[\"socialProviders\"]\n}\n\n// A failed credential sign-in, written to the activity feed — but only when\n// the attempted email maps to a real account (activityLogs.userId is NOT\n// NULL, and a miss has no user to attribute the row to). Best-effort: runs\n// in an after-hook and must never disturb the sign-in response.\nasync function logFailedLogin(ctx: { body?: unknown; headers?: Headers | null }) {\n try {\n const raw = (ctx.body as { email?: unknown } | undefined)?.email\n const email = typeof raw === \"string\" ? raw.toLowerCase() : null\n if (!email) return\n\n const [user] = await db\n .select({ id: users.id })\n .from(users)\n .where(eq(users.email, email))\n .limit(1)\n if (!user) return\n\n await logActivity({\n userId: user.id,\n action: \"login_failed\",\n description: \"Failed login attempt\",\n ipAddress: getClientIp(ctx.headers),\n userAgent: ctx.headers?.get(\"user-agent\") ?? null,\n })\n } catch (error) {\n console.error(\"Failed to log login_failed activity\", error)\n }\n}\n\n// Email the account owner that a passkey was added or removed. Runs from the\n// auth `after` hook on a successful /passkey/verify-registration or\n// /passkey/delete-passkey; best-effort — a mail failure must not fail the\n// endpoint. Also written to the activity feed. Self-disables when Resend\n// isn't configured.\nasync function notifyPasskeyChange(\n ctx: Parameters<Parameters<typeof createAuthMiddleware>[0]>[0],\n action: \"added\" | \"removed\",\n) {\n try {\n const session = await getSessionFromCtx(ctx)\n const user = session?.user\n if (!user) return\n\n const ip = getClientIp(ctx.headers)\n\n await logActivity({\n userId: user.id,\n action: action === \"added\" ? \"passkey_added\" : \"passkey_removed\",\n description: `Passkey ${action}`,\n ipAddress: ip,\n userAgent: ctx.headers?.get(\"user-agent\") ?? null,\n })\n\n if (!config.RESEND_API_KEY) return\n\n const { EmailTemplatePasskeyChange } = await import(\"@/components/email/email-template-passkey-change\")\n await (await getResend()).emails.send({\n from: EMAIL_FROM,\n to: [user.email],\n subject: action === \"added\" ? \"A passkey was added to your account\" : \"A passkey was removed from your account\",\n react: EmailTemplatePasskeyChange({\n firstName: user.name || \"there\",\n action,\n device: formatDeviceInfo(parseUserAgent(ctx.headers?.get(\"user-agent\") ?? null)),\n location: formatLocation(lookupGeoLocation(ip)),\n ipAddress: ip || \"Unknown\",\n when: new Date().toUTCString(),\n manageUrl: `${process.env.BETTER_AUTH_URL || process.env.NEXTAUTH_URL || process.env.NEXT_PUBLIC_APP_URL || \"\"}/dashboard/settings`,\n }),\n })\n } catch (error) {\n console.error(\"Failed to send passkey-change notification\", error)\n }\n}\n\nexport function createAuth(overrides: AuthOverrides = {}) {\n const socialProviders: NonNullable<BetterAuthOptions[\"socialProviders\"]> = {\n ...(config.AUTH_ENABLE_GOOGLE ? {\n google: {\n clientId: process.env.AUTH_GOOGLE_ID || process.env.GOOGLE_CLIENT_ID || \"\",\n clientSecret: process.env.AUTH_GOOGLE_SECRET || process.env.GOOGLE_CLIENT_SECRET || \"\",\n },\n } : {}),\n ...overrides.socialProviders,\n }\n const emailAndPasswordEnabled = config.AUTH_ENABLE_EMAIL_PASSWORD\n\n // Guard on the *final* merged config, after overrides.socialProviders is applied —\n // this only fails when there's truly no way to sign in. A project that disables both\n // built-ins via AUTH_ENABLE_GOOGLE=false / AUTH_ENABLE_EMAIL_PASSWORD=false but adds\n // its own provider (e.g. apple) here is a valid config and won't trip this.\n const hasEnabledSocialProvider = Object.values(socialProviders).some(Boolean)\n\n if (!hasEnabledSocialProvider && !emailAndPasswordEnabled) {\n throw new Error(\n \"No auth provider is enabled — every social provider is off and emailAndPassword.enabled is false. \" +\n \"Enable AUTH_ENABLE_GOOGLE / AUTH_ENABLE_EMAIL_PASSWORD, or pass your own provider via createAuth({ socialProviders: {...} }).\"\n )\n }\n\n // Shared by the `baseURL` field below and the passkey plugin's rpID/origin\n // — WebAuthn credentials are bound to this exact origin, so it must never\n // fall back to better-auth's own \"localhost\" passkey default in production.\n const baseURL = process.env.NEXTAUTH_URL || process.env.BETTER_AUTH_URL || \"http://localhost:3000\"\n\n return betterAuth({\n database: drizzleAdapter(db, {\n provider: \"pg\",\n schema: {\n user: users,\n account: accounts,\n session: sessions,\n verification: verifications,\n twoFactor: twoFactors,\n passkey: passkeys,\n },\n }),\n\n // Session configuration (30 days expiration matching previous NextAuth setup)\n session: {\n expiresIn: 60 * 60 * 24 * 30, // 30 days\n updateAge: 60 * 60 * 24, // Update session every 24 hours\n cookieCache: {\n enabled: true, // Enable hybrid session caching for performance\n maxAge: 5 * 60, // 5 minutes\n }\n },\n\n // Email & Password Authentication (replaces Credentials provider)\n // Toggle per-project via AUTH_ENABLE_EMAIL_PASSWORD in that project's own .env.*\n emailAndPassword: {\n enabled: emailAndPasswordEnabled,\n requireEmailVerification: false,\n // `url` is fully composed by better-auth (baseURL + verification token +\n // callbackURL) — see app/(auth)/forgot-password and reset-password for\n // the pages this points at.\n async sendResetPassword({ user, url }) {\n const { EmailTemplateResetPassword } = await import(\"@/components/email/email-template-reset-password\")\n await (await getResend()).emails.send({\n from: EMAIL_FROM,\n to: [user.email],\n subject: \"Reset your password\",\n react: EmailTemplateResetPassword({ firstName: user.name || \"there\", url }),\n })\n },\n },\n\n // Email verification. `requireEmailVerification` above stays false, so this\n // is opt-in: users trigger it from the dashboard via\n // authClient.sendVerificationEmail(). better-auth composes `url`\n // (baseURL + token + callbackURL) and serves the /api/auth/verify-email\n // callback that flips users.emailVerified on click.\n emailVerification: {\n autoSignInAfterVerification: true,\n async sendVerificationEmail({ user, url }) {\n const { EmailTemplateVerifyEmail } = await import(\"@/components/email/email-template-verify-email\")\n await (await getResend()).emails.send({\n from: EMAIL_FROM,\n to: [user.email],\n subject: \"Confirm your email\",\n react: EmailTemplateVerifyEmail({ firstName: user.name || \"there\", url }),\n })\n },\n },\n\n // Social Providers — google is included by default; toggle per-project via\n // AUTH_ENABLE_GOOGLE. overrides.socialProviders can still add more (or replace it).\n socialProviders,\n\n // User schema configuration\n user: {\n additionalFields: {\n role: {\n type: \"string\",\n required: false,\n defaultValue: \"user\",\n input: false, // Prevent users from setting their own role\n },\n },\n // Self-service \"Delete account\" (dashboard Settings → Danger zone).\n // Credential users confirm with their password; OAuth-only users have\n // no password, so better-auth instead requires a session younger than\n // `freshAge` (default 1 day). Every user-owned table cascades on delete.\n // Impersonation and last-admin guards live in hooks.before below.\n deleteUser: { enabled: true },\n },\n\n // Advanced options\n advanced: {\n database: {\n generateId: () => createId(),\n },\n },\n\n // Every new session row corresponds to a sign-in (credential or OAuth) —\n // use that as the \"last logged in\" signal rather than session updates,\n // which also fire on cookie-cache refresh / expiry extension.\n databaseHooks: {\n session: {\n create: {\n async after(session, context) {\n // Impersonation sessions (created by the admin plugin's\n // impersonateUser endpoint) also go through session.create —\n // don't let \"an admin looked at this account\" show up as the\n // user's own last login, and log it as an audit event instead.\n const impersonatedBy = (session as { impersonatedBy?: string | null }).impersonatedBy\n if (impersonatedBy) {\n const target = await db\n .select({ email: users.email })\n .from(users)\n .where(eq(users.id, session.userId))\n .limit(1)\n\n await logActivity({\n userId: impersonatedBy,\n action: 'impersonation_started',\n description: `Admin started impersonating ${target[0]?.email ?? session.userId}`,\n metadata: { impersonatedUserId: session.userId },\n })\n return\n }\n\n try {\n await db\n .update(users)\n .set({ lastLoginAt: new Date() })\n .where(eq(users.id, session.userId))\n } catch (error) {\n console.error(\"Failed to update lastLoginAt\", error)\n }\n\n try {\n const headers = context?.headers\n const ipAddress = getClientIp(headers)\n const userAgent = headers?.get('user-agent') ?? null\n // For OAuth, the live referer header on this request is the\n // provider's own callback page (e.g. accounts.google.com), not\n // the page the user actually came from — socialLogin.tsx stashes\n // the real page in a cookie right before the redirect, which\n // survives the round trip since it's set on our own origin.\n // Falls back to the live header for email/password sign-in,\n // where this request *is* the originating request.\n const referrerUrl = context?.getCookie?.(LOGIN_REFERRER_COOKIE)\n || headers?.get('referer')\n || null\n const device = parseUserAgent(userAgent)\n const geo = lookupGeoLocation(ipAddress)\n\n // Every session row for this user, including the one just\n // created — count === 1 means signup just auto-signed them in\n // (better-auth's default), anything higher is a returning login.\n const [{ count }] = await db\n .select({ count: sql<number>`count(*)` })\n .from(sessions)\n .where(eq(sessions.userId, session.userId))\n const isFirstSession = Number(count) <= 1\n\n await logActivity({\n userId: session.userId,\n action: 'login',\n description: isFirstSession\n ? 'New account registered and signed in'\n : 'User logged in',\n ipAddress,\n userAgent,\n referrerUrl,\n os: device.os,\n browser: device.browser,\n deviceType: device.deviceType,\n country: geo.country,\n city: geo.city,\n })\n } catch (error) {\n console.error(\"Failed to log login activity\", error)\n }\n },\n },\n },\n },\n // better-auth's admin plugin ends impersonation by deleting the\n // impersonation session and restoring the admin's original session\n // straight from a signed cookie — it never runs session.create, so\n // there's no databaseHooks entry point to log the stop event from.\n // Reading the session here (before the endpoint deletes it) is the only\n // point where both the admin id and the impersonated user id are still\n // available together.\n hooks: {\n before: createAuthMiddleware(async (ctx) => {\n // Per-device abuse throttle for the two credential entry points.\n // Primarily keyed by the client's FingerprintJS visitorId\n // (x-device-fingerprint header, set in emailPasswordLogin/Signup), but\n // that header is client-supplied and unsigned — omitting it or\n // randomizing it per request bypasses a fingerprint-only check\n // entirely. IP is a lower-ceiling-tolerant backstop (shared behind\n // NAT/CGNAT/VPN, so it uses MAX_IP, not MAX) that still applies when\n // the header is missing or spoofed.\n if (ctx.path === \"/sign-in/email\" || ctx.path === \"/sign-up/email\") {\n const fingerprint = ctx.headers?.get(DEVICE_FINGERPRINT_HEADER)\n const ip = getClientIp(ctx.headers)\n const throttled =\n (fingerprint && (await isDeviceThrottled(fingerprint))) ||\n (ip && (await isDeviceThrottled(`ip:${ip}`, MAX_IP)))\n if (throttled) {\n throw new APIError(\"TOO_MANY_REQUESTS\", {\n message: \"Too many attempts from this device. Try again in a few minutes.\",\n })\n }\n }\n\n // Live kill-switch for the two built-in sign-in methods, on top of\n // the (untouched) provider registration above — lets an admin\n // disable a method from the System Settings page and have it take\n // effect immediately, even for a client that already has the button\n // rendered, instead of only on next deploy.\n if (ctx.path === \"/sign-in/social\") {\n // Scoped to \"google\" specifically since overrides.socialProviders\n // can register other providers (e.g. apple) this flag shouldn't gate.\n if (ctx.body?.provider === \"google\") {\n const flags = await getEffectiveAuthFlags()\n if (!flags.google) {\n throw new APIError(\"FORBIDDEN\", { message: \"Google sign-in is currently disabled.\" })\n }\n }\n return\n }\n\n if (ctx.path === \"/sign-in/email\") {\n const flags = await getEffectiveAuthFlags()\n if (!flags.emailPassword) {\n throw new APIError(\"FORBIDDEN\", { message: \"Email/password sign-in is currently disabled.\" })\n }\n return\n }\n\n // One Tap isn't a separate identity, it's a UI shortcut into the\n // same Google account — its plugin hits its own endpoint entirely\n // (not /sign-in/social), so it needs its own guard, and it needs\n // both flags: Google itself must still be allowed, and the One Tap\n // prompt specifically must be enabled.\n if (ctx.path === \"/one-tap/callback\") {\n const flags = await getEffectiveAuthFlags()\n if (!flags.google || !flags.oneTap) {\n throw new APIError(\"FORBIDDEN\", { message: \"Google One Tap is currently disabled.\" })\n }\n return\n }\n\n if (ctx.path === \"/delete-user\") {\n const current = await getSessionFromCtx(ctx, { disableCookieCache: true })\n if (!current) return // better-auth's own session check rejects it\n // An admin \"viewing as\" a user must not be able to destroy their account.\n if (current.session.impersonatedBy) {\n throw new APIError(\"FORBIDDEN\", { message: \"Stop impersonating before deleting an account.\" })\n }\n // Deleting the last admin would leave nobody able to reach the admin dashboard.\n if ((current.user as { role?: string }).role === \"admin\") {\n const [{ count }] = await db\n .select({ count: sql<number>`count(*)` })\n .from(users)\n .where(eq(users.role, \"admin\"))\n if (Number(count) <= 1) {\n throw new APIError(\"FORBIDDEN\", { message: \"You're the only admin. Promote another user to admin first.\" })\n }\n }\n return\n }\n\n if (ctx.path !== \"/admin/stop-impersonating\") return\n\n const current = await getSessionFromCtx(ctx, { disableCookieCache: true })\n if (current?.session.impersonatedBy) {\n const target = await db\n .select({ email: users.email })\n .from(users)\n .where(eq(users.id, current.session.userId))\n .limit(1)\n\n await logActivity({\n userId: current.session.impersonatedBy,\n action: 'impersonation_stopped',\n description: `Admin stopped impersonating ${target[0]?.email ?? current.session.userId}`,\n metadata: { impersonatedUserId: current.session.userId },\n })\n }\n }),\n after: createAuthMiddleware(async (ctx) => {\n // Passkey add/remove is a security-sensitive change to how the account\n // can be signed into — email the user whenever one lands, the same way\n // new-device sign-ins are surfaced. Best-effort: never disturb the\n // endpoint's own response.\n if (ctx.path === \"/passkey/verify-registration\" || ctx.path === \"/passkey/delete-passkey\") {\n if (ctx.context.returned instanceof APIError) return\n await notifyPasskeyChange(\n ctx,\n ctx.path === \"/passkey/verify-registration\" ? \"added\" : \"removed\",\n )\n return\n }\n\n if (ctx.path !== \"/sign-in/email\" && ctx.path !== \"/sign-up/email\") return\n\n const fingerprint = ctx.headers?.get(DEVICE_FINGERPRINT_HEADER)\n const ip = getClientIp(ctx.headers)\n const ipKey = ip ? `ip:${ip}` : null\n if (!fingerprint && !ipKey) return\n\n const failed = ctx.context.returned instanceof APIError\n const rawEmail = (ctx.body as { email?: unknown } | undefined)?.email\n const meta = {\n ipAddress: ip,\n userAgent: ctx.headers?.get(\"user-agent\") ?? null,\n email: typeof rawEmail === \"string\" ? rawEmail.toLowerCase() : null,\n }\n const keys = [fingerprint, ipKey].filter((k): k is string => !!k)\n\n // Sign-up abuse is mass account creation — the *successful* ones are\n // the problem — so every sign-up from this device counts.\n if (ctx.path === \"/sign-up/email\") {\n await Promise.all(keys.map((key) => recordDeviceAttempt(key, { ...meta, kind: \"signup\" })))\n return\n }\n\n // /sign-in/email: count failures; a success clears the counter.\n // ponytail: a successful sign-in wipes the device's failure budget, so\n // a credential-stuffing hit resets itself — per-outcome counters if\n // that ceiling ever bites.\n if (failed) {\n await Promise.all(keys.map((key) => recordDeviceAttempt(key, { ...meta, kind: \"signin\" })))\n await logFailedLogin(ctx)\n } else {\n await Promise.all(keys.map((key) => clearDeviceThrottle(key)))\n }\n }),\n },\n\n // Base URL for callbacks\n baseURL,\n\n // Secret for signing cookies and tokens\n secret: process.env.AUTH_SECRET!,\n\n // The OAuth `state` is a one-time verification row: the callback consumes\n // and deletes it. When the same callback URL gets hit twice (browser\n // prefetch / bfcache / double nav — common on a repeat Google sign-in\n // where Google auto-redirects with no consent screen), the first hit\n // logs the user in and the second finds no `state` row and throws\n // `please_restart_the_process`. Default sends that to better-auth's bare\n // error page; point it at /login instead, which already bounces an\n // authenticated user straight to their callbackUrl — so the losing race\n // just lands them logged in. See better-auth#5658 / #6544.\n onAPIError: {\n errorURL: \"/login\",\n },\n\n plugins: [\n // Registered for impersonation (auth.api.impersonateUser / stopImpersonating)\n // only — role/ban/delete stay on our own custom routes above. adminRoles\n // matches our existing role column so the plugin's own permission checks\n // line up with hasRole(role, 'admin'). banReason is remapped onto our\n // existing bannedReason column instead of adding a duplicate field.\n adminPlugin({\n adminRoles: [\"admin\"],\n schema: {\n user: {\n fields: {\n banReason: \"bannedReason\",\n },\n },\n },\n }),\n // TOTP second factor + encrypted backup codes. `issuer` is the label\n // shown in the user's authenticator app.\n twoFactor({ issuer: config.APP_NAME }),\n // WebAuthn passkeys (Touch ID / Windows Hello / security keys) as a\n // passwordless sign-in method. rpID must be the bare host and origin\n // the full URL — both derived from baseURL above.\n passkey({\n rpID: new URL(baseURL).hostname,\n rpName: config.APP_NAME,\n origin: baseURL,\n }),\n // checks to see if oneTap is enabled and if google provider is available, then add the oneTap plugin.\n // Keep this spread last: a leading spread turns `plugins` from a tuple into a plain\n // array and better-auth 1.7 then drops plugin fields (e.g. session.impersonatedBy).\n ...(config.AUTH_ENABLE_ONE_TAP && Boolean(socialProviders.google) ? [oneTap()] : []),\n ]\n })\n}\n\n// Lazy: constructing the default instance validates config and throws if no\n// provider is enabled (see the guard above). Building it eagerly at module\n// load would mean a downstream project that only imports `createAuth` to\n// build its *own* customized instance (README §5) still pays that throw —\n// merely importing this module would run it, regardless of whether the\n// consuming project ever touches this default export. Deferring construction\n// to first property access means the throw only fires for code that actually\n// uses the package's own unconfigured `auth` singleton.\nlet _auth: ReturnType<typeof createAuth> | undefined\nfunction getDefaultAuth() {\n if (!_auth) _auth = createAuth()\n return _auth\n}\n\nexport const auth = new Proxy({} as ReturnType<typeof createAuth>, {\n get(_target, prop) {\n const instance = getDefaultAuth()\n const value = Reflect.get(instance, prop)\n return typeof value === \"function\" ? value.bind(instance) : value\n },\n // \"prop\" in auth (e.g. better-auth's toNextJsHandler does `\"handler\" in auth`)\n // must also see the real instance — without this it checks the empty\n // placeholder target and always reports false.\n has(_target, prop) {\n return Reflect.has(getDefaultAuth(), prop)\n },\n ownKeys(_target) {\n return Reflect.ownKeys(getDefaultAuth())\n },\n getOwnPropertyDescriptor(_target, prop) {\n return Reflect.getOwnPropertyDescriptor(getDefaultAuth(), prop)\n },\n})\n","import \"server-only\"\n// POST handler behind components/dashboard/feedbackDialog.tsx. Shipped as\n// `@xk2800/nextjs-template/api/feedback` so apps mount it with a one-line\n// app/api/feedback/route.ts: export { POST } from \"@xk2800/nextjs-template/api/feedback\"\nimport { NextRequest, NextResponse } from \"next/server\"\nimport { headers } from \"next/headers\"\nimport { z } from \"zod\"\nimport { auth } from \"../server/auth\"\nimport { config } from \"../config/env\"\nimport { getResend, EMAIL_FROM } from \"./resend\"\n\nconst feedbackSchema = z.object({\n type: z.enum([\"bug\", \"feedback\"]),\n // Keep in sync with FEEDBACK_MAX_LENGTH in components/dashboard/feedbackDialog.tsx.\n message: z.string().trim().min(1).max(2000),\n page: z.string().max(300).optional(),\n})\n\nconst COOLDOWN_MS = 30_000\n// const COOLDOWN_MS = 0\n// ponytail: in-memory, per instance; move to a DB/Redis counter if one user\n// spamming across instances ever matters.\nconst lastSentAt = new Map<string, number>()\n\nexport async function POST(request: NextRequest) {\n const discordUrl = config.FEEDBACK_DISCORD_WEBHOOK_URL\n const emailTo = config.FEEDBACK_EMAIL\n if (!discordUrl && !emailTo) {\n return NextResponse.json({ error: \"Feedback is not configured\" }, { status: 503 })\n }\n\n const session = await auth.api.getSession({ headers: await headers() })\n if (!session?.user) {\n return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n }\n\n const parsed = feedbackSchema.safeParse(await request.json().catch(() => null))\n if (!parsed.success) {\n return NextResponse.json({ error: \"Message must be 1-2000 characters\" }, { status: 400 })\n }\n\n const now = Date.now()\n if (now - (lastSentAt.get(session.user.id) ?? 0) < COOLDOWN_MS) {\n return NextResponse.json(\n { error: \"Please wait a moment before sending more feedback\" },\n { status: 429 }\n )\n }\n lastSentAt.set(session.user.id, now)\n\n const { type, message, page } = parsed.data\n const { user } = session\n const title = type === \"bug\" ? \"🐞 Bug report\" : \"💬 Feedback\"\n const userAgent = request.headers.get(\"user-agent\") || \"unknown\"\n\n const sends: Promise<unknown>[] = []\n\n if (discordUrl) {\n sends.push(\n fetch(discordUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n username: config.APP_NAME,\n // User text must never ping @everyone/@here/roles in the channel.\n allowed_mentions: { parse: [] },\n embeds: [{\n title,\n description: message,\n color: type === \"bug\" ? 0xef4444 : 0x3b82f6,\n fields: [\n { name: \"User\", value: `${user.name} (${user.email})`.slice(0, 1024) },\n { name: \"Page\", value: page || \"unknown\", inline: true },\n { name: \"User agent\", value: userAgent.slice(0, 1024) },\n ],\n timestamp: new Date(now).toISOString(),\n }],\n }),\n }).then((res) => {\n if (!res.ok) throw new Error(`Discord webhook responded ${res.status}`)\n })\n )\n }\n\n if (emailTo) {\n sends.push(\n getResend()\n .then((resend) => resend.emails.send({\n from: EMAIL_FROM,\n to: emailTo.split(\",\").map((s) => s.trim()),\n replyTo: user.email,\n subject: `[${config.APP_NAME}] ${title} from ${user.name}`,\n text: `${message}\\n\\n---\\nUser: ${user.name} <${user.email}>\\nPage: ${page || \"unknown\"}\\nUser agent: ${userAgent}`,\n }))\n .then(({ error }) => {\n if (error) throw new Error(error.message)\n })\n )\n }\n\n const results = await Promise.allSettled(sends)\n results.forEach((r) => {\n if (r.status === \"rejected\") console.error(\"Feedback delivery failed:\", r.reason)\n })\n\n if (!results.some((r) => r.status === \"fulfilled\")) {\n lastSentAt.delete(user.id)\n return NextResponse.json({ error: \"Failed to send feedback\" }, { status: 502 })\n }\n return NextResponse.json({ success: true })\n}\n"]}
@@ -0,0 +1,107 @@
1
+ import { auth, getResend, EMAIL_FROM } from '../chunk-GS33BWOL.mjs';
2
+ import '../chunk-G3B7NC5B.mjs';
3
+ import '../chunk-N6ECRCHY.mjs';
4
+ import '../chunk-IFZGEDMN.mjs';
5
+ import '../chunk-LKPF3JCV.mjs';
6
+ import '../chunk-YPKOQOTA.mjs';
7
+ import { config } from '../chunk-TMJYAOC2.mjs';
8
+ import '../chunk-3465SI5N.mjs';
9
+ import 'server-only';
10
+ import { NextResponse } from 'next/server';
11
+ import { headers } from 'next/headers';
12
+ import { z } from 'zod';
13
+
14
+ var feedbackSchema = z.object({
15
+ type: z.enum(["bug", "feedback"]),
16
+ // Keep in sync with FEEDBACK_MAX_LENGTH in components/dashboard/feedbackDialog.tsx.
17
+ message: z.string().trim().min(1).max(2e3),
18
+ page: z.string().max(300).optional()
19
+ });
20
+ var COOLDOWN_MS = 3e4;
21
+ var lastSentAt = /* @__PURE__ */ new Map();
22
+ async function POST(request) {
23
+ var _a;
24
+ const discordUrl = config.FEEDBACK_DISCORD_WEBHOOK_URL;
25
+ const emailTo = config.FEEDBACK_EMAIL;
26
+ if (!discordUrl && !emailTo) {
27
+ return NextResponse.json({ error: "Feedback is not configured" }, { status: 503 });
28
+ }
29
+ const session = await auth.api.getSession({ headers: await headers() });
30
+ if (!(session == null ? void 0 : session.user)) {
31
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
32
+ }
33
+ const parsed = feedbackSchema.safeParse(await request.json().catch(() => null));
34
+ if (!parsed.success) {
35
+ return NextResponse.json({ error: "Message must be 1-2000 characters" }, { status: 400 });
36
+ }
37
+ const now = Date.now();
38
+ if (now - ((_a = lastSentAt.get(session.user.id)) != null ? _a : 0) < COOLDOWN_MS) {
39
+ return NextResponse.json(
40
+ { error: "Please wait a moment before sending more feedback" },
41
+ { status: 429 }
42
+ );
43
+ }
44
+ lastSentAt.set(session.user.id, now);
45
+ const { type, message, page } = parsed.data;
46
+ const { user } = session;
47
+ const title = type === "bug" ? "\u{1F41E} Bug report" : "\u{1F4AC} Feedback";
48
+ const userAgent = request.headers.get("user-agent") || "unknown";
49
+ const sends = [];
50
+ if (discordUrl) {
51
+ sends.push(
52
+ fetch(discordUrl, {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ body: JSON.stringify({
56
+ username: config.APP_NAME,
57
+ // User text must never ping @everyone/@here/roles in the channel.
58
+ allowed_mentions: { parse: [] },
59
+ embeds: [{
60
+ title,
61
+ description: message,
62
+ color: type === "bug" ? 15680580 : 3900150,
63
+ fields: [
64
+ { name: "User", value: `${user.name} (${user.email})`.slice(0, 1024) },
65
+ { name: "Page", value: page || "unknown", inline: true },
66
+ { name: "User agent", value: userAgent.slice(0, 1024) }
67
+ ],
68
+ timestamp: new Date(now).toISOString()
69
+ }]
70
+ })
71
+ }).then((res) => {
72
+ if (!res.ok) throw new Error(`Discord webhook responded ${res.status}`);
73
+ })
74
+ );
75
+ }
76
+ if (emailTo) {
77
+ sends.push(
78
+ getResend().then((resend) => resend.emails.send({
79
+ from: EMAIL_FROM,
80
+ to: emailTo.split(",").map((s) => s.trim()),
81
+ replyTo: user.email,
82
+ subject: `[${config.APP_NAME}] ${title} from ${user.name}`,
83
+ text: `${message}
84
+
85
+ ---
86
+ User: ${user.name} <${user.email}>
87
+ Page: ${page || "unknown"}
88
+ User agent: ${userAgent}`
89
+ })).then(({ error }) => {
90
+ if (error) throw new Error(error.message);
91
+ })
92
+ );
93
+ }
94
+ const results = await Promise.allSettled(sends);
95
+ results.forEach((r) => {
96
+ if (r.status === "rejected") console.error("Feedback delivery failed:", r.reason);
97
+ });
98
+ if (!results.some((r) => r.status === "fulfilled")) {
99
+ lastSentAt.delete(user.id);
100
+ return NextResponse.json({ error: "Failed to send feedback" }, { status: 502 });
101
+ }
102
+ return NextResponse.json({ success: true });
103
+ }
104
+
105
+ export { POST };
106
+ //# sourceMappingURL=feedback.mjs.map
107
+ //# sourceMappingURL=feedback.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../lib/feedback-route.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAWA,IAAM,cAAA,GAAiB,EAAE,MAAA,CAAO;AAAA,EAC9B,MAAM,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,UAAU,CAAC,CAAA;AAAA;AAAA,EAEhC,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,IAAA,GAAO,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,GAAI,CAAA;AAAA,EAC1C,MAAM,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,GAAG,EAAE,QAAA;AAC5B,CAAC,CAAA;AAED,IAAM,WAAA,GAAc,GAAA;AAIpB,IAAM,UAAA,uBAAiB,GAAA,EAAoB;AAE3C,eAAsB,KAAK,OAAA,EAAsB;AAxBjD,EAAA,IAAA,EAAA;AAyBE,EAAA,MAAM,aAAa,MAAA,CAAO,4BAAA;AAC1B,EAAA,MAAM,UAAU,MAAA,CAAO,cAAA;AACvB,EAAA,IAAI,CAAC,UAAA,IAAc,CAAC,OAAA,EAAS;AAC3B,IAAA,OAAO,YAAA,CAAa,KAAK,EAAE,KAAA,EAAO,8BAA6B,EAAG,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAAA,EACnF;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA,CAAI,UAAA,CAAW,EAAE,OAAA,EAAS,MAAM,OAAA,EAAQ,EAAG,CAAA;AACtE,EAAA,IAAI,EAAC,mCAAS,IAAA,CAAA,EAAM;AAClB,IAAA,OAAO,YAAA,CAAa,KAAK,EAAE,KAAA,EAAO,gBAAe,EAAG,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,MAAA,GAAS,cAAA,CAAe,SAAA,CAAU,MAAM,OAAA,CAAQ,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAC,CAAA;AAC9E,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,YAAA,CAAa,KAAK,EAAE,KAAA,EAAO,qCAAoC,EAAG,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAAA,EAC1F;AAEA,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,IAAI,GAAA,IAAA,CAAO,gBAAW,GAAA,CAAI,OAAA,CAAQ,KAAK,EAAE,CAAA,KAA9B,IAAA,GAAA,EAAA,GAAmC,CAAA,CAAA,GAAK,WAAA,EAAa;AAC9D,IAAA,OAAO,YAAA,CAAa,IAAA;AAAA,MAClB,EAAE,OAAO,mDAAA,EAAoD;AAAA,MAC7D,EAAE,QAAQ,GAAA;AAAI,KAChB;AAAA,EACF;AACA,EAAA,UAAA,CAAW,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,EAAA,EAAI,GAAG,CAAA;AAEnC,EAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,KAAS,MAAA,CAAO,IAAA;AACvC,EAAA,MAAM,EAAE,MAAK,GAAI,OAAA;AACjB,EAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,KAAA,GAAQ,sBAAA,GAAkB,oBAAA;AACjD,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,IAAK,SAAA;AAEvD,EAAA,MAAM,QAA4B,EAAC;AAEnC,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,MAAM,UAAA,EAAY;AAAA,QAChB,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,QAC9C,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,UACnB,UAAU,MAAA,CAAO,QAAA;AAAA;AAAA,UAEjB,gBAAA,EAAkB,EAAE,KAAA,EAAO,EAAC,EAAE;AAAA,UAC9B,QAAQ,CAAC;AAAA,YACP,KAAA;AAAA,YACA,WAAA,EAAa,OAAA;AAAA,YACb,KAAA,EAAO,IAAA,KAAS,KAAA,GAAQ,QAAA,GAAW,OAAA;AAAA,YACnC,MAAA,EAAQ;AAAA,cACN,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,IAAA,CAAK,KAAK,CAAA,CAAA,CAAA,CAAI,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA,EAAE;AAAA,cACrE,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAO,IAAA,IAAQ,SAAA,EAAW,QAAQ,IAAA,EAAK;AAAA,cACvD,EAAE,MAAM,YAAA,EAAc,KAAA,EAAO,UAAU,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA;AAAE,aACxD;AAAA,YACA,SAAA,EAAW,IAAI,IAAA,CAAK,GAAG,EAAE,WAAA;AAAY,WACtC;AAAA,SACF;AAAA,OACF,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ;AACf,QAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,MACxE,CAAC;AAAA,KACH;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,KAAA,CAAM,IAAA;AAAA,MACJ,WAAU,CACP,IAAA,CAAK,CAAC,MAAA,KAAW,MAAA,CAAO,OAAO,IAAA,CAAK;AAAA,QACnC,IAAA,EAAM,UAAA;AAAA,QACN,EAAA,EAAI,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AAAA,QAC1C,SAAS,IAAA,CAAK,KAAA;AAAA,QACd,OAAA,EAAS,IAAI,MAAA,CAAO,QAAQ,KAAK,KAAK,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAAA,QACxD,IAAA,EAAM,GAAG,OAAO;;AAAA;AAAA,MAAA,EAAkB,IAAA,CAAK,IAAI,CAAA,EAAA,EAAK,IAAA,CAAK,KAAK,CAAA;AAAA,MAAA,EAAY,QAAQ,SAAS;AAAA,YAAA,EAAiB,SAAS,CAAA;AAAA,OAClH,CAAC,CAAA,CACD,KAAK,CAAC,EAAE,OAAM,KAAM;AACnB,QAAA,IAAI,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,MAAM,OAAO,CAAA;AAAA,MAC1C,CAAC;AAAA,KACL;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA;AAC9C,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM;AACrB,IAAA,IAAI,EAAE,MAAA,KAAW,UAAA,UAAoB,KAAA,CAAM,2BAAA,EAA6B,EAAE,MAAM,CAAA;AAAA,EAClF,CAAC,CAAA;AAED,EAAA,IAAI,CAAC,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,WAAW,CAAA,EAAG;AAClD,IAAA,UAAA,CAAW,MAAA,CAAO,KAAK,EAAE,CAAA;AACzB,IAAA,OAAO,YAAA,CAAa,KAAK,EAAE,KAAA,EAAO,2BAA0B,EAAG,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAAA,EAChF;AACA,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,EAAE,OAAA,EAAS,MAAM,CAAA;AAC5C","file":"feedback.mjs","sourcesContent":["import \"server-only\"\n// POST handler behind components/dashboard/feedbackDialog.tsx. Shipped as\n// `@xk2800/nextjs-template/api/feedback` so apps mount it with a one-line\n// app/api/feedback/route.ts: export { POST } from \"@xk2800/nextjs-template/api/feedback\"\nimport { NextRequest, NextResponse } from \"next/server\"\nimport { headers } from \"next/headers\"\nimport { z } from \"zod\"\nimport { auth } from \"../server/auth\"\nimport { config } from \"../config/env\"\nimport { getResend, EMAIL_FROM } from \"./resend\"\n\nconst feedbackSchema = z.object({\n type: z.enum([\"bug\", \"feedback\"]),\n // Keep in sync with FEEDBACK_MAX_LENGTH in components/dashboard/feedbackDialog.tsx.\n message: z.string().trim().min(1).max(2000),\n page: z.string().max(300).optional(),\n})\n\nconst COOLDOWN_MS = 30_000\n// const COOLDOWN_MS = 0\n// ponytail: in-memory, per instance; move to a DB/Redis counter if one user\n// spamming across instances ever matters.\nconst lastSentAt = new Map<string, number>()\n\nexport async function POST(request: NextRequest) {\n const discordUrl = config.FEEDBACK_DISCORD_WEBHOOK_URL\n const emailTo = config.FEEDBACK_EMAIL\n if (!discordUrl && !emailTo) {\n return NextResponse.json({ error: \"Feedback is not configured\" }, { status: 503 })\n }\n\n const session = await auth.api.getSession({ headers: await headers() })\n if (!session?.user) {\n return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n }\n\n const parsed = feedbackSchema.safeParse(await request.json().catch(() => null))\n if (!parsed.success) {\n return NextResponse.json({ error: \"Message must be 1-2000 characters\" }, { status: 400 })\n }\n\n const now = Date.now()\n if (now - (lastSentAt.get(session.user.id) ?? 0) < COOLDOWN_MS) {\n return NextResponse.json(\n { error: \"Please wait a moment before sending more feedback\" },\n { status: 429 }\n )\n }\n lastSentAt.set(session.user.id, now)\n\n const { type, message, page } = parsed.data\n const { user } = session\n const title = type === \"bug\" ? \"🐞 Bug report\" : \"💬 Feedback\"\n const userAgent = request.headers.get(\"user-agent\") || \"unknown\"\n\n const sends: Promise<unknown>[] = []\n\n if (discordUrl) {\n sends.push(\n fetch(discordUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n username: config.APP_NAME,\n // User text must never ping @everyone/@here/roles in the channel.\n allowed_mentions: { parse: [] },\n embeds: [{\n title,\n description: message,\n color: type === \"bug\" ? 0xef4444 : 0x3b82f6,\n fields: [\n { name: \"User\", value: `${user.name} (${user.email})`.slice(0, 1024) },\n { name: \"Page\", value: page || \"unknown\", inline: true },\n { name: \"User agent\", value: userAgent.slice(0, 1024) },\n ],\n timestamp: new Date(now).toISOString(),\n }],\n }),\n }).then((res) => {\n if (!res.ok) throw new Error(`Discord webhook responded ${res.status}`)\n })\n )\n }\n\n if (emailTo) {\n sends.push(\n getResend()\n .then((resend) => resend.emails.send({\n from: EMAIL_FROM,\n to: emailTo.split(\",\").map((s) => s.trim()),\n replyTo: user.email,\n subject: `[${config.APP_NAME}] ${title} from ${user.name}`,\n text: `${message}\\n\\n---\\nUser: ${user.name} <${user.email}>\\nPage: ${page || \"unknown\"}\\nUser agent: ${userAgent}`,\n }))\n .then(({ error }) => {\n if (error) throw new Error(error.message)\n })\n )\n }\n\n const results = await Promise.allSettled(sends)\n results.forEach((r) => {\n if (r.status === \"rejected\") console.error(\"Feedback delivery failed:\", r.reason)\n })\n\n if (!results.some((r) => r.status === \"fulfilled\")) {\n lastSentAt.delete(user.id)\n return NextResponse.json({ error: \"Failed to send feedback\" }, { status: 502 })\n }\n return NextResponse.json({ success: true })\n}\n"]}
@@ -27,8 +27,8 @@ declare const getCurrentSession: () => Promise<{
27
27
  image?: string | null | undefined | undefined;
28
28
  role: string | null | undefined;
29
29
  banned: boolean | null | undefined;
30
- banReason?: string | null | undefined;
31
30
  banExpires?: Date | null | undefined;
31
+ banReason?: string | null | undefined;
32
32
  twoFactorEnabled: boolean | null | undefined;
33
33
  };
34
34
  } | null>;
@@ -61,8 +61,8 @@ declare const requireAuth: () => Promise<{
61
61
  image?: string | null | undefined | undefined;
62
62
  role: string | null | undefined;
63
63
  banned: boolean | null | undefined;
64
- banReason?: string | null | undefined;
65
64
  banExpires?: Date | null | undefined;
65
+ banReason?: string | null | undefined;
66
66
  twoFactorEnabled: boolean | null | undefined;
67
67
  };
68
68
  }>;
@@ -95,8 +95,8 @@ declare function requireRole(requiredRole: 'user' | 'admin'): Promise<{
95
95
  image?: string | null | undefined | undefined;
96
96
  role: string | null | undefined;
97
97
  banned: boolean | null | undefined;
98
- banReason?: string | null | undefined;
99
98
  banExpires?: Date | null | undefined;
99
+ banReason?: string | null | undefined;
100
100
  twoFactorEnabled: boolean | null | undefined;
101
101
  };
102
102
  }>;
@@ -27,8 +27,8 @@ declare const getCurrentSession: () => Promise<{
27
27
  image?: string | null | undefined | undefined;
28
28
  role: string | null | undefined;
29
29
  banned: boolean | null | undefined;
30
- banReason?: string | null | undefined;
31
30
  banExpires?: Date | null | undefined;
31
+ banReason?: string | null | undefined;
32
32
  twoFactorEnabled: boolean | null | undefined;
33
33
  };
34
34
  } | null>;
@@ -61,8 +61,8 @@ declare const requireAuth: () => Promise<{
61
61
  image?: string | null | undefined | undefined;
62
62
  role: string | null | undefined;
63
63
  banned: boolean | null | undefined;
64
- banReason?: string | null | undefined;
65
64
  banExpires?: Date | null | undefined;
65
+ banReason?: string | null | undefined;
66
66
  twoFactorEnabled: boolean | null | undefined;
67
67
  };
68
68
  }>;
@@ -95,8 +95,8 @@ declare function requireRole(requiredRole: 'user' | 'admin'): Promise<{
95
95
  image?: string | null | undefined | undefined;
96
96
  role: string | null | undefined;
97
97
  banned: boolean | null | undefined;
98
- banReason?: string | null | undefined;
99
98
  banExpires?: Date | null | undefined;
99
+ banReason?: string | null | undefined;
100
100
  twoFactorEnabled: boolean | null | undefined;
101
101
  };
102
102
  }>;
@@ -1,4 +1,4 @@
1
- import { auth } from '../chunk-SAO257GQ.mjs';
1
+ import { auth } from '../chunk-GS33BWOL.mjs';
2
2
  import '../chunk-G3B7NC5B.mjs';
3
3
  import '../chunk-N6ECRCHY.mjs';
4
4
  import '../chunk-IFZGEDMN.mjs';
@@ -1,4 +1,4 @@
1
- export { auth, createAuth } from '../chunk-SAO257GQ.mjs';
1
+ export { auth, createAuth } from '../chunk-GS33BWOL.mjs';
2
2
  import '../chunk-G3B7NC5B.mjs';
3
3
  import '../chunk-N6ECRCHY.mjs';
4
4
  import '../chunk-IFZGEDMN.mjs';
@@ -826,8 +826,8 @@ declare const authClient: better_auth_react.ReactAuthClient<{
826
826
  $Infer: {
827
827
  body: {
828
828
  permissions: {
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;
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;
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?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
920
- readonly session?: ("list" | "delete" | "revoke")[] | undefined;
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;
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
- banReason?: string | null | undefined;
2427
2426
  banExpires?: Date | null | undefined;
2427
+ banReason?: string | 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: (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";
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 & {});
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: (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";
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 & {});
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: (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";
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 & {});
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
- banReason?: string | null | undefined;
2537
2536
  banExpires?: Date | null | undefined;
2537
+ banReason?: string | 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
- banReason?: string | null | undefined;
2585
2584
  banExpires?: Date | null | undefined;
2585
+ banReason?: string | 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?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3485
- readonly session?: ("list" | "delete" | "revoke")[] | undefined;
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;
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?: ("create" | "list" | "set-role" | "ban" | "impersonate" | "impersonate-admins" | "delete" | "set-password" | "set-email" | "get" | "update")[] | undefined;
3575
- readonly session?: ("list" | "delete" | "revoke")[] | undefined;
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;
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
- banReason?: string | null | undefined;
5159
5158
  banExpires?: Date | null | undefined;
5159
+ banReason?: string | 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
- banReason?: string | null | undefined;
5188
5187
  banExpires?: Date | null | undefined;
5188
+ banReason?: string | null | undefined;
5189
5189
  } & {
5190
5190
  twoFactorEnabled: boolean | null | undefined;
5191
5191
  } & {}>;