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
52 changes: 40 additions & 12 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface ModuleOptions {
* @docs https://supabase.com/blog/jwt-signing-keys
*/
secretKey: string

/**
* Redirect automatically to login page if user is not authenticated
* @default `true`
Expand Down Expand Up @@ -181,11 +182,27 @@ export default defineNuxtModule<ModuleOptions>({
logger.warn('Missing supabase url, set it either in `nuxt.config.ts` or via env variable')
}
else {
// Use the default storage key as defined by the supabase-js client if no cookiePrefix is set.
// Source: https://github.com/supabase/supabase-js/blob/3316f2426d7c2e5babaab7ddc17c30bfa189f500/src/SupabaseClient.ts#L86
const defaultStorageKey = `sb-${new URL(finalUrl).hostname.split('.')[0]}-auth-token`
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || defaultStorageKey
try {
// Use the default storage key as defined by the supabase-js client if no cookiePrefix is set.
// Source: https://github.com/supabase/supabase-js/blob/3316f2426d7c2e5babaab7ddc17c30bfa189f500/src/SupabaseClient.ts#L86
const defaultStorageKey = `sb-${new URL(finalUrl).hostname.split('.')[0]}-auth-token`
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || defaultStorageKey
}
catch (error) {
logger.error(
`Invalid Supabase URL: "${finalUrl}". `
+ `Please provide a valid URL (e.g., https://example.supabase.co or http://localhost:5432)`, error)

// Use fallback prefix
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || 'sb-auth-token'

// Fail build in production
if (!nuxt.options.dev) {
throw new Error('Invalid Supabase URL configuration')
}
}
}

// Warn if the key isn't set.
Expand Down Expand Up @@ -266,13 +283,24 @@ export default defineNuxtModule<ModuleOptions>({
filename: 'types/supabase-database.d.ts',
getContents: async () => {
if (options.types) {
// resolvePath is used to minify user input error.
const path = await resolvePath(options.types)
const typesPath = await resolvePath('~~/.nuxt/types/') // this is the default path for nuxt types

if (fs.existsSync(path)) {
// Make the path relative to the "types" directory.
return `export * from '${relative(typesPath, path)}'`
try {
// resolvePath is used to minify user input error.
const path = await resolvePath(options.types)
const typesPath = await resolvePath('~~/.nuxt/types/') // this is the default path for nuxt types

if (fs.existsSync(path)) {
// Make the path relative to the "types" directory.
return `export * from '${relative(typesPath, path)}'`
}
else {
logger.warn(
`Database types configured at "${options.types}" but file not found at "${path}". `
+ `Using "Database = unknown".`,
)
}
}
catch (error) {
logger.error(`Failed to load Supabase database types from "${options.types}":`, error)
}
}

Expand Down
23 changes: 13 additions & 10 deletions src/runtime/plugins/auth-redirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import type { Plugin } from '#app'
import { defineNuxtPlugin, addRouteMiddleware, defineNuxtRouteMiddleware, useRuntimeConfig, navigateTo } from '#imports'
import type { RouteLocationNormalized } from '#vue-router'

function matchesAnyPattern(path: string, patterns: (string | undefined)[]): boolean {
return patterns.some((pattern) => {
if (!pattern) return false
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
return regex.test(path)
})
}

export default defineNuxtPlugin({
name: 'auth-redirect',
setup() {
Expand All @@ -15,21 +23,16 @@ export default defineNuxtPlugin({

// Redirect only on included routes (if defined)
if (include && include.length > 0) {
const isIncluded = include.some((path: string) => {
const regex = new RegExp(`^${path.replace(/\*/g, '.*')}$`)
return regex.test(to.path)
})
if (!isIncluded) {
if (!matchesAnyPattern(to.path, include)) {
return
}
}

// Do not redirect on login route, callback route and excluded routes
const isExcluded = [...exclude ?? [], login, callback]?.some((path) => {
const regex = new RegExp(`^${path.replace(/\*/g, '.*')}$`)
return regex.test(to.path)
})
if (isExcluded) return
const excludePatterns = [login, callback, ...(exclude ?? [])]
if (matchesAnyPattern(to.path, excludePatterns)) {
return
}

const session = useSupabaseSession()
if (!session.value) {
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/server/services/serverSupabaseServiceRole.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SupabaseClient } from '@supabase/supabase-js'
import { createClient } from '@supabase/supabase-js'
import { fetchWithRetry } from '../../utils/fetch-retry'
import type { H3Event } from 'h3'
import { useRuntimeConfig } from '#imports'
// @ts-expect-error - `#supabase/database` is a runtime alias
Expand Down Expand Up @@ -27,6 +28,9 @@ export const serverSupabaseServiceRole: <T = Database>(event: H3Event) => Supaba
persistSession: false,
autoRefreshToken: false,
},
global: {
fetch: fetchWithRetry,
},
})
}

Expand Down
3 changes: 3 additions & 0 deletions src/runtime/utils/fetch-retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export async function fetchWithRetry(req: RequestInfo | URL, init?: RequestInit)
throw error
}
console.warn(`Retrying fetch attempt ${attempt + 1} for request: ${req}`)

// Small incremental delay before retry
await new Promise(resolve => setTimeout(resolve, 100 * attempt))
}
}
throw new Error('Unreachable code')
Expand Down
Loading