nucleus-core-ts 0.9.7 → 0.9.8

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.
Files changed (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ var __create=Object.create;var{getPrototypeOf:__getProtoOf,defineProperty:__defP
16
16
  ${textElements}
17
17
  </g>
18
18
  ${overlayLines}
19
- </svg>`}function generatePuzzleChallenge(difficulty){let pieceCount=DIFFICULTY_CONFIG[difficulty].puzzlePieces,pieces=[],correctOrder=[];for(let i=0;i<pieceCount;i++)pieces.push({id:i,x:i%3*100,y:Math.floor(i/3)*100}),correctOrder.push(i);let shuffledPieces=[...pieces];for(let i=shuffledPieces.length-1;i>0;i--){let j=getSecureRandomInt(0,i),itemI=shuffledPieces[i],itemJ=shuffledPieces[j];if(itemI&&itemJ)shuffledPieces[i]=itemJ,shuffledPieces[j]=itemI}return{question:"Arrange the pieces in correct order",answer:correctOrder.join(","),puzzleData:{pieces:shuffledPieces,correctOrder}}}class CaptchaService{redis;logger;config;constructor(deps){this.redis=deps.redis,this.logger=deps.logger,this.config={enabled:deps.config.enabled??!0,type:deps.config.type??"math",difficulty:deps.config.difficulty??"medium",expiresIn:deps.config.expiresIn??"5m",maxAttempts:deps.config.maxAttempts??3,caseSensitive:deps.config.caseSensitive??!1,rateLimit:{maxGeneratePerMinute:deps.config.rateLimit?.maxGeneratePerMinute??10,maxGeneratePerHour:deps.config.rateLimit?.maxGeneratePerHour??100}}}async generate(type,difficulty,ipAddress){if(ipAddress){if(await this.checkRateLimit(ipAddress))return this.logger.warn("[CAPTCHA] Rate limit exceeded",{ipAddress}),{success:!1,challengeId:"",type:type??this.config.type,question:"",expiresAt:0,rateLimited:!0,message:"Too many requests. Please try again later."};await this.incrementRateLimit(ipAddress)}let captchaType=type??this.config.type,captchaDifficulty=difficulty??this.config.difficulty,challengeId=generateSecureId(),expiresInSeconds=parseTimeToSeconds(this.config.expiresIn),expiresAt=Date.now()+expiresInSeconds*1000,question,answer,imageData,puzzleData;switch(captchaType){case"math":{let mathResult=generateMathChallenge(captchaDifficulty);question=mathResult.question,answer=mathResult.answer;break}case"text":{let textResult=generateTextChallenge(captchaDifficulty);question=textResult.question,answer=textResult.answer;break}case"image":{let imageResult=generateImageChallenge(captchaDifficulty);question=imageResult.question,answer=imageResult.answer,imageData=imageResult.imageData;break}case"puzzle":{let puzzleResult=generatePuzzleChallenge(captchaDifficulty);question=puzzleResult.question,answer=puzzleResult.answer,puzzleData=puzzleResult.puzzleData;break}default:{let defaultResult=generateMathChallenge(captchaDifficulty);question=defaultResult.question,answer=defaultResult.answer}}let normalizedAnswer=this.config.caseSensitive?answer:answer.toUpperCase(),answerHash=hashAnswer(normalizedAnswer),challenge={id:challengeId,type:captchaType,question,answer:answerHash,imageData,puzzleData,expiresAt,attempts:0,maxAttempts:this.config.maxAttempts,caseSensitive:this.config.caseSensitive};await this.redis.set(`captcha:${challengeId}`,JSON.stringify(challenge),{ex:expiresInSeconds}),this.logger.info("[CAPTCHA] Challenge generated",{challengeId,type:captchaType,difficulty:captchaDifficulty,expiresAt});let result={success:!0,challengeId,type:captchaType,question,expiresAt};if(imageData)result.imageData=imageData;if(puzzleData)result.puzzleData={pieces:puzzleData.pieces};return result}async validate(challengeId,userAnswer){let key=`captcha:${challengeId}`,stored=await this.redis.get(key);if(!stored)return this.logger.warn("[CAPTCHA] Challenge not found or expired",{challengeId}),{success:!1,valid:!1,message:"Challenge expired or not found"};let challenge=JSON.parse(stored);if(Date.now()>challenge.expiresAt)return await this.redis.del(key),this.logger.warn("[CAPTCHA] Challenge expired",{challengeId}),{success:!1,valid:!1,message:"Challenge expired"};if(challenge.attempts+=1,challenge.attempts>challenge.maxAttempts)return await this.redis.del(key),this.logger.warn("[CAPTCHA] Max attempts exceeded",{challengeId,attempts:challenge.attempts}),{success:!1,valid:!1,message:"Maximum attempts exceeded"};let normalizedUserAnswer=challenge.caseSensitive?userAnswer.trim():userAnswer.trim().toUpperCase(),userAnswerHash=hashAnswer(normalizedUserAnswer);if(timingSafeEqual(challenge.answer,userAnswerHash))return await this.redis.del(key),this.logger.info("[CAPTCHA] Challenge validated successfully",{challengeId}),{success:!0,valid:!0,message:"Captcha validated successfully"};let remainingAttempts=challenge.maxAttempts-challenge.attempts;if(remainingAttempts<=0)await this.redis.del(key);else{let ttl=Math.floor((challenge.expiresAt-Date.now())/1000);await this.redis.set(key,JSON.stringify(challenge),{ex:ttl})}return this.logger.warn("[CAPTCHA] Invalid answer",{challengeId,attemptsRemaining:remainingAttempts}),{success:!0,valid:!1,message:"Incorrect answer",attemptsRemaining:remainingAttempts}}async invalidate(challengeId){await this.redis.del(`captcha:${challengeId}`),this.logger.info("[CAPTCHA] Challenge invalidated",{challengeId})}isEnabled(){return this.config.enabled}async checkRateLimit(ipAddress){let minuteKey=`captcha_rate:${ipAddress}:minute`,hourKey=`captcha_rate:${ipAddress}:hour`,[minuteCount,hourCount]=await Promise.all([this.redis.get(minuteKey),this.redis.get(hourKey)]),minuteRequests=minuteCount?Number.parseInt(minuteCount,10):0,hourRequests=hourCount?Number.parseInt(hourCount,10):0,maxPerMinute=this.config.rateLimit.maxGeneratePerMinute??10,maxPerHour=this.config.rateLimit.maxGeneratePerHour??100;return minuteRequests>=maxPerMinute||hourRequests>=maxPerHour}async incrementRateLimit(ipAddress){let minuteKey=`captcha_rate:${ipAddress}:minute`,hourKey=`captcha_rate:${ipAddress}:hour`,[minuteCount,hourCount]=await Promise.all([this.redis.get(minuteKey),this.redis.get(hourKey)]),newMinuteCount=(minuteCount?Number.parseInt(minuteCount,10):0)+1,newHourCount=(hourCount?Number.parseInt(hourCount,10):0)+1;await Promise.all([this.redis.set(minuteKey,newMinuteCount.toString(),{ex:60}),this.redis.set(hourKey,newHourCount.toString(),{ex:3600})])}}var DIFFICULTY_CONFIG;var init_Captcha=__esm(()=>{DIFFICULTY_CONFIG={easy:{mathRange:{min:1,max:10},textLength:4,puzzlePieces:4},medium:{mathRange:{min:10,max:50},textLength:6,puzzlePieces:6},hard:{mathRange:{min:50,max:100},textLength:8,puzzlePieces:9}}});class AzureEmailService{client=null;senderAddress;fromName;logger;initialized=!1;constructor(config,logger2){if(this.senderAddress=config.senderAddress,this.fromName=config.fromName||"Nucleus",this.logger=logger2,config.enabled)this.initialize(config.connectionString)}isAvailable(){return this.initialized&&this.client!==null}initialize(connectionString){try{if(!connectionString){this.logger.warn("[AzureEmailService] No connection string provided");return}if(!this.senderAddress){this.logger.warn("[AzureEmailService] No sender address configured");return}let{EmailClient}=(()=>{throw new Error("Cannot require module "+"@azure/communication-email");})();this.client=new EmailClient(connectionString),this.initialized=!0,this.logger.info("[AzureEmailService] Initialized",{senderAddress:this.senderAddress})}catch(error){this.logger.error("[AzureEmailService] Initialization failed",{error})}}async sendEmail(payload){if(!this.client)return this.logger.error("[AzureEmailService] Not initialized"),{success:!1,error:"Azure Email service not initialized"};try{let toAddresses=Array.isArray(payload.to)?payload.to:[payload.to],result=await(await this.client.beginSend({senderAddress:payload.from||this.senderAddress,content:{subject:payload.subject,plainText:payload.text,html:payload.html},recipients:{to:toAddresses.map((addr)=>({address:addr}))},attachments:payload.attachments?.map((att)=>({name:att.filename,contentType:att.contentType,contentInBase64:att.content.toString("base64")}))})).pollUntilDone();if(result.status==="Succeeded")return this.logger.info("[AzureEmailService] Email sent",{to:payload.to,subject:payload.subject,operationId:result.id}),{success:!0};let errorMsg=result.error?.message||`Send failed with status: ${result.status}`;return this.logger.error("[AzureEmailService] Send failed",{status:result.status,error:errorMsg,to:payload.to}),{success:!1,error:errorMsg}}catch(error){let message=error instanceof Error?error.message:"Unknown error";return this.logger.error("[AzureEmailService] Send failed",{error:message,to:payload.to}),{success:!1,error:message}}}emailWrapper(content,appName){return`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><meta name="color-scheme" content="light dark"><meta name="supported-color-schemes" content="light dark"><title>${appName}</title><style type="text/css">:root{color-scheme:light dark;supported-color-schemes:light dark;}@media(prefers-color-scheme:dark){.em-bg{background-color:#18181b!important;}.em-card{background-color:#27272a!important;border-color:#3f3f46!important;}.em-h{color:#fafafa!important;}.em-t{color:#d4d4d8!important;}.em-m{color:#a1a1aa!important;}.em-hr{background-color:#3f3f46!important;}.em-btn{background-color:#fafafa!important;}.em-btn-text{color:#18181b!important;}.em-link{color:#60a5fa!important;}.em-lb{background-color:#3f3f46!important;border-color:#52525b!important;}.em-lb-t{color:#a1a1aa!important;}.em-sn{background-color:#3f3f46!important;border-color:#52525b!important;color:#a1a1aa!important;}}</style></head><body style="margin:0;padding:0;background-color:#f4f4f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;-webkit-font-smoothing:antialiased;"><table role="presentation" class="em-bg" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f4f4f5;"><tr><td align="center" style="padding:40px 16px;"><table role="presentation" width="480" cellpadding="0" cellspacing="0" style="max-width:480px;width:100%;"><tr><td align="center" style="padding-bottom:24px;"><span class="em-m" style="font-size:14px;font-weight:600;color:#71717a;letter-spacing:-0.2px;">${appName}</span></td></tr><tr><td class="em-card" style="background-color:#ffffff;border:1px solid #e4e4e7;border-radius:12px;overflow:hidden;">${content}</td></tr><tr><td align="center" style="padding-top:24px;"><p class="em-m" style="margin:0;font-size:12px;color:#a1a1aa;line-height:1.6;">This email was sent by <strong>${appName}</strong>.<br>Please do not reply to this email.</p></td></tr></table></td></tr></table></body></html>`}emailButton(href,label){return`<table role="presentation" cellpadding="0" cellspacing="0" width="100%"><tr><td align="center" style="padding:28px 32px;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-btn" align="center" style="background-color:#18181b;border-radius:8px;"><a class="em-btn-text" href="${href}" target="_blank" style="display:inline-block;padding:12px 32px;font-size:14px;font-weight:600;color:#fafafa;text-decoration:none;letter-spacing:-0.1px;">${label}</a></td></tr></table></td></tr></table>`}emailDivider(){return'<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:0 32px;"><div class="em-hr" style="height:1px;background-color:#e4e4e7;"></div></td></tr></table>'}emailLinkFallback(link,notice){let html=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:4px 32px 32px 32px;"><p class="em-m" style="margin:0 0 8px 0;font-size:11px;font-weight:600;color:#a1a1aa;text-transform:uppercase;letter-spacing:0.5px;">Or copy this link</p><table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td class="em-lb" style="background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:8px;padding:12px 14px;"><a class="em-link" href="${link}" style="font-size:12px;color:#2563eb;word-break:break-all;line-height:1.5;text-decoration:none;">${link}</a></td></tr></table>`;if(notice)html+=`<p class="em-m" style="margin:16px 0 0 0;font-size:12px;color:#a1a1aa;">${notice}</p>`;return html+="</td></tr></table>",html}async sendWelcomeEmail(email,name,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Welcome to ${appName}</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Your account has been created successfully</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 32px 32px;"><p class="em-t" style="margin:0 0 16px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Welcome aboard! You now have full access to <strong>${appName}</strong>. We're excited to have you.</p></td></tr></table>`;return this.sendEmail({to:email,subject:`Welcome to ${appName}`,html:this.emailWrapper(content,appName)})}async sendVerificationEmail(email,name,verificationLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Verify your email</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Confirm your address to get started</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0 0 4px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Please verify your email address by clicking the button below.</p></td></tr><tr><td>${this.emailButton(verificationLink,"Verify Email Address")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(verificationLink)}`;return this.sendEmail({to:email,subject:`Verify your email \u2014 ${appName}`,html:this.emailWrapper(content,appName)})}async sendPasswordResetEmail(email,name,resetLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Reset your password</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">We received a request to reset your password</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0 0 4px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Click the button below to choose a new password. If you didn't make this request, you can safely ignore this email.</p></td></tr><tr><td>${this.emailButton(resetLink,"Reset Password")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(resetLink,"This link expires in 1 hour and can only be used once.")}`;return this.sendEmail({to:email,subject:`Reset your password \u2014 ${appName}`,html:this.emailWrapper(content,appName)})}async sendMagicLinkEmail(email,magicLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Sign in to ${appName}</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">No password needed \u2014 just click to sign in</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Click the button below to securely sign in to your <strong class="em-h" style="color:#18181b;">${appName}</strong> account.</p></td></tr><tr><td>${this.emailButton(magicLink,"Sign In")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(magicLink,"This link expires in 15 minutes and can only be used once.")}`;return this.sendEmail({to:email,subject:`Sign in to ${appName}`,html:this.emailWrapper(content,appName)})}async sendAlertEmail(to,subject,html){return this.sendEmail({to,subject,html})}async sendInvitationEmail(email,inviteLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">You're Invited</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Join <strong class="em-h" style="color:#18181b;">${appName}</strong> and start collaborating</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:0 32px 8px 32px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">1</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Click the button below</td></tr></table></td></tr><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">2</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Choose a secure password</td></tr></table></td></tr><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">3</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Start collaborating</td></tr></table></td></tr></table></td></tr></table>${this.emailButton(inviteLink,"Accept Invitation")}${this.emailDivider()}${this.emailLinkFallback(inviteLink,"This invitation expires in 7 days.")}`;return this.sendEmail({to:email,subject:`You're invited to join ${appName}`,html:this.emailWrapper(content,appName)})}}var init_Email=()=>{};import fs2 from"fs";import{google}from"googleapis";class GmailService{gmail=null;fromEmail;fromName;logger;initialized=!1;constructor(config,logger2){if(this.fromEmail=config.fromEmail,this.fromName=config.fromName||"Vorion",this.logger=logger2,config.enabled)this.initialize(config.jsonFilePath)}encodeHeaderValue(value){let hasNonAscii=!1;for(let i=0;i<value.length;i++)if(value.charCodeAt(i)>127){hasNonAscii=!0;break}if(!hasNonAscii)return value;return`=?UTF-8?B?${Buffer.from(value,"utf-8").toString("base64")}?=`}isAvailable(){return this.initialized&&this.gmail!==null}initialize(jsonFilePath){try{if(!jsonFilePath){this.logger.warn("[GmailService] No JSON file path provided");return}if(!fs2.existsSync(jsonFilePath)){this.logger.error("[GmailService] JSON file not found",{path:jsonFilePath});return}let keyContent=fs2.readFileSync(jsonFilePath,"utf8"),credentials=JSON.parse(keyContent);if(!this.fromEmail){this.logger.warn("[GmailService] From email not configured");return}if(!credentials?.client_email||!credentials?.private_key){this.logger.error("[GmailService] Invalid credentials format");return}let privateKey=credentials.private_key;if(!privateKey.includes(`
19
+ </svg>`}function generatePuzzleChallenge(difficulty){let pieceCount=DIFFICULTY_CONFIG[difficulty].puzzlePieces,pieces=[],correctOrder=[];for(let i=0;i<pieceCount;i++)pieces.push({id:i,x:i%3*100,y:Math.floor(i/3)*100}),correctOrder.push(i);let shuffledPieces=[...pieces];for(let i=shuffledPieces.length-1;i>0;i--){let j=getSecureRandomInt(0,i),itemI=shuffledPieces[i],itemJ=shuffledPieces[j];if(itemI&&itemJ)shuffledPieces[i]=itemJ,shuffledPieces[j]=itemI}return{question:"Arrange the pieces in correct order",answer:correctOrder.join(","),puzzleData:{pieces:shuffledPieces,correctOrder}}}class CaptchaService{redis;logger;config;constructor(deps){this.redis=deps.redis,this.logger=deps.logger,this.config={enabled:deps.config.enabled??!0,type:deps.config.type??"math",difficulty:deps.config.difficulty??"medium",expiresIn:deps.config.expiresIn??"5m",maxAttempts:deps.config.maxAttempts??3,caseSensitive:deps.config.caseSensitive??!1,rateLimit:{maxGeneratePerMinute:deps.config.rateLimit?.maxGeneratePerMinute??10,maxGeneratePerHour:deps.config.rateLimit?.maxGeneratePerHour??100}}}async generate(type,difficulty,ipAddress){if(ipAddress){if(await this.checkRateLimit(ipAddress))return this.logger.warn("[CAPTCHA] Rate limit exceeded",{ipAddress}),{success:!1,challengeId:"",type:type??this.config.type,question:"",expiresAt:0,rateLimited:!0,message:"Too many requests. Please try again later."};await this.incrementRateLimit(ipAddress)}let captchaType=type??this.config.type,captchaDifficulty=difficulty??this.config.difficulty,challengeId=generateSecureId(),expiresInSeconds=parseTimeToSeconds(this.config.expiresIn),expiresAt=Date.now()+expiresInSeconds*1000,question,answer,imageData,puzzleData;switch(captchaType){case"math":{let mathResult=generateMathChallenge(captchaDifficulty);question=mathResult.question,answer=mathResult.answer;break}case"text":{let textResult=generateTextChallenge(captchaDifficulty);question=textResult.question,answer=textResult.answer;break}case"image":{let imageResult=generateImageChallenge(captchaDifficulty);question=imageResult.question,answer=imageResult.answer,imageData=imageResult.imageData;break}case"puzzle":{let puzzleResult=generatePuzzleChallenge(captchaDifficulty);question=puzzleResult.question,answer=puzzleResult.answer,puzzleData=puzzleResult.puzzleData;break}default:{let defaultResult=generateMathChallenge(captchaDifficulty);question=defaultResult.question,answer=defaultResult.answer}}let normalizedAnswer=this.config.caseSensitive?answer:answer.toUpperCase(),answerHash=hashAnswer(normalizedAnswer),challenge={id:challengeId,type:captchaType,question,answer:answerHash,imageData,puzzleData,expiresAt,attempts:0,maxAttempts:this.config.maxAttempts,caseSensitive:this.config.caseSensitive};await this.redis.set(`captcha:${challengeId}`,JSON.stringify(challenge),{ex:expiresInSeconds}),this.logger.info("[CAPTCHA] Challenge generated",{challengeId,type:captchaType,difficulty:captchaDifficulty,expiresAt});let result={success:!0,challengeId,type:captchaType,question,expiresAt};if(imageData)result.imageData=imageData;if(puzzleData)result.puzzleData={pieces:puzzleData.pieces};return result}async validate(challengeId,userAnswer){let key=`captcha:${challengeId}`,stored=await this.redis.get(key);if(!stored)return this.logger.warn("[CAPTCHA] Challenge not found or expired",{challengeId}),{success:!1,valid:!1,message:"Challenge expired or not found"};let challenge=JSON.parse(stored);if(Date.now()>challenge.expiresAt)return await this.redis.del(key),this.logger.warn("[CAPTCHA] Challenge expired",{challengeId}),{success:!1,valid:!1,message:"Challenge expired"};if(challenge.attempts+=1,challenge.attempts>challenge.maxAttempts)return await this.redis.del(key),this.logger.warn("[CAPTCHA] Max attempts exceeded",{challengeId,attempts:challenge.attempts}),{success:!1,valid:!1,message:"Maximum attempts exceeded"};let normalizedUserAnswer=challenge.caseSensitive?userAnswer.trim():userAnswer.trim().toUpperCase(),userAnswerHash=hashAnswer(normalizedUserAnswer);if(timingSafeEqual(challenge.answer,userAnswerHash))return await this.redis.del(key),this.logger.info("[CAPTCHA] Challenge validated successfully",{challengeId}),{success:!0,valid:!0,message:"Captcha validated successfully"};let remainingAttempts=challenge.maxAttempts-challenge.attempts;if(remainingAttempts<=0)await this.redis.del(key);else{let ttl=Math.floor((challenge.expiresAt-Date.now())/1000);await this.redis.set(key,JSON.stringify(challenge),{ex:ttl})}return this.logger.warn("[CAPTCHA] Invalid answer",{challengeId,attemptsRemaining:remainingAttempts}),{success:!0,valid:!1,message:"Incorrect answer",attemptsRemaining:remainingAttempts}}async invalidate(challengeId){await this.redis.del(`captcha:${challengeId}`),this.logger.info("[CAPTCHA] Challenge invalidated",{challengeId})}isEnabled(){return this.config.enabled}async checkRateLimit(ipAddress){let minuteKey=`captcha_rate:${ipAddress}:minute`,hourKey=`captcha_rate:${ipAddress}:hour`,[minuteCount,hourCount]=await Promise.all([this.redis.get(minuteKey),this.redis.get(hourKey)]),minuteRequests=minuteCount?Number.parseInt(minuteCount,10):0,hourRequests=hourCount?Number.parseInt(hourCount,10):0,maxPerMinute=this.config.rateLimit.maxGeneratePerMinute??10,maxPerHour=this.config.rateLimit.maxGeneratePerHour??100;return minuteRequests>=maxPerMinute||hourRequests>=maxPerHour}async incrementRateLimit(ipAddress){let minuteKey=`captcha_rate:${ipAddress}:minute`,hourKey=`captcha_rate:${ipAddress}:hour`,[minuteCount,hourCount]=await Promise.all([this.redis.get(minuteKey),this.redis.get(hourKey)]),newMinuteCount=(minuteCount?Number.parseInt(minuteCount,10):0)+1,newHourCount=(hourCount?Number.parseInt(hourCount,10):0)+1;await Promise.all([this.redis.set(minuteKey,newMinuteCount.toString(),{ex:60}),this.redis.set(hourKey,newHourCount.toString(),{ex:3600})])}}var DIFFICULTY_CONFIG;var init_Captcha=__esm(()=>{DIFFICULTY_CONFIG={easy:{mathRange:{min:1,max:10},textLength:4,puzzlePieces:4},medium:{mathRange:{min:10,max:50},textLength:6,puzzlePieces:6},hard:{mathRange:{min:50,max:100},textLength:8,puzzlePieces:9}}});class AzureEmailService{client=null;senderAddress;fromName;logger;initialized=!1;constructor(config,logger2){if(this.senderAddress=config.senderAddress,this.fromName=config.fromName||"Nucleus",this.logger=logger2,config.enabled)this.initialize(config.connectionString)}isAvailable(){return this.initialized&&this.client!==null}initialize(connectionString){try{if(!connectionString){this.logger.warn("[AzureEmailService] No connection string provided");return}if(!this.senderAddress){this.logger.warn("[AzureEmailService] No sender address configured");return}let{EmailClient}=(()=>{throw new Error("Cannot require module "+"@azure/communication-email");})();this.client=new EmailClient(connectionString),this.initialized=!0,this.logger.info("[AzureEmailService] Initialized",{senderAddress:this.senderAddress})}catch(error){let message=error instanceof Error?error.message:String(error);this.logger.error("[AzureEmailService] Initialization failed",{error:message})}}async sendEmail(payload){if(!this.client)return this.logger.error("[AzureEmailService] Not initialized"),{success:!1,error:"Azure Email service not initialized"};try{let toAddresses=Array.isArray(payload.to)?payload.to:[payload.to],result=await(await this.client.beginSend({senderAddress:payload.from||this.senderAddress,content:{subject:payload.subject,plainText:payload.text,html:payload.html},recipients:{to:toAddresses.map((addr)=>({address:addr}))},attachments:payload.attachments?.map((att)=>({name:att.filename,contentType:att.contentType,contentInBase64:att.content.toString("base64")}))})).pollUntilDone();if(result.status==="Succeeded")return this.logger.info("[AzureEmailService] Email sent",{to:payload.to,subject:payload.subject,operationId:result.id}),{success:!0};let errorMsg=result.error?.message||`Send failed with status: ${result.status}`;return this.logger.error("[AzureEmailService] Send failed",{status:result.status,error:errorMsg,to:payload.to}),{success:!1,error:errorMsg}}catch(error){let message=error instanceof Error?error.message:"Unknown error";return this.logger.error("[AzureEmailService] Send failed",{error:message,to:payload.to}),{success:!1,error:message}}}emailWrapper(content,appName){return`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><meta name="color-scheme" content="light dark"><meta name="supported-color-schemes" content="light dark"><title>${appName}</title><style type="text/css">:root{color-scheme:light dark;supported-color-schemes:light dark;}@media(prefers-color-scheme:dark){.em-bg{background-color:#18181b!important;}.em-card{background-color:#27272a!important;border-color:#3f3f46!important;}.em-h{color:#fafafa!important;}.em-t{color:#d4d4d8!important;}.em-m{color:#a1a1aa!important;}.em-hr{background-color:#3f3f46!important;}.em-btn{background-color:#fafafa!important;}.em-btn-text{color:#18181b!important;}.em-link{color:#60a5fa!important;}.em-lb{background-color:#3f3f46!important;border-color:#52525b!important;}.em-lb-t{color:#a1a1aa!important;}.em-sn{background-color:#3f3f46!important;border-color:#52525b!important;color:#a1a1aa!important;}}</style></head><body style="margin:0;padding:0;background-color:#f4f4f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;-webkit-font-smoothing:antialiased;"><table role="presentation" class="em-bg" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f4f4f5;"><tr><td align="center" style="padding:40px 16px;"><table role="presentation" width="480" cellpadding="0" cellspacing="0" style="max-width:480px;width:100%;"><tr><td align="center" style="padding-bottom:24px;"><span class="em-m" style="font-size:14px;font-weight:600;color:#71717a;letter-spacing:-0.2px;">${appName}</span></td></tr><tr><td class="em-card" style="background-color:#ffffff;border:1px solid #e4e4e7;border-radius:12px;overflow:hidden;">${content}</td></tr><tr><td align="center" style="padding-top:24px;"><p class="em-m" style="margin:0;font-size:12px;color:#a1a1aa;line-height:1.6;">This email was sent by <strong>${appName}</strong>.<br>Please do not reply to this email.</p></td></tr></table></td></tr></table></body></html>`}emailButton(href,label){return`<table role="presentation" cellpadding="0" cellspacing="0" width="100%"><tr><td align="center" style="padding:28px 32px;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-btn" align="center" style="background-color:#18181b;border-radius:8px;"><a class="em-btn-text" href="${href}" target="_blank" style="display:inline-block;padding:12px 32px;font-size:14px;font-weight:600;color:#fafafa;text-decoration:none;letter-spacing:-0.1px;">${label}</a></td></tr></table></td></tr></table>`}emailDivider(){return'<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:0 32px;"><div class="em-hr" style="height:1px;background-color:#e4e4e7;"></div></td></tr></table>'}emailLinkFallback(link,notice){let html=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:4px 32px 32px 32px;"><p class="em-m" style="margin:0 0 8px 0;font-size:11px;font-weight:600;color:#a1a1aa;text-transform:uppercase;letter-spacing:0.5px;">Or copy this link</p><table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td class="em-lb" style="background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:8px;padding:12px 14px;"><a class="em-link" href="${link}" style="font-size:12px;color:#2563eb;word-break:break-all;line-height:1.5;text-decoration:none;">${link}</a></td></tr></table>`;if(notice)html+=`<p class="em-m" style="margin:16px 0 0 0;font-size:12px;color:#a1a1aa;">${notice}</p>`;return html+="</td></tr></table>",html}async sendWelcomeEmail(email,name,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Welcome to ${appName}</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Your account has been created successfully</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 32px 32px;"><p class="em-t" style="margin:0 0 16px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Welcome aboard! You now have full access to <strong>${appName}</strong>. We're excited to have you.</p></td></tr></table>`;return this.sendEmail({to:email,subject:`Welcome to ${appName}`,html:this.emailWrapper(content,appName)})}async sendVerificationEmail(email,name,verificationLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Verify your email</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Confirm your address to get started</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0 0 4px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Please verify your email address by clicking the button below.</p></td></tr><tr><td>${this.emailButton(verificationLink,"Verify Email Address")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(verificationLink)}`;return this.sendEmail({to:email,subject:`Verify your email \u2014 ${appName}`,html:this.emailWrapper(content,appName)})}async sendPasswordResetEmail(email,name,resetLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Reset your password</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">We received a request to reset your password</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0 0 4px 0;font-size:15px;color:#3f3f46;line-height:1.7;">Hello <strong class="em-h" style="color:#18181b;">${name}</strong>,</p><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Click the button below to choose a new password. If you didn't make this request, you can safely ignore this email.</p></td></tr><tr><td>${this.emailButton(resetLink,"Reset Password")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(resetLink,"This link expires in 1 hour and can only be used once.")}`;return this.sendEmail({to:email,subject:`Reset your password \u2014 ${appName}`,html:this.emailWrapper(content,appName)})}async sendMagicLinkEmail(email,magicLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">Sign in to ${appName}</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">No password needed \u2014 just click to sign in</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:24px 32px 0 32px;"><p class="em-t" style="margin:0;font-size:15px;color:#3f3f46;line-height:1.7;">Click the button below to securely sign in to your <strong class="em-h" style="color:#18181b;">${appName}</strong> account.</p></td></tr><tr><td>${this.emailButton(magicLink,"Sign In")}</td></tr></table>${this.emailDivider()}${this.emailLinkFallback(magicLink,"This link expires in 15 minutes and can only be used once.")}`;return this.sendEmail({to:email,subject:`Sign in to ${appName}`,html:this.emailWrapper(content,appName)})}async sendAlertEmail(to,subject,html){return this.sendEmail({to,subject,html})}async sendInvitationEmail(email,inviteLink,appName="Nucleus"){let content=`<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:40px 32px 24px 32px;text-align:center;"><h1 class="em-h" style="margin:0;font-size:24px;font-weight:700;color:#18181b;letter-spacing:-0.5px;">You're Invited</h1><p class="em-t" style="margin:8px 0 0 0;font-size:15px;color:#52525b;line-height:1.6;">Join <strong class="em-h" style="color:#18181b;">${appName}</strong> and start collaborating</p></td></tr></table>${this.emailDivider()}<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:0 32px 8px 32px;"><table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">1</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Click the button below</td></tr></table></td></tr><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">2</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Choose a secure password</td></tr></table></td></tr><tr><td style="padding:8px 0;"><table role="presentation" cellpadding="0" cellspacing="0"><tr><td class="em-sn" style="width:28px;height:28px;background-color:#f4f4f5;border:1px solid #e4e4e7;border-radius:50%;text-align:center;line-height:28px;font-size:12px;font-weight:600;color:#71717a;">3</td><td class="em-t" style="padding-left:14px;font-size:14px;color:#52525b;">Start collaborating</td></tr></table></td></tr></table></td></tr></table>${this.emailButton(inviteLink,"Accept Invitation")}${this.emailDivider()}${this.emailLinkFallback(inviteLink,"This invitation expires in 7 days.")}`;return this.sendEmail({to:email,subject:`You're invited to join ${appName}`,html:this.emailWrapper(content,appName)})}}var init_Email=()=>{};import fs2 from"fs";import{google}from"googleapis";class GmailService{gmail=null;fromEmail;fromName;logger;initialized=!1;constructor(config,logger2){if(this.fromEmail=config.fromEmail,this.fromName=config.fromName||"Vorion",this.logger=logger2,config.enabled)this.initialize(config.jsonFilePath)}encodeHeaderValue(value){let hasNonAscii=!1;for(let i=0;i<value.length;i++)if(value.charCodeAt(i)>127){hasNonAscii=!0;break}if(!hasNonAscii)return value;return`=?UTF-8?B?${Buffer.from(value,"utf-8").toString("base64")}?=`}isAvailable(){return this.initialized&&this.gmail!==null}initialize(jsonFilePath){try{if(!jsonFilePath){this.logger.warn("[GmailService] No JSON file path provided");return}if(!fs2.existsSync(jsonFilePath)){this.logger.error("[GmailService] JSON file not found",{path:jsonFilePath});return}let keyContent=fs2.readFileSync(jsonFilePath,"utf8"),credentials=JSON.parse(keyContent);if(!this.fromEmail){this.logger.warn("[GmailService] From email not configured");return}if(!credentials?.client_email||!credentials?.private_key){this.logger.error("[GmailService] Invalid credentials format");return}let privateKey=credentials.private_key;if(!privateKey.includes(`
20
20
  `)&&privateKey.includes("\\n"))privateKey=privateKey.replace(/\\n/g,`
21
21
  `);let auth=new google.auth.JWT({email:credentials.client_email,key:privateKey,scopes:["https://www.googleapis.com/auth/gmail.send"],subject:this.fromEmail});this.gmail=google.gmail({version:"v1",auth}),this.initialized=!0,this.logger.info("[GmailService] Initialized",{serviceAccount:credentials.client_email,fromEmail:this.fromEmail})}catch(error){this.logger.error("[GmailService] Initialization failed",{error})}}createEmailMessage(payload){let{to,subject,html,text,from,replyTo,attachments}=payload,toAddresses=Array.isArray(to)?to.join(", "):to,fromAddress=from||`${this.fromName} <${this.fromEmail}>`,encodedSubject=this.encodeHeaderValue(subject);if(!attachments||attachments.length===0){let messageParts2=[`From: ${fromAddress}`,`To: ${toAddresses}`,`Subject: ${encodedSubject}`,"MIME-Version: 1.0","Content-Type: text/html; charset=utf-8"];if(replyTo)messageParts2.push(`Reply-To: ${replyTo}`);return messageParts2.push("",html||text||""),Buffer.from(messageParts2.join(`
22
22
  `)).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}let boundary=`----Nucleus_${Date.now()}_${Math.random().toString(36).substring(7)}`,messageParts=[`From: ${fromAddress}`,`To: ${toAddresses}`,`Subject: ${encodedSubject}`,"MIME-Version: 1.0",`Content-Type: multipart/mixed; boundary="${boundary}"`];if(replyTo)messageParts.push(`Reply-To: ${replyTo}`);messageParts.push("",`--${boundary}`),messageParts.push("Content-Type: text/html; charset=utf-8","Content-Transfer-Encoding: base64","",Buffer.from(html||text||"").toString("base64"),"");for(let attachment of attachments)messageParts.push(`--${boundary}`,`Content-Type: ${attachment.contentType}`,`Content-Disposition: attachment; filename="${attachment.filename}"`,"Content-Transfer-Encoding: base64","",attachment.content.toString("base64"),"");return messageParts.push(`--${boundary}--`),Buffer.from(messageParts.join(`\r
@@ -1616,7 +1616,7 @@ data: ${payload}
1616
1616
  </script>
1617
1617
  <script src="${cdn?cdn:`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${version}/dist/browser/standalone.min.js`}" crossorigin></script>
1618
1618
  </body>
1619
- </html>`;var Kind=Symbol.for("TypeBox.Kind"),toOpenAPIPath=(path4)=>path4.split("/").map((x)=>{if(x.startsWith(":")){if(x=x.slice(1,x.length),x.endsWith("?"))x=x.slice(0,-1);x=`{${x}}`}return x}).join("/"),mapProperties=(name,schema,models)=>{if(schema===void 0)return[];if(typeof schema==="string")if(schema in models)schema=models[schema];else throw Error(`Can't find model ${schema}`);return Object.entries(schema?.properties??[]).map(([key,value])=>{let{type:valueType=void 0,description,examples,...schemaKeywords}=value;return{description,examples,schema:{type:valueType,...schemaKeywords},in:name,name:key,required:schema.required?.includes(key)??!1}})},mapTypesResponse=(types11,schema)=>{if(typeof schema==="object"&&["void","undefined","null"].includes(schema.type))return;let responses={};for(let type of types11)responses[type]={schema:typeof schema==="string"?{$ref:`#/components/schemas/${schema}`}:("$ref"in schema)&&(Kind in schema)&&schema[Kind]==="Ref"?{...schema,$ref:`#/components/schemas/${schema.$ref}`}:replaceSchemaType({...schema},{from:t7.Ref(""),to:({$ref,...options})=>{if(!$ref.startsWith("#/components/schemas/"))return t7.Ref(`#/components/schemas/${$ref}`,options);return t7.Ref($ref,options)}})};return responses},capitalize=(word)=>word.charAt(0).toUpperCase()+word.slice(1),generateOperationId=(method,paths)=>{let operationId=method.toLowerCase();if(paths==="/")return operationId+"Index";for(let path4 of paths.split("/"))if(path4.charCodeAt(0)===123)operationId+="By"+capitalize(path4.slice(1,-1));else operationId+=capitalize(path4);return operationId},cloneHook=(hook)=>{if(!hook)return;if(typeof hook==="string")return hook;if(Array.isArray(hook))return[...hook];return{...hook}},registerSchemaPath=({schema,path:path4,method,hook,models})=>{if(hook=cloneHook(hook),hook.parse&&!Array.isArray(hook.parse))hook.parse=[hook.parse];let contentType=hook.parse?.map((x)=>{switch(typeof x){case"string":return x;case"object":if(x&&typeof x?.fn!=="string")return;switch(x?.fn){case"json":case"application/json":return"application/json";case"text":case"text/plain":return"text/plain";case"urlencoded":case"application/x-www-form-urlencoded":return"application/x-www-form-urlencoded";case"arrayBuffer":case"application/octet-stream":return"application/octet-stream";case"formdata":case"multipart/form-data":return"multipart/form-data"}}}).filter((x)=>x!==void 0);if(!contentType||contentType.length===0)contentType=["application/json","multipart/form-data","text/plain"];path4=toOpenAPIPath(path4);let contentTypes=typeof contentType==="string"?[contentType]:contentType??["application/json"],bodySchema=cloneHook(hook?.body),paramsSchema=cloneHook(hook?.params),headerSchema=cloneHook(hook?.headers),querySchema=cloneHook(hook?.query),responseSchema=cloneHook(hook?.response);if(typeof responseSchema==="object")if(Kind in responseSchema){let{type,properties,required,additionalProperties,patternProperties,$ref,...rest}=responseSchema;responseSchema={"200":{...rest,description:rest.description,content:mapTypesResponse(contentTypes,type==="object"||type==="array"?{type,properties,patternProperties,items:responseSchema.items,required}:responseSchema)}}}else Object.entries(responseSchema).forEach(([key,value])=>{if(typeof value==="string"){if(!models[value])return;let{type,properties,required,additionalProperties:_1,patternProperties:_2,...rest}=models[value];responseSchema[key]={...rest,description:rest.description,content:mapTypesResponse(contentTypes,value)}}else{let{type,properties,required,additionalProperties,patternProperties,...rest}=value;responseSchema[key]={...rest,description:rest.description,content:mapTypesResponse(contentTypes,type==="object"||type==="array"?{type,properties,patternProperties,items:value.items,required}:value)}}});else if(typeof responseSchema==="string"){if(!(responseSchema in models))return;let{type,properties,required,$ref,additionalProperties:_1,patternProperties:_2,...rest}=models[responseSchema];responseSchema={"200":{...rest,content:mapTypesResponse(contentTypes,responseSchema)}}}let parameters=[...mapProperties("header",headerSchema,models),...mapProperties("path",paramsSchema,models),...mapProperties("query",querySchema,models)];schema[path4]={...schema[path4]?schema[path4]:{},[method.toLowerCase()]:{...headerSchema||paramsSchema||querySchema||bodySchema?{parameters}:{},...responseSchema?{responses:responseSchema}:{},operationId:hook?.detail?.operationId??generateOperationId(method,path4),...hook?.detail,...bodySchema?{requestBody:{required:!0,content:mapTypesResponse(contentTypes,typeof bodySchema==="string"?{$ref:`#/components/schemas/${bodySchema}`}:bodySchema)}}:null}}},filterPaths=(paths,{excludeStaticFile=!0,exclude=[]})=>{let newPaths={};for(let[key,value]of Object.entries(paths))if(!exclude.some((x)=>{if(typeof x==="string")return key===x;return x.test(key)})&&!key.includes("*")&&(excludeStaticFile?!key.includes("."):!0))Object.keys(value).forEach((method)=>{let schema=value[method];if(key.includes("{")){if(!schema.parameters)schema.parameters=[];schema.parameters=[...key.split("/").filter((x)=>x.startsWith("{")&&!schema.parameters.find((params)=>params.in==="path"&&params.name===x.slice(1,x.length-1))).map((x)=>({schema:{type:"string"},in:"path",name:x.slice(1,x.length-1),required:!0})),...schema.parameters]}if(!schema.responses)schema.responses={200:{}}}),newPaths[key]=value;return newPaths},swagger=({provider="scalar",scalarVersion="latest",scalarCDN="",scalarConfig={},documentation={},version="5.9.0",excludeStaticFile=!0,path:path4="/swagger",specPath=`${path4}/json`,exclude=[],swaggerOptions={},theme=`https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,autoDarkMode=!0,excludeMethods=["OPTIONS"],excludeTags=[]}={})=>{let schema={},totalRoutes=0;if(!version)version=`https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`;let info={title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...documentation.info},relativePath=specPath.startsWith("/")?specPath.slice(1):specPath,app=new Elysia8({name:"@elysiajs/swagger"}),page=new Response(provider==="swagger-ui"?SwaggerUIRender(info,version,theme,JSON.stringify({url:relativePath,dom_id:"#swagger-ui",...swaggerOptions},(_,value)=>typeof value==="function"?void 0:value),autoDarkMode):ScalarRender(info,scalarVersion,{spec:{url:relativePath,...scalarConfig.spec},...scalarConfig,_integration:"elysiajs"},scalarCDN),{headers:{"content-type":"text/html; charset=utf8"}});return app.get(path4,page,{detail:{hide:!0}}).get(specPath,function(){let routes=app.getGlobalRoutes();if(routes.length!==totalRoutes){let ALLOWED_METHODS=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"];totalRoutes=routes.length,routes.forEach((route)=>{if(route.hooks?.detail?.hide===!0)return;if(excludeMethods.includes(route.method))return;if(ALLOWED_METHODS.includes(route.method)===!1&&route.method!=="ALL")return;if(route.method==="ALL")ALLOWED_METHODS.forEach((method)=>{registerSchemaPath({schema,hook:route.hooks,method,path:route.path,models:app.getGlobalDefinitions?.().type,contentType:route.hooks.type})});else registerSchemaPath({schema,hook:route.hooks,method:route.method,path:route.path,models:app.getGlobalDefinitions?.().type,contentType:route.hooks.type})})}return{openapi:"3.0.3",...{...documentation,tags:documentation.tags?.filter((tag)=>!excludeTags?.includes(tag?.name)),info:{title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...documentation.info}},paths:{...filterPaths(schema,{excludeStaticFile,exclude:Array.isArray(exclude)?exclude:[exclude]}),...documentation.paths},components:{...documentation.components,schemas:{...app.getGlobalDefinitions?.().type,...documentation.components?.schemas}}}},{detail:{hide:!0}}),app};function createSwaggerPlugin(config){if(config?.enabled===!1)return null;let swaggerConfig={path:config?.path??"/swagger",provider:config?.provider??"scalar",excludeStaticFile:config?.excludeStaticFile??!0,exclude:config?.exclude??[],documentation:{info:{title:config?.documentation?.info?.title??"Nucleus API",description:config?.documentation?.info?.description??"Auto-generated API documentation",version:config?.documentation?.info?.version??"1.0.0",contact:config?.documentation?.info?.contact,license:config?.documentation?.info?.license},tags:config?.documentation?.tags??[],servers:config?.documentation?.servers},scalarConfig:config?.scalarConfig};return swagger(swaggerConfig)}init_utils5();init_auth();init_backup();init_storage();init_tenant();var mergeEntitiesByName=(entities)=>{let entityMap=new Map;for(let entity of entities){let existing=entityMap.get(entity.table_name),mergedColumns=entity.columns??existing?.columns;entityMap.set(entity.table_name,{...existing||{},...entity,columns:mergedColumns})}return Array.from(entityMap.values())},normalizeSystemTable=(table)=>({table_name:table.table_name,excluded_methods:table.excluded_methods?[...table.excluded_methods]:void 0,columns:table.columns?table.columns.map((column)=>({name:column.name,type:column.type})):void 0}),extractSchemaTableEntities=(schemaTables)=>{let entities=[];for(let[_key,tableValue]of Object.entries(schemaTables)){if(!tableValue||typeof tableValue!=="object")continue;let tableObj=tableValue,underscoreMeta=tableObj._;if(underscoreMeta?.name){entities.push({table_name:underscoreMeta.name});continue}let symbols3=Object.getOwnPropertySymbols(tableObj);for(let sym of symbols3){let symValue=tableObj[sym];if(symValue&&typeof symValue==="object"){let symMeta=symValue;if(symMeta.name&&typeof symMeta.name==="string"){entities.push({table_name:symMeta.name});break}}}}return entities};async function NucleusElysiaPlugin(config){let plugin=new Elysia28;if(plugin.get("/health",()=>({status:"ok",timestamp:Date.now()})),config.staticAssets!==!1){let path4=__require("path"),fs4=__require("fs"),assetsPath;if(typeof config.staticAssets==="string")assetsPath=config.staticAssets;else{let localPath=path4.join(process.cwd(),"public"),resolvedPkgPath="";for(let pkgName of["nucleus-core-ts","nucleus-core"])try{let pkgJson=__require.resolve(`${pkgName}/package.json`),candidate=path4.join(path4.dirname(pkgJson),"public");if(fs4.existsSync(candidate)){resolvedPkgPath=candidate;break}}catch{}if(resolvedPkgPath)assetsPath=resolvedPkgPath;else if(fs4.existsSync(localPath))assetsPath=localPath;else assetsPath=localPath}try{plugin.use(await staticPlugin({prefix:"/nucleus-core",assets:assetsPath}))}catch{}}let publicRoutes=[],resolvedOptions,configDir=process.cwd();if(typeof config.options==="string"){let fs4=__require("fs"),path4=__require("path"),configPath=path4.isAbsolute(config.options)?config.options:path4.resolve(process.cwd(),config.options);configDir=path4.dirname(configPath);let configContent=fs4.readFileSync(configPath,"utf-8");resolvedOptions=JSON.parse(configContent)}else resolvedOptions=config.options;if(resolvedOptions.email?.gmail?.json_file_path){let path4=__require("path"),gmailPath=resolvedOptions.email.gmail.json_file_path;if(!path4.isAbsolute(gmailPath))resolvedOptions.email.gmail.json_file_path=path4.resolve(configDir,gmailPath)}let{authentication,audit,entities,database}=resolvedOptions,isDev=resolvedOptions.mode==="development",loggingConfig=resolvedOptions.logging,logger2=new Logger({service:resolvedOptions.appId||"nucleus",level:loggingConfig?.level||(isDev?"debug":"info"),prettyPrint:isDev,colorize:isDev,auditEnabled:audit?.enabled??!1,enabledScopes:loggingConfig?.scopes||["*"]}),envValidation=validateEnvVariables(resolvedOptions);if(!envValidation.valid){for(let error3 of envValidation.errors)logger2.error(`[CONFIG] ${error3.message}`,{field:error3.field,envName:error3.envName});throw Error("Nucleus configuration error: Missing required environment variables. Check logs for details.")}let{resolved:envResolved}=envValidation,tokenNames={access_token:authentication?.accessToken?.name||"access_token",refresh_token:authentication?.refreshToken?.name||"refresh_token",session_token:authentication?.sessionToken?.name||"session_token"},targetSchemaName=database?.schemas?.[0]||"main",targetSchema=pgSchema2(targetSchemaName);if(envResolved.databaseUrl)await ensureDatabaseExists(envResolved.databaseUrl,logger2);let db=envResolved.databaseUrl?drizzle2(envResolved.databaseUrl):null,isMultiTenant=database?.isMultiTenant===!0,tenantRegistry=null,schemaTables={},schemaRelations={};if(config.schema){let schemasPath=__require("path").resolve(process.cwd(),config.schema),schemas=__require(schemasPath);schemaTables=schemas.createAllTablesForSchema?schemas.createAllTablesForSchema(targetSchema):{}}if(config.relations){let relationsPath=__require("path").resolve(process.cwd(),config.relations);schemaRelations=__require(relationsPath)}let swaggerPlugin=createSwaggerPlugin(config.swagger);if(swaggerPlugin)plugin.use(swaggerPlugin);let systemTables2=config.systemTables||[];publicRoutes=buildPublicRoutes(resolvedOptions,systemTables2,"",targetSchemaName),logger2.info(`[AUTH] Built ${publicRoutes.length} public routes`);let rateLimiter=null,monitoringService=null,liveMonitoringService=null,emailService=null;if((resolvedOptions.email?.provider||(resolvedOptions.email?.gmail?.enabled?"gmail":resolvedOptions.email?.azure?.enabled?"azure":null))==="azure"&&resolvedOptions.email?.azure?.enabled){let azureConfig=resolvedOptions.email.azure,connectionString=azureConfig.connection_string?process.env[azureConfig.connection_string]||azureConfig.connection_string:void 0;logger2.info("[AzureEmailService] Initializing...",{senderAddress:azureConfig.sender_address}),emailService=new AzureEmailService({enabled:!0,connectionString,senderAddress:azureConfig.sender_address||"",fromName:azureConfig.from_name},logger2),logger2.info("[AzureEmailService] isAvailable:",{available:emailService.isAvailable()})}else if(resolvedOptions.email?.gmail?.enabled&&resolvedOptions.email.gmail.json_file_path)logger2.info("[GmailService] Initializing...",{jsonFilePath:resolvedOptions.email.gmail.json_file_path,fromEmail:resolvedOptions.email.gmail.from_email}),emailService=new GmailService({enabled:!0,jsonFilePath:resolvedOptions.email.gmail.json_file_path,fromEmail:resolvedOptions.email.gmail.from_email||"",fromName:resolvedOptions.email.gmail.from_name},logger2),logger2.info("[GmailService] isAvailable:",{available:emailService.isAvailable()});if(resolvedOptions.liveMonitoring?.enabled){let liveBasePath=resolvedOptions.liveMonitoring.basePath||"/monitoring",liveStreamInterval=resolvedOptions.liveMonitoring.streamInterval||150;plugin.use(createLiveMonitoringRoutes({getService:()=>liveMonitoringService,logger:logger2,basePath:liveBasePath,streamInterval:liveStreamInterval}))}plugin.onStart(async()=>{initiateRedisManager(resolvedOptions);let redis=getRedisManager();if(redis&&resolvedOptions.rateLimit?.enabled!==!1)rateLimiter=new RateLimiter({redis,logger:logger2,config:resolvedOptions.rateLimit||{}}),logger2.info(`[RateLimit] Enabled with strategy: ${resolvedOptions.rateLimit?.strategy||"sliding-window"}`);if(redis&&resolvedOptions.monitoring?.enabled){if(monitoringService=new MonitoringService({redis,logger:logger2,emailService:emailService||void 0,config:resolvedOptions.monitoring,appId:resolvedOptions.appId}),monitoringService.start(),logger2.info("[Monitoring] Service started"),resolvedOptions.monitoring.endpoints?.enabled){let monitoringEndpoints={enabled:!0,basePath:resolvedOptions.monitoring.endpoints.basePath||"/monitoring",stream:{enabled:resolvedOptions.monitoring.endpoints.stream?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.stream?.path||"/stream",interval:resolvedOptions.monitoring.endpoints.stream?.interval||"5s"},snapshot:{enabled:resolvedOptions.monitoring.endpoints.snapshot?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.snapshot?.path||"/snapshot"},history:{enabled:resolvedOptions.monitoring.endpoints.history?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.history?.path||"/history",maxMinutes:resolvedOptions.monitoring.endpoints.history?.maxMinutes||60},alerts:{enabled:resolvedOptions.monitoring.endpoints.alerts?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.alerts?.path||"/alerts"}};plugin.use(createMonitoringRoutes({monitoringService,logger:logger2,endpoints:monitoringEndpoints}))}}if(resolvedOptions.liveMonitoring?.enabled)liveMonitoringService=new LiveMonitoringService(resolvedOptions.liveMonitoring),liveMonitoringService.start(),logger2.info("[LiveMonitoring] Service started");let isConsumerModeOnStart=authentication?.mode==="consumer",consumerAllowedTableKeys=isConsumerModeOnStart&&entities?new Set(entities.map((e)=>e.table_name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase()))):null;if(consumerAllowedTableKeys){let opts=resolvedOptions,verificationEnabled=opts.verification?.enabled===!0,notificationEnabled=opts.notification?.enabled===!0,auditEnabled=opts.audit?.enabled===!0;for(let sysTable of SYSTEM_TABLES)if(sysTable.feature_set.some((f)=>{if(f==="authentication"||f==="authorization")return!1;if(f==="verification")return verificationEnabled;if(f==="notification")return notificationEnabled;if(f==="audit")return auditEnabled;return!1})){let camelKey=sysTable.table_name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());consumerAllowedTableKeys.add(camelKey)}}if(db&&config.schema){let schemas=await import(__require("path").resolve(process.cwd(),config.schema)),auditLogsTable=schemaTables.auditLogs||schemas.auditLogs;if(audit?.enabled&&auditLogsTable)logger2.addAuditTransport(new DatabaseAuditTransport({db,table:auditLogsTable,enabled:!0}));let{ensureSchemaExists:ensureSchemaExists2}=await Promise.resolve().then(() => (init_schema(),exports_schema));try{logger2.info(`Syncing schema to database (target: ${targetSchemaName})...`),await ensureSchemaExists2(db,targetSchemaName);try{let filteredTables=Object.fromEntries(Object.entries(schemaTables).filter(([key,v])=>{if(v===void 0||v===null)return!1;if(consumerAllowedTableKeys&&!consumerAllowedTableKeys.has(key))return!1;if(typeof v==="object"&&v!==null)return Object.getOwnPropertySymbols(v).length>0||v._!==void 0;return!1})),tableNames=Object.keys(filteredTables);if(logger2.info("[Schema] Tables to sync:",{tables:tableNames,count:tableNames.length,mode:isConsumerModeOnStart?"consumer":"full"}),!isConsumerModeOnStart){let usersTableDef=filteredTables.users;if(usersTableDef){let columnSymbols=Object.getOwnPropertyNames(usersTableDef).filter((k)=>!k.startsWith("_"));logger2.info("[Schema] Users table columns:",{columns:columnSymbols})}}await(await pushSchema({schema:targetSchema,...filteredTables},db,[targetSchemaName])).apply(),logger2.info("[Schema] pushSchema completed successfully")}catch(pushError){let msg=pushError instanceof Error?pushError.message:String(pushError);logger2.warn(`[Schema] pushSchema warning: ${msg}`)}console.log("Schema sync completed")}catch(error3){let msg=error3 instanceof Error?error3.message:String(error3);console.error("Schema sync failed:",msg)}if(console.log("Database connection established"),isMultiTenant&&db&&config.schema){let schemasForTenant=await import(__require("path").resolve(process.cwd(),config.schema)),createAllFn=schemasForTenant.createAllTablesForSchema;if(createAllFn){tenantRegistry=new TenantRegistry({db,logger:logger2,mainSchemaName:targetSchemaName,mainSchemaTables:schemaTables,mainSchemaRelations:schemaRelations,createAllTablesForSchema:createAllFn,createAllRelationsForSchema:schemasForTenant.createAllRelationsForSchema,appId:resolvedOptions.appId,authMode:authentication?.mode,tenantResolution:database?.tenantResolution||"both",tenantHeader:database?.tenantHeader||"x-tenant-id",redisCacheTtlSeconds:300}),await tenantRegistry.initialize();for(let schemaName of tenantRegistry.getAllSchemaNames()){if(schemaName===targetSchemaName)continue;let ctx=tenantRegistry.getSchemaContext(schemaName);if(ctx){await ensureSchemaExists2(db,schemaName);try{let tenantFilteredTables=Object.fromEntries(Object.entries(ctx.schemaTables).filter(([key,v])=>{if(v===void 0||v===null)return!1;if(consumerAllowedTableKeys&&!consumerAllowedTableKeys.has(key))return!1;if(typeof v==="object"&&v!==null)return Object.getOwnPropertySymbols(v).length>0||v._!==void 0;return!1})),tenantSchema=pgSchema2(schemaName);await(await pushSchema({schema:tenantSchema,...tenantFilteredTables},db,[schemaName])).apply(),logger2.info(`[Schema] Tenant schema synced: ${schemaName}`)}catch(tenantPushError){let msg=tenantPushError instanceof Error?tenantPushError.message:String(tenantPushError);logger2.warn(`[Schema] Tenant schema sync warning for ${schemaName}: ${msg}`)}}}logger2.info(`[MultiTenant] Registry initialized with ${tenantRegistry.getAllSchemaNames().length} schemas`)}}if(resolvedOptions.authorization?.enabled&&!isConsumerModeOnStart){let authConfig={...DEFAULT_AUTHORIZATION_CONFIG,...resolvedOptions.authorization};if(authConfig.autoSeedClaims){let schemaEntities=extractSchemaTableEntities(schemaTables),systemEntities=SYSTEM_TABLES.map((table)=>normalizeSystemTable(table)),configEntities=resolvedOptions.entities||[],externalEntities=resolvedOptions.authorization?.externalEntities||[],claimEntities=mergeEntitiesByName([...schemaEntities,...configEntities,...systemEntities,...config.systemTables||[],...externalEntities]);logger2.info("[Authorization] Seeding claims...",{schemaEntities:schemaEntities.length,systemEntities:systemEntities.length,configEntities:configEntities.length,externalEntities:externalEntities.length,totalEntities:claimEntities.length}),await seedClaims(db,schemaTables,schemaRelations,claimEntities,authConfig,logger2)}if(authConfig.godminEmail&&authConfig.godminPassword)logger2.info("[Authorization] Setting up godmin..."),await setupGodmin(db,schemaTables,authConfig,logger2);if(logger2.info("[Authorization] Enabled"),isMultiTenant&&tenantRegistry){let tenantSchemas=tenantRegistry.getAllSchemaNames().filter((name)=>name!==targetSchemaName);for(let tenantSchemaName of tenantSchemas){let tenantCtx=tenantRegistry.getSchemaContext(tenantSchemaName);if(!tenantCtx)continue;try{if(authConfig.autoSeedClaims){let tenantSchemaEntities=extractSchemaTableEntities(tenantCtx.schemaTables),tenantClaimEntities=mergeEntitiesByName([...tenantSchemaEntities,...SYSTEM_TABLES.map((table)=>normalizeSystemTable(table)),...resolvedOptions.entities||[],...config.systemTables||[],...resolvedOptions.authorization?.externalEntities||[]]);await seedClaims(db,tenantCtx.schemaTables,tenantCtx.schemaRelations,tenantClaimEntities,authConfig,logger2)}if(tenantCtx.tenant?.godAdminEmail&&authConfig.godminPassword)await setupGodmin(db,tenantCtx.schemaTables,{...authConfig,godminEmail:tenantCtx.tenant.godAdminEmail},logger2);logger2.info(`[Authorization] Tenant schema seeded: ${tenantSchemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);logger2.warn(`[Authorization] Failed to seed tenant ${tenantSchemaName}: ${msg}`)}}logger2.info(`[Authorization] Multi-tenant seeding complete for ${tenantSchemas.length} schemas`)}}let sessionsTableRef=schemaTables.userSessions;if(!isConsumerModeOnStart&&sessionsTableRef&&resolvedOptions.authentication?.sessions?.enabled){let{lt}=await import("drizzle-orm"),expiredCount=await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(sessionsTableRef.isActive,!0),lt(sessionsTableRef.expiresAt,new Date)));logger2.info("[AUTH] Expired sessions cleanup completed",{expiredCount}),setInterval(async()=>{try{await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(sessionsTableRef.isActive,!0),lt(sessionsTableRef.expiresAt,new Date)));let approvalTtlMs=86400000,approvalCutoff=new Date(Date.now()-approvalTtlMs);await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"approval_token_expired",approvalStatus:"rejected",approvalToken:null}).where(and7(eq23(sessionsTableRef.approvalStatus,"pending"),lt(sessionsTableRef.approvalRequestedAt,approvalCutoff)))}catch(err){logger2.warn("[AUTH] Session cleanup failed",{error:err})}},3600000)}if(isMultiTenant&&tenantRegistry&&resolvedOptions.authentication?.sessions?.enabled){let{lt}=await import("drizzle-orm"),tenantSchemaNames=tenantRegistry.getAllSchemaNames().filter((name)=>name!==targetSchemaName);for(let tenantSchemaName of tenantSchemaNames){let tenantCtx=tenantRegistry.getSchemaContext(tenantSchemaName);if(!tenantCtx)continue;let tenantSessionsTable=tenantCtx.schemaTables.userSessions||tenantCtx.schemaTables.user_sessions||tenantCtx.schemaTables.sessions;if(!tenantSessionsTable)continue;try{await db.update(tenantSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(tenantSessionsTable.isActive,!0),lt(tenantSessionsTable.expiresAt,new Date))),logger2.info(`[AUTH] Tenant session cleanup completed: ${tenantSchemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);logger2.warn(`[AUTH] Tenant session cleanup failed for ${tenantSchemaName}: ${msg}`)}}}}}).onRequest(async({request,set:set2})=>{request.headers.delete("x-user-id"),request.headers.delete("x-auth-type"),request.headers.delete("x-api-key-id"),request.headers.delete("x-api-key-owner-type"),request.headers.delete("x-user-roles"),request.headers.delete("x-user-claims"),request.headers.delete("x-session-id"),request.headers.delete("x-access-token"),request.headers.delete("x-refresh-token"),request.headers.delete("x-tenant-schema");let requestStartTime=Date.now(),requestSchemaTables=schemaTables;if(tenantRegistry){let tenantResult=tenantRegistry.resolveFromRequest(request);if(tenantResult.resolved)requestSchemaTables=tenantResult.context.schemaTables,request.headers.set("x-tenant-schema",tenantResult.context.schemaName);else if(new URL(request.url).pathname!=="/health")return set2.status=tenantResult.statusCode,Response.json({isSuccess:!1,message:tenantResult.error,status:tenantResult.statusCode,errors:[{message:tenantResult.error}],data:null})}request.headers.set("x-request-start-time",String(requestStartTime));let url=new URL(request.url),pathname=url.pathname,method=request.method,query=url.search,clientIp=request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||request.headers.get("x-real-ip")?.trim()||"unknown",userAgent=request.headers.get("user-agent")||"unknown",tokens,parsedBody={};if(request.method!=="GET"&&request.method!=="HEAD")try{let text=await request.clone().text();parsedBody=text?JSON.parse(text):{}}catch{parsedBody={}}let auditPayload=audit?.enabled?{id:randomUUID5(),user_id:"unknown",entity_name:pathname.split("/").filter(Boolean)[0]||"root",entity_id:null,operation_type:method,summary:"",old_values:{},new_values:parsedBody,ip_address:clientIp,user_agent:userAgent,timestamp:new Date().toISOString(),path:pathname,query}:null,isPublic=isPublicRoute(publicRoutes,pathname,method);if(rateLimiter){let routeCategory=isPublic?"public":"private",authType;if(pathname.includes("/auth/login"))authType="login";else if(pathname.includes("/auth/register"))authType="register";else if(pathname.includes("/auth/password-reset"))authType="passwordReset";else if(pathname.includes("/auth/magic-link"))authType="magicLink";else if(pathname.includes("/sessions/approve")||pathname.includes("/sessions/reject"))authType="login";let category=authType?"auth":routeCategory,rateLimitResult=await rateLimiter.check({ip:clientIp,endpoint:pathname,category,authType}),headers=rateLimiter.getHeaders(rateLimitResult);for(let[key,value2]of Object.entries(headers))set2.headers[key]=value2;if(!rateLimitResult.allowed){if(set2.status=429,rateLimitResult.retryAfter)set2.headers["Retry-After"]=String(rateLimitResult.retryAfter);if(logger2.warn(`[RateLimit] Blocked request from ${clientIp} to ${pathname}`),monitoringService)monitoringService.recordRateLimitBlock();return new Response(JSON.stringify({error:"Too Many Requests",retryAfter:rateLimitResult.retryAfter}),{status:429,headers:{"Content-Type":"application/json"}})}}if(pathname==="/health")return;if(authentication?.enabled&&!isPublic){let apiKeyRaw=extractApiKeyFromHeader(request.headers),apiKeysTableRef=requestSchemaTables.apiKeys;if(apiKeyRaw&&authentication.apiKeys?.enabled&&apiKeysTableRef&&db){let keyHash=hashApiKey(apiKeyRaw),apiKeyRecord=(await db.select().from(apiKeysTableRef).where(eq23(apiKeysTableRef.keyHash,keyHash)).limit(1))[0];if(!apiKeyRecord)return set2.status=401,logger2.traceSync({message:"Invalid API key",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"Invalid API key")}),Error("Invalid API key");let validation=validateApiKeyRecord(apiKeyRecord);if(!validation.valid)return set2.status=401,logger2.traceSync({message:`API key rejected: ${validation.reason}`,level:"warn",context:{path:pathname,method,keyId:apiKeyRecord.id},audit:toAudit(auditPayload,`API key rejected: ${validation.reason}`)}),Error(validation.reason);let apiKeyUserId=apiKeyRecord.userId,keyAllowedRoles=apiKeyRecord.allowedRoles||[],keyAllowedClaims=apiKeyRecord.allowedClaims||[],effectiveRoles=keyAllowedRoles,effectiveClaims=keyAllowedClaims,userRolesTable=requestSchemaTables.userRoles,rolesTable=requestSchemaTables.roles,roleClaimsTable=requestSchemaTables.roleClaims,claimsTable=requestSchemaTables.claims;if(userRolesTable&&rolesTable){let currentUserRoles=(await db.select({name:rolesTable.name}).from(userRolesTable).innerJoin(rolesTable,eq23(rolesTable.id,userRolesTable.roleId)).where(eq23(userRolesTable.userId,apiKeyUserId))).map((r2)=>r2.name).filter((n2)=>n2!==void 0);effectiveRoles=intersectPermissions(currentUserRoles,keyAllowedRoles)}if(userRolesTable&&roleClaimsTable&&claimsTable){let userClaimRows=await db.select({action:claimsTable.action}).from(userRolesTable).innerJoin(roleClaimsTable,eq23(roleClaimsTable.roleId,userRolesTable.roleId)).innerJoin(claimsTable,eq23(claimsTable.id,roleClaimsTable.claimId)).where(eq23(userRolesTable.userId,apiKeyUserId)),currentUserClaims=[...new Set(userClaimRows.map((r2)=>r2.action).filter((a12)=>a12!==void 0))];effectiveClaims=intersectPermissions(currentUserClaims,keyAllowedClaims)}if(db.update(apiKeysTableRef).set({lastUsedAt:new Date,lastUsedIp:clientIp,usageCount:apiKeyRecord.usageCount+1}).where(eq23(apiKeysTableRef.id,apiKeyRecord.id)).catch(()=>{}),effectiveRoles=effectiveRoles.filter((r2)=>r2!=="godmin"),request.headers.set("x-user-id",apiKeyUserId),request.headers.set("x-auth-type","api_key"),request.headers.set("x-api-key-id",apiKeyRecord.id),request.headers.set("x-api-key-owner-type",apiKeyRecord.ownerType||"personal"),effectiveRoles.length>0)request.headers.set("x-user-roles",effectiveRoles.join(","));if(effectiveClaims.length>0)request.headers.set("x-user-claims",effectiveClaims.join(","));logger2.info("[AUTH] API key authenticated",{userId:apiKeyUserId,keyId:apiKeyRecord.id,ownerType:apiKeyRecord.ownerType,path:pathname,method,effectiveRoles:effectiveRoles.length,effectiveClaims:effectiveClaims.length});return}if(!authentication.accessToken?.secret)return set2.status=500,logger2.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:pathname,method},audit:toAudit(auditPayload,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(authentication.mode==="consumer"){tokens=parseTokenValuesFromHeaders(request.headers,tokenNames);let jwtResult=verifyJWT(tokens.access_token||"",envResolved.accessTokenSecret||"");if(!jwtResult.valid)return set2.status=401,logger2.traceSync({message:"Invalid or missing access token",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"Invalid or missing access token")}),Error("Unauthenticated");let userId=jwtResult.payload.sub,roles=jwtResult.payload.roles,claimsFromToken=jwtResult.payload.claims;if(request.headers.set("x-access-token",tokens.access_token||""),request.headers.set("x-user-id",userId||""),roles&&roles.length>0)request.headers.set("x-user-roles",roles.join(","));if(claimsFromToken&&claimsFromToken.length>0)request.headers.set("x-user-claims",claimsFromToken.join(","))}else{if(!authentication.refreshToken?.secret||!authentication.sessionToken?.secret)return set2.status=500,logger2.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:pathname,method},audit:toAudit(auditPayload,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(tokens=parseTokenValuesFromHeaders(request.headers,tokenNames),!tokens.session_token)return set2.status=401,logger2.traceSync({message:"No session token",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"No session token")}),Error("Unauthenticated");let sessionData=await readSession({sessionId:tokens.session_token});if(!sessionData)return set2.status=401,logger2.traceSync({message:"Invalid session",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token},audit:toAudit(auditPayload,"Invalid session")}),Error("Unauthenticated");let sessionsTableCheck=requestSchemaTables.userSessions;if(sessionsTableCheck&&db){let session=(await db.select().from(sessionsTableCheck).where(eq23(sessionsTableCheck.id,tokens.session_token)).limit(1))[0],isRevoked=session?.revokedAt!==null&&session?.revokedAt!==void 0&&!(typeof session?.revokedAt==="object"&&Object.keys(session.revokedAt).length===0);if(!session||session.isActive===!1||isRevoked)return set2.status=401,logger2.traceSync({message:"Session revoked or inactive",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,isActive:session?.isActive,revokedAt:session?.revokedAt},audit:toAudit(auditPayload,"Session revoked")}),Error("Session has been revoked");if(session.expiresAt&&new Date(session.expiresAt)<new Date)return set2.status=401,logger2.traceSync({message:"Session expired",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,expiresAt:session.expiresAt},audit:toAudit(auditPayload,"Session expired")}),Error("Session has expired")}if(sessionData.lastActiveAt&&authentication.sessions?.inactivityTimeout){let lastActive=new Date(sessionData.lastActiveAt).getTime(),inactivityMs=parseTimeToSeconds2(authentication.sessions.inactivityTimeout)*1000;if(Date.now()-lastActive>inactivityMs)return set2.status=401,logger2.traceSync({message:"Session inactive timeout",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,lastActiveAt:sessionData.lastActiveAt},audit:toAudit(auditPayload,"Session inactive timeout")}),Error("Session expired due to inactivity")}updateLastActiveAt(tokens.session_token).catch(()=>{});let sessionsTableRef=requestSchemaTables.userSessions;if(sessionsTableRef&&db)db.update(sessionsTableRef).set({lastActivityAt:new Date}).where(eq23(sessionsTableRef.id,tokens.session_token)).catch(()=>{});let jwtResult=verifyJWT(tokens.access_token||"",envResolved.accessTokenSecret||""),isAccessTokenValid=tokens.access_token?jwtResult.valid:!1,isRefreshTokenValid=tokens.refresh_token?verifyJWT(tokens.refresh_token,envResolved.refreshTokenSecret||"").valid:!1;if(!isAccessTokenValid&&isRefreshTokenValid&&tokens.refresh_token&&sessionData.rememberMe===!0){let refreshResult=await refreshAccessTokenWithLock(sessionData.userId,sessionData.id,()=>signNewAccessToken({refreshTokenId:tokens.refresh_token,options:resolvedOptions,sessionData}));if(refreshResult.success&&refreshResult.accessToken){tokens.access_token=refreshResult.accessToken;let rawDomain=authentication.cookieDomain,resolvedDomain=rawDomain?process.env[rawDomain]??rawDomain:void 0,domainPart=resolvedDomain?`; Domain=${resolvedDomain}`:"",cookieValue=`${tokenNames.access_token}=${refreshResult.accessToken}; Path=/; HttpOnly; SameSite=Strict; Secure; Max-Age=${parseTimeToSeconds2(authentication.accessToken.expiresIn??"15m")}${domainPart}`;set2.headers["Set-Cookie"]=cookieValue}}let userId=jwtResult.valid?jwtResult.payload.sub:sessionData.userId,roles=jwtResult.valid?jwtResult.payload.roles:void 0,claimsFromToken=jwtResult.valid?jwtResult.payload.claims:void 0;if(request.headers.set("x-access-token",tokens.access_token||""),request.headers.set("x-refresh-token",tokens.refresh_token||""),request.headers.set("x-session-id",tokens.session_token||""),request.headers.set("x-user-id",userId||""),roles&&roles.length>0)request.headers.set("x-user-roles",roles.join(","));if(claimsFromToken&&claimsFromToken.length>0)request.headers.set("x-user-claims",claimsFromToken.join(","))}}}).onAfterHandle(({request,set:set2})=>{if(monitoringService){let startTimeStr=request.headers.get("x-request-start-time"),startTime=startTimeStr?parseInt(startTimeStr,10):Date.now(),responseTimeMs=Date.now()-startTime,url=new URL(request.url),status=typeof set2.status==="number"?set2.status:200;monitoringService.recordRequest({endpoint:url.pathname,method:request.method,status,responseTimeMs,isError:status>=400,errorType:status>=500?"server_error":status>=400?"client_error":void 0})}if(liveMonitoringService){let url=new URL(request.url),headersObj={};request.headers.forEach((value2,key)=>{headersObj[key]=value2}),liveMonitoringService.recordRequest({path:url.pathname,method:request.method,timestamp:Date.now(),headers:headersObj})}}).onError((ctx)=>{let{set:set2,code,error:error3}=ctx,status=typeof code==="number"?code:500,message="Internal Server Error";if(error3 instanceof Error){let cause=error3.cause,pgCode=cause?.code;if(pgCode==="23505")message=`Duplicate value: ${cause?.detail||"A record with this value already exists"}`;else if(pgCode==="23503")message=`Invalid reference: ${cause?.detail||"Referenced record does not exist"}`;else if(pgCode==="23502")message=`Missing required field: ${cause?.column||cause?.detail||"A required field is empty"}`;else if(pgCode==="22P02")message=`Invalid input: ${cause?.routine==="string_to_uuid"?"Invalid ID format":cause?.detail||"Invalid data format"}`;else if(pgCode)message=`Database error (${pgCode}): ${cause?.detail||cause?.message||error3.message}`;else message=error3.message}return set2.status=status,Response.json({isSuccess:!1,message,status,errors:[{message}],data:null})}),logger2.info("Creating routes for entities"),createEntityRoutes(plugin,{db,schemaTables,schemaRelations,entities,logger:logger2,databaseUrl:envResolved.databaseUrl,storage:resolvedOptions.storage,authorization:resolvedOptions.authorization,authMode:authentication?.mode,idpUrl:authentication?.idpUrl?process.env[authentication.idpUrl]||authentication.idpUrl:void 0,emailServiceAvailable:!!emailService?.isAvailable(),tenantRegistry});let isConsumerMode=authentication?.mode==="consumer";if(isMultiTenant&&tenantRegistry&&db&&!isConsumerMode){let{createTenantRoutes:createTenantRoutes2}=(init_tenant(),__toCommonJS(exports_tenant));createTenantRoutes2(plugin,{db,logger:logger2,tenantRegistry,schemaName:targetSchemaName}),logger2.info("[MultiTenant] Tenant provisioning routes registered")}if(authentication?.enabled&&!isConsumerMode&&db){let resolveTableForTenant=(tableName,reqSchemaName)=>{if(reqSchemaName&&tenantRegistry){let ctx=tenantRegistry.getSchemaContext(reqSchemaName);if(ctx?.schemaTables[tableName])return ctx.schemaTables[tableName]}return schemaTables[tableName]},usersTable=schemaTables.users,sessionsTable=schemaTables.userSessions||schemaTables.user_sessions||schemaTables.sessions;if(!sessionsTable&&authentication.sessions?.enabled)logger2.warn("[AUTH] sessions is enabled but user_sessions table not found in schema. Disabling sessions.");if(usersTable){initiateRedisManager(resolvedOptions);let{createAuthRoutes:createAuthRoutes2}=(init_auth(),__toCommonJS(exports_auth)),{signJWT:signJWT2,verifyJWT:verifyJWT2}=(init_JWT(),__toCommonJS(exports_JWT)),{generateSession:generateSession2,deleteSession:deleteSession2}=(init_SessionStore(),__toCommonJS(exports_SessionStore));createAuthRoutes2(plugin,{authConfig:{db,logger:logger2,usersTable,sessionsTable,userRolesTable:schemaTables.userRoles,rolesTable:schemaTables.roles,roleClaimsTable:schemaTables.roleClaims,claimsTable:schemaTables.claims,authentication:{enabled:authentication.enabled,cookieDomain:resolvedOptions.authentication?.cookieDomain,accessToken:authentication.accessToken,refreshToken:authentication.refreshToken,sessionToken:authentication.sessionToken}},features:{login:authentication.login,register:authentication.register,logout:authentication.logout,refresh:authentication.refresh,passwordReset:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.passwordReset?.enabled&&!emailAvailable)return logger2.warn("[AUTH] passwordReset is enabled but no email provider is configured. Disabling passwordReset."),{...authentication.passwordReset,enabled:!1};return authentication.passwordReset})(),passwordChange:authentication.passwordChange,passwordSet:authentication.passwordSet,sessions:authentication.sessions,magicLink:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.magicLink?.enabled&&!emailAvailable)return logger2.warn("[AUTH] magicLink is enabled but no email provider is configured. Disabling magicLink."),{...authentication.magicLink,enabled:!1};return authentication.magicLink})(),me:authentication.me||{enabled:!0,route:"/auth/me"},invite:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.invite?.enabled&&!emailAvailable)return logger2.warn("[AUTH] invite is enabled but no email provider is configured. Disabling invite."),{...authentication.invite,enabled:!1};return authentication.invite})(),captcha:authentication.captcha,oauth:authentication.oauth?.enabled&&envResolved.oauthProviders?{...authentication.oauth,providers:envResolved.oauthProviders}:void 0,apiKeys:authentication.apiKeys?.enabled?{enabled:!0,route:authentication.apiKeys.route,keyPrefix:authentication.apiKeys.keyPrefix,maxKeysPerUser:authentication.apiKeys.maxKeysPerUser,defaultExpiresIn:authentication.apiKeys.defaultExpiresIn,allowApplicationKeys:authentication.apiKeys.allowApplicationKeys,preventApiKeyManagement:authentication.apiKeys.preventApiKeyManagement}:void 0},sessionsTable,oauthAccountsTable:schemaTables.oauthAccounts,apiKeysTable:schemaTables.apiKeys,schemaTables,schemaRelations,tenantRegistry,databaseUrl:envResolved.databaseUrl,admin:{impersonate:{enabled:!0},changeUserId:{enabled:!0}},schemaName:targetSchemaName,emailService,appName:resolvedOptions.appId,captchaService:(()=>{let redisManager=getRedisManager();if(!authentication.captcha?.enabled||!redisManager)return null;return new CaptchaService({redis:{get:async(key)=>{let result=await redisManager.read(key);return result.success?result.data:null},set:async(key,value2,options)=>{await redisManager.create(key,value2,options?.ex)},del:async(key)=>{await redisManager.remove(key)}},logger:logger2,config:{enabled:!0,type:authentication.captcha.type||"math",difficulty:authentication.captcha.difficulty||"medium",expiresIn:authentication.captcha.expiresIn||"5m",maxAttempts:authentication.captcha.maxAttempts||3,caseSensitive:authentication.captcha.caseSensitive??!1}})})(),tokenResponseConfig:{accessToken:{setHeadersEnabled:authentication.accessToken?.setHeadersEnabled??!0,returnJson:authentication.accessToken?.returnJson??!0},refreshToken:{setHeadersEnabled:authentication.refreshToken?.setHeadersEnabled??!0,returnJson:authentication.refreshToken?.returnJson??!0},sessionToken:{setHeadersEnabled:authentication.sessionToken?.setHeadersEnabled??!0,returnJson:authentication.sessionToken?.returnJson??!0}},helpers:{signAccessToken:(userId,roles,claims)=>signJWT2({subject:userId,expiresInSeconds:parseTimeToSeconds2(authentication.accessToken?.expiresIn||"15m"),issuer:authentication.accessToken?.issuer,audience:authentication.accessToken?.audience,customClaims:{...roles&&roles.length>0?{roles}:{},...claims&&claims.length>0?{claims}:{}}},envResolved.accessTokenSecret||"",authentication.accessToken?.algorithm||"HS256"),signRefreshToken:(userId)=>signJWT2({subject:userId,expiresInSeconds:parseTimeToSeconds2(authentication.refreshToken?.expiresIn||"7d"),issuer:authentication.refreshToken?.issuer,audience:authentication.refreshToken?.audience},envResolved.refreshTokenSecret||"",authentication.refreshToken?.algorithm||"HS256"),verifyRefreshToken:(token)=>verifyJWT2(token,envResolved.refreshTokenSecret||""),createSession:async(params)=>{let result=await generateSession2({userId:params.userId,deviceInfo:params.deviceInfo});return result.success?result.session.id:""},destroySession:async(sessionId)=>deleteSession2({sessionId}),saveSessionToDb:async(sessionId,params,reqSchemaName)=>{let resolvedSessionsTable=resolveTableForTenant("userSessions",reqSchemaName)||resolveTableForTenant("user_sessions",reqSchemaName)||resolveTableForTenant("sessions",reqSchemaName)||sessionsTable;if(!resolvedSessionsTable||!db)return;let sessionsConfig=authentication.sessions,deviceInfo=params.deviceInfo||{},deviceFingerprint=deviceInfo.deviceHint?`${deviceInfo.browserName||""}-${deviceInfo.osName||""}-${deviceInfo.deviceType||""}-${deviceInfo.deviceHint}`:`${deviceInfo.browserName||""}-${deviceInfo.osName||""}-${deviceInfo.deviceType||""}`,existingSessions=await db.select().from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))),hasValidFingerprint=deviceFingerprint&&!deviceFingerprint.includes("--unknown")&&!deviceFingerprint.includes("Bot/Crawler")&&!deviceFingerprint.includes("Headless")&&deviceFingerprint!=="--"&&deviceFingerprint!=="--unknown",allUserSessions=await db.select().from(resolvedSessionsTable).where(eq23(resolvedSessionsTable.userId,params.userId)),isNewDevice=hasValidFingerprint?!existingSessions.some((s)=>s.deviceFingerprint===deviceFingerprint):!1,wasPreviouslyApproved=hasValidFingerprint?allUserSessions.some((s)=>{let sess=s;return sess.deviceFingerprint===deviceFingerprint&&sess.approvalStatus==="approved"}):!1,hasAnyApprovedSession=existingSessions.some((s)=>s.approvalStatus==="approved"),isImpersonationLogin=params.loginMethod==="impersonation"||params.loginMethod==="impersonation_stop",isOAuthLogin=params.loginMethod?.startsWith("oauth:"),requiresApproval=!isImpersonationLogin&&sessionsConfig?.trustNewDevices===!1&&isNewDevice&&!wasPreviouslyApproved&&hasValidFingerprint&&hasAnyApprovedSession;logger2.info("[AUTH] Device fingerprint analysis",{userId:params.userId,deviceFingerprint,hasValidFingerprint,isNewDevice,wasPreviouslyApproved,loginMethod:params.loginMethod,isImpersonationLogin,isOAuthLogin,existingSessionCount:existingSessions.length,hasAnyApprovedSession,requiresApproval});let approvalToken=null,approvalStatus="approved";if(requiresApproval){let existingPending=allUserSessions.find((s)=>{let sess=s;return sess.deviceFingerprint===deviceFingerprint&&(sess.approvalStatus==="pending"||sess.approval_status==="pending")&&sess.approvalToken});if(existingPending){let pendingSess=existingPending,pendingRequestedAt=pendingSess.approvalRequestedAt||pendingSess.approval_requested_at;if(pendingRequestedAt?Date.now()-new Date(pendingRequestedAt).getTime()<86400000:!0)return logger2.info("[AUTH] Reusing existing pending session for same device",{userId:params.userId,deviceFingerprint,existingSessionId:pendingSess.id}),{requiresApproval:!0,sessionId:pendingSess.id}}let{randomBytes:randomBytes4}=await import("crypto");approvalToken=randomBytes4(32).toString("hex"),approvalStatus="pending",logger2.info("[AUTH] New device requires approval",{userId:params.userId,deviceFingerprint,ipAddress:deviceInfo.ipAddress})}let staleBotSessions=existingSessions.filter((s)=>{let sess=s,fp=(sess.deviceFingerprint||"").toLowerCase(),ip=sess.ipAddress||"",ua=(sess.userAgent||"").toLowerCase(),isBotFingerprint=!fp||fp==="--"||fp==="--unknown"||fp.includes("bot/crawler")||fp.includes("headless")||fp.includes("unknown-unknown"),isServerAction=ua.includes("nucleusserveraction")||ua.includes("serveraction")||ua.includes("node-fetch")||ua.includes("undici");return isBotFingerprint&&(ip==="127.0.0.1"||ip==="::1"||ip==="localhost"||!ip)||isServerAction});if(staleBotSessions.length>0){for(let botSession of staleBotSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"bot_session_cleanup"}).where(eq23(resolvedSessionsTable.id,botSession.id));logger2.info("[AUTH] Cleaned up stale bot/crawler sessions",{userId:params.userId,cleanedCount:staleBotSessions.length})}if(!sessionsConfig?.allowMultipleDevices&&existingSessions.length>0){if(existingSessions.filter((s)=>s.deviceFingerprint===deviceFingerprint).length===0&&isNewDevice)for(let oldSession of existingSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"new_device_login"}).where(eq23(resolvedSessionsTable.id,oldSession.id))}if(sessionsConfig?.maxActiveSessions){let{count:count2}=await import("drizzle-orm"),currentCount=(await db.select({count:count2()}).from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))))[0]?.count||0;if(currentCount>=sessionsConfig.maxActiveSessions){let{asc:asc2}=await import("drizzle-orm"),oldestSessions=await db.select().from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))).orderBy(asc2(resolvedSessionsTable.createdAt)).limit(currentCount-sessionsConfig.maxActiveSessions+1);for(let oldSession of oldestSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"max_sessions_exceeded"}).where(eq23(resolvedSessionsTable.id,oldSession.id))}}let trustScore=100;if(deviceInfo.isHeadless)trustScore-=50;if(deviceInfo.isBot)trustScore-=40;if(deviceInfo.isSuspicious)logger2.warn("[AUTH] Suspicious login detected",{userId:params.userId,suspiciousPatterns:deviceInfo.suspiciousPatterns,userAgent:deviceInfo.userAgent,ipAddress:deviceInfo.ipAddress});if(isNewDevice)trustScore-=25;if(!deviceInfo.ipAddress||deviceInfo.ipAddress==="unknown")trustScore-=20;if(!deviceInfo.browserName)trustScore-=15;if(!deviceInfo.osName)trustScore-=15;if(!deviceInfo.deviceType||deviceInfo.deviceType==="unknown")trustScore-=10;if(!deviceInfo.deviceName||deviceInfo.deviceName==="Unknown Device")trustScore-=5;let validFingerprint=deviceFingerprint&&!deviceFingerprint.includes("--unknown")&&deviceFingerprint!=="--",validIp=deviceInfo.ipAddress&&deviceInfo.ipAddress!=="unknown";if(validFingerprint){if(existingSessions.filter((s)=>s.deviceFingerprint===deviceFingerprint).length>0)trustScore+=20}if(validIp){if(existingSessions.filter((s)=>s.ipAddress===deviceInfo.ipAddress).length>0)trustScore+=15}trustScore=Math.max(0,Math.min(100,trustScore));let LOW_TRUST_THRESHOLD=50;if(await db.insert(resolvedSessionsTable).values({id:sessionId,userId:params.userId,tokenHash:sessionId,deviceFingerprint,deviceName:deviceInfo.deviceName,deviceType:deviceInfo.deviceType,browserName:deviceInfo.browserName,browserVersion:deviceInfo.browserVersion,osName:deviceInfo.osName,osVersion:deviceInfo.osVersion,ipAddress:deviceInfo.ipAddress,locationCountry:deviceInfo.locationCountry,locationCity:deviceInfo.locationCity,loginMethod:params.loginMethod||"password",rememberMe:params.rememberMe??!1,trustScore,lastActivityAt:new Date,createdAt:new Date,expiresAt:new Date(Date.now()+parseTimeToSeconds2(authentication.sessionToken?.expiresIn||"30d")*1000),isActive:approvalStatus==="approved",approvalStatus,approvalToken,approvalRequestedAt:requiresApproval?new Date:null}),!isImpersonationLogin&&emailService&&(sessionsConfig?.notifyOnNewDevice&&isNewDevice||trustScore<LOW_TRUST_THRESHOLD||requiresApproval)){let resolvedUsersTable=resolveTableForTenant("users",reqSchemaName);if(resolvedUsersTable){let user=(await db.select().from(resolvedUsersTable).where(eq23(resolvedUsersTable.id,params.userId)).limit(1))[0];if(user?.email){let isLowTrust=trustScore<LOW_TRUST_THRESHOLD,sessionsRoute=authentication.sessions?.route||"/auth/sessions",configuredUrl=authentication.sessions?.approvalRedirectUrl||"",isLegacyFrontendUrl=!configuredUrl||configuredUrl.endsWith("/devices"),approvalBase;if(!isLegacyFrontendUrl)approvalBase=configuredUrl;else{let origin=params.requestOrigin;if(!origin&&configuredUrl)try{origin=new URL(configuredUrl).origin}catch{}approvalBase=`${origin||"http://localhost:9000"}${sessionsRoute}`}let approveUrl=approvalToken?`${approvalBase}/approve-page?token=${approvalToken}`:"",rejectUrl=approvalToken?`${approvalBase}/reject-page?token=${approvalToken}`:"",subject,emailHtml,brandName=resolvedOptions.appId||"Nucleus",loginTime=new Date().toLocaleString("en-US",{dateStyle:"medium",timeStyle:"short"}),deviceSummary=`${deviceInfo.browserName||"Unknown"} ${deviceInfo.browserVersion||""} on ${deviceInfo.osName||"Unknown"} ${deviceInfo.osVersion||""}`,emailWrapper=(content)=>`
1619
+ </html>`;var Kind=Symbol.for("TypeBox.Kind"),toOpenAPIPath=(path4)=>path4.split("/").map((x)=>{if(x.startsWith(":")){if(x=x.slice(1,x.length),x.endsWith("?"))x=x.slice(0,-1);x=`{${x}}`}return x}).join("/"),mapProperties=(name,schema,models)=>{if(schema===void 0)return[];if(typeof schema==="string")if(schema in models)schema=models[schema];else throw Error(`Can't find model ${schema}`);return Object.entries(schema?.properties??[]).map(([key,value])=>{let{type:valueType=void 0,description,examples,...schemaKeywords}=value;return{description,examples,schema:{type:valueType,...schemaKeywords},in:name,name:key,required:schema.required?.includes(key)??!1}})},mapTypesResponse=(types11,schema)=>{if(typeof schema==="object"&&["void","undefined","null"].includes(schema.type))return;let responses={};for(let type of types11)responses[type]={schema:typeof schema==="string"?{$ref:`#/components/schemas/${schema}`}:("$ref"in schema)&&(Kind in schema)&&schema[Kind]==="Ref"?{...schema,$ref:`#/components/schemas/${schema.$ref}`}:replaceSchemaType({...schema},{from:t7.Ref(""),to:({$ref,...options})=>{if(!$ref.startsWith("#/components/schemas/"))return t7.Ref(`#/components/schemas/${$ref}`,options);return t7.Ref($ref,options)}})};return responses},capitalize=(word)=>word.charAt(0).toUpperCase()+word.slice(1),generateOperationId=(method,paths)=>{let operationId=method.toLowerCase();if(paths==="/")return operationId+"Index";for(let path4 of paths.split("/"))if(path4.charCodeAt(0)===123)operationId+="By"+capitalize(path4.slice(1,-1));else operationId+=capitalize(path4);return operationId},cloneHook=(hook)=>{if(!hook)return;if(typeof hook==="string")return hook;if(Array.isArray(hook))return[...hook];return{...hook}},registerSchemaPath=({schema,path:path4,method,hook,models})=>{if(hook=cloneHook(hook),hook.parse&&!Array.isArray(hook.parse))hook.parse=[hook.parse];let contentType=hook.parse?.map((x)=>{switch(typeof x){case"string":return x;case"object":if(x&&typeof x?.fn!=="string")return;switch(x?.fn){case"json":case"application/json":return"application/json";case"text":case"text/plain":return"text/plain";case"urlencoded":case"application/x-www-form-urlencoded":return"application/x-www-form-urlencoded";case"arrayBuffer":case"application/octet-stream":return"application/octet-stream";case"formdata":case"multipart/form-data":return"multipart/form-data"}}}).filter((x)=>x!==void 0);if(!contentType||contentType.length===0)contentType=["application/json","multipart/form-data","text/plain"];path4=toOpenAPIPath(path4);let contentTypes=typeof contentType==="string"?[contentType]:contentType??["application/json"],bodySchema=cloneHook(hook?.body),paramsSchema=cloneHook(hook?.params),headerSchema=cloneHook(hook?.headers),querySchema=cloneHook(hook?.query),responseSchema=cloneHook(hook?.response);if(typeof responseSchema==="object")if(Kind in responseSchema){let{type,properties,required,additionalProperties,patternProperties,$ref,...rest}=responseSchema;responseSchema={"200":{...rest,description:rest.description,content:mapTypesResponse(contentTypes,type==="object"||type==="array"?{type,properties,patternProperties,items:responseSchema.items,required}:responseSchema)}}}else Object.entries(responseSchema).forEach(([key,value])=>{if(typeof value==="string"){if(!models[value])return;let{type,properties,required,additionalProperties:_1,patternProperties:_2,...rest}=models[value];responseSchema[key]={...rest,description:rest.description,content:mapTypesResponse(contentTypes,value)}}else{let{type,properties,required,additionalProperties,patternProperties,...rest}=value;responseSchema[key]={...rest,description:rest.description,content:mapTypesResponse(contentTypes,type==="object"||type==="array"?{type,properties,patternProperties,items:value.items,required}:value)}}});else if(typeof responseSchema==="string"){if(!(responseSchema in models))return;let{type,properties,required,$ref,additionalProperties:_1,patternProperties:_2,...rest}=models[responseSchema];responseSchema={"200":{...rest,content:mapTypesResponse(contentTypes,responseSchema)}}}let parameters=[...mapProperties("header",headerSchema,models),...mapProperties("path",paramsSchema,models),...mapProperties("query",querySchema,models)];schema[path4]={...schema[path4]?schema[path4]:{},[method.toLowerCase()]:{...headerSchema||paramsSchema||querySchema||bodySchema?{parameters}:{},...responseSchema?{responses:responseSchema}:{},operationId:hook?.detail?.operationId??generateOperationId(method,path4),...hook?.detail,...bodySchema?{requestBody:{required:!0,content:mapTypesResponse(contentTypes,typeof bodySchema==="string"?{$ref:`#/components/schemas/${bodySchema}`}:bodySchema)}}:null}}},filterPaths=(paths,{excludeStaticFile=!0,exclude=[]})=>{let newPaths={};for(let[key,value]of Object.entries(paths))if(!exclude.some((x)=>{if(typeof x==="string")return key===x;return x.test(key)})&&!key.includes("*")&&(excludeStaticFile?!key.includes("."):!0))Object.keys(value).forEach((method)=>{let schema=value[method];if(key.includes("{")){if(!schema.parameters)schema.parameters=[];schema.parameters=[...key.split("/").filter((x)=>x.startsWith("{")&&!schema.parameters.find((params)=>params.in==="path"&&params.name===x.slice(1,x.length-1))).map((x)=>({schema:{type:"string"},in:"path",name:x.slice(1,x.length-1),required:!0})),...schema.parameters]}if(!schema.responses)schema.responses={200:{}}}),newPaths[key]=value;return newPaths},swagger=({provider="scalar",scalarVersion="latest",scalarCDN="",scalarConfig={},documentation={},version="5.9.0",excludeStaticFile=!0,path:path4="/swagger",specPath=`${path4}/json`,exclude=[],swaggerOptions={},theme=`https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,autoDarkMode=!0,excludeMethods=["OPTIONS"],excludeTags=[]}={})=>{let schema={},totalRoutes=0;if(!version)version=`https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`;let info={title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...documentation.info},relativePath=specPath.startsWith("/")?specPath.slice(1):specPath,app=new Elysia8({name:"@elysiajs/swagger"}),page=new Response(provider==="swagger-ui"?SwaggerUIRender(info,version,theme,JSON.stringify({url:relativePath,dom_id:"#swagger-ui",...swaggerOptions},(_,value)=>typeof value==="function"?void 0:value),autoDarkMode):ScalarRender(info,scalarVersion,{spec:{url:relativePath,...scalarConfig.spec},...scalarConfig,_integration:"elysiajs"},scalarCDN),{headers:{"content-type":"text/html; charset=utf8"}});return app.get(path4,page,{detail:{hide:!0}}).get(specPath,function(){let routes=app.getGlobalRoutes();if(routes.length!==totalRoutes){let ALLOWED_METHODS=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"];totalRoutes=routes.length,routes.forEach((route)=>{if(route.hooks?.detail?.hide===!0)return;if(excludeMethods.includes(route.method))return;if(ALLOWED_METHODS.includes(route.method)===!1&&route.method!=="ALL")return;if(route.method==="ALL")ALLOWED_METHODS.forEach((method)=>{registerSchemaPath({schema,hook:route.hooks,method,path:route.path,models:app.getGlobalDefinitions?.().type,contentType:route.hooks.type})});else registerSchemaPath({schema,hook:route.hooks,method:route.method,path:route.path,models:app.getGlobalDefinitions?.().type,contentType:route.hooks.type})})}return{openapi:"3.0.3",...{...documentation,tags:documentation.tags?.filter((tag)=>!excludeTags?.includes(tag?.name)),info:{title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...documentation.info}},paths:{...filterPaths(schema,{excludeStaticFile,exclude:Array.isArray(exclude)?exclude:[exclude]}),...documentation.paths},components:{...documentation.components,schemas:{...app.getGlobalDefinitions?.().type,...documentation.components?.schemas}}}},{detail:{hide:!0}}),app};function createSwaggerPlugin(config){if(config?.enabled===!1)return null;let swaggerConfig={path:config?.path??"/swagger",provider:config?.provider??"scalar",excludeStaticFile:config?.excludeStaticFile??!0,exclude:config?.exclude??[],documentation:{info:{title:config?.documentation?.info?.title??"Nucleus API",description:config?.documentation?.info?.description??"Auto-generated API documentation",version:config?.documentation?.info?.version??"1.0.0",contact:config?.documentation?.info?.contact,license:config?.documentation?.info?.license},tags:config?.documentation?.tags??[],servers:config?.documentation?.servers},scalarConfig:config?.scalarConfig};return swagger(swaggerConfig)}init_utils5();init_auth();init_backup();init_storage();init_tenant();var mergeEntitiesByName=(entities)=>{let entityMap=new Map;for(let entity of entities){let existing=entityMap.get(entity.table_name),mergedColumns=entity.columns??existing?.columns;entityMap.set(entity.table_name,{...existing||{},...entity,columns:mergedColumns})}return Array.from(entityMap.values())},normalizeSystemTable=(table)=>({table_name:table.table_name,excluded_methods:table.excluded_methods?[...table.excluded_methods]:void 0,columns:table.columns?table.columns.map((column)=>({name:column.name,type:column.type})):void 0}),extractSchemaTableEntities=(schemaTables)=>{let entities=[];for(let[_key,tableValue]of Object.entries(schemaTables)){if(!tableValue||typeof tableValue!=="object")continue;let tableObj=tableValue,underscoreMeta=tableObj._;if(underscoreMeta?.name){entities.push({table_name:underscoreMeta.name});continue}let symbols3=Object.getOwnPropertySymbols(tableObj);for(let sym of symbols3){let symValue=tableObj[sym];if(symValue&&typeof symValue==="object"){let symMeta=symValue;if(symMeta.name&&typeof symMeta.name==="string"){entities.push({table_name:symMeta.name});break}}}}return entities};async function NucleusElysiaPlugin(config){let plugin=new Elysia28;if(plugin.get("/health",()=>({status:"ok",timestamp:Date.now()})),config.staticAssets!==!1){let path4=__require("path"),fs4=__require("fs"),assetsPath;if(typeof config.staticAssets==="string")assetsPath=config.staticAssets;else{let localPath=path4.join(process.cwd(),"public"),resolvedPkgPath="";for(let pkgName of["nucleus-core-ts","nucleus-core"])try{let pkgJson=__require.resolve(`${pkgName}/package.json`),candidate=path4.join(path4.dirname(pkgJson),"public");if(fs4.existsSync(candidate)){resolvedPkgPath=candidate;break}}catch{}if(resolvedPkgPath)assetsPath=resolvedPkgPath;else if(fs4.existsSync(localPath))assetsPath=localPath;else assetsPath=localPath}try{plugin.use(await staticPlugin({prefix:"/nucleus-core",assets:assetsPath}))}catch{}}let publicRoutes=[],resolvedOptions,configDir=process.cwd();if(typeof config.options==="string"){let fs4=__require("fs"),path4=__require("path"),configPath=path4.isAbsolute(config.options)?config.options:path4.resolve(process.cwd(),config.options);configDir=path4.dirname(configPath);let configContent=fs4.readFileSync(configPath,"utf-8");resolvedOptions=JSON.parse(configContent)}else resolvedOptions=config.options;if(resolvedOptions.email?.gmail?.json_file_path){let path4=__require("path"),gmailPath=resolvedOptions.email.gmail.json_file_path;if(!path4.isAbsolute(gmailPath))resolvedOptions.email.gmail.json_file_path=path4.resolve(configDir,gmailPath)}let{authentication,audit,entities,database}=resolvedOptions,isDev=resolvedOptions.mode==="development",loggingConfig=resolvedOptions.logging,logger2=new Logger({service:resolvedOptions.appId||"nucleus",level:loggingConfig?.level||(isDev?"debug":"info"),prettyPrint:isDev,colorize:isDev,auditEnabled:audit?.enabled??!1,enabledScopes:loggingConfig?.scopes||["*"]}),envValidation=validateEnvVariables(resolvedOptions);if(!envValidation.valid){for(let error3 of envValidation.errors)logger2.error(`[CONFIG] ${error3.message}`,{field:error3.field,envName:error3.envName});throw Error("Nucleus configuration error: Missing required environment variables. Check logs for details.")}let{resolved:envResolved}=envValidation,tokenNames={access_token:authentication?.accessToken?.name||"access_token",refresh_token:authentication?.refreshToken?.name||"refresh_token",session_token:authentication?.sessionToken?.name||"session_token"},targetSchemaName=database?.schemas?.[0]||"main",targetSchema=pgSchema2(targetSchemaName);if(envResolved.databaseUrl)await ensureDatabaseExists(envResolved.databaseUrl,logger2);let db=envResolved.databaseUrl?drizzle2(envResolved.databaseUrl):null,isMultiTenant=database?.isMultiTenant===!0,tenantRegistry=null,schemaTables={},schemaRelations={};if(config.schema){let schemasPath=__require("path").resolve(process.cwd(),config.schema),schemas=__require(schemasPath);schemaTables=schemas.createAllTablesForSchema?schemas.createAllTablesForSchema(targetSchema):{}}if(config.relations){let relationsPath=__require("path").resolve(process.cwd(),config.relations);schemaRelations=__require(relationsPath)}let swaggerPlugin=createSwaggerPlugin(config.swagger);if(swaggerPlugin)plugin.use(swaggerPlugin);let systemTables2=config.systemTables||[];publicRoutes=buildPublicRoutes(resolvedOptions,systemTables2,"",targetSchemaName),logger2.info(`[AUTH] Built ${publicRoutes.length} public routes`);let rateLimiter=null,monitoringService=null,liveMonitoringService=null,emailService=null;if((resolvedOptions.email?.provider||(resolvedOptions.email?.gmail?.enabled?"gmail":resolvedOptions.email?.azure?.enabled?"azure":null))==="azure"&&resolvedOptions.email?.azure?.enabled){let azureConfig=resolvedOptions.email.azure,connectionString=azureConfig.connection_string?process.env[azureConfig.connection_string]||azureConfig.connection_string:void 0,senderAddress=azureConfig.sender_address?process.env[azureConfig.sender_address]||azureConfig.sender_address:"";logger2.info("[AzureEmailService] Initializing...",{senderAddress}),emailService=new AzureEmailService({enabled:!0,connectionString,senderAddress,fromName:azureConfig.from_name},logger2),logger2.info("[AzureEmailService] isAvailable:",{available:emailService.isAvailable()})}else if(resolvedOptions.email?.gmail?.enabled&&resolvedOptions.email.gmail.json_file_path)logger2.info("[GmailService] Initializing...",{jsonFilePath:resolvedOptions.email.gmail.json_file_path,fromEmail:resolvedOptions.email.gmail.from_email}),emailService=new GmailService({enabled:!0,jsonFilePath:resolvedOptions.email.gmail.json_file_path,fromEmail:resolvedOptions.email.gmail.from_email||"",fromName:resolvedOptions.email.gmail.from_name},logger2),logger2.info("[GmailService] isAvailable:",{available:emailService.isAvailable()});if(resolvedOptions.liveMonitoring?.enabled){let liveBasePath=resolvedOptions.liveMonitoring.basePath||"/monitoring",liveStreamInterval=resolvedOptions.liveMonitoring.streamInterval||150;plugin.use(createLiveMonitoringRoutes({getService:()=>liveMonitoringService,logger:logger2,basePath:liveBasePath,streamInterval:liveStreamInterval}))}plugin.onStart(async()=>{initiateRedisManager(resolvedOptions);let redis=getRedisManager();if(redis&&resolvedOptions.rateLimit?.enabled!==!1)rateLimiter=new RateLimiter({redis,logger:logger2,config:resolvedOptions.rateLimit||{}}),logger2.info(`[RateLimit] Enabled with strategy: ${resolvedOptions.rateLimit?.strategy||"sliding-window"}`);if(redis&&resolvedOptions.monitoring?.enabled){if(monitoringService=new MonitoringService({redis,logger:logger2,emailService:emailService||void 0,config:resolvedOptions.monitoring,appId:resolvedOptions.appId}),monitoringService.start(),logger2.info("[Monitoring] Service started"),resolvedOptions.monitoring.endpoints?.enabled){let monitoringEndpoints={enabled:!0,basePath:resolvedOptions.monitoring.endpoints.basePath||"/monitoring",stream:{enabled:resolvedOptions.monitoring.endpoints.stream?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.stream?.path||"/stream",interval:resolvedOptions.monitoring.endpoints.stream?.interval||"5s"},snapshot:{enabled:resolvedOptions.monitoring.endpoints.snapshot?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.snapshot?.path||"/snapshot"},history:{enabled:resolvedOptions.monitoring.endpoints.history?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.history?.path||"/history",maxMinutes:resolvedOptions.monitoring.endpoints.history?.maxMinutes||60},alerts:{enabled:resolvedOptions.monitoring.endpoints.alerts?.enabled!==!1,path:resolvedOptions.monitoring.endpoints.alerts?.path||"/alerts"}};plugin.use(createMonitoringRoutes({monitoringService,logger:logger2,endpoints:monitoringEndpoints}))}}if(resolvedOptions.liveMonitoring?.enabled)liveMonitoringService=new LiveMonitoringService(resolvedOptions.liveMonitoring),liveMonitoringService.start(),logger2.info("[LiveMonitoring] Service started");let isConsumerModeOnStart=authentication?.mode==="consumer",consumerAllowedTableKeys=isConsumerModeOnStart&&entities?new Set(entities.map((e)=>e.table_name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase()))):null;if(consumerAllowedTableKeys){let opts=resolvedOptions,verificationEnabled=opts.verification?.enabled===!0,notificationEnabled=opts.notification?.enabled===!0,auditEnabled=opts.audit?.enabled===!0;for(let sysTable of SYSTEM_TABLES)if(sysTable.feature_set.some((f)=>{if(f==="authentication"||f==="authorization")return!1;if(f==="verification")return verificationEnabled;if(f==="notification")return notificationEnabled;if(f==="audit")return auditEnabled;return!1})){let camelKey=sysTable.table_name.replace(/_([a-z])/g,(_,c)=>c.toUpperCase());consumerAllowedTableKeys.add(camelKey)}}if(db&&config.schema){let schemas=await import(__require("path").resolve(process.cwd(),config.schema)),auditLogsTable=schemaTables.auditLogs||schemas.auditLogs;if(audit?.enabled&&auditLogsTable)logger2.addAuditTransport(new DatabaseAuditTransport({db,table:auditLogsTable,enabled:!0}));let{ensureSchemaExists:ensureSchemaExists2}=await Promise.resolve().then(() => (init_schema(),exports_schema));try{logger2.info(`Syncing schema to database (target: ${targetSchemaName})...`),await ensureSchemaExists2(db,targetSchemaName);try{let filteredTables=Object.fromEntries(Object.entries(schemaTables).filter(([key,v])=>{if(v===void 0||v===null)return!1;if(consumerAllowedTableKeys&&!consumerAllowedTableKeys.has(key))return!1;if(typeof v==="object"&&v!==null)return Object.getOwnPropertySymbols(v).length>0||v._!==void 0;return!1})),tableNames=Object.keys(filteredTables);if(logger2.info("[Schema] Tables to sync:",{tables:tableNames,count:tableNames.length,mode:isConsumerModeOnStart?"consumer":"full"}),!isConsumerModeOnStart){let usersTableDef=filteredTables.users;if(usersTableDef){let columnSymbols=Object.getOwnPropertyNames(usersTableDef).filter((k)=>!k.startsWith("_"));logger2.info("[Schema] Users table columns:",{columns:columnSymbols})}}await(await pushSchema({schema:targetSchema,...filteredTables},db,[targetSchemaName])).apply(),logger2.info("[Schema] pushSchema completed successfully")}catch(pushError){let msg=pushError instanceof Error?pushError.message:String(pushError);logger2.warn(`[Schema] pushSchema warning: ${msg}`)}console.log("Schema sync completed")}catch(error3){let msg=error3 instanceof Error?error3.message:String(error3);console.error("Schema sync failed:",msg)}if(console.log("Database connection established"),isMultiTenant&&db&&config.schema){let schemasForTenant=await import(__require("path").resolve(process.cwd(),config.schema)),createAllFn=schemasForTenant.createAllTablesForSchema;if(createAllFn){tenantRegistry=new TenantRegistry({db,logger:logger2,mainSchemaName:targetSchemaName,mainSchemaTables:schemaTables,mainSchemaRelations:schemaRelations,createAllTablesForSchema:createAllFn,createAllRelationsForSchema:schemasForTenant.createAllRelationsForSchema,appId:resolvedOptions.appId,authMode:authentication?.mode,tenantResolution:database?.tenantResolution||"both",tenantHeader:database?.tenantHeader||"x-tenant-id",redisCacheTtlSeconds:300}),await tenantRegistry.initialize();for(let schemaName of tenantRegistry.getAllSchemaNames()){if(schemaName===targetSchemaName)continue;let ctx=tenantRegistry.getSchemaContext(schemaName);if(ctx){await ensureSchemaExists2(db,schemaName);try{let tenantFilteredTables=Object.fromEntries(Object.entries(ctx.schemaTables).filter(([key,v])=>{if(v===void 0||v===null)return!1;if(consumerAllowedTableKeys&&!consumerAllowedTableKeys.has(key))return!1;if(typeof v==="object"&&v!==null)return Object.getOwnPropertySymbols(v).length>0||v._!==void 0;return!1})),tenantSchema=pgSchema2(schemaName);await(await pushSchema({schema:tenantSchema,...tenantFilteredTables},db,[schemaName])).apply(),logger2.info(`[Schema] Tenant schema synced: ${schemaName}`)}catch(tenantPushError){let msg=tenantPushError instanceof Error?tenantPushError.message:String(tenantPushError);logger2.warn(`[Schema] Tenant schema sync warning for ${schemaName}: ${msg}`)}}}logger2.info(`[MultiTenant] Registry initialized with ${tenantRegistry.getAllSchemaNames().length} schemas`)}}if(resolvedOptions.authorization?.enabled&&!isConsumerModeOnStart){let authConfig={...DEFAULT_AUTHORIZATION_CONFIG,...resolvedOptions.authorization};if(authConfig.autoSeedClaims){let schemaEntities=extractSchemaTableEntities(schemaTables),systemEntities=SYSTEM_TABLES.map((table)=>normalizeSystemTable(table)),configEntities=resolvedOptions.entities||[],externalEntities=resolvedOptions.authorization?.externalEntities||[],claimEntities=mergeEntitiesByName([...schemaEntities,...configEntities,...systemEntities,...config.systemTables||[],...externalEntities]);logger2.info("[Authorization] Seeding claims...",{schemaEntities:schemaEntities.length,systemEntities:systemEntities.length,configEntities:configEntities.length,externalEntities:externalEntities.length,totalEntities:claimEntities.length}),await seedClaims(db,schemaTables,schemaRelations,claimEntities,authConfig,logger2)}if(authConfig.godminEmail&&authConfig.godminPassword)logger2.info("[Authorization] Setting up godmin..."),await setupGodmin(db,schemaTables,authConfig,logger2);if(logger2.info("[Authorization] Enabled"),isMultiTenant&&tenantRegistry){let tenantSchemas=tenantRegistry.getAllSchemaNames().filter((name)=>name!==targetSchemaName);for(let tenantSchemaName of tenantSchemas){let tenantCtx=tenantRegistry.getSchemaContext(tenantSchemaName);if(!tenantCtx)continue;try{if(authConfig.autoSeedClaims){let tenantSchemaEntities=extractSchemaTableEntities(tenantCtx.schemaTables),tenantClaimEntities=mergeEntitiesByName([...tenantSchemaEntities,...SYSTEM_TABLES.map((table)=>normalizeSystemTable(table)),...resolvedOptions.entities||[],...config.systemTables||[],...resolvedOptions.authorization?.externalEntities||[]]);await seedClaims(db,tenantCtx.schemaTables,tenantCtx.schemaRelations,tenantClaimEntities,authConfig,logger2)}if(tenantCtx.tenant?.godAdminEmail&&authConfig.godminPassword)await setupGodmin(db,tenantCtx.schemaTables,{...authConfig,godminEmail:tenantCtx.tenant.godAdminEmail},logger2);logger2.info(`[Authorization] Tenant schema seeded: ${tenantSchemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);logger2.warn(`[Authorization] Failed to seed tenant ${tenantSchemaName}: ${msg}`)}}logger2.info(`[Authorization] Multi-tenant seeding complete for ${tenantSchemas.length} schemas`)}}let sessionsTableRef=schemaTables.userSessions;if(!isConsumerModeOnStart&&sessionsTableRef&&resolvedOptions.authentication?.sessions?.enabled){let{lt}=await import("drizzle-orm"),expiredCount=await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(sessionsTableRef.isActive,!0),lt(sessionsTableRef.expiresAt,new Date)));logger2.info("[AUTH] Expired sessions cleanup completed",{expiredCount}),setInterval(async()=>{try{await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(sessionsTableRef.isActive,!0),lt(sessionsTableRef.expiresAt,new Date)));let approvalTtlMs=86400000,approvalCutoff=new Date(Date.now()-approvalTtlMs);await db.update(sessionsTableRef).set({isActive:!1,revokedAt:new Date,revokedReason:"approval_token_expired",approvalStatus:"rejected",approvalToken:null}).where(and7(eq23(sessionsTableRef.approvalStatus,"pending"),lt(sessionsTableRef.approvalRequestedAt,approvalCutoff)))}catch(err){logger2.warn("[AUTH] Session cleanup failed",{error:err})}},3600000)}if(isMultiTenant&&tenantRegistry&&resolvedOptions.authentication?.sessions?.enabled){let{lt}=await import("drizzle-orm"),tenantSchemaNames=tenantRegistry.getAllSchemaNames().filter((name)=>name!==targetSchemaName);for(let tenantSchemaName of tenantSchemaNames){let tenantCtx=tenantRegistry.getSchemaContext(tenantSchemaName);if(!tenantCtx)continue;let tenantSessionsTable=tenantCtx.schemaTables.userSessions||tenantCtx.schemaTables.user_sessions||tenantCtx.schemaTables.sessions;if(!tenantSessionsTable)continue;try{await db.update(tenantSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(and7(eq23(tenantSessionsTable.isActive,!0),lt(tenantSessionsTable.expiresAt,new Date))),logger2.info(`[AUTH] Tenant session cleanup completed: ${tenantSchemaName}`)}catch(err){let msg=err instanceof Error?err.message:String(err);logger2.warn(`[AUTH] Tenant session cleanup failed for ${tenantSchemaName}: ${msg}`)}}}}}).onRequest(async({request,set:set2})=>{request.headers.delete("x-user-id"),request.headers.delete("x-auth-type"),request.headers.delete("x-api-key-id"),request.headers.delete("x-api-key-owner-type"),request.headers.delete("x-user-roles"),request.headers.delete("x-user-claims"),request.headers.delete("x-session-id"),request.headers.delete("x-access-token"),request.headers.delete("x-refresh-token"),request.headers.delete("x-tenant-schema");let requestStartTime=Date.now(),requestSchemaTables=schemaTables;if(tenantRegistry){let tenantResult=tenantRegistry.resolveFromRequest(request);if(tenantResult.resolved)requestSchemaTables=tenantResult.context.schemaTables,request.headers.set("x-tenant-schema",tenantResult.context.schemaName);else if(new URL(request.url).pathname!=="/health")return set2.status=tenantResult.statusCode,Response.json({isSuccess:!1,message:tenantResult.error,status:tenantResult.statusCode,errors:[{message:tenantResult.error}],data:null})}request.headers.set("x-request-start-time",String(requestStartTime));let url=new URL(request.url),pathname=url.pathname,method=request.method,query=url.search,clientIp=request.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||request.headers.get("x-real-ip")?.trim()||"unknown",userAgent=request.headers.get("user-agent")||"unknown",tokens,parsedBody={};if(request.method!=="GET"&&request.method!=="HEAD")try{let text=await request.clone().text();parsedBody=text?JSON.parse(text):{}}catch{parsedBody={}}let auditPayload=audit?.enabled?{id:randomUUID5(),user_id:"unknown",entity_name:pathname.split("/").filter(Boolean)[0]||"root",entity_id:null,operation_type:method,summary:"",old_values:{},new_values:parsedBody,ip_address:clientIp,user_agent:userAgent,timestamp:new Date().toISOString(),path:pathname,query}:null,isPublic=isPublicRoute(publicRoutes,pathname,method);if(rateLimiter){let routeCategory=isPublic?"public":"private",authType;if(pathname.includes("/auth/login"))authType="login";else if(pathname.includes("/auth/register"))authType="register";else if(pathname.includes("/auth/password-reset"))authType="passwordReset";else if(pathname.includes("/auth/magic-link"))authType="magicLink";else if(pathname.includes("/sessions/approve")||pathname.includes("/sessions/reject"))authType="login";let category=authType?"auth":routeCategory,rateLimitResult=await rateLimiter.check({ip:clientIp,endpoint:pathname,category,authType}),headers=rateLimiter.getHeaders(rateLimitResult);for(let[key,value2]of Object.entries(headers))set2.headers[key]=value2;if(!rateLimitResult.allowed){if(set2.status=429,rateLimitResult.retryAfter)set2.headers["Retry-After"]=String(rateLimitResult.retryAfter);if(logger2.warn(`[RateLimit] Blocked request from ${clientIp} to ${pathname}`),monitoringService)monitoringService.recordRateLimitBlock();return new Response(JSON.stringify({error:"Too Many Requests",retryAfter:rateLimitResult.retryAfter}),{status:429,headers:{"Content-Type":"application/json"}})}}if(pathname==="/health")return;if(authentication?.enabled&&!isPublic){let apiKeyRaw=extractApiKeyFromHeader(request.headers),apiKeysTableRef=requestSchemaTables.apiKeys;if(apiKeyRaw&&authentication.apiKeys?.enabled&&apiKeysTableRef&&db){let keyHash=hashApiKey(apiKeyRaw),apiKeyRecord=(await db.select().from(apiKeysTableRef).where(eq23(apiKeysTableRef.keyHash,keyHash)).limit(1))[0];if(!apiKeyRecord)return set2.status=401,logger2.traceSync({message:"Invalid API key",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"Invalid API key")}),Error("Invalid API key");let validation=validateApiKeyRecord(apiKeyRecord);if(!validation.valid)return set2.status=401,logger2.traceSync({message:`API key rejected: ${validation.reason}`,level:"warn",context:{path:pathname,method,keyId:apiKeyRecord.id},audit:toAudit(auditPayload,`API key rejected: ${validation.reason}`)}),Error(validation.reason);let apiKeyUserId=apiKeyRecord.userId,keyAllowedRoles=apiKeyRecord.allowedRoles||[],keyAllowedClaims=apiKeyRecord.allowedClaims||[],effectiveRoles=keyAllowedRoles,effectiveClaims=keyAllowedClaims,userRolesTable=requestSchemaTables.userRoles,rolesTable=requestSchemaTables.roles,roleClaimsTable=requestSchemaTables.roleClaims,claimsTable=requestSchemaTables.claims;if(userRolesTable&&rolesTable){let currentUserRoles=(await db.select({name:rolesTable.name}).from(userRolesTable).innerJoin(rolesTable,eq23(rolesTable.id,userRolesTable.roleId)).where(eq23(userRolesTable.userId,apiKeyUserId))).map((r2)=>r2.name).filter((n2)=>n2!==void 0);effectiveRoles=intersectPermissions(currentUserRoles,keyAllowedRoles)}if(userRolesTable&&roleClaimsTable&&claimsTable){let userClaimRows=await db.select({action:claimsTable.action}).from(userRolesTable).innerJoin(roleClaimsTable,eq23(roleClaimsTable.roleId,userRolesTable.roleId)).innerJoin(claimsTable,eq23(claimsTable.id,roleClaimsTable.claimId)).where(eq23(userRolesTable.userId,apiKeyUserId)),currentUserClaims=[...new Set(userClaimRows.map((r2)=>r2.action).filter((a12)=>a12!==void 0))];effectiveClaims=intersectPermissions(currentUserClaims,keyAllowedClaims)}if(db.update(apiKeysTableRef).set({lastUsedAt:new Date,lastUsedIp:clientIp,usageCount:apiKeyRecord.usageCount+1}).where(eq23(apiKeysTableRef.id,apiKeyRecord.id)).catch(()=>{}),effectiveRoles=effectiveRoles.filter((r2)=>r2!=="godmin"),request.headers.set("x-user-id",apiKeyUserId),request.headers.set("x-auth-type","api_key"),request.headers.set("x-api-key-id",apiKeyRecord.id),request.headers.set("x-api-key-owner-type",apiKeyRecord.ownerType||"personal"),effectiveRoles.length>0)request.headers.set("x-user-roles",effectiveRoles.join(","));if(effectiveClaims.length>0)request.headers.set("x-user-claims",effectiveClaims.join(","));logger2.info("[AUTH] API key authenticated",{userId:apiKeyUserId,keyId:apiKeyRecord.id,ownerType:apiKeyRecord.ownerType,path:pathname,method,effectiveRoles:effectiveRoles.length,effectiveClaims:effectiveClaims.length});return}if(!authentication.accessToken?.secret)return set2.status=500,logger2.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:pathname,method},audit:toAudit(auditPayload,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(authentication.mode==="consumer"){tokens=parseTokenValuesFromHeaders(request.headers,tokenNames);let jwtResult=verifyJWT(tokens.access_token||"",envResolved.accessTokenSecret||"");if(!jwtResult.valid)return set2.status=401,logger2.traceSync({message:"Invalid or missing access token",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"Invalid or missing access token")}),Error("Unauthenticated");let userId=jwtResult.payload.sub,roles=jwtResult.payload.roles,claimsFromToken=jwtResult.payload.claims;if(request.headers.set("x-access-token",tokens.access_token||""),request.headers.set("x-user-id",userId||""),roles&&roles.length>0)request.headers.set("x-user-roles",roles.join(","));if(claimsFromToken&&claimsFromToken.length>0)request.headers.set("x-user-claims",claimsFromToken.join(","))}else{if(!authentication.refreshToken?.secret||!authentication.sessionToken?.secret)return set2.status=500,logger2.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:pathname,method},audit:toAudit(auditPayload,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(tokens=parseTokenValuesFromHeaders(request.headers,tokenNames),!tokens.session_token)return set2.status=401,logger2.traceSync({message:"No session token",level:"warn",context:{path:pathname,method},audit:toAudit(auditPayload,"No session token")}),Error("Unauthenticated");let sessionData=await readSession({sessionId:tokens.session_token});if(!sessionData)return set2.status=401,logger2.traceSync({message:"Invalid session",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token},audit:toAudit(auditPayload,"Invalid session")}),Error("Unauthenticated");let sessionsTableCheck=requestSchemaTables.userSessions;if(sessionsTableCheck&&db){let session=(await db.select().from(sessionsTableCheck).where(eq23(sessionsTableCheck.id,tokens.session_token)).limit(1))[0],isRevoked=session?.revokedAt!==null&&session?.revokedAt!==void 0&&!(typeof session?.revokedAt==="object"&&Object.keys(session.revokedAt).length===0);if(!session||session.isActive===!1||isRevoked)return set2.status=401,logger2.traceSync({message:"Session revoked or inactive",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,isActive:session?.isActive,revokedAt:session?.revokedAt},audit:toAudit(auditPayload,"Session revoked")}),Error("Session has been revoked");if(session.expiresAt&&new Date(session.expiresAt)<new Date)return set2.status=401,logger2.traceSync({message:"Session expired",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,expiresAt:session.expiresAt},audit:toAudit(auditPayload,"Session expired")}),Error("Session has expired")}if(sessionData.lastActiveAt&&authentication.sessions?.inactivityTimeout){let lastActive=new Date(sessionData.lastActiveAt).getTime(),inactivityMs=parseTimeToSeconds2(authentication.sessions.inactivityTimeout)*1000;if(Date.now()-lastActive>inactivityMs)return set2.status=401,logger2.traceSync({message:"Session inactive timeout",level:"warn",context:{path:pathname,method,sessionId:tokens.session_token,lastActiveAt:sessionData.lastActiveAt},audit:toAudit(auditPayload,"Session inactive timeout")}),Error("Session expired due to inactivity")}updateLastActiveAt(tokens.session_token).catch(()=>{});let sessionsTableRef=requestSchemaTables.userSessions;if(sessionsTableRef&&db)db.update(sessionsTableRef).set({lastActivityAt:new Date}).where(eq23(sessionsTableRef.id,tokens.session_token)).catch(()=>{});let jwtResult=verifyJWT(tokens.access_token||"",envResolved.accessTokenSecret||""),isAccessTokenValid=tokens.access_token?jwtResult.valid:!1,isRefreshTokenValid=tokens.refresh_token?verifyJWT(tokens.refresh_token,envResolved.refreshTokenSecret||"").valid:!1;if(!isAccessTokenValid&&isRefreshTokenValid&&tokens.refresh_token&&sessionData.rememberMe===!0){let refreshResult=await refreshAccessTokenWithLock(sessionData.userId,sessionData.id,()=>signNewAccessToken({refreshTokenId:tokens.refresh_token,options:resolvedOptions,sessionData}));if(refreshResult.success&&refreshResult.accessToken){tokens.access_token=refreshResult.accessToken;let rawDomain=authentication.cookieDomain,resolvedDomain=rawDomain?process.env[rawDomain]??rawDomain:void 0,domainPart=resolvedDomain?`; Domain=${resolvedDomain}`:"",cookieValue=`${tokenNames.access_token}=${refreshResult.accessToken}; Path=/; HttpOnly; SameSite=Strict; Secure; Max-Age=${parseTimeToSeconds2(authentication.accessToken.expiresIn??"15m")}${domainPart}`;set2.headers["Set-Cookie"]=cookieValue}}let userId=jwtResult.valid?jwtResult.payload.sub:sessionData.userId,roles=jwtResult.valid?jwtResult.payload.roles:void 0,claimsFromToken=jwtResult.valid?jwtResult.payload.claims:void 0;if(request.headers.set("x-access-token",tokens.access_token||""),request.headers.set("x-refresh-token",tokens.refresh_token||""),request.headers.set("x-session-id",tokens.session_token||""),request.headers.set("x-user-id",userId||""),roles&&roles.length>0)request.headers.set("x-user-roles",roles.join(","));if(claimsFromToken&&claimsFromToken.length>0)request.headers.set("x-user-claims",claimsFromToken.join(","))}}}).onAfterHandle(({request,set:set2})=>{if(monitoringService){let startTimeStr=request.headers.get("x-request-start-time"),startTime=startTimeStr?parseInt(startTimeStr,10):Date.now(),responseTimeMs=Date.now()-startTime,url=new URL(request.url),status=typeof set2.status==="number"?set2.status:200;monitoringService.recordRequest({endpoint:url.pathname,method:request.method,status,responseTimeMs,isError:status>=400,errorType:status>=500?"server_error":status>=400?"client_error":void 0})}if(liveMonitoringService){let url=new URL(request.url),headersObj={};request.headers.forEach((value2,key)=>{headersObj[key]=value2}),liveMonitoringService.recordRequest({path:url.pathname,method:request.method,timestamp:Date.now(),headers:headersObj})}}).onError((ctx)=>{let{set:set2,code,error:error3}=ctx,status=typeof code==="number"?code:500,message="Internal Server Error";if(error3 instanceof Error){let cause=error3.cause,pgCode=cause?.code;if(pgCode==="23505")message=`Duplicate value: ${cause?.detail||"A record with this value already exists"}`;else if(pgCode==="23503")message=`Invalid reference: ${cause?.detail||"Referenced record does not exist"}`;else if(pgCode==="23502")message=`Missing required field: ${cause?.column||cause?.detail||"A required field is empty"}`;else if(pgCode==="22P02")message=`Invalid input: ${cause?.routine==="string_to_uuid"?"Invalid ID format":cause?.detail||"Invalid data format"}`;else if(pgCode)message=`Database error (${pgCode}): ${cause?.detail||cause?.message||error3.message}`;else message=error3.message}return set2.status=status,Response.json({isSuccess:!1,message,status,errors:[{message}],data:null})}),logger2.info("Creating routes for entities"),createEntityRoutes(plugin,{db,schemaTables,schemaRelations,entities,logger:logger2,databaseUrl:envResolved.databaseUrl,storage:resolvedOptions.storage,authorization:resolvedOptions.authorization,authMode:authentication?.mode,idpUrl:authentication?.idpUrl?process.env[authentication.idpUrl]||authentication.idpUrl:void 0,emailServiceAvailable:!!emailService?.isAvailable(),tenantRegistry});let isConsumerMode=authentication?.mode==="consumer";if(isMultiTenant&&tenantRegistry&&db&&!isConsumerMode){let{createTenantRoutes:createTenantRoutes2}=(init_tenant(),__toCommonJS(exports_tenant));createTenantRoutes2(plugin,{db,logger:logger2,tenantRegistry,schemaName:targetSchemaName}),logger2.info("[MultiTenant] Tenant provisioning routes registered")}if(authentication?.enabled&&!isConsumerMode&&db){let resolveTableForTenant=(tableName,reqSchemaName)=>{if(reqSchemaName&&tenantRegistry){let ctx=tenantRegistry.getSchemaContext(reqSchemaName);if(ctx?.schemaTables[tableName])return ctx.schemaTables[tableName]}return schemaTables[tableName]},usersTable=schemaTables.users,sessionsTable=schemaTables.userSessions||schemaTables.user_sessions||schemaTables.sessions;if(!sessionsTable&&authentication.sessions?.enabled)logger2.warn("[AUTH] sessions is enabled but user_sessions table not found in schema. Disabling sessions.");if(usersTable){initiateRedisManager(resolvedOptions);let{createAuthRoutes:createAuthRoutes2}=(init_auth(),__toCommonJS(exports_auth)),{signJWT:signJWT2,verifyJWT:verifyJWT2}=(init_JWT(),__toCommonJS(exports_JWT)),{generateSession:generateSession2,deleteSession:deleteSession2}=(init_SessionStore(),__toCommonJS(exports_SessionStore));createAuthRoutes2(plugin,{authConfig:{db,logger:logger2,usersTable,sessionsTable,userRolesTable:schemaTables.userRoles,rolesTable:schemaTables.roles,roleClaimsTable:schemaTables.roleClaims,claimsTable:schemaTables.claims,authentication:{enabled:authentication.enabled,cookieDomain:resolvedOptions.authentication?.cookieDomain,accessToken:authentication.accessToken,refreshToken:authentication.refreshToken,sessionToken:authentication.sessionToken}},features:{login:authentication.login,register:authentication.register,logout:authentication.logout,refresh:authentication.refresh,passwordReset:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.passwordReset?.enabled&&!emailAvailable)return logger2.warn("[AUTH] passwordReset is enabled but no email provider is configured. Disabling passwordReset."),{...authentication.passwordReset,enabled:!1};return authentication.passwordReset})(),passwordChange:authentication.passwordChange,passwordSet:authentication.passwordSet,sessions:authentication.sessions,magicLink:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.magicLink?.enabled&&!emailAvailable)return logger2.warn("[AUTH] magicLink is enabled but no email provider is configured. Disabling magicLink."),{...authentication.magicLink,enabled:!1};return authentication.magicLink})(),me:authentication.me||{enabled:!0,route:"/auth/me"},invite:(()=>{let emailAvailable=!!emailService?.isAvailable();if(authentication.invite?.enabled&&!emailAvailable)return logger2.warn("[AUTH] invite is enabled but no email provider is configured. Disabling invite."),{...authentication.invite,enabled:!1};return authentication.invite})(),captcha:authentication.captcha,oauth:authentication.oauth?.enabled&&envResolved.oauthProviders?{...authentication.oauth,providers:envResolved.oauthProviders}:void 0,apiKeys:authentication.apiKeys?.enabled?{enabled:!0,route:authentication.apiKeys.route,keyPrefix:authentication.apiKeys.keyPrefix,maxKeysPerUser:authentication.apiKeys.maxKeysPerUser,defaultExpiresIn:authentication.apiKeys.defaultExpiresIn,allowApplicationKeys:authentication.apiKeys.allowApplicationKeys,preventApiKeyManagement:authentication.apiKeys.preventApiKeyManagement}:void 0},sessionsTable,oauthAccountsTable:schemaTables.oauthAccounts,apiKeysTable:schemaTables.apiKeys,schemaTables,schemaRelations,tenantRegistry,databaseUrl:envResolved.databaseUrl,admin:{impersonate:{enabled:!0},changeUserId:{enabled:!0}},schemaName:targetSchemaName,emailService,appName:resolvedOptions.appId,captchaService:(()=>{let redisManager=getRedisManager();if(!authentication.captcha?.enabled||!redisManager)return null;return new CaptchaService({redis:{get:async(key)=>{let result=await redisManager.read(key);return result.success?result.data:null},set:async(key,value2,options)=>{await redisManager.create(key,value2,options?.ex)},del:async(key)=>{await redisManager.remove(key)}},logger:logger2,config:{enabled:!0,type:authentication.captcha.type||"math",difficulty:authentication.captcha.difficulty||"medium",expiresIn:authentication.captcha.expiresIn||"5m",maxAttempts:authentication.captcha.maxAttempts||3,caseSensitive:authentication.captcha.caseSensitive??!1}})})(),tokenResponseConfig:{accessToken:{setHeadersEnabled:authentication.accessToken?.setHeadersEnabled??!0,returnJson:authentication.accessToken?.returnJson??!0},refreshToken:{setHeadersEnabled:authentication.refreshToken?.setHeadersEnabled??!0,returnJson:authentication.refreshToken?.returnJson??!0},sessionToken:{setHeadersEnabled:authentication.sessionToken?.setHeadersEnabled??!0,returnJson:authentication.sessionToken?.returnJson??!0}},helpers:{signAccessToken:(userId,roles,claims)=>signJWT2({subject:userId,expiresInSeconds:parseTimeToSeconds2(authentication.accessToken?.expiresIn||"15m"),issuer:authentication.accessToken?.issuer,audience:authentication.accessToken?.audience,customClaims:{...roles&&roles.length>0?{roles}:{},...claims&&claims.length>0?{claims}:{}}},envResolved.accessTokenSecret||"",authentication.accessToken?.algorithm||"HS256"),signRefreshToken:(userId)=>signJWT2({subject:userId,expiresInSeconds:parseTimeToSeconds2(authentication.refreshToken?.expiresIn||"7d"),issuer:authentication.refreshToken?.issuer,audience:authentication.refreshToken?.audience},envResolved.refreshTokenSecret||"",authentication.refreshToken?.algorithm||"HS256"),verifyRefreshToken:(token)=>verifyJWT2(token,envResolved.refreshTokenSecret||""),createSession:async(params)=>{let result=await generateSession2({userId:params.userId,deviceInfo:params.deviceInfo});return result.success?result.session.id:""},destroySession:async(sessionId)=>deleteSession2({sessionId}),saveSessionToDb:async(sessionId,params,reqSchemaName)=>{let resolvedSessionsTable=resolveTableForTenant("userSessions",reqSchemaName)||resolveTableForTenant("user_sessions",reqSchemaName)||resolveTableForTenant("sessions",reqSchemaName)||sessionsTable;if(!resolvedSessionsTable||!db)return;let sessionsConfig=authentication.sessions,deviceInfo=params.deviceInfo||{},deviceFingerprint=deviceInfo.deviceHint?`${deviceInfo.browserName||""}-${deviceInfo.osName||""}-${deviceInfo.deviceType||""}-${deviceInfo.deviceHint}`:`${deviceInfo.browserName||""}-${deviceInfo.osName||""}-${deviceInfo.deviceType||""}`,existingSessions=await db.select().from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))),hasValidFingerprint=deviceFingerprint&&!deviceFingerprint.includes("--unknown")&&!deviceFingerprint.includes("Bot/Crawler")&&!deviceFingerprint.includes("Headless")&&deviceFingerprint!=="--"&&deviceFingerprint!=="--unknown",allUserSessions=await db.select().from(resolvedSessionsTable).where(eq23(resolvedSessionsTable.userId,params.userId)),isNewDevice=hasValidFingerprint?!existingSessions.some((s)=>s.deviceFingerprint===deviceFingerprint):!1,wasPreviouslyApproved=hasValidFingerprint?allUserSessions.some((s)=>{let sess=s;return sess.deviceFingerprint===deviceFingerprint&&sess.approvalStatus==="approved"}):!1,hasAnyApprovedSession=existingSessions.some((s)=>s.approvalStatus==="approved"),isImpersonationLogin=params.loginMethod==="impersonation"||params.loginMethod==="impersonation_stop",isOAuthLogin=params.loginMethod?.startsWith("oauth:"),requiresApproval=!isImpersonationLogin&&sessionsConfig?.trustNewDevices===!1&&isNewDevice&&!wasPreviouslyApproved&&hasValidFingerprint&&hasAnyApprovedSession;logger2.info("[AUTH] Device fingerprint analysis",{userId:params.userId,deviceFingerprint,hasValidFingerprint,isNewDevice,wasPreviouslyApproved,loginMethod:params.loginMethod,isImpersonationLogin,isOAuthLogin,existingSessionCount:existingSessions.length,hasAnyApprovedSession,requiresApproval});let approvalToken=null,approvalStatus="approved";if(requiresApproval){let existingPending=allUserSessions.find((s)=>{let sess=s;return sess.deviceFingerprint===deviceFingerprint&&(sess.approvalStatus==="pending"||sess.approval_status==="pending")&&sess.approvalToken});if(existingPending){let pendingSess=existingPending,pendingRequestedAt=pendingSess.approvalRequestedAt||pendingSess.approval_requested_at;if(pendingRequestedAt?Date.now()-new Date(pendingRequestedAt).getTime()<86400000:!0)return logger2.info("[AUTH] Reusing existing pending session for same device",{userId:params.userId,deviceFingerprint,existingSessionId:pendingSess.id}),{requiresApproval:!0,sessionId:pendingSess.id}}let{randomBytes:randomBytes4}=await import("crypto");approvalToken=randomBytes4(32).toString("hex"),approvalStatus="pending",logger2.info("[AUTH] New device requires approval",{userId:params.userId,deviceFingerprint,ipAddress:deviceInfo.ipAddress})}let staleBotSessions=existingSessions.filter((s)=>{let sess=s,fp=(sess.deviceFingerprint||"").toLowerCase(),ip=sess.ipAddress||"",ua=(sess.userAgent||"").toLowerCase(),isBotFingerprint=!fp||fp==="--"||fp==="--unknown"||fp.includes("bot/crawler")||fp.includes("headless")||fp.includes("unknown-unknown"),isServerAction=ua.includes("nucleusserveraction")||ua.includes("serveraction")||ua.includes("node-fetch")||ua.includes("undici");return isBotFingerprint&&(ip==="127.0.0.1"||ip==="::1"||ip==="localhost"||!ip)||isServerAction});if(staleBotSessions.length>0){for(let botSession of staleBotSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"bot_session_cleanup"}).where(eq23(resolvedSessionsTable.id,botSession.id));logger2.info("[AUTH] Cleaned up stale bot/crawler sessions",{userId:params.userId,cleanedCount:staleBotSessions.length})}if(!sessionsConfig?.allowMultipleDevices&&existingSessions.length>0){if(existingSessions.filter((s)=>s.deviceFingerprint===deviceFingerprint).length===0&&isNewDevice)for(let oldSession of existingSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"new_device_login"}).where(eq23(resolvedSessionsTable.id,oldSession.id))}if(sessionsConfig?.maxActiveSessions){let{count:count2}=await import("drizzle-orm"),currentCount=(await db.select({count:count2()}).from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))))[0]?.count||0;if(currentCount>=sessionsConfig.maxActiveSessions){let{asc:asc2}=await import("drizzle-orm"),oldestSessions=await db.select().from(resolvedSessionsTable).where(and7(eq23(resolvedSessionsTable.userId,params.userId),eq23(resolvedSessionsTable.isActive,!0))).orderBy(asc2(resolvedSessionsTable.createdAt)).limit(currentCount-sessionsConfig.maxActiveSessions+1);for(let oldSession of oldestSessions)await db.update(resolvedSessionsTable).set({isActive:!1,revokedAt:new Date,revokedReason:"max_sessions_exceeded"}).where(eq23(resolvedSessionsTable.id,oldSession.id))}}let trustScore=100;if(deviceInfo.isHeadless)trustScore-=50;if(deviceInfo.isBot)trustScore-=40;if(deviceInfo.isSuspicious)logger2.warn("[AUTH] Suspicious login detected",{userId:params.userId,suspiciousPatterns:deviceInfo.suspiciousPatterns,userAgent:deviceInfo.userAgent,ipAddress:deviceInfo.ipAddress});if(isNewDevice)trustScore-=25;if(!deviceInfo.ipAddress||deviceInfo.ipAddress==="unknown")trustScore-=20;if(!deviceInfo.browserName)trustScore-=15;if(!deviceInfo.osName)trustScore-=15;if(!deviceInfo.deviceType||deviceInfo.deviceType==="unknown")trustScore-=10;if(!deviceInfo.deviceName||deviceInfo.deviceName==="Unknown Device")trustScore-=5;let validFingerprint=deviceFingerprint&&!deviceFingerprint.includes("--unknown")&&deviceFingerprint!=="--",validIp=deviceInfo.ipAddress&&deviceInfo.ipAddress!=="unknown";if(validFingerprint){if(existingSessions.filter((s)=>s.deviceFingerprint===deviceFingerprint).length>0)trustScore+=20}if(validIp){if(existingSessions.filter((s)=>s.ipAddress===deviceInfo.ipAddress).length>0)trustScore+=15}trustScore=Math.max(0,Math.min(100,trustScore));let LOW_TRUST_THRESHOLD=50;if(await db.insert(resolvedSessionsTable).values({id:sessionId,userId:params.userId,tokenHash:sessionId,deviceFingerprint,deviceName:deviceInfo.deviceName,deviceType:deviceInfo.deviceType,browserName:deviceInfo.browserName,browserVersion:deviceInfo.browserVersion,osName:deviceInfo.osName,osVersion:deviceInfo.osVersion,ipAddress:deviceInfo.ipAddress,locationCountry:deviceInfo.locationCountry,locationCity:deviceInfo.locationCity,loginMethod:params.loginMethod||"password",rememberMe:params.rememberMe??!1,trustScore,lastActivityAt:new Date,createdAt:new Date,expiresAt:new Date(Date.now()+parseTimeToSeconds2(authentication.sessionToken?.expiresIn||"30d")*1000),isActive:approvalStatus==="approved",approvalStatus,approvalToken,approvalRequestedAt:requiresApproval?new Date:null}),!isImpersonationLogin&&emailService&&(sessionsConfig?.notifyOnNewDevice&&isNewDevice||trustScore<LOW_TRUST_THRESHOLD||requiresApproval)){let resolvedUsersTable=resolveTableForTenant("users",reqSchemaName);if(resolvedUsersTable){let user=(await db.select().from(resolvedUsersTable).where(eq23(resolvedUsersTable.id,params.userId)).limit(1))[0];if(user?.email){let isLowTrust=trustScore<LOW_TRUST_THRESHOLD,sessionsRoute=authentication.sessions?.route||"/auth/sessions",configuredUrl=authentication.sessions?.approvalRedirectUrl||"",isLegacyFrontendUrl=!configuredUrl||configuredUrl.endsWith("/devices"),approvalBase;if(!isLegacyFrontendUrl)approvalBase=configuredUrl;else{let origin=params.requestOrigin;if(!origin&&configuredUrl)try{origin=new URL(configuredUrl).origin}catch{}approvalBase=`${origin||"http://localhost:9000"}${sessionsRoute}`}let approveUrl=approvalToken?`${approvalBase}/approve-page?token=${approvalToken}`:"",rejectUrl=approvalToken?`${approvalBase}/reject-page?token=${approvalToken}`:"",subject,emailHtml,brandName=resolvedOptions.appId||"Nucleus",loginTime=new Date().toLocaleString("en-US",{dateStyle:"medium",timeStyle:"short"}),deviceSummary=`${deviceInfo.browserName||"Unknown"} ${deviceInfo.browserVersion||""} on ${deviceInfo.osName||"Unknown"} ${deviceInfo.osVersion||""}`,emailWrapper=(content)=>`
1620
1620
  <!DOCTYPE html>
1621
1621
  <html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"></head>
1622
1622
  <body style="margin:0;padding:0;background-color:#f4f4f5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
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",