UNPKG

better-auth-cloudflare

Version:

Seamlessly integrate better-auth with Cloudflare Workers, D1, Hyperdrive, KV, R2, and geolocation services.

173 lines (169 loc) 6 kB
'use strict'; const drizzle = require('better-auth/adapters/drizzle'); const api = require('better-auth/api'); const schema = require('./schema.cjs'); const r2 = require('./r2.cjs'); const client = require('./client.cjs'); const types = require('./types.cjs'); require('zod'); const cloudflare = (options) => { const opts = options ?? {}; const geolocationTrackingEnabled = opts.geolocationTracking === void 0 || opts.geolocationTracking; let r2Storage = null; return { id: "cloudflare", schema: schema.schema(opts), endpoints: { getGeolocation: api.createAuthEndpoint( "/cloudflare/geolocation", { method: "GET" }, async (ctx) => { const session = await api.getSessionFromCtx(ctx); if (!session) { return ctx.json({ error: "Unauthorized" }, { status: 401 }); } const cf = await Promise.resolve(opts.cf); if (!cf) { return ctx.json({ error: "Cloudflare context is not available" }, { status: 404 }); } const context = extractGeolocationData(cf); return ctx.json(context); } ), ...opts.r2 ? r2.createR2Endpoints(() => r2Storage, opts.r2) : {} }, init(init_ctx) { if (opts.r2) { r2Storage = r2.createR2Storage(opts.r2, init_ctx.generateId); } return { options: { databaseHooks: { session: { create: { before: async (s) => { if (!geolocationTrackingEnabled) { return s; } const cf = await Promise.resolve(opts.cf); if (!cf) { return s; } const geoData = extractGeolocationData(cf); s.timezone = geoData.timezone; s.city = geoData.city; s.country = geoData.country; s.region = geoData.region; s.regionCode = geoData.regionCode; s.colo = geoData.colo; s.latitude = geoData.latitude; s.longitude = geoData.longitude; return s; } } } } } }; } }; }; function extractGeolocationData(input) { if (!input || typeof input !== "object") { return {}; } return { timezone: input.timezone || void 0, city: input.city || void 0, country: input.country || void 0, region: input.region || void 0, regionCode: input.regionCode || void 0, colo: input.colo || void 0, latitude: input.latitude || void 0, longitude: input.longitude || void 0 }; } const createKVStorage = (kv) => { return { get: async (key) => { return kv.get(key); }, set: async (key, value, ttl) => { return kv.put(key, value, ttl ? { expirationTtl: ttl } : void 0); }, delete: async (key) => { return kv.delete(key); } }; }; const withCloudflare = (cloudFlareOptions, options) => { const autoDetectIpEnabled = cloudFlareOptions.autoDetectIpAddress === void 0 || cloudFlareOptions.autoDetectIpAddress === true; const geolocationTrackingForSession = cloudFlareOptions.geolocationTracking === void 0 || cloudFlareOptions.geolocationTracking === true; if (autoDetectIpEnabled || geolocationTrackingForSession) { if (!cloudFlareOptions.cf) { throw new Error( "Cloudflare context is required for geolocation or IP detection features. Be sure to pass the `cf` option to the withCloudflare function." ); } } let updatedAdvanced = { ...options.advanced }; if (autoDetectIpEnabled) { updatedAdvanced.ipAddress = { ...updatedAdvanced.ipAddress ?? {}, ipAddressHeaders: ["cf-connecting-ip", "x-real-ip", ...updatedAdvanced.ipAddress?.ipAddressHeaders ?? []] }; } else if (updatedAdvanced.ipAddress?.ipAddressHeaders) ; let updatedSession = { ...options.session }; if (geolocationTrackingForSession) { updatedSession.storeSessionInDatabase = true; } else if (options.session?.storeSessionInDatabase === void 0) ; const dbConfigs = [cloudFlareOptions.postgres, cloudFlareOptions.mysql, cloudFlareOptions.d1].filter(Boolean); if (dbConfigs.length > 1) { throw new Error( "Only one database configuration can be provided. Please provide only one of postgres, mysql, or d1." ); } let database; if (cloudFlareOptions.postgres) { database = drizzle.drizzleAdapter(cloudFlareOptions.postgres.db, { provider: "pg", ...cloudFlareOptions.postgres.options }); } else if (cloudFlareOptions.mysql) { database = drizzle.drizzleAdapter(cloudFlareOptions.mysql.db, { provider: "mysql", ...cloudFlareOptions.mysql.options }); } else if (cloudFlareOptions.d1) { database = drizzle.drizzleAdapter(cloudFlareOptions.d1.db, { provider: "sqlite", ...cloudFlareOptions.d1.options }); } return { ...options, database, secondaryStorage: cloudFlareOptions.kv ? createKVStorage(cloudFlareOptions.kv) : void 0, plugins: [cloudflare(cloudFlareOptions), ...options.plugins ?? []], advanced: updatedAdvanced, session: updatedSession }; }; exports.schema = schema.schema; exports.R2_ERROR_CODES = r2.R2_ERROR_CODES; exports.createFileMetadataSchema = r2.createFileMetadataSchema; exports.createFileValidator = r2.createFileValidator; exports.createR2Endpoints = r2.createR2Endpoints; exports.createR2Storage = r2.createR2Storage; exports.createUploadFileSchema = r2.createUploadFileSchema; exports.error = r2.error; exports.fileIdSchema = r2.fileIdSchema; exports.listFilesSchema = r2.listFilesSchema; exports.success = r2.success; exports.cloudflareClient = client.cloudflareClient; exports.createR2Config = types.createR2Config; exports.cloudflare = cloudflare; exports.createKVStorage = createKVStorage; exports.withCloudflare = withCloudflare;