Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@
"./config": {
"types": "./dist/runtime/config.d.ts",
"import": "./dist/runtime/config.js"
},
"./adapters/convex": {
"types": "./dist/runtime/adapters/convex.d.ts",
"import": "./dist/runtime/adapters/convex.js"
}
},
"main": "./dist/module.mjs",
Expand All @@ -40,9 +36,6 @@
],
"config": [
"./dist/runtime/config.d.ts"
],
"adapters/convex": [
"./dist/runtime/adapters/convex.d.ts"
]
}
},
Expand All @@ -67,15 +60,11 @@
},
"peerDependencies": {
"@nuxthub/core": ">=0.10.5",
"better-auth": ">=1.0.0",
"convex": ">=1.25.0"
"better-auth": ">=1.0.0"
},
"peerDependenciesMeta": {
"@nuxthub/core": {
"optional": true
},
"convex": {
"optional": true
}
},
"dependencies": {
Expand All @@ -91,6 +80,7 @@
"devDependencies": {
"@antfu/eslint-config": "^4.12.0",
"@libsql/client": "^0.15.15",
"@libsql/linux-x64-gnu": "0.5.22",
"@nuxt/devtools": "^3.1.1",
"@nuxt/devtools-kit": "^3.1.1",
"@nuxt/module-builder": "^1.0.2",
Expand Down
399 changes: 189 additions & 210 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

53 changes: 17 additions & 36 deletions src/database-provider.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
export type DatabaseProvider = 'none' | 'nuxthub' | 'convex'
import type { ModuleDatabaseProviderId } from './runtime/config'
import type { BetterAuthDatabaseProviderDefinition, BetterAuthDatabaseProviderEnabledContext } from './types/hooks'

export interface ResolveDatabaseProviderInput {
clientOnly: boolean
hasHubDb: boolean
hasConvexModule: boolean
convexUrl: string
selectedProvider?: DatabaseProvider
providers: Record<string, BetterAuthDatabaseProviderDefinition>
context: BetterAuthDatabaseProviderEnabledContext
}

export interface ResolvedDatabaseProvider {
provider: DatabaseProvider
convexUrl: string
id: ModuleDatabaseProviderId
definition: BetterAuthDatabaseProviderDefinition
}

/**
* Resolves the database provider by filtering enabled providers and selecting
* the highest priority (default 0). Ties preserve registration order.
*/
export function resolveDatabaseProvider(input: ResolveDatabaseProviderInput): ResolvedDatabaseProvider {
const provider = input.selectedProvider
const enabledProviders = Object.entries(input.providers)
.filter(([_id, provider]) => provider.isEnabled?.(input.context) ?? true)

if (provider === 'nuxthub') {
if (!input.hasHubDb) {
throw new Error('[nuxt-better-auth] auth.database.provider is set to "nuxthub", but @nuxthub/core with hub.db is not configured.')
}
return { provider: 'nuxthub', convexUrl: '' }
if (!enabledProviders.length) {
throw new Error('[nuxt-better-auth] No database provider is enabled. Register one with the better-auth:database:providers hook.')
}

if (provider === 'convex') {
if (input.clientOnly) {
throw new Error('[nuxt-better-auth] auth.database.provider "convex" is not available in clientOnly mode.')
}
if (!input.hasConvexModule) {
throw new Error('[nuxt-better-auth] auth.database.provider is set to "convex", but nuxt-convex is not installed.')
}
if (!input.convexUrl) {
throw new Error('[nuxt-better-auth] auth.database.provider is set to "convex", but no Convex URL was found. Set auth.database.convexUrl, convex.url, runtimeConfig.public.convex.url, CONVEX_URL, or NUXT_PUBLIC_CONVEX_URL.')
}
return { provider: 'convex', convexUrl: input.convexUrl }
}

if (provider === 'none') {
return { provider: 'none', convexUrl: '' }
}

if (input.hasHubDb) {
return { provider: 'nuxthub', convexUrl: '' }
}

return { provider: 'none', convexUrl: '' }
enabledProviders.sort((a, b) => (b[1].priority ?? 0) - (a[1].priority ?? 0))
const [id, definition] = enabledProviders[0]
return { id: id as ModuleDatabaseProviderId, definition }
}
122 changes: 78 additions & 44 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import type { NuxtHubOptions } from './module/hub'
import type { BetterAuthModuleOptions } from './runtime/config'
import type { BetterAuthModuleOptions, ModuleDatabaseProviderId } from './runtime/config'
import type {
BetterAuthDatabaseProviderBuildContext,
BetterAuthDatabaseProviderDefinition,
BetterAuthDatabaseProviderEnabledContext,
BetterAuthDatabaseProviderSetupContext,
} from './types/hooks'
import { existsSync } from 'node:fs'
import { mkdir, writeFile } from 'node:fs/promises'
import { addTemplate, createResolver, defineNuxtModule, hasNuxtModule } from '@nuxt/kit'
import { consola as _consola } from 'consola'
import { dirname, join } from 'pathe'
import { version } from '../package.json'
import { resolveDatabaseProvider } from './database-provider'
import { resolveConvexUrl } from './module/convex'
import { registerAuthMiddlewareHook, registerDevtools, registerRouteRulesMetaHook, registerServerRuntime, registerTemplateHmrHook } from './module/hooks'
import { getHubCasing, getHubDialect } from './module/hub'
import { setupRuntimeConfig } from './module/runtime'
import { setupBetterAuthSchema, setupConvexAuthSchema } from './module/schema'
import { setupBetterAuthSchema } from './module/schema'
import { promptForSecret } from './module/secret'
import { applyConvexRuntimeConfig, buildDatabaseCode, buildSecondaryStorageCode } from './module/templates'
import { buildDatabaseCode, buildSecondaryStorageCode } from './module/templates'
import { registerServerTypeTemplates, registerSharedTypeTemplates } from './module/type-templates'

import './types/hooks'
Expand Down Expand Up @@ -100,40 +105,75 @@ export default defineNuxtModule<BetterAuthModuleOptions>({
const hasNuxtHub = hasNuxtModule('@nuxthub/core', nuxt)
const hub = hasNuxtHub ? (nuxt.options as { hub?: NuxtHubOptions }).hub : undefined
const hasHubDbAvailable = !clientOnly && hasNuxtHub && !!hub?.db
const hasConvexModule = hasNuxtModule('nuxt-convex', nuxt)
const detectedConvexUrl = resolveConvexUrl(nuxt, options.database?.convexUrl)

const resolvedDatabase = resolveDatabaseProvider({
clientOnly,
hasHubDb: hasHubDbAvailable,
hasConvexModule,
convexUrl: detectedConvexUrl,
selectedProvider: options.database?.provider,
})
const deprecatedProvider = (options as { database?: { provider?: string } }).database?.provider
if (deprecatedProvider) {
throw new Error(
`[nuxt-better-auth] auth.database.provider has been removed. Remove auth.database.provider="${deprecatedProvider}". To configure a database, either set "database" directly in server/auth.config.ts (defineServerAuth) or install a provider module that registers better-auth:database:providers.`,
)
}

const databaseProvider = resolvedDatabase.provider
const hasHubDb = databaseProvider === 'nuxthub'
const hasConvexDb = databaseProvider === 'convex'

if (hasConvexDb)
consola.info('Using Convex HTTP adapter for Better Auth database')

const { secondaryStorageEnabled } = setupRuntimeConfig({
nuxt,
options,
clientOnly,
databaseProvider,
hasNuxtHub,
hub,
consola,
})
let databaseProvider: ModuleDatabaseProviderId = 'none'
let hasHubDb = false

nuxt.options.alias['#nuxt-better-auth'] = resolver.resolve('./runtime/types/augment')
if (!clientOnly)
nuxt.options.alias['#auth/server'] = serverConfigPath
nuxt.options.alias['#auth/client'] = clientConfigPath

if (!clientOnly) {
if (clientOnly) {
setupRuntimeConfig({
nuxt,
options,
clientOnly,
databaseProvider,
hasNuxtHub,
hub,
consola,
})
}
else {
const hubDialect = getHubDialect(hub) ?? 'sqlite'
const usePlural = options.schema?.usePlural ?? false
const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== 'snake_case'

const providers: Record<string, BetterAuthDatabaseProviderDefinition> = {
nuxthub: {
priority: 100,
isEnabled: ({ hasHubDbAvailable }) => hasHubDbAvailable,
buildDatabaseCode: () => buildDatabaseCode({
provider: 'nuxthub',
hubDialect,
usePlural,
camelCase,
}),
},
none: {
priority: 0,
buildDatabaseCode: () => buildDatabaseCode({
provider: 'none',
hubDialect,
usePlural,
camelCase,
}),
},
}

const enabledCtx: BetterAuthDatabaseProviderEnabledContext = { nuxt, options, clientOnly, hasHubDbAvailable }
await nuxt.callHook('better-auth:database:providers', providers)
const resolvedProvider = resolveDatabaseProvider({ providers, context: enabledCtx })
databaseProvider = resolvedProvider.id
hasHubDb = databaseProvider === 'nuxthub'

const { secondaryStorageEnabled } = setupRuntimeConfig({
nuxt,
options,
clientOnly,
databaseProvider,
hasNuxtHub,
hub,
consola,
})

if (secondaryStorageEnabled && !nuxt.options.alias['hub:kv']) {
throw new Error('[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.')
}
Expand All @@ -149,16 +189,13 @@ export default defineNuxtModule<BetterAuthModuleOptions>({
throw new Error('[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.')
}

const hubDialect = getHubDialect(hub) ?? 'sqlite'
const usePlural = options.schema?.usePlural ?? false
const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== 'snake_case'

if (hasConvexDb)
applyConvexRuntimeConfig(nuxt, resolvedDatabase.convexUrl)
const setupCtx: BetterAuthDatabaseProviderSetupContext = { nuxt, options, clientOnly }
await resolvedProvider.definition.setup?.(setupCtx)

const buildCtx: BetterAuthDatabaseProviderBuildContext = { hubDialect, usePlural, camelCase }
const databaseTemplate = addTemplate({
filename: 'better-auth/database.mjs',
getContents: () => buildDatabaseCode({ provider: databaseProvider, hubDialect, usePlural, camelCase }),
getContents: () => resolvedProvider.definition.buildDatabaseCode(buildCtx),
write: true,
})
nuxt.options.alias['#auth/database'] = databaseTemplate.dst
Expand All @@ -168,6 +205,9 @@ export default defineNuxtModule<BetterAuthModuleOptions>({
hasHubDb,
runtimeTypesPath: resolver.resolve('./runtime/types'),
})

if (hasHubDb)
await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola)
}

registerSharedTypeTemplates({
Expand All @@ -180,12 +220,6 @@ export default defineNuxtModule<BetterAuthModuleOptions>({
registerServerRuntime({ clientOnly, resolve: resolver.resolve })
registerAuthMiddlewareHook(nuxt, resolver.resolve)

if (hasHubDb)
await setupBetterAuthSchema(nuxt, serverConfigPath, options, consola)

if (hasConvexDb)
await setupConvexAuthSchema(nuxt, serverConfigPath, consola)

await registerDevtools({ nuxt, clientOnly, hasHubDb, resolve: resolver.resolve })
registerRouteRulesMetaHook(nuxt)
},
Expand Down
22 changes: 0 additions & 22 deletions src/module/convex.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/module/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { Nuxt } from '@nuxt/schema'
import type { ConsolaInstance } from 'consola'
import type { DatabaseProvider } from '../database-provider'
import type { AuthPrivateRuntimeConfig, AuthRuntimeConfig, BetterAuthModuleOptions } from '../runtime/config'
import type { AuthPrivateRuntimeConfig, AuthRuntimeConfig, BetterAuthModuleOptions, ModuleDatabaseProviderId } from '../runtime/config'
import type { NuxtHubOptions } from './hub'
import { defu } from 'defu'

interface SetupRuntimeConfigInput {
nuxt: Nuxt
options: BetterAuthModuleOptions
clientOnly: boolean
databaseProvider: DatabaseProvider
databaseProvider: ModuleDatabaseProviderId
hasNuxtHub: boolean
hub?: NuxtHubOptions
consola: ConsolaInstance
Expand Down Expand Up @@ -44,6 +43,7 @@ export function setupRuntimeConfig(input: SetupRuntimeConfigInput): { secondaryS
redirects: { login: options.redirects?.login ?? '/login', guest: options.redirects?.guest ?? '/' },
useDatabase: databaseProvider !== 'none',
databaseProvider,
databaseSource: 'module',
clientOnly,
session: {
skipHydratedSsrGetSession: options.session?.skipHydratedSsrGetSession ?? false,
Expand Down
29 changes: 1 addition & 28 deletions src/module/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { existsSync } from 'node:fs'
import { mkdir, writeFile } from 'node:fs/promises'
import { addTemplate } from '@nuxt/kit'
import { join } from 'pathe'
import { generateConvexSchema, generateDrizzleSchema, loadUserAuthConfig } from '../schema-generator'
import { generateDrizzleSchema, loadUserAuthConfig } from '../schema-generator'
import { getHubCasing, getHubDialect } from './hub'

interface SchemaContext {
Expand Down Expand Up @@ -90,30 +90,3 @@ export async function setupBetterAuthSchema(
consola.error('Failed to generate schema:', error)
}
}

export async function setupConvexAuthSchema(nuxt: Nuxt, serverConfigPath: string, consola: ConsolaInstance): Promise<void> {
const context: SchemaContext = { nuxt, serverConfigPath }

try {
const { userConfig, plugins } = await loadAuthOptions(context)
const authOptions = { ...userConfig, plugins }
const schemaCode = await generateConvexSchema(authOptions)

const schemaDir = join(nuxt.options.buildDir, 'better-auth')
const schemaPath = join(schemaDir, 'auth-tables.convex.ts')

await mkdir(schemaDir, { recursive: true })
await writeFile(schemaPath, schemaCode)

addTemplate({ filename: 'better-auth/auth-tables.convex.ts', getContents: () => schemaCode, write: true })
nuxt.options.alias['#auth/convex-schema'] = schemaPath

consola.info('Generated Convex auth schema at .nuxt/better-auth/auth-tables.convex.ts')
}
catch (error) {
const isProduction = !nuxt.options.dev
if (isProduction)
throw error
consola.error('Failed to generate Convex schema:', error)
}
}
Loading
Loading