nucleus-core-ts 0.9.786 → 0.9.787

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -512,7 +512,7 @@ ${strMDSAlgs}`)}if(attestationStatementAlg!==void 0&&authenticatorGetInfo?.algor
512
512
  </table>
513
513
  </td></tr>
514
514
  </table>
515
- </body></html>`};sessionsRoutes.get(`${baseRoute}/approve-page`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return new Response("Service unavailable",{status:503});let token=new URL(ctx.request.url).searchParams.get("token");if(!token){let html2=buildApprovalPageHtml({},"approve","","invalid");return new Response(html2,{status:400,headers:{"Content-Type":"text/html; charset=utf-8"}})}let sessions=await db.select().from(sessionsTable2).where(eq54(col15("approvalToken"),token)).limit(1);if(sessions.length===0){let html2=buildApprovalPageHtml({},"approve",token,"invalid");return new Response(html2,{status:404,headers:{"Content-Type":"text/html; charset=utf-8"}})}let session=sessions[0];if(session.approvalStatus!=="pending"&&session.approval_status!=="pending"){let html2=buildApprovalPageHtml(session,"approve",token,"already_processed");return new Response(html2,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})}let requestedAt=session.approvalRequestedAt||session.approval_requested_at;if(requestedAt&&Date.now()-new Date(requestedAt).getTime()>APPROVAL_TOKEN_TTL_MS){let html2=buildApprovalPageHtml(session,"approve",token,"expired");return new Response(html2,{status:410,headers:{"Content-Type":"text/html; charset=utf-8"}})}let html=buildApprovalPageHtml(session,"approve",token,"pending");return new Response(html,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})},{detail:{tags:["Authentication"],summary:"Approval page",description:"Standalone HTML page for approving a device (no frontend auth required)"}}),sessionsRoutes.get(`${baseRoute}/reject-page`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return new Response("Service unavailable",{status:503});let token=new URL(ctx.request.url).searchParams.get("token");if(!token){let html2=buildApprovalPageHtml({},"reject","","invalid");return new Response(html2,{status:400,headers:{"Content-Type":"text/html; charset=utf-8"}})}let sessions=await db.select().from(sessionsTable2).where(eq54(col15("approvalToken"),token)).limit(1);if(sessions.length===0){let html2=buildApprovalPageHtml({},"reject",token,"invalid");return new Response(html2,{status:404,headers:{"Content-Type":"text/html; charset=utf-8"}})}let session=sessions[0];if(session.approvalStatus!=="pending"&&session.approval_status!=="pending"){let html2=buildApprovalPageHtml(session,"reject",token,"already_processed");return new Response(html2,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})}let requestedAt=session.approvalRequestedAt||session.approval_requested_at;if(requestedAt&&Date.now()-new Date(requestedAt).getTime()>APPROVAL_TOKEN_TTL_MS){let html2=buildApprovalPageHtml(session,"reject",token,"expired");return new Response(html2,{status:410,headers:{"Content-Type":"text/html; charset=utf-8"}})}let html=buildApprovalPageHtml(session,"reject",token,"pending");return new Response(html,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})},{detail:{tags:["Authentication"],summary:"Rejection page",description:"Standalone HTML page for rejecting a device (no frontend auth required)"}}),sessionsRoutes.get(`${baseRoute}/approval-status`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let sessionId=new URL(ctx.request.url).searchParams.get("sessionId");if(!sessionId)return{success:!1,message:"sessionId is required"};let sessions=await db.select().from(sessionsTable2).where(eq54(col15("id"),sessionId)).limit(1);if(sessions.length===0)return{success:!1,message:"Session not found"};let session=sessions[0];return{success:!0,data:{approvalStatus:session.approvalStatus||session.approval_status||"unknown",isActive:session.isActive||session.is_active}}},{detail:{tags:["Authentication"],summary:"Check approval status",description:"Check the approval status of a pending session"}}),sessionsRoutes.get(`${baseRoute}/devices`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let dtTable=getSessionsForRequest(ctx.request).trustedDevicesTable;if(!dtTable||!db)return{success:!0,data:{items:[]}};let{listUserDevices:listUserDevices2}=await Promise.resolve().then(() => (init_store2(),exports_store));return{success:!0,data:{items:await listUserDevices2(db,dtTable,userId)}}},{detail:{tags:["Authentication"],summary:"List remembered devices",description:"List the caller's remembered (device-trust) devices; never returns the token."}}),sessionsRoutes.delete(`${baseRoute}/devices/:id`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let id=ctx.params.id,dtTable=getSessionsForRequest(ctx.request).trustedDevicesTable;if(!dtTable||!db)return{success:!1,message:"Device trust not enabled"};let{revokeDeviceById:revokeDeviceById2}=await Promise.resolve().then(() => (init_store2(),exports_store));if(await revokeDeviceById2(db,dtTable,id,userId,"user_revoked")===0)return ctx.set.status=404,{success:!1,message:"Device not found"};return log4.info("Remembered device revoked",{userId,deviceId:id}),{success:!0,message:"Device revoked"}},{detail:{tags:["Authentication"],summary:"Revoke a remembered device",description:"Owner-scoped revoke of one remembered device (untrusts it)."}});let adminGuard=createGodminGuard2({logger:logger2,db,schemaTables:config.schemaTables??{},area:"session management",allowedRoles:sessionsConfig.adminRoles??[]});return sessionsRoutes.get(`${baseRoute}/admin/:userId`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let{userId}=ctx.params,sessions=await db.select().from(sessionsTable2).where(and20(eq54(col15("userId"),userId),eq54(col15("isActive"),!0),isNull3(col15("revokedAt")))).orderBy(desc6(col15("lastActivityAt")));return{success:!0,data:{sessions:sessions.map((session)=>formatSessionForResponse(session)),totalCount:sessions.length}}},{beforeHandle:adminGuard,detail:{tags:["Authentication"],summary:"List another user's active sessions (admin)",description:"Godmin or a role named in authentication.sessions.adminRoles. Used for offboarding and lost-device response."}}),sessionsRoutes.delete(`${baseRoute}/admin/:userId`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let{userId}=ctx.params,body=ctx.body??{},actorId=ctx.request.headers.get("x-user-id")||void 0,target2=body.sessionId?and20(eq54(col15("id"),body.sessionId),eq54(col15("userId"),userId),eq54(col15("isActive"),!0)):and20(eq54(col15("userId"),userId),eq54(col15("isActive"),!0)),affected=await db.update(sessionsTable2).set({isActive:!1,revokedAt:new Date,revokedReason:body.reason||"admin_revoked"}).where(target2).returning({id:col15("id")});log4.warn("Sessions revoked by an administrator",{actorId,subjectUserId:userId,sessionId:body.sessionId,revoked:affected.length,reason:body.reason||"admin_revoked"});let reqUrl=new URL(ctx.request.url);return logger2.audit({entityName:"user_sessions",entityId:body.sessionId,operation:"SESSION_REVOKE_ALL",userId:actorId,summary:body.sessionId?`Admin revoked one session of user ${userId}`:`Admin revoked ${affected.length} session(s) of user ${userId}`,ipAddress:ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||ctx.request.headers.get("x-real-ip")?.trim()||"unknown",userAgent:ctx.request.headers.get("user-agent")||"unknown",path:reqUrl.pathname,query:reqUrl.search}),{success:!0,message:`${affected.length} oturum sonland\u0131r\u0131ld\u0131`,data:{revoked:affected.length}}},{beforeHandle:adminGuard,body:AdminRevokeSessionsBodySchema,detail:{tags:["Authentication"],summary:"End another user's sessions (admin)",description:"Ends every active session of the given user, or just one when `sessionId` is supplied. Godmin or authentication.sessions.adminRoles."}}),sessionsRoutes}var init_sessions=__esm(()=>{init_scopes();init_godminGuard();init_types20();init_utils10();init_types20()});function resolveCookies(cookieConfig){return{accessTokenName:cookieConfig?.accessTokenName||"access_token",refreshTokenName:cookieConfig?.refreshTokenName||"refresh_token",sessionTokenName:cookieConfig?.sessionTokenName||"session_token",accessTokenMaxAge:cookieConfig?.accessTokenMaxAge||900,refreshTokenMaxAge:cookieConfig?.refreshTokenMaxAge||604800,sessionTokenMaxAge:cookieConfig?.sessionTokenMaxAge||2592000,secure:cookieConfig?.secure??!0,httpOnly:cookieConfig?.httpOnly??!0,sameSite:cookieConfig?.sameSite||"lax",path:cookieConfig?.path||"/",domain:cookieConfig?.domain}}function buildCookieHeaders(cookies,tokens){let securePart=cookies.secure?"; Secure":"",domainPart=cookies.domain?`; Domain=${cookies.domain}`:"",cookieOptions=`; Path=${cookies.path}; HttpOnly; SameSite=${cookies.sameSite}${securePart}${domainPart}`,headers=new Headers;return headers.set("Content-Type","application/json"),headers.set("x-session-id",tokens.sessionId),headers.append("Set-Cookie",`${cookies.accessTokenName}=${tokens.accessToken}${cookieOptions}; Max-Age=${cookies.accessTokenMaxAge}`),headers.append("Set-Cookie",`${cookies.refreshTokenName}=${tokens.refreshToken}${cookieOptions}; Max-Age=${cookies.refreshTokenMaxAge}`),headers.append("Set-Cookie",`${cookies.sessionTokenName}=${tokens.sessionId}${cookieOptions}; Max-Age=${cookies.sessionTokenMaxAge}`),headers}var WebAuthnRegisterOptionsSchema,WebAuthnRegisterVerifySchema,WebAuthnAuthOptionsSchema,WebAuthnAuthVerifySchema,WebAuthnRenameSchema;var init_types21=__esm(()=>{init_esm3();WebAuthnRegisterOptionsSchema=Type.Object({nickname:Type.Optional(Type.String({maxLength:128}))}),WebAuthnRegisterVerifySchema=Type.Object({response:Type.Any(),challenge:Type.String(),nickname:Type.Optional(Type.String({maxLength:128}))}),WebAuthnAuthOptionsSchema=Type.Object({email:Type.Optional(Type.String({format:"email"}))}),WebAuthnAuthVerifySchema=Type.Object({response:Type.Any(),challenge:Type.String(),rememberMe:Type.Optional(Type.Boolean())}),WebAuthnRenameSchema=Type.Object({nickname:Type.String({minLength:1,maxLength:128})})});import{eq as eq55}from"drizzle-orm";import{Elysia as Elysia43}from"elysia";function getSchema(ctx){return ctx.request.headers.get("x-tenant-schema")||void 0}function getUserId2(ctx){return ctx.request.headers.get("x-user-id")}function createWebAuthnRoute(config,routeOptions,webauthnService,helpers3,cookieConfig){let{db,logger:logger2}=config,basePath=routeOptions.basePath||"/auth/webauthn",cookies=resolveCookies(cookieConfig),app=new Elysia43;if(!routeOptions.enabled)return app;return app.post(`${basePath}/register/options`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(!db||!usersTable)return{success:!1,message:"Database not configured"};let user=(await db.select().from(usersTable).where(eq55(usersTable.id,userId)).limit(1))[0];if(!user)return ctx.set.status=404,{success:!1,message:"User not found"};return{success:!0,data:await webauthnService.createRegistrationOptions({userId,userName:user.email||userId,userDisplayName:user.email||userId,schemaName:getSchema(ctx)})}},{body:WebAuthnRegisterOptionsSchema}),app.post(`${basePath}/register/verify`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let body=ctx.body,credential=await webauthnService.verifyRegistration({response:body.response,expectedChallenge:body.challenge,userId,nickname:body.nickname,schemaName:getSchema(ctx)});if(!credential)return logger2.warn("[WEBAUTHN] Registration verification failed",{userId}),{success:!1,message:"Registration failed"};return{success:!0,data:{id:credential.id,credentialId:credential.credentialId,nickname:credential.nickname,deviceType:credential.deviceType,backedUp:credential.backedUp,authenticatorAttachment:credential.authenticatorAttachment,aaguid:credential.aaguid,transports:credential.transports,lastUsedAt:credential.lastUsedAt,revokedAt:credential.revokedAt,createdAt:credential.createdAt}}},{body:WebAuthnRegisterVerifySchema}),app.post(`${basePath}/authenticate/options`,async(ctx)=>{let body=ctx.body,resolvedUserId;if(body.email){let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(db&&usersTable){let user=(await db.select().from(usersTable).where(eq55(usersTable.email,body.email.toLowerCase())).limit(1))[0];if(user)resolvedUserId=user.id}}return{success:!0,data:await webauthnService.createAuthenticationOptions({userId:resolvedUserId,schemaName:getSchema(ctx)})}},{body:WebAuthnAuthOptionsSchema}),app.post(`${basePath}/authenticate/verify`,async(ctx)=>{let body=ctx.body,result=await webauthnService.verifyAuthentication({response:body.response,expectedChallenge:body.challenge,schemaName:getSchema(ctx)});if(!result.verified||!result.userId)return ctx.set.status=401,{success:!1,message:"Authentication failed"};let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(!db||!usersTable)return{success:!1,message:"Database not configured"};let user=(await db.select().from(usersTable).where(eq55(usersTable.id,result.userId)).limit(1))[0];if(!user)return ctx.set.status=401,{success:!1,message:"User not found"};let webauthnState=evaluateAccountState(user);if(!webauthnState.allowed)return ctx.set.status=401,{success:!1,message:webauthnState.message};let roles=[],claims=[],claimScopes={};try{let resolved=resolveAuthTablesForRequest(ctx.request,config),rc=await fetchUserRolesAndClaims(db,result.userId,resolved);roles=rc.roles,claims=rc.claims,claimScopes=rc.claimScopes}catch{}let accessToken=helpers3.signAccessToken(result.userId,roles.length>0?roles:void 0,claims.length>0?claims:void 0,getSchema(ctx),claimScopes),refreshToken=helpers3.signRefreshToken(result.userId),ipAddress=ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||ctx.request.headers.get("x-real-ip")?.trim()||"unknown",userAgent=ctx.request.headers.get("user-agent")||"",sessionParams={userId:result.userId,deviceInfo:ensureDeviceInfo({ipAddress,userAgent},deviceContextFromRequest(ctx.request).clientHints),loginMethod:"webauthn",rememberMe:body.rememberMe??!1},sessionId=await helpers3.createSession(sessionParams),deviceTokenCookie;if(helpers3.saveSessionToDb){let dtCfg=config.authentication?.deviceTrust;if(dtCfg?.enabled)sessionParams.deviceToken=readDeviceTokenFromCookieHeader(ctx.request.headers.get("cookie"),dtCfg);let saveResult=await helpers3.saveSessionToDb(sessionId,sessionParams,getSchema(ctx));if(saveResult?.denied)return ctx.set.status=403,{success:!1,message:"This device could not be recognized. Please contact support."};if(deviceTokenCookie=saveResult?.deviceTokenCookie,saveResult?.requiresApproval){if(deviceTokenCookie)ctx.set.headers["Set-Cookie"]=deviceTokenCookie;return{success:!1,requiresApproval:!0,sessionId:saveResult.sessionId??sessionId,message:"Login requires approval"}}}let headers=buildCookieHeaders(cookies,{accessToken,refreshToken,sessionId});if(deviceTokenCookie)headers.append("Set-Cookie",deviceTokenCookie);return logger2.info("[WEBAUTHN] Authentication successful",{userId:result.userId,credentialId:result.credentialId}),new Response(JSON.stringify({success:!0,data:{user:{id:user.id,email:user.email},accessToken,refreshToken,sessionId}}),{status:200,headers})},{body:WebAuthnAuthVerifySchema}),app.get(`${basePath}/credentials`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};return{success:!0,data:{credentials:(await webauthnService.listUserCredentials(userId,getSchema(ctx))).map((cred)=>({id:cred.id,credentialId:cred.credentialId,nickname:cred.nickname,deviceType:cred.deviceType,backedUp:cred.backedUp,authenticatorAttachment:cred.authenticatorAttachment,transports:cred.transports,aaguid:cred.aaguid,lastUsedAt:cred.lastUsedAt,revokedAt:cred.revokedAt,createdAt:cred.createdAt}))}}}),app.delete(`${basePath}/credentials/:credentialId`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{credentialId}=ctx.params;if(!await webauthnService.revokeCredential(credentialId,userId,getSchema(ctx)))return ctx.set.status=404,{success:!1,message:"Credential not found"};return{success:!0,message:"Credential revoked"}}),app.patch(`${basePath}/credentials/:credentialId`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{credentialId}=ctx.params,body=ctx.body;if(!await webauthnService.renameCredential(credentialId,userId,body.nickname,getSchema(ctx)))return ctx.set.status=404,{success:!1,message:"Credential not found"};return{success:!0,message:"Credential renamed"}},{body:WebAuthnRenameSchema}),app}var init_webauthn=__esm(()=>{init_DeviceTrust();init_fetchUserRolesAndClaims();init_deviceInfo();init_types21();init_types21()});var exports_auth={};__export(exports_auth,{createWebAuthnRoute:()=>createWebAuthnRoute,createSessionsRoute:()=>createSessionsRoute,createRegisterRoute:()=>createRegisterRoute,createRefreshRoute:()=>createRefreshRoute,createPasswordSetRoute:()=>createPasswordSetRoute,createPasswordResetRoute:()=>createPasswordResetRoute,createPasswordChangeRoute:()=>createPasswordChangeRoute,createMeRoute:()=>createMeRoute,createMagicLinkRoute:()=>createMagicLinkRoute,createLogoutRoute:()=>createLogoutRoute,createLoginRoute:()=>createLoginRoute,createInviteRoute:()=>createInviteRoute,createImpersonateRoute:()=>createImpersonateRoute,createHardDeleteUserRoute:()=>createHardDeleteUserRoute,createEmailVerificationRoutes:()=>createEmailVerificationRoutes,createCreateUserRoute:()=>createCreateUserRoute,createChangeUserIdRoute:()=>createChangeUserIdRoute,createAuthRoutes:()=>createAuthRoutes,createApiKeyRoutes:()=>createApiKeyRoutes});function createAuthRoutes(app,config){let{authConfig,features,helpers:helpers3}=config;if(config.oauthAccountsTable)authConfig.oauthAccountsTable=config.oauthAccountsTable;if(config.schemaTables)authConfig.schemaTables=config.schemaTables;if(config.tenantRegistry)authConfig.tenantRegistry=config.tenantRegistry;if(config.getTenantRegistry)authConfig.getTenantRegistry=config.getTenantRegistry;let buffer=authConfig.authentication?.cookieMaxAgeBufferSeconds??0,rawCookieDomain=authConfig.authentication?.cookieDomain,resolvedCookieDomainRaw=rawCookieDomain?process.env[rawCookieDomain]??rawCookieDomain:void 0,resolvedCookieDomain=resolvedCookieDomainRaw==="localhost"||resolvedCookieDomainRaw===".localhost"?void 0:resolvedCookieDomainRaw,isLocalDev=!resolvedCookieDomain,cookieConfig={accessTokenName:authConfig.authentication?.accessToken?.name||"access_token",refreshTokenName:authConfig.authentication?.refreshToken?.name||"refresh_token",sessionTokenName:authConfig.authentication?.sessionToken?.name||"session_token",accessTokenMaxAge:Math.max(0,parseTimeToSeconds2(authConfig.authentication?.accessToken?.expiresIn||"15m")-buffer),refreshTokenMaxAge:parseTimeToSeconds2(authConfig.authentication?.refreshToken?.expiresIn||"7d"),sessionTokenMaxAge:parseTimeToSeconds2(authConfig.authentication?.sessionToken?.expiresIn||"30d"),domain:resolvedCookieDomain,secure:!isLocalDev,httpOnly:!0,sameSite:"lax",path:"/"};if(authConfig.logger.info("[AUTH] Cookie config created",{accessTokenMaxAge:cookieConfig.accessTokenMaxAge,refreshTokenMaxAge:cookieConfig.refreshTokenMaxAge,sessionTokenMaxAge:cookieConfig.sessionTokenMaxAge,accessTokenExpiresIn:authConfig.authentication?.accessToken?.expiresIn,sessionTokenExpiresIn:authConfig.authentication?.sessionToken?.expiresIn}),features.login?.enabled){let loginRoutes=createLoginRoute(authConfig,features.login,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig,config.tokenResponseConfig);app.use(loginRoutes)}if(features.register?.enabled){let registerRoutes=createRegisterRoute(authConfig,features.register,helpers3.sendWelcomeEmail,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,cookieConfig,config.tokenResponseConfig,config.getEmailService,config.appName,helpers3.saveSessionToDb);if(app.use(registerRoutes),features.register.emailVerification?.enabled){let emailVerificationRoutes=createEmailVerificationRoutes({authConfig,registerConfig:features.register,getEmailService:config.getEmailService,appName:config.appName});app.use(emailVerificationRoutes)}}if(features.logout?.enabled){let logoutRoutes=createLogoutRoute(authConfig,features.logout,helpers3.destroySession,helpers3.revokeSessionInDb,cookieConfig);app.use(logoutRoutes)}if(features.refresh?.enabled){let refreshRoutes=createRefreshRoute(authConfig,features.refresh,helpers3.verifyRefreshToken,helpers3.signAccessToken,helpers3.signRefreshToken,config.tokenResponseConfig,buffer,cookieConfig);app.use(refreshRoutes)}if(features.passwordReset?.enabled&&helpers3.storeResetToken&&helpers3.getResetToken&&helpers3.deleteResetToken){let passwordResetRoutes=createPasswordResetRoute(authConfig,features.passwordReset,helpers3.storeResetToken,helpers3.getResetToken,helpers3.deleteResetToken,helpers3.sendResetEmail);app.use(passwordResetRoutes)}if(features.passwordChange?.enabled){let passwordChangeRoutes=createPasswordChangeRoute(authConfig,features.passwordChange);app.use(passwordChangeRoutes)}if(features.passwordSet?.enabled){let passwordSetRoutes=createPasswordSetRoute(authConfig,features.passwordSet,helpers3.getMagicToken,helpers3.deleteMagicToken);app.use(passwordSetRoutes)}if(features.sessions?.enabled&&config.sessionsTable){let sessionsRoutes=createSessionsRoute(authConfig,features.sessions,config.sessionsTable);app.use(sessionsRoutes)}if(features.magicLink?.enabled&&helpers3.storeMagicToken&&helpers3.getMagicToken&&helpers3.deleteMagicToken){let magicLinkRoutes=createMagicLinkRoute(authConfig,features.magicLink,config.getEmailService,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.storeMagicToken,helpers3.getMagicToken,helpers3.deleteMagicToken,config.appName,cookieConfig,helpers3.saveSessionToDb);app.use(magicLinkRoutes)}if(features.me?.enabled){let meRoutes=createMeRoute(authConfig,features.me,config.schemaTables||{},config.schemaRelations||{},config.databaseUrl);app.use(meRoutes)}if(features.invite?.enabled&&helpers3.storeMagicToken){let inviteRoutes=createInviteRoute(authConfig,features.invite,config.getEmailService,helpers3.storeMagicToken,config.appName,helpers3.getMagicToken);app.use(inviteRoutes)}if(features.captcha?.enabled&&config.captchaService){let captchaRoutes=createCaptchaRoutes({captchaService:config.captchaService,logger:authConfig.logger,basePath:features.captcha.route||"/auth/captcha"});app.use(captchaRoutes)}if(features.oauth?.enabled&&features.oauth.providers){let oauthService=new OAuthService(features.oauth,config.oauthStateStore),oauthRoutes=createOAuthRoutes(authConfig,oauthService,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig,config.tokenResponseConfig,config.getEmailService,helpers3.storeMagicToken,config.appName);app.use(oauthRoutes)}if(features.apiKeys?.enabled&&config.apiKeysTable)authConfig.apiKeysTable=config.apiKeysTable,createApiKeyRoutes(app,authConfig,features.apiKeys);if(features.webauthn?.enabled&&config.webauthnService){let webauthnRoutes=createWebAuthnRoute(authConfig,{basePath:features.webauthn.route||"/auth/webauthn",isPublic:features.webauthn.isPublic,enabled:!0},config.webauthnService,{signAccessToken:helpers3.signAccessToken,signRefreshToken:helpers3.signRefreshToken,createSession:helpers3.createSession,saveSessionToDb:helpers3.saveSessionToDb},cookieConfig);app.use(webauthnRoutes)}let checkRoutes=createCheckRoute(authConfig,{route:"/auth/check",isPublic:!1,enabled:!0});if(app.use(checkRoutes),config.admin?.impersonate?.enabled!==!1){let impersonateRoutes=createImpersonateRoute(authConfig,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.verifyRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig);app.use(impersonateRoutes)}if(config.admin?.changeUserId?.enabled!==!1){let changeUserIdRoutes=createChangeUserIdRoute(authConfig,config.schemaName||"public");app.use(changeUserIdRoutes)}if(config.admin?.createUser?.enabled!==!1){let createUserRoutes=createCreateUserRoute(authConfig,config.admin?.createUser);app.use(createUserRoutes)}if(config.admin?.hardDelete?.enabled!==!1){let hardDeleteRoutes=createHardDeleteUserRoute(authConfig);app.use(hardDeleteRoutes)}return app}var init_auth=__esm(()=>{init_OAuth();init_utils5();init_changeUserId();init_createUser();init_hardDeleteUser();init_impersonate();init_apiKeys();init_captcha();init_check();init_emailVerification();init_invite();init_login();init_logout();init_magicLink();init_me();init_oauth();init_passwordChange();init_passwordReset();init_passwordSet();init_refresh();init_register();init_sessions();init_webauthn();init_changeUserId();init_createUser();init_hardDeleteUser();init_impersonate();init_apiKeys();init_emailVerification();init_invite();init_login();init_logout();init_magicLink();init_me();init_passwordChange();init_passwordReset();init_passwordSet();init_refresh();init_register();init_sessions();init_webauthn()});var exports_backup={};__export(exports_backup,{createBackupRoutes:()=>createBackupRoutes});import{Elysia as Elysia44,t as t37}from"elysia";function createBackupRoutes(routeConfig){let{db,logger:logger2,schemaTables,schemaName,tenantRegistry,lock}=routeConfig,config={...DEFAULT_BACKUP_CONFIG,...routeConfig.config},basePath=config.basePath,backupLogsTable=schemaTables.backupLogs||schemaTables.backup_logs,backupService=new BackupService({db,logger:logger2,config,schemaTables,schemaName,backupLogsTable,lock});if(config.schedule.enabled)backupService.startScheduler();let routes=new Elysia44;return routes.onBeforeHandle(createGodminGuard({db,schemaTables,logger:logger2},"backup management")),routes.post(basePath,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let targetSchema=ctx.body?.schema,targetSchemaTables=schemaTables,targetSchemaName=schemaName;if(targetSchema&&tenantRegistry){let tenantCtx=tenantRegistry.getSchemaContext(targetSchema);if(!tenantCtx)return ctx.set.status=404,{success:!1,message:`Schema not found: ${targetSchema}`};targetSchemaTables=tenantCtx.schemaTables,targetSchemaName=targetSchema}let result=await backupService.createBackup("manual",userId,targetSchemaName,targetSchemaTables);if(result.status==="failed")return ctx.set.status=500,{success:!1,message:result.errorMessage||"Backup failed"};return{success:!0,message:"Backup created successfully",data:{id:result.id,backupName:result.backupName,schemaName:result.schemaName,tableCount:result.tableCount,rowCount:result.rowCount,sizeBytes:result.sizeBytes,status:result.status}}},{body:t37.Optional(t37.Object({schema:t37.Optional(t37.String())})),detail:{tags:["Backup"],summary:"Create backup",description:"Create a new database backup. Optionally specify a schema for multi-tenant backups."}}),routes.get(basePath,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required",data:null};let backups=await backupService.listBackups();return{success:!0,data:{items:backups,totalCount:backups.length}}},{detail:{tags:["Backup"],summary:"List backups",description:"List all backup records."}}),routes.get(`${basePath}/:id/download`,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required"};let{id}=ctx.params,filePath=await backupService.getBackupFilePath(id);if(!filePath)return ctx.set.status=404,{success:!1,message:"Backup not found"};try{let{readFile:readFile2}=await import("fs/promises"),content=await readFile2(filePath,"utf-8");return new Response(content,{headers:{"Content-Type":"application/json","Content-Disposition":`attachment; filename="${id}.json"`}})}catch{return ctx.set.status=404,{success:!1,message:"Backup file not found on disk"}}},{detail:{tags:["Backup"],summary:"Download backup",description:"Download a backup file by ID."}}),routes.post(`${basePath}/:id/restore`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};if(!config.allowRestore)return ctx.set.status=403,{success:!1,message:"Restore is disabled in configuration"};let{id}=ctx.params,targetSchema=ctx.body?.schema,targetSchemaTables,targetSchemaName;if(targetSchema&&tenantRegistry){let tenantCtx=tenantRegistry.getSchemaContext(targetSchema);if(!tenantCtx)return ctx.set.status=404,{success:!1,message:`Schema not found: ${targetSchema}`};targetSchemaTables=tenantCtx.schemaTables,targetSchemaName=targetSchema}let result=await backupService.restoreFromBackup(id,userId,targetSchemaName,targetSchemaTables);if(!result.success)return ctx.set.status=400,result;return await logger2.audit({entityName:"backup_logs",entityId:id,operation:"RESTORE",userId,summary:`Database restored from backup ${id}`,ipAddress:ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown",userAgent:ctx.request.headers.get("user-agent")||"unknown",path:new URL(ctx.request.url).pathname,query:""}),result},{detail:{tags:["Backup"],summary:"Restore from backup",description:"Restore database from a specific backup. Creates a pre-restore backup automatically."}}),routes.delete(`${basePath}/:id`,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required"};let{id}=ctx.params;if(!await backupService.deleteBackup(id))return ctx.set.status=404,{success:!1,message:"Backup not found"};return{success:!0,message:"Backup deleted"}},{detail:{tags:["Backup"],summary:"Delete backup",description:"Delete a specific backup and its file."}}),{routes,backupService}}var DEFAULT_BACKUP_CONFIG;var init_backup=__esm(()=>{init_Backup();init_adminGuard();DEFAULT_BACKUP_CONFIG={enabled:!1,basePath:"/admin/backup",storagePath:"./backups",format:"json",maxBackups:50,allowRestore:!0,excludeTables:["audit_logs","backup_logs"],schedule:{enabled:!1,cron:"0 2 * * *",retentionDays:30}}});var validateAmount=(v,field="amount",opts={})=>{if(typeof v!=="number"||!Number.isFinite(v))return{ok:!1,error:`${field} must be a finite number`};if(opts.allowZero?v<0:v<=0)return{ok:!1,error:`${field} must be ${opts.allowZero?">= 0":"> 0"}`};return{ok:!0,value:v}},isValidCurrency=(v)=>v===void 0||v===null||typeof v==="string"&&/^[A-Za-z]{3}$/.test(v);import{eq as eq56}from"drizzle-orm";var col15=(table,name2)=>table[name2],str4=(v)=>typeof v==="string"?v:null,num3=(v)=>typeof v==="number"?v:null,handleChargeRefunded=async(deps,obj)=>{let candidates=[str4(obj.id),str4(obj.payment_intent)].filter((v)=>v!==null);for(let candidate of candidates){let updated=await deps.db.update(deps.transactionsTable).set({status:"refunded",refundedAmount:num3(obj.amount_refunded)??void 0,refundedAt:new Date,statusMessage:"Refunded via Stripe webhook",updatedAt:new Date}).where(eq56(col15(deps.transactionsTable,"providerPaymentId"),candidate)).returning();if(Array.isArray(updated)&&updated.length>0)return}},handleDisputeCreated=async(deps,obj)=>{let payoutService=deps.getPayoutService?.();if(!payoutService)return;let metadata=obj.metadata??{};await payoutService.openDispute({provider:"stripe",providerDisputeId:str4(obj.id)??void 0,ownerType:str4(metadata.ownerType)==="seller"?"seller":void 0,ownerId:str4(metadata.ownerId)??void 0,amount:num3(obj.amount)??void 0,currency:str4(obj.currency)?.toUpperCase(),reason:str4(obj.reason)??void 0})},handleDisputeClosed=async(deps,obj)=>{let payoutService=deps.getPayoutService?.(),providerId=str4(obj.id);if(!payoutService||!providerId)return;await payoutService.reconcileDisputeByProvider(providerId,str4(obj.status)??"")},handlePayoutOutcome=async(deps,obj,outcome,reason)=>{let payoutService=deps.getPayoutService?.(),providerId=str4(obj.id);if(!payoutService||!providerId)return;await payoutService.reconcilePayoutByProvider(providerId,outcome,reason)},dispatch=async(deps,eventType,obj)=>{switch(eventType){case"charge.refunded":await handleChargeRefunded(deps,obj);break;case"charge.dispute.created":await handleDisputeCreated(deps,obj);break;case"charge.dispute.closed":case"charge.dispute.funds_reinstated":await handleDisputeClosed(deps,obj);break;case"payout.paid":await handlePayoutOutcome(deps,obj,"completed");break;case"payout.failed":case"payout.canceled":await handlePayoutOutcome(deps,obj,"failed",str4(obj.failure_message)??eventType);break;case"transfer.reversed":await handlePayoutOutcome(deps,obj,"failed","transfer reversed");break;default:break}},registerStripeWebhookRoute=(app,deps)=>{app.post("/webhook",async(ctx)=>{if(deps.provider.name!=="stripe")return ctx.set.status=501,{received:!1,error:"Webhooks are only supported for the Stripe provider"};if(!deps.webhookSecret)return deps.logger.error("[Payment] Webhook received but STRIPE_WEBHOOK_SECRET is not configured"),ctx.set.status=500,{received:!1,error:"Webhook secret not configured"};let rawBody=await ctx.request.text(),signature=ctx.request.headers.get("stripe-signature")??"",verified=await verifyStripeWebhook(rawBody,signature,deps.webhookSecret);if(!verified.success||!verified.event)return deps.logger.warn("[Payment] Webhook signature verification failed",{error:verified.error}),ctx.set.status=400,{received:!1,error:"Invalid signature"};let event=verified.event,eventId=str4(event.id)??"",eventType=str4(event.type)??"",obj=event.data?.object??{},idempotencyKey=`stripe:${eventId}`,inserted=await deps.db.insert(deps.webhookLogsTable).values({provider:"stripe",eventType,providerPaymentId:str4(obj.payment_intent)??str4(obj.id),rawPayload:event,processed:!1,idempotencyKey,ipAddress:ctx.request.headers.get("x-forwarded-for")??ctx.request.headers.get("x-real-ip")??null}).onConflictDoNothing().returning();if(!Array.isArray(inserted)||inserted.length===0)return{received:!0,duplicate:!0,eventType};try{return await dispatch(deps,eventType,obj),await deps.db.update(deps.webhookLogsTable).set({processed:!0,processingResult:{ok:!0},updatedAt:new Date}).where(eq56(col15(deps.webhookLogsTable,"idempotencyKey"),idempotencyKey)),deps.logger.info("[Payment] Webhook processed",{eventType,eventId}),{received:!0,eventType}}catch(err){let message=err instanceof Error?err.message:String(err);return deps.logger.error("[Payment] Webhook handler failed",{eventType,error:message}),await deps.db.update(deps.webhookLogsTable).set({errorMessage:message,updatedAt:new Date}).where(eq56(col15(deps.webhookLogsTable,"idempotencyKey"),idempotencyKey)).catch(()=>{}),ctx.set.status=500,{received:!1,error:"Handler error"}}})};var init_webhook=__esm(()=>{init_Payment()});var exports_payment={};__export(exports_payment,{createPaymentRoutes:()=>createPaymentRoutes});import{and as and21,eq as eq57,inArray as inArray15,sql as sql10}from"drizzle-orm";import{Elysia as Elysia45}from"elysia";var escapeHtml2=(value2)=>value2.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),generateRedirectHtml=(url,title)=>{let safeTitle=escapeHtml2(title),safeUrlAttr=escapeHtml2(url),safeUrlJs=JSON.stringify(url).replace(/</g,"\\u003c").replace(/>/g,"\\u003e");return`
515
+ </body></html>`};sessionsRoutes.get(`${baseRoute}/approve-page`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return new Response("Service unavailable",{status:503});let token=new URL(ctx.request.url).searchParams.get("token");if(!token){let html2=buildApprovalPageHtml({},"approve","","invalid");return new Response(html2,{status:400,headers:{"Content-Type":"text/html; charset=utf-8"}})}let sessions=await db.select().from(sessionsTable2).where(eq54(col15("approvalToken"),token)).limit(1);if(sessions.length===0){let html2=buildApprovalPageHtml({},"approve",token,"invalid");return new Response(html2,{status:404,headers:{"Content-Type":"text/html; charset=utf-8"}})}let session=sessions[0];if(session.approvalStatus!=="pending"&&session.approval_status!=="pending"){let html2=buildApprovalPageHtml(session,"approve",token,"already_processed");return new Response(html2,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})}let requestedAt=session.approvalRequestedAt||session.approval_requested_at;if(requestedAt&&Date.now()-new Date(requestedAt).getTime()>APPROVAL_TOKEN_TTL_MS){let html2=buildApprovalPageHtml(session,"approve",token,"expired");return new Response(html2,{status:410,headers:{"Content-Type":"text/html; charset=utf-8"}})}let html=buildApprovalPageHtml(session,"approve",token,"pending");return new Response(html,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})},{detail:{tags:["Authentication"],summary:"Approval page",description:"Standalone HTML page for approving a device (no frontend auth required)"}}),sessionsRoutes.get(`${baseRoute}/reject-page`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return new Response("Service unavailable",{status:503});let token=new URL(ctx.request.url).searchParams.get("token");if(!token){let html2=buildApprovalPageHtml({},"reject","","invalid");return new Response(html2,{status:400,headers:{"Content-Type":"text/html; charset=utf-8"}})}let sessions=await db.select().from(sessionsTable2).where(eq54(col15("approvalToken"),token)).limit(1);if(sessions.length===0){let html2=buildApprovalPageHtml({},"reject",token,"invalid");return new Response(html2,{status:404,headers:{"Content-Type":"text/html; charset=utf-8"}})}let session=sessions[0];if(session.approvalStatus!=="pending"&&session.approval_status!=="pending"){let html2=buildApprovalPageHtml(session,"reject",token,"already_processed");return new Response(html2,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})}let requestedAt=session.approvalRequestedAt||session.approval_requested_at;if(requestedAt&&Date.now()-new Date(requestedAt).getTime()>APPROVAL_TOKEN_TTL_MS){let html2=buildApprovalPageHtml(session,"reject",token,"expired");return new Response(html2,{status:410,headers:{"Content-Type":"text/html; charset=utf-8"}})}let html=buildApprovalPageHtml(session,"reject",token,"pending");return new Response(html,{status:200,headers:{"Content-Type":"text/html; charset=utf-8"}})},{detail:{tags:["Authentication"],summary:"Rejection page",description:"Standalone HTML page for rejecting a device (no frontend auth required)"}}),sessionsRoutes.get(`${baseRoute}/approval-status`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let sessionId=new URL(ctx.request.url).searchParams.get("sessionId");if(!sessionId)return{success:!1,message:"sessionId is required"};let sessions=await db.select().from(sessionsTable2).where(eq54(col15("id"),sessionId)).limit(1);if(sessions.length===0)return{success:!1,message:"Session not found"};let session=sessions[0];return{success:!0,data:{approvalStatus:session.approvalStatus||session.approval_status||"unknown",isActive:session.isActive||session.is_active}}},{detail:{tags:["Authentication"],summary:"Check approval status",description:"Check the approval status of a pending session"}}),sessionsRoutes.get(`${baseRoute}/devices`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let dtTable=getSessionsForRequest(ctx.request).trustedDevicesTable;if(!dtTable||!db)return{success:!0,data:{items:[]}};let{listUserDevices:listUserDevices2}=await Promise.resolve().then(() => (init_store2(),exports_store));return{success:!0,data:{items:await listUserDevices2(db,dtTable,userId)}}},{detail:{tags:["Authentication"],summary:"List remembered devices",description:"List the caller's remembered (device-trust) devices; never returns the token."}}),sessionsRoutes.delete(`${baseRoute}/devices/:id`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let id=ctx.params.id,dtTable=getSessionsForRequest(ctx.request).trustedDevicesTable;if(!dtTable||!db)return{success:!1,message:"Device trust not enabled"};let{revokeDeviceById:revokeDeviceById2}=await Promise.resolve().then(() => (init_store2(),exports_store));if(await revokeDeviceById2(db,dtTable,id,userId,"user_revoked")===0)return ctx.set.status=404,{success:!1,message:"Device not found"};return log4.info("Remembered device revoked",{userId,deviceId:id}),{success:!0,message:"Device revoked"}},{detail:{tags:["Authentication"],summary:"Revoke a remembered device",description:"Owner-scoped revoke of one remembered device (untrusts it)."}});let adminGuard=createGodminGuard2({logger:logger2,db,schemaTables:config.schemaTables??{},area:"session management",allowedRoles:sessionsConfig.adminRoles??[]});return sessionsRoutes.get(`${baseRoute}/admin/:userId`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let{userId}=ctx.params,sessions=await db.select().from(sessionsTable2).where(and20(eq54(col15("userId"),userId),eq54(col15("isActive"),!0),isNull3(col15("revokedAt")))).orderBy(desc6(col15("lastActivityAt")));return{success:!0,data:{sessions:sessions.map((session)=>formatSessionForResponse(session)),totalCount:sessions.length}}},{beforeHandle:adminGuard,detail:{tags:["Authentication"],summary:"List another user's active sessions (admin)",description:"Godmin or a role named in authentication.sessions.adminRoles. Used for offboarding and lost-device response."}}),sessionsRoutes.delete(`${baseRoute}/admin/:userId`,async(ctx)=>{let{sessionsTable:sessionsTable2,col:col15}=getSessionsForRequest(ctx.request);if(!db)return{success:!1,message:"Database not configured"};let{userId}=ctx.params,body=ctx.body??{},actorId=ctx.request.headers.get("x-user-id")||void 0,target2=body.sessionId?and20(eq54(col15("id"),body.sessionId),eq54(col15("userId"),userId),eq54(col15("isActive"),!0)):and20(eq54(col15("userId"),userId),eq54(col15("isActive"),!0)),affected=await db.update(sessionsTable2).set({isActive:!1,revokedAt:new Date,revokedReason:body.reason||"admin_revoked"}).where(target2).returning({id:col15("id")});log4.warn("Sessions revoked by an administrator",{actorId,subjectUserId:userId,sessionId:body.sessionId,revoked:affected.length,reason:body.reason||"admin_revoked"});let reqUrl=new URL(ctx.request.url);return logger2.audit({entityName:"user_sessions",entityId:body.sessionId,operation:"SESSION_REVOKE_ALL",userId:actorId,summary:body.sessionId?`Admin revoked one session of user ${userId}`:`Admin revoked ${affected.length} session(s) of user ${userId}`,ipAddress:ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||ctx.request.headers.get("x-real-ip")?.trim()||"unknown",userAgent:ctx.request.headers.get("user-agent")||"unknown",path:reqUrl.pathname,query:reqUrl.search}),{success:!0,message:`${affected.length} oturum sonland\u0131r\u0131ld\u0131`,data:{revoked:affected.length}}},{beforeHandle:adminGuard,body:AdminRevokeSessionsBodySchema,detail:{tags:["Authentication"],summary:"End another user's sessions (admin)",description:"Ends every active session of the given user, or just one when `sessionId` is supplied. Godmin or authentication.sessions.adminRoles."}}),sessionsRoutes}var init_sessions=__esm(()=>{init_scopes();init_godminGuard();init_types20();init_utils10();init_types20()});function resolveCookies(cookieConfig){return{accessTokenName:cookieConfig?.accessTokenName||"access_token",refreshTokenName:cookieConfig?.refreshTokenName||"refresh_token",sessionTokenName:cookieConfig?.sessionTokenName||"session_token",accessTokenMaxAge:cookieConfig?.accessTokenMaxAge||900,refreshTokenMaxAge:cookieConfig?.refreshTokenMaxAge||604800,sessionTokenMaxAge:cookieConfig?.sessionTokenMaxAge||2592000,secure:cookieConfig?.secure??!0,httpOnly:cookieConfig?.httpOnly??!0,sameSite:cookieConfig?.sameSite||"lax",path:cookieConfig?.path||"/",domain:cookieConfig?.domain}}function buildCookieHeaders(cookies,tokens){let securePart=cookies.secure?"; Secure":"",domainPart=cookies.domain?`; Domain=${cookies.domain}`:"",cookieOptions=`; Path=${cookies.path}; HttpOnly; SameSite=${cookies.sameSite}${securePart}${domainPart}`,headers=new Headers;return headers.set("Content-Type","application/json"),headers.set("x-session-id",tokens.sessionId),headers.append("Set-Cookie",`${cookies.accessTokenName}=${tokens.accessToken}${cookieOptions}; Max-Age=${cookies.accessTokenMaxAge}`),headers.append("Set-Cookie",`${cookies.refreshTokenName}=${tokens.refreshToken}${cookieOptions}; Max-Age=${cookies.refreshTokenMaxAge}`),headers.append("Set-Cookie",`${cookies.sessionTokenName}=${tokens.sessionId}${cookieOptions}; Max-Age=${cookies.sessionTokenMaxAge}`),headers}var WebAuthnRegisterOptionsSchema,WebAuthnRegisterVerifySchema,WebAuthnAuthOptionsSchema,WebAuthnAuthVerifySchema,WebAuthnRenameSchema;var init_types21=__esm(()=>{init_esm3();WebAuthnRegisterOptionsSchema=Type.Object({nickname:Type.Optional(Type.String({maxLength:128}))}),WebAuthnRegisterVerifySchema=Type.Object({response:Type.Any(),challenge:Type.String(),nickname:Type.Optional(Type.String({maxLength:128}))}),WebAuthnAuthOptionsSchema=Type.Object({email:Type.Optional(Type.String({format:"email"}))}),WebAuthnAuthVerifySchema=Type.Object({response:Type.Any(),challenge:Type.String(),rememberMe:Type.Optional(Type.Boolean())}),WebAuthnRenameSchema=Type.Object({nickname:Type.String({minLength:1,maxLength:128})})});import{eq as eq55}from"drizzle-orm";import{Elysia as Elysia43}from"elysia";function getSchema(ctx){return ctx.request.headers.get("x-tenant-schema")||void 0}function getUserId2(ctx){return ctx.request.headers.get("x-user-id")}function createWebAuthnRoute(config,routeOptions,webauthnService,helpers3,cookieConfig){let{db,logger:logger2}=config,basePath=routeOptions.basePath||"/auth/webauthn",cookies=resolveCookies(cookieConfig),app=new Elysia43;if(!routeOptions.enabled)return app;return app.post(`${basePath}/register/options`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(!db||!usersTable)return{success:!1,message:"Database not configured"};let user=(await db.select().from(usersTable).where(eq55(usersTable.id,userId)).limit(1))[0];if(!user)return ctx.set.status=404,{success:!1,message:"User not found"};return{success:!0,data:await webauthnService.createRegistrationOptions({userId,userName:user.email||userId,userDisplayName:user.email||userId,schemaName:getSchema(ctx)})}},{body:WebAuthnRegisterOptionsSchema}),app.post(`${basePath}/register/verify`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let body=ctx.body,credential=await webauthnService.verifyRegistration({response:body.response,expectedChallenge:body.challenge,userId,nickname:body.nickname,schemaName:getSchema(ctx)});if(!credential)return logger2.warn("[WEBAUTHN] Registration verification failed",{userId}),{success:!1,message:"Registration failed"};return{success:!0,data:{id:credential.id,credentialId:credential.credentialId,nickname:credential.nickname,deviceType:credential.deviceType,backedUp:credential.backedUp,authenticatorAttachment:credential.authenticatorAttachment,aaguid:credential.aaguid,transports:credential.transports,lastUsedAt:credential.lastUsedAt,revokedAt:credential.revokedAt,createdAt:credential.createdAt}}},{body:WebAuthnRegisterVerifySchema}),app.post(`${basePath}/authenticate/options`,async(ctx)=>{let body=ctx.body,resolvedUserId;if(body.email){let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(db&&usersTable){let user=(await db.select().from(usersTable).where(eq55(usersTable.email,body.email.toLowerCase())).limit(1))[0];if(user)resolvedUserId=user.id}}return{success:!0,data:await webauthnService.createAuthenticationOptions({userId:resolvedUserId,schemaName:getSchema(ctx)})}},{body:WebAuthnAuthOptionsSchema}),app.post(`${basePath}/authenticate/verify`,async(ctx)=>{let body=ctx.body,result=await webauthnService.verifyAuthentication({response:body.response,expectedChallenge:body.challenge,schemaName:getSchema(ctx)});if(!result.verified||!result.userId)return ctx.set.status=401,{success:!1,message:"Authentication failed"};let{usersTable}=resolveAuthTablesForRequest(ctx.request,config);if(!db||!usersTable)return{success:!1,message:"Database not configured"};let user=(await db.select().from(usersTable).where(eq55(usersTable.id,result.userId)).limit(1))[0];if(!user)return ctx.set.status=401,{success:!1,message:"User not found"};let webauthnState=evaluateAccountState(user);if(!webauthnState.allowed)return ctx.set.status=401,{success:!1,message:webauthnState.message};let roles=[],claims=[],claimScopes={};try{let resolved=resolveAuthTablesForRequest(ctx.request,config),rc=await fetchUserRolesAndClaims(db,result.userId,resolved);roles=rc.roles,claims=rc.claims,claimScopes=rc.claimScopes}catch{}let accessToken=helpers3.signAccessToken(result.userId,roles.length>0?roles:void 0,claims.length>0?claims:void 0,getSchema(ctx),claimScopes),refreshToken=helpers3.signRefreshToken(result.userId),ipAddress=ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||ctx.request.headers.get("x-real-ip")?.trim()||"unknown",userAgent=ctx.request.headers.get("user-agent")||"",sessionParams={userId:result.userId,deviceInfo:ensureDeviceInfo({ipAddress,userAgent},deviceContextFromRequest(ctx.request).clientHints),loginMethod:"webauthn",rememberMe:body.rememberMe??!1},sessionId=await helpers3.createSession(sessionParams),deviceTokenCookie;if(helpers3.saveSessionToDb){let dtCfg=config.authentication?.deviceTrust;if(dtCfg?.enabled)sessionParams.deviceToken=readDeviceTokenFromCookieHeader(ctx.request.headers.get("cookie"),dtCfg);let saveResult=await helpers3.saveSessionToDb(sessionId,sessionParams,getSchema(ctx));if(saveResult?.denied)return ctx.set.status=403,{success:!1,message:"This device could not be recognized. Please contact support."};if(deviceTokenCookie=saveResult?.deviceTokenCookie,saveResult?.requiresApproval){if(deviceTokenCookie)ctx.set.headers["Set-Cookie"]=deviceTokenCookie;return{success:!1,requiresApproval:!0,sessionId:saveResult.sessionId??sessionId,message:"Login requires approval"}}}let headers=buildCookieHeaders(cookies,{accessToken,refreshToken,sessionId});if(deviceTokenCookie)headers.append("Set-Cookie",deviceTokenCookie);return logger2.info("[WEBAUTHN] Authentication successful",{userId:result.userId,credentialId:result.credentialId}),new Response(JSON.stringify({success:!0,data:{user:{id:user.id,email:user.email},accessToken,refreshToken,sessionId}}),{status:200,headers})},{body:WebAuthnAuthVerifySchema}),app.get(`${basePath}/credentials`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};return{success:!0,data:{credentials:(await webauthnService.listUserCredentials(userId,getSchema(ctx))).map((cred)=>({id:cred.id,credentialId:cred.credentialId,nickname:cred.nickname,deviceType:cred.deviceType,backedUp:cred.backedUp,authenticatorAttachment:cred.authenticatorAttachment,transports:cred.transports,aaguid:cred.aaguid,lastUsedAt:cred.lastUsedAt,revokedAt:cred.revokedAt,createdAt:cred.createdAt}))}}}),app.delete(`${basePath}/credentials/:credentialId`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{credentialId}=ctx.params;if(!await webauthnService.revokeCredential(credentialId,userId,getSchema(ctx)))return ctx.set.status=404,{success:!1,message:"Credential not found"};return{success:!0,message:"Credential revoked"}}),app.patch(`${basePath}/credentials/:credentialId`,async(ctx)=>{let userId=getUserId2(ctx);if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let{credentialId}=ctx.params,body=ctx.body;if(!await webauthnService.renameCredential(credentialId,userId,body.nickname,getSchema(ctx)))return ctx.set.status=404,{success:!1,message:"Credential not found"};return{success:!0,message:"Credential renamed"}},{body:WebAuthnRenameSchema}),app}var init_webauthn=__esm(()=>{init_DeviceTrust();init_fetchUserRolesAndClaims();init_deviceInfo();init_types21();init_types21()});var exports_auth={};__export(exports_auth,{createWebAuthnRoute:()=>createWebAuthnRoute,createSessionsRoute:()=>createSessionsRoute,createRegisterRoute:()=>createRegisterRoute,createRefreshRoute:()=>createRefreshRoute,createPasswordSetRoute:()=>createPasswordSetRoute,createPasswordResetRoute:()=>createPasswordResetRoute,createPasswordChangeRoute:()=>createPasswordChangeRoute,createMeRoute:()=>createMeRoute,createMagicLinkRoute:()=>createMagicLinkRoute,createLogoutRoute:()=>createLogoutRoute,createLoginRoute:()=>createLoginRoute,createInviteRoute:()=>createInviteRoute,createImpersonateRoute:()=>createImpersonateRoute,createHardDeleteUserRoute:()=>createHardDeleteUserRoute,createEmailVerificationRoutes:()=>createEmailVerificationRoutes,createCreateUserRoute:()=>createCreateUserRoute,createChangeUserIdRoute:()=>createChangeUserIdRoute,createAuthRoutes:()=>createAuthRoutes,createApiKeyRoutes:()=>createApiKeyRoutes});function createAuthRoutes(app,config){let{authConfig,features,helpers:helpers3}=config;if(config.oauthAccountsTable)authConfig.oauthAccountsTable=config.oauthAccountsTable;if(config.schemaTables)authConfig.schemaTables=config.schemaTables;if(config.tenantRegistry)authConfig.tenantRegistry=config.tenantRegistry;if(config.getTenantRegistry)authConfig.getTenantRegistry=config.getTenantRegistry;let buffer=authConfig.authentication?.cookieMaxAgeBufferSeconds??0,rawCookieDomain=authConfig.authentication?.cookieDomain,resolvedCookieDomainRaw=rawCookieDomain?process.env[rawCookieDomain]??rawCookieDomain:void 0,resolvedCookieDomain=resolvedCookieDomainRaw==="localhost"||resolvedCookieDomainRaw===".localhost"?void 0:resolvedCookieDomainRaw,isLocalDev=!resolvedCookieDomain,cookieConfig={accessTokenName:authConfig.authentication?.accessToken?.name||"access_token",refreshTokenName:authConfig.authentication?.refreshToken?.name||"refresh_token",sessionTokenName:authConfig.authentication?.sessionToken?.name||"session_token",accessTokenMaxAge:Math.max(0,parseTimeToSeconds2(authConfig.authentication?.accessToken?.expiresIn||"15m")-buffer),refreshTokenMaxAge:parseTimeToSeconds2(authConfig.authentication?.refreshToken?.expiresIn||"7d"),sessionTokenMaxAge:parseTimeToSeconds2(authConfig.authentication?.sessionToken?.expiresIn||"30d"),domain:resolvedCookieDomain,secure:!isLocalDev,httpOnly:!0,sameSite:"lax",path:"/"};if(authConfig.logger.info("[AUTH] Cookie config created",{accessTokenMaxAge:cookieConfig.accessTokenMaxAge,refreshTokenMaxAge:cookieConfig.refreshTokenMaxAge,sessionTokenMaxAge:cookieConfig.sessionTokenMaxAge,accessTokenExpiresIn:authConfig.authentication?.accessToken?.expiresIn,sessionTokenExpiresIn:authConfig.authentication?.sessionToken?.expiresIn}),features.login?.enabled){let loginRoutes=createLoginRoute(authConfig,features.login,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig,config.tokenResponseConfig,config.captchaService??void 0);app.use(loginRoutes)}if(features.register?.enabled){let registerRoutes=createRegisterRoute(authConfig,features.register,helpers3.sendWelcomeEmail,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,cookieConfig,config.tokenResponseConfig,config.getEmailService,config.appName,helpers3.saveSessionToDb);if(app.use(registerRoutes),features.register.emailVerification?.enabled){let emailVerificationRoutes=createEmailVerificationRoutes({authConfig,registerConfig:features.register,getEmailService:config.getEmailService,appName:config.appName});app.use(emailVerificationRoutes)}}if(features.logout?.enabled){let logoutRoutes=createLogoutRoute(authConfig,features.logout,helpers3.destroySession,helpers3.revokeSessionInDb,cookieConfig);app.use(logoutRoutes)}if(features.refresh?.enabled){let refreshRoutes=createRefreshRoute(authConfig,features.refresh,helpers3.verifyRefreshToken,helpers3.signAccessToken,helpers3.signRefreshToken,config.tokenResponseConfig,buffer,cookieConfig);app.use(refreshRoutes)}if(features.passwordReset?.enabled&&helpers3.storeResetToken&&helpers3.getResetToken&&helpers3.deleteResetToken){let passwordResetRoutes=createPasswordResetRoute(authConfig,features.passwordReset,helpers3.storeResetToken,helpers3.getResetToken,helpers3.deleteResetToken,helpers3.sendResetEmail);app.use(passwordResetRoutes)}if(features.passwordChange?.enabled){let passwordChangeRoutes=createPasswordChangeRoute(authConfig,features.passwordChange);app.use(passwordChangeRoutes)}if(features.passwordSet?.enabled){let passwordSetRoutes=createPasswordSetRoute(authConfig,features.passwordSet,helpers3.getMagicToken,helpers3.deleteMagicToken);app.use(passwordSetRoutes)}if(features.sessions?.enabled&&config.sessionsTable){let sessionsRoutes=createSessionsRoute(authConfig,features.sessions,config.sessionsTable);app.use(sessionsRoutes)}if(features.magicLink?.enabled&&helpers3.storeMagicToken&&helpers3.getMagicToken&&helpers3.deleteMagicToken){let magicLinkRoutes=createMagicLinkRoute(authConfig,features.magicLink,config.getEmailService,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.storeMagicToken,helpers3.getMagicToken,helpers3.deleteMagicToken,config.appName,cookieConfig,helpers3.saveSessionToDb);app.use(magicLinkRoutes)}if(features.me?.enabled){let meRoutes=createMeRoute(authConfig,features.me,config.schemaTables||{},config.schemaRelations||{},config.databaseUrl);app.use(meRoutes)}if(features.invite?.enabled&&helpers3.storeMagicToken){let inviteRoutes=createInviteRoute(authConfig,features.invite,config.getEmailService,helpers3.storeMagicToken,config.appName,helpers3.getMagicToken);app.use(inviteRoutes)}if(features.captcha?.enabled&&config.captchaService){let captchaRoutes=createCaptchaRoutes({captchaService:config.captchaService,logger:authConfig.logger,basePath:features.captcha.route||"/auth/captcha"});app.use(captchaRoutes)}if(features.oauth?.enabled&&features.oauth.providers){let oauthService=new OAuthService(features.oauth,config.oauthStateStore),oauthRoutes=createOAuthRoutes(authConfig,oauthService,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig,config.tokenResponseConfig,config.getEmailService,helpers3.storeMagicToken,config.appName);app.use(oauthRoutes)}if(features.apiKeys?.enabled&&config.apiKeysTable)authConfig.apiKeysTable=config.apiKeysTable,createApiKeyRoutes(app,authConfig,features.apiKeys);if(features.webauthn?.enabled&&config.webauthnService){let webauthnRoutes=createWebAuthnRoute(authConfig,{basePath:features.webauthn.route||"/auth/webauthn",isPublic:features.webauthn.isPublic,enabled:!0},config.webauthnService,{signAccessToken:helpers3.signAccessToken,signRefreshToken:helpers3.signRefreshToken,createSession:helpers3.createSession,saveSessionToDb:helpers3.saveSessionToDb},cookieConfig);app.use(webauthnRoutes)}let checkRoutes=createCheckRoute(authConfig,{route:"/auth/check",isPublic:!1,enabled:!0});if(app.use(checkRoutes),config.admin?.impersonate?.enabled!==!1){let impersonateRoutes=createImpersonateRoute(authConfig,helpers3.signAccessToken,helpers3.signRefreshToken,helpers3.verifyRefreshToken,helpers3.createSession,helpers3.saveSessionToDb,cookieConfig);app.use(impersonateRoutes)}if(config.admin?.changeUserId?.enabled!==!1){let changeUserIdRoutes=createChangeUserIdRoute(authConfig,config.schemaName||"public");app.use(changeUserIdRoutes)}if(config.admin?.createUser?.enabled!==!1){let createUserRoutes=createCreateUserRoute(authConfig,config.admin?.createUser);app.use(createUserRoutes)}if(config.admin?.hardDelete?.enabled!==!1){let hardDeleteRoutes=createHardDeleteUserRoute(authConfig);app.use(hardDeleteRoutes)}return app}var init_auth=__esm(()=>{init_OAuth();init_utils5();init_changeUserId();init_createUser();init_hardDeleteUser();init_impersonate();init_apiKeys();init_captcha();init_check();init_emailVerification();init_invite();init_login();init_logout();init_magicLink();init_me();init_oauth();init_passwordChange();init_passwordReset();init_passwordSet();init_refresh();init_register();init_sessions();init_webauthn();init_changeUserId();init_createUser();init_hardDeleteUser();init_impersonate();init_apiKeys();init_emailVerification();init_invite();init_login();init_logout();init_magicLink();init_me();init_passwordChange();init_passwordReset();init_passwordSet();init_refresh();init_register();init_sessions();init_webauthn()});var exports_backup={};__export(exports_backup,{createBackupRoutes:()=>createBackupRoutes});import{Elysia as Elysia44,t as t37}from"elysia";function createBackupRoutes(routeConfig){let{db,logger:logger2,schemaTables,schemaName,tenantRegistry,lock}=routeConfig,config={...DEFAULT_BACKUP_CONFIG,...routeConfig.config},basePath=config.basePath,backupLogsTable=schemaTables.backupLogs||schemaTables.backup_logs,backupService=new BackupService({db,logger:logger2,config,schemaTables,schemaName,backupLogsTable,lock});if(config.schedule.enabled)backupService.startScheduler();let routes=new Elysia44;return routes.onBeforeHandle(createGodminGuard({db,schemaTables,logger:logger2},"backup management")),routes.post(basePath,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};let targetSchema=ctx.body?.schema,targetSchemaTables=schemaTables,targetSchemaName=schemaName;if(targetSchema&&tenantRegistry){let tenantCtx=tenantRegistry.getSchemaContext(targetSchema);if(!tenantCtx)return ctx.set.status=404,{success:!1,message:`Schema not found: ${targetSchema}`};targetSchemaTables=tenantCtx.schemaTables,targetSchemaName=targetSchema}let result=await backupService.createBackup("manual",userId,targetSchemaName,targetSchemaTables);if(result.status==="failed")return ctx.set.status=500,{success:!1,message:result.errorMessage||"Backup failed"};return{success:!0,message:"Backup created successfully",data:{id:result.id,backupName:result.backupName,schemaName:result.schemaName,tableCount:result.tableCount,rowCount:result.rowCount,sizeBytes:result.sizeBytes,status:result.status}}},{body:t37.Optional(t37.Object({schema:t37.Optional(t37.String())})),detail:{tags:["Backup"],summary:"Create backup",description:"Create a new database backup. Optionally specify a schema for multi-tenant backups."}}),routes.get(basePath,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required",data:null};let backups=await backupService.listBackups();return{success:!0,data:{items:backups,totalCount:backups.length}}},{detail:{tags:["Backup"],summary:"List backups",description:"List all backup records."}}),routes.get(`${basePath}/:id/download`,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required"};let{id}=ctx.params,filePath=await backupService.getBackupFilePath(id);if(!filePath)return ctx.set.status=404,{success:!1,message:"Backup not found"};try{let{readFile:readFile2}=await import("fs/promises"),content=await readFile2(filePath,"utf-8");return new Response(content,{headers:{"Content-Type":"application/json","Content-Disposition":`attachment; filename="${id}.json"`}})}catch{return ctx.set.status=404,{success:!1,message:"Backup file not found on disk"}}},{detail:{tags:["Backup"],summary:"Download backup",description:"Download a backup file by ID."}}),routes.post(`${basePath}/:id/restore`,async(ctx)=>{let userId=ctx.request.headers.get("x-user-id");if(!userId)return ctx.set.status=401,{success:!1,message:"Authentication required"};if(!config.allowRestore)return ctx.set.status=403,{success:!1,message:"Restore is disabled in configuration"};let{id}=ctx.params,targetSchema=ctx.body?.schema,targetSchemaTables,targetSchemaName;if(targetSchema&&tenantRegistry){let tenantCtx=tenantRegistry.getSchemaContext(targetSchema);if(!tenantCtx)return ctx.set.status=404,{success:!1,message:`Schema not found: ${targetSchema}`};targetSchemaTables=tenantCtx.schemaTables,targetSchemaName=targetSchema}let result=await backupService.restoreFromBackup(id,userId,targetSchemaName,targetSchemaTables);if(!result.success)return ctx.set.status=400,result;return await logger2.audit({entityName:"backup_logs",entityId:id,operation:"RESTORE",userId,summary:`Database restored from backup ${id}`,ipAddress:ctx.request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||"unknown",userAgent:ctx.request.headers.get("user-agent")||"unknown",path:new URL(ctx.request.url).pathname,query:""}),result},{detail:{tags:["Backup"],summary:"Restore from backup",description:"Restore database from a specific backup. Creates a pre-restore backup automatically."}}),routes.delete(`${basePath}/:id`,async(ctx)=>{if(!ctx.request.headers.get("x-user-id"))return ctx.set.status=401,{success:!1,message:"Authentication required"};let{id}=ctx.params;if(!await backupService.deleteBackup(id))return ctx.set.status=404,{success:!1,message:"Backup not found"};return{success:!0,message:"Backup deleted"}},{detail:{tags:["Backup"],summary:"Delete backup",description:"Delete a specific backup and its file."}}),{routes,backupService}}var DEFAULT_BACKUP_CONFIG;var init_backup=__esm(()=>{init_Backup();init_adminGuard();DEFAULT_BACKUP_CONFIG={enabled:!1,basePath:"/admin/backup",storagePath:"./backups",format:"json",maxBackups:50,allowRestore:!0,excludeTables:["audit_logs","backup_logs"],schedule:{enabled:!1,cron:"0 2 * * *",retentionDays:30}}});var validateAmount=(v,field="amount",opts={})=>{if(typeof v!=="number"||!Number.isFinite(v))return{ok:!1,error:`${field} must be a finite number`};if(opts.allowZero?v<0:v<=0)return{ok:!1,error:`${field} must be ${opts.allowZero?">= 0":"> 0"}`};return{ok:!0,value:v}},isValidCurrency=(v)=>v===void 0||v===null||typeof v==="string"&&/^[A-Za-z]{3}$/.test(v);import{eq as eq56}from"drizzle-orm";var col15=(table,name2)=>table[name2],str4=(v)=>typeof v==="string"?v:null,num3=(v)=>typeof v==="number"?v:null,handleChargeRefunded=async(deps,obj)=>{let candidates=[str4(obj.id),str4(obj.payment_intent)].filter((v)=>v!==null);for(let candidate of candidates){let updated=await deps.db.update(deps.transactionsTable).set({status:"refunded",refundedAmount:num3(obj.amount_refunded)??void 0,refundedAt:new Date,statusMessage:"Refunded via Stripe webhook",updatedAt:new Date}).where(eq56(col15(deps.transactionsTable,"providerPaymentId"),candidate)).returning();if(Array.isArray(updated)&&updated.length>0)return}},handleDisputeCreated=async(deps,obj)=>{let payoutService=deps.getPayoutService?.();if(!payoutService)return;let metadata=obj.metadata??{};await payoutService.openDispute({provider:"stripe",providerDisputeId:str4(obj.id)??void 0,ownerType:str4(metadata.ownerType)==="seller"?"seller":void 0,ownerId:str4(metadata.ownerId)??void 0,amount:num3(obj.amount)??void 0,currency:str4(obj.currency)?.toUpperCase(),reason:str4(obj.reason)??void 0})},handleDisputeClosed=async(deps,obj)=>{let payoutService=deps.getPayoutService?.(),providerId=str4(obj.id);if(!payoutService||!providerId)return;await payoutService.reconcileDisputeByProvider(providerId,str4(obj.status)??"")},handlePayoutOutcome=async(deps,obj,outcome,reason)=>{let payoutService=deps.getPayoutService?.(),providerId=str4(obj.id);if(!payoutService||!providerId)return;await payoutService.reconcilePayoutByProvider(providerId,outcome,reason)},dispatch=async(deps,eventType,obj)=>{switch(eventType){case"charge.refunded":await handleChargeRefunded(deps,obj);break;case"charge.dispute.created":await handleDisputeCreated(deps,obj);break;case"charge.dispute.closed":case"charge.dispute.funds_reinstated":await handleDisputeClosed(deps,obj);break;case"payout.paid":await handlePayoutOutcome(deps,obj,"completed");break;case"payout.failed":case"payout.canceled":await handlePayoutOutcome(deps,obj,"failed",str4(obj.failure_message)??eventType);break;case"transfer.reversed":await handlePayoutOutcome(deps,obj,"failed","transfer reversed");break;default:break}},registerStripeWebhookRoute=(app,deps)=>{app.post("/webhook",async(ctx)=>{if(deps.provider.name!=="stripe")return ctx.set.status=501,{received:!1,error:"Webhooks are only supported for the Stripe provider"};if(!deps.webhookSecret)return deps.logger.error("[Payment] Webhook received but STRIPE_WEBHOOK_SECRET is not configured"),ctx.set.status=500,{received:!1,error:"Webhook secret not configured"};let rawBody=await ctx.request.text(),signature=ctx.request.headers.get("stripe-signature")??"",verified=await verifyStripeWebhook(rawBody,signature,deps.webhookSecret);if(!verified.success||!verified.event)return deps.logger.warn("[Payment] Webhook signature verification failed",{error:verified.error}),ctx.set.status=400,{received:!1,error:"Invalid signature"};let event=verified.event,eventId=str4(event.id)??"",eventType=str4(event.type)??"",obj=event.data?.object??{},idempotencyKey=`stripe:${eventId}`,inserted=await deps.db.insert(deps.webhookLogsTable).values({provider:"stripe",eventType,providerPaymentId:str4(obj.payment_intent)??str4(obj.id),rawPayload:event,processed:!1,idempotencyKey,ipAddress:ctx.request.headers.get("x-forwarded-for")??ctx.request.headers.get("x-real-ip")??null}).onConflictDoNothing().returning();if(!Array.isArray(inserted)||inserted.length===0)return{received:!0,duplicate:!0,eventType};try{return await dispatch(deps,eventType,obj),await deps.db.update(deps.webhookLogsTable).set({processed:!0,processingResult:{ok:!0},updatedAt:new Date}).where(eq56(col15(deps.webhookLogsTable,"idempotencyKey"),idempotencyKey)),deps.logger.info("[Payment] Webhook processed",{eventType,eventId}),{received:!0,eventType}}catch(err){let message=err instanceof Error?err.message:String(err);return deps.logger.error("[Payment] Webhook handler failed",{eventType,error:message}),await deps.db.update(deps.webhookLogsTable).set({errorMessage:message,updatedAt:new Date}).where(eq56(col15(deps.webhookLogsTable,"idempotencyKey"),idempotencyKey)).catch(()=>{}),ctx.set.status=500,{received:!1,error:"Handler error"}}})};var init_webhook=__esm(()=>{init_Payment()});var exports_payment={};__export(exports_payment,{createPaymentRoutes:()=>createPaymentRoutes});import{and as and21,eq as eq57,inArray as inArray15,sql as sql10}from"drizzle-orm";import{Elysia as Elysia45}from"elysia";var escapeHtml2=(value2)=>value2.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),generateRedirectHtml=(url,title)=>{let safeTitle=escapeHtml2(title),safeUrlAttr=escapeHtml2(url),safeUrlJs=JSON.stringify(url).replace(/</g,"\\u003c").replace(/>/g,"\\u003e");return`
516
516
  <!DOCTYPE html>
517
517
  <html>
518
518
  <head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.786",
3
+ "version": "0.9.787",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",