nucleus-core-ts 0.8.81 → 0.8.82

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 +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1523,7 +1523,7 @@ data: ${t}
1523
1523
  </script>
1524
1524
  <script src="${o?o:`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${r}/dist/browser/standalone.min.js`}" crossorigin></script>
1525
1525
  </body>
1526
- </html>`;var kl=Symbol.for("TypeBox.Kind"),aA=(n)=>n.split("/").map((r)=>{if(r.startsWith(":")){if(r=r.slice(1,r.length),r.endsWith("?"))r=r.slice(0,-1);r=`{${r}}`}return r}).join("/"),Rl=(n,r,t)=>{if(r===void 0)return[];if(typeof r==="string")if(r in t)r=t[r];else throw Error(`Can't find model ${r}`);return Object.entries(r?.properties??[]).map(([o,a])=>{let{type:c=void 0,description:e,examples:i,...s}=a;return{description:e,examples:i,schema:{type:c,...s},in:n,name:o,required:r.required?.includes(o)??!1}})},Wc=(n,r)=>{if(typeof r==="object"&&["void","undefined","null"].includes(r.type))return;let t={};for(let o of n)t[o]={schema:typeof r==="string"?{$ref:`#/components/schemas/${r}`}:("$ref"in r)&&(kl in r)&&r[kl]==="Ref"?{...r,$ref:`#/components/schemas/${r.$ref}`}:oA({...r},{from:Al.Ref(""),to:({$ref:a,...c})=>{if(!a.startsWith("#/components/schemas/"))return Al.Ref(`#/components/schemas/${a}`,c);return Al.Ref(a,c)}})};return t},r_=(n)=>n.charAt(0).toUpperCase()+n.slice(1),cA=(n,r)=>{let t=n.toLowerCase();if(r==="/")return t+"Index";for(let o of r.split("/"))if(o.charCodeAt(0)===123)t+="By"+r_(o.slice(1,-1));else t+=r_(o);return t},Ya=(n)=>{if(!n)return;if(typeof n==="string")return n;if(Array.isArray(n))return[...n];return{...n}},t_=({schema:n,path:r,method:t,hook:o,models:a})=>{if(o=Ya(o),o.parse&&!Array.isArray(o.parse))o.parse=[o.parse];let c=o.parse?.map((w)=>{switch(typeof w){case"string":return w;case"object":if(w&&typeof w?.fn!=="string")return;switch(w?.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((w)=>w!==void 0);if(!c||c.length===0)c=["application/json","multipart/form-data","text/plain"];r=aA(r);let e=typeof c==="string"?[c]:c??["application/json"],i=Ya(o?.body),s=Ya(o?.params),f=Ya(o?.headers),l=Ya(o?.query),d=Ya(o?.response);if(typeof d==="object")if(kl in d){let{type:w,properties:h,required:_,additionalProperties:g,patternProperties:R,$ref:k,...M}=d;d={"200":{...M,description:M.description,content:Wc(e,w==="object"||w==="array"?{type:w,properties:h,patternProperties:R,items:d.items,required:_}:d)}}}else Object.entries(d).forEach(([w,h])=>{if(typeof h==="string"){if(!a[h])return;let{type:_,properties:g,required:R,additionalProperties:k,patternProperties:M,...C}=a[h];d[w]={...C,description:C.description,content:Wc(e,h)}}else{let{type:_,properties:g,required:R,additionalProperties:k,patternProperties:M,...C}=h;d[w]={...C,description:C.description,content:Wc(e,_==="object"||_==="array"?{type:_,properties:g,patternProperties:M,items:h.items,required:R}:h)}}});else if(typeof d==="string"){if(!(d in a))return;let{type:w,properties:h,required:_,$ref:g,additionalProperties:R,patternProperties:k,...M}=a[d];d={"200":{...M,content:Wc(e,d)}}}let b=[...Rl("header",f,a),...Rl("path",s,a),...Rl("query",l,a)];n[r]={...n[r]?n[r]:{},[t.toLowerCase()]:{...f||s||l||i?{parameters:b}:{},...d?{responses:d}:{},operationId:o?.detail?.operationId??cA(t,r),...o?.detail,...i?{requestBody:{required:!0,content:Wc(e,typeof i==="string"?{$ref:`#/components/schemas/${i}`}:i)}}:null}}},eA=(n,{excludeStaticFile:r=!0,exclude:t=[]})=>{let o={};for(let[a,c]of Object.entries(n))if(!t.some((e)=>{if(typeof e==="string")return a===e;return e.test(a)})&&!a.includes("*")&&(r?!a.includes("."):!0))Object.keys(c).forEach((e)=>{let i=c[e];if(a.includes("{")){if(!i.parameters)i.parameters=[];i.parameters=[...a.split("/").filter((s)=>s.startsWith("{")&&!i.parameters.find((f)=>f.in==="path"&&f.name===s.slice(1,s.length-1))).map((s)=>({schema:{type:"string"},in:"path",name:s.slice(1,s.length-1),required:!0})),...i.parameters]}if(!i.responses)i.responses={200:{}}}),o[a]=c;return o},a_=({provider:n="scalar",scalarVersion:r="latest",scalarCDN:t="",scalarConfig:o={},documentation:a={},version:c="5.9.0",excludeStaticFile:e=!0,path:i="/swagger",specPath:s=`${i}/json`,exclude:f=[],swaggerOptions:l={},theme:d=`https://unpkg.com/swagger-ui-dist@${c}/swagger-ui.css`,autoDarkMode:b=!0,excludeMethods:w=["OPTIONS"],excludeTags:h=[]}={})=>{let _={},g=0;if(!c)c=`https://unpkg.com/swagger-ui-dist@${c}/swagger-ui.css`;let R={title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...a.info},k=s.startsWith("/")?s.slice(1):s,M=new y$({name:"@elysiajs/swagger"}),C=new Response(n==="swagger-ui"?rA(R,c,d,JSON.stringify({url:k,dom_id:"#swagger-ui",...l},(N,m)=>typeof m==="function"?void 0:m),b):tA(R,r,{spec:{url:k,...o.spec},...o,_integration:"elysiajs"},t),{headers:{"content-type":"text/html; charset=utf8"}});return M.get(i,C,{detail:{hide:!0}}).get(s,function(){let m=M.getGlobalRoutes();if(m.length!==g){let A=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"];g=m.length,m.forEach((H)=>{if(H.hooks?.detail?.hide===!0)return;if(w.includes(H.method))return;if(A.includes(H.method)===!1&&H.method!=="ALL")return;if(H.method==="ALL")A.forEach((D)=>{t_({schema:_,hook:H.hooks,method:D,path:H.path,models:M.getGlobalDefinitions?.().type,contentType:H.hooks.type})});else t_({schema:_,hook:H.hooks,method:H.method,path:H.path,models:M.getGlobalDefinitions?.().type,contentType:H.hooks.type})})}return{openapi:"3.0.3",...{...a,tags:a.tags?.filter((A)=>!h?.includes(A?.name)),info:{title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...a.info}},paths:{...eA(_,{excludeStaticFile:e,exclude:Array.isArray(f)?f:[f]}),...a.paths},components:{...a.components,schemas:{...M.getGlobalDefinitions?.().type,...a.components?.schemas}}}},{detail:{hide:!0}}),M};function El(n){if(n?.enabled===!1)return null;let r={path:n?.path??"/swagger",provider:n?.provider??"scalar",excludeStaticFile:n?.excludeStaticFile??!0,exclude:n?.exclude??[],documentation:{info:{title:n?.documentation?.info?.title??"Nucleus API",description:n?.documentation?.info?.description??"Auto-generated API documentation",version:n?.documentation?.info?.version??"1.0.0",contact:n?.documentation?.info?.contact,license:n?.documentation?.info?.license},tags:n?.documentation?.tags??[],servers:n?.documentation?.servers},scalarConfig:n?.scalarConfig};return a_(r)}Ie();Cd();ni();var DE=(n)=>{let r=new Map;for(let t of n){let o=r.get(t.table_name),a=t.columns??o?.columns;r.set(t.table_name,{...o||{},...t,columns:a})}return Array.from(r.values())},HE=(n)=>({table_name:n.table_name,excluded_methods:n.excluded_methods?[...n.excluded_methods]:void 0,columns:n.columns?n.columns.map((r)=>({name:r.name,type:r.type})):void 0}),zE=(n)=>{let r=[];for(let[t,o]of Object.entries(n)){if(!o||typeof o!=="object")continue;let a=o,c=a._;if(c?.name){r.push({table_name:c.name});continue}let e=Object.getOwnPropertySymbols(a);for(let i of e){let s=a[i];if(s&&typeof s==="object"){let f=s;if(f.name&&typeof f.name==="string"){r.push({table_name:f.name});break}}}}return r};async function UE(n){let r=new ME;if(r.get("/health",()=>({status:"ok",timestamp:Date.now()})),n.staticAssets!==!1){let E=Mn("path"),S=Mn("fs"),z;if(typeof n.staticAssets==="string")z=n.staticAssets;else{let U=E.join(process.cwd(),"public"),P="";for(let p of["nucleus-core-ts","nucleus-core"])try{let K=Mn.resolve(`${p}/package.json`),B=E.join(E.dirname(K),"public");if(S.existsSync(B)){P=B;break}}catch{}if(P)z=P;else if(S.existsSync(U))z=U;else z=U}try{r.use(await ou({prefix:"/nucleus-core",assets:z}))}catch{}}let t=[],o,a=process.cwd();if(typeof n.options==="string"){let E=Mn("fs"),S=Mn("path"),z=S.isAbsolute(n.options)?n.options:S.resolve(process.cwd(),n.options);a=S.dirname(z);let U=E.readFileSync(z,"utf-8");o=JSON.parse(U)}else o=n.options;if(o.email?.gmail?.json_file_path){let E=Mn("path"),S=o.email.gmail.json_file_path;if(!E.isAbsolute(S))o.email.gmail.json_file_path=E.resolve(a,S)}let{authentication:c,audit:e,entities:i,database:s}=o,f=o.mode==="development",l=new Vr({service:o.appId||"nucleus",prettyPrint:f,colorize:f,auditEnabled:e?.enabled??!1}),d=dg(o);if(!d.valid){for(let E of d.errors)l.error(`[CONFIG] ${E.message}`,{field:E.field,envName:E.envName});throw Error("Nucleus configuration error: Missing required environment variables. Check logs for details.")}let{resolved:b}=d,w={access_token:c?.accessToken?.name||"access_token",refresh_token:c?.refreshToken?.name||"refresh_token",session_token:c?.sessionToken?.name||"session_token"},h=s?.schemas?.[0]||"main",_=SE(h);if(b.databaseUrl)await ug(b.databaseUrl,l);let g=b.databaseUrl?EE(b.databaseUrl):null,R={},k={};if(n.schema){let S=Mn("path").resolve(process.cwd(),n.schema),z=Mn(S);R=z.createAllTablesForSchema?z.createAllTablesForSchema(_):{}}if(n.relations){let S=Mn("path").resolve(process.cwd(),n.relations);k=Mn(S)}let M=El(n.swagger);if(M)r.use(M);let C=n.systemTables||[];t=Of(o,C,"",h),l.info(`[AUTH] Built ${t.length} public routes`);let N=null,m=null,A=null,H=null;if(o.email?.gmail?.enabled&&o.email.gmail.json_file_path)l.info("[GmailService] Initializing...",{jsonFilePath:o.email.gmail.json_file_path,fromEmail:o.email.gmail.from_email}),H=new Yf({enabled:!0,jsonFilePath:o.email.gmail.json_file_path,fromEmail:o.email.gmail.from_email||"",fromName:o.email.gmail.from_name},l),l.info("[GmailService] isAvailable:",{available:H.isAvailable()});if(o.liveMonitoring?.enabled){let E=o.liveMonitoring.basePath||"/monitoring",S=o.liveMonitoring.streamInterval||150;r.use($g({getService:()=>A,logger:l,basePath:E,streamInterval:S}))}r.onStart(async()=>{rl(o);let E=Te();if(E&&o.rateLimit?.enabled!==!1)N=new Cf({redis:E,logger:l,config:o.rateLimit||{}}),l.info(`[RateLimit] Enabled with strategy: ${o.rateLimit?.strategy||"sliding-window"}`);if(E&&o.monitoring?.enabled){if(m=new Lf({redis:E,logger:l,gmail:H||void 0,config:o.monitoring,appId:o.appId}),m.start(),l.info("[Monitoring] Service started"),o.monitoring.endpoints?.enabled){let z={enabled:!0,basePath:o.monitoring.endpoints.basePath||"/monitoring",stream:{enabled:o.monitoring.endpoints.stream?.enabled!==!1,path:o.monitoring.endpoints.stream?.path||"/stream",interval:o.monitoring.endpoints.stream?.interval||"5s"},snapshot:{enabled:o.monitoring.endpoints.snapshot?.enabled!==!1,path:o.monitoring.endpoints.snapshot?.path||"/snapshot"},history:{enabled:o.monitoring.endpoints.history?.enabled!==!1,path:o.monitoring.endpoints.history?.path||"/history",maxMinutes:o.monitoring.endpoints.history?.maxMinutes||60},alerts:{enabled:o.monitoring.endpoints.alerts?.enabled!==!1,path:o.monitoring.endpoints.alerts?.path||"/alerts"}};r.use(fl({monitoringService:m,logger:l,endpoints:z}))}}if(o.liveMonitoring?.enabled)A=new Ce(o.liveMonitoring),A.start(),l.info("[LiveMonitoring] Service started");let S=c?.mode==="consumer";if(g&&n.schema&&!S){let P=await import(Mn("path").resolve(process.cwd(),n.schema)),p=R.auditLogs||P.auditLogs;if(e?.enabled&&p)l.addAuditTransport(new de({db:g,table:p,enabled:!0}));try{l.info(`Syncing schema to database (target: ${h})...`);let{sql:B}=await import("drizzle-orm");await g.execute(B.raw(`CREATE SCHEMA IF NOT EXISTS "${h}"`));try{let L=Object.fromEntries(Object.entries(R).filter(([q,F])=>{if(F===void 0||F===null)return!1;if(typeof F==="object"&&F!==null)return Object.getOwnPropertySymbols(F).length>0||F._!==void 0;return!1})),Y=Object.keys(L);l.info("[Schema] Tables to sync:",{tables:Y,count:Y.length});let Q=L.users;if(Q){let q=Object.getOwnPropertyNames(Q).filter((F)=>!F.startsWith("_"));l.info("[Schema] Users table columns:",{columns:q})}await(await kE({schema:_,...L},g,[h])).apply(),l.info("[Schema] pushSchema completed successfully")}catch(L){let Y=L instanceof Error?L.message:String(L);l.warn(`[Schema] pushSchema warning: ${Y}`)}console.log("Schema sync completed")}catch(B){let L=B instanceof Error?B.message:String(B);console.error("Schema sync failed:",L)}if(console.log("Database connection established"),o.authorization?.enabled){let B={...eb,...o.authorization};if(B.autoSeedClaims){let L=zE(R),Y=qe.map((q)=>HE(q)),Q=o.entities||[],V=DE([...L,...Q,...Y,...n.systemTables||[]]);l.info("[Authorization] Seeding claims...",{schemaEntities:L.length,systemEntities:Y.length,configEntities:Q.length,totalEntities:V.length}),await Zu(g,R,k,V,B,l)}if(B.godminEmail&&B.godminPassword)l.info("[Authorization] Setting up godmin..."),await nb(g,R,B,l);l.info("[Authorization] Enabled")}let K=R.userSessions;if(K&&o.authentication?.sessions?.enabled){let{lt:B}=await import("drizzle-orm"),L=await g.update(K).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(ie(wr(K.isActive,!0),B(K.expiresAt,new Date)));l.info("[AUTH] Expired sessions cleanup completed",{expiredCount:L}),setInterval(async()=>{try{await g.update(K).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(ie(wr(K.isActive,!0),B(K.expiresAt,new Date)))}catch(Y){l.warn("[AUTH] Session cleanup failed",{error:Y})}},3600000)}}}).onRequest(async({request:E,set:S})=>{let z=Date.now();E.headers.set("x-request-start-time",String(z));let U=new URL(E.url),P=U.pathname,p=E.method,K=U.search,B=E.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||E.headers.get("x-real-ip")?.trim()||"unknown",L=E.headers.get("user-agent")||"unknown",Y,Q={};if(E.method!=="GET"&&E.method!=="HEAD")try{let v=await E.clone().text();Q=v?JSON.parse(v):{}}catch{Q={}}let V=e?.enabled?{id:RE(),user_id:"unknown",entity_name:P.split("/").filter(Boolean)[0]||"root",entity_id:null,operation_type:p,summary:"",old_values:{},new_values:Q,ip_address:B,user_agent:L,timestamp:new Date().toISOString(),path:P,query:K}:null,q=Nf(t,P,p);if(N){let F=q?"public":"private",v;if(P.includes("/auth/login"))v="login";else if(P.includes("/auth/register"))v="register";else if(P.includes("/auth/password-reset"))v="passwordReset";else if(P.includes("/auth/magic-link"))v="magicLink";let un=v?"auth":F,tn=await N.check({ip:B,endpoint:P,category:un,authType:v}),bn=N.getHeaders(tn);for(let[fn,Jn]of Object.entries(bn))S.headers[fn]=Jn;if(!tn.allowed){if(S.status=429,tn.retryAfter)S.headers["Retry-After"]=String(tn.retryAfter);if(l.warn(`[RateLimit] Blocked request from ${B} to ${P}`),m)m.recordRateLimitBlock();return new Response(JSON.stringify({error:"Too Many Requests",retryAfter:tn.retryAfter}),{status:429,headers:{"Content-Type":"application/json"}})}}if(P==="/health")return;if(c?.enabled&&!q){if(!c.accessToken?.secret)return S.status=500,l.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:P,method:p},audit:xt(V,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(c.mode==="consumer"){Y=nl(E.headers,w);let v=so(Y.access_token||"",b.accessTokenSecret||"");if(!v.valid)return S.status=401,l.traceSync({message:"Invalid or missing access token",level:"warn",context:{path:P,method:p},audit:xt(V,"Invalid or missing access token")}),Error("Unauthenticated");let un=v.payload.sub,tn=v.payload.roles,bn=v.payload.claims;if(E.headers.set("x-access-token",Y.access_token||""),E.headers.set("x-user-id",un||""),tn&&tn.length>0)E.headers.set("x-user-roles",tn.join(","));if(bn&&bn.length>0)E.headers.set("x-user-claims",bn.join(","))}else{if(!c.refreshToken?.secret||!c.sessionToken?.secret)return S.status=500,l.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:P,method:p},audit:xt(V,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(Y=nl(E.headers,w),!Y.session_token)return S.status=401,l.traceSync({message:"No session token",level:"warn",context:{path:P,method:p},audit:xt(V,"No session token")}),Error("Unauthenticated");let v=await Qo({sessionId:Y.session_token});if(!v)return S.status=401,l.traceSync({message:"Invalid session",level:"warn",context:{path:P,method:p,sessionId:Y.session_token},audit:xt(V,"Invalid session")}),Error("Unauthenticated");let un=R.userSessions;if(un&&g){let T=(await g.select().from(un).where(wr(un.id,Y.session_token)).limit(1))[0];if(!T||T.isActive===!1||T.revokedAt)return S.status=401,l.traceSync({message:"Session revoked or inactive",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,isActive:T?.isActive,revokedAt:T?.revokedAt},audit:xt(V,"Session revoked")}),Error("Session has been revoked");if(T.expiresAt&&new Date(T.expiresAt)<new Date)return S.status=401,l.traceSync({message:"Session expired",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,expiresAt:T.expiresAt},audit:xt(V,"Session expired")}),Error("Session has expired")}if(v.lastActiveAt&&c.sessions?.inactivityTimeout){let gn=new Date(v.lastActiveAt).getTime(),T=st(c.sessions.inactivityTimeout)*1000;if(Date.now()-gn>T)return S.status=401,l.traceSync({message:"Session inactive timeout",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,lastActiveAt:v.lastActiveAt},audit:xt(V,"Session inactive timeout")}),Error("Session expired due to inactivity")}$f(Y.session_token).catch(()=>{});let tn=R.userSessions;if(tn&&g)g.update(tn).set({lastActivityAt:new Date}).where(wr(tn.id,Y.session_token)).catch(()=>{});let bn=so(Y.access_token||"",b.accessTokenSecret||""),fn=Y.access_token?bn.valid:!1,Jn=Y.refresh_token?so(Y.refresh_token,b.refreshTokenSecret||"").valid:!1;if(!fn&&Jn&&Y.refresh_token&&v.rememberMe===!0){let gn=await lg(v.userId,v.id,()=>sg({refreshTokenId:Y.refresh_token,options:o,sessionData:v}));if(gn.success&&gn.accessToken){Y.access_token=gn.accessToken;let T=`${w.access_token}=${gn.accessToken}; Path=/; HttpOnly; SameSite=Strict;Secure; Max-Age=${st(c.accessToken.expiresIn??"15m")}`;S.headers["Set-Cookie"]=T}}let wn=bn.valid?bn.payload.sub:v.userId,cr=bn.valid?bn.payload.roles:void 0,ct=bn.valid?bn.payload.claims:void 0;if(E.headers.set("x-access-token",Y.access_token||""),E.headers.set("x-refresh-token",Y.refresh_token||""),E.headers.set("x-session-id",Y.session_token||""),E.headers.set("x-user-id",wn||""),cr&&cr.length>0)E.headers.set("x-user-roles",cr.join(","));if(ct&&ct.length>0)E.headers.set("x-user-claims",ct.join(","))}}}).onAfterHandle(({request:E,set:S})=>{if(m){let z=E.headers.get("x-request-start-time"),U=z?parseInt(z,10):Date.now(),P=Date.now()-U,p=new URL(E.url),K=typeof S.status==="number"?S.status:200;m.recordRequest({endpoint:p.pathname,method:E.method,status:K,responseTimeMs:P,isError:K>=400,errorType:K>=500?"server_error":K>=400?"client_error":void 0})}if(A){let z=new URL(E.url),U={};E.headers.forEach((P,p)=>{U[p]=P}),A.recordRequest({path:z.pathname,method:E.method,timestamp:Date.now(),headers:U})}}).onError((E)=>{let{set:S,code:z,error:U}=E,P=typeof z==="number"?z:500,p=U instanceof Error?U.message:"Internal Server Error";return S.status=P,Response.json({isSuccess:!1,message:p,status:P,errors:[U],data:null})}),l.info("Creating routes for entities"),sl(r,{db:g,schemaTables:R,schemaRelations:k,entities:i,logger:l,databaseUrl:b.databaseUrl,storage:o.storage,authorization:o.authorization,emailServiceAvailable:!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path)});let D=c?.mode==="consumer";if(c?.enabled&&!D&&g){let E=R.users,S=R.userSessions||R.user_sessions||R.sessions;if(!S&&c.sessions?.enabled)l.warn("[AUTH] sessions is enabled but user_sessions table not found in schema. Disabling sessions.");if(E){rl(o);let{createAuthRoutes:z}=(Cd(),se(S2)),{signJWT:U,verifyJWT:P}=(Vo(),se(mu)),{generateSession:p,deleteSession:K}=(Af(),se(Tu));z(r,{authConfig:{db:g,logger:l,usersTable:E,sessionsTable:S,userRolesTable:R.userRoles,rolesTable:R.roles,roleClaimsTable:R.roleClaims,claimsTable:R.claims,authentication:{enabled:c.enabled,accessToken:c.accessToken,refreshToken:c.refreshToken,sessionToken:c.sessionToken}},features:{login:c.login,register:c.register,logout:c.logout,refresh:c.refresh,passwordReset:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.passwordReset?.enabled&&!B)return l.warn("[AUTH] passwordReset is enabled but no email provider is configured. Disabling passwordReset."),{...c.passwordReset,enabled:!1};return c.passwordReset})(),passwordChange:c.passwordChange,passwordSet:c.passwordSet,sessions:c.sessions,magicLink:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.magicLink?.enabled&&!B)return l.warn("[AUTH] magicLink is enabled but no email provider is configured. Disabling magicLink."),{...c.magicLink,enabled:!1};return c.magicLink})(),me:c.me||{enabled:!0,route:"/auth/me"},invite:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.invite?.enabled&&!B)return l.warn("[AUTH] invite is enabled but no email provider is configured. Disabling invite."),{...c.invite,enabled:!1};return c.invite})(),captcha:c.captcha,oauth:c.oauth?.enabled&&b.oauthProviders?{...c.oauth,providers:b.oauthProviders}:void 0},sessionsTable:S,oauthAccountsTable:R.oauthAccounts,schemaTables:R,schemaRelations:k,databaseUrl:s?.url,emailService:H,appName:o.appId,captchaService:(()=>{let B=Te();if(!c.captcha?.enabled||!B)return null;return new Bf({redis:{get:async(Y)=>{let Q=await B.read(Y);return Q.success?Q.data:null},set:async(Y,Q,V)=>{await B.create(Y,Q,V?.ex)},del:async(Y)=>{await B.remove(Y)}},logger:l,config:{enabled:!0,type:c.captcha.type||"math",difficulty:c.captcha.difficulty||"medium",expiresIn:c.captcha.expiresIn||"5m",maxAttempts:c.captcha.maxAttempts||3,caseSensitive:c.captcha.caseSensitive??!1}})})(),tokenResponseConfig:{accessToken:{setHeadersEnabled:c.accessToken?.setHeadersEnabled??!0,returnJson:c.accessToken?.returnJson??!0},refreshToken:{setHeadersEnabled:c.refreshToken?.setHeadersEnabled??!0,returnJson:c.refreshToken?.returnJson??!0},sessionToken:{setHeadersEnabled:c.sessionToken?.setHeadersEnabled??!0,returnJson:c.sessionToken?.returnJson??!0}},helpers:{signAccessToken:(B,L,Y)=>U({subject:B,expiresInSeconds:st(c.accessToken?.expiresIn||"15m"),issuer:c.accessToken?.issuer,audience:c.accessToken?.audience,customClaims:{...L&&L.length>0?{roles:L}:{},...Y&&Y.length>0?{claims:Y}:{}}},b.accessTokenSecret||"",c.accessToken?.algorithm||"HS256"),signRefreshToken:(B)=>U({subject:B,expiresInSeconds:st(c.refreshToken?.expiresIn||"7d"),issuer:c.refreshToken?.issuer,audience:c.refreshToken?.audience},b.refreshTokenSecret||"",c.refreshToken?.algorithm||"HS256"),verifyRefreshToken:(B)=>P(B,b.refreshTokenSecret||""),createSession:async(B)=>{let L=await p({userId:B.userId,deviceInfo:B.deviceInfo});return L.success?L.session.id:""},destroySession:async(B)=>K({sessionId:B}),saveSessionToDb:async(B,L)=>{if(!S||!g)return;let Y=c.sessions,Q=L.deviceInfo||{},V=`${Q.browserName||""}-${Q.osName||""}-${Q.deviceType||""}`,q=await g.select().from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))),F=V&&!V.includes("--unknown")&&!V.includes("Bot/Crawler")&&!V.includes("Headless")&&V!=="--"&&V!=="--unknown",v=F?!q.some((an)=>an.deviceFingerprint===V):!1,un=q.some((an)=>an.approvalStatus==="approved"),tn=Y?.trustNewDevices===!1&&v&&F&&un;l.info("[AUTH] Device fingerprint analysis",{userId:L.userId,deviceFingerprint:V,hasValidFingerprint:F,isNewDevice:v,existingSessionCount:q.length,hasAnyApprovedSession:un,requiresApproval:tn});let bn=null,fn="approved";if(tn){let{randomBytes:an}=await import("crypto");bn=an(32).toString("hex"),fn="pending",l.info("[AUTH] New device requires approval",{userId:L.userId,deviceFingerprint:V,ipAddress:Q.ipAddress})}let Jn=q.filter((an)=>{let er=an,fr=er.deviceFingerprint||"",Mr=er.ipAddress||"";return(!fr||fr==="--"||fr==="--unknown"||fr.includes("Bot/Crawler")||fr.includes("Headless")||fr.includes("unknown-unknown"))&&(Mr==="127.0.0.1"||Mr==="::1"||Mr==="localhost"||!Mr)});if(Jn.length>0){for(let an of Jn)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"bot_session_cleanup"}).where(wr(S.id,an.id));l.info("[AUTH] Cleaned up stale bot/crawler sessions",{userId:L.userId,cleanedCount:Jn.length})}if(F&&!v){let an=q.filter((er)=>er.deviceFingerprint===V);for(let er of an)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"relogin_same_device"}).where(wr(S.id,er.id));l.info("[AUTH] Revoked old sessions from same device",{userId:L.userId,deviceFingerprint:V,revokedCount:an.length})}if(!Y?.allowMultipleDevices&&q.length>0){if(q.filter((er)=>er.deviceFingerprint===V).length===0&&v)for(let er of q)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"new_device_login"}).where(wr(S.id,er.id))}if(Y?.maxActiveSessions){let{count:an}=await import("drizzle-orm"),fr=(await g.select({count:an()}).from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))))[0]?.count||0;if(fr>=Y.maxActiveSessions){let{asc:Mr}=await import("drizzle-orm"),et=await g.select().from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))).orderBy(Mr(S.createdAt)).limit(fr-Y.maxActiveSessions+1);for(let Ct of et)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"max_sessions_exceeded"}).where(wr(S.id,Ct.id))}}let wn=100;if(Q.isHeadless)wn-=50;if(Q.isBot)wn-=40;if(Q.isSuspicious)l.warn("[AUTH] Suspicious login detected",{userId:L.userId,suspiciousPatterns:Q.suspiciousPatterns,userAgent:Q.userAgent,ipAddress:Q.ipAddress});if(v)wn-=25;if(!Q.ipAddress||Q.ipAddress==="unknown")wn-=20;if(!Q.browserName)wn-=15;if(!Q.osName)wn-=15;if(!Q.deviceType||Q.deviceType==="unknown")wn-=10;if(!Q.deviceName||Q.deviceName==="Unknown Device")wn-=5;let cr=V&&!V.includes("--unknown")&&V!=="--",ct=Q.ipAddress&&Q.ipAddress!=="unknown";if(cr){if(q.filter((er)=>er.deviceFingerprint===V).length>0)wn+=20}if(ct){if(q.filter((er)=>er.ipAddress===Q.ipAddress).length>0)wn+=15}wn=Math.max(0,Math.min(100,wn));let gn=50;if(await g.insert(S).values({id:B,userId:L.userId,tokenHash:B,deviceFingerprint:V,deviceName:Q.deviceName,deviceType:Q.deviceType,browserName:Q.browserName,browserVersion:Q.browserVersion,osName:Q.osName,osVersion:Q.osVersion,ipAddress:Q.ipAddress,locationCountry:Q.locationCountry,locationCity:Q.locationCity,loginMethod:L.loginMethod||"password",rememberMe:L.rememberMe??!1,trustScore:wn,lastActivityAt:new Date,createdAt:new Date,expiresAt:new Date(Date.now()+st(c.sessionToken?.expiresIn||"30d")*1000),isActive:fn==="approved",approvalStatus:fn,approvalToken:bn,approvalRequestedAt:tn?new Date:null}),H&&(Y?.notifyOnNewDevice&&v||wn<gn||tn)){let an=R.users;if(an){let fr=(await g.select().from(an).where(wr(an.id,L.userId)).limit(1))[0];if(fr?.email){let Mr=wn<gn,et=c.sessions?.approvalRedirectUrl||"http://localhost:3000/devices",Ct=bn?`${et}?action=approve&token=${bn}`:"",hr=bn?`${et}?action=reject&token=${bn}`:"",Zn,Rs;if(tn)Zn="\uD83D\uDD10 New Device Login Requires Approval",Rs=`
1526
+ </html>`;var kl=Symbol.for("TypeBox.Kind"),aA=(n)=>n.split("/").map((r)=>{if(r.startsWith(":")){if(r=r.slice(1,r.length),r.endsWith("?"))r=r.slice(0,-1);r=`{${r}}`}return r}).join("/"),Rl=(n,r,t)=>{if(r===void 0)return[];if(typeof r==="string")if(r in t)r=t[r];else throw Error(`Can't find model ${r}`);return Object.entries(r?.properties??[]).map(([o,a])=>{let{type:c=void 0,description:e,examples:i,...s}=a;return{description:e,examples:i,schema:{type:c,...s},in:n,name:o,required:r.required?.includes(o)??!1}})},Wc=(n,r)=>{if(typeof r==="object"&&["void","undefined","null"].includes(r.type))return;let t={};for(let o of n)t[o]={schema:typeof r==="string"?{$ref:`#/components/schemas/${r}`}:("$ref"in r)&&(kl in r)&&r[kl]==="Ref"?{...r,$ref:`#/components/schemas/${r.$ref}`}:oA({...r},{from:Al.Ref(""),to:({$ref:a,...c})=>{if(!a.startsWith("#/components/schemas/"))return Al.Ref(`#/components/schemas/${a}`,c);return Al.Ref(a,c)}})};return t},r_=(n)=>n.charAt(0).toUpperCase()+n.slice(1),cA=(n,r)=>{let t=n.toLowerCase();if(r==="/")return t+"Index";for(let o of r.split("/"))if(o.charCodeAt(0)===123)t+="By"+r_(o.slice(1,-1));else t+=r_(o);return t},Ya=(n)=>{if(!n)return;if(typeof n==="string")return n;if(Array.isArray(n))return[...n];return{...n}},t_=({schema:n,path:r,method:t,hook:o,models:a})=>{if(o=Ya(o),o.parse&&!Array.isArray(o.parse))o.parse=[o.parse];let c=o.parse?.map((w)=>{switch(typeof w){case"string":return w;case"object":if(w&&typeof w?.fn!=="string")return;switch(w?.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((w)=>w!==void 0);if(!c||c.length===0)c=["application/json","multipart/form-data","text/plain"];r=aA(r);let e=typeof c==="string"?[c]:c??["application/json"],i=Ya(o?.body),s=Ya(o?.params),f=Ya(o?.headers),l=Ya(o?.query),d=Ya(o?.response);if(typeof d==="object")if(kl in d){let{type:w,properties:h,required:_,additionalProperties:g,patternProperties:R,$ref:k,...M}=d;d={"200":{...M,description:M.description,content:Wc(e,w==="object"||w==="array"?{type:w,properties:h,patternProperties:R,items:d.items,required:_}:d)}}}else Object.entries(d).forEach(([w,h])=>{if(typeof h==="string"){if(!a[h])return;let{type:_,properties:g,required:R,additionalProperties:k,patternProperties:M,...C}=a[h];d[w]={...C,description:C.description,content:Wc(e,h)}}else{let{type:_,properties:g,required:R,additionalProperties:k,patternProperties:M,...C}=h;d[w]={...C,description:C.description,content:Wc(e,_==="object"||_==="array"?{type:_,properties:g,patternProperties:M,items:h.items,required:R}:h)}}});else if(typeof d==="string"){if(!(d in a))return;let{type:w,properties:h,required:_,$ref:g,additionalProperties:R,patternProperties:k,...M}=a[d];d={"200":{...M,content:Wc(e,d)}}}let b=[...Rl("header",f,a),...Rl("path",s,a),...Rl("query",l,a)];n[r]={...n[r]?n[r]:{},[t.toLowerCase()]:{...f||s||l||i?{parameters:b}:{},...d?{responses:d}:{},operationId:o?.detail?.operationId??cA(t,r),...o?.detail,...i?{requestBody:{required:!0,content:Wc(e,typeof i==="string"?{$ref:`#/components/schemas/${i}`}:i)}}:null}}},eA=(n,{excludeStaticFile:r=!0,exclude:t=[]})=>{let o={};for(let[a,c]of Object.entries(n))if(!t.some((e)=>{if(typeof e==="string")return a===e;return e.test(a)})&&!a.includes("*")&&(r?!a.includes("."):!0))Object.keys(c).forEach((e)=>{let i=c[e];if(a.includes("{")){if(!i.parameters)i.parameters=[];i.parameters=[...a.split("/").filter((s)=>s.startsWith("{")&&!i.parameters.find((f)=>f.in==="path"&&f.name===s.slice(1,s.length-1))).map((s)=>({schema:{type:"string"},in:"path",name:s.slice(1,s.length-1),required:!0})),...i.parameters]}if(!i.responses)i.responses={200:{}}}),o[a]=c;return o},a_=({provider:n="scalar",scalarVersion:r="latest",scalarCDN:t="",scalarConfig:o={},documentation:a={},version:c="5.9.0",excludeStaticFile:e=!0,path:i="/swagger",specPath:s=`${i}/json`,exclude:f=[],swaggerOptions:l={},theme:d=`https://unpkg.com/swagger-ui-dist@${c}/swagger-ui.css`,autoDarkMode:b=!0,excludeMethods:w=["OPTIONS"],excludeTags:h=[]}={})=>{let _={},g=0;if(!c)c=`https://unpkg.com/swagger-ui-dist@${c}/swagger-ui.css`;let R={title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...a.info},k=s.startsWith("/")?s.slice(1):s,M=new y$({name:"@elysiajs/swagger"}),C=new Response(n==="swagger-ui"?rA(R,c,d,JSON.stringify({url:k,dom_id:"#swagger-ui",...l},(N,m)=>typeof m==="function"?void 0:m),b):tA(R,r,{spec:{url:k,...o.spec},...o,_integration:"elysiajs"},t),{headers:{"content-type":"text/html; charset=utf8"}});return M.get(i,C,{detail:{hide:!0}}).get(s,function(){let m=M.getGlobalRoutes();if(m.length!==g){let A=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"];g=m.length,m.forEach((H)=>{if(H.hooks?.detail?.hide===!0)return;if(w.includes(H.method))return;if(A.includes(H.method)===!1&&H.method!=="ALL")return;if(H.method==="ALL")A.forEach((D)=>{t_({schema:_,hook:H.hooks,method:D,path:H.path,models:M.getGlobalDefinitions?.().type,contentType:H.hooks.type})});else t_({schema:_,hook:H.hooks,method:H.method,path:H.path,models:M.getGlobalDefinitions?.().type,contentType:H.hooks.type})})}return{openapi:"3.0.3",...{...a,tags:a.tags?.filter((A)=>!h?.includes(A?.name)),info:{title:"Elysia Documentation",description:"Development documentation",version:"0.0.0",...a.info}},paths:{...eA(_,{excludeStaticFile:e,exclude:Array.isArray(f)?f:[f]}),...a.paths},components:{...a.components,schemas:{...M.getGlobalDefinitions?.().type,...a.components?.schemas}}}},{detail:{hide:!0}}),M};function El(n){if(n?.enabled===!1)return null;let r={path:n?.path??"/swagger",provider:n?.provider??"scalar",excludeStaticFile:n?.excludeStaticFile??!0,exclude:n?.exclude??[],documentation:{info:{title:n?.documentation?.info?.title??"Nucleus API",description:n?.documentation?.info?.description??"Auto-generated API documentation",version:n?.documentation?.info?.version??"1.0.0",contact:n?.documentation?.info?.contact,license:n?.documentation?.info?.license},tags:n?.documentation?.tags??[],servers:n?.documentation?.servers},scalarConfig:n?.scalarConfig};return a_(r)}Ie();Cd();ni();var DE=(n)=>{let r=new Map;for(let t of n){let o=r.get(t.table_name),a=t.columns??o?.columns;r.set(t.table_name,{...o||{},...t,columns:a})}return Array.from(r.values())},HE=(n)=>({table_name:n.table_name,excluded_methods:n.excluded_methods?[...n.excluded_methods]:void 0,columns:n.columns?n.columns.map((r)=>({name:r.name,type:r.type})):void 0}),zE=(n)=>{let r=[];for(let[t,o]of Object.entries(n)){if(!o||typeof o!=="object")continue;let a=o,c=a._;if(c?.name){r.push({table_name:c.name});continue}let e=Object.getOwnPropertySymbols(a);for(let i of e){let s=a[i];if(s&&typeof s==="object"){let f=s;if(f.name&&typeof f.name==="string"){r.push({table_name:f.name});break}}}}return r};async function UE(n){let r=new ME;if(r.get("/health",()=>({status:"ok",timestamp:Date.now()})),n.staticAssets!==!1){let E=Mn("path"),S=Mn("fs"),z;if(typeof n.staticAssets==="string")z=n.staticAssets;else{let U=E.join(process.cwd(),"public"),P="";for(let p of["nucleus-core-ts","nucleus-core"])try{let K=Mn.resolve(`${p}/package.json`),B=E.join(E.dirname(K),"public");if(S.existsSync(B)){P=B;break}}catch{}if(P)z=P;else if(S.existsSync(U))z=U;else z=U}try{r.use(await ou({prefix:"/nucleus-core",assets:z}))}catch{}}let t=[],o,a=process.cwd();if(typeof n.options==="string"){let E=Mn("fs"),S=Mn("path"),z=S.isAbsolute(n.options)?n.options:S.resolve(process.cwd(),n.options);a=S.dirname(z);let U=E.readFileSync(z,"utf-8");o=JSON.parse(U)}else o=n.options;if(o.email?.gmail?.json_file_path){let E=Mn("path"),S=o.email.gmail.json_file_path;if(!E.isAbsolute(S))o.email.gmail.json_file_path=E.resolve(a,S)}let{authentication:c,audit:e,entities:i,database:s}=o,f=o.mode==="development",l=new Vr({service:o.appId||"nucleus",prettyPrint:f,colorize:f,auditEnabled:e?.enabled??!1}),d=dg(o);if(!d.valid){for(let E of d.errors)l.error(`[CONFIG] ${E.message}`,{field:E.field,envName:E.envName});throw Error("Nucleus configuration error: Missing required environment variables. Check logs for details.")}let{resolved:b}=d,w={access_token:c?.accessToken?.name||"access_token",refresh_token:c?.refreshToken?.name||"refresh_token",session_token:c?.sessionToken?.name||"session_token"},h=s?.schemas?.[0]||"main",_=SE(h);if(b.databaseUrl)await ug(b.databaseUrl,l);let g=b.databaseUrl?EE(b.databaseUrl):null,R={},k={};if(n.schema){let S=Mn("path").resolve(process.cwd(),n.schema),z=Mn(S);R=z.createAllTablesForSchema?z.createAllTablesForSchema(_):{}}if(n.relations){let S=Mn("path").resolve(process.cwd(),n.relations);k=Mn(S)}let M=El(n.swagger);if(M)r.use(M);let C=n.systemTables||[];t=Of(o,C,"",h),l.info(`[AUTH] Built ${t.length} public routes`);let N=null,m=null,A=null,H=null;if(o.email?.gmail?.enabled&&o.email.gmail.json_file_path)l.info("[GmailService] Initializing...",{jsonFilePath:o.email.gmail.json_file_path,fromEmail:o.email.gmail.from_email}),H=new Yf({enabled:!0,jsonFilePath:o.email.gmail.json_file_path,fromEmail:o.email.gmail.from_email||"",fromName:o.email.gmail.from_name},l),l.info("[GmailService] isAvailable:",{available:H.isAvailable()});if(o.liveMonitoring?.enabled){let E=o.liveMonitoring.basePath||"/monitoring",S=o.liveMonitoring.streamInterval||150;r.use($g({getService:()=>A,logger:l,basePath:E,streamInterval:S}))}r.onStart(async()=>{rl(o);let E=Te();if(E&&o.rateLimit?.enabled!==!1)N=new Cf({redis:E,logger:l,config:o.rateLimit||{}}),l.info(`[RateLimit] Enabled with strategy: ${o.rateLimit?.strategy||"sliding-window"}`);if(E&&o.monitoring?.enabled){if(m=new Lf({redis:E,logger:l,gmail:H||void 0,config:o.monitoring,appId:o.appId}),m.start(),l.info("[Monitoring] Service started"),o.monitoring.endpoints?.enabled){let z={enabled:!0,basePath:o.monitoring.endpoints.basePath||"/monitoring",stream:{enabled:o.monitoring.endpoints.stream?.enabled!==!1,path:o.monitoring.endpoints.stream?.path||"/stream",interval:o.monitoring.endpoints.stream?.interval||"5s"},snapshot:{enabled:o.monitoring.endpoints.snapshot?.enabled!==!1,path:o.monitoring.endpoints.snapshot?.path||"/snapshot"},history:{enabled:o.monitoring.endpoints.history?.enabled!==!1,path:o.monitoring.endpoints.history?.path||"/history",maxMinutes:o.monitoring.endpoints.history?.maxMinutes||60},alerts:{enabled:o.monitoring.endpoints.alerts?.enabled!==!1,path:o.monitoring.endpoints.alerts?.path||"/alerts"}};r.use(fl({monitoringService:m,logger:l,endpoints:z}))}}if(o.liveMonitoring?.enabled)A=new Ce(o.liveMonitoring),A.start(),l.info("[LiveMonitoring] Service started");let S=c?.mode==="consumer";if(g&&n.schema&&!S){let P=await import(Mn("path").resolve(process.cwd(),n.schema)),p=R.auditLogs||P.auditLogs;if(e?.enabled&&p)l.addAuditTransport(new de({db:g,table:p,enabled:!0}));try{l.info(`Syncing schema to database (target: ${h})...`);let{sql:B}=await import("drizzle-orm");await g.execute(B.raw(`CREATE SCHEMA IF NOT EXISTS "${h}"`));try{let L=Object.fromEntries(Object.entries(R).filter(([q,F])=>{if(F===void 0||F===null)return!1;if(typeof F==="object"&&F!==null)return Object.getOwnPropertySymbols(F).length>0||F._!==void 0;return!1})),Y=Object.keys(L);l.info("[Schema] Tables to sync:",{tables:Y,count:Y.length});let Q=L.users;if(Q){let q=Object.getOwnPropertyNames(Q).filter((F)=>!F.startsWith("_"));l.info("[Schema] Users table columns:",{columns:q})}await(await kE({schema:_,...L},g,[h])).apply(),l.info("[Schema] pushSchema completed successfully")}catch(L){let Y=L instanceof Error?L.message:String(L);l.warn(`[Schema] pushSchema warning: ${Y}`)}console.log("Schema sync completed")}catch(B){let L=B instanceof Error?B.message:String(B);console.error("Schema sync failed:",L)}if(console.log("Database connection established"),o.authorization?.enabled){let B={...eb,...o.authorization};if(B.autoSeedClaims){let L=zE(R),Y=qe.map((q)=>HE(q)),Q=o.entities||[],V=DE([...L,...Q,...Y,...n.systemTables||[]]);l.info("[Authorization] Seeding claims...",{schemaEntities:L.length,systemEntities:Y.length,configEntities:Q.length,totalEntities:V.length}),await Zu(g,R,k,V,B,l)}if(B.godminEmail&&B.godminPassword)l.info("[Authorization] Setting up godmin..."),await nb(g,R,B,l);l.info("[Authorization] Enabled")}let K=R.userSessions;if(K&&o.authentication?.sessions?.enabled){let{lt:B}=await import("drizzle-orm"),L=await g.update(K).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(ie(wr(K.isActive,!0),B(K.expiresAt,new Date)));l.info("[AUTH] Expired sessions cleanup completed",{expiredCount:L}),setInterval(async()=>{try{await g.update(K).set({isActive:!1,revokedAt:new Date,revokedReason:"expired"}).where(ie(wr(K.isActive,!0),B(K.expiresAt,new Date)))}catch(Y){l.warn("[AUTH] Session cleanup failed",{error:Y})}},3600000)}}}).onRequest(async({request:E,set:S})=>{let z=Date.now();E.headers.set("x-request-start-time",String(z));let U=new URL(E.url),P=U.pathname,p=E.method,K=U.search,B=E.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||E.headers.get("x-real-ip")?.trim()||"unknown",L=E.headers.get("user-agent")||"unknown",Y,Q={};if(E.method!=="GET"&&E.method!=="HEAD")try{let v=await E.clone().text();Q=v?JSON.parse(v):{}}catch{Q={}}let V=e?.enabled?{id:RE(),user_id:"unknown",entity_name:P.split("/").filter(Boolean)[0]||"root",entity_id:null,operation_type:p,summary:"",old_values:{},new_values:Q,ip_address:B,user_agent:L,timestamp:new Date().toISOString(),path:P,query:K}:null,q=Nf(t,P,p);if(N){let F=q?"public":"private",v;if(P.includes("/auth/login"))v="login";else if(P.includes("/auth/register"))v="register";else if(P.includes("/auth/password-reset"))v="passwordReset";else if(P.includes("/auth/magic-link"))v="magicLink";let un=v?"auth":F,tn=await N.check({ip:B,endpoint:P,category:un,authType:v}),bn=N.getHeaders(tn);for(let[fn,Jn]of Object.entries(bn))S.headers[fn]=Jn;if(!tn.allowed){if(S.status=429,tn.retryAfter)S.headers["Retry-After"]=String(tn.retryAfter);if(l.warn(`[RateLimit] Blocked request from ${B} to ${P}`),m)m.recordRateLimitBlock();return new Response(JSON.stringify({error:"Too Many Requests",retryAfter:tn.retryAfter}),{status:429,headers:{"Content-Type":"application/json"}})}}if(P==="/health")return;if(c?.enabled&&!q){if(!c.accessToken?.secret)return S.status=500,l.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:P,method:p},audit:xt(V,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(c.mode==="consumer"){Y=nl(E.headers,w);let v=so(Y.access_token||"",b.accessTokenSecret||"");if(!v.valid)return S.status=401,l.traceSync({message:"Invalid or missing access token",level:"warn",context:{path:P,method:p},audit:xt(V,"Invalid or missing access token")}),Error("Unauthenticated");let un=v.payload.sub,tn=v.payload.roles,bn=v.payload.claims;if(E.headers.set("x-access-token",Y.access_token||""),E.headers.set("x-user-id",un||""),tn&&tn.length>0)E.headers.set("x-user-roles",tn.join(","));if(bn&&bn.length>0)E.headers.set("x-user-claims",bn.join(","))}else{if(!c.refreshToken?.secret||!c.sessionToken?.secret)return S.status=500,l.traceSync({message:"Authentication secrets not defined",level:"error",context:{path:P,method:p},audit:xt(V,"Authentication secrets not defined")}),Error("One or more authentication secrets are not defined");if(Y=nl(E.headers,w),!Y.session_token)return S.status=401,l.traceSync({message:"No session token",level:"warn",context:{path:P,method:p},audit:xt(V,"No session token")}),Error("Unauthenticated");let v=await Qo({sessionId:Y.session_token});if(!v)return S.status=401,l.traceSync({message:"Invalid session",level:"warn",context:{path:P,method:p,sessionId:Y.session_token},audit:xt(V,"Invalid session")}),Error("Unauthenticated");let un=R.userSessions;if(un&&g){let T=(await g.select().from(un).where(wr(un.id,Y.session_token)).limit(1))[0];if(!T||T.isActive===!1||T.revokedAt)return S.status=401,l.traceSync({message:"Session revoked or inactive",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,isActive:T?.isActive,revokedAt:T?.revokedAt},audit:xt(V,"Session revoked")}),Error("Session has been revoked");if(T.expiresAt&&new Date(T.expiresAt)<new Date)return S.status=401,l.traceSync({message:"Session expired",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,expiresAt:T.expiresAt},audit:xt(V,"Session expired")}),Error("Session has expired")}if(v.lastActiveAt&&c.sessions?.inactivityTimeout){let gn=new Date(v.lastActiveAt).getTime(),T=st(c.sessions.inactivityTimeout)*1000;if(Date.now()-gn>T)return S.status=401,l.traceSync({message:"Session inactive timeout",level:"warn",context:{path:P,method:p,sessionId:Y.session_token,lastActiveAt:v.lastActiveAt},audit:xt(V,"Session inactive timeout")}),Error("Session expired due to inactivity")}$f(Y.session_token).catch(()=>{});let tn=R.userSessions;if(tn&&g)g.update(tn).set({lastActivityAt:new Date}).where(wr(tn.id,Y.session_token)).catch(()=>{});let bn=so(Y.access_token||"",b.accessTokenSecret||""),fn=Y.access_token?bn.valid:!1,Jn=Y.refresh_token?so(Y.refresh_token,b.refreshTokenSecret||"").valid:!1;if(!fn&&Jn&&Y.refresh_token&&v.rememberMe===!0){let gn=await lg(v.userId,v.id,()=>sg({refreshTokenId:Y.refresh_token,options:o,sessionData:v}));if(gn.success&&gn.accessToken){Y.access_token=gn.accessToken;let T=`${w.access_token}=${gn.accessToken}; Path=/; HttpOnly; SameSite=Strict;Secure; Max-Age=${st(c.accessToken.expiresIn??"15m")}`;S.headers["Set-Cookie"]=T}}let wn=bn.valid?bn.payload.sub:v.userId,cr=bn.valid?bn.payload.roles:void 0,ct=bn.valid?bn.payload.claims:void 0;if(E.headers.set("x-access-token",Y.access_token||""),E.headers.set("x-refresh-token",Y.refresh_token||""),E.headers.set("x-session-id",Y.session_token||""),E.headers.set("x-user-id",wn||""),cr&&cr.length>0)E.headers.set("x-user-roles",cr.join(","));if(ct&&ct.length>0)E.headers.set("x-user-claims",ct.join(","))}}}).onAfterHandle(({request:E,set:S})=>{if(m){let z=E.headers.get("x-request-start-time"),U=z?parseInt(z,10):Date.now(),P=Date.now()-U,p=new URL(E.url),K=typeof S.status==="number"?S.status:200;m.recordRequest({endpoint:p.pathname,method:E.method,status:K,responseTimeMs:P,isError:K>=400,errorType:K>=500?"server_error":K>=400?"client_error":void 0})}if(A){let z=new URL(E.url),U={};E.headers.forEach((P,p)=>{U[p]=P}),A.recordRequest({path:z.pathname,method:E.method,timestamp:Date.now(),headers:U})}}).onError((E)=>{let{set:S,code:z,error:U}=E,P=typeof z==="number"?z:500,p=U instanceof Error?U.message:"Internal Server Error";return S.status=P,Response.json({isSuccess:!1,message:p,status:P,errors:[U],data:null})}),l.info("Creating routes for entities"),sl(r,{db:g,schemaTables:R,schemaRelations:k,entities:i,logger:l,databaseUrl:b.databaseUrl,storage:o.storage,authorization:o.authorization,emailServiceAvailable:!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path)});let D=c?.mode==="consumer";if(c?.enabled&&!D&&g){let E=R.users,S=R.userSessions||R.user_sessions||R.sessions;if(!S&&c.sessions?.enabled)l.warn("[AUTH] sessions is enabled but user_sessions table not found in schema. Disabling sessions.");if(E){rl(o);let{createAuthRoutes:z}=(Cd(),se(S2)),{signJWT:U,verifyJWT:P}=(Vo(),se(mu)),{generateSession:p,deleteSession:K}=(Af(),se(Tu));z(r,{authConfig:{db:g,logger:l,usersTable:E,sessionsTable:S,userRolesTable:R.userRoles,rolesTable:R.roles,roleClaimsTable:R.roleClaims,claimsTable:R.claims,authentication:{enabled:c.enabled,accessToken:c.accessToken,refreshToken:c.refreshToken,sessionToken:c.sessionToken}},features:{login:c.login,register:c.register,logout:c.logout,refresh:c.refresh,passwordReset:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.passwordReset?.enabled&&!B)return l.warn("[AUTH] passwordReset is enabled but no email provider is configured. Disabling passwordReset."),{...c.passwordReset,enabled:!1};return c.passwordReset})(),passwordChange:c.passwordChange,passwordSet:c.passwordSet,sessions:c.sessions,magicLink:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.magicLink?.enabled&&!B)return l.warn("[AUTH] magicLink is enabled but no email provider is configured. Disabling magicLink."),{...c.magicLink,enabled:!1};return c.magicLink})(),me:c.me||{enabled:!0,route:"/auth/me"},invite:(()=>{let B=!!(o.email?.gmail?.enabled&&o.email.gmail.json_file_path);if(c.invite?.enabled&&!B)return l.warn("[AUTH] invite is enabled but no email provider is configured. Disabling invite."),{...c.invite,enabled:!1};return c.invite})(),captcha:c.captcha,oauth:c.oauth?.enabled&&b.oauthProviders?{...c.oauth,providers:b.oauthProviders}:void 0},sessionsTable:S,oauthAccountsTable:R.oauthAccounts,schemaTables:R,schemaRelations:k,databaseUrl:b.databaseUrl,emailService:H,appName:o.appId,captchaService:(()=>{let B=Te();if(!c.captcha?.enabled||!B)return null;return new Bf({redis:{get:async(Y)=>{let Q=await B.read(Y);return Q.success?Q.data:null},set:async(Y,Q,V)=>{await B.create(Y,Q,V?.ex)},del:async(Y)=>{await B.remove(Y)}},logger:l,config:{enabled:!0,type:c.captcha.type||"math",difficulty:c.captcha.difficulty||"medium",expiresIn:c.captcha.expiresIn||"5m",maxAttempts:c.captcha.maxAttempts||3,caseSensitive:c.captcha.caseSensitive??!1}})})(),tokenResponseConfig:{accessToken:{setHeadersEnabled:c.accessToken?.setHeadersEnabled??!0,returnJson:c.accessToken?.returnJson??!0},refreshToken:{setHeadersEnabled:c.refreshToken?.setHeadersEnabled??!0,returnJson:c.refreshToken?.returnJson??!0},sessionToken:{setHeadersEnabled:c.sessionToken?.setHeadersEnabled??!0,returnJson:c.sessionToken?.returnJson??!0}},helpers:{signAccessToken:(B,L,Y)=>U({subject:B,expiresInSeconds:st(c.accessToken?.expiresIn||"15m"),issuer:c.accessToken?.issuer,audience:c.accessToken?.audience,customClaims:{...L&&L.length>0?{roles:L}:{},...Y&&Y.length>0?{claims:Y}:{}}},b.accessTokenSecret||"",c.accessToken?.algorithm||"HS256"),signRefreshToken:(B)=>U({subject:B,expiresInSeconds:st(c.refreshToken?.expiresIn||"7d"),issuer:c.refreshToken?.issuer,audience:c.refreshToken?.audience},b.refreshTokenSecret||"",c.refreshToken?.algorithm||"HS256"),verifyRefreshToken:(B)=>P(B,b.refreshTokenSecret||""),createSession:async(B)=>{let L=await p({userId:B.userId,deviceInfo:B.deviceInfo});return L.success?L.session.id:""},destroySession:async(B)=>K({sessionId:B}),saveSessionToDb:async(B,L)=>{if(!S||!g)return;let Y=c.sessions,Q=L.deviceInfo||{},V=`${Q.browserName||""}-${Q.osName||""}-${Q.deviceType||""}`,q=await g.select().from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))),F=V&&!V.includes("--unknown")&&!V.includes("Bot/Crawler")&&!V.includes("Headless")&&V!=="--"&&V!=="--unknown",v=F?!q.some((an)=>an.deviceFingerprint===V):!1,un=q.some((an)=>an.approvalStatus==="approved"),tn=Y?.trustNewDevices===!1&&v&&F&&un;l.info("[AUTH] Device fingerprint analysis",{userId:L.userId,deviceFingerprint:V,hasValidFingerprint:F,isNewDevice:v,existingSessionCount:q.length,hasAnyApprovedSession:un,requiresApproval:tn});let bn=null,fn="approved";if(tn){let{randomBytes:an}=await import("crypto");bn=an(32).toString("hex"),fn="pending",l.info("[AUTH] New device requires approval",{userId:L.userId,deviceFingerprint:V,ipAddress:Q.ipAddress})}let Jn=q.filter((an)=>{let er=an,fr=er.deviceFingerprint||"",Mr=er.ipAddress||"";return(!fr||fr==="--"||fr==="--unknown"||fr.includes("Bot/Crawler")||fr.includes("Headless")||fr.includes("unknown-unknown"))&&(Mr==="127.0.0.1"||Mr==="::1"||Mr==="localhost"||!Mr)});if(Jn.length>0){for(let an of Jn)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"bot_session_cleanup"}).where(wr(S.id,an.id));l.info("[AUTH] Cleaned up stale bot/crawler sessions",{userId:L.userId,cleanedCount:Jn.length})}if(F&&!v){let an=q.filter((er)=>er.deviceFingerprint===V);for(let er of an)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"relogin_same_device"}).where(wr(S.id,er.id));l.info("[AUTH] Revoked old sessions from same device",{userId:L.userId,deviceFingerprint:V,revokedCount:an.length})}if(!Y?.allowMultipleDevices&&q.length>0){if(q.filter((er)=>er.deviceFingerprint===V).length===0&&v)for(let er of q)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"new_device_login"}).where(wr(S.id,er.id))}if(Y?.maxActiveSessions){let{count:an}=await import("drizzle-orm"),fr=(await g.select({count:an()}).from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))))[0]?.count||0;if(fr>=Y.maxActiveSessions){let{asc:Mr}=await import("drizzle-orm"),et=await g.select().from(S).where(ie(wr(S.userId,L.userId),wr(S.isActive,!0))).orderBy(Mr(S.createdAt)).limit(fr-Y.maxActiveSessions+1);for(let Ct of et)await g.update(S).set({isActive:!1,revokedAt:new Date,revokedReason:"max_sessions_exceeded"}).where(wr(S.id,Ct.id))}}let wn=100;if(Q.isHeadless)wn-=50;if(Q.isBot)wn-=40;if(Q.isSuspicious)l.warn("[AUTH] Suspicious login detected",{userId:L.userId,suspiciousPatterns:Q.suspiciousPatterns,userAgent:Q.userAgent,ipAddress:Q.ipAddress});if(v)wn-=25;if(!Q.ipAddress||Q.ipAddress==="unknown")wn-=20;if(!Q.browserName)wn-=15;if(!Q.osName)wn-=15;if(!Q.deviceType||Q.deviceType==="unknown")wn-=10;if(!Q.deviceName||Q.deviceName==="Unknown Device")wn-=5;let cr=V&&!V.includes("--unknown")&&V!=="--",ct=Q.ipAddress&&Q.ipAddress!=="unknown";if(cr){if(q.filter((er)=>er.deviceFingerprint===V).length>0)wn+=20}if(ct){if(q.filter((er)=>er.ipAddress===Q.ipAddress).length>0)wn+=15}wn=Math.max(0,Math.min(100,wn));let gn=50;if(await g.insert(S).values({id:B,userId:L.userId,tokenHash:B,deviceFingerprint:V,deviceName:Q.deviceName,deviceType:Q.deviceType,browserName:Q.browserName,browserVersion:Q.browserVersion,osName:Q.osName,osVersion:Q.osVersion,ipAddress:Q.ipAddress,locationCountry:Q.locationCountry,locationCity:Q.locationCity,loginMethod:L.loginMethod||"password",rememberMe:L.rememberMe??!1,trustScore:wn,lastActivityAt:new Date,createdAt:new Date,expiresAt:new Date(Date.now()+st(c.sessionToken?.expiresIn||"30d")*1000),isActive:fn==="approved",approvalStatus:fn,approvalToken:bn,approvalRequestedAt:tn?new Date:null}),H&&(Y?.notifyOnNewDevice&&v||wn<gn||tn)){let an=R.users;if(an){let fr=(await g.select().from(an).where(wr(an.id,L.userId)).limit(1))[0];if(fr?.email){let Mr=wn<gn,et=c.sessions?.approvalRedirectUrl||"http://localhost:3000/devices",Ct=bn?`${et}?action=approve&token=${bn}`:"",hr=bn?`${et}?action=reject&token=${bn}`:"",Zn,Rs;if(tn)Zn="\uD83D\uDD10 New Device Login Requires Approval",Rs=`
1527
1527
  <h2>\uD83D\uDD10 New Device Login Requires Your Approval</h2>
1528
1528
  <p>Someone is trying to log into your account from a new device:</p>
1529
1529
  <ul>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.8.81",
3
+ "version": "0.8.82",
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",