molnos 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/dist/databases.mjs +8 -8
- package/dist/functions.mjs +6 -6
- package/dist/molnos_core.mjs +20 -20
- package/dist/observability.mjs +5 -5
- package/dist/sites.mjs +3 -3
- package/dist/storage.mjs +3 -3
- package/package.json +1 -2
- package/sbom.json +1 -1
package/dist/molnos_core.mjs
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
// MolnOS Core - See LICENSE file for copyright and license details.
|
|
2
|
-
import
|
|
2
|
+
import Z,{createHash as uo,createHmac as po,scryptSync as ss,randomBytes as is,createCipheriv as os,createDecipheriv as ns}from"crypto";import{URL as as}from"url";var Ve=class extends Error{constructor(t){super(t),this.name="ValidationError",this.message=t||"Validation did not pass",this.cause={statusCode:400}}};import{existsSync as Jr,readFileSync as Xr}from"node:fs";var X=class{config={};options=[];validators=[];autoValidate=!0;constructor(t){let e=t?.configFilePath,r=t?.args||[],s=t?.config||{};this.options=t?.options||[],this.validators=t?.validators||[],t?.autoValidate!==void 0&&(this.autoValidate=t.autoValidate),this.config=this.createConfig(e,r,s)}deepMerge(t,e){let r={...t};for(let s in e)e[s]!==void 0&&(e[s]!==null&&typeof e[s]=="object"&&!Array.isArray(e[s])&&s in t&&t[s]!==null&&typeof t[s]=="object"&&!Array.isArray(t[s])?r[s]=this.deepMerge(t[s],e[s]):e[s]!==void 0&&(r[s]=e[s]));return r}setValueAtPath(t,e,r){let s=e.split("."),i=t;for(let n=0;n<s.length-1;n++){let a=s[n];!(a in i)||i[a]===null?i[a]={}:typeof i[a]!="object"&&(i[a]={}),i=i[a]}let o=s[s.length-1];i[o]=r}getValueAtPath(t,e){let r=e.split("."),s=t;for(let i of r){if(s==null)return;s=s[i]}return s}createConfig(t,e=[],r={}){let s={};for(let a of this.options)a.defaultValue!==void 0&&this.setValueAtPath(s,a.path,a.defaultValue);let i={};if(t&&Jr(t))try{let a=Xr(t,"utf8");i=JSON.parse(a),console.log(`Loaded configuration from ${t}`)}catch(a){console.error(`Error reading config file: ${a instanceof Error?a.message:String(a)}`)}let o=this.parseCliArgs(e),n=this.deepMerge({},s);return n=this.deepMerge(n,i),n=this.deepMerge(n,r),n=this.deepMerge(n,o),n}parseCliArgs(t){let e={},r=t[0]?.endsWith("node")||t[0]?.endsWith("node.exe")?2:0;for(;r<t.length;){let s=t[r++],i=this.options.find(o=>o.flag===s);if(i)if(i.isFlag)this.setValueAtPath(e,i.path,!0);else if(r<t.length&&!t[r].startsWith("-")){let o=t[r++];if(i.parser)try{o=i.parser(o)}catch(n){console.error(`Error parsing value for ${i.flag}: ${n instanceof Error?n.message:String(n)}`);continue}if(i.validator){let n=i.validator(o);if(n!==!0&&typeof n=="string"){console.error(`Invalid value for ${i.flag}: ${n}`);continue}if(n===!1){console.error(`Invalid value for ${i.flag}`);continue}}this.setValueAtPath(e,i.path,o)}else console.error(`Missing value for option ${s}`)}return e}validate(){for(let t of this.validators){let e=this.getValueAtPath(this.config,t.path),r=t.validator(e,this.config);if(r===!1)throw new Ve(t.message);if(typeof r=="string")throw new Ve(r)}}get(){return this.autoValidate&&this.validate(),this.config}getValue(t,e){let r=this.getValueAtPath(this.config,t);return r!==void 0?r:e}setValue(t,e){if(typeof e=="object"&&e!==null&&!Array.isArray(e)){let r=this.getValueAtPath(this.config,t)||{};if(typeof r=="object"&&!Array.isArray(r)){let s=this.deepMerge(r,e);this.setValueAtPath(this.config,t,s);return}}this.setValueAtPath(this.config,t,e)}getHelpText(){let t=`Available configuration options:
|
|
3
3
|
|
|
4
4
|
`;for(let e of this.options)t+=`${e.flag}${e.isFlag?"":" <value>"}
|
|
5
5
|
`,e.description&&(t+=` ${e.description}
|
|
6
6
|
`),e.defaultValue!==void 0&&(t+=` Default: ${JSON.stringify(e.defaultValue)}
|
|
7
7
|
`),t+=`
|
|
8
|
-
`;return t}};var
|
|
9
|
-
`);if(
|
|
8
|
+
`;return t}};var Y={int:t=>{let e=t.trim();if(!/^[+-]?\d+$/.test(e))throw new Error(`Cannot parse "${t}" as an integer`);let r=Number.parseInt(e,10);if(Number.isNaN(r))throw new Error(`Cannot parse "${t}" as an integer`);return r},float:t=>{let e=t.trim();if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(e)){if(e==="Infinity"||e==="-Infinity")return e==="Infinity"?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY;throw new Error(`Cannot parse "${t}" as a number`)}let r=Number.parseFloat(e);if(Number.isNaN(r))throw new Error(`Cannot parse "${t}" as a number`);return r},boolean:t=>{let e=t.trim().toLowerCase();if(["true","yes","1","y"].includes(e))return!0;if(["false","no","0","n"].includes(e))return!1;throw new Error(`Cannot parse "${t}" as a boolean`)},array:t=>t.split(",").map(e=>e.trim()),json:t=>{try{return JSON.parse(t)}catch{throw new Error(`Cannot parse "${t}" as JSON`)}}};import{EventEmitter as cs}from"events";var lt=class extends Error{constructor(t){super(),this.name="ValidationError",this.message=t,this.cause={statusCode:400}}};import{existsSync as Yr,readFileSync as Zr}from"fs";var ut=class{config;defaults={configFilePath:"mikromail.config.json",args:[]};constructor(t){let e=t?.config||{},r=t?.configFilePath||this.defaults.configFilePath,s=t?.args||this.defaults.args;this.config=this.create(r,s,e)}create(t,e,r){let s={host:"",user:"",password:"",port:465,secure:!0,debug:!1,maxRetries:2,skipEmailValidation:!1,skipMXRecordCheck:!1},i={};if(Yr(t))try{let n=Zr(t,"utf8");i=JSON.parse(n),console.log(`Loaded configuration from ${t}`)}catch(n){console.error(`Error reading config file: ${n instanceof Error?n.message:String(n)}`)}let o=this.parseCliArgs(e);return{...s,...r,...i,...o}}parseCliArgs(t){let e={};for(let r=2;r<t.length;r++)switch(t[r]){case"--host":r+1<t.length&&(e.host=t[++r]);break;case"--user":r+1<t.length&&(e.user=t[++r]);break;case"--password":r+1<t.length&&(e.password=t[++r]);break;case"--port":if(r+1<t.length){let i=Number.parseInt(t[++r],10);Number.isNaN(i)||(e.port=i)}break;case"--secure":e.secure=!0;break;case"--debug":e.debug=!0;break;case"--retries":if(r+1<t.length){let i=Number.parseInt(t[++r],10);Number.isNaN(i)||(e.maxRetries=i)}break}return e}validate(){if(!this.config.host)throw new lt("Host value not found")}get(){return this.validate(),this.config}};import{promises as Qr}from"dns";function de(t){try{let[e,r]=t.split("@");if(!e||e.length>64||e.startsWith(".")||e.endsWith(".")||e.includes("..")||!/^[a-zA-Z0-9!#$%&'*+\-/=?^_`{|}~.]+$/.test(e)||!r||r.length>255)return!1;if(r.startsWith("[")&&r.endsWith("]")){let i=r.slice(1,-1);return i.startsWith("IPv6:")?!0:/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(i)}if(r.startsWith(".")||r.endsWith(".")||r.includes(".."))return!1;let s=r.split(".");if(s.length<2||s[s.length-1].length<2)return!1;for(let i of s)if(!i||i.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(i))return!1;return!0}catch{return!1}}async function es(t){try{let e=await Qr.resolveMx(t);return!!e&&e.length>0}catch{return!1}}async function dt(t){try{let e=t.split("@")[1];return e?await es(e):!1}catch{return!1}}import{Buffer as pe}from"buffer";import qe from"crypto";import ts from"net";import rs from"os";import pt from"tls";var ht=class{config;socket;connected;lastCommand;serverCapabilities;secureMode;retryCount;maxEmailSize=10485760;constructor(t){this.config={host:t.host,user:t.user,password:t.password,port:t.port??(t.secure?465:587),secure:t.secure??!0,debug:t.debug??!1,timeout:t.timeout??1e4,clientName:t.clientName??rs.hostname(),maxRetries:t.maxRetries??3,retryDelay:t.retryDelay??1e3,skipAuthentication:t.skipAuthentication||!1,skipEmailValidation:t.skipEmailValidation||!1,skipMXRecordCheck:t.skipMXRecordCheck||!1},this.socket=null,this.connected=!1,this.lastCommand="",this.serverCapabilities=[],this.secureMode=this.config.secure,this.retryCount=0}log(t,e=!1){this.config.debug&&console.log(`${e?"SMTP ERROR: ":"SMTP: "}${t}`)}async connect(){return new Promise((t,e)=>{let r=setTimeout(()=>{e(new Error(`Connection timeout after ${this.config.timeout}ms`)),this.socket?.destroy()},this.config.timeout);try{this.config.secure?this.createTLSConnection(r,t,e):this.createPlainConnection(r,t,e)}catch(s){clearTimeout(r),this.log(`Failed to create socket: ${s.message}`,!0),e(s)}})}createTLSConnection(t,e,r){this.socket=pt.connect({host:this.config.host,port:this.config.port,rejectUnauthorized:!0,minVersion:"TLSv1.2",ciphers:"HIGH:!aNULL:!MD5:!RC4"}),this.setupSocketEventHandlers(t,e,r)}createPlainConnection(t,e,r){this.socket=ts.createConnection({host:this.config.host,port:this.config.port}),this.setupSocketEventHandlers(t,e,r)}setupSocketEventHandlers(t,e,r){this.socket&&(this.socket.once("error",s=>{clearTimeout(t),this.log(`Connection error: ${s.message}`,!0),r(new Error(`SMTP connection error: ${s.message}`))}),this.socket.once("connect",()=>{this.log("Connected to SMTP server"),clearTimeout(t),this.socket.once("data",s=>{let i=s.toString().trim();this.log(`Server greeting: ${i}`),i.startsWith("220")?(this.connected=!0,this.secureMode=this.config.secure,e()):(r(new Error(`Unexpected server greeting: ${i}`)),this.socket.destroy())})}),this.socket.once("close",s=>{this.connected?this.log(`Connection closed${s?" with error":""}`):(clearTimeout(t),r(new Error("Connection closed before initialization completed"))),this.connected=!1}))}async upgradeToTLS(){if(!(!this.socket||this.secureMode))return new Promise((t,e)=>{let s={socket:this.socket,host:this.config.host,rejectUnauthorized:!0,minVersion:"TLSv1.2",ciphers:"HIGH:!aNULL:!MD5:!RC4"},i=pt.connect(s);i.once("error",o=>{this.log(`TLS upgrade error: ${o.message}`,!0),e(new Error(`STARTTLS error: ${o.message}`))}),i.once("secureConnect",()=>{this.log("Connection upgraded to TLS"),i.authorized?(this.socket=i,this.secureMode=!0,t()):e(new Error(`TLS certificate verification failed: ${i.authorizationError}`))})})}async sendCommand(t,e,r=this.config.timeout){if(!this.socket||!this.connected)throw new Error("Not connected to SMTP server");return new Promise((s,i)=>{let o=setTimeout(()=>{this.socket?.removeListener("data",a),i(new Error(`Command timeout after ${r}ms: ${t}`))},r),n="",a=c=>{n+=c.toString();let l=n.split(`\r
|
|
9
|
+
`);if(l.length>0&&l[l.length-1]===""){let p=l[l.length-2]||"",u=/^(\d{3})(.?)/.exec(p);u?.[1]&&u[2]!=="-"&&(this.socket?.removeListener("data",a),clearTimeout(o),this.log(`SMTP Response: ${n.trim()}`),u[1]===e.toString()?s(n.trim()):i(new Error(`SMTP Error: ${n.trim()}`)))}};this.socket.on("data",a),t.startsWith("AUTH PLAIN")||t.startsWith("AUTH LOGIN")||this.lastCommand==="AUTH LOGIN"&&!t.startsWith("AUTH")?this.log("SMTP Command: [Credentials hidden]"):this.log(`SMTP Command: ${t}`),this.lastCommand=t,this.socket.write(`${t}\r
|
|
10
10
|
`)})}parseCapabilities(t){let e=t.split(`\r
|
|
11
|
-
`);this.serverCapabilities=[];for(let r=1;r<e.length;r++){let
|
|
11
|
+
`);this.serverCapabilities=[];for(let r=1;r<e.length;r++){let s=e[r];if(s.match(/^\d{3}/)&&s.charAt(3)===" "){let i=s.substr(4).toUpperCase();this.serverCapabilities.push(i)}}this.log(`Server capabilities: ${this.serverCapabilities.join(", ")}`)}getBestAuthMethod(){if(this.serverCapabilities.map(e=>e.split(" ")[0]).includes("AUTH")){let e=this.serverCapabilities.find(r=>r.startsWith("AUTH "));if(e){let r=e.split(" ").slice(1);if(r.includes("CRAM-MD5"))return"CRAM-MD5";if(r.includes("LOGIN"))return"LOGIN";if(r.includes("PLAIN"))return"PLAIN"}}return"PLAIN"}async authenticate(){switch(this.getBestAuthMethod()){case"CRAM-MD5":await this.authenticateCramMD5();break;case"LOGIN":await this.authenticateLogin();break;default:await this.authenticatePlain();break}}async authenticatePlain(){let t=pe.from(`\0${this.config.user}\0${this.config.password}`).toString("base64");await this.sendCommand(`AUTH PLAIN ${t}`,235)}async authenticateLogin(){await this.sendCommand("AUTH LOGIN",334),await this.sendCommand(pe.from(this.config.user).toString("base64"),334),await this.sendCommand(pe.from(this.config.password).toString("base64"),235)}async authenticateCramMD5(){let t=await this.sendCommand("AUTH CRAM-MD5",334),e=pe.from(t.substr(4),"base64").toString("utf8"),r=qe.createHmac("md5",this.config.password);r.update(e);let s=r.digest("hex"),i=`${this.config.user} ${s}`,o=pe.from(i).toString("base64");await this.sendCommand(o,235)}generateMessageId(){let t=qe.randomBytes(16).toString("hex"),e=this.config.user.split("@")[1]||"localhost";return`<${t}@${e}>`}generateBoundary(){return`----=_NextPart_${qe.randomBytes(12).toString("hex")}`}encodeHeaderValue(t){return/^[\x00-\x7F]*$/.test(t)?t:`=?UTF-8?Q?${t.replace(/[^\x00-\x7F]/g,e=>{let r=e.charCodeAt(0).toString(16).toUpperCase();return`=${r.length<2?`0${r}`:r}`})}?=`}sanitizeHeader(t){let e=t.replace(/[\r\n\t]+/g," ").replace(/\s{2,}/g," ").trim();return this.encodeHeaderValue(e)}createEmailHeaders(t){let e=this.generateMessageId(),r=new Date().toUTCString(),s=t.from||this.config.user,i=Array.isArray(t.to)?t.to.join(", "):t.to,o=[`From: ${this.sanitizeHeader(s)}`,`To: ${this.sanitizeHeader(i)}`,`Subject: ${this.sanitizeHeader(t.subject)}`,`Message-ID: ${e}`,`Date: ${r}`,"MIME-Version: 1.0"];if(t.cc){let n=Array.isArray(t.cc)?t.cc.join(", "):t.cc;o.push(`Cc: ${this.sanitizeHeader(n)}`)}if(t.replyTo&&o.push(`Reply-To: ${this.sanitizeHeader(t.replyTo)}`),t.headers)for(let[n,a]of Object.entries(t.headers))/^[a-zA-Z0-9-]+$/.test(n)&&(/^(from|to|cc|bcc|subject|date|message-id)$/i.test(n)||o.push(`${n}: ${this.sanitizeHeader(a)}`));return o}createMultipartEmail(t){let{text:e,html:r}=t,s=this.createEmailHeaders(t),i=this.generateBoundary();return r&&e?(s.push(`Content-Type: multipart/alternative; boundary="${i}"`),`${s.join(`\r
|
|
12
12
|
`)}\r
|
|
13
13
|
\r
|
|
14
|
-
--${
|
|
14
|
+
--${i}\r
|
|
15
15
|
Content-Type: text/plain; charset=utf-8\r
|
|
16
16
|
\r
|
|
17
17
|
${e||""}\r
|
|
18
18
|
\r
|
|
19
|
-
--${
|
|
19
|
+
--${i}\r
|
|
20
20
|
Content-Type: text/html; charset=utf-8\r
|
|
21
21
|
\r
|
|
22
22
|
${r||""}\r
|
|
23
23
|
\r
|
|
24
|
-
--${
|
|
25
|
-
`):(
|
|
24
|
+
--${i}--\r
|
|
25
|
+
`):(s.push("Content-Type: text/html; charset=utf-8"),r?`${s.join(`\r
|
|
26
26
|
`)}\r
|
|
27
27
|
\r
|
|
28
|
-
${r}`:`${
|
|
28
|
+
${r}`:`${s.join(`\r
|
|
29
29
|
`)}\r
|
|
30
30
|
\r
|
|
31
|
-
${e||""}`)}async smtpHandshake(){let t=await this.sendCommand(`EHLO ${this.config.clientName}`,250);if(this.parseCapabilities(t),!this.secureMode&&this.serverCapabilities.includes("STARTTLS")){await this.sendCommand("STARTTLS",220),await this.upgradeToTLS();let e=await this.sendCommand(`EHLO ${this.config.clientName}`,250);this.parseCapabilities(e)}this.config.skipAuthentication?this.log("Authentication skipped (testing mode)"):await this.authenticate()}async sendEmail(t){let e=t.from||this.config.user,{to:r,subject:
|
|
32
|
-
.`,250);let c=/Message-ID: (.*)/i.exec(a);return{success:!0,messageId:c?c[1].trim():void 0,message:"Email sent successfully"}}catch(a){let c=a.message;if(this.log(`Error sending email: ${c}`,!0),c.includes("5.")||c.includes("Authentication failed")||c.includes("certificate")||this.retryCount>=this.config.maxRetries)return{success:!1,error:c};try{this.connected&&await this.sendCommand("RSET",250)}catch{}this.socket?.end(),this.connected=!1}return{success:!1,error:"Maximum retry count exceeded"}}async close(){try{this.connected&&await this.sendCommand("QUIT",221)}catch(t){this.log(`Error during QUIT: ${t.message}`,!0)}finally{this.socket&&(this.socket.destroy(),this.socket=null,this.connected=!1)}}};var
|
|
31
|
+
${e||""}`)}async smtpHandshake(){let t=await this.sendCommand(`EHLO ${this.config.clientName}`,250);if(this.parseCapabilities(t),!this.secureMode&&this.serverCapabilities.includes("STARTTLS")){await this.sendCommand("STARTTLS",220),await this.upgradeToTLS();let e=await this.sendCommand(`EHLO ${this.config.clientName}`,250);this.parseCapabilities(e)}this.config.skipAuthentication?this.log("Authentication skipped (testing mode)"):await this.authenticate()}async sendEmail(t){let e=t.from||this.config.user,{to:r,subject:s}=t,i=t.text||"",o=t.html||"";if(!e||!r||!s||!i&&!o)return{success:!1,error:"Missing required email parameters (from, to, subject, and either text or html)"};let n=Array.isArray(t.to)?t.to:[t.to];if(this.config.skipEmailValidation)this.log("Email validation skipped (testing mode)");else{if(!de(e))return{success:!1,error:"Invalid email address format"};for(let a of n)if(!de(a))return{success:!1,error:`Invalid recipient email address format: ${a}`}}for(this.retryCount=0;this.retryCount<=this.config.maxRetries;this.retryCount++)try{this.retryCount>0&&(this.log(`Retrying email send (attempt ${this.retryCount} of ${this.config.maxRetries})...`),await new Promise(p=>setTimeout(p,this.config.retryDelay))),this.connected||(await this.connect(),await this.smtpHandshake()),await this.sendCommand(`MAIL FROM:<${e}>`,250);for(let p of n)await this.sendCommand(`RCPT TO:<${p}>`,250);if(t.cc){let p=Array.isArray(t.cc)?t.cc:[t.cc];for(let u of p)de(u)&&await this.sendCommand(`RCPT TO:<${u}>`,250)}if(t.bcc){let p=Array.isArray(t.bcc)?t.bcc:[t.bcc];for(let u of p)de(u)&&await this.sendCommand(`RCPT TO:<${u}>`,250)}await this.sendCommand("DATA",354);let a=this.createMultipartEmail(t);if(a.length>this.maxEmailSize)return{success:!1,error:"Email size exceeds maximum allowed"};await this.sendCommand(`${a}\r
|
|
32
|
+
.`,250);let c=/Message-ID: (.*)/i.exec(a);return{success:!0,messageId:c?c[1].trim():void 0,message:"Email sent successfully"}}catch(a){let c=a.message;if(this.log(`Error sending email: ${c}`,!0),c.includes("5.")||c.includes("Authentication failed")||c.includes("certificate")||this.retryCount>=this.config.maxRetries)return{success:!1,error:c};try{this.connected&&await this.sendCommand("RSET",250)}catch{}this.socket?.end(),this.connected=!1}return{success:!1,error:"Maximum retry count exceeded"}}async close(){try{this.connected&&await this.sendCommand("QUIT",221)}catch(t){this.log(`Error during QUIT: ${t.message}`,!0)}finally{this.socket&&(this.socket.destroy(),this.socket=null,this.connected=!1)}}};var Fe=class{smtpClient;config;constructor(t){let e=new ut(t).get(),r=new ht(e);this.smtpClient=r,this.config=e}async send(t){try{let e=Array.isArray(t.to)?t.to:[t.to];if(!this.config.skipMXRecordCheck)for(let s of e)await dt(s)||console.error(`Warning: No MX records found for recipient domain: ${s}`);let r=await this.smtpClient.sendEmail(t);r.success?console.log(`Message ID: ${r.messageId}`):console.error(`Failed to send email: ${r.error}`),await this.smtpClient.close()}catch(e){console.error("Error in email sending process:",e.message)}}};var Be=()=>{let t=ls(process.env.DEBUG)||!1;return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:3600,refreshTokenExpirySeconds:604800,maxActiveSessions:3,appUrl:process.env.APP_URL||"http://localhost:3000",templates:null,debug:t},email:{emailSubject:"Your Secure Login Link",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:t},storage:{databaseDirectory:"mikroauth",encryptionKey:process.env.STORAGE_KEY||"",debug:t},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"],debug:t}}};function ls(t){return t==="true"||t===!0}var us=class{algorithm="HS256";secret="HS256";constructor(t){if(process.env.NODE_ENV==="production"&&(!t||t.length<32||t===Be().auth.jwtSecret))throw new Error("Production environment requires a strong JWT secret (min 32 chars)");this.secret=t}sign(t,e={}){let r={alg:this.algorithm,typ:"JWT"},s=Math.floor(Date.now()/1e3),i={...t,iat:s};e.exp!==void 0&&(i.exp=s+e.exp),e.notBefore!==void 0&&(i.nbf=s+e.notBefore),e.issuer&&(i.iss=e.issuer),e.audience&&(i.aud=e.audience),e.subject&&(i.sub=e.subject),e.jwtid&&(i.jti=e.jwtid);let o=this.base64UrlEncode(JSON.stringify(r)),n=this.base64UrlEncode(JSON.stringify(i)),a=`${o}.${n}`,c=this.createSignature(a);return`${a}.${c}`}verify(t,e={}){let r=this.decode(t);if(r.header.alg!==this.algorithm)throw new Error(`Invalid algorithm. Expected ${this.algorithm}, got ${r.header.alg}`);let[s,i]=t.split("."),o=`${s}.${i}`;if(this.createSignature(o)!==r.signature)throw new Error("Invalid signature");let n=r.payload,a=Math.floor(Date.now()/1e3),c=e.clockTolerance||0;if(n.exp!==void 0&&n.exp+c<a)throw new Error("Token expired");if(n.nbf!==void 0&&n.nbf-c>a)throw new Error("Token not yet valid");if(e.issuer&&n.iss!==e.issuer)throw new Error("Invalid issuer");if(e.audience&&n.aud!==e.audience)throw new Error("Invalid audience");if(e.subject&&n.sub!==e.subject)throw new Error("Invalid subject");return n}decode(t){let e=t.split(".");if(e.length!==3)throw new Error("Invalid token format");try{let[r,s,i]=e,o=JSON.parse(this.base64UrlDecode(r)),n=JSON.parse(this.base64UrlDecode(s));return{header:o,payload:n,signature:i}}catch{throw new Error("Failed to decode token")}}createSignature(t){let e=Z.createHmac("sha256",this.secret).update(t).digest();return this.base64UrlEncode(e)}base64UrlEncode(t){let e;return typeof t=="string"?e=Buffer.from(t):e=t,e.toString("base64").replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}base64UrlDecode(t){let e=t.replace(/-/g,"+").replace(/_/g,"/");switch(e.length%4){case 0:break;case 2:e+="==";break;case 3:e+="=";break;default:throw new Error("Invalid base64 string")}return Buffer.from(e,"base64").toString()}},ds=class{templates;constructor(t){t?this.templates=t:this.templates=ps}getText(t,e,r){return this.templates.textVersion(t,e,r).trim()}getHtml(t,e,r){return this.templates.htmlVersion(t,e,r).trim()}},ps={textVersion:(t,e,r)=>`
|
|
33
33
|
Click this link to login: ${t}
|
|
34
34
|
|
|
35
35
|
Security Information:
|
|
@@ -108,11 +108,11 @@ If you didn't request this link, please ignore this email.
|
|
|
108
108
|
</div>
|
|
109
109
|
</body>
|
|
110
110
|
</html>
|
|
111
|
-
`},Or=class{constructor(t){this.options=t}sentEmails=[];async sendMail(t){this.sentEmails.push(t),this.options?.logToConsole&&(console.log("Email sent:"),console.log(`From: ${t.from}`),console.log(`To: ${t.to}`),console.log(`Subject: ${t.subject}`),console.log(`Text: ${t.text}`)),this.options?.onSend&&this.options.onSend(t)}getSentEmails(){return[...this.sentEmails]}clearSentEmails(){this.sentEmails=[]}},Ur=class{data=new Map;collections=new Map;expiryEmitter=new Tr;expiryCheckInterval;constructor(t=1e3){this.expiryCheckInterval=setInterval(()=>this.checkExpiredItems(),t)}destroy(){clearInterval(this.expiryCheckInterval),this.data.clear(),this.collections.clear(),this.expiryEmitter.removeAllListeners()}checkExpiredItems(){let t=Date.now();for(let[e,r]of this.data.entries())r.expiry&&r.expiry<t&&(this.data.delete(e),this.expiryEmitter.emit("expired",e));for(let[e,r]of this.collections.entries())r.expiry&&r.expiry<t&&(this.collections.delete(e),this.expiryEmitter.emit("expired",e))}async set(t,e,r){let i=r?Date.now()+r*1e3:null;this.data.set(t,{value:e,expiry:i})}async get(t){let e=this.data.get(t);return e?e.expiry&&e.expiry<Date.now()?(this.data.delete(t),null):e.value:null}async delete(t){this.data.delete(t),this.collections.delete(t)}async addToCollection(t,e,r){this.collections.has(t)||this.collections.set(t,{items:[],expiry:r?Date.now()+r*1e3:null});let i=this.collections.get(t);i&&(r&&(i.expiry=Date.now()+r*1e3),i.items.push(e))}async removeFromCollection(t,e){let r=this.collections.get(t);r&&(r.items=r.items.filter(i=>i!==e))}async getCollection(t){let e=this.collections.get(t);return e?[...e.items]:[]}async getCollectionSize(t){let e=this.collections.get(t);return e?e.items.length:0}async removeOldestFromCollection(t){let e=this.collections.get(t);return!e||e.items.length===0?null:e.items.shift()||null}async findKeys(t){let e=t.replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),i=Array.from(this.data.keys()).filter(o=>r.test(o)),s=Array.from(this.collections.keys()).filter(o=>r.test(o));return[...new Set([...i,...s])]}};function Ge(t){if(!t||t.trim()===""||(t.match(/@/g)||[]).length!==1)return!1;let[e,r]=t.split("@");return!(!e||!r||t.includes("..")||!Lr(e)||!Hr(r))}function Lr(t){return t.startsWith('"')&&t.endsWith('"')?!t.slice(1,-1).includes('"'):t.length>64||t.startsWith(".")||t.endsWith(".")?!1:/^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$/.test(t)}function Hr(t){if(t.startsWith("[")&&t.endsWith("]")){let r=t.slice(1,-1);return r.startsWith("IPv6:")?Vr(r.slice(5)):Dr(r)}let e=t.split(".");if(e.length===0)return!1;for(let r of e)if(!r||r.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(r))return!1;if(e.length>1){let r=e[e.length-1];if(!/^[a-zA-Z]{2,}$/.test(r))return!1}return!0}function Dr(t){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/.test(t)}function Vr(t){if(!/^[a-fA-F0-9:]+$/.test(t))return!1;let e=t.split(":");return!(e.length<2||e.length>8)}var b=$e(),Nr=t=>{let e={configFilePath:"mikroauth.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:b.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:b.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:b.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:b.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:b.auth.maxActiveSessions},{flag:"--appUrl",path:"auth.appUrl",defaultValue:b.auth.appUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:b.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:b.email.host},{flag:"--emailUser",path:"email.user",defaultValue:b.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:b.email.password},{flag:"--emailPort",path:"email.port",defaultValue:b.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:b.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:b.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:b.email.debug},{flag:"--dir",path:"storage.databaseDirectory",defaultValue:b.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:b.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:b.storage.debug},{flag:"--port",path:"server.port",defaultValue:b.server.port},{flag:"--host",path:"server.host",defaultValue:b.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:b.server.useHttps},{flag:"--https",path:"server.useHttp2",isFlag:!0,defaultValue:b.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:b.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:b.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:b.server.sslCa},{flag:"--ratelimit",path:"server.rateLimit.enabled",defaultValue:b.server.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"server.rateLimit.requestsPerMinute",defaultValue:b.server.rateLimit.requestsPerMinute},{flag:"--allowed",path:"server.allowedDomains",defaultValue:b.server.allowedDomains,parser:q.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:b.server.debug}]};return t&&(e.config=t),e},de={linkSent:"If a matching account was found, a magic link has been sent.",revokedSuccess:"All other sessions revoked successfully.",logoutSuccess:"Logged out successfully."},Je=class{config;email;storage;jwtService;templates;constructor(t,e,r){let i=new z(Nr({auth:t.auth,email:t.email})).get();i.auth.debug&&console.log("Using configuration:",i),this.config=i,this.email=e||new Or,this.storage=r||new Ur,this.jwtService=new jr(i.auth.jwtSecret),this.templates=new $r(i?.auth.templates),this.checkIfUsingDefaultCredentialsInProduction()}checkIfUsingDefaultCredentialsInProduction(){process.env.NODE_ENV==="production"&&this.config.auth.jwtSecret===$e().auth.jwtSecret&&(console.error("WARNING: Using default secrets in production environment!"),process.exit(1))}generateToken(t){let e=Date.now().toString(),r=_.randomBytes(32).toString("hex");return _.createHash("sha256").update(`${t}:${e}:${r}`).digest("hex")}generateJsonWebToken(t){return this.jwtService.sign({sub:t.id,email:t.email,username:t.username,role:t.role,exp:Math.floor(Date.now()/1e3)+3600*24})}generateRefreshToken(){return _.randomBytes(40).toString("hex")}async trackSession(t,e,r){let i=`sessions:${t}`;if(await this.storage.getCollectionSize(i)>=this.config.auth.maxActiveSessions){let s=await this.storage.removeOldestFromCollection(i);s&&await this.storage.delete(`refresh:${s}`)}await this.storage.addToCollection(i,e,this.config.auth.refreshTokenExpirySeconds),await this.storage.set(`refresh:${e}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds)}generateMagicLinkUrl(t){let{token:e,email:r,appUrl:i}=t,s=i||this.config.auth.appUrl;try{return new Pr(s),`${s}?token=${encodeURIComponent(e)}&email=${encodeURIComponent(r)}`}catch{throw new Error("Invalid base URL configuration")}}async createMagicLink(t){let{email:e,ip:r,metadata:i,appUrl:s,subject:o}=t;if(!Ge(e))throw new Error("Valid email required");try{let n=this.generateToken(e),a=`magic_link:${n}`,c={email:e,ipAddress:r||"unknown",createdAt:Date.now()};await this.storage.set(a,JSON.stringify(c),this.config.auth.magicLinkExpirySeconds);let d=await this.storage.findKeys("magic_link:*");for(let f of d){if(f===a)continue;let v=await this.storage.get(f);if(v)try{JSON.parse(v).email===e&&await this.storage.delete(f)}catch{}}let p=this.generateMagicLinkUrl({token:n,email:e,appUrl:s}),u=Math.ceil(this.config.auth.magicLinkExpirySeconds/60);return await this.email.sendMail({from:this.config.email.user,to:e,subject:o||this.config.email.emailSubject,text:this.templates.getText(p,u,i),html:this.templates.getHtml(p,u,i)}),{message:de.linkSent}}catch(n){throw console.error(`Failed to process magic link request: ${n}`),new Error("Failed to process magic link request")}}async createToken(t){let{email:e,username:r,role:i,ip:s}=t;if(!Ge(e))throw new Error("Valid email required");try{let o=_.randomBytes(16).toString("hex"),n=this.generateRefreshToken(),a=Date.now(),c={sub:e,username:r,role:i,jti:o,lastLogin:a,metadata:{ip:s||"unknown"},exp:Math.floor(Date.now()/1e3)+3600*24},d=this.jwtService.sign(c,{exp:this.config.auth.jwtExpirySeconds}),p={email:e,username:r,role:i,ipAddress:s||"unknown",tokenId:o,createdAt:a,lastLogin:a};return await this.trackSession(e,n,p),{accessToken:d,refreshToken:n,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(o){throw console.error("Token creation error:",o),new Error("Token creation failed")}}async verifyToken(t){let{token:e,email:r}=t;try{let i=`magic_link:${e}`,s=await this.storage.get(i);if(!s)throw new Error("Invalid or expired token");let o=JSON.parse(s);if(o.email!==r)throw new Error("Email mismatch");let n=o.username,a=o.role;await this.storage.delete(i);let c=_.randomBytes(16).toString("hex"),d=this.generateRefreshToken(),p={sub:r,username:n,role:a,jti:c,lastLogin:o.createdAt,metadata:{ip:o.ipAddress},exp:Math.floor(Date.now()/1e3)+3600*24},u=this.jwtService.sign(p,{exp:this.config.auth.jwtExpirySeconds});return await this.trackSession(r,d,{...o,tokenId:c,createdAt:Date.now()}),{accessToken:u,refreshToken:d,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(i){throw console.error("Token verification error:",i),new Error("Verification failed")}}async refreshAccessToken(t){try{let e=await this.storage.get(`refresh:${t}`);if(!e)throw new Error("Invalid or expired refresh token");let r=JSON.parse(e),i=r.email;if(!i)throw new Error("Invalid refresh token data");let s=r.username,o=r.role,n=_.randomBytes(16).toString("hex"),a={sub:i,username:s,role:o,jti:n,lastLogin:r.lastLogin||r.createdAt,metadata:{ip:r.ipAddress}},c=this.jwtService.sign(a,{exp:this.config.auth.jwtExpirySeconds});return r.lastUsed=Date.now(),await this.storage.set(`refresh:${t}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds),{accessToken:c,refreshToken:t,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(e){throw console.error("Token refresh error:",e),new Error("Token refresh failed")}}verify(t){try{return this.jwtService.verify(t)}catch{throw new Error("Invalid token")}}async logout(t){try{if(!t||typeof t!="string")throw new Error("Refresh token is required");let e=await this.storage.get(`refresh:${t}`);if(!e)return{message:de.logoutSuccess};let r=JSON.parse(e).email;if(!r)throw new Error("Invalid refresh token data");await this.storage.delete(`refresh:${t}`);let i=`sessions:${r}`;return await this.storage.removeFromCollection(i,t),{message:de.logoutSuccess}}catch(e){throw console.error("Logout error:",e),new Error("Logout failed")}}async getSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,i=`sessions:${e}`,s=(await this.storage.getCollection(i)).map(async n=>{try{let a=await this.storage.get(`refresh:${n}`);if(!a)return await this.storage.removeFromCollection(i,n),null;let c=JSON.parse(a);return{id:`${n.substring(0,8)}...`,createdAt:c.createdAt||0,lastLogin:c.lastLogin||c.createdAt||0,lastUsed:c.lastUsed||c.createdAt||0,metadata:{ip:c.ipAddress},isCurrentSession:n===r}}catch{return await this.storage.removeFromCollection(i,n),null}}),o=(await Promise.all(s)).filter(Boolean);return o.sort((n,a)=>a.createdAt-n.createdAt),{sessions:o}}catch(e){throw console.error("Get sessions error:",e),new Error("Failed to fetch sessions")}}async revokeSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,i=`sessions:${e}`,s=await this.storage.getCollection(i);for(let o of s)r&&o===r||await this.storage.delete(`refresh:${o}`);return await this.storage.delete(i),r&&await this.storage.get(`refresh:${r}`)&&await this.storage.addToCollection(i,r,this.config.auth.refreshTokenExpirySeconds),{message:de.revokedSuccess}}catch(e){throw console.error("Revoke sessions error:",e),new Error("Failed to revoke sessions")}}authenticate(t,e){try{let r=t.headers?.authorization;if(!r||!r.startsWith("Bearer "))throw new Error("Authentication required");let i=r.split(" ")[1];try{let s=this.verify(i);t.user={email:s.sub},e()}catch{throw new Error("Invalid or expired token")}}catch(r){e(r)}}},Fr=class{key;algorithm="aes-256-gcm";keyLength=32;constructor(t){this.key=Ar(t,"mikroauth-salt",this.keyLength)}encrypt(t){let e=Ir(12),r=Er(this.algorithm,this.key,e),i=Buffer.concat([r.update(t,"utf8"),r.final()]),s=r.getAuthTag();return`${e.toString("hex")}:${s.toString("hex")}:${i.toString("hex")}`}decrypt(t){let e=t.split(":");if(e.length!==3)throw new Error("Invalid encrypted data format");let[r,i,s]=e,o=Buffer.from(r,"hex"),n=Buffer.from(i,"hex"),a=Buffer.from(s,"hex"),c=kr(this.algorithm,this.key,o);return c.setAuthTag(n),Buffer.concat([c.update(a),c.final()]).toString("utf8")}},Xe=class{db;encryption;PREFIX_KV="kv:";PREFIX_COLLECTION="coll:";TABLE_NAME="mikroauth";constructor(t,e){this.db=t,e&&(this.encryption=new Fr(e))}async start(){await this.db.start()}async close(){await this.db.close()}async set(t,e,r){let i=`${this.PREFIX_KV}${t}`,s=this.encryption?this.encryption.encrypt(e):e,o=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,i,s,o)}async get(t){let e=`${this.PREFIX_KV}${t}`,r=await this.db.get(this.TABLE_NAME,e);return r?this.encryption?this.encryption.decrypt(r):r:null}async delete(t){let e=`${this.PREFIX_KV}${t}`;await this.db.delete(this.TABLE_NAME,e)}async addToCollection(t,e,r){let i=`${this.PREFIX_COLLECTION}${t}`,s=await this.db.get(this.TABLE_NAME,i),o=[];if(s){let d=this.encryption?this.encryption.decrypt(s):s;o=JSON.parse(d)}o.includes(e)||o.push(e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n,c=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,i,a,c)}async removeFromCollection(t,e){let r=`${this.PREFIX_COLLECTION}${t}`,i=await this.db.get(this.TABLE_NAME,r);if(!i)return;let s=this.encryption?this.encryption.decrypt(i):i,o=JSON.parse(s);o=o.filter(c=>c!==e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n;await this.db.write(this.TABLE_NAME,r,a)}async getCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return[];let i=this.encryption?this.encryption.decrypt(r):r;return JSON.parse(i)}async getCollectionSize(t){return(await this.getCollection(t)).length}async removeOldestFromCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return null;let i=this.encryption?this.encryption.decrypt(r):r,s=JSON.parse(i);if(s.length===0)return null;let o=s.shift(),n=JSON.stringify(s),a=this.encryption?this.encryption.encrypt(n):n;return await this.db.write(this.TABLE_NAME,e,a),o}async findKeys(t){let e=t.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),i=await this.db.get(this.TABLE_NAME);return Array.isArray(i)?i.filter(s=>{let o=s[0];return typeof o=="string"&&o.startsWith(this.PREFIX_KV)}).map(s=>s[0].substring(this.PREFIX_KV.length)).filter(s=>r.test(s)):[]}},Ye=class{email;sender;constructor(t){this.sender=t.user,this.email=new je({config:t})}async sendMail(t){await this.email.send({from:this.sender,to:t.to,cc:t.cc,bcc:t.bcc,subject:t.subject,text:t.text,html:t.html})}};function ue(t){if(!t.deflate&&!t.inflate)throw new Error("Dictionary must provide either deflate or inflate mapping");if(t.deflate&&t.inflate)throw new Error("Dictionary should provide only one of deflate or inflate (not both). The inverse will be auto-generated.");return t.deflate?{deflate:t.deflate,inflate:Ze(t.deflate)}:{deflate:Ze(t.inflate),inflate:t.inflate}}function Ze(t){let e={};for(let[r,i]of Object.entries(t))e[i]=r;return e}function Z(t,e){if(t==null||typeof t!="object")return t;if(Array.isArray(t))return t.map(i=>Z(i,e));let r={};for(let[i,s]of Object.entries(t)){let o=e[i]||i;r[o]=Z(s,e)}return r}function W(t){if(!t||typeof t!="string")throw new Error("Table name must be a non-empty string");if(t.length>255)throw new Error("Table name must not exceed 255 characters");if(t.includes("/")||t.includes("\\"))throw new Error("Table name must not contain path separators");if(t.includes(".."))throw new Error('Table name must not contain ".."');if(t.startsWith("."))throw new Error('Table name must not start with "."');if(t.includes("\0"))throw new Error("Table name must not contain null bytes");if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(t.toUpperCase()))throw new Error(`Table name "${t}" is reserved by the filesystem`)}function pe(t){if(t==null)throw new Error("Key must be defined");if(typeof t!="string")throw new Error("Key must be a string");if(t.length===0)throw new Error("Key must not be empty");if(t.length>1024)throw new Error("Key must not exceed 1024 characters");if(t.includes("\0"))throw new Error("Key must not contain null bytes")}function Qe(t){if(t===void 0)throw new Error("Value must not be undefined (use null instead)");let e=typeof t;if(e==="function")throw new Error("Value must be JSON-serializable: functions are not supported");if(e==="symbol")throw new Error("Value must be JSON-serializable: symbols are not supported");try{if(JSON.stringify(t)===void 0)throw new Error("Value must be JSON-serializable: value cannot be serialized")}catch(r){throw new Error(`Value must be JSON-serializable: ${r instanceof Error?r.message:String(r)}`)}}import{existsSync as Me,mkdirSync as Br,readdirSync as zr,openSync as qr,closeSync as _r}from"fs";import{readFile as Wr,writeFile as Kr,rename as Gr,unlink as et,open as Jr}from"fs/promises";import{join as Oe,dirname as Xr}from"path";var K=class{data=new Map;databaseDirectory;dictionaries=new Map;useFsync;constructor(t){this.databaseDirectory=t.databaseDirectory,this.useFsync=t.durableWrites??!1,t.dictionaries&&Object.entries(t.dictionaries).forEach(([e,r])=>{let i=ue(r);this.dictionaries.set(e,i)}),Me(this.databaseDirectory)||Br(this.databaseDirectory,{recursive:!0})}async start(){try{let t=zr(this.databaseDirectory);for(let e of t)!e.endsWith(".tmp")&&!e.startsWith(".")&&await this.loadTable(e)}catch(t){throw console.error("Failed to start database:",t),t}}async write(t,e,r,i,s){if(W(t),pe(e),Qe(r),s&&!this.dictionaries.has(s))throw new Error(`Dictionary "${s}" not found. Available dictionaries: ${Array.from(this.dictionaries.keys()).join(", ")||"none"}`);try{this.data.has(t)||this.data.set(t,new Map);let o=this.data.get(t),a=(o.get(e)?.version||0)+1,c={value:r,version:a,timestamp:Date.now(),expiration:i||null,dictionaryName:s||void 0};return o.set(e,c),await this.persistTable(t),!0}catch(o){return console.error(`Write failed for ${t}:${e}:`,o),!1}}async get(t,e){W(t),e!==void 0&&pe(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r)return e?void 0:[];if(e!==void 0){let o=r.get(e);if(!o)return;if(this.isExpired(o)){r.delete(e),await this.persistTable(t);return}return o.value}let i=[],s=[];for(let[o,n]of r.entries())this.isExpired(n)?s.push(o):i.push([o,n.value]);if(s.length>0){for(let o of s)r.delete(o);await this.persistTable(t)}return i}catch(r){return console.error(`Read failed for ${t}:${e}:`,r),e?void 0:[]}}async delete(t,e){W(t),pe(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r||!r.has(e))return!1;let i=r.get(e);return i?this.isExpired(i)?(r.delete(e),await this.persistTable(t),!1):(r.delete(e),await this.persistTable(t),!0):!1}catch(r){return console.error(`Delete failed for ${t}:${e}:`,r),!1}}async getTableSize(t){W(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=0,i=[];for(let[s,o]of e.entries())this.isExpired(o)?i.push(s):r++;if(i.length>0){for(let s of i)e.delete(s);await this.persistTable(t)}return r}catch(e){return console.error(`Get table size failed for ${t}:`,e),0}}isExpired(t){return t.expiration===null?!1:Date.now()>t.expiration}async cleanupExpired(t){W(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=[];for(let[i,s]of e.entries())this.isExpired(s)&&r.push(i);for(let i of r)e.delete(i);return r.length>0&&await this.persistTable(t),r.length}catch(e){return console.error(`Cleanup failed for ${t}:`,e),0}}async cleanupAllExpired(){let t=0;for(let e of this.data.keys())t+=await this.cleanupExpired(e);return t}async deleteTable(t){W(t);try{this.data.delete(t);let e=Oe(this.databaseDirectory,t);return Me(e)&&await et(e),!0}catch(e){return console.error(`Delete table failed for ${t}:`,e),!1}}listTables(){return Array.from(this.data.keys())}async flush(){try{let t=Array.from(this.data.keys()).map(e=>this.persistTable(e));await Promise.all(t)}catch(t){throw console.error("Flush failed:",t),t}}async close(){await this.flush()}addDictionary(t,e){let r=ue(e);this.dictionaries.set(t,r)}removeDictionary(t){return this.dictionaries.delete(t)}listDictionaries(){return Array.from(this.dictionaries.keys())}async loadTable(t){let e=Oe(this.databaseDirectory,t);if(!Me(e)){this.data.set(t,new Map);return}try{let r=await Wr(e);if(r.length===0){this.data.set(t,new Map);return}let i=this.deserializeTable(r);this.data.set(t,i)}catch(r){console.error(`Failed to load table ${t}:`,r),this.data.set(t,new Map)}}async persistTable(t){let e=this.data.get(t);if(!e)return;let r=this.serializeTable(e),i=Oe(this.databaseDirectory,t),s=`${i}.tmp.${Date.now()}.${Math.random().toString(36).substring(7)}`;try{if(await Kr(s,r),this.useFsync){let o=await Jr(s,"r+");try{await o.sync()}finally{await o.close()}}if(await Gr(s,i),this.useFsync){let o=Xr(i),n=qr(o,"r");try{_r(n)}catch{}}}catch(o){try{await et(s)}catch{}throw o}}serializeTable(t){let e=Array.from(t.entries()).map(([r,i])=>{let s=i.dictionaryName?this.dictionaries.get(i.dictionaryName):void 0,o={d:s?Z(i.value,s.deflate):i.value,v:i.version,t:i.timestamp,x:i.expiration};return i.dictionaryName&&(o.n=i.dictionaryName),[r,o]});return Buffer.from(JSON.stringify(e),"utf8")}deserializeTable(t){let r=JSON.parse(t.toString("utf8")).map(([i,s])=>{let o=s.n,n=o?this.dictionaries.get(o):void 0;return[i,{value:n?Z(s.d,n.inflate):s.d,version:s.v,timestamp:s.t,expiration:s.x,dictionaryName:o||void 0}]});return new Map(r)}};import{join as ki}from"node:path";import wi from"node:http";import{join as De}from"node:path";var tt=class{requests=new Map;limit;windowMs;constructor(t=100,e=60){this.limit=t,this.windowMs=e*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(t){let e=Date.now(),r=t||"unknown",i=this.requests.get(r);return(!i||i.resetTime<e)&&(i={count:0,resetTime:e+this.windowMs},this.requests.set(r,i)),i.count++,i.count<=this.limit}getRemainingRequests(t){let e=Date.now(),r=t||"unknown",i=this.requests.get(r);return!i||i.resetTime<e?this.limit:Math.max(0,this.limit-i.count)}getResetTime(t){let e=Date.now(),r=t||"unknown",i=this.requests.get(r);return!i||i.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(i.resetTime/1e3)}cleanup(){let t=Date.now();for(let[e,r]of this.requests.entries())r.resetTime<t&&this.requests.delete(e)}};import{URL as Yr}from"url";var rt=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(t){return this.globalMiddlewares.push(t),this}get(t,...e){let r=e.pop();return this.register("GET",t,r,e)}post(t,...e){let r=e.pop();return this.register("POST",t,r,e)}put(t,...e){let r=e.pop();return this.register("PUT",t,r,e)}delete(t,...e){let r=e.pop();return this.register("DELETE",t,r,e)}patch(t,...e){let r=e.pop();return this.register("PATCH",t,r,e)}any(t,...e){let r=e.pop(),i=e;return this.register("GET",t,r,i),this.register("POST",t,r,i),this.register("PUT",t,r,i),this.register("DELETE",t,r,i),this.register("PATCH",t,r,i),this.register("OPTIONS",t,r,i),this}options(t,...e){let r=e.pop();return this.register("OPTIONS",t,r,e)}match(t,e){for(let r of this.routes){if(r.method!==t)continue;let i=this.pathPatterns.get(r.path);if(!i)continue;let s=i.pattern.exec(e);if(!s)continue;let o={};return i.paramNames.forEach((n,a)=>{o[n]=s[a+1]||""}),{route:r,params:o}}return null}async handle(t,e){let r=t.method||"GET",i=new Yr(t.url||"/",`http://${t.headers.host}`),s=i.pathname,o=this.match(r,s);if(!o)return null;let{route:n,params:a}=o,c={};i.searchParams.forEach((u,f)=>{c[f]=u});let d={req:t,res:e,params:a,query:c,body:t.body||{},headers:t.headers,path:s,state:{},raw:()=>e,binary:(u,f="application/octet-stream",v=200)=>({statusCode:v,body:u,headers:{"Content-Type":f,"Content-Length":u.length.toString()},isRaw:!0}),text:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"text/plain"}}),form:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"application/json"}}),html:(u,f=200)=>({statusCode:f,body:u,headers:{"Content-Type":"text/html"}}),redirect:(u,f=302)=>({statusCode:f,body:null,headers:{Location:u}}),status:function(u){return{raw:()=>e,binary:(f,v="application/octet-stream")=>({statusCode:u,body:f,headers:{"Content-Type":v,"Content-Length":f.length.toString()},isRaw:!0}),text:f=>({statusCode:u,body:f,headers:{"Content-Type":"text/plain"}}),json:f=>({statusCode:u,body:f,headers:{"Content-Type":"application/json"}}),html:f=>({statusCode:u,body:f,headers:{"Content-Type":"text/html"}}),form:f=>({statusCode:u,body:f,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(f,v=302)=>({statusCode:v,body:null,headers:{Location:f}}),status:f=>this.status(f)}}},p=[...this.globalMiddlewares,...n.middlewares];return this.executeMiddlewareChain(d,p,n.handler)}register(t,e,r,i=[]){return this.routes.push({method:t,path:e,handler:r,middlewares:i}),this.pathPatterns.set(e,this.createPathPattern(e)),this}createPathPattern(t){let e=[],r=t.replace(/\/:[^/]+/g,i=>{let s=i.slice(2);return e.push(s),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,e.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:e}}async executeMiddlewareChain(t,e,r){let i=0,s=async()=>{if(i<e.length){let o=e[i++];return o(t,s)}return r(t)};return s()}};var he=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:Zr(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function Zr(t){return t==="true"||t===!0}var R=he(),it=t=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:R.port},{flag:"--host",path:"host",defaultValue:R.host},{flag:"--https",path:"useHttps",defaultValue:R.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:R.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:R.sslCert},{flag:"--key",path:"sslKey",defaultValue:R.sslKey},{flag:"--ca",path:"sslCa",defaultValue:R.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:R.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:R.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:R.allowedDomains,parser:q.array},{flag:"--debug",path:"debug",defaultValue:R.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:R.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:R.requestTimeout}],config:t});function st(t,e){let r=e.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let i=r[1]||r[2],s=Buffer.from(`--${i}`),o=Buffer.from(`--${i}--`),n={},a={},c=Qr(t,s);for(let d of c){if(d.length===0||d.equals(o.subarray(s.length)))continue;let p=ei(d);if(!p)continue;let{name:u,filename:f,contentType:v,data:T}=p;if(f){let S={filename:f,contentType:v||"application/octet-stream",data:T,size:T.length};a[u]?Array.isArray(a[u])?a[u].push(S):a[u]=[a[u],S]:a[u]=S}else{let S=T.toString("utf8");n[u]?Array.isArray(n[u])?n[u].push(S):n[u]=[n[u],S]:n[u]=S}}return{fields:n,files:a}}function Qr(t,e){let r=[],i=0;for(;i<t.length;){let s=t.indexOf(e,i);if(s===-1)break;i!==s&&r.push(t.subarray(i,s)),i=s+e.length,i<t.length&&t[i]===13&&t[i+1]===10&&(i+=2)}return r}function ei(t){let e=Buffer.from(`\r
|
|
111
|
+
`},hs=class{constructor(t){this.options=t}sentEmails=[];async sendMail(t){this.sentEmails.push(t),this.options?.logToConsole&&(console.log("Email sent:"),console.log(`From: ${t.from}`),console.log(`To: ${t.to}`),console.log(`Subject: ${t.subject}`),console.log(`Text: ${t.text}`)),this.options?.onSend&&this.options.onSend(t)}getSentEmails(){return[...this.sentEmails]}clearSentEmails(){this.sentEmails=[]}},ms=class{data=new Map;collections=new Map;expiryEmitter=new cs;expiryCheckInterval;constructor(t=1e3){this.expiryCheckInterval=setInterval(()=>this.checkExpiredItems(),t)}destroy(){clearInterval(this.expiryCheckInterval),this.data.clear(),this.collections.clear(),this.expiryEmitter.removeAllListeners()}checkExpiredItems(){let t=Date.now();for(let[e,r]of this.data.entries())r.expiry&&r.expiry<t&&(this.data.delete(e),this.expiryEmitter.emit("expired",e));for(let[e,r]of this.collections.entries())r.expiry&&r.expiry<t&&(this.collections.delete(e),this.expiryEmitter.emit("expired",e))}async set(t,e,r){let s=r?Date.now()+r*1e3:null;this.data.set(t,{value:e,expiry:s})}async get(t){let e=this.data.get(t);return e?e.expiry&&e.expiry<Date.now()?(this.data.delete(t),null):e.value:null}async delete(t){this.data.delete(t),this.collections.delete(t)}async addToCollection(t,e,r){this.collections.has(t)||this.collections.set(t,{items:[],expiry:r?Date.now()+r*1e3:null});let s=this.collections.get(t);s&&(r&&(s.expiry=Date.now()+r*1e3),s.items.push(e))}async removeFromCollection(t,e){let r=this.collections.get(t);r&&(r.items=r.items.filter(s=>s!==e))}async getCollection(t){let e=this.collections.get(t);return e?[...e.items]:[]}async getCollectionSize(t){let e=this.collections.get(t);return e?e.items.length:0}async removeOldestFromCollection(t){let e=this.collections.get(t);return!e||e.items.length===0?null:e.items.shift()||null}async findKeys(t){let e=t.replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),s=Array.from(this.data.keys()).filter(o=>r.test(o)),i=Array.from(this.collections.keys()).filter(o=>r.test(o));return[...new Set([...s,...i])]}};function mt(t){if(!t||t.trim()===""||(t.match(/@/g)||[]).length!==1)return!1;let[e,r]=t.split("@");return!(!e||!r||t.includes("..")||!fs(e)||!gs(r))}function fs(t){return t.startsWith('"')&&t.endsWith('"')?!t.slice(1,-1).includes('"'):t.length>64||t.startsWith(".")||t.endsWith(".")?!1:/^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$/.test(t)}function gs(t){if(t.startsWith("[")&&t.endsWith("]")){let r=t.slice(1,-1);return r.startsWith("IPv6:")?vs(r.slice(5)):ys(r)}let e=t.split(".");if(e.length===0)return!1;for(let r of e)if(!r||r.length>63||!/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(r))return!1;if(e.length>1){let r=e[e.length-1];if(!/^[a-zA-Z]{2,}$/.test(r))return!1}return!0}function ys(t){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/.test(t)}function vs(t){if(!/^[a-fA-F0-9:]+$/.test(t))return!1;let e=t.split(":");return!(e.length<2||e.length>8)}var A=Be(),ws=t=>{let e={configFilePath:"mikroauth.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:A.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:A.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:A.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:A.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:A.auth.maxActiveSessions},{flag:"--appUrl",path:"auth.appUrl",defaultValue:A.auth.appUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:A.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:A.email.host},{flag:"--emailUser",path:"email.user",defaultValue:A.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:A.email.password},{flag:"--emailPort",path:"email.port",defaultValue:A.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:A.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:A.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:A.email.debug},{flag:"--dir",path:"storage.databaseDirectory",defaultValue:A.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:A.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:A.storage.debug},{flag:"--port",path:"server.port",defaultValue:A.server.port},{flag:"--host",path:"server.host",defaultValue:A.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:A.server.useHttps},{flag:"--https",path:"server.useHttp2",isFlag:!0,defaultValue:A.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:A.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:A.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:A.server.sslCa},{flag:"--ratelimit",path:"server.rateLimit.enabled",defaultValue:A.server.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"server.rateLimit.requestsPerMinute",defaultValue:A.server.rateLimit.requestsPerMinute},{flag:"--allowed",path:"server.allowedDomains",defaultValue:A.server.allowedDomains,parser:Y.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:A.server.debug}]};return t&&(e.config=t),e},ge={linkSent:"If a matching account was found, a magic link has been sent.",revokedSuccess:"All other sessions revoked successfully.",logoutSuccess:"Logged out successfully."},ft=class{config;email;storage;jwtService;templates;constructor(t,e,r){let s=new X(ws({auth:t.auth,email:t.email})).get();s.auth.debug&&console.log("Using configuration:",s),this.config=s,this.email=e||new hs,this.storage=r||new ms,this.jwtService=new us(s.auth.jwtSecret),this.templates=new ds(s?.auth.templates),this.checkIfUsingDefaultCredentialsInProduction()}checkIfUsingDefaultCredentialsInProduction(){process.env.NODE_ENV==="production"&&this.config.auth.jwtSecret===Be().auth.jwtSecret&&(console.error("WARNING: Using default secrets in production environment!"),process.exit(1))}generateToken(t){let e=Date.now().toString(),r=Z.randomBytes(32).toString("hex");return Z.createHash("sha256").update(`${t}:${e}:${r}`).digest("hex")}generateJsonWebToken(t){return this.jwtService.sign({sub:t.id,email:t.email,username:t.username,role:t.role,exp:Math.floor(Date.now()/1e3)+3600*24})}generateRefreshToken(){return Z.randomBytes(40).toString("hex")}async trackSession(t,e,r){let s=`sessions:${t}`;if(await this.storage.getCollectionSize(s)>=this.config.auth.maxActiveSessions){let i=await this.storage.removeOldestFromCollection(s);i&&await this.storage.delete(`refresh:${i}`)}await this.storage.addToCollection(s,e,this.config.auth.refreshTokenExpirySeconds),await this.storage.set(`refresh:${e}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds)}generateMagicLinkUrl(t){let{token:e,email:r,appUrl:s}=t,i=s||this.config.auth.appUrl;try{return new as(i),`${i}?token=${encodeURIComponent(e)}&email=${encodeURIComponent(r)}`}catch{throw new Error("Invalid base URL configuration")}}async createMagicLink(t){let{email:e,ip:r,metadata:s,appUrl:i,subject:o}=t;if(!mt(e))throw new Error("Valid email required");try{let n=this.generateToken(e),a=`magic_link:${n}`,c={email:e,ipAddress:r||"unknown",createdAt:Date.now()};await this.storage.set(a,JSON.stringify(c),this.config.auth.magicLinkExpirySeconds);let l=await this.storage.findKeys("magic_link:*");for(let h of l){if(h===a)continue;let v=await this.storage.get(h);if(v)try{JSON.parse(v).email===e&&await this.storage.delete(h)}catch{}}let p=this.generateMagicLinkUrl({token:n,email:e,appUrl:i}),u=Math.ceil(this.config.auth.magicLinkExpirySeconds/60);return await this.email.sendMail({from:this.config.email.user,to:e,subject:o||this.config.email.emailSubject,text:this.templates.getText(p,u,s),html:this.templates.getHtml(p,u,s)}),{message:ge.linkSent}}catch(n){throw console.error(`Failed to process magic link request: ${n}`),new Error("Failed to process magic link request")}}async createToken(t){let{email:e,username:r,role:s,ip:i}=t;if(!mt(e))throw new Error("Valid email required");try{let o=Z.randomBytes(16).toString("hex"),n=this.generateRefreshToken(),a=Date.now(),c={sub:e,username:r,role:s,jti:o,lastLogin:a,metadata:{ip:i||"unknown"},exp:Math.floor(Date.now()/1e3)+3600*24},l=this.jwtService.sign(c,{exp:this.config.auth.jwtExpirySeconds}),p={email:e,username:r,role:s,ipAddress:i||"unknown",tokenId:o,createdAt:a,lastLogin:a};return await this.trackSession(e,n,p),{accessToken:l,refreshToken:n,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(o){throw console.error("Token creation error:",o),new Error("Token creation failed")}}async verifyToken(t){let{token:e,email:r}=t;try{let s=`magic_link:${e}`,i=await this.storage.get(s);if(!i)throw new Error("Invalid or expired token");let o=JSON.parse(i);if(o.email!==r)throw new Error("Email mismatch");let n=o.username,a=o.role;await this.storage.delete(s);let c=Z.randomBytes(16).toString("hex"),l=this.generateRefreshToken(),p={sub:r,username:n,role:a,jti:c,lastLogin:o.createdAt,metadata:{ip:o.ipAddress},exp:Math.floor(Date.now()/1e3)+3600*24},u=this.jwtService.sign(p,{exp:this.config.auth.jwtExpirySeconds});return await this.trackSession(r,l,{...o,tokenId:c,createdAt:Date.now()}),{accessToken:u,refreshToken:l,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(s){throw console.error("Token verification error:",s),new Error("Verification failed")}}async refreshAccessToken(t){try{let e=await this.storage.get(`refresh:${t}`);if(!e)throw new Error("Invalid or expired refresh token");let r=JSON.parse(e),s=r.email;if(!s)throw new Error("Invalid refresh token data");let i=r.username,o=r.role,n=Z.randomBytes(16).toString("hex"),a={sub:s,username:i,role:o,jti:n,lastLogin:r.lastLogin||r.createdAt,metadata:{ip:r.ipAddress}},c=this.jwtService.sign(a,{exp:this.config.auth.jwtExpirySeconds});return r.lastUsed=Date.now(),await this.storage.set(`refresh:${t}`,JSON.stringify(r),this.config.auth.refreshTokenExpirySeconds),{accessToken:c,refreshToken:t,exp:this.config.auth.jwtExpirySeconds,tokenType:"Bearer"}}catch(e){throw console.error("Token refresh error:",e),new Error("Token refresh failed")}}verify(t){try{return this.jwtService.verify(t)}catch{throw new Error("Invalid token")}}async logout(t){try{if(!t||typeof t!="string")throw new Error("Refresh token is required");let e=await this.storage.get(`refresh:${t}`);if(!e)return{message:ge.logoutSuccess};let r=JSON.parse(e).email;if(!r)throw new Error("Invalid refresh token data");await this.storage.delete(`refresh:${t}`);let s=`sessions:${r}`;return await this.storage.removeFromCollection(s,t),{message:ge.logoutSuccess}}catch(e){throw console.error("Logout error:",e),new Error("Logout failed")}}async getSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,s=`sessions:${e}`,i=(await this.storage.getCollection(s)).map(async n=>{try{let a=await this.storage.get(`refresh:${n}`);if(!a)return await this.storage.removeFromCollection(s,n),null;let c=JSON.parse(a);return{id:`${n.substring(0,8)}...`,createdAt:c.createdAt||0,lastLogin:c.lastLogin||c.createdAt||0,lastUsed:c.lastUsed||c.createdAt||0,metadata:{ip:c.ipAddress},isCurrentSession:n===r}}catch{return await this.storage.removeFromCollection(s,n),null}}),o=(await Promise.all(i)).filter(Boolean);return o.sort((n,a)=>a.createdAt-n.createdAt),{sessions:o}}catch(e){throw console.error("Get sessions error:",e),new Error("Failed to fetch sessions")}}async revokeSessions(t){try{if(!t.user?.email)throw new Error("User not authenticated");let e=t.user.email,r=t.body?.refreshToken,s=`sessions:${e}`,i=await this.storage.getCollection(s);for(let o of i)r&&o===r||await this.storage.delete(`refresh:${o}`);return await this.storage.delete(s),r&&await this.storage.get(`refresh:${r}`)&&await this.storage.addToCollection(s,r,this.config.auth.refreshTokenExpirySeconds),{message:ge.revokedSuccess}}catch(e){throw console.error("Revoke sessions error:",e),new Error("Failed to revoke sessions")}}authenticate(t,e){try{let r=t.headers?.authorization;if(!r||!r.startsWith("Bearer "))throw new Error("Authentication required");let s=r.split(" ")[1];try{let i=this.verify(s);t.user={email:i.sub},e()}catch{throw new Error("Invalid or expired token")}}catch(r){e(r)}}},Ss=class{key;algorithm="aes-256-gcm";keyLength=32;constructor(t){this.key=ss(t,"mikroauth-salt",this.keyLength)}encrypt(t){let e=is(12),r=os(this.algorithm,this.key,e),s=Buffer.concat([r.update(t,"utf8"),r.final()]),i=r.getAuthTag();return`${e.toString("hex")}:${i.toString("hex")}:${s.toString("hex")}`}decrypt(t){let e=t.split(":");if(e.length!==3)throw new Error("Invalid encrypted data format");let[r,s,i]=e,o=Buffer.from(r,"hex"),n=Buffer.from(s,"hex"),a=Buffer.from(i,"hex"),c=ns(this.algorithm,this.key,o);return c.setAuthTag(n),Buffer.concat([c.update(a),c.final()]).toString("utf8")}},gt=class{db;encryption;PREFIX_KV="kv:";PREFIX_COLLECTION="coll:";TABLE_NAME="mikroauth";constructor(t,e){this.db=t,e&&(this.encryption=new Ss(e))}async start(){await this.db.start()}async close(){await this.db.close()}async set(t,e,r){let s=`${this.PREFIX_KV}${t}`,i=this.encryption?this.encryption.encrypt(e):e,o=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,s,i,o)}async get(t){let e=`${this.PREFIX_KV}${t}`,r=await this.db.get(this.TABLE_NAME,e);return r?this.encryption?this.encryption.decrypt(r):r:null}async delete(t){let e=`${this.PREFIX_KV}${t}`;await this.db.delete(this.TABLE_NAME,e)}async addToCollection(t,e,r){let s=`${this.PREFIX_COLLECTION}${t}`,i=await this.db.get(this.TABLE_NAME,s),o=[];if(i){let l=this.encryption?this.encryption.decrypt(i):i;o=JSON.parse(l)}o.includes(e)||o.push(e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n,c=r?Date.now()+r*1e3:void 0;await this.db.write(this.TABLE_NAME,s,a,c)}async removeFromCollection(t,e){let r=`${this.PREFIX_COLLECTION}${t}`,s=await this.db.get(this.TABLE_NAME,r);if(!s)return;let i=this.encryption?this.encryption.decrypt(s):s,o=JSON.parse(i);o=o.filter(c=>c!==e);let n=JSON.stringify(o),a=this.encryption?this.encryption.encrypt(n):n;await this.db.write(this.TABLE_NAME,r,a)}async getCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return[];let s=this.encryption?this.encryption.decrypt(r):r;return JSON.parse(s)}async getCollectionSize(t){return(await this.getCollection(t)).length}async removeOldestFromCollection(t){let e=`${this.PREFIX_COLLECTION}${t}`,r=await this.db.get(this.TABLE_NAME,e);if(!r)return null;let s=this.encryption?this.encryption.decrypt(r):r,i=JSON.parse(s);if(i.length===0)return null;let o=i.shift(),n=JSON.stringify(i),a=this.encryption?this.encryption.encrypt(n):n;return await this.db.write(this.TABLE_NAME,e,a),o}async findKeys(t){let e=t.replace(/\./g,"\\.").replace(/\*/g,".*").replace(/\?/g,"."),r=new RegExp(`^${e}$`),s=await this.db.get(this.TABLE_NAME);return Array.isArray(s)?s.filter(i=>{let o=i[0];return typeof o=="string"&&o.startsWith(this.PREFIX_KV)}).map(i=>i[0].substring(this.PREFIX_KV.length)).filter(i=>r.test(i)):[]}},yt=class{email;sender;constructor(t){this.sender=t.user,this.email=new Fe({config:t})}async sendMail(t){await this.email.send({from:this.sender,to:t.to,cc:t.cc,bcc:t.bcc,subject:t.subject,text:t.text,html:t.html})}};function ye(t){if(!t.deflate&&!t.inflate)throw new Error("Dictionary must provide either deflate or inflate mapping");if(t.deflate&&t.inflate)throw new Error("Dictionary should provide only one of deflate or inflate (not both). The inverse will be auto-generated.");return t.deflate?{deflate:t.deflate,inflate:vt(t.deflate)}:{deflate:vt(t.inflate),inflate:t.inflate}}function vt(t){let e={};for(let[r,s]of Object.entries(t))e[s]=r;return e}function se(t,e){if(t==null||typeof t!="object")return t;if(Array.isArray(t))return t.map(s=>se(s,e));let r={};for(let[s,i]of Object.entries(t)){let o=e[s]||s;r[o]=se(i,e)}return r}function Q(t){if(!t||typeof t!="string")throw new Error("Table name must be a non-empty string");if(t.length>255)throw new Error("Table name must not exceed 255 characters");if(t.includes("/")||t.includes("\\"))throw new Error("Table name must not contain path separators");if(t.includes(".."))throw new Error('Table name must not contain ".."');if(t.startsWith("."))throw new Error('Table name must not start with "."');if(t.includes("\0"))throw new Error("Table name must not contain null bytes");if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(t.toUpperCase()))throw new Error(`Table name "${t}" is reserved by the filesystem`)}function ve(t){if(t==null)throw new Error("Key must be defined");if(typeof t!="string")throw new Error("Key must be a string");if(t.length===0)throw new Error("Key must not be empty");if(t.length>1024)throw new Error("Key must not exceed 1024 characters");if(t.includes("\0"))throw new Error("Key must not contain null bytes")}function wt(t){if(t===void 0)throw new Error("Value must not be undefined (use null instead)");let e=typeof t;if(e==="function")throw new Error("Value must be JSON-serializable: functions are not supported");if(e==="symbol")throw new Error("Value must be JSON-serializable: symbols are not supported");try{if(JSON.stringify(t)===void 0)throw new Error("Value must be JSON-serializable: value cannot be serialized")}catch(r){throw new Error(`Value must be JSON-serializable: ${r instanceof Error?r.message:String(r)}`)}}import{existsSync as _e,mkdirSync as bs,readdirSync as xs,openSync as Cs,closeSync as Is}from"fs";import{readFile as As,writeFile as Es,rename as ks,unlink as St,open as Ps}from"fs/promises";import{join as ze,dirname as Ts}from"path";var ee=class{data=new Map;databaseDirectory;dictionaries=new Map;useFsync;constructor(t){this.databaseDirectory=t.databaseDirectory,this.useFsync=t.durableWrites??!1,t.dictionaries&&Object.entries(t.dictionaries).forEach(([e,r])=>{let s=ye(r);this.dictionaries.set(e,s)}),_e(this.databaseDirectory)||bs(this.databaseDirectory,{recursive:!0})}async start(){try{let t=xs(this.databaseDirectory);for(let e of t)!e.endsWith(".tmp")&&!e.startsWith(".")&&await this.loadTable(e)}catch(t){throw console.error("Failed to start database:",t),t}}async write(t,e,r,s,i){if(Q(t),ve(e),wt(r),i&&!this.dictionaries.has(i))throw new Error(`Dictionary "${i}" not found. Available dictionaries: ${Array.from(this.dictionaries.keys()).join(", ")||"none"}`);try{this.data.has(t)||this.data.set(t,new Map);let o=this.data.get(t),a=(o.get(e)?.version||0)+1,c={value:r,version:a,timestamp:Date.now(),expiration:s||null,dictionaryName:i||void 0};return o.set(e,c),await this.persistTable(t),!0}catch(o){return console.error(`Write failed for ${t}:${e}:`,o),!1}}async get(t,e){Q(t),e!==void 0&&ve(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r)return e?void 0:[];if(e!==void 0){let o=r.get(e);if(!o)return;if(this.isExpired(o)){r.delete(e),await this.persistTable(t);return}return o.value}let s=[],i=[];for(let[o,n]of r.entries())this.isExpired(n)?i.push(o):s.push([o,n.value]);if(i.length>0){for(let o of i)r.delete(o);await this.persistTable(t)}return s}catch(r){return console.error(`Read failed for ${t}:${e}:`,r),e?void 0:[]}}async delete(t,e){Q(t),ve(e);try{this.data.has(t)||await this.loadTable(t);let r=this.data.get(t);if(!r||!r.has(e))return!1;let s=r.get(e);return s?this.isExpired(s)?(r.delete(e),await this.persistTable(t),!1):(r.delete(e),await this.persistTable(t),!0):!1}catch(r){return console.error(`Delete failed for ${t}:${e}:`,r),!1}}async getTableSize(t){Q(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=0,s=[];for(let[i,o]of e.entries())this.isExpired(o)?s.push(i):r++;if(s.length>0){for(let i of s)e.delete(i);await this.persistTable(t)}return r}catch(e){return console.error(`Get table size failed for ${t}:`,e),0}}isExpired(t){return t.expiration===null?!1:Date.now()>t.expiration}async cleanupExpired(t){Q(t);try{this.data.has(t)||await this.loadTable(t);let e=this.data.get(t);if(!e)return 0;let r=[];for(let[s,i]of e.entries())this.isExpired(i)&&r.push(s);for(let s of r)e.delete(s);return r.length>0&&await this.persistTable(t),r.length}catch(e){return console.error(`Cleanup failed for ${t}:`,e),0}}async cleanupAllExpired(){let t=0;for(let e of this.data.keys())t+=await this.cleanupExpired(e);return t}async deleteTable(t){Q(t);try{this.data.delete(t);let e=ze(this.databaseDirectory,t);return _e(e)&&await St(e),!0}catch(e){return console.error(`Delete table failed for ${t}:`,e),!1}}listTables(){return Array.from(this.data.keys())}async flush(){try{let t=Array.from(this.data.keys()).map(e=>this.persistTable(e));await Promise.all(t)}catch(t){throw console.error("Flush failed:",t),t}}async close(){await this.flush()}addDictionary(t,e){let r=ye(e);this.dictionaries.set(t,r)}removeDictionary(t){return this.dictionaries.delete(t)}listDictionaries(){return Array.from(this.dictionaries.keys())}async loadTable(t){let e=ze(this.databaseDirectory,t);if(!_e(e)){this.data.set(t,new Map);return}try{let r=await As(e);if(r.length===0){this.data.set(t,new Map);return}let s=this.deserializeTable(r);this.data.set(t,s)}catch(r){console.error(`Failed to load table ${t}:`,r),this.data.set(t,new Map)}}async persistTable(t){let e=this.data.get(t);if(!e)return;let r=this.serializeTable(e),s=ze(this.databaseDirectory,t),i=`${s}.tmp.${Date.now()}.${Math.random().toString(36).substring(7)}`;try{if(await Es(i,r),this.useFsync){let o=await Ps(i,"r+");try{await o.sync()}finally{await o.close()}}if(await ks(i,s),this.useFsync){let o=Ts(s),n=Cs(o,"r");try{Is(n)}catch{}}}catch(o){try{await St(i)}catch{}throw o}}serializeTable(t){let e=Array.from(t.entries()).map(([r,s])=>{let i=s.dictionaryName?this.dictionaries.get(s.dictionaryName):void 0,o={d:i?se(s.value,i.deflate):s.value,v:s.version,t:s.timestamp,x:s.expiration};return s.dictionaryName&&(o.n=s.dictionaryName),[r,o]});return Buffer.from(JSON.stringify(e),"utf8")}deserializeTable(t){let r=JSON.parse(t.toString("utf8")).map(([s,i])=>{let o=i.n,n=o?this.dictionaries.get(o):void 0;return[s,{value:n?se(i.d,n.inflate):i.d,version:i.v,timestamp:i.t,expiration:i.x,dictionaryName:o||void 0}]});return new Map(r)}};import{join as yi}from"node:path";import pi from"node:http";import{join as it}from"node:path";var bt=class{requests=new Map;limit;windowMs;constructor(t=100,e=60){this.limit=t,this.windowMs=e*1e3,setInterval(()=>this.cleanup(),this.windowMs)}getLimit(){return this.limit}isAllowed(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return(!s||s.resetTime<e)&&(s={count:0,resetTime:e+this.windowMs},this.requests.set(r,s)),s.count++,s.count<=this.limit}getRemainingRequests(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?this.limit:Math.max(0,this.limit-s.count)}getResetTime(t){let e=Date.now(),r=t||"unknown",s=this.requests.get(r);return!s||s.resetTime<e?Math.floor((e+this.windowMs)/1e3):Math.floor(s.resetTime/1e3)}cleanup(){let t=Date.now();for(let[e,r]of this.requests.entries())r.resetTime<t&&this.requests.delete(e)}};import{URL as Rs}from"url";var xt=class{routes=[];globalMiddlewares=[];pathPatterns=new Map;use(t){return this.globalMiddlewares.push(t),this}get(t,...e){let r=e.pop();return this.register("GET",t,r,e)}post(t,...e){let r=e.pop();return this.register("POST",t,r,e)}put(t,...e){let r=e.pop();return this.register("PUT",t,r,e)}delete(t,...e){let r=e.pop();return this.register("DELETE",t,r,e)}patch(t,...e){let r=e.pop();return this.register("PATCH",t,r,e)}any(t,...e){let r=e.pop(),s=e;return this.register("GET",t,r,s),this.register("POST",t,r,s),this.register("PUT",t,r,s),this.register("DELETE",t,r,s),this.register("PATCH",t,r,s),this.register("OPTIONS",t,r,s),this}options(t,...e){let r=e.pop();return this.register("OPTIONS",t,r,e)}match(t,e){for(let r of this.routes){if(r.method!==t)continue;let s=this.pathPatterns.get(r.path);if(!s)continue;let i=s.pattern.exec(e);if(!i)continue;let o={};return s.paramNames.forEach((n,a)=>{o[n]=i[a+1]||""}),{route:r,params:o}}return null}async handle(t,e){let r=t.method||"GET",s=new Rs(t.url||"/",`http://${t.headers.host}`),i=s.pathname,o=this.match(r,i);if(!o)return null;let{route:n,params:a}=o,c={};s.searchParams.forEach((u,h)=>{c[h]=u});let l={req:t,res:e,params:a,query:c,body:t.body||{},headers:t.headers,path:i,state:{},raw:()=>e,binary:(u,h="application/octet-stream",v=200)=>({statusCode:v,body:u,headers:{"Content-Type":h,"Content-Length":u.length.toString()},isRaw:!0}),text:(u,h=200)=>({statusCode:h,body:u,headers:{"Content-Type":"text/plain"}}),form:(u,h=200)=>({statusCode:h,body:u,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),json:(u,h=200)=>({statusCode:h,body:u,headers:{"Content-Type":"application/json"}}),html:(u,h=200)=>({statusCode:h,body:u,headers:{"Content-Type":"text/html"}}),redirect:(u,h=302)=>({statusCode:h,body:null,headers:{Location:u}}),status:function(u){return{raw:()=>e,binary:(h,v="application/octet-stream")=>({statusCode:u,body:h,headers:{"Content-Type":v,"Content-Length":h.length.toString()},isRaw:!0}),text:h=>({statusCode:u,body:h,headers:{"Content-Type":"text/plain"}}),json:h=>({statusCode:u,body:h,headers:{"Content-Type":"application/json"}}),html:h=>({statusCode:u,body:h,headers:{"Content-Type":"text/html"}}),form:h=>({statusCode:u,body:h,headers:{"Content-Type":"application/x-www-form-urlencoded"}}),redirect:(h,v=302)=>({statusCode:v,body:null,headers:{Location:h}}),status:h=>this.status(h)}}},p=[...this.globalMiddlewares,...n.middlewares];return this.executeMiddlewareChain(l,p,n.handler)}register(t,e,r,s=[]){return this.routes.push({method:t,path:e,handler:r,middlewares:s}),this.pathPatterns.set(e,this.createPathPattern(e)),this}createPathPattern(t){let e=[],r=t.replace(/\/:[^/]+/g,s=>{let i=s.slice(2);return e.push(i),"/([^/]+)"});return r.endsWith("/*")?(r=`${r.slice(0,-2)}(?:/(.*))?`,e.push("wildcard")):r=r.replace(/\/$/,"/?"),{pattern:new RegExp(`^${r}$`),paramNames:e}}async executeMiddlewareChain(t,e,r){let s=0,i=async()=>{if(s<e.length){let o=e[s++];return o(t,i)}return r(t)};return i()}};var we=()=>({port:Number(process.env.PORT)||3e3,host:process.env.HOST||"0.0.0.0",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",debug:js(process.env.DEBUG)||!1,maxBodySize:1048576,requestTimeout:3e4,rateLimit:{enabled:!0,requestsPerMinute:100},allowedDomains:["*"]});function js(t){return t==="true"||t===!0}var M=we(),Ct=t=>({configFilePath:"mikroserve.config.json",args:process.argv,options:[{flag:"--port",path:"port",defaultValue:M.port},{flag:"--host",path:"host",defaultValue:M.host},{flag:"--https",path:"useHttps",defaultValue:M.useHttps,isFlag:!0},{flag:"--http2",path:"useHttp2",defaultValue:M.useHttp2,isFlag:!0},{flag:"--cert",path:"sslCert",defaultValue:M.sslCert},{flag:"--key",path:"sslKey",defaultValue:M.sslKey},{flag:"--ca",path:"sslCa",defaultValue:M.sslCa},{flag:"--ratelimit",path:"rateLimit.enabled",defaultValue:M.rateLimit.enabled,isFlag:!0},{flag:"--rps",path:"rateLimit.requestsPerMinute",defaultValue:M.rateLimit.requestsPerMinute},{flag:"--allowed",path:"allowedDomains",defaultValue:M.allowedDomains,parser:Y.array},{flag:"--debug",path:"debug",defaultValue:M.debug,isFlag:!0},{flag:"--max-body-size",path:"maxBodySize",defaultValue:M.maxBodySize},{flag:"--request-timeout",path:"requestTimeout",defaultValue:M.requestTimeout}],config:t});function It(t,e){let r=e.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!r)throw new Error("Invalid multipart/form-data: missing boundary");let s=r[1]||r[2],i=Buffer.from(`--${s}`),o=Buffer.from(`--${s}--`),n={},a={},c=$s(t,i);for(let l of c){if(l.length===0||l.equals(o.subarray(i.length)))continue;let p=Os(l);if(!p)continue;let{name:u,filename:h,contentType:v,data:j}=p;if(h){let I={filename:h,contentType:v||"application/octet-stream",data:j,size:j.length};a[u]?Array.isArray(a[u])?a[u].push(I):a[u]=[a[u],I]:a[u]=I}else{let I=j.toString("utf8");n[u]?Array.isArray(n[u])?n[u].push(I):n[u]=[n[u],I]:n[u]=I}}return{fields:n,files:a}}function $s(t,e){let r=[],s=0;for(;s<t.length;){let i=t.indexOf(e,s);if(i===-1)break;s!==i&&r.push(t.subarray(s,i)),s=i+e.length,s<t.length&&t[s]===13&&t[s+1]===10&&(s+=2)}return r}function Os(t){let e=Buffer.from(`\r
|
|
112
112
|
\r
|
|
113
|
-
`),r=t.indexOf(e);if(r===-1)return null;let
|
|
114
|
-
`),a="",c="",d,p;for(let f of n){let v=f.toLowerCase();if(v.startsWith("content-disposition:")){a=f.substring(20).trim();let T=a.match(/name="([^"]+)"/);T&&(c=T[1]);let S=a.match(/filename="([^"]+)"/);S&&(d=S[1])}else v.startsWith("content-type:")&&(p=f.substring(13).trim())}if(!c)return null;let u=s;return u.length>=2&&u[u.length-2]===13&&u[u.length-1]===10&&(u=u.subarray(0,u.length-2)),{disposition:a,name:c,filename:d,contentType:p,data:u}}import{readFileSync as Q}from"fs";import Ue from"http";import ti from"http2";import ri from"https";var Le=class{config;rateLimiter;router;shutdownHandlers=[];constructor(t){let e=new z(it(t||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new rt;let r=e.rateLimit.requestsPerMinute||he().rateLimit.requestsPerMinute;this.rateLimiter=new tt(r,60),e.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(t){return this.router.use(t),this}get(t,...e){return this.router.get(t,...e),this}post(t,...e){return this.router.post(t,...e),this}put(t,...e){return this.router.put(t,...e),this}delete(t,...e){return this.router.delete(t,...e),this}patch(t,...e){return this.router.patch(t,...e),this}any(t,...e){return this.router.any(t,...e),this}options(t,...e){return this.router.options(t,...e),this}start(){let t=this.createServer(),{port:e,host:r}=this.config;return this.setupGracefulShutdown(t),t.listen(e,r,()=>{let i=t.address(),s=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${s}://${i.address!=="::"?i.address:"localhost"}:${i.port}`)}),t}createServer(){let t=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let e={key:Q(this.config.sslKey),cert:Q(this.config.sslCert),...this.config.sslCa?{ca:Q(this.config.sslCa)}:{}};return ti.createSecureServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let e={key:Q(this.config.sslKey),cert:Q(this.config.sslCert),...this.config.sslCa?{ca:Q(this.config.sslCa)}:{}};return ri.createServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}return Ue.createServer(t)}async rateLimitMiddleware(t,e){let r=t.req.socket.remoteAddress||"unknown";return t.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),t.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),t.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?e():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(t,e){let r=Date.now(),i=t.method||"UNKNOWN",s=t.url||"/unknown",o=this.config.debug;try{if(this.setCorsHeaders(e,t),this.setSecurityHeaders(e,this.config.useHttps),o&&console.log(`${i} ${s}`),t.method==="OPTIONS"){if(e instanceof Ue.ServerResponse)e.statusCode=204,e.end();else{let a=e;a.writeHead(204),a.end()}return}try{t.body=await this.parseBody(t)}catch(a){return o&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let n=await this.router.handle(t,e);return n?n._handled?void 0:this.respond(e,n):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(n){return console.error("Server error:",n),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:o?n.message:"An unexpected error occurred"}})}finally{o&&this.logDuration(r,i,s)}}logDuration(t,e,r){let i=Date.now()-t;console.log(`${e} ${r} completed in ${i}ms`)}async parseBody(t){return new Promise((e,r)=>{let i=[],s=0,o=this.config.maxBodySize,n=!1,a=null,c=this.config.debug,d=t.headers["content-type"]||"";c&&console.log("Content-Type:",d),this.config.requestTimeout>0&&(a=setTimeout(()=>{n||(n=!0,c&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let p=()=>{a&&(clearTimeout(a),a=null)};t.on("data",u=>{if(!n){if(s+=u.length,c&&console.log(`Received chunk: ${u.length} bytes, total size: ${s}`),s>o){n=!0,p(),c&&console.log(`Body size exceeded limit: ${s} > ${o}`),r(new Error("Request body too large"));return}i.push(u)}}),t.on("end",()=>{if(!n){n=!0,p(),c&&console.log(`Request body complete: ${s} bytes`);try{if(i.length>0){let u=Buffer.concat(i);if(d.includes("application/json"))try{let f=u.toString("utf8");e(JSON.parse(f))}catch(f){r(new Error(`Invalid JSON in request body: ${f.message}`))}else if(d.includes("application/x-www-form-urlencoded")){let f=u.toString("utf8"),v={};new URLSearchParams(f).forEach((T,S)=>{v[S]=T}),e(v)}else if(d.includes("multipart/form-data"))try{let f=st(u,d);e(f)}catch(f){r(new Error(`Invalid multipart form data: ${f.message}`))}else this.isBinaryContentType(d)?e(u):e(u.toString("utf8"))}else e({})}catch(u){r(new Error(`Invalid request body: ${u}`))}}}),t.on("error",u=>{n||(n=!0,p(),r(new Error(`Error reading request body: ${u.message}`)))}),t.on("close",()=>{p()})})}isBinaryContentType(t){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>t.includes(r))}setCorsHeaders(t,e){let r=e.headers.origin,{allowedDomains:i=["*"]}=this.config;!r||i.length===0||i.includes("*")?t.setHeader("Access-Control-Allow-Origin","*"):i.includes(r)&&(t.setHeader("Access-Control-Allow-Origin",r),t.setHeader("Vary","Origin")),t.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(t,e=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((e||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),t instanceof Ue.ServerResponse)Object.entries(r).forEach(([i,s])=>{t.setHeader(i,s)});else{let i=t;Object.entries(r).forEach(([s,o])=>{i.setHeader(s,o)})}}respond(t,e){let r={...e.headers||{}};(s=>typeof s.writeHead=="function"&&typeof s.end=="function")(t)?(t.writeHead(e.statusCode,r),e.body===null||e.body===void 0?t.end():e.isRaw||typeof e.body=="string"?t.end(e.body):t.end(JSON.stringify(e.body))):(console.warn("Unexpected response object type without writeHead/end methods"),t.writeHead?.(e.statusCode,r),e.body===null||e.body===void 0?t.end?.():e.isRaw||typeof e.body=="string"?t.end?.(e.body):t.end?.(JSON.stringify(e.body)))}setupGracefulShutdown(t){let e=n=>{console.log("Shutting down MikroServe server..."),n&&console.error("Error:",n),this.cleanupShutdownHandlers(),t.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(n?1:0))})},r=()=>e(),i=()=>e(),s=n=>e(n),o=n=>e(n);this.shutdownHandlers=[r,i,s,o],process.on("SIGINT",r),process.on("SIGTERM",i),process.on("uncaughtException",s),process.on("unhandledRejection",o)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[t,e,r,i]=this.shutdownHandlers;process.removeListener("SIGINT",t),process.removeListener("SIGTERM",e),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",i),this.shutdownHandlers=[]}}};import{getRandomValues as ii}from"node:crypto";var D=class{options;defaultLength=16;defaultOnlyLowerCase=!1;defaultStyle="extended";defaultUrlSafe=!0;constructor(t){if(this.options={},t)for(let[e,r]of Object.entries(t))this.options[e]=this.generateConfig(r)}add(t){if(!t?.name)throw new Error("Missing name for the ID configuration");let e=this.generateConfig(t);this.options[t.name]=e}remove(t){if(!t?.name)throw new Error("Missing name for the ID configuration");delete this.options[t.name]}create(t,e,r,i){let s=this.generateConfig({length:t,style:e,onlyLowerCase:r,urlSafe:i});return this.generateId(s)}custom(t){if(this.options[t])return this.generateId(this.options[t]);throw new Error(`No configuration found with name: ${t}`)}generateConfig(t){return{name:t?.name||"",length:t?.length||this.defaultLength,onlyLowerCase:t?.onlyLowerCase??this.defaultOnlyLowerCase,style:t?.style||this.defaultStyle,urlSafe:t?.urlSafe??this.defaultUrlSafe}}getCharacterSet(t,e,r){if(t==="hex")return e?"0123456789abcdef":"0123456789ABCDEFabcdef";if(t==="alphanumeric")return e?"abcdefghijklmnopqrstuvwxyz0123456789":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";if(t==="extended")return r?e?"abcdefghijklmnopqrstuvwxyz0123456789-._~":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~":e?"abcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:";throw new Error(`Unknown ID style "${t} provided. Must be one of "extended" (default), "alphanumeric", or "hex".`)}generateId(t){let{length:e,onlyLowerCase:r,style:i,urlSafe:s}=t;if(e<0||e===0)throw new Error("ID length cannot be negative");let o=this.getCharacterSet(i,r,s),n=(2<<Math.log(o.length-1)/Math.LN2)-1,a=Math.ceil(1.6*n*e/o.length),c="";for(;c.length<e;){let d=new Uint8Array(a);ii(d);for(let p=0;p<a;p++){let u=d[p]&n;if(u<o.length&&(c+=o[u],c.length===e))break}}return c}};import si from"node:crypto";function nt(t,e,r){if(!r)return!1;let i=typeof t=="string"?[t]:t,s=typeof e=="string"?[e]:e;if(r.roles?.some(a=>a==="administrator"||a.id==="administrator"))return!0;let n=(r.roles||[]).flatMap(a=>typeof a=="string"?[]:(a.policies||[]).flatMap(d=>d?.permissions&&d?.targets?{permissions:d.permissions,targets:d.targets}:[]));for(let a of i)for(let c of s)if(!n.some(p=>ot(p?.permissions,a)&&ot(p?.targets,c)))return!1;return!0}function ot(t,e){return!t||t.length===0?!1:t.some(r=>{if(r==="*"||r===e)return!0;if(r.endsWith("*")&&r!=="*"){let i=r.slice(0,-1);return e.startsWith(i)}return!1})}var C=class extends Error{constructor(e){super(),this.name="ValidationError",this.message=e||"Invalid input",this.cause={statusCode:400}}},ae=class extends Error{constructor(e){super(),this.name="IdentityAlreadyExistsError",this.message=e||"Identity already exists",this.cause={statusCode:400}}},F=class extends Error{constructor(e){super(),this.name="NotFoundError",this.message=e||"Resource not found",this.cause={statusCode:404}}},me=class extends Error{constructor(e){super(),this.name="InvalidInputError",this.message=e||"Invalid input",this.cause={statusCode:400}}};var fe=class extends Error{constructor(e){super(),this.name="ConfigurationError",this.message=e||"Invalid configuration",this.cause={statusCode:400}}};var ee=class extends Error{constructor(e){super(),this.name="PermissionDeniedError",this.message=e||"Permission denied",this.cause={statusCode:403}}},B=class extends Error{constructor(e){super(),this.name="AlreadyExistsError",this.message=e||"Resource already exists",this.cause={statusCode:409}}},ge=class extends Error{constructor(e){super(),this.name="PortInUseError",this.message=e||"Port already in use",this.cause={statusCode:409}}},ce=class extends Error{constructor(e){super(),this.name="RoleNotFoundError",this.message=e||"Role not found",this.cause={statusCode:404}}},ye=class extends Error{constructor(e){super(),this.name="ProtectedResourceError",this.message=e||"Cannot modify protected resource",this.cause={statusCode:403}}},ve=class extends Error{constructor(e){super(),this.name="ServiceRequestError",this.message=e||"Service request failed",this.cause={statusCode:502}}};var G=class{id;name;type;roles;metadata;constructor(e){let{id:r,name:i,type:s,roles:o,metadata:n}=this.createIdentity(e);this.id=r,this.name=i,this.type=s,this.roles=o,this.metadata=n}createIdentity(e){let r=e?.id||this.createId(),i=e?.name||"",s=e?.type||"service_account",o=e?.metadata||{},n=e?.roles||["user"];return e&&this.validate({id:r,name:i,type:s,metadata:o,roles:n}),{id:r,name:i,type:s,metadata:{...o,createdAt:new Date().toISOString()},roles:n}}changeName(e){this.name=e}changeEmail(e){this.metadata||(this.metadata={}),this.metadata.email=e}updateMetadata(e){let r=this.metadata?JSON.parse(JSON.stringify(this.metadata)):{};this.metadata={...r,...e}}updateRoles(e){this.validateRoles(e),this.roles=e}createId(){return new D().create()}isValidRoleId(e){return typeof e!="string"||e.length===0?!1:/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)}isValidType(e){return["user","service_account"].includes(e)}validate(e){let r=e.id||"",i=e.name||"",s=e.type||"",o=e.metadata||{},n=e.roles||[];if(!r)throw new C("Missing ID");if(!i)throw new C("Missing name");if(!s||!this.isValidType(s))throw new C("Missing or invalid type");if(s==="user"&&!o?.email)throw new C("Missing email for user identity");if(!n||n.length===0)throw new C("Must have at least one role");this.validateRoles(n)}validateRoles(e){(e||[]).forEach(r=>{let i=r.id||r;if(!this.isValidRoleId(i))throw new C(`Invalid role ID '${i}'`)})}can(e,r,i){return nt(e,r,i)}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.type=e.type,this.metadata=e.metadata||{},this.roles=e.roles,this}toDTO(){return{id:this.id,name:this.name,type:this.type,metadata:this.metadata,roles:this.roles}}};function at(){return[{id:"administrator",name:"Administrator",description:"Full system administrator with all permissions",policies:[{permissions:["*"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!0,audit_level:"high"}}},{id:"user",name:"User",description:"Read-only access to resources",policies:[{permissions:["*.read"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"low"}}}]}var we=class{mikroAuth;db;tableName="identity_service";initialUser;roles;identities;serviceAccountTokens;constructor(e,r,i){this.mikroAuth=e,this.db=r,this.initialUser=i,this.roles=[],this.identities=[],this.serviceAccountTokens=new Map}async start(){await this.loadState(),this.roles.find(e=>e.id==="administrator")||this.createBaseRoles(),this.identities.length===0&&this.createInitialUser()}createBaseRoles(){let e=at();for(let r of e)this.roles.push(r)}async createInitialUser(){let e=this.initialUser.email,r=this.initialUser.userName,i=await this.addUser(e,r,["administrator"]);return await this.mikroAuth.createMagicLink({email:e}),i}async getUserByEmail(e){let r=this.identities.find(i=>i.type==="user"&&i.metadata?.email===e&&i.metadata?.active!==!1);if(r)return this.enrichIdentityWithRoles(r)}async addUser(e,r,i=["user"],s=!0){if((await this.getUsers()).find(c=>c.metadata.email===e))throw new ae(`User with email ${e} already exists`);let a=new G({name:r,type:"user",metadata:{email:e,active:s},roles:i}).toDTO();return this.identities.push(a),await this.saveState(),this.enrichIdentityWithRoles(a)}async addServiceAccount(e,r,i){if((await this.getServiceAccounts()).find(c=>c.name===e))throw new ae(`Service account with name ${e} already exists`);let n=new G({name:e,type:"service_account",metadata:{description:r},roles:i}).toDTO(),a=this.generateServiceAccountToken();return this.serviceAccountTokens.set(a,n.id),this.identities.push(n),await this.saveState(),{...this.enrichIdentityWithRoles(n),apiKey:a}}async rotateServiceAccountKey(e){let r=await this.getIdentityById(e);if(!r||r.type!=="service_account")return;for(let[s,o]of this.serviceAccountTokens.entries())o===e&&this.serviceAccountTokens.delete(s);let i=this.generateServiceAccountToken();return this.serviceAccountTokens.set(i,e),await this.saveState(),i}async deleteIdentity(e){for(let[i,s]of this.serviceAccountTokens.entries())s===e&&this.serviceAccountTokens.delete(i);let r=this.identities.filter(i=>i.id!==e);this.identities=r,await this.saveState()}async updateIdentity(e,r){let i=await this.getIdentityById(e);if(!i)return;let s=new G().fromDTO(i);r?.name&&s.changeName(r.name),r?.email&&s.changeEmail(r.email),r?.roles&&s.updateRoles(r.roles),r?.metadata&&s.updateMetadata(r.metadata);let o=s.toDTO();return this.updateIdentityList(o),await this.saveState(),this.enrichIdentityWithRoles(o)}updateIdentityList(e){if(!e)throw new C("Cannot update identity list: updatedIdentity is null or undefined");let r=JSON.parse(JSON.stringify(this.identities)),i=r.find(s=>s.id===e.id);i&&Object.assign(i,e),this.identities=r}generateServiceAccountToken(){return`sa.${new D().create()}.${si.randomBytes(32).toString("hex")}`}async getUserFromToken(e){if(e&&e.startsWith("Bearer ")){let r=e.split(" ")[1];if(r.startsWith("sa.")){let s=this.serviceAccountTokens.get(r);if(s){let o=this.identities.find(n=>n.id===s&&n.type==="service_account");if(o)return this.enrichIdentityWithRoles(o)}}else try{let s=this.mikroAuth.verify(r),o=s.email||s.sub;return this.getUserByEmail(o)}catch{let o=new Error("Invalid token");throw o.cause={statusCode:401},o}}}async getIdentityById(e){return this.identities.find(r=>r.id===e)}async getUserById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="user"))return this.enrichIdentityWithRoles(r)}async getServiceAccountById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="service_account"))return this.enrichIdentityWithRoles(r)}async updateUser(e,r){let i=await this.getIdentityById(e);if(!(!i||i.type!=="user"))return this.updateIdentity(e,r)}async updateServiceAccount(e,r){let i=await this.getIdentityById(e);if(!i||i.type!=="service_account")return;let s={...r};return r.description!==void 0&&(s.metadata={...s.metadata||{},description:r.description},delete s.description),this.updateIdentity(e,s)}async deleteUser(e){let r=await this.getIdentityById(e);return!r||r.type!=="user"?!1:(await this.deleteIdentity(e),!0)}async deleteServiceAccount(e){let r=await this.getIdentityById(e);return!r||r.type!=="service_account"?!1:(await this.deleteIdentity(e),!0)}enrichIdentityWithRoles(e){let r=JSON.parse(JSON.stringify(e));return e.roles?r.roles=e.roles.map(i=>this.roles.find(s=>s.id===i)).filter(Boolean):r.roles=[],r}async getIdentities(){return this.identities}async getUsers(){return this.identities.filter(e=>e.type==="user")}async getServiceAccounts(){return this.identities.filter(e=>e.type==="service_account")}can(e,r,i){return new G(i).can(e,r,i)}async createCustomRole(e,r,i,s,o){if(this.roles.find(d=>d.id===e))throw new B(`Role with ID ${e} already exists`);let c={id:e,name:r,description:i,policies:[{permissions:s,targets:["*"]}],constraints:o||{assumable_by:{identities:[],roles:[],services:["functions"]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"medium"}}};this.roles.push(c),await this.saveState()}async updateRole(e,r){let i=this.roles.find(s=>s.id===e);if(!i)throw new ce(`Role with ID ${e} not found`);r.name!==void 0&&(i.name=r.name),r.description!==void 0&&(i.description=r.description),r.permissions!==void 0&&(i.policies=[{permissions:r.permissions,targets:["*"]}]),r.constraints!==void 0&&(i.constraints=r.constraints),await this.saveState()}async deleteRole(e){let r=this.roles.findIndex(i=>i.id===e);if(r===-1)throw new ce(`Role with ID ${e} not found`);if(e==="administrator"||e==="user")throw new ye("Cannot delete base roles");this.roles.splice(r,1),await this.saveState()}async getRoles(){return this.roles}async getRole(e){return this.roles.find(r=>r.id===e)}async loadState(){let e=await this.load("identities");e&&(this.identities=e);let r=await this.load("roles");r&&(this.roles=r);let i=await this.load("serviceAccountTokens");if(i){let s=new Map(Object.entries(i));i&&Object.keys(i).length>0&&(this.serviceAccountTokens=s)}}async saveState(){let e=Object.fromEntries(this.serviceAccountTokens);await this.write("identities",this.identities),await this.write("roles",this.roles),await this.write("serviceAccountTokens",e)}async load(e){return await this.db.get(this.tableName,e)}async write(e,r){await this.db.write(this.tableName,e,r)}};import{existsSync as oi}from"node:fs";import{spawn as ni}from"node:child_process";import{createServer as ai}from"node:net";var ct={storage:{name:"storage",path:"./storage.mjs",port:3001,prefix:"/storage",args:["--force"]},functions:{name:"functions",path:"./functions.mjs",port:3002,prefix:"/functions",args:["--force"]},sites:{name:"sites",path:"./sites.mjs",port:3003,prefix:"/sites",args:["--force"]},databases:{name:"databases",path:"./databases.mjs",port:3004,prefix:"/databases",args:["--force"]},observability:{name:"observability",path:"./observability.mjs",port:3005,prefix:"/observability",args:["--force"]}},Se=class{db;services=new Map;tableName="molnosmanagement";environmentVariables={};setEnvironmentVariables(e){this.environmentVariables={...this.environmentVariables,...e}}async isPortInUse(e){return new Promise(r=>{let i=ai();i.once("error",s=>{s.code==="EADDRINUSE"?r(!0):r(!1)}),i.once("listening",()=>{i.close(),r(!1)}),i.listen(e,"127.0.0.1")})}constructor(e){this.db=new K({databaseDirectory:e.dbPath})}getServiceDefinition(e){let r=e.toLowerCase(),i=ct[r];if(!i)throw new F(`Unknown service: ${e}. Valid services are: ${Object.keys(ct).join(", ")}`);return i}async start(){await this.db.start();let e=await this.db.get(this.tableName,"services")||[];for(let r of e)r.restartPolicy&&(r.restartPolicy.attempts=0),r.healthCheck&&(r.healthCheck.consecutiveFailures=0,r.healthCheck.consecutiveSuccesses=0,r.healthCheck.healthy=!0),this.services.set(r.name,r),r.active&&await this.startService(r.name)}async stopAllServices(){let e=[];for(let[r,i]of this.services.entries())(i.active||i.process)&&e.push(this.stopService(r));await Promise.all(e)}async shutdown(){await this.stopAllServices()}validateServiceConfig(e){if(!e.name||e.name.trim()==="")throw new C("Service name is required");if(!e.path||e.path.trim()==="")throw new C("Service path is required");if(!oi(e.path))throw new fe(`Service path does not exist: ${e.path}`);if(!e.port||e.port<1||e.port>65535)throw new C(`Invalid port: ${e.port}`);if(!e.prefix||!e.prefix.startsWith("/"))throw new C("Service prefix must start with /")}cleanServiceForStorage(e){let{process:r,...i}=e;if(i.healthCheck){let{timer:s,consecutiveFailures:o,consecutiveSuccesses:n,healthy:a,...c}=i.healthCheck;i.healthCheck=c}if(i.restartPolicy){let{attempts:s,...o}=i.restartPolicy;i.restartPolicy=o}return i}async registerService(e,r){let i;typeof e=="string"?i={...this.getServiceDefinition(e),...r}:i=e,this.validateServiceConfig(i);let s={...i,active:!1},o=await this.getServices(),n=[],a=[];if(o.forEach(d=>{n.push(d.name),a.push(d.port)}),a.includes(s.port))throw new ge(`Port ${s.port} already in use`);if(n.includes(s.name))throw new B(`Service with name '${s.name}' already exists`);s.restartPolicy&&(s.restartPolicy.attempts=0),s.healthCheck&&(s.healthCheck.consecutiveFailures=0,s.healthCheck.consecutiveSuccesses=0,s.healthCheck.healthy=!0),this.services.set(s.name,s);let c=await this.db.get(this.tableName,"services")||[];return c.push(this.cleanServiceForStorage(s)),await this.db.write(this.tableName,"services",c),s.name}async startService(e){let r=this.services.get(e);if(!r)return!1;if(r.process&&r.active)return!0;if(await this.isPortInUse(r.port))return console.error(`[ManagementService] Cannot start ${e}: Port ${r.port} is already in use. Please kill the process using this port.`),!1;try{let s={stdio:"pipe",detached:!1,env:{...process.env,...this.environmentVariables}},o=r.args||[],n=`--port=${r.port}`,a=ni("node",[r.path,n,...o],s);r.process=a,r.restartPolicy&&(r.restartPolicy.attempts=0);let c=!1;return a.stdout.on("data",d=>{let p=d.toString();console.log(`[${e}] ${p}`)}),a.stderr.on("data",d=>{let p=d.toString();console.error(`[${e}] ERROR: ${p}`),(p.includes("EADDRINUSE")||p.includes("address already in use"))&&(c=!0)}),a.on("exit",d=>{r.active=!1,r.process=void 0,c&&console.error(`[ManagementService] Service ${e} failed to start due to startup error`),this.handleServiceExit(r,d||0)}),await new Promise(d=>setTimeout(d,500)),r.process&&!r.process.killed?(r.active=!0,await this.updateServiceStatus(e,!0),r.healthCheck&&this.startHealthCheck(r),!0):(console.error(`[ManagementService] Service ${e} exited immediately after spawn`),!1)}catch(s){return console.error(`[ManagementService] Failed to start ${e}:`,s),!1}}startHealthCheck(e){let r=e.healthCheck;if(!r)return;let i=async()=>{if(e.healthCheck)try{let s=new AbortController,o=setTimeout(()=>s.abort(),e.healthCheck.timeoutMs),n=await fetch(`http://127.0.0.1:${e.port}${e.healthCheck.path}`,{signal:s.signal});clearTimeout(o),n.ok&&e.healthCheck?(e.healthCheck.consecutiveSuccesses=(e.healthCheck.consecutiveSuccesses||0)+1,e.healthCheck.consecutiveFailures=0,e.healthCheck.consecutiveSuccesses>=e.healthCheck.successThreshold&&(e.healthCheck.healthy=!0)):this.handleHealthCheckFailure(e)}catch{this.handleHealthCheckFailure(e)}};r.timer=setInterval(i,r.intervalMs)}handleHealthCheckFailure(e){e.healthCheck&&(e.healthCheck.consecutiveFailures=(e.healthCheck.consecutiveFailures||0)+1,e.healthCheck.consecutiveSuccesses=0,e.healthCheck.consecutiveFailures>=e.healthCheck.failureThreshold&&(e.healthCheck.healthy=!1,e.healthCheck.restartOnFailure&&this.restartService(e.name).catch(()=>{})))}async handleServiceExit(e,r){if(await this.updateServiceStatus(e.name,!1),!e.restartPolicy||e.restartPolicy.type==="never"||e.restartPolicy.type==="on-failure"&&r===0||e.restartPolicy.maxAttempts>0&&(e.restartPolicy.attempts||0)>=e.restartPolicy.maxAttempts)return;e.restartPolicy.attempts=(e.restartPolicy.attempts||0)+1;let i=e.restartPolicy.backoffMs;setTimeout(()=>{this.startService(e.name).catch(()=>{})},i)}async stopService(e){let r=this.services.get(e);if(!r)return!1;if(r.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),!r.process)return r.active&&(r.active=!1,await this.updateServiceStatus(e,!1)),!0;try{let i=s=>{try{return process.kill(s,0),!0}catch{return!1}};if(r.process?.pid){let s=r.process.pid;if(i(s))try{process.kill(s,"SIGTERM");let o=setTimeout(()=>{try{i(s)&&process.kill(s,"SIGKILL")}catch{}},5*1e3);await new Promise(n=>{if(!r.process){clearTimeout(o),n();return}let a=setTimeout(()=>{clearTimeout(o),n()},6*1e3);r.process.once("exit",()=>{clearTimeout(o),clearTimeout(a),n()})})}catch(o){if(o.code!=="ESRCH")throw o}}return r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1),!0}catch{return r.active&&(r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1)),!1}}async restartService(e){return await this.stopService(e),await new Promise(r=>setTimeout(r,500)),this.startService(e)}async updateServiceStatus(e,r){let i=await this.db.get(this.tableName,"services")||[],s=i.findIndex(o=>o.name===e);if(s>=0){let o=this.services.get(e);o?(o.active=r,i[s]=this.cleanServiceForStorage(o)):i[s].active=r,await this.db.write(this.tableName,"services",i)}}async getService(e){return(await this.getServices()).find(i=>i.name===e)}async getServices(){return(await this.db.get(this.tableName,"services")||[]).map(({process:r,...i})=>i)}async getServiceStats(e){let r=this.services.get(e);return r?{name:r.name,active:r.active,health:r.healthCheck?{healthy:r.healthCheck.healthy||!1,consecutiveSuccesses:r.healthCheck.consecutiveSuccesses||0,consecutiveFailures:r.healthCheck.consecutiveFailures||0}:null,restarts:r.restartPolicy&&r.restartPolicy.attempts||0}:null}async updateService(e,r){let i=this.services.get(e);if(!i)return!1;let s=i.active;Object.assign(i,r);let o=await this.db.get(this.tableName,"services")||[],n=o.findIndex(a=>a.name===e);return n>=0?(o[n]=this.cleanServiceForStorage(i),await this.db.write(this.tableName,"services",o),s!==i.active&&(i.active?await this.startService(e):await this.stopService(e)),!0):!1}async removeService(e){let r=this.services.get(e);r?.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),await this.stopService(e),this.services.delete(e);let s=(await this.db.get(this.tableName,"services")||[]).filter(o=>o.name!==e);return await this.db.write(this.tableName,"services",s),!0}};var V=class{id;name;description;redirectUris;metadata;owners;constructor(e){let r=this.createApplication(e);this.id=r.id,this.name=r.name,this.description=r.description,this.redirectUris=r.redirectUris,this.metadata=r.metadata,this.owners=r.owners}createApplication(e){let r=e?.id||this.createId(),i=e?.name||"",s=e?.description,o=e?.redirectUris||[],n=e?.owners||[],a=new Date().toISOString(),c={...e?.metadata||{},createdAt:e?.metadata?.createdAt||a,updatedAt:a,createdBy:e?.metadata?.createdBy||n[0]||""};return e&&this.validate({id:r,name:i,description:s,redirectUris:o,metadata:c,owners:n}),{id:r,name:i,description:s,redirectUris:o,metadata:c,owners:n}}update(e){e.name!==void 0&&(this.name=e.name),e.description!==void 0&&(this.description=e.description),e.redirectUris!==void 0&&(this.validateRedirectUris(e.redirectUris),this.redirectUris=e.redirectUris),e.owners!==void 0&&(this.owners=e.owners),e.metadata!==void 0?this.metadata={...this.metadata,...e.metadata,updatedAt:new Date().toISOString()}:this.metadata.updatedAt=new Date().toISOString(),this.validate(this.toDTO())}isValidRedirectUri(e){return this.redirectUris.includes(e)}isOwner(e){return this.owners.includes(e)}createId(){return new D().create()}validate(e){let{id:r,name:i,redirectUris:s,metadata:o,owners:n}=e;if(!r)throw new C("Missing ID");if(!i||i.trim().length===0)throw new C("Missing or invalid name");if(!o)throw new C("Missing metadata");if(!n||n.length===0)throw new C("Application must have at least one owner");this.validateRedirectUris(s)}validateRedirectUris(e){if(!e||e.length===0)throw new C("At least one redirect URI is required");for(let r of e)try{let i=new URL(r);if(i.protocol!=="https:"&&i.protocol!=="http:"&&i.protocol!=="custom:")throw new C(`Invalid redirect URI protocol: ${r}. Must use https:, http: (localhost only), or custom: (for native apps)`);i.protocol==="http:"&&!i.hostname.match(/^(localhost|127\.0\.0\.1|\[::1\])$/)&&console.warn(`Warning: HTTP redirect URI detected for non-localhost: ${r}. HTTPS is recommended for production.`)}catch{throw new C(`Invalid redirect URI format: ${r}`)}}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.description=e.description,this.redirectUris=e.redirectUris,this.metadata=e.metadata,this.owners=e.owners,this}toDTO(){return{id:this.id,name:this.name,description:this.description,redirectUris:this.redirectUris,metadata:this.metadata,owners:this.owners}}};var be=class{db;tableName="molnosapplications";key="applications";applications;constructor(e){this.db=new K({databaseDirectory:e.dbPath}),this.applications=[]}async start(){await this.db.start(),await this.loadState()}async loadState(){let e=await this.db.get(this.tableName,this.key)||[];this.applications=e}async saveState(){await this.db.write(this.tableName,this.key,this.applications)}async createApplication(e,r){let i=e.owners.includes(r)?e.owners:[...e.owners,r],o=new V({...e,owners:i,metadata:{...e.metadata,createdBy:r}}).toDTO();if(this.applications.find(a=>a.name===o.name))throw new B(`Application with name '${o.name}' already exists`);return this.applications.push(o),await this.saveState(),o}async getApplication(e,r){let i=this.applications.find(o=>o.id===e);if(!i)throw new F(`Application with ID '${e}' not found`);let s=new V().fromDTO(i);if(r&&!s.isOwner(r))throw new ee("Only application owners can view application details");return s.toDTO()}async getApplicationById(e){return this.applications.find(i=>i.id===e)||null}async listApplications(e){return this.applications.filter(r=>r.owners.includes(e)).map(r=>new V().fromDTO(r).toDTO())}async updateApplication(e,r,i){let s=this.applications.findIndex(a=>a.id===e);if(s===-1)throw new F(`Application with ID '${e}' not found`);let o=this.applications[s],n=new V().fromDTO(o);if(!n.isOwner(i))throw new ee("Only application owners can update the application");if(r.owners&&r.owners.length===0)throw new C("Application must have at least one owner");if(r.owners&&!r.owners.includes(i)&&o.owners.length===1&&o.owners[0]===i)throw new C("Cannot remove yourself as the last owner of the application");return n.update(r),this.applications[s]=n.toDTO(),await this.saveState(),this.applications[s]}async deleteApplication(e,r){let i=this.applications.findIndex(n=>n.id===e);if(i===-1)throw new F(`Application with ID '${e}' not found`);let s=this.applications[i];if(!new V().fromDTO(s).isOwner(r))throw new ee("Only application owners can delete the application");this.applications.splice(i,1),await this.saveState()}async validateRedirectUri(e,r){let i=await this.getApplicationById(e);return i?new V().fromDTO(i).isValidRedirectUri(r):!1}getAllApplicationsInternal(){return this.applications}};async function I(t){return t.body||{}}function m(t,e="An error occurred"){let r=t?.message||t||e,i=t?.cause?.statusCode||t?.statusCode||400;return{message:r,status:i}}async function lt(t,e,r,i){try{let s=await I(t),{email:o,redirectUrl:n,applicationId:a}=s;if(!o)return t.json({error:"Email is required"},400);let c=o.trim().toLowerCase(),d;if(n){if(!a)return t.json({error:"applicationId is required when redirectUrl is provided"},400);if(!await i.validateRedirectUri(a,n))return t.json({error:"Invalid redirectUrl or applicationId. The redirect URI must be registered for this application."},400);d=(await i.getApplicationById(a))?.name}let p=!1;return await e.getUserByEmail(c)&&(p=!0),p&&await r.createMagicLink({email:c,subject:`Sign In To ${d||"MolnOS"}`,appUrl:n,metadata:{appName:d,redirectUrl:n,applicationId:a}}),t.json({success:!0,message:"If a matching account was found, a magic link has been sent."})}catch(s){let{message:o,status:n}=m(s,"Error during login");return t.json({error:o},n)}}async function dt(t,e,r){try{let i=t.query.token,s=t.query.email;if(!i)return t.json({error:"Token is required as query parameter"},400);if(!s)return t.json({error:"Email is required as query parameter"},400);try{let o=await e.verifyToken({token:i,email:s});if(!o)return t.json({error:"Invalid or expired token"},401);let n=o.metadata;if(n?.redirectUrl&&n?.applicationId)if(await r.validateRedirectUri(n.applicationId,n.redirectUrl)){let c=new URL(n.redirectUrl);return c.searchParams.set("access_token",o.accessToken),c.searchParams.set("refresh_token",o.refreshToken),c.searchParams.set("expires_in",o.expiresIn.toString()),t.redirect(c.toString(),302)}else return console.warn(`Redirect URL validation failed for applicationId: ${n.applicationId}`),t.json({...o,warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);return t.json(o,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(i){let{message:s,status:o}=m(i,"Error verifying token");return t.json({error:s},o)}}async function ut(t,e){try{let i=(await I(t)).refreshToken;if(!i)return t.json({error:"Value for refreshToken is required"},400);try{let s=await e.refreshAccessToken(i);return t.json(s,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(r){let{message:i,status:s}=m(r,"Error refreshing token");return t.json({error:i},s)}}async function pt(t){try{let e=t.state.user;return t.json(e)}catch(e){let{message:r,status:i}=m(e,"Error getting user info");return t.json({error:r},i)}}function h(t,e,r={}){let i=Array.isArray(e)?e:[e];if(!t||!t.roles||!Array.isArray(t.roles)){let n=new Error("Unauthorized: User or roles missing");throw n.cause={statusCode:403},n}let s=t.roles.flatMap(n=>n?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(d=>typeof d=="string"?d:d&&typeof d=="object"&&d.actions?d.actions:[]))||[]);if(!i.every(n=>s.includes(n)||s.includes("*")?!0:s.some(a=>{if(a.endsWith(".*")){let c=a.slice(0,-2);return n.startsWith(`${c}.`)}return!1}))){let n=new Error("Unauthorized");throw n.cause={statusCode:403},n}return!0}async function ht(t,e){try{let r=t.state.user;h(r,"identity.identities.get",{});let i=await e.getIdentities();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting identities");return t.json({error:i},s)}}var O=class{isSilent;propertyPath="";constructor(t=!1){this.isSilent=t}test(t,e){if(!e)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:i}=this.validate(t.properties,e),s=this.compileErrors(r,i),o=this.isSuccessful(r,s);return{errors:s,success:o}}compileErrors(t,e){let r=t.filter(i=>i.success===!1);return[...e,...r].flatMap(i=>i)}isSuccessful(t,e){return t.every(r=>r.success===!0)&&e.length===0}validate(t,e,r=[],i=[]){let s=t?.additionalProperties??!0,o=t?.required||[];i=this.checkForRequiredKeysErrors(o,e,i),i=this.checkForDisallowedProperties(Object.keys(e),Object.keys(t),i,s);for(let n in t){let a=o.includes(n)&&n!=="required",c=t[n],d=e[n],p=c.additionalProperties??!0;a&&(i=this.checkForRequiredKeysErrors(c.required||[],d,i)),this.isDefined(d)&&(this.handleValidation(n,d,c,r),i=this.checkForDisallowedProperties(Object.keys(d),Object.keys(c),i,p),this.handleNestedObject(d,c,r,i))}return{results:r,errors:i}}updatePropertyPath(t,e=""){if(!t){this.propertyPath="";return}e&&(this.propertyPath=e),this.propertyPath=`${this.propertyPath}.${t}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(t){return!!(typeof t=="number"&&t===0||t||t===""||typeof t=="boolean")}checkForRequiredKeysErrors(t,e,r){if(!this.areRequiredKeysPresent(t,e)){let i=e?Object.keys(e):[],s=this.findNonOverlappingElements(t,i),o=s.length>0?`Missing the required key: '${s.join(", ")}'!`:`Missing values for required keys: '${i.filter(n=>!e[n]).join(", ")}'!`;r.push({key:"",value:e,success:!1,error:o})}return r}checkForDisallowedProperties(t,e,r,i){if(!i){let s=this.findNonOverlappingElements(t,e);s.length>0&&r.push({key:`${e}`,value:t,success:!1,error:`Has additional (disallowed) properties: '${s.join(", ")}'!`})}return r}handleValidation(t,e,r,i){this.updatePropertyPath(t);let s=this.validateProperty(this.propertyPath,r,e);i.push(...s);let o=(a,c)=>{a.forEach(d=>{let p=this.validateProperty(this.propertyPath,c.items,d);i.push(...p)}),this.updatePropertyPath()},n=a=>{let c=Object.keys(a),d=this.propertyPath;c.forEach(p=>{if(this.updatePropertyPath(p,d),this.isArray(a[p])&&r[p]?.items!=null)o(a[p],r[p]);else{let u=this.validateProperty(this.propertyPath,r[p],a[p]);i.push(...u)}})};this.isArray(e)&&r.items!=null?o(e,r):this.isObject(e)?n(e):this.updatePropertyPath()}handleNestedObject(t,e,r,i){if(this.isObject(t)){let s=this.getNestedObjects(t);for(let o of s){let n=e[o],a=t[o];n&&typeof a=="object"&&this.validate(n,a,r,i)}}}getNestedObjects(t){return Object.keys(t).filter(e=>{if(this.isObject(t))return e})}findNonOverlappingElements(t,e){return t.filter(r=>!e.includes(r))}areRequiredKeysPresent(t,e=[]){return t.every(r=>Object.keys(e).includes(r)?this.isDefined(e[r]):!1)}validateProperty(t,e,r){return this.validateInput(e,r).map(s=>{let{success:o,error:n}=s;return{key:t,value:r,success:o,error:n??""}})}validateInput(t,e){if(t){let r=[{condition:()=>t.type,validator:()=>this.isCorrectType(t.type,e),error:"Invalid type"},{condition:()=>t.format,validator:()=>this.isCorrectFormat(t.format,e),error:"Invalid format"},{condition:()=>t.minLength,validator:()=>this.isMinimumLength(t.minLength,e),error:"Length too short"},{condition:()=>t.maxLength,validator:()=>this.isMaximumLength(t.maxLength,e),error:"Length too long"},{condition:()=>t.minValue,validator:()=>this.isMinimumValue(t.minValue,e),error:"Value too small"},{condition:()=>t.maxValue,validator:()=>this.isMaximumValue(t.maxValue,e),error:"Value too large"},{condition:()=>t.matchesPattern,validator:()=>this.matchesPattern(t.matchesPattern,e),error:"Pattern does not match"}],i=[];for(let s of r)s.condition()&&!s.validator()&&i.push({success:!1,error:s.error});return i}else this.isSilent||console.warn(`Missing property '${t}' for match '${e}'. Skipping...`);return[{success:!0}]}isCorrectType(t,e){return Array.isArray(t)||(t=[t]),t.some(r=>{switch(r){case"string":return typeof e=="string";case"number":return typeof e=="number"&&!isNaN(e);case"boolean":return typeof e=="boolean";case"object":return this.isObject(e);case"array":return this.isArray(e)}})}isObject(t){return t!==null&&!this.isArray(t)&&typeof t=="object"&&t instanceof Object&&Object.prototype.toString.call(t)==="[object Object]"}isArray(t){return Array.isArray(t)}isCorrectFormat(t,e){switch(t){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(e);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(e);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(e);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(e);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(e);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(e)}}isMinimumLength(t,e){return Array.isArray(e)?e.length>=t:e?.toString().length>=t}isMaximumLength(t,e){return Array.isArray(e)?e.length<=t:e.toString().length<=t}isMinimumValue(t,e){return e>=t}isMaximumValue(t,e){return e<=t}matchesPattern(t,e){return new RegExp(t).test(e)}schemaFrom(t){let e={properties:{additionalProperties:!1,required:[]}};for(let r in t){let i=t[r];e.properties.required.push(r),Array.isArray(i)?e.properties[r]=this.generateArraySchema(i):typeof i=="object"&&i!==null?e.properties[r]=this.generateNestedObjectSchema(i):e.properties[r]=this.generatePropertySchema(i)}return e}generateArraySchema(t){let e={type:"array"},r=t.filter(i=>i);if(r.length>0){let i=r[0];r.every(o=>typeof o==typeof i)?typeof i=="object"&&!Array.isArray(i)?e.items=this.generateNestedObjectSchema(i):e.items=this.generatePropertySchema(i):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return e}generateNestedObjectSchema(t){let e={type:"object",additionalProperties:!1,required:[]};for(let r in t){let i=t[r];e.required.push(r),typeof i=="object"&&!Array.isArray(i)&&i!==null?e[r]=this.generateNestedObjectSchema(i):e[r]=this.generatePropertySchema(i)}return e}generatePropertySchema(t){let e=typeof t,r={type:e};return e==="string"&&(r.minLength=1),r}};var mt={properties:{email:{type:"string",format:"email",minLength:3},name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}},verified:{type:"boolean"}},required:["email","name","roles"],additionalProperties:!1};var li=new O;async function ft(t,e){try{let r=t.state.user,i=await I(t),s=li.test(mt,i);if(!s.success)return t.json({error:"Invalid input",details:s.errors},400);h(r,"identity.user.create",{});let{email:o,name:n,roles:a,verified:c}=i,d=await e.addUser(o,n,a,c);return t.json(d,201)}catch(r){let{message:i,status:s}=m(r,"Error creating user");return t.json({error:i},s)}}async function gt(t,e){try{let r=t.state.user;h(r,"identity.user.get",{});let i=await e.getUsers();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting users");return t.json({error:i},s)}}function x(t){return t.params}async function yt(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i;h(r,"identity.user.get",{userId:s});let o=await e.getUserById(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting user");return t.json({error:i},s)}}var vt={properties:{name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var ui=new O;async function wt(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i,o=await I(t),n=ui.test(vt,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);h(r,"identity.user.update",{userId:s});let{name:a,roles:c}=o,d=await e.updateUser(s,{name:a,roles:c});return d?t.json(d):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error updating user");return t.json({error:i},s)}}async function St(t,e){try{let r=t.state.user,i=x(t),{userId:s}=i;return h(r,"identity.user.delete",{userId:s}),await e.deleteUser(s)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting user");return t.json({error:i},s)}}async function bt(t,e){try{let r=t.state.user,i=await I(t);if(!i.name||!i.description||!i.roles||!Array.isArray(i.roles))return t.json({error:"Invalid input: name, description, and roles are required"},400);if(!["user","administrator"].every(c=>i.roles.includes(c)||!i.roles.includes(c)))return t.json({error:"Invalid input: roles must be user or administrator"},400);h(r,"identity.service-account.create",{});let{name:s,description:o,roles:n}=i,a=await e.addServiceAccount(s,o,n);return t.json(a,201)}catch(r){let{message:i,status:s}=m(r,"Error creating service account");return t.json({error:i},s)}}async function xt(t,e){try{let r=t.state.user;h(r,"identity.service-account.get",{});let i=await e.getServiceAccounts();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting service accounts");return t.json({error:i},s)}}async function Ct(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;h(r,"identity.service-account.get",{serviceAccountId:s});let o=await e.getServiceAccountById(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting service account");return t.json({error:i},s)}}var At={properties:{name:{type:"string",minLength:1,maxLength:100},description:{type:"string",minLength:1,maxLength:500},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var hi=new O;async function It(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i,o=await I(t),n=hi.test(At,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);h(r,"identity.service-account.update",{serviceAccountId:s});let{name:a,description:c,roles:d}=o,p=await e.updateServiceAccount(s,{name:a,description:c,roles:d});return p?t.json(p):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error updating service account");return t.json({error:i},s)}}async function Et(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;return h(r,"identity.service-account.delete",{serviceAccountId:s}),await e.deleteServiceAccount(s)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting service account");return t.json({error:i},s)}}async function kt(t,e){try{let r=t.state.user,i=x(t),{serviceAccountId:s}=i;h(r,"identity.service-account.update",{serviceAccountId:s});let o=await e.rotateServiceAccountKey(s);return o?t.json({apiKey:o}):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error rotating service account key");return t.json({error:i},s)}}async function Pt(t,e){try{let r=t.state.user;h(r,"identity.role.get",{});let i=await e.getRoles();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting roles");return t.json({error:i},s)}}async function Tt(t,e){try{let r=t.state.user;h(r,"identity.role.get",{});let i=t.params.roleId;if(!i)return t.text("Missing role ID",400);let s=await e.getRole(i);return s?t.json(s):t.text("Role not found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting role");return t.json({error:i},s)}}async function Rt(t,e){try{let r=t.state.user;h(r,"identity.role.create",{});let i=await I(t),{roleId:s,name:o,description:n,permissions:a,constraints:c}=i;if(!s||!o||!n)return t.text("Missing required fields: roleId, name, description",400);if(!a||!Array.isArray(a))return t.text("Permissions must be a non-empty array",400);await e.createCustomRole(s,o,n,a,c);let d=await e.getRole(s);return t.json(d,201)}catch(r){let{message:i,status:s}=m(r,"Error creating role");return t.json({error:i},s)}}async function jt(t,e){try{let r=t.state.user;h(r,"identity.role.update",{});let i=t.params.roleId;if(!i)return t.text("Missing role ID",400);let s=await I(t),{name:o,description:n,permissions:a,constraints:c}=s;await e.updateRole(i,{name:o,description:n,permissions:a,constraints:c});let d=await e.getRole(i);return t.json(d)}catch(r){let{message:i,status:s}=m(r,"Error updating role");return t.json({error:i},s)}}async function $t(t,e){try{let r=t.state.user;h(r,"identity.role.delete",{});let i=t.params.roleId;return i?i==="administrator"||i==="user"?t.text("Cannot delete base roles",403):(await e.deleteRole(i),t.json({message:"Role deleted successfully"})):t.text("Missing role ID",400)}catch(r){let{message:i,status:s}=m(r,"Error deleting role");return t.json({error:i},s)}}async function Mt(t,e){try{let r=t.state.user;h(r,"management.services.get",{});let i=await e.getServices();return t.json(i)}catch(r){let{message:i,status:s}=m(r,"Error getting services");return t.json({error:i},s)}}async function Ot(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;h(r,"management.service.get",{serviceName:s});let o=await e.getService(s);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error getting service");return t.json({error:i},s)}}var Ut={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var fi=new O;async function Lt(t,e){try{let r=t.state.user,i=await I(t),s=fi.test(Ut,i);if(!s.success)return t.json({error:"Invalid input",details:s.errors},400);if(h(r,"management.service.create",{}),["storage","functions","sites","databases","observability"].includes(i.name.toLowerCase())){let{name:a,...c}=i;await e.registerService(a,c)}else{if(!i.path||!i.port||!i.prefix)return t.json({error:"Custom services require: name, path, port, and prefix"},400);await e.registerService(i)}return t.text("",201)}catch(r){let{message:i,status:s}=m(r,"Error creating service");return t.json({error:i},s)}}var Ht={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var yi=new O;async function Dt(t,e){try{let r=t.state.user,i=x(t),s=await I(t),o=s.name,n=yi.test(Ht,s);return n.success?(h(r,"management.service.update",{serviceName:o}),o!==i.serviceName?t.json({error:"Service name in body must match path parameter"},400):(await e.updateService(o,s),t.body(null,204))):t.json({error:"Invalid input",details:n.errors},400)}catch(r){let{message:i,status:s}=m(r,"Error updating service");return t.json({error:i},s)}}async function Vt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;return h(r,"management.service.delete",{serviceName:s}),await e.getService(s)?(await e.removeService(s),t.body(null,204)):t.text("Not Found",404)}catch(r){let{message:i,status:s}=m(r,"Error deleting service");return t.json({error:i},s)}}async function Nt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.start",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.startService(s);return t.json({serviceName:s,isStarted:n})}catch(r){let{message:i,status:s}=m(r,"Error starting service");return t.json({error:i},s)}}async function Ft(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.stop",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.stopService(s);return t.json({serviceName:s,isStopped:n})}catch(r){let{message:i,status:s}=m(r,"Error stopping service");return t.json({error:i},s)}}async function Bt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.get",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=new URL("http://127.0.0.1:3005/events");n.searchParams.set("service",s),t.query.startTime&&n.searchParams.set("startTime",t.query.startTime),t.query.endTime&&n.searchParams.set("endTime",t.query.endTime),t.query.level&&n.searchParams.set("level",t.query.level),t.query.limit&&n.searchParams.set("limit",t.query.limit),t.query.offset&&n.searchParams.set("offset",t.query.offset);let a=await fetch(n.toString());if(!a.ok)throw new ve(`Observability service error: ${a.statusText}`);let c=await a.json();return t.json({serviceName:s,logs:c.events,count:c.count})}catch(r){let{message:i,status:s}=m(r,"Error getting service logs");return t.json({error:i},s)}}async function zt(t,e){try{let r=t.state.user,i=x(t),{serviceName:s}=i;if(h(r,"management.service.start",{serviceName:s}),!await e.getService(s))return t.text("Not Found",404);let n=await e.restartService(s);return t.json({serviceName:s,restarted:n})}catch(r){let{message:i,status:s}=m(r,"Error restarting service");return t.json({error:i},s)}}function qt(t,e,r,i,s){let o=t.find(c=>c.service===e);if(!o){let c=new Error(`Function bindings do not include access to service: ${e}`);throw c.cause={statusCode:403},c}if(!o.permissions||o.permissions.length===0)return;for(let c of o.permissions)if(!(c.resource&&c.resource!==r)){if(!c.resource||!c.actions||c.actions.length===0)return;if(c.actions.includes(i)){if(c.targets&&c.targets.length>0){if(!s)return;if(!c.targets.includes(s))continue}return}}let n=s?`:${s}`:"",a=new Error(`Function bindings do not allow: ${e}.${r}.${i}${n}`);throw a.cause={statusCode:403},a}async function H(t,e,r,i,s,o,n){if(!e)return null;let a=t.state.user;if(!a){let d=t.headers.authorization;if(!d){let u=new Error("Unauthorized: No authentication provided");throw u.cause={statusCode:401},u}let p=d.replace(/^Bearer\s+/i,"");if(a=await e.getUserFromToken(p),!a){let u=new Error("Unauthorized: Invalid token");throw u.cause={statusCode:401},u}}h(a,r,{});let c=t.headers["x-function-bindings"];if(c)try{let d=Array.isArray(c)?c[0]:c,p=JSON.parse(d);qt(p,i,s,o,n)}catch(d){if(d.cause?.statusCode===403)throw d;let p=new Error("Invalid function bindings header");throw p.cause={statusCode:400},p}return a}async function _t(t,e,r){try{let i=await I(t),s=t.state.requestingIdentity;if(!s)return t.json({error:"Authentication required"},401);let{name:o,description:n,redirectUris:a,metadata:c}=i;if(!o||!a)return t.json({error:"Missing required fields: name and redirectUris are required"},400);await H(t,r,"applications.application.create","applications","application","create",o);let d={name:o,description:n,redirectUris:Array.isArray(a)?a:[a],metadata:c,owners:[s.id]},p=await e.createApplication(d,s.id);return t.json(p,201)}catch(i){let{message:s,status:o}=m(i,"Error creating application");return t.json({error:s},o)}}async function Wt(t,e,r){try{let i=t.params.id,s=t.state.requestingIdentity;if(!i)return t.json({error:"Application ID is required"},400);await H(t,r,"applications.application.read","applications","application","read",i);let o=await e.getApplication(i,s?.id);return t.json(o,200)}catch(i){let{message:s,status:o}=m(i,"Error retrieving application");return t.json({error:s},o)}}async function Kt(t,e,r){try{let i=t.state.requestingIdentity;if(!i)return t.json({error:"Authentication required"},401);await H(t,r,"applications.application.list","applications","application","list");let s=await e.listApplications(i.id);return t.json({applications:s,total:s.length},200)}catch(i){let{message:s,status:o}=m(i,"Error listing applications");return t.json({error:s},o)}}async function Gt(t,e,r){try{let i=t.params.id,s=await I(t),o=t.state.requestingIdentity;if(!o)return t.json({error:"Authentication required"},401);if(!i)return t.json({error:"Application ID is required"},400);await H(t,r,"applications.application.update","applications","application","update",i);let n=await e.updateApplication(i,s,o.id);return t.json(n,200)}catch(i){let{message:s,status:o}=m(i,"Error updating application");return t.json({error:s},o)}}async function Jt(t,e,r){try{let i=t.params.id,s=t.state.requestingIdentity;return s?i?(await H(t,r,"applications.application.delete","applications","application","delete",i),await e.deleteApplication(i,s.id),t.json({success:!0,message:"Application deleted"},200)):t.json({error:"Application ID is required"},400):t.json({error:"Authentication required"},401)}catch(i){let{message:s,status:o}=m(i,"Error deleting application");return t.json({error:s},o)}}async function Xt(t,e,r,i){try{let s=t.headers["x-forwarded-for"],o=(Array.isArray(s)?s[0]:s)?.split(",")[0]?.trim()||t.headers["x-real-ip"]||"unknown";if(!r.checkRateLimit(`oauth:${o}`)){let p=r.getRateLimitInfo(`oauth:${o}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((p.reset-Date.now())/1e3)},429)}let n=Array.isArray(t.query.redirect_uri)?t.query.redirect_uri[0]:t.query.redirect_uri,a=Array.isArray(t.query.application_id)?t.query.application_id[0]:t.query.application_id,c=r.generateState(t,i,n,a),d=e.getAuthorizationUrl(c);return t.redirect(d,302)}catch(s){let{message:o,status:n}=m(s,"Error initiating OAuth flow");return t.json({error:o},n)}}async function Yt(t,e,r,i,s,o){try{let n=t.headers["x-forwarded-for"],a=Array.isArray(n)?n[0]:n,c=t.headers["x-real-ip"],d=Array.isArray(c)?c[0]:c,p=a?.split(",")[0]?.trim()||d||"unknown";if(!r.checkRateLimit(`oauth:${p}`)){let T=r.getRateLimitInfo(`oauth:${p}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((T.reset-Date.now())/1e3)},429)}let u=r.validateCallback(t,o);if(!u.valid)return t.json({error:u.error||"Invalid OAuth callback"},400);let f=await e.handleCallback(u.code,p),v=await i.getUserByEmail(f.user.email);if(!v)return t.json({error:"User not found. You must be invited before signing in with OAuth."},403);if(await i.updateUser(v.id,{lastLogin:new Date().toISOString()}),u.redirectUrl){if(u.applicationId&&!await s.validateRedirectUri(u.applicationId,u.redirectUrl))return console.warn(`Redirect URL validation failed for applicationId: ${u.applicationId}`),t.json({success:!0,accessToken:f.accessToken,refreshToken:f.refreshToken,expiresIn:f.expiresIn,tokenType:f.tokenType,user:{id:v.id,email:f.user.email,name:f.user.name,username:f.user.username},warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);let T=new URL(u.redirectUrl);return T.searchParams.set("access_token",f.accessToken),T.searchParams.set("refresh_token",f.refreshToken),T.searchParams.set("expires_in",f.expiresIn.toString()),t.redirect(T.toString(),302)}return t.json({success:!0,accessToken:f.accessToken,refreshToken:f.refreshToken,expiresIn:f.expiresIn,tokenType:f.tokenType,user:{id:v.id,email:f.user.email,name:f.user.name,username:f.user.username}})}catch(n){let{message:a,status:c}=m(n,"OAuth authentication failed");return t.json({error:a},c)}}async function Zt(t,e){try{let r=Array.from(e.values()).map(i=>i.getPublicInfo());return t.json({providers:r,count:r.length})}catch(r){let{message:i,status:s}=m(r,"Error listing OAuth providers");return t.json({error:i},s)}}var xe=class{constructor(e,r){this.config=e;this.mikroAuth=r}getAuthorizationUrl(e){let r=Array.isArray(this.config.scopes)?this.config.scopes.join(" "):this.config.scopes,i=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:"code",scope:r,state:e,...this.config.authorizationParams});return`${this.config.authorizationUrl}?${i}`}async exchangeCodeForTokens(e){let r=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,code:e,grant_type:"authorization_code",redirect_uri:this.config.redirectUri}),i=await fetch(this.config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json",...this.config.tokenHeaders},body:r.toString()});if(!i.ok){let s=await i.text();throw new Error(`Token exchange failed: ${i.status} ${i.statusText} - ${s}`)}return i.json()}async fetchUserInfo(e){let r=await fetch(this.config.userInfoUrl,{headers:{Authorization:`Bearer ${e}`,Accept:"application/json",...this.config.userInfoHeaders}});if(!r.ok){let i=await r.text();throw new Error(`User info fetch failed: ${r.status} ${r.statusText} - ${i}`)}return r.json()}mapUserInfo(e){return this.config.userMapping?this.config.userMapping(e):{id:e.sub||e.id?.toString(),email:e.email,name:e.name||e.preferred_username,username:e.preferred_username||e.username||e.email?.split("@")[0]}}async handleCallback(e,r){let i=await this.exchangeCodeForTokens(e),s=await this.fetchUserInfo(i.access_token),o=this.mapUserInfo(s);if(!o.email)throw new Error("OAuth provider did not return user email");let n=await this.mikroAuth.createToken({email:o.email,username:o.username||o.name,role:"user",ip:r});return{accessToken:n.accessToken,refreshToken:n.refreshToken,expiresIn:n.exp,tokenType:n.tokenType,user:o}}getPublicInfo(){return{id:this.config.id,name:this.config.name,loginUrl:`/auth/oauth/${this.config.id}`}}};import vi from"node:crypto";var Ce=class{stateStore=new Map;rateLimitStore=new Map;stateExpiryMs;rateLimitMaxAttempts;rateLimitWindowMs;stateCleanupInterval;rateLimitCleanupInterval;constructor(e){this.stateExpiryMs=(e?.stateExpirySeconds||600)*1e3,this.rateLimitMaxAttempts=e?.rateLimiting?.maxAttempts||10,this.rateLimitWindowMs=e?.rateLimiting?.windowMs||900*1e3,this.startCleanupTasks()}generateState(e,r,i,s){let o=vi.randomBytes(32).toString("hex"),n=this.getClientIp(e),a=e.headers["user-agent"]||"";return this.stateStore.set(o,{expires:Date.now()+this.stateExpiryMs,providerId:r,ip:n,userAgent:a,redirectUrl:i,applicationId:s}),o}validateState(e,r,i,s){let o=this.stateStore.get(e);return o?o.expires<Date.now()?(this.stateStore.delete(e),!1):o.providerId!==r||o.ip!==i||s&&o.userAgent&&o.userAgent!==s?!1:(this.stateStore.delete(e),!0):!1}checkRateLimit(e){let r=Date.now(),i=this.rateLimitStore.get(e);return!i||i.resetAt<r?(this.rateLimitStore.set(e,{count:1,resetAt:r+this.rateLimitWindowMs}),!0):i.count>=this.rateLimitMaxAttempts?!1:(i.count++,!0)}getRateLimitInfo(e){let r=this.rateLimitStore.get(e),i=Date.now();return!r||r.resetAt<i?{limit:this.rateLimitMaxAttempts,remaining:this.rateLimitMaxAttempts-1,reset:i+this.rateLimitWindowMs}:{limit:this.rateLimitMaxAttempts,remaining:Math.max(0,this.rateLimitMaxAttempts-r.count),reset:r.resetAt}}validateCallback(e,r){let i=Array.isArray(e.query.code)?e.query.code[0]:e.query.code,s=Array.isArray(e.query.state)?e.query.state[0]:e.query.state,o=Array.isArray(e.query.error)?e.query.error[0]:e.query.error,n=Array.isArray(e.query.error_description)?e.query.error_description[0]:e.query.error_description;if(o)return{valid:!1,error:`OAuth provider error: ${n||o}`};if(!i||!s)return{valid:!1,error:"Missing code or state parameter"};let a=this.stateStore.get(s),c=this.getClientIp(e),d=e.headers["user-agent"];return this.validateState(s,r,c,d)?{valid:!0,code:i,state:s,redirectUrl:a?.redirectUrl,applicationId:a?.applicationId}:{valid:!1,error:"Invalid or expired state token (CSRF protection failed)"}}getClientIp(e){let r=e.headers["x-forwarded-for"],i=Array.isArray(r)?r[0]:r,s=e.headers["x-real-ip"],o=Array.isArray(s)?s[0]:s;return i?.split(",")[0]?.trim()||o||"unknown"}startCleanupTasks(){this.stateCleanupInterval=setInterval(()=>{this.cleanupExpiredStates()},300*1e3),this.rateLimitCleanupInterval=setInterval(()=>{this.cleanupExpiredRateLimits()},600*1e3)}cleanupExpiredStates(){let e=Date.now(),r=0;for(let[i,s]of this.stateStore.entries())s.expires<e&&(this.stateStore.delete(i),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired state tokens`)}cleanupExpiredRateLimits(){let e=Date.now(),r=0;for(let[i,s]of this.rateLimitStore.entries())s.resetAt<e&&(this.rateLimitStore.delete(i),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired rate limit records`)}shutdown(){this.stateCleanupInterval&&clearInterval(this.stateCleanupInterval),this.rateLimitCleanupInterval&&clearInterval(this.rateLimitCleanupInterval)}getStats(){return{stateCount:this.stateStore.size,rateLimitCount:this.rateLimitStore.size}}};var Qt={google:{id:"google",name:"Google",authorizationUrl:"https://accounts.google.com/o/oauth2/v2/auth",tokenUrl:"https://oauth2.googleapis.com/token",userInfoUrl:"https://www.googleapis.com/oauth2/v2/userinfo",scopes:"openid email profile",authorizationParams:{access_type:"offline",prompt:"consent"},userMapping:t=>({id:t.sub,email:t.email,name:t.name,username:t.email.split("@")[0]})},github:{id:"github",name:"GitHub",authorizationUrl:"https://github.com/login/oauth/authorize",tokenUrl:"https://github.com/login/oauth/access_token",userInfoUrl:"https://api.github.com/user",scopes:"user:email read:user",tokenHeaders:{Accept:"application/json"},userInfoHeaders:{Accept:"application/vnd.github.v3+json"},userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name||t.login,username:t.login})},microsoft:{id:"microsoft",name:"Microsoft",authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",userInfoUrl:"https://graph.microsoft.com/v1.0/me",scopes:"openid email profile User.Read",userMapping:t=>({id:t.id,email:t.mail||t.userPrincipalName,name:t.displayName,username:t.userPrincipalName?.split("@")[0]})},gitlab:{id:"gitlab",name:"GitLab",authorizationUrl:"https://gitlab.com/oauth/authorize",tokenUrl:"https://gitlab.com/oauth/token",userInfoUrl:"https://gitlab.com/api/v4/user",scopes:"read_user email",userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name,username:t.username})}};function er(t){let e=[];if(t.presets)for(let[r,i]of Object.entries(t.presets)){let s=Qt[r];if(!s){console.warn(`[OAuth] Unknown preset provider: ${r}`);continue}e.push({...s,clientId:i.clientId,clientSecret:i.clientSecret,redirectUri:i.redirectUri,scopes:i.scopes||s.scopes,authorizationParams:{...s.authorizationParams,...i.authorizationParams}})}return t.custom&&e.push(...t.custom),e}async function tr(t,e){let r=await e(),i=r.headers||{};return i["X-Frame-Options"]="DENY",i["X-Content-Type-Options"]="nosniff",i["X-XSS-Protection"]="1; mode=block",i["Referrer-Policy"]="strict-origin-when-cross-origin",i["Content-Security-Policy"]="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'",i["Permissions-Policy"]="geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()",process.env.NODE_ENV==="production"&&(i["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),{...r,headers:i}}async function He(t,e){return process.env.NODE_ENV==="development"||process.env.NODE_ENV==="test"||(t.headers["x-forwarded-proto"]||t.headers["x-forwarded-protocol"])==="https"||t.req.url?.startsWith("https://")?e():t.json({error:"HTTPS required",message:"OAuth authentication requires HTTPS. Please use https:// instead of http://"},403)}function rr(t,e,r){if(r.startsWith("/databases/")){r.includes("/table")||r.includes("/get")?h(t,"databases.table.get",{}):r.includes("/write")||r.includes("/update")||e==="PUT"||e==="PATCH"?h(t,"databases.table.update",{}):r.includes("/delete")||e==="DELETE"?h(t,"databases.table.delete",{}):e==="POST"&&h(t,"databases.table.create",{});return}if(r.startsWith("/functions/")){r.includes("/deploy")||e==="POST"?h(t,"functions.function.create",{}):r.includes("/run/")?h(t,"functions.function.execute",{}):e==="PUT"||e==="PATCH"?h(t,"functions.function.update",{}):e==="DELETE"?h(t,"functions.function.delete",{}):e==="GET"&&h(t,"functions.function.get",{});return}if(r.startsWith("/storage/")){let i=r.match(/^\/storage\/buckets\/[^/]+$/),s=r.includes("/objects");i?e==="POST"?h(t,"storage.bucket.create",{}):e==="PATCH"||e==="PUT"?h(t,"storage.bucket.update",{}):e==="DELETE"?h(t,"storage.bucket.delete",{}):e==="GET"&&h(t,"storage.bucket.get",{}):s||r.includes("/upload")||r.includes("/download")||r.includes("/get")?r.includes("/upload")||e==="POST"?h(t,"storage.object.create",{}):r.includes("/download")||r.includes("/get")?h(t,"storage.object.get",{}):e==="PUT"||e==="PATCH"?h(t,"storage.object.update",{}):e==="DELETE"?h(t,"storage.object.delete",{}):e==="GET"&&h(t,"storage.object.get",{}):e==="GET"&&h(t,"storage.bucket.get",{});return}if(r.startsWith("/sites/")){if(r.match(/^\/sites\/projects\/[^/]+/)&&e==="GET")return;r.includes("/deploy")||e==="POST"?h(t,"sites.site.create",{}):e==="PUT"||e==="PATCH"?h(t,"sites.site.update",{}):e==="DELETE"?h(t,"sites.site.delete",{}):e==="GET"&&h(t,"sites.site.get",{});return}if(r.startsWith("/observability/")){e==="POST"?h(t,"observability.metrics.create",{}):e==="GET"&&h(t,"observability.metrics.get",{});return}}var Ae=class{baseUrl;authToken;constructor(e,r){this.baseUrl=e.replace(/\/$/,""),this.authToken=r}async createCustomRole(e,r,i,s){let o=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:e,name:r,description:i,permissions:s})});if(!o.ok){let n=`Failed to create role: ${o.statusText}`;try{n=(await o.json()).error||n}catch{n=await o.text().catch(()=>o.statusText)||n}throw new Error(n)}}async updateRole(e,r){let i=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!i.ok){let s=await i.json();throw new Error(s.error||`Failed to update role: ${i.statusText}`)}}async deleteRole(e){let r=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let i=await r.json();throw new Error(i.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(e,r,i){let s=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:e,description:r,roles:i})});if(!s.ok){let n=`Failed to create service account: ${s.statusText}`;try{n=(await s.json()).error||n}catch{n=await s.text().catch(()=>s.statusText)||n}throw new Error(n)}let o=await s.json();return{id:o.id,apiKey:o.apiKey}}async deleteIdentity(e){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let i=await r.json();throw new Error(i.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(e){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let e={"Content-Type":"application/json"};return this.authToken&&(e.Authorization=`Bearer ${this.authToken}`),e}};function ir(t){return t?new Ae(t):null}function sr(t){return t&&typeof t=="number"&&t>0?t*1024*1024:0}async function or(t){let{auth:e,db:r,config:i,molnos:s,oauth:o}=t,n=new we(e,r,s.initialUser);await n.start();let c=`${i.useHttps||i.useHttp2?"https":"http"}://${i.host||"127.0.0.1"}:${i.port||3e3}`,d=ir(c),p,u;if(o){p=new Ce(o),u=new Map;let l=er(o);for(let P of l){let k=new xe(P,e);u.set(P.id,k)}}let f=De(s.dataPath,"applications"),v=new be({dbPath:f});await v.start();let T=De(s.dataPath,"management"),S=new Se({dbPath:T});await S.start();let Ie=!1,ur=De(s.dataPath,".runtime-config.json");S.setEnvironmentVariables({MOLNOS_RUNTIME_CONFIG:ur});let g=new Le({port:i.port||3e3,host:i.host||"127.0.0.1",allowedDomains:i.allowedDomains,maxBodySize:sr(1e3)});g.use(async(l,P)=>Ie?l.text("Service Unavailable - Server is shutting down",503):P()),g.use(tr),g.get("/health",async l=>l.text("OK"));let E=async l=>l.text("Not Found",404);if(g.post("/auth/login",l=>lt(l,n,e,v)),g.get("/auth/verify",l=>dt(l,e,v)),g.post("/auth/refresh",l=>ut(l,e)),u&&p){g.get("/auth/oauth/providers",l=>Zt(l,u));for(let[l,P]of u.entries())g.get(`/auth/oauth/${l}`,He,k=>Xt(k,P,p,l)),g.get(`/auth/oauth/${l}/callback`,He,k=>Yt(k,P,p,n,v,l))}g.post("/management/service",y,l=>Lt(l,S)),g.get("/management/services",y,l=>Mt(l,S)),g.get("/management/service/:serviceName",y,l=>Ot(l,S)),g.put("/management/service/:serviceName",y,l=>Dt(l,S)),g.delete("/management/service/:serviceName",y,l=>Vt(l,S)),g.get("/management/service/:serviceName/start",y,l=>Nt(l,S)),g.get("/management/service/:serviceName/stop",y,l=>Ft(l,S)),g.get("/management/service/:serviceName/logs",y,l=>Bt(l,S)),g.get("/management/service/:serviceName/restart",y,l=>zt(l,S)),g.get("/identity/whoami",y,l=>pt(l)),g.get("/identity/identities",y,l=>ht(l,n)),g.post("/identity/users",y,l=>ft(l,n)),g.get("/identity/users",y,l=>gt(l,n)),g.get("/identity/users/:userId",y,l=>yt(l,n)),g.patch("/identity/users/:userId",y,l=>wt(l,n)),g.delete("/identity/users/:userId",y,l=>St(l,n)),g.post("/identity/service-accounts",y,l=>bt(l,n)),g.get("/identity/service-accounts",y,l=>xt(l,n)),g.get("/identity/service-accounts/:serviceAccountId",y,l=>Ct(l,n)),g.patch("/identity/service-accounts/:serviceAccountId",y,l=>It(l,n)),g.delete("/identity/service-accounts/:serviceAccountId",y,l=>Et(l,n)),g.post("/identity/service-accounts/:serviceAccountId/rotate-key",y,l=>kt(l,n)),g.post("/identity/roles",y,l=>Rt(l,n)),g.get("/identity/roles",y,l=>Pt(l,n)),g.get("/identity/roles/:roleId",y,l=>Tt(l,n)),g.patch("/identity/roles/:roleId",y,l=>jt(l,n)),g.delete("/identity/roles/:roleId",y,l=>$t(l,n)),g.post("/applications",y,l=>_t(l,v,d)),g.get("/applications",y,l=>Kt(l,v,d)),g.get("/applications/:id",y,l=>Wt(l,v,d)),g.patch("/applications/:id",y,l=>Gt(l,v,d)),g.delete("/applications/:id",y,l=>Jt(l,v,d)),g.get("/databases/*",y,A,E),g.post("/databases/*",y,A,E),g.put("/databases/*",y,A,E),g.patch("/databases/*",y,A,E),g.delete("/databases/*",y,A,E),g.get("/functions/*",A,E),g.post("/functions/*",A,E),g.put("/functions/*",A,E),g.patch("/functions/*",A,E),g.delete("/functions/*",A,E),g.get("/sites/projects/:projectId/*",A,async l=>l.text("Not Found",404)),g.get("/sites/*",y,A,E),g.post("/sites/*",y,A,E),g.put("/sites/*",y,A,E),g.patch("/sites/*",y,A,E),g.delete("/sites/*",y,A,E);async function te(l,P){let k=l.req.method;if(l.path.match(/^\/storage\/buckets\/[^/]+\/objects\/.+/)&&(k==="GET"||k==="HEAD")){let U=l.headers.authorization;if(U)try{let J=await n.getUserFromToken(U);J&&(l.state.user=J,l.state.requestingIdentity=J)}catch{}return P()}return y(l,P)}g.get("/storage/*",te,A,E),g.post("/storage/*",te,A,E),g.put("/storage/*",te,A,E),g.patch("/storage/*",te,A,E),g.delete("/storage/*",te,A,E),g.get("/observability/*",y,A,E),g.post("/observability/*",y,A,E),g.put("/observability/*",y,A,E),g.patch("/observability/*",y,A,E),g.delete("/observability/*",y,A,E);async function A(l,P){try{let k=l.path,N=Object.keys(l.query).length>0?`?${new URLSearchParams(l.query).toString()}`:"",re=l.req.method||"",U,J=0,hr=await S.getServices();for(let $ of hr)$.active&&k.startsWith($.prefix)&&$.prefix.length>J&&(U=$,J=$.prefix.length);if(!U)return P();let Fe=l.state.user;if(!(k.match(/^\/sites\/projects\/[^/]+.*/)&&re==="GET")&&Fe&&rr(Fe,re,k),U.healthCheck&&U.healthCheck.healthy===!1)return l.json({error:"Service unavailable (unhealthy)"},503);let mr=(k.substring(U.prefix.length)||"/")+N,j=null,Ee=null;if(["POST","PUT","PATCH","DELETE"].includes(re)){let $=l.headers["content-type"]||"";if($.includes("application/json"))try{let X=await I(l);j=JSON.stringify(X)}catch{j=null}else if($.includes("multipart/form-data"))try{j=JSON.stringify(l.body),Ee="application/json"}catch{j=null}else try{j=Buffer.isBuffer(l.body)?l.body:Buffer.from(l.body)}catch{j=null}}return new Promise($=>{let X={...l.headers,host:`127.0.0.1:${U.port}`};Ee&&(X["content-type"]=Ee),j&&(typeof j=="string"?X["content-length"]=Buffer.byteLength(j).toString():X["content-length"]=j.length.toString());let fr={method:re,hostname:"127.0.0.1",port:U.port,path:mr,headers:X},ie=wi.request(fr,M=>{let Be=[];M.on("data",Y=>{Be.push(Y)}),M.on("end",()=>{let Y=Buffer.concat(Be),ke={};Object.entries(M.headers).forEach(([Pe,le])=>{le!==void 0&&(ke[Pe]=Array.isArray(le)?le.join(", "):le)});let se=M.headers["content-type"]||"",L;if(se.includes("application/json"))try{let Pe=JSON.parse(Y.toString("utf-8"));L=l.json(Pe,M.statusCode||200)}catch{L=l.text(Y.toString("utf-8"),M.statusCode||200)}else se.startsWith("text/")||se.includes("javascript")||se.includes("xml")?L=l.text(Y.toString("utf-8"),M.statusCode||200):(L=l.binary(Y,se||"application/octet-stream"),M.statusCode&&M.statusCode!==200&&(L.statusCode=M.statusCode));L.headers?L.headers={...L.headers,...ke}:L.headers=ke,$(L)})});ie.on("error",M=>{$(l.json({error:"Proxy Error",message:M.message},502))}),ie.on("timeout",()=>{ie.destroy(),$(l.json({error:"Gateway Timeout"},504))}),j&&ie.write(j),ie.end()})}catch(k){return Ve(l,k)}}async function y(l,P){try{let k=l.headers.authorization;if(!k)return l.text("Unauthorized",401);let N=await n.getUserFromToken(k);return N?(l.state.user=N,l.state.requestingIdentity=N,P()):l.text("Unauthorized",401)}catch(k){return Ve(l,k)}}function Ve(l,P){let k=P.message||P,N=P?.cause?.statusCode||400;return l.json({error:k},N)}let Ne=g.start();async function pr(){Ie||(Ie=!0,await new Promise((l,P)=>{Ne.close(k=>{k?P(k):l()})}),console.log("[Server] Stopping managed services..."),await S.shutdown(),p&&p.shutdown(),await r.close())}return{server:Ne,shutdown:pr,processManager:S,identityService:n}}var Si="molnosid",bi={name:Si,length:12,urlSafe:!0},nr=()=>{let t=xi(process.env.DEBUG)||!1;return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:900,refreshTokenExpirySeconds:10080*60,maxActiveSessions:3,consoleUrl:process.env.CONSOLE_URL||"http://localhost:8000",debug:t},email:{emailSubject:"Sign In To MolnOS",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:t},storage:{databaseDirectory:"molnosdb",encryptionKey:process.env.STORAGE_KEY||"",debug:t},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"127.0.0.1",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",allowedDomains:["http://localhost:8000"],debug:t},molnos:{dataPath:process.env.DATA_PATH||"data",initialUser:{id:process.env.INITIAL_USER_ID||new D().add(bi),userName:process.env.INITIAL_USER_NAME||"",email:process.env.INITIAL_USER_EMAIL||""},rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:process.env.SIGNED_URL_SECRET||"molnos-default-signed-url-secret"}}};function xi(t){return t==="true"||t===!0}var w=nr(),ar={configFilePath:"molnos.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:w.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:w.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:w.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:w.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:w.auth.maxActiveSessions},{flag:"--consoleUrl",path:"auth.consoleUrl",defaultValue:w.auth.consoleUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:w.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:w.email.host},{flag:"--emailUser",path:"email.user",defaultValue:w.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:w.email.password},{flag:"--emailPort",path:"email.port",defaultValue:w.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:w.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:w.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:w.email.debug},{flag:"--db",path:"storage.databaseDirectory",defaultValue:w.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:w.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:w.storage.debug},{flag:"--port",path:"server.port",defaultValue:w.server.port},{flag:"--host",path:"server.host",defaultValue:w.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:w.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:w.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:w.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:w.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:w.server.sslCa},{flag:"--allowed",path:"server.allowedDomains",defaultValue:w.server.allowedDomains,parser:q.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:w.server.debug},{flag:"--data-path",path:"dataPath",defaultValue:w.molnos.dataPath},{flag:"--initialUserId",path:"molnos.initialUser.id",defaultValue:w.molnos.initialUser.id},{flag:"--initialUserName",path:"molnos.initialUser.userName",defaultValue:w.molnos.initialUser.userName},{flag:"--initialUserEmail",path:"molnos.initialUser.email",defaultValue:w.molnos.initialUser.email}]};function cr(t){return{...t,auth:{...t.auth,templates:{textVersion:(e,r,i)=>{let s=new URL(e),o=s.searchParams.get("token")||"",n=s.searchParams.get("email")||"",a=i?.appName||"MolnOS",c=i?.redirectUrl&&i?.applicationId,d=c?`Sign in to ${a}`:"Sign in to MolnOS",p=c?`You're signing in to ${a}. Click the link below to complete the authentication process.`:"Click the link below to sign in to your account.";return`
|
|
115
|
-
${
|
|
113
|
+
`),r=t.indexOf(e);if(r===-1)return null;let s=t.subarray(0,r),i=t.subarray(r+4),n=s.toString("utf8").split(`\r
|
|
114
|
+
`),a="",c="",l,p;for(let h of n){let v=h.toLowerCase();if(v.startsWith("content-disposition:")){a=h.substring(20).trim();let j=a.match(/name="([^"]+)"/);j&&(c=j[1]);let I=a.match(/filename="([^"]+)"/);I&&(l=I[1])}else v.startsWith("content-type:")&&(p=h.substring(13).trim())}if(!c)return null;let u=i;return u.length>=2&&u[u.length-2]===13&&u[u.length-1]===10&&(u=u.subarray(0,u.length-2)),{disposition:a,name:c,filename:l,contentType:p,data:u}}import{readFileSync as ie}from"fs";import Ge from"http";import Us from"http2";import Ms from"https";var We=class{config;rateLimiter;router;shutdownHandlers=[];constructor(t){let e=new X(Ct(t||{})).get();e.debug&&console.log("Using configuration:",e),this.config=e,this.router=new xt;let r=e.rateLimit.requestsPerMinute||we().rateLimit.requestsPerMinute;this.rateLimiter=new bt(r,60),e.rateLimit.enabled===!0&&this.use(this.rateLimitMiddleware.bind(this))}use(t){return this.router.use(t),this}get(t,...e){return this.router.get(t,...e),this}post(t,...e){return this.router.post(t,...e),this}put(t,...e){return this.router.put(t,...e),this}delete(t,...e){return this.router.delete(t,...e),this}patch(t,...e){return this.router.patch(t,...e),this}any(t,...e){return this.router.any(t,...e),this}options(t,...e){return this.router.options(t,...e),this}start(){let t=this.createServer(),{port:e,host:r}=this.config;return this.setupGracefulShutdown(t),t.listen(e,r,()=>{let s=t.address(),i=this.config.useHttps||this.config.useHttp2?"https":"http";console.log(`MikroServe running at ${i}://${s.address!=="::"?s.address:"localhost"}:${s.port}`)}),t}createServer(){let t=this.requestHandler.bind(this);if(this.config.useHttp2){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttp2 is true");try{let e={key:ie(this.config.sslKey),cert:ie(this.config.sslCert),...this.config.sslCa?{ca:ie(this.config.sslCa)}:{}};return Us.createSecureServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}else if(this.config.useHttps){if(!this.config.sslCert||!this.config.sslKey)throw new Error("SSL certificate and key paths are required when useHttps is true");try{let e={key:ie(this.config.sslKey),cert:ie(this.config.sslCert),...this.config.sslCa?{ca:ie(this.config.sslCa)}:{}};return Ms.createServer(e,t)}catch(e){throw e.message.includes("key values mismatch")?new Error(`SSL certificate and key do not match: ${e.message}`):e}}return Ge.createServer(t)}async rateLimitMiddleware(t,e){let r=t.req.socket.remoteAddress||"unknown";return t.res.setHeader("X-RateLimit-Limit",this.rateLimiter.getLimit().toString()),t.res.setHeader("X-RateLimit-Remaining",this.rateLimiter.getRemainingRequests(r).toString()),t.res.setHeader("X-RateLimit-Reset",this.rateLimiter.getResetTime(r).toString()),this.rateLimiter.isAllowed(r)?e():{statusCode:429,body:{error:"Too Many Requests",message:"Rate limit exceeded, please try again later"},headers:{"Content-Type":"application/json"}}}async requestHandler(t,e){let r=Date.now(),s=t.method||"UNKNOWN",i=t.url||"/unknown",o=this.config.debug;try{if(this.setCorsHeaders(e,t),this.setSecurityHeaders(e,this.config.useHttps),o&&console.log(`${s} ${i}`),t.method==="OPTIONS"){if(e instanceof Ge.ServerResponse)e.statusCode=204,e.end();else{let a=e;a.writeHead(204),a.end()}return}try{t.body=await this.parseBody(t)}catch(a){return o&&console.error("Body parsing error:",a.message),this.respond(e,{statusCode:400,body:{error:"Bad Request",message:a.message}})}let n=await this.router.handle(t,e);return n?n._handled?void 0:this.respond(e,n):this.respond(e,{statusCode:404,body:{error:"Not Found",message:"The requested endpoint does not exist"}})}catch(n){return console.error("Server error:",n),this.respond(e,{statusCode:500,body:{error:"Internal Server Error",message:o?n.message:"An unexpected error occurred"}})}finally{o&&this.logDuration(r,s,i)}}logDuration(t,e,r){let s=Date.now()-t;console.log(`${e} ${r} completed in ${s}ms`)}async parseBody(t){return new Promise((e,r)=>{let s=[],i=0,o=this.config.maxBodySize,n=!1,a=null,c=this.config.debug,l=t.headers["content-type"]||"";c&&console.log("Content-Type:",l),this.config.requestTimeout>0&&(a=setTimeout(()=>{n||(n=!0,c&&console.log("Request timeout exceeded"),r(new Error("Request timeout")))},this.config.requestTimeout));let p=()=>{a&&(clearTimeout(a),a=null)};t.on("data",u=>{if(!n){if(i+=u.length,c&&console.log(`Received chunk: ${u.length} bytes, total size: ${i}`),i>o){n=!0,p(),c&&console.log(`Body size exceeded limit: ${i} > ${o}`),r(new Error("Request body too large"));return}s.push(u)}}),t.on("end",()=>{if(!n){n=!0,p(),c&&console.log(`Request body complete: ${i} bytes`);try{if(s.length>0){let u=Buffer.concat(s);if(l.includes("application/json"))try{let h=u.toString("utf8");e(JSON.parse(h))}catch(h){r(new Error(`Invalid JSON in request body: ${h.message}`))}else if(l.includes("application/x-www-form-urlencoded")){let h=u.toString("utf8"),v={};new URLSearchParams(h).forEach((j,I)=>{v[I]=j}),e(v)}else if(l.includes("multipart/form-data"))try{let h=It(u,l);e(h)}catch(h){r(new Error(`Invalid multipart form data: ${h.message}`))}else this.isBinaryContentType(l)?e(u):e(u.toString("utf8"))}else e({})}catch(u){r(new Error(`Invalid request body: ${u}`))}}}),t.on("error",u=>{n||(n=!0,p(),r(new Error(`Error reading request body: ${u.message}`)))}),t.on("close",()=>{p()})})}isBinaryContentType(t){return["application/octet-stream","application/pdf","application/zip","application/gzip","application/x-tar","application/x-rar-compressed","application/x-7z-compressed","image/","video/","audio/","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument","application/msword","application/vnd.ms-powerpoint"].some(r=>t.includes(r))}setCorsHeaders(t,e){let r=e.headers.origin,{allowedDomains:s=["*"]}=this.config;!r||s.length===0||s.includes("*")?t.setHeader("Access-Control-Allow-Origin","*"):s.includes(r)&&(t.setHeader("Access-Control-Allow-Origin",r),t.setHeader("Vary","Origin")),t.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),t.setHeader("Access-Control-Max-Age","86400")}setSecurityHeaders(t,e=!1){let r={"X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'self'; script-src 'self'; object-src 'none'","X-XSS-Protection":"1; mode=block"};if((e||this.config.useHttp2)&&(r["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),t instanceof Ge.ServerResponse)Object.entries(r).forEach(([s,i])=>{t.setHeader(s,i)});else{let s=t;Object.entries(r).forEach(([i,o])=>{s.setHeader(i,o)})}}respond(t,e){let r={...e.headers||{}};(i=>typeof i.writeHead=="function"&&typeof i.end=="function")(t)?(t.writeHead(e.statusCode,r),e.body===null||e.body===void 0?t.end():e.isRaw||typeof e.body=="string"?t.end(e.body):t.end(JSON.stringify(e.body))):(console.warn("Unexpected response object type without writeHead/end methods"),t.writeHead?.(e.statusCode,r),e.body===null||e.body===void 0?t.end?.():e.isRaw||typeof e.body=="string"?t.end?.(e.body):t.end?.(JSON.stringify(e.body)))}setupGracefulShutdown(t){let e=n=>{console.log("Shutting down MikroServe server..."),n&&console.error("Error:",n),this.cleanupShutdownHandlers(),t.close(()=>{console.log("Server closed successfully"),process.env.NODE_ENV!=="test"&&process.env.VITEST!=="true"&&setImmediate(()=>process.exit(n?1:0))})},r=()=>e(),s=()=>e(),i=n=>e(n),o=n=>e(n);this.shutdownHandlers=[r,s,i,o],process.on("SIGINT",r),process.on("SIGTERM",s),process.on("uncaughtException",i),process.on("unhandledRejection",o)}cleanupShutdownHandlers(){if(this.shutdownHandlers.length>0){let[t,e,r,s]=this.shutdownHandlers;process.removeListener("SIGINT",t),process.removeListener("SIGTERM",e),process.removeListener("uncaughtException",r),process.removeListener("unhandledRejection",s),this.shutdownHandlers=[]}}};import Fs from"node:crypto";import{getRandomValues as Ls}from"node:crypto";var Ke=class{options;defaultLength=16;defaultOnlyLowerCase=!1;defaultStyle="extended";defaultUrlSafe=!0;constructor(t){if(this.options={},t)for(let[e,r]of Object.entries(t))this.options[e]=this.generateConfig(r)}add(t){if(!t?.name)throw new Error("Missing name for the ID configuration");let e=this.generateConfig(t);this.options[t.name]=e}remove(t){if(!t?.name)throw new Error("Missing name for the ID configuration");delete this.options[t.name]}create(t,e,r,s){let i=this.generateConfig({length:t,style:e,onlyLowerCase:r,urlSafe:s});return this.generateId(i)}custom(t){if(this.options[t])return this.generateId(this.options[t]);throw new Error(`No configuration found with name: ${t}`)}generateConfig(t){return{name:t?.name||"",length:t?.length||this.defaultLength,onlyLowerCase:t?.onlyLowerCase??this.defaultOnlyLowerCase,style:t?.style||this.defaultStyle,urlSafe:t?.urlSafe??this.defaultUrlSafe}}getCharacterSet(t,e,r){if(t==="hex")return e?"0123456789abcdef":"0123456789ABCDEFabcdef";if(t==="alphanumeric")return e?"abcdefghijklmnopqrstuvwxyz0123456789":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";if(t==="extended")return r?e?"abcdefghijklmnopqrstuvwxyz0123456789-._~":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~":e?"abcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$()*+,;=:";throw new Error(`Unknown ID style "${t} provided. Must be one of "extended" (default), "alphanumeric", or "hex".`)}generateId(t){let{length:e,onlyLowerCase:r,style:s,urlSafe:i}=t;if(e<0||e===0)throw new Error("ID length cannot be negative");let o=this.getCharacterSet(s,r,i),n=(2<<Math.log(o.length-1)/Math.LN2)-1,a=Math.ceil(1.6*n*e/o.length),c="";for(;c.length<e;){let l=new Uint8Array(a);Ls(l);for(let p=0;p<a;p++){let u=l[p]&n;if(u<o.length&&(c+=o[u],c.length===e))break}}return c}};var Hs=8,Ds=40,Ns=/^[a-zA-Z0-9_-]{1,40}$/;function he(){return new Ke().create(Hs,"alphanumeric",!1,!0)}function Vs(t){return Ns.test(t)}function qs(t){if(!Vs(t))throw new Error(`Invalid ID format: "${t}". IDs must be 1-${Ds} characters using only alphanumeric characters, underscores, and hyphens.`)}function Se(t){return t?(qs(t),t):he()}function Et(t,e,r){if(!r)return!1;let s=typeof t=="string"?[t]:t,i=typeof e=="string"?[e]:e;if(r.roles?.some(a=>a==="administrator"||a.id==="administrator"))return!0;let n=(r.roles||[]).flatMap(a=>typeof a=="string"?[]:(a.policies||[]).flatMap(l=>l?.permissions&&l?.targets?{permissions:l.permissions,targets:l.targets}:[]));for(let a of s)for(let c of i)if(!n.some(p=>At(p?.permissions,a)&&At(p?.targets,c)))return!1;return!0}function At(t,e){return!t||t.length===0?!1:t.some(r=>{if(r==="*"||r===e)return!0;if(r.endsWith("*")&&r!=="*"){let s=r.slice(0,-1);return e.startsWith(s)}return!1})}var b=class extends Error{constructor(e){super(),this.name="ValidationError",this.message=e||"Invalid input",this.cause={statusCode:400}}},me=class extends Error{constructor(e){super(),this.name="IdentityAlreadyExistsError",this.message=e||"Identity already exists",this.cause={statusCode:400}}},V=class extends Error{constructor(e){super(),this.name="NotFoundError",this.message=e||"Resource not found",this.cause={statusCode:404}}},be=class extends Error{constructor(e){super(),this.name="InvalidInputError",this.message=e||"Invalid input",this.cause={statusCode:400}}};var xe=class extends Error{constructor(e){super(),this.name="ConfigurationError",this.message=e||"Invalid configuration",this.cause={statusCode:400}}};var oe=class extends Error{constructor(e){super(),this.name="PermissionDeniedError",this.message=e||"Permission denied",this.cause={statusCode:403}}},F=class extends Error{constructor(e){super(),this.name="AlreadyExistsError",this.message=e||"Resource already exists",this.cause={statusCode:409}}},Ce=class extends Error{constructor(e){super(),this.name="PortInUseError",this.message=e||"Port already in use",this.cause={statusCode:409}}},fe=class extends Error{constructor(e){super(),this.name="RoleNotFoundError",this.message=e||"Role not found",this.cause={statusCode:404}}},Ie=class extends Error{constructor(e){super(),this.name="ProtectedResourceError",this.message=e||"Cannot modify protected resource",this.cause={statusCode:403}}},Ae=class extends Error{constructor(e){super(),this.name="ServiceRequestError",this.message=e||"Service request failed",this.cause={statusCode:502}}};var te=class{id;name;type;roles;metadata;constructor(e){let{id:r,name:s,type:i,roles:o,metadata:n}=this.createIdentity(e);this.id=r,this.name=s,this.type=i,this.roles=o,this.metadata=n}createIdentity(e){let r=e?.id||this.createId(),s=e?.name||"",i=e?.type||"service_account",o=e?.metadata||{},n=e?.roles||["user"];return e&&this.validate({id:r,name:s,type:i,metadata:o,roles:n}),{id:r,name:s,type:i,metadata:{...o,createdAt:new Date().toISOString()},roles:n}}changeName(e){this.name=e}changeEmail(e){this.metadata||(this.metadata={}),this.metadata.email=e}updateMetadata(e){let r=this.metadata?JSON.parse(JSON.stringify(this.metadata)):{};this.metadata={...r,...e}}updateRoles(e){this.validateRoles(e),this.roles=e}createId(){return Se()}isValidRoleId(e){return typeof e!="string"||e.length===0?!1:/^[a-z0-9]+(-[a-z0-9]+)*$/.test(e)}isValidType(e){return["user","service_account"].includes(e)}validate(e){let r=e.id||"",s=e.name||"",i=e.type||"",o=e.metadata||{},n=e.roles||[];if(!r)throw new b("Missing ID");if(!s)throw new b("Missing name");if(!i||!this.isValidType(i))throw new b("Missing or invalid type");if(i==="user"&&!o?.email)throw new b("Missing email for user identity");if(!n||n.length===0)throw new b("Must have at least one role");this.validateRoles(n)}validateRoles(e){(e||[]).forEach(r=>{let s=r.id||r;if(!this.isValidRoleId(s))throw new b(`Invalid role ID '${s}'`)})}can(e,r,s){return Et(e,r,s)}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.type=e.type,this.metadata=e.metadata||{},this.roles=e.roles,this}toDTO(){return{id:this.id,name:this.name,type:this.type,metadata:this.metadata,roles:this.roles}}};function kt(){return[{id:"administrator",name:"Administrator",description:"Full system administrator with all permissions",policies:[{permissions:["*"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!0,audit_level:"high"}}},{id:"user",name:"User",description:"Read-only access to resources",policies:[{permissions:["*.read"],targets:["*"]}],constraints:{assumable_by:{identities:[],roles:[],services:[]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"low"}}}]}var Ee=class{mikroAuth;db;tableName="identity_service";initialUser;roles;identities;serviceAccountTokens;constructor(e,r,s){this.mikroAuth=e,this.db=r,this.initialUser=s,this.roles=[],this.identities=[],this.serviceAccountTokens=new Map}async start(){await this.loadState(),this.roles.find(e=>e.id==="administrator")||this.createBaseRoles(),this.identities.length===0&&this.createInitialUser()}createBaseRoles(){let e=kt();for(let r of e)this.roles.push(r)}async createInitialUser(){let e=this.initialUser.email,r=this.initialUser.userName,s=await this.addUser(e,r,["administrator"]);return await this.mikroAuth.createMagicLink({email:e}),s}async getUserByEmail(e){let r=this.identities.find(s=>s.type==="user"&&s.metadata?.email===e&&s.metadata?.active!==!1);if(r)return this.enrichIdentityWithRoles(r)}async addUser(e,r,s=["user"],i=!0,o){if((await this.getUsers()).find(l=>l.metadata.email===e))throw new me(`User with email ${e} already exists`);let c=new te({id:o,name:r,type:"user",metadata:{email:e,active:i},roles:s}).toDTO();return this.identities.push(c),await this.saveState(),this.enrichIdentityWithRoles(c)}async addServiceAccount(e,r,s,i){if((await this.getServiceAccounts()).find(l=>l.name===e))throw new me(`Service account with name ${e} already exists`);let a=new te({id:i,name:e,type:"service_account",metadata:{description:r},roles:s}).toDTO(),c=this.generateServiceAccountToken();return this.serviceAccountTokens.set(c,a.id),this.identities.push(a),await this.saveState(),{...this.enrichIdentityWithRoles(a),apiKey:c}}async rotateServiceAccountKey(e){let r=await this.getIdentityById(e);if(!r||r.type!=="service_account")return;for(let[i,o]of this.serviceAccountTokens.entries())o===e&&this.serviceAccountTokens.delete(i);let s=this.generateServiceAccountToken();return this.serviceAccountTokens.set(s,e),await this.saveState(),s}async deleteIdentity(e){for(let[s,i]of this.serviceAccountTokens.entries())i===e&&this.serviceAccountTokens.delete(s);let r=this.identities.filter(s=>s.id!==e);this.identities=r,await this.saveState()}async updateIdentity(e,r){let s=await this.getIdentityById(e);if(!s)return;let i=new te().fromDTO(s);r?.name&&i.changeName(r.name),r?.email&&i.changeEmail(r.email),r?.roles&&i.updateRoles(r.roles),r?.metadata&&i.updateMetadata(r.metadata);let o=i.toDTO();return this.updateIdentityList(o),await this.saveState(),this.enrichIdentityWithRoles(o)}updateIdentityList(e){if(!e)throw new b("Cannot update identity list: updatedIdentity is null or undefined");let r=JSON.parse(JSON.stringify(this.identities)),s=r.find(i=>i.id===e.id);s&&Object.assign(s,e),this.identities=r}generateServiceAccountToken(){return`sa.${he()}.${Fs.randomBytes(32).toString("hex")}`}async getUserFromToken(e){if(e&&e.startsWith("Bearer ")){let r=e.split(" ")[1];if(r.startsWith("sa.")){let i=this.serviceAccountTokens.get(r);if(i){let o=this.identities.find(n=>n.id===i&&n.type==="service_account");if(o)return this.enrichIdentityWithRoles(o)}}else try{let i=this.mikroAuth.verify(r),o=i.email||i.sub;return this.getUserByEmail(o)}catch{let o=new Error("Invalid token");throw o.cause={statusCode:401},o}}}async getIdentityById(e){return this.identities.find(r=>r.id===e)}async getUserById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="user"))return this.enrichIdentityWithRoles(r)}async getServiceAccountById(e){let r=await this.getIdentityById(e);if(!(!r||r.type!=="service_account"))return this.enrichIdentityWithRoles(r)}async updateUser(e,r){let s=await this.getIdentityById(e);if(!(!s||s.type!=="user"))return this.updateIdentity(e,r)}async updateServiceAccount(e,r){let s=await this.getIdentityById(e);if(!s||s.type!=="service_account")return;let i={...r};return r.description!==void 0&&(i.metadata={...i.metadata||{},description:r.description},delete i.description),this.updateIdentity(e,i)}async deleteUser(e){let r=await this.getIdentityById(e);return!r||r.type!=="user"?!1:(await this.deleteIdentity(e),!0)}async deleteServiceAccount(e){let r=await this.getIdentityById(e);return!r||r.type!=="service_account"?!1:(await this.deleteIdentity(e),!0)}enrichIdentityWithRoles(e){let r=JSON.parse(JSON.stringify(e));return e.roles?r.roles=e.roles.map(s=>this.roles.find(i=>i.id===s)).filter(Boolean):r.roles=[],r}async getIdentities(){return this.identities}async getUsers(){return this.identities.filter(e=>e.type==="user")}async getServiceAccounts(){return this.identities.filter(e=>e.type==="service_account")}can(e,r,s){return new te(s).can(e,r,s)}async createCustomRole(e,r,s,i,o){if(this.roles.find(l=>l.id===e))throw new F(`Role with ID ${e} already exists`);let c={id:e,name:r,description:s,policies:[{permissions:i,targets:["*"]}],constraints:o||{assumable_by:{identities:[],roles:[],services:["functions"]},assumption_constraints:{max_duration:3600,require_reason:!1,audit_level:"medium"}}};this.roles.push(c),await this.saveState()}async updateRole(e,r){let s=this.roles.find(i=>i.id===e);if(!s)throw new fe(`Role with ID ${e} not found`);r.name!==void 0&&(s.name=r.name),r.description!==void 0&&(s.description=r.description),r.permissions!==void 0&&(s.policies=[{permissions:r.permissions,targets:["*"]}]),r.constraints!==void 0&&(s.constraints=r.constraints),await this.saveState()}async deleteRole(e){let r=this.roles.findIndex(s=>s.id===e);if(r===-1)throw new fe(`Role with ID ${e} not found`);if(e==="administrator"||e==="user")throw new Ie("Cannot delete base roles");this.roles.splice(r,1),await this.saveState()}async getRoles(){return this.roles}async getRole(e){return this.roles.find(r=>r.id===e)}async loadState(){let e=await this.load("identities");e&&(this.identities=e);let r=await this.load("roles");r&&(this.roles=r);let s=await this.load("serviceAccountTokens");if(s){let i=new Map(Object.entries(s));s&&Object.keys(s).length>0&&(this.serviceAccountTokens=i)}}async saveState(){let e=Object.fromEntries(this.serviceAccountTokens);await this.write("identities",this.identities),await this.write("roles",this.roles),await this.write("serviceAccountTokens",e)}async load(e){return await this.db.get(this.tableName,e)}async write(e,r){await this.db.write(this.tableName,e,r)}};import{existsSync as Bs}from"node:fs";import{spawn as _s}from"node:child_process";import{createServer as zs}from"node:net";var Pt={storage:{name:"storage",path:"./storage.mjs",port:3001,prefix:"/storage",args:["--force"]},functions:{name:"functions",path:"./functions.mjs",port:3002,prefix:"/functions",args:["--force"]},sites:{name:"sites",path:"./sites.mjs",port:3003,prefix:"/sites",args:["--force"]},databases:{name:"databases",path:"./databases.mjs",port:3004,prefix:"/databases",args:["--force"]},observability:{name:"observability",path:"./observability.mjs",port:3005,prefix:"/observability",args:["--force"]}},ke=class{db;services=new Map;tableName="molnosmanagement";environmentVariables={};setEnvironmentVariables(e){this.environmentVariables={...this.environmentVariables,...e}}async isPortInUse(e){return new Promise(r=>{let s=zs();s.once("error",i=>{i.code==="EADDRINUSE"?r(!0):r(!1)}),s.once("listening",()=>{s.close(),r(!1)}),s.listen(e,"127.0.0.1")})}constructor(e){this.db=new ee({databaseDirectory:e.dbPath})}getServiceDefinition(e){let r=e.toLowerCase(),s=Pt[r];if(!s)throw new V(`Unknown service: ${e}. Valid services are: ${Object.keys(Pt).join(", ")}`);return s}async start(){await this.db.start();let e=await this.db.get(this.tableName,"services")||[];for(let r of e)r.restartPolicy&&(r.restartPolicy.attempts=0),r.healthCheck&&(r.healthCheck.consecutiveFailures=0,r.healthCheck.consecutiveSuccesses=0,r.healthCheck.healthy=!0),this.services.set(r.name,r),r.active&&await this.startService(r.name)}async stopAllServices(){let e=[];for(let[r,s]of this.services.entries())(s.active||s.process)&&e.push(this.stopService(r));await Promise.all(e)}async shutdown(){await this.stopAllServices()}validateServiceConfig(e){if(!e.name||e.name.trim()==="")throw new b("Service name is required");if(!e.path||e.path.trim()==="")throw new b("Service path is required");if(!Bs(e.path))throw new xe(`Service path does not exist: ${e.path}`);if(!e.port||e.port<1||e.port>65535)throw new b(`Invalid port: ${e.port}`);if(!e.prefix||!e.prefix.startsWith("/"))throw new b("Service prefix must start with /")}cleanServiceForStorage(e){let{process:r,...s}=e;if(s.healthCheck){let{timer:i,consecutiveFailures:o,consecutiveSuccesses:n,healthy:a,...c}=s.healthCheck;s.healthCheck=c}if(s.restartPolicy){let{attempts:i,...o}=s.restartPolicy;s.restartPolicy=o}return s}async registerService(e,r){let s;typeof e=="string"?s={...this.getServiceDefinition(e),...r}:s=e,this.validateServiceConfig(s);let i={...s,active:!1},o=await this.getServices(),n=[],a=[];if(o.forEach(l=>{n.push(l.name),a.push(l.port)}),a.includes(i.port))throw new Ce(`Port ${i.port} already in use`);if(n.includes(i.name))throw new F(`Service with name '${i.name}' already exists`);i.restartPolicy&&(i.restartPolicy.attempts=0),i.healthCheck&&(i.healthCheck.consecutiveFailures=0,i.healthCheck.consecutiveSuccesses=0,i.healthCheck.healthy=!0),this.services.set(i.name,i);let c=await this.db.get(this.tableName,"services")||[];return c.push(this.cleanServiceForStorage(i)),await this.db.write(this.tableName,"services",c),i.name}async startService(e){let r=this.services.get(e);if(!r)return!1;if(r.process&&r.active)return!0;if(await this.isPortInUse(r.port))return console.error(`[ManagementService] Cannot start ${e}: Port ${r.port} is already in use. Please kill the process using this port.`),!1;try{let i={stdio:"pipe",detached:!1,env:{...process.env,...this.environmentVariables}},o=r.args||[],n=`--port=${r.port}`,a=_s("node",[r.path,n,...o],i);r.process=a,r.restartPolicy&&(r.restartPolicy.attempts=0);let c=!1;return a.stdout.on("data",l=>{let p=l.toString();console.log(`[${e}] ${p}`)}),a.stderr.on("data",l=>{let p=l.toString();console.error(`[${e}] ERROR: ${p}`),(p.includes("EADDRINUSE")||p.includes("address already in use"))&&(c=!0)}),a.on("exit",l=>{r.active=!1,r.process=void 0,c&&console.error(`[ManagementService] Service ${e} failed to start due to startup error`),this.handleServiceExit(r,l||0)}),await new Promise(l=>setTimeout(l,500)),r.process&&!r.process.killed?(r.active=!0,await this.updateServiceStatus(e,!0),r.healthCheck&&this.startHealthCheck(r),!0):(console.error(`[ManagementService] Service ${e} exited immediately after spawn`),!1)}catch(i){return console.error(`[ManagementService] Failed to start ${e}:`,i),!1}}startHealthCheck(e){let r=e.healthCheck;if(!r)return;let s=async()=>{if(e.healthCheck)try{let i=new AbortController,o=setTimeout(()=>i.abort(),e.healthCheck.timeoutMs),n=await fetch(`http://127.0.0.1:${e.port}${e.healthCheck.path}`,{signal:i.signal});clearTimeout(o),n.ok&&e.healthCheck?(e.healthCheck.consecutiveSuccesses=(e.healthCheck.consecutiveSuccesses||0)+1,e.healthCheck.consecutiveFailures=0,e.healthCheck.consecutiveSuccesses>=e.healthCheck.successThreshold&&(e.healthCheck.healthy=!0)):this.handleHealthCheckFailure(e)}catch{this.handleHealthCheckFailure(e)}};r.timer=setInterval(s,r.intervalMs)}handleHealthCheckFailure(e){e.healthCheck&&(e.healthCheck.consecutiveFailures=(e.healthCheck.consecutiveFailures||0)+1,e.healthCheck.consecutiveSuccesses=0,e.healthCheck.consecutiveFailures>=e.healthCheck.failureThreshold&&(e.healthCheck.healthy=!1,e.healthCheck.restartOnFailure&&this.restartService(e.name).catch(()=>{})))}async handleServiceExit(e,r){if(await this.updateServiceStatus(e.name,!1),!e.restartPolicy||e.restartPolicy.type==="never"||e.restartPolicy.type==="on-failure"&&r===0||e.restartPolicy.maxAttempts>0&&(e.restartPolicy.attempts||0)>=e.restartPolicy.maxAttempts)return;e.restartPolicy.attempts=(e.restartPolicy.attempts||0)+1;let s=e.restartPolicy.backoffMs;setTimeout(()=>{this.startService(e.name).catch(()=>{})},s)}async stopService(e){let r=this.services.get(e);if(!r)return!1;if(r.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),!r.process)return r.active&&(r.active=!1,await this.updateServiceStatus(e,!1)),!0;try{let s=i=>{try{return process.kill(i,0),!0}catch{return!1}};if(r.process?.pid){let i=r.process.pid;if(s(i))try{process.kill(i,"SIGTERM");let o=setTimeout(()=>{try{s(i)&&process.kill(i,"SIGKILL")}catch{}},5*1e3);await new Promise(n=>{if(!r.process){clearTimeout(o),n();return}let a=setTimeout(()=>{clearTimeout(o),n()},6*1e3);r.process.once("exit",()=>{clearTimeout(o),clearTimeout(a),n()})})}catch(o){if(o.code!=="ESRCH")throw o}}return r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1),!0}catch{return r.active&&(r.active=!1,r.process=void 0,await this.updateServiceStatus(e,!1)),!1}}async restartService(e){return await this.stopService(e),await new Promise(r=>setTimeout(r,500)),this.startService(e)}async updateServiceStatus(e,r){let s=await this.db.get(this.tableName,"services")||[],i=s.findIndex(o=>o.name===e);if(i>=0){let o=this.services.get(e);o?(o.active=r,s[i]=this.cleanServiceForStorage(o)):s[i].active=r,await this.db.write(this.tableName,"services",s)}}async getService(e){return(await this.getServices()).find(s=>s.name===e)}async getServices(){return(await this.db.get(this.tableName,"services")||[]).map(({process:r,...s})=>s)}async getServiceStats(e){let r=this.services.get(e);return r?{name:r.name,active:r.active,health:r.healthCheck?{healthy:r.healthCheck.healthy||!1,consecutiveSuccesses:r.healthCheck.consecutiveSuccesses||0,consecutiveFailures:r.healthCheck.consecutiveFailures||0}:null,restarts:r.restartPolicy&&r.restartPolicy.attempts||0}:null}async updateService(e,r){let s=this.services.get(e);if(!s)return!1;let i=s.active;Object.assign(s,r);let o=await this.db.get(this.tableName,"services")||[],n=o.findIndex(a=>a.name===e);return n>=0?(o[n]=this.cleanServiceForStorage(s),await this.db.write(this.tableName,"services",o),i!==s.active&&(s.active?await this.startService(e):await this.stopService(e)),!0):!1}async removeService(e){let r=this.services.get(e);r?.healthCheck?.timer&&(clearInterval(r.healthCheck.timer),r.healthCheck.timer=void 0),await this.stopService(e),this.services.delete(e);let i=(await this.db.get(this.tableName,"services")||[]).filter(o=>o.name!==e);return await this.db.write(this.tableName,"services",i),!0}};var _=class{id;name;description;redirectUris;metadata;owners;constructor(e){let r=this.createApplication(e);this.id=r.id,this.name=r.name,this.description=r.description,this.redirectUris=r.redirectUris,this.metadata=r.metadata,this.owners=r.owners}createApplication(e){let r=e?.id||this.createId(),s=e?.name||"",i=e?.description,o=e?.redirectUris||[],n=e?.owners||[],a=new Date().toISOString(),c={...e?.metadata||{},createdAt:e?.metadata?.createdAt||a,updatedAt:a,createdBy:e?.metadata?.createdBy||n[0]||""};return e&&this.validate({id:r,name:s,description:i,redirectUris:o,metadata:c,owners:n}),{id:r,name:s,description:i,redirectUris:o,metadata:c,owners:n}}update(e){e.name!==void 0&&(this.name=e.name),e.description!==void 0&&(this.description=e.description),e.redirectUris!==void 0&&(this.validateRedirectUris(e.redirectUris),this.redirectUris=e.redirectUris),e.owners!==void 0&&(this.owners=e.owners),e.metadata!==void 0?this.metadata={...this.metadata,...e.metadata,updatedAt:new Date().toISOString()}:this.metadata.updatedAt=new Date().toISOString(),this.validate(this.toDTO())}isValidRedirectUri(e){return this.redirectUris.includes(e)}isOwner(e){return this.owners.includes(e)}createId(){return Se()}validate(e){let{id:r,name:s,redirectUris:i,metadata:o,owners:n}=e;if(!r)throw new b("Missing ID");if(!s||s.trim().length===0)throw new b("Missing or invalid name");if(!o)throw new b("Missing metadata");if(!n||n.length===0)throw new b("Application must have at least one owner");this.validateRedirectUris(i)}validateRedirectUris(e){if(!e||e.length===0)throw new b("At least one redirect URI is required");for(let r of e)try{let s=new URL(r);if(s.protocol!=="https:"&&s.protocol!=="http:"&&s.protocol!=="custom:")throw new b(`Invalid redirect URI protocol: ${r}. Must use https:, http: (localhost only), or custom: (for native apps)`);s.protocol==="http:"&&!s.hostname.match(/^(localhost|127\.0\.0\.1|\[::1\])$/)&&console.warn(`Warning: HTTP redirect URI detected for non-localhost: ${r}. HTTPS is recommended for production.`)}catch{throw new b(`Invalid redirect URI format: ${r}`)}}fromDTO(e){return this.validate(e),this.id=e.id,this.name=e.name,this.description=e.description,this.redirectUris=e.redirectUris,this.metadata=e.metadata,this.owners=e.owners,this}toDTO(){return{id:this.id,name:this.name,description:this.description,redirectUris:this.redirectUris,metadata:this.metadata,owners:this.owners}}};var Pe=class{db;tableName="molnosapplications";key="applications";applications;constructor(e){this.db=new ee({databaseDirectory:e.dbPath}),this.applications=[]}async start(){await this.db.start(),await this.loadState()}async loadState(){let e=await this.db.get(this.tableName,this.key)||[];this.applications=e}async saveState(){await this.db.write(this.tableName,this.key,this.applications)}async createApplication(e,r){let s=e.owners.includes(r)?e.owners:[...e.owners,r],o=new _({...e,owners:s,metadata:{...e.metadata,createdBy:r}}).toDTO();if(this.applications.find(a=>a.name===o.name))throw new F(`Application with name '${o.name}' already exists`);return this.applications.push(o),await this.saveState(),o}async getApplication(e,r){let s=this.applications.find(o=>o.id===e);if(!s)throw new V(`Application with ID '${e}' not found`);let i=new _().fromDTO(s);if(r&&!i.isOwner(r))throw new oe("Only application owners can view application details");return i.toDTO()}async getApplicationById(e){return this.applications.find(s=>s.id===e)||null}async listApplications(e){return this.applications.filter(r=>r.owners.includes(e)).map(r=>new _().fromDTO(r).toDTO())}async updateApplication(e,r,s){let i=this.applications.findIndex(a=>a.id===e);if(i===-1)throw new V(`Application with ID '${e}' not found`);let o=this.applications[i],n=new _().fromDTO(o);if(!n.isOwner(s))throw new oe("Only application owners can update the application");if(r.owners&&r.owners.length===0)throw new b("Application must have at least one owner");if(r.owners&&!r.owners.includes(s)&&o.owners.length===1&&o.owners[0]===s)throw new b("Cannot remove yourself as the last owner of the application");return n.update(r),this.applications[i]=n.toDTO(),await this.saveState(),this.applications[i]}async deleteApplication(e,r){let s=this.applications.findIndex(n=>n.id===e);if(s===-1)throw new V(`Application with ID '${e}' not found`);let i=this.applications[s];if(!new _().fromDTO(i).isOwner(r))throw new oe("Only application owners can delete the application");this.applications.splice(s,1),await this.saveState()}async validateRedirectUri(e,r){let s=await this.getApplicationById(e);return s?new _().fromDTO(s).isValidRedirectUri(r):!1}getAllApplicationsInternal(){return this.applications}};async function w(t){return t.body||{}}function m(t,e="An error occurred"){let r=t?.message||t||e,s=t?.cause?.statusCode||t?.statusCode||400;return{message:r,status:s}}async function Tt(t,e,r,s){try{let i=await w(t),{email:o,redirectUrl:n,applicationId:a}=i;if(!o)return t.json({error:"Email is required"},400);let c=o.trim().toLowerCase(),l;if(n){if(!a)return t.json({error:"applicationId is required when redirectUrl is provided"},400);if(!await s.validateRedirectUri(a,n))return t.json({error:"Invalid redirectUrl or applicationId. The redirect URI must be registered for this application."},400);l=(await s.getApplicationById(a))?.name}let p=!1;return await e.getUserByEmail(c)&&(p=!0),p&&await r.createMagicLink({email:c,subject:`Sign In To ${l||"MolnOS"}`,appUrl:n,metadata:{appName:l,redirectUrl:n,applicationId:a}}),t.json({success:!0,message:"If a matching account was found, a magic link has been sent."})}catch(i){let{message:o,status:n}=m(i,"Error during login");return t.json({error:o},n)}}async function Rt(t,e,r){try{let s=t.query.token,i=t.query.email;if(!s)return t.json({error:"Token is required as query parameter"},400);if(!i)return t.json({error:"Email is required as query parameter"},400);try{let o=await e.verifyToken({token:s,email:i});if(!o)return t.json({error:"Invalid or expired token"},401);let n=o.metadata;if(n?.redirectUrl&&n?.applicationId)if(await r.validateRedirectUri(n.applicationId,n.redirectUrl)){let c=new URL(n.redirectUrl);return c.searchParams.set("access_token",o.accessToken),c.searchParams.set("refresh_token",o.refreshToken),c.searchParams.set("expires_in",o.expiresIn.toString()),t.redirect(c.toString(),302)}else return console.warn(`Redirect URL validation failed for applicationId: ${n.applicationId}`),t.json({...o,warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);return t.json(o,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(s){let{message:i,status:o}=m(s,"Error verifying token");return t.json({error:i},o)}}async function jt(t,e){try{let s=(await w(t)).refreshToken;if(!s)return t.json({error:"Value for refreshToken is required"},400);try{let i=await e.refreshAccessToken(s);return t.json(i,200)}catch{return t.json({error:"Invalid or expired token"},401)}}catch(r){let{message:s,status:i}=m(r,"Error refreshing token");return t.json({error:s},i)}}async function $t(t){try{let e=t.state.user;return t.json(e)}catch(e){let{message:r,status:s}=m(e,"Error getting user info");return t.json({error:r},s)}}function f(t,e,r={}){let s=Array.isArray(e)?e:[e];if(!t||!t.roles||!Array.isArray(t.roles)){let n=new Error("Unauthorized: User or roles missing");throw n.cause={statusCode:403},n}let i=t.roles.flatMap(n=>n?.policies?.flatMap(a=>(a?.permissions||[]).flatMap(l=>typeof l=="string"?l:l&&typeof l=="object"&&l.actions?l.actions:[]))||[]);if(!s.every(n=>i.includes(n)||i.includes("*")?!0:i.some(a=>{if(a.endsWith(".*")){let c=a.slice(0,-2);return n.startsWith(`${c}.`)}return!1}))){let n=new Error("Unauthorized");throw n.cause={statusCode:403},n}return!0}async function Ot(t,e){try{let r=t.state.user;f(r,"identity.identities.get",{});let s=await e.getIdentities();return t.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting identities");return t.json({error:s},i)}}var U=class{isSilent;propertyPath="";constructor(t=!1){this.isSilent=t}test(t,e){if(!e)throw new Error("Missing input!");this.updatePropertyPath();let{results:r,errors:s}=this.validate(t.properties,e),i=this.compileErrors(r,s),o=this.isSuccessful(r,i);return{errors:i,success:o}}compileErrors(t,e){let r=t.filter(s=>s.success===!1);return[...e,...r].flatMap(s=>s)}isSuccessful(t,e){return t.every(r=>r.success===!0)&&e.length===0}validate(t,e,r=[],s=[]){let i=t?.additionalProperties??!0,o=t?.required||[];s=this.checkForRequiredKeysErrors(o,e,s),s=this.checkForDisallowedProperties(Object.keys(e),Object.keys(t),s,i);for(let n in t){let a=o.includes(n)&&n!=="required",c=t[n],l=e[n],p=c.additionalProperties??!0;a&&(s=this.checkForRequiredKeysErrors(c.required||[],l,s)),this.isDefined(l)&&(this.handleValidation(n,l,c,r),s=this.checkForDisallowedProperties(Object.keys(l),Object.keys(c),s,p),this.handleNestedObject(l,c,r,s))}return{results:r,errors:s}}updatePropertyPath(t,e=""){if(!t){this.propertyPath="";return}e&&(this.propertyPath=e),this.propertyPath=`${this.propertyPath}.${t}`,this.propertyPath.startsWith(".")&&(this.propertyPath=this.propertyPath.substring(1,this.propertyPath.length))}isDefined(t){return!!(typeof t=="number"&&t===0||t||t===""||typeof t=="boolean")}checkForRequiredKeysErrors(t,e,r){if(!this.areRequiredKeysPresent(t,e)){let s=e?Object.keys(e):[],i=this.findNonOverlappingElements(t,s),o=i.length>0?`Missing the required key: '${i.join(", ")}'!`:`Missing values for required keys: '${s.filter(n=>!e[n]).join(", ")}'!`;r.push({key:"",value:e,success:!1,error:o})}return r}checkForDisallowedProperties(t,e,r,s){if(!s){let i=this.findNonOverlappingElements(t,e);i.length>0&&r.push({key:`${e}`,value:t,success:!1,error:`Has additional (disallowed) properties: '${i.join(", ")}'!`})}return r}handleValidation(t,e,r,s){this.updatePropertyPath(t);let i=this.validateProperty(this.propertyPath,r,e);s.push(...i);let o=(a,c)=>{a.forEach(l=>{let p=this.validateProperty(this.propertyPath,c.items,l);s.push(...p)}),this.updatePropertyPath()},n=a=>{let c=Object.keys(a),l=this.propertyPath;c.forEach(p=>{if(this.updatePropertyPath(p,l),this.isArray(a[p])&&r[p]?.items!=null)o(a[p],r[p]);else{let u=this.validateProperty(this.propertyPath,r[p],a[p]);s.push(...u)}})};this.isArray(e)&&r.items!=null?o(e,r):this.isObject(e)?n(e):this.updatePropertyPath()}handleNestedObject(t,e,r,s){if(this.isObject(t)){let i=this.getNestedObjects(t);for(let o of i){let n=e[o],a=t[o];n&&typeof a=="object"&&this.validate(n,a,r,s)}}}getNestedObjects(t){return Object.keys(t).filter(e=>{if(this.isObject(t))return e})}findNonOverlappingElements(t,e){return t.filter(r=>!e.includes(r))}areRequiredKeysPresent(t,e=[]){return t.every(r=>Object.keys(e).includes(r)?this.isDefined(e[r]):!1)}validateProperty(t,e,r){return this.validateInput(e,r).map(i=>{let{success:o,error:n}=i;return{key:t,value:r,success:o,error:n??""}})}validateInput(t,e){if(t){let r=[{condition:()=>t.type,validator:()=>this.isCorrectType(t.type,e),error:"Invalid type"},{condition:()=>t.format,validator:()=>this.isCorrectFormat(t.format,e),error:"Invalid format"},{condition:()=>t.minLength,validator:()=>this.isMinimumLength(t.minLength,e),error:"Length too short"},{condition:()=>t.maxLength,validator:()=>this.isMaximumLength(t.maxLength,e),error:"Length too long"},{condition:()=>t.minValue,validator:()=>this.isMinimumValue(t.minValue,e),error:"Value too small"},{condition:()=>t.maxValue,validator:()=>this.isMaximumValue(t.maxValue,e),error:"Value too large"},{condition:()=>t.matchesPattern,validator:()=>this.matchesPattern(t.matchesPattern,e),error:"Pattern does not match"}],s=[];for(let i of r)i.condition()&&!i.validator()&&s.push({success:!1,error:i.error});return s}else this.isSilent||console.warn(`Missing property '${t}' for match '${e}'. Skipping...`);return[{success:!0}]}isCorrectType(t,e){return Array.isArray(t)||(t=[t]),t.some(r=>{switch(r){case"string":return typeof e=="string";case"number":return typeof e=="number"&&!isNaN(e);case"boolean":return typeof e=="boolean";case"object":return this.isObject(e);case"array":return this.isArray(e)}})}isObject(t){return t!==null&&!this.isArray(t)&&typeof t=="object"&&t instanceof Object&&Object.prototype.toString.call(t)==="[object Object]"}isArray(t){return Array.isArray(t)}isCorrectFormat(t,e){switch(t){case"alphanumeric":return new RegExp(/^[a-zA-Z0-9]+$/).test(e);case"numeric":return new RegExp(/^-?\d+(\.\d+)?$/).test(e);case"email":return new RegExp(/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/).test(e);case"date":return new RegExp(/^\d{4}-\d{2}-\d{2}$/).test(e);case"url":return new RegExp(/^(https?):\/\/[^\s$.?#].[^\s]*$/).test(e);case"hexColor":return new RegExp(/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i).test(e)}}isMinimumLength(t,e){return Array.isArray(e)?e.length>=t:e?.toString().length>=t}isMaximumLength(t,e){return Array.isArray(e)?e.length<=t:e.toString().length<=t}isMinimumValue(t,e){return e>=t}isMaximumValue(t,e){return e<=t}matchesPattern(t,e){return new RegExp(t).test(e)}schemaFrom(t){let e={properties:{additionalProperties:!1,required:[]}};for(let r in t){let s=t[r];e.properties.required.push(r),Array.isArray(s)?e.properties[r]=this.generateArraySchema(s):typeof s=="object"&&s!==null?e.properties[r]=this.generateNestedObjectSchema(s):e.properties[r]=this.generatePropertySchema(s)}return e}generateArraySchema(t){let e={type:"array"},r=t.filter(s=>s);if(r.length>0){let s=r[0];r.every(o=>typeof o==typeof s)?typeof s=="object"&&!Array.isArray(s)?e.items=this.generateNestedObjectSchema(s):e.items=this.generatePropertySchema(s):console.warn("All elements in array are not of the same type. Unable to generate a schema for these elements.")}return e}generateNestedObjectSchema(t){let e={type:"object",additionalProperties:!1,required:[]};for(let r in t){let s=t[r];e.required.push(r),typeof s=="object"&&!Array.isArray(s)&&s!==null?e[r]=this.generateNestedObjectSchema(s):e[r]=this.generatePropertySchema(s)}return e}generatePropertySchema(t){let e=typeof t,r={type:e};return e==="string"&&(r.minLength=1),r}};var Ut={properties:{id:{type:"string",minLength:1,maxLength:40,pattern:"^[a-zA-Z0-9_-]{1,40}$"},email:{type:"string",format:"email",minLength:3},name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}},verified:{type:"boolean"}},required:["email","name","roles"],additionalProperties:!1};var Ws=new U(!0);async function Mt(t,e){try{let r=t.state.user,s=await w(t),i=Ws.test(Ut,s);if(!i.success)return t.json({error:"Invalid input",details:i.errors},400);f(r,"identity.user.create",{});let{id:o,email:n,name:a,roles:c,verified:l}=s,p=await e.addUser(n,a,c,l,o);return t.json(p,201)}catch(r){let{message:s,status:i}=m(r,"Error creating user");return t.json({error:s},i)}}async function Lt(t,e){try{let r=t.state.user;f(r,"identity.user.get",{});let s=await e.getUsers();return t.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting users");return t.json({error:s},i)}}function E(t){return t.params}async function Ht(t,e){try{let r=t.state.user,s=E(t),{userId:i}=s;f(r,"identity.user.get",{userId:i});let o=await e.getUserById(i);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting user");return t.json({error:s},i)}}var Dt={properties:{name:{type:"string",minLength:1},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var Js=new U(!0);async function Nt(t,e){try{let r=t.state.user,s=E(t),{userId:i}=s,o=await w(t),n=Js.test(Dt,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);f(r,"identity.user.update",{userId:i});let{name:a,roles:c}=o,l=await e.updateUser(i,{name:a,roles:c});return l?t.json(l):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error updating user");return t.json({error:s},i)}}async function Vt(t,e){try{let r=t.state.user,s=E(t),{userId:i}=s;return f(r,"identity.user.delete",{userId:i}),await e.deleteUser(i)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting user");return t.json({error:s},i)}}async function qt(t,e){try{let r=t.state.user,s=await w(t);if(!s.name||!s.description||!s.roles||!Array.isArray(s.roles))return t.json({error:"Invalid input: name, description, and roles are required"},400);if(!["user","administrator"].every(l=>s.roles.includes(l)||!s.roles.includes(l)))return t.json({error:"Invalid input: roles must be user or administrator"},400);f(r,"identity.service-account.create",{});let{id:i,name:o,description:n,roles:a}=s,c=await e.addServiceAccount(o,n,a,i);return t.json(c,201)}catch(r){let{message:s,status:i}=m(r,"Error creating service account");return t.json({error:s},i)}}async function Ft(t,e){try{let r=t.state.user;f(r,"identity.service-account.get",{});let s=await e.getServiceAccounts();return t.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting service accounts");return t.json({error:s},i)}}async function Bt(t,e){try{let r=t.state.user,s=E(t),{serviceAccountId:i}=s;f(r,"identity.service-account.get",{serviceAccountId:i});let o=await e.getServiceAccountById(i);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting service account");return t.json({error:s},i)}}var _t={properties:{name:{type:"string",minLength:1,maxLength:100},description:{type:"string",minLength:1,maxLength:500},roles:{type:"array",items:{type:"string"}}},additionalProperties:!1};var Ys=new U(!0);async function zt(t,e){try{let r=t.state.user,s=E(t),{serviceAccountId:i}=s,o=await w(t),n=Ys.test(_t,o);if(!n.success)return t.json({error:"Invalid input",details:n.errors},400);f(r,"identity.service-account.update",{serviceAccountId:i});let{name:a,description:c,roles:l}=o,p=await e.updateServiceAccount(i,{name:a,description:c,roles:l});return p?t.json(p):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error updating service account");return t.json({error:s},i)}}async function Gt(t,e){try{let r=t.state.user,s=E(t),{serviceAccountId:i}=s;return f(r,"identity.service-account.delete",{serviceAccountId:i}),await e.deleteServiceAccount(i)?t.body(null,204):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting service account");return t.json({error:s},i)}}async function Wt(t,e){try{let r=t.state.user,s=E(t),{serviceAccountId:i}=s;f(r,"identity.service-account.update",{serviceAccountId:i});let o=await e.rotateServiceAccountKey(i);return o?t.json({apiKey:o}):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error rotating service account key");return t.json({error:s},i)}}async function Kt(t,e){try{let r=t.state.user;f(r,"identity.role.get",{});let s=await e.getRoles();return t.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting roles");return t.json({error:s},i)}}async function Jt(t,e){try{let r=t.state.user;f(r,"identity.role.get",{});let s=t.params.roleId;if(!s)return t.text("Missing role ID",400);let i=await e.getRole(s);return i?t.json(i):t.text("Role not found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting role");return t.json({error:s},i)}}async function Xt(t,e){try{let r=t.state.user;f(r,"identity.role.create",{});let s=await w(t),{roleId:i,name:o,description:n,permissions:a,constraints:c}=s;if(!i||!o||!n)return t.text("Missing required fields: roleId, name, description",400);if(!a||!Array.isArray(a))return t.text("Permissions must be a non-empty array",400);await e.createCustomRole(i,o,n,a,c);let l=await e.getRole(i);return t.json(l,201)}catch(r){let{message:s,status:i}=m(r,"Error creating role");return t.json({error:s},i)}}async function Yt(t,e){try{let r=t.state.user;f(r,"identity.role.update",{});let s=t.params.roleId;if(!s)return t.text("Missing role ID",400);let i=await w(t),{name:o,description:n,permissions:a,constraints:c}=i;await e.updateRole(s,{name:o,description:n,permissions:a,constraints:c});let l=await e.getRole(s);return t.json(l)}catch(r){let{message:s,status:i}=m(r,"Error updating role");return t.json({error:s},i)}}async function Zt(t,e){try{let r=t.state.user;f(r,"identity.role.delete",{});let s=t.params.roleId;return s?s==="administrator"||s==="user"?t.text("Cannot delete base roles",403):(await e.deleteRole(s),t.json({message:"Role deleted successfully"})):t.text("Missing role ID",400)}catch(r){let{message:s,status:i}=m(r,"Error deleting role");return t.json({error:s},i)}}async function Qt(t,e){try{let r=t.state.user;f(r,"management.services.get",{});let s=await e.getServices();return t.json(s)}catch(r){let{message:s,status:i}=m(r,"Error getting services");return t.json({error:s},i)}}async function er(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;f(r,"management.service.get",{serviceName:i});let o=await e.getService(i);return o?t.json(o):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error getting service");return t.json({error:s},i)}}var tr={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var Qs=new U(!0);async function rr(t,e){try{let r=t.state.user,s=await w(t),i=Qs.test(tr,s);if(!i.success)return t.json({error:"Invalid input",details:i.errors},400);if(f(r,"management.service.create",{}),["storage","functions","sites","databases","observability"].includes(s.name.toLowerCase())){let{name:a,...c}=s;await e.registerService(a,c)}else{if(!s.path||!s.port||!s.prefix)return t.json({error:"Custom services require: name, path, port, and prefix"},400);await e.registerService(s)}return t.text("",201)}catch(r){let{message:s,status:i}=m(r,"Error creating service");return t.json({error:s},i)}}var sr={properties:{name:{type:"string",minLength:1},path:{type:"string"},port:{type:"number",minValue:1,maxValue:65535},prefix:{type:"string"},healthCheckPath:{type:"string"},healthCheckInterval:{type:"number",minValue:1e3}},required:["name"],additionalProperties:!0};var ti=new U(!0);async function ir(t,e){try{let r=t.state.user,s=E(t),i=await w(t),o=i.name,n=ti.test(sr,i);return n.success?(f(r,"management.service.update",{serviceName:o}),o!==s.serviceName?t.json({error:"Service name in body must match path parameter"},400):(await e.updateService(o,i),t.body(null,204))):t.json({error:"Invalid input",details:n.errors},400)}catch(r){let{message:s,status:i}=m(r,"Error updating service");return t.json({error:s},i)}}async function or(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;return f(r,"management.service.delete",{serviceName:i}),await e.getService(i)?(await e.removeService(i),t.body(null,204)):t.text("Not Found",404)}catch(r){let{message:s,status:i}=m(r,"Error deleting service");return t.json({error:s},i)}}async function nr(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;if(f(r,"management.service.start",{serviceName:i}),!await e.getService(i))return t.text("Not Found",404);let n=await e.startService(i);return t.json({serviceName:i,isStarted:n})}catch(r){let{message:s,status:i}=m(r,"Error starting service");return t.json({error:s},i)}}async function ar(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;if(f(r,"management.service.stop",{serviceName:i}),!await e.getService(i))return t.text("Not Found",404);let n=await e.stopService(i);return t.json({serviceName:i,isStopped:n})}catch(r){let{message:s,status:i}=m(r,"Error stopping service");return t.json({error:s},i)}}async function cr(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;if(f(r,"management.service.get",{serviceName:i}),!await e.getService(i))return t.text("Not Found",404);let n=new URL("http://127.0.0.1:3005/events");n.searchParams.set("service",i),t.query.startTime&&n.searchParams.set("startTime",t.query.startTime),t.query.endTime&&n.searchParams.set("endTime",t.query.endTime),t.query.level&&n.searchParams.set("level",t.query.level),t.query.limit&&n.searchParams.set("limit",t.query.limit),t.query.offset&&n.searchParams.set("offset",t.query.offset);let a=await fetch(n.toString());if(!a.ok)throw new Ae(`Observability service error: ${a.statusText}`);let c=await a.json();return t.json({serviceName:i,logs:c.events,count:c.count})}catch(r){let{message:s,status:i}=m(r,"Error getting service logs");return t.json({error:s},i)}}async function lr(t,e){try{let r=t.state.user,s=E(t),{serviceName:i}=s;if(f(r,"management.service.start",{serviceName:i}),!await e.getService(i))return t.text("Not Found",404);let n=await e.restartService(i);return t.json({serviceName:i,restarted:n})}catch(r){let{message:s,status:i}=m(r,"Error restarting service");return t.json({error:s},i)}}function ur(t,e,r,s,i){let o=t.find(c=>c.service===e);if(!o){let c=new Error(`Function bindings do not include access to service: ${e}`);throw c.cause={statusCode:403},c}if(!o.permissions||o.permissions.length===0)return;for(let c of o.permissions)if(!(c.resource&&c.resource!==r)){if(!c.resource||!c.actions||c.actions.length===0)return;if(c.actions.includes(s)){if(c.targets&&c.targets.length>0){if(!i)return;if(!c.targets.includes(i))continue}return}}let n=i?`:${i}`:"",a=new Error(`Function bindings do not allow: ${e}.${r}.${s}${n}`);throw a.cause={statusCode:403},a}async function k(t,e,r,s,i,o,n){if(!e)return null;let a=t.state.user;if(!a){let l=t.headers.authorization;if(!l){let u=new Error("Unauthorized: No authentication provided");throw u.cause={statusCode:401},u}let p=l.replace(/^Bearer\s+/i,"");if(a=await e.getUserFromToken(p),!a){let u=new Error("Unauthorized: Invalid token");throw u.cause={statusCode:401},u}}f(a,r,{});let c=t.headers["x-function-bindings"];if(c)try{let l=Array.isArray(c)?c[0]:c,p=JSON.parse(l);ur(p,s,i,o,n)}catch(l){if(l.cause?.statusCode===403)throw l;let p=new Error("Invalid function bindings header");throw p.cause={statusCode:400},p}return a}async function dr(t,e,r){try{let s=await w(t),i=t.state.requestingIdentity;if(!i)return t.json({error:"Authentication required"},401);let{id:o,name:n,description:a,redirectUris:c,metadata:l}=s;if(!n||!c)return t.json({error:"Missing required fields: name and redirectUris are required"},400);await k(t,r,"applications.application.create","applications","application","create",n);let p={id:o,name:n,description:a,redirectUris:Array.isArray(c)?c:[c],metadata:l,owners:[i.id]},u=await e.createApplication(p,i.id);return t.json(u,201)}catch(s){let{message:i,status:o}=m(s,"Error creating application");return t.json({error:i},o)}}async function pr(t,e,r){try{let s=t.params.id,i=t.state.requestingIdentity;if(!s)return t.json({error:"Application ID is required"},400);await k(t,r,"applications.application.read","applications","application","read",s);let o=await e.getApplication(s,i?.id);return t.json(o,200)}catch(s){let{message:i,status:o}=m(s,"Error retrieving application");return t.json({error:i},o)}}async function hr(t,e,r){try{let s=t.state.requestingIdentity;if(!s)return t.json({error:"Authentication required"},401);await k(t,r,"applications.application.list","applications","application","list");let i=await e.listApplications(s.id);return t.json({applications:i,total:i.length},200)}catch(s){let{message:i,status:o}=m(s,"Error listing applications");return t.json({error:i},o)}}async function mr(t,e,r){try{let s=t.params.id,i=await w(t),o=t.state.requestingIdentity;if(!o)return t.json({error:"Authentication required"},401);if(!s)return t.json({error:"Application ID is required"},400);await k(t,r,"applications.application.update","applications","application","update",s);let n=await e.updateApplication(s,i,o.id);return t.json(n,200)}catch(s){let{message:i,status:o}=m(s,"Error updating application");return t.json({error:i},o)}}async function fr(t,e,r){try{let s=t.params.id,i=t.state.requestingIdentity;return i?s?(await k(t,r,"applications.application.delete","applications","application","delete",s),await e.deleteApplication(s,i.id),t.json({success:!0,message:"Application deleted"},200)):t.json({error:"Application ID is required"},400):t.json({error:"Authentication required"},401)}catch(s){let{message:i,status:o}=m(s,"Error deleting application");return t.json({error:i},o)}}async function gr(t,e,r){try{await k(t,r,"contexts.context.get","contexts","context","get");let i=e.listContexts().map(o=>{let n=e.getContextResources(o.name);return{...o,resourceCount:{functions:n.functions.length,databases:n.databases.length,storage:n.storage.length,sites:n.sites.length,users:n.users.length,serviceAccounts:n.serviceAccounts.length}}});return t.json({success:!0,contexts:i},200)}catch(s){let{message:i,status:o}=m(s,"Error listing contexts");return t.json({error:i},o)}}async function yr(t,e,r){try{let s=t.params.contextName;if(!s)return t.json({error:"contextName parameter is required"},400);await k(t,r,"contexts.context.get","contexts","context","get",s);let i=e.getContextWithResources(s);return t.json({success:!0,context:i},200)}catch(s){let{message:i,status:o}=m(s,"Error getting context");return t.json({error:i},o)}}var ri=new U(!0),si={properties:{name:{type:"string",minLength:1,maxLength:64},intent:{type:"string",maxLength:1e3},attributes:{type:"object"}},required:["name"],additionalProperties:!1};async function vr(t,e,r){try{let s=await w(t),i=ri.test(si,s);if(!i.success)return t.json({error:"Invalid input",details:i.errors},400);await k(t,r,"contexts.context.create","contexts","context","create");let{name:o,intent:n,attributes:a}=s,c=await e.createContext({name:o,intent:n,attributes:a});return t.json({success:!0,context:c},201)}catch(s){let{message:i,status:o}=m(s,"Error creating context");return t.json({error:i},o)}}var ii=new U(!0),oi={properties:{intent:{type:"string",maxLength:1e3},attributes:{type:"object"}},additionalProperties:!1};async function wr(t,e,r){try{let s=t.params.contextName;if(!s)return t.json({error:"contextName parameter is required"},400);let i=await w(t),o=ii.test(oi,i||{});if(!o.success)return t.json({error:"Invalid input",details:o.errors},400);await k(t,r,"contexts.context.update","contexts","context","update",s);let{intent:n,attributes:a}=i||{},c=await e.updateContext(s,{intent:n,attributes:a});return t.json({success:!0,context:c},200)}catch(s){let{message:i,status:o}=m(s,"Error updating context");return t.json({error:i},o)}}async function Sr(t,e,r){try{let s=t.params.contextName;if(!s)return t.json({error:"contextName parameter is required"},400);if(s==="default")return t.json({error:"Cannot delete the default context"},400);await k(t,r,"contexts.context.delete","contexts","context","delete",s);let i=e.getContextResources(s);return await e.deleteContext(s),t.json({success:!0,message:`Context '${s}' deleted`,orphanedResources:i},200)}catch(s){let{message:i,status:o}=m(s,"Error deleting context");return t.json({error:i},o)}}async function br(t,e,r,s){try{let i=t.params.contextName;if(!i)return t.json({error:"contextName parameter is required"},400);await k(t,r,"contexts.context.delete-resources","contexts","context","delete-resources",i);let o=t.headers.authorization,n=o?.startsWith("Bearer ")?o.slice(7):o,a=s(n),c=e.getContextResources(i),l={success:!0,context:i,deleted:[],errors:[],summary:{totalResources:0,deletedCount:0,failedCount:0}};if(l.summary.totalResources=c.functions.length+c.databases.length+c.storage.length+c.sites.length+c.applications.length+c.users.length+c.serviceAccounts.length,a.functions){let u=await a.functions.list();for(let h of c.functions)try{let v=u.find(j=>j.id===h);v&&(await a.functions.delete(h),await e.unregisterResource(i,"function",h),l.deleted.push({type:"function",name:v.name}))}catch(v){l.errors.push({type:"function",name:h,error:v.message||"Unknown error"}),l.success=!1}}if(a.databases)for(let u of c.databases)try{await a.databases.deleteTable(u),await e.unregisterResource(i,"database",u),l.deleted.push({type:"database",name:u})}catch(h){l.errors.push({type:"database",name:u,error:h.message||"Unknown error"}),l.success=!1}if(a.storage)for(let u of c.storage)try{await a.storage.deleteBucket(u),await e.unregisterResource(i,"storage",u),l.deleted.push({type:"storage",name:u})}catch(h){l.errors.push({type:"storage",name:u,error:h.message||"Unknown error"}),l.success=!1}if(a.sites)for(let u of c.sites)try{await a.sites.deleteProject(u),await e.unregisterResource(i,"site",u),l.deleted.push({type:"site",name:u})}catch(h){l.errors.push({type:"site",name:u,error:h.message||"Unknown error"}),l.success=!1}if(a.applications){let u=await a.applications.list();for(let h of c.applications)try{let v=u.find(j=>j.id===h);v&&(await a.applications.delete(h),await e.unregisterResource(i,"application",h),l.deleted.push({type:"application",name:v.name}))}catch(v){l.errors.push({type:"application",name:h,error:v.message||"Unknown error"}),l.success=!1}}if(a.identity)for(let u of c.users)try{let h=await a.identity.getUser(u);h&&(await a.identity.deleteUser(h.id),await e.unregisterResource(i,"user",u),l.deleted.push({type:"user",name:u}))}catch(h){l.errors.push({type:"user",name:u,error:h.message||"Unknown error"}),l.success=!1}if(a.identity)for(let u of c.serviceAccounts)try{let h=await a.identity.getServiceAccount(u);h&&(await a.identity.deleteServiceAccount(h.id),await e.unregisterResource(i,"serviceAccount",u),l.deleted.push({type:"serviceAccount",name:u}))}catch(h){l.errors.push({type:"serviceAccount",name:u,error:h.message||"Unknown error"}),l.success=!1}l.summary.deletedCount=l.deleted.length,l.summary.failedCount=l.errors.length;let p=l.success?200:207;return t.json(l,p)}catch(i){let{message:o,status:n}=m(i,"Error deleting context resources");return t.json({error:o},n)}}async function xr(t,e,r){try{await k(t,r,"iac.config.get","iac","config","get");let s=await w(t);if(!s)return t.json({error:"Request body is required"},400);let i=s,n=e().validate(i);return t.json({success:!0,valid:n.valid,errors:n.errors,warnings:n.warnings},200)}catch(s){let{message:i,status:o}=m(s,"Error validating IaC");return t.json({error:i},o)}}async function Cr(t,e,r){try{await k(t,r,"iac.config.create","iac","config","create");let s=await w(t);if(!s)return t.json({error:"Request body is required"},400);let i=s,o=t.headers.authorization,n=o?.startsWith("Bearer ")?o.slice(7):o,a=e(n),c=t.query?.prune==="true",l=await a.apply(i,{prune:c}),p=l.success?200:207;return t.json({success:l.success,context:l.context,created:l.created,updated:l.updated,unchanged:l.unchanged,deleted:l.deleted,errors:l.errors},p)}catch(s){let{message:i,status:o}=m(s,"Error applying IaC");return t.json({error:i},o)}}var z="default",Te=class{db;tableName="context_service";contexts;resourceMappings;constructor(e){this.db=e,this.contexts=[],this.resourceMappings=[]}async start(){await this.loadState()}async loadState(){try{let e=await this.db.get(this.tableName,"state");e&&(this.contexts=e.contexts||[],this.resourceMappings=e.resourceMappings||[])}catch{this.contexts=[],this.resourceMappings=[]}}async saveState(){await this.db.write(this.tableName,"state",{contexts:this.contexts,resourceMappings:this.resourceMappings})}async createContext(e){let r=e.name;if(this.contexts.find(o=>o.name===r))throw new F(`Context '${r}' already exists`);let s=new Date().toISOString(),i={name:r,intent:e.intent,attributes:e.attributes,createdAt:s,updatedAt:s};return this.contexts.push(i),await this.saveState(),i}getContext(e){let r=this.contexts.find(s=>s.name===e);if(!r)throw new V(`Context '${e}' not found`);return r}getContextWithResources(e){let r=this.getContext(e),s=this.getContextResources(e);return{...r,resources:s}}async updateContext(e,r){let s=this.contexts.findIndex(n=>n.name===e);if(s===-1)throw new V(`Context '${e}' not found`);let i=this.contexts[s],o={...i,intent:r.intent??i.intent,attributes:r.attributes??i.attributes,updatedAt:new Date().toISOString()};return this.contexts[s]=o,await this.saveState(),o}async deleteContext(e){let r=this.contexts.findIndex(s=>s.name===e);if(r===-1)throw new V(`Context '${e}' not found`);this.contexts.splice(r,1),this.resourceMappings=this.resourceMappings.filter(s=>s.context!==e),await this.saveState()}contextExists(e){return this.contexts.some(r=>r.name===e)}listContexts(){return[...this.contexts].sort((e,r)=>e.name.localeCompare(r.name))}async ensureContext(e){let r=e||z;return this.contextExists(r)||await this.createContext({name:r,intent:r===z?"Default context for resources without explicit context assignment.":void 0}),r}async registerResource(e,r,s){await this.ensureContext(e),this.resourceMappings.find(o=>o.context===e&&o.resourceType===r&&o.resourceName===s)||(this.resourceMappings.push({context:e,resourceType:r,resourceName:s,createdAt:new Date().toISOString()}),await this.saveState())}async unregisterResource(e,r,s){this.resourceMappings=this.resourceMappings.filter(i=>!(i.context===e&&i.resourceType===r&&i.resourceName===s)),await this.saveState()}getContextResources(e){let r=this.resourceMappings.filter(i=>i.context===e),s={functions:[],databases:[],storage:[],sites:[],users:[],serviceAccounts:[],applications:[]};for(let i of r)switch(i.resourceType){case"function":s.functions.push(i.resourceName);break;case"database":s.databases.push(i.resourceName);break;case"storage":s.storage.push(i.resourceName);break;case"site":s.sites.push(i.resourceName);break;case"user":s.users.push(i.resourceName);break;case"serviceAccount":s.serviceAccounts.push(i.resourceName);break;case"application":s.applications.push(i.resourceName);break}return s}getResourceContext(e,r){return this.resourceMappings.find(i=>i.resourceType===e&&i.resourceName===r)?.context||z}async moveResource(e,r,s,i){await this.unregisterResource(s,e,r),await this.registerResource(i,e,r)}listResourcesByType(e,r){return this.resourceMappings.filter(s=>s.context===e&&s.resourceType===r).map(s=>s.resourceName)}};async function Ir(t,e,r){try{await k(t,r,"iac.config.get","iac","config","get");let s=await w(t);if(!s)return t.json({error:"Request body is required"},400);let i=s,o=t.headers.authorization,n=o?.startsWith("Bearer ")?o.slice(7):o,a=e(n),c=a.validate(i);if(!c.valid)return t.json({success:!1,valid:!1,errors:c.errors,warnings:c.warnings},400);let l=await a.diff(i);return t.json({success:!0,context:i.context?.name||z,toCreate:l.toCreate,toUpdate:l.toUpdate,toDelete:l.toDelete,unchanged:l.unchanged,warnings:c.warnings},200)}catch(s){let{message:i,status:o}=m(s,"Error generating IaC diff");return t.json({error:i},o)}}import{promises as Re}from"node:fs";import{join as Je,resolve as ai}from"node:path";var Ar={$schema:"https://json-schema.org/draft-07/schema#",$id:"https://schemas.molnos.cloud/schema-config-v1.json",title:"MolnOS IaC Configuration",description:"Infrastructure-as-Code configuration file for MolnOS contexts",type:"object",properties:{$schema:{type:"string",description:"JSON Schema reference for editor support"},version:{type:"string",enum:["1"],description:"IaC schema version"},context:{type:"object",description:"Context definition (optional, defaults to 'default' context)",properties:{name:{type:"string",minLength:1,maxLength:64,pattern:"^[a-z][a-z0-9-]*$",description:"Context name (lowercase, alphanumeric with hyphens)"},intent:{type:"string",maxLength:1e3,description:"Human-readable description of what this context does"},attributes:{type:"object",description:"Freeform intent attributes for policy hints and visualization"}},required:["name"]},resources:{type:"object",description:"Resource declarations",properties:{functions:{type:"object",description:"Function deployments"},databases:{type:"object",description:"Database table declarations"},storage:{type:"object",description:"Storage bucket declarations"},sites:{type:"object",description:"Static site declarations"}}},identities:{type:"object",description:"Identity declarations",properties:{users:{type:"object",description:"User declarations"},serviceAccounts:{type:"object",description:"Service account declarations"}}}},required:["version"]};var ci=new U(!0),je=class{contextService;clients;constructor(e,r){this.contextService=e,this.clients=r}async loadConfiguration(e){let r=ai(e);try{let s=await Re.readFile(r,"utf-8");return JSON.parse(s)}catch(s){throw s.code==="ENOENT"?new b(`IaC file not found: ${e}`):s instanceof SyntaxError?new b(`Invalid JSON in IaC file: ${s.message}`):s}}validate(e){let r=[],s=[],i=ci.test(Ar,e);if(!i.success)for(let o of i.errors||[])r.push({path:o.key||"",message:o.error||"Validation error"});if(e.version!=="1"&&r.push({path:"version",message:`Unsupported IaC version: ${e.version}. Only version "1" is supported.`}),e.resources?.functions){for(let[o,n]of Object.entries(e.resources.functions))if(n.bindings)for(let a of n.bindings){let c=a.resource;c.includes(":")||(a.service==="databases"?e.resources?.databases?.[c]||s.push({path:`resources.functions.${o}.bindings`,message:`Database '${c}' is not declared in this configuration`}):a.service==="storage"&&(e.resources?.storage?.[c]||s.push({path:`resources.functions.${o}.bindings`,message:`Storage bucket '${c}' is not declared in this configuration`})))}}return{valid:r.length===0,errors:r,warnings:s}}async apply(e,r={}){let s=this.validate(e);if(!s.valid)return{success:!1,context:e.context?.name||z,created:[],updated:[],unchanged:[],deleted:[],errors:s.errors.map(n=>({type:"validation",name:n.path,error:n.message}))};let i=e.context?.name||z,o={success:!0,context:i,created:[],updated:[],unchanged:[],deleted:[],errors:[]};if(await this.contextService.ensureContext(i),e.context)try{await this.contextService.contextExists(i)&&await this.contextService.updateContext(i,{intent:e.context.intent,attributes:e.context.attributes})}catch{}if(e.resources?.databases&&this.clients.databases)for(let[n]of Object.entries(e.resources.databases))try{await this.clients.databases.tableExists(n)?o.unchanged.push({type:"database",name:n}):(await this.clients.databases.createTable(n),await this.contextService.registerResource(i,"database",n),o.created.push({type:"database",name:n}))}catch(a){o.errors.push({type:"database",name:n,error:a.message||"Unknown error"}),o.success=!1}if(e.resources?.storage&&this.clients.storage)for(let[n,a]of Object.entries(e.resources.storage))try{await this.clients.storage.bucketExists(n)?await this.clients.storage.isBucketPublic(n)!==(a.public||!1)?(await this.clients.storage.updateBucket(n,{public:a.public}),o.updated.push({type:"storage",name:n})):o.unchanged.push({type:"storage",name:n}):(await this.clients.storage.createBucket(n,a.public||!1),await this.contextService.registerResource(i,"storage",n),o.created.push({type:"storage",name:n}))}catch(c){o.errors.push({type:"storage",name:n,error:c.message||"Unknown error"}),o.success=!1}if(e.resources?.functions&&this.clients.functions)for(let[n,a]of Object.entries(e.resources.functions))try{let c;if(a.code)c=a.code;else if(a.source){let p=r.basePath?Je(r.basePath,a.source):a.source;try{c=await Re.readFile(p,"utf-8")}catch{throw new Error(`Cannot read function source: ${a.source}`)}}else throw new Error('Function must have either "code" or "source" defined');let l=await this.clients.functions.get(n);if(l)await this.clients.functions.update(l.id,{code:c,methods:a.methods,bindings:a.bindings,allowUnauthenticated:a.allowUnauthenticated,passAllHeaders:a.passAllHeaders}),o.updated.push({type:"function",name:n});else{let p=await this.clients.functions.deploy(n,c,{id:a.id,methods:a.methods,bindings:a.bindings,allowUnauthenticated:a.allowUnauthenticated,passAllHeaders:a.passAllHeaders});await this.contextService.registerResource(i,"function",p.id),o.created.push({type:"function",name:n})}}catch(c){o.errors.push({type:"function",name:n,error:c.message||"Unknown error"}),o.success=!1}if(e.resources?.sites&&this.clients.sites)for(let[n,a]of Object.entries(e.resources.sites))try{let c;if(a.files&&a.files.length>0)c=a.files;else if(a.source){let p=r.basePath?Je(r.basePath,a.source):a.source;if(c=await this.loadSiteFiles(p),c.length===0)throw new Error(`No files found in site source: ${a.source}`)}else throw new Error('Site must have either "files" or "source" defined');let l=await this.clients.sites.getProject(n);await this.clients.sites.uploadProject(c,n),await this.contextService.registerResource(i,"site",n),l?o.updated.push({type:"site",name:n}):o.created.push({type:"site",name:n})}catch(c){o.errors.push({type:"site",name:n,error:c.message||"Unknown error"}),o.success=!1}if(e.identities?.users&&this.clients.identity)for(let[n,a]of Object.entries(e.identities.users))try{let c=await this.clients.identity.getUser(a.email);c?(await this.clients.identity.updateUser(c.id,{roles:a.roles}),o.updated.push({type:"user",name:a.email})):(await this.clients.identity.createUser(a.email,n,a.roles,{id:a.id}),await this.contextService.registerResource(i,"user",a.email),o.created.push({type:"user",name:a.email}))}catch(c){o.errors.push({type:"user",name:a.email,error:c.message||"Unknown error"}),o.success=!1}if(e.identities?.serviceAccounts&&this.clients.identity)for(let[n,a]of Object.entries(e.identities.serviceAccounts))try{let c=await this.clients.identity.getServiceAccount(n);c?(await this.clients.identity.updateServiceAccount(c.id,{roles:a.roles,description:a.description}),o.updated.push({type:"serviceAccount",name:n})):(await this.clients.identity.createServiceAccount(n,a.roles,a.description,{id:a.id}),await this.contextService.registerResource(i,"serviceAccount",n),o.created.push({type:"serviceAccount",name:n}))}catch(c){o.errors.push({type:"serviceAccount",name:n,error:c.message||"Unknown error"}),o.success=!1}if(e.resources?.applications&&this.clients.applications)for(let[n,a]of Object.entries(e.resources.applications))try{let c=await this.clients.applications.get(n);if(c)await this.clients.applications.update(c.id,{description:a.description,redirectUris:a.redirectUris}),o.updated.push({type:"application",name:n});else{let l=await this.clients.applications.create(n,a.description,a.redirectUris,{id:a.id});await this.contextService.registerResource(i,"application",l.id),o.created.push({type:"application",name:n})}}catch(c){o.errors.push({type:"application",name:n,error:c.message||"Unknown error"}),o.success=!1}if(r.prune){let n=await this.contextService.getContextResources(i);if(this.clients.functions){let a=Object.keys(e.resources?.functions||{}),c=await this.clients.functions.list();for(let l of n.functions){let p=c.find(u=>u.id===l);if(p&&!a.includes(p.name))try{await this.clients.functions.delete(l),await this.contextService.unregisterResource(i,"function",l),o.deleted.push({type:"function",name:p.name})}catch(u){o.errors.push({type:"function",name:p.name,error:`Failed to delete: ${u.message||"Unknown error"}`}),o.success=!1}}}if(this.clients.databases){let a=Object.keys(e.resources?.databases||{});for(let c of n.databases)if(!a.includes(c))try{await this.clients.databases.deleteTable(c),await this.contextService.unregisterResource(i,"database",c),o.deleted.push({type:"database",name:c})}catch(l){o.errors.push({type:"database",name:c,error:`Failed to delete: ${l.message||"Unknown error"}`}),o.success=!1}}if(this.clients.storage){let a=Object.keys(e.resources?.storage||{});for(let c of n.storage)if(!a.includes(c))try{await this.clients.storage.deleteBucket(c),await this.contextService.unregisterResource(i,"storage",c),o.deleted.push({type:"storage",name:c})}catch(l){o.errors.push({type:"storage",name:c,error:`Failed to delete: ${l.message||"Unknown error"}`}),o.success=!1}}if(this.clients.sites){let a=Object.keys(e.resources?.sites||{});for(let c of n.sites)if(!a.includes(c))try{await this.clients.sites.deleteProject(c),await this.contextService.unregisterResource(i,"site",c),o.deleted.push({type:"site",name:c})}catch(l){o.errors.push({type:"site",name:c,error:`Failed to delete: ${l.message||"Unknown error"}`}),o.success=!1}}if(this.clients.applications){let a=Object.keys(e.resources?.applications||{}),c=await this.clients.applications.list();for(let l of n.applications){let p=c.find(u=>u.id===l);if(p&&!a.includes(p.name))try{await this.clients.applications.delete(l),await this.contextService.unregisterResource(i,"application",l),o.deleted.push({type:"application",name:p.name})}catch(u){o.errors.push({type:"application",name:p.name,error:`Failed to delete: ${u.message||"Unknown error"}`}),o.success=!1}}}if(this.clients.identity){let a=Object.values(e.identities?.users||{}).map(c=>c.email);for(let c of n.users)if(!a.includes(c))try{let l=await this.clients.identity.getUser(c);l&&(await this.clients.identity.deleteUser(l.id),await this.contextService.unregisterResource(i,"user",c),o.deleted.push({type:"user",name:c}))}catch(l){o.errors.push({type:"user",name:c,error:`Failed to delete: ${l.message||"Unknown error"}`}),o.success=!1}}if(this.clients.identity){let a=Object.keys(e.identities?.serviceAccounts||{});for(let c of n.serviceAccounts)if(!a.includes(c))try{let l=await this.clients.identity.getServiceAccount(c);l&&(await this.clients.identity.deleteServiceAccount(l.id),await this.contextService.unregisterResource(i,"serviceAccount",c),o.deleted.push({type:"serviceAccount",name:c}))}catch(l){o.errors.push({type:"serviceAccount",name:c,error:`Failed to delete: ${l.message||"Unknown error"}`}),o.success=!1}}}return o}async loadSiteFiles(e,r=""){let s=[];try{let i=await Re.readdir(e,{withFileTypes:!0});for(let o of i){let n=Je(e,o.name),a=r?`${r}/${o.name}`:o.name;if(o.isDirectory()){let c=await this.loadSiteFiles(n,a);s.push(...c)}else if(o.isFile()){let l=(await Re.readFile(n)).toString("base64");s.push({path:a,content:l})}}}catch(i){if(i.code!=="ENOENT")throw i}return s}async diff(e){let r=e.context?.name||z,s=[],i=[],o=[],n=[],a=await this.contextService.getContextResources(r);if(this.clients.databases){let c=Object.keys(e.resources?.databases||{});for(let l of c)await this.clients.databases.tableExists(l)?n.push({type:"database",name:l}):s.push({type:"database",name:l});for(let l of a.databases)c.includes(l)||o.push({type:"database",name:l})}if(this.clients.storage){let c=Object.keys(e.resources?.storage||{});for(let l of c)if(await this.clients.storage.bucketExists(l)){let u=e.resources?.storage?.[l];await this.clients.storage.isBucketPublic(l)!==(u?.public||!1)?i.push({type:"storage",name:l}):n.push({type:"storage",name:l})}else s.push({type:"storage",name:l});for(let l of a.storage)c.includes(l)||o.push({type:"storage",name:l})}if(this.clients.functions){let c=Object.keys(e.resources?.functions||{});for(let l of c)await this.clients.functions.get(l)?i.push({type:"function",name:l}):s.push({type:"function",name:l});for(let l of a.functions)c.includes(l)||o.push({type:"function",name:l})}if(this.clients.sites){let c=Object.keys(e.resources?.sites||{});for(let l of c)await this.clients.sites.getProject(l)?i.push({type:"site",name:l}):s.push({type:"site",name:l});for(let l of a.sites)c.includes(l)||o.push({type:"site",name:l})}if(this.clients.applications){let c=Object.keys(e.resources?.applications||{});for(let l of c)await this.clients.applications.get(l)?i.push({type:"application",name:l}):s.push({type:"application",name:l});for(let l of a.applications)c.includes(l)||o.push({type:"application",name:l})}return{toCreate:s,toUpdate:i,toDelete:o,unchanged:n}}};var K=class{constructor(e,r){this.baseUrl=e;this.authToken=r}async request(e,r,s,i){let o=`${this.baseUrl}${r}`;if(i){let u=new URLSearchParams(i);o+=`?${u.toString()}`}let n={"Content-Type":"application/json"};this.authToken&&(n.Authorization=`Bearer ${this.authToken}`);let a={method:e,headers:n};s&&(a.body=JSON.stringify(s));let c=await fetch(o,a),l=c.headers.get("content-type"),p;if(l?.includes("application/json")?p=await c.json():p=await c.text(),!c.ok){let u=typeof p=="object"&&p.error?p.error:typeof p=="string"?p:JSON.stringify(p);throw new Error(u)}return p}},Xe=class extends K{async createTable(e){await this.request("POST",`/tables/${e}`)}async tableExists(e){try{return((await this.request("GET","/tables")).tables||[]).some(i=>i.name===e)}catch{return!1}}async deleteTable(e){await this.request("DELETE",`/tables/${e}`)}async listTables(){return((await this.request("GET","/tables")).tables||[]).map(s=>s.name)}},Ye=class extends K{async createBucket(e,r){await this.request("POST",`/buckets/${e}`,{public:r})}async bucketExists(e){try{return await this.request("GET",`/buckets/${e}`),!0}catch{return!1}}async updateBucket(e,r){await this.request("PATCH",`/buckets/${e}`,r)}async deleteBucket(e){await this.request("DELETE",`/buckets/${e}`)}async listBuckets(){return(await this.request("GET","/buckets")).buckets||[]}async isBucketPublic(e){try{return(await this.request("GET",`/buckets/${e}`)).public===!0}catch{return!1}}},Ze=class extends K{async deploy(e,r,s){let i=await this.request("POST","/deploy",{name:e,code:r,id:s.id,methods:s.methods,bindings:s.bindings,allowUnauthenticated:s.allowUnauthenticated,passAllHeaders:s.passAllHeaders,env:s.env});return i.function||i}async get(e){try{return((await this.request("GET","/list")).functions||[]).find(i=>i.name===e)||null}catch{return null}}async update(e,r){let s=await this.request("PUT",`/${e}`,r);return s.function||s}async delete(e){await this.request("DELETE",`/${e}`)}async list(){return(await this.request("GET","/list")).functions||[]}},Qe=class extends K{async uploadProject(e,r){return await this.request("POST","/projects",{files:e,projectId:r})}async getProject(e){try{return((await this.request("GET","/projects")).projects||[]).find(i=>i.projectId===e)||null}catch{return null}}async deleteProject(e){await this.request("DELETE",`/projects/${e}`)}async listProjects(){return(await this.request("GET","/projects")).projects||[]}},et=class extends K{async createUser(e,r,s,i){let o=await this.request("POST","/identity/users",{email:e,name:r,roles:s,id:i?.id});return o.user||o}async getUser(e){try{return((await this.request("GET","/identity/users")).users||[]).find(i=>i.metadata?.email===e)||null}catch{return null}}async updateUser(e,r){let s=await this.request("PATCH",`/identity/users/${e}`,r);return s.user||s}async deleteUser(e){await this.request("DELETE",`/identity/users/${e}`)}async listUsers(){return(await this.request("GET","/identity/users")).users||[]}async createServiceAccount(e,r,s,i){let o=await this.request("POST","/identity/service-accounts",{name:e,roles:r,description:s,id:i?.id});return o.serviceAccount||o}async getServiceAccount(e){try{return((await this.request("GET","/identity/service-accounts")).serviceAccounts||[]).find(i=>i.name===e)||null}catch{return null}}async updateServiceAccount(e,r){let s=await this.request("PATCH",`/identity/service-accounts/${e}`,r);return s.serviceAccount||s}async deleteServiceAccount(e){await this.request("DELETE",`/identity/service-accounts/${e}`)}async listServiceAccounts(){return(await this.request("GET","/identity/service-accounts")).serviceAccounts||[]}},tt=class extends K{async create(e,r,s,i){return await this.request("POST","/applications",{name:e,description:r,redirectUris:s,id:i?.id})}async get(e){try{return((await this.request("GET","/applications")).applications||[]).find(i=>i.name===e)||null}catch{return null}}async update(e,r){return await this.request("PATCH",`/applications/${e}`,r)}async delete(e){await this.request("DELETE",`/applications/${e}`)}async list(){return(await this.request("GET","/applications")).applications||[]}};function rt(t){let{serviceUrls:e,authToken:r}=t,s={};return e.databases&&(s.databases=new Xe(e.databases,r)),e.storage&&(s.storage=new Ye(e.storage,r)),e.functions&&(s.functions=new Ze(e.functions,r)),e.sites&&(s.sites=new Qe(e.sites,r)),e.identity&&(s.identity=new et(e.identity,r)),e.applications&&(s.applications=new tt(e.applications,r)),s}import{readFileSync as li,existsSync as ui}from"node:fs";function Er(){let t=process.env.MOLNOS_RUNTIME_CONFIG,e={molnos:{dataPath:"data",rateLimit:{global:{enabled:!1,requestsPerMinute:0}},signedUrlSecret:"molnos-default-signed-url-secret"},server:{host:"127.0.0.1",port:3e3},identity:{apiUrl:"http://127.0.0.1:3000"},context:{apiUrl:"http://127.0.0.1:3000"},services:{storage:{host:"127.0.0.1",port:3001,url:"http://127.0.0.1:3001"},functions:{host:"127.0.0.1",port:3002,url:"http://127.0.0.1:3002"},sites:{host:"127.0.0.1",port:3003,url:"http://127.0.0.1:3003"},databases:{host:"127.0.0.1",port:3004,url:"http://127.0.0.1:3004"},observability:{host:"127.0.0.1",port:3005,url:"http://127.0.0.1:3005"}}};if(!t||!ui(t))return e;try{let r=li(t,"utf-8");return JSON.parse(r)}catch(r){return console.error("[loadRuntimeConfig] Failed to load runtime config:",r),e}}async function kr(t,e,r,s){try{let i=t.headers["x-forwarded-for"],o=(Array.isArray(i)?i[0]:i)?.split(",")[0]?.trim()||t.headers["x-real-ip"]||"unknown";if(!r.checkRateLimit(`oauth:${o}`)){let p=r.getRateLimitInfo(`oauth:${o}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((p.reset-Date.now())/1e3)},429)}let n=Array.isArray(t.query.redirect_uri)?t.query.redirect_uri[0]:t.query.redirect_uri,a=Array.isArray(t.query.application_id)?t.query.application_id[0]:t.query.application_id,c=r.generateState(t,s,n,a),l=e.getAuthorizationUrl(c);return t.redirect(l,302)}catch(i){let{message:o,status:n}=m(i,"Error initiating OAuth flow");return t.json({error:o},n)}}async function Pr(t,e,r,s,i,o){try{let n=t.headers["x-forwarded-for"],a=Array.isArray(n)?n[0]:n,c=t.headers["x-real-ip"],l=Array.isArray(c)?c[0]:c,p=a?.split(",")[0]?.trim()||l||"unknown";if(!r.checkRateLimit(`oauth:${p}`)){let j=r.getRateLimitInfo(`oauth:${p}`);return t.json({error:"Too many authentication attempts. Please try again later.",retryAfter:Math.ceil((j.reset-Date.now())/1e3)},429)}let u=r.validateCallback(t,o);if(!u.valid)return t.json({error:u.error||"Invalid OAuth callback"},400);let h=await e.handleCallback(u.code,p),v=await s.getUserByEmail(h.user.email);if(!v)return t.json({error:"User not found. You must be invited before signing in with OAuth."},403);if(await s.updateUser(v.id,{lastLogin:new Date().toISOString()}),u.redirectUrl){if(u.applicationId&&!await i.validateRedirectUri(u.applicationId,u.redirectUrl))return console.warn(`Redirect URL validation failed for applicationId: ${u.applicationId}`),t.json({success:!0,accessToken:h.accessToken,refreshToken:h.refreshToken,expiresIn:h.expiresIn,tokenType:h.tokenType,user:{id:v.id,email:h.user.email,name:h.user.name,username:h.user.username},warning:"Redirect URL validation failed. Tokens provided but redirect was not performed."},200);let j=new URL(u.redirectUrl);return j.searchParams.set("access_token",h.accessToken),j.searchParams.set("refresh_token",h.refreshToken),j.searchParams.set("expires_in",h.expiresIn.toString()),t.redirect(j.toString(),302)}return t.json({success:!0,accessToken:h.accessToken,refreshToken:h.refreshToken,expiresIn:h.expiresIn,tokenType:h.tokenType,user:{id:v.id,email:h.user.email,name:h.user.name,username:h.user.username}})}catch(n){let{message:a,status:c}=m(n,"OAuth authentication failed");return t.json({error:a},c)}}async function Tr(t,e){try{let r=Array.from(e.values()).map(s=>s.getPublicInfo());return t.json({providers:r,count:r.length})}catch(r){let{message:s,status:i}=m(r,"Error listing OAuth providers");return t.json({error:s},i)}}var $e=class{constructor(e,r){this.config=e;this.mikroAuth=r}getAuthorizationUrl(e){let r=Array.isArray(this.config.scopes)?this.config.scopes.join(" "):this.config.scopes,s=new URLSearchParams({client_id:this.config.clientId,redirect_uri:this.config.redirectUri,response_type:"code",scope:r,state:e,...this.config.authorizationParams});return`${this.config.authorizationUrl}?${s}`}async exchangeCodeForTokens(e){let r=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,code:e,grant_type:"authorization_code",redirect_uri:this.config.redirectUri}),s=await fetch(this.config.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json",...this.config.tokenHeaders},body:r.toString()});if(!s.ok){let i=await s.text();throw new Error(`Token exchange failed: ${s.status} ${s.statusText} - ${i}`)}return s.json()}async fetchUserInfo(e){let r=await fetch(this.config.userInfoUrl,{headers:{Authorization:`Bearer ${e}`,Accept:"application/json",...this.config.userInfoHeaders}});if(!r.ok){let s=await r.text();throw new Error(`User info fetch failed: ${r.status} ${r.statusText} - ${s}`)}return r.json()}mapUserInfo(e){return this.config.userMapping?this.config.userMapping(e):{id:e.sub||e.id?.toString(),email:e.email,name:e.name||e.preferred_username,username:e.preferred_username||e.username||e.email?.split("@")[0]}}async handleCallback(e,r){let s=await this.exchangeCodeForTokens(e),i=await this.fetchUserInfo(s.access_token),o=this.mapUserInfo(i);if(!o.email)throw new Error("OAuth provider did not return user email");let n=await this.mikroAuth.createToken({email:o.email,username:o.username||o.name,role:"user",ip:r});return{accessToken:n.accessToken,refreshToken:n.refreshToken,expiresIn:n.exp,tokenType:n.tokenType,user:o}}getPublicInfo(){return{id:this.config.id,name:this.config.name,loginUrl:`/auth/oauth/${this.config.id}`}}};import di from"node:crypto";var Oe=class{stateStore=new Map;rateLimitStore=new Map;stateExpiryMs;rateLimitMaxAttempts;rateLimitWindowMs;stateCleanupInterval;rateLimitCleanupInterval;constructor(e){this.stateExpiryMs=(e?.stateExpirySeconds||600)*1e3,this.rateLimitMaxAttempts=e?.rateLimiting?.maxAttempts||10,this.rateLimitWindowMs=e?.rateLimiting?.windowMs||900*1e3,this.startCleanupTasks()}generateState(e,r,s,i){let o=di.randomBytes(32).toString("hex"),n=this.getClientIp(e),a=e.headers["user-agent"]||"";return this.stateStore.set(o,{expires:Date.now()+this.stateExpiryMs,providerId:r,ip:n,userAgent:a,redirectUrl:s,applicationId:i}),o}validateState(e,r,s,i){let o=this.stateStore.get(e);return o?o.expires<Date.now()?(this.stateStore.delete(e),!1):o.providerId!==r||o.ip!==s||i&&o.userAgent&&o.userAgent!==i?!1:(this.stateStore.delete(e),!0):!1}checkRateLimit(e){let r=Date.now(),s=this.rateLimitStore.get(e);return!s||s.resetAt<r?(this.rateLimitStore.set(e,{count:1,resetAt:r+this.rateLimitWindowMs}),!0):s.count>=this.rateLimitMaxAttempts?!1:(s.count++,!0)}getRateLimitInfo(e){let r=this.rateLimitStore.get(e),s=Date.now();return!r||r.resetAt<s?{limit:this.rateLimitMaxAttempts,remaining:this.rateLimitMaxAttempts-1,reset:s+this.rateLimitWindowMs}:{limit:this.rateLimitMaxAttempts,remaining:Math.max(0,this.rateLimitMaxAttempts-r.count),reset:r.resetAt}}validateCallback(e,r){let s=Array.isArray(e.query.code)?e.query.code[0]:e.query.code,i=Array.isArray(e.query.state)?e.query.state[0]:e.query.state,o=Array.isArray(e.query.error)?e.query.error[0]:e.query.error,n=Array.isArray(e.query.error_description)?e.query.error_description[0]:e.query.error_description;if(o)return{valid:!1,error:`OAuth provider error: ${n||o}`};if(!s||!i)return{valid:!1,error:"Missing code or state parameter"};let a=this.stateStore.get(i),c=this.getClientIp(e),l=e.headers["user-agent"];return this.validateState(i,r,c,l)?{valid:!0,code:s,state:i,redirectUrl:a?.redirectUrl,applicationId:a?.applicationId}:{valid:!1,error:"Invalid or expired state token (CSRF protection failed)"}}getClientIp(e){let r=e.headers["x-forwarded-for"],s=Array.isArray(r)?r[0]:r,i=e.headers["x-real-ip"],o=Array.isArray(i)?i[0]:i;return s?.split(",")[0]?.trim()||o||"unknown"}startCleanupTasks(){this.stateCleanupInterval=setInterval(()=>{this.cleanupExpiredStates()},300*1e3),this.rateLimitCleanupInterval=setInterval(()=>{this.cleanupExpiredRateLimits()},600*1e3)}cleanupExpiredStates(){let e=Date.now(),r=0;for(let[s,i]of this.stateStore.entries())i.expires<e&&(this.stateStore.delete(s),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired state tokens`)}cleanupExpiredRateLimits(){let e=Date.now(),r=0;for(let[s,i]of this.rateLimitStore.entries())i.resetAt<e&&(this.rateLimitStore.delete(s),r++);r>0&&console.log(`[OAuthSecurity] Cleaned up ${r} expired rate limit records`)}shutdown(){this.stateCleanupInterval&&clearInterval(this.stateCleanupInterval),this.rateLimitCleanupInterval&&clearInterval(this.rateLimitCleanupInterval)}getStats(){return{stateCount:this.stateStore.size,rateLimitCount:this.rateLimitStore.size}}};var Rr={google:{id:"google",name:"Google",authorizationUrl:"https://accounts.google.com/o/oauth2/v2/auth",tokenUrl:"https://oauth2.googleapis.com/token",userInfoUrl:"https://www.googleapis.com/oauth2/v2/userinfo",scopes:"openid email profile",authorizationParams:{access_type:"offline",prompt:"consent"},userMapping:t=>({id:t.sub,email:t.email,name:t.name,username:t.email.split("@")[0]})},github:{id:"github",name:"GitHub",authorizationUrl:"https://github.com/login/oauth/authorize",tokenUrl:"https://github.com/login/oauth/access_token",userInfoUrl:"https://api.github.com/user",scopes:"user:email read:user",tokenHeaders:{Accept:"application/json"},userInfoHeaders:{Accept:"application/vnd.github.v3+json"},userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name||t.login,username:t.login})},microsoft:{id:"microsoft",name:"Microsoft",authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",userInfoUrl:"https://graph.microsoft.com/v1.0/me",scopes:"openid email profile User.Read",userMapping:t=>({id:t.id,email:t.mail||t.userPrincipalName,name:t.displayName,username:t.userPrincipalName?.split("@")[0]})},gitlab:{id:"gitlab",name:"GitLab",authorizationUrl:"https://gitlab.com/oauth/authorize",tokenUrl:"https://gitlab.com/oauth/token",userInfoUrl:"https://gitlab.com/api/v4/user",scopes:"read_user email",userMapping:t=>({id:t.id.toString(),email:t.email,name:t.name,username:t.username})}};function jr(t){let e=[];if(t.presets)for(let[r,s]of Object.entries(t.presets)){let i=Rr[r];if(!i){console.warn(`[OAuth] Unknown preset provider: ${r}`);continue}e.push({...i,clientId:s.clientId,clientSecret:s.clientSecret,redirectUri:s.redirectUri,scopes:s.scopes||i.scopes,authorizationParams:{...i.authorizationParams,...s.authorizationParams}})}return t.custom&&e.push(...t.custom),e}async function $r(t,e){let r=await e(),s=r.headers||{};return s["X-Frame-Options"]="DENY",s["X-Content-Type-Options"]="nosniff",s["X-XSS-Protection"]="1; mode=block",s["Referrer-Policy"]="strict-origin-when-cross-origin",s["Content-Security-Policy"]="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'",s["Permissions-Policy"]="geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()",process.env.NODE_ENV==="production"&&(s["Strict-Transport-Security"]="max-age=31536000; includeSubDomains"),{...r,headers:s}}async function st(t,e){return process.env.NODE_ENV==="development"||process.env.NODE_ENV==="test"||(t.headers["x-forwarded-proto"]||t.headers["x-forwarded-protocol"])==="https"||t.req.url?.startsWith("https://")?e():t.json({error:"HTTPS required",message:"OAuth authentication requires HTTPS. Please use https:// instead of http://"},403)}function Or(t,e,r){if(r.startsWith("/databases/")){r.includes("/table")||r.includes("/get")?f(t,"databases.table.get",{}):r.includes("/write")||r.includes("/update")||e==="PUT"||e==="PATCH"?f(t,"databases.table.update",{}):r.includes("/delete")||e==="DELETE"?f(t,"databases.table.delete",{}):e==="POST"&&f(t,"databases.table.create",{});return}if(r.startsWith("/functions/")){r.includes("/deploy")||e==="POST"?f(t,"functions.function.create",{}):r.includes("/run/")?f(t,"functions.function.execute",{}):e==="PUT"||e==="PATCH"?f(t,"functions.function.update",{}):e==="DELETE"?f(t,"functions.function.delete",{}):e==="GET"&&f(t,"functions.function.get",{});return}if(r.startsWith("/storage/")){let s=r.match(/^\/storage\/buckets\/[^/]+$/),i=r.includes("/objects");s?e==="POST"?f(t,"storage.bucket.create",{}):e==="PATCH"||e==="PUT"?f(t,"storage.bucket.update",{}):e==="DELETE"?f(t,"storage.bucket.delete",{}):e==="GET"&&f(t,"storage.bucket.get",{}):i||r.includes("/upload")||r.includes("/download")||r.includes("/get")?r.includes("/upload")||e==="POST"?f(t,"storage.object.create",{}):r.includes("/download")||r.includes("/get")?f(t,"storage.object.get",{}):e==="PUT"||e==="PATCH"?f(t,"storage.object.update",{}):e==="DELETE"?f(t,"storage.object.delete",{}):e==="GET"&&f(t,"storage.object.get",{}):e==="GET"&&f(t,"storage.bucket.get",{});return}if(r.startsWith("/sites/")){if(r.match(/^\/sites\/projects\/[^/]+/)&&e==="GET")return;r.includes("/deploy")||e==="POST"?f(t,"sites.site.create",{}):e==="PUT"||e==="PATCH"?f(t,"sites.site.update",{}):e==="DELETE"?f(t,"sites.site.delete",{}):e==="GET"&&f(t,"sites.site.get",{});return}if(r.startsWith("/observability/")){e==="POST"?f(t,"observability.metrics.create",{}):e==="GET"&&f(t,"observability.metrics.get",{});return}}var Ue=class{baseUrl;authToken;constructor(e,r){this.baseUrl=e.replace(/\/$/,""),this.authToken=r}async createCustomRole(e,r,s,i){let o=await fetch(`${this.baseUrl}/identity/roles`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({roleId:e,name:r,description:s,permissions:i})});if(!o.ok){let n=`Failed to create role: ${o.statusText}`;try{n=(await o.json()).error||n}catch{n=await o.text().catch(()=>o.statusText)||n}throw new Error(n)}}async updateRole(e,r){let s=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"PATCH",headers:this.getHeaders(),body:JSON.stringify(r)});if(!s.ok){let i=await s.json();throw new Error(i.error||`Failed to update role: ${s.statusText}`)}}async deleteRole(e){let r=await fetch(`${this.baseUrl}/identity/roles/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete role: ${r.statusText}`)}}async addServiceAccount(e,r,s){let i=await fetch(`${this.baseUrl}/identity/service-accounts`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify({name:e,description:r,roles:s})});if(!i.ok){let n=`Failed to create service account: ${i.statusText}`;try{n=(await i.json()).error||n}catch{n=await i.text().catch(()=>i.statusText)||n}throw new Error(n)}let o=await i.json();return{id:o.id,apiKey:o.apiKey}}async deleteIdentity(e){let r=await fetch(`${this.baseUrl}/identity/service-accounts/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok){let s=await r.json();throw new Error(s.error||`Failed to delete service account: ${r.statusText}`)}}async getUserFromToken(e){try{let r=await fetch(`${this.baseUrl}/identity/whoami`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});return r.ok?await r.json():null}catch{return null}}getHeaders(){let e={"Content-Type":"application/json"};return this.authToken&&(e.Authorization=`Bearer ${this.authToken}`),e}};function Ur(t){return t?new Ue(t):null}function Mr(t){return t&&typeof t=="number"&&t>0?t*1024*1024:0}async function Lr(t){let{auth:e,db:r,config:s,molnos:i,oauth:o}=t,n=new Ee(e,r,i.initialUser);await n.start();let c=`${s.useHttps||s.useHttp2?"https":"http"}://${s.host||"127.0.0.1"}:${s.port||3e3}`,l=Ur(c),p,u;if(o){p=new Oe(o),u=new Map;let d=jr(o);for(let C of d){let S=new $e(C,e);u.set(C.id,S)}}let h=it(i.dataPath,"applications"),v=new Pe({dbPath:h});await v.start();let j=it(i.dataPath,"management"),I=new ke({dbPath:j}),Br=it(i.dataPath,".runtime-config.json");I.setEnvironmentVariables({MOLNOS_RUNTIME_CONFIG:Br}),await I.start();let B=new Te(r);await B.start();let L=Er(),Me=d=>{let C=rt({serviceUrls:{databases:L.services.databases.url,storage:L.services.storage.url,functions:L.services.functions.url,sites:L.services.sites.url,identity:L.identity.apiUrl,applications:L.identity.apiUrl},authToken:d});return new je(B,C)},Le=!1,g=new We({port:s.port||3e3,host:s.host||"127.0.0.1",allowedDomains:s.allowedDomains,maxBodySize:Mr(1e3),rateLimit:i.rateLimit?.global||{enabled:!1,requestsPerMinute:0}});g.use(async(d,C)=>Le?d.text("Service Unavailable - Server is shutting down",503):C()),g.use($r),g.get("/health",async d=>d.text("OK"));let T=async d=>d.text("Not Found",404);if(g.post("/auth/login",d=>Tt(d,n,e,v)),g.get("/auth/verify",d=>Rt(d,e,v)),g.post("/auth/refresh",d=>jt(d,e)),u&&p){g.get("/auth/oauth/providers",d=>Tr(d,u));for(let[d,C]of u.entries())g.get(`/auth/oauth/${d}`,st,S=>kr(S,C,p,d)),g.get(`/auth/oauth/${d}/callback`,st,S=>Pr(S,C,p,n,v,d))}g.post("/management/service",y,d=>rr(d,I)),g.get("/management/services",y,d=>Qt(d,I)),g.get("/management/service/:serviceName",y,d=>er(d,I)),g.put("/management/service/:serviceName",y,d=>ir(d,I)),g.delete("/management/service/:serviceName",y,d=>or(d,I)),g.get("/management/service/:serviceName/start",y,d=>nr(d,I)),g.get("/management/service/:serviceName/stop",y,d=>ar(d,I)),g.get("/management/service/:serviceName/logs",y,d=>cr(d,I)),g.get("/management/service/:serviceName/restart",y,d=>lr(d,I)),g.get("/identity/whoami",y,d=>$t(d)),g.get("/identity/identities",y,d=>Ot(d,n)),g.post("/identity/users",y,d=>Mt(d,n)),g.get("/identity/users",y,d=>Lt(d,n)),g.get("/identity/users/:userId",y,d=>Ht(d,n)),g.patch("/identity/users/:userId",y,d=>Nt(d,n)),g.delete("/identity/users/:userId",y,d=>Vt(d,n)),g.post("/identity/service-accounts",y,d=>qt(d,n)),g.get("/identity/service-accounts",y,d=>Ft(d,n)),g.get("/identity/service-accounts/:serviceAccountId",y,d=>Bt(d,n)),g.patch("/identity/service-accounts/:serviceAccountId",y,d=>zt(d,n)),g.delete("/identity/service-accounts/:serviceAccountId",y,d=>Gt(d,n)),g.post("/identity/service-accounts/:serviceAccountId/rotate-key",y,d=>Wt(d,n)),g.post("/identity/roles",y,d=>Xt(d,n)),g.get("/identity/roles",y,d=>Kt(d,n)),g.get("/identity/roles/:roleId",y,d=>Jt(d,n)),g.patch("/identity/roles/:roleId",y,d=>Yt(d,n)),g.delete("/identity/roles/:roleId",y,d=>Zt(d,n)),g.post("/applications",y,d=>dr(d,v,l)),g.get("/applications",y,d=>hr(d,v,l)),g.get("/applications/:id",y,d=>pr(d,v,l)),g.patch("/applications/:id",y,d=>mr(d,v,l)),g.delete("/applications/:id",y,d=>fr(d,v,l)),g.get("/contexts",y,d=>gr(d,B,l)),g.post("/contexts",y,d=>vr(d,B,l)),g.get("/contexts/:contextName",y,d=>yr(d,B,l)),g.patch("/contexts/:contextName",y,d=>wr(d,B,l)),g.delete("/contexts/:contextName",y,d=>Sr(d,B,l)),g.delete("/contexts/:contextName/resources",y,d=>br(d,B,l,C=>rt({serviceUrls:{databases:L.services.databases.url,storage:L.services.storage.url,functions:L.services.functions.url,sites:L.services.sites.url,identity:L.identity.apiUrl,applications:L.identity.apiUrl},authToken:C}))),g.post("/iac/validate",y,d=>xr(d,Me,l)),g.post("/iac/apply",y,d=>Cr(d,Me,l)),g.post("/iac/diff",y,d=>Ir(d,Me,l)),g.get("/databases/*",y,P,T),g.post("/databases/*",y,P,T),g.put("/databases/*",y,P,T),g.patch("/databases/*",y,P,T),g.delete("/databases/*",y,P,T);async function ne(d,C){if(d.path.match(/^\/functions\/run\//)){let O=d.headers.authorization;if(O)try{let R=await n.getUserFromToken(O);R&&(d.state.user=R,d.state.requestingIdentity=R)}catch{}return C()}return y(d,C)}g.get("/functions/*",ne,P,T),g.post("/functions/*",ne,P,T),g.put("/functions/*",ne,P,T),g.patch("/functions/*",ne,P,T),g.delete("/functions/*",ne,P,T),g.get("/sites/projects/:projectId/*",P,async d=>d.text("Not Found",404)),g.get("/sites/*",y,P,T),g.post("/sites/*",y,P,T),g.put("/sites/*",y,P,T),g.patch("/sites/*",y,P,T),g.delete("/sites/*",y,P,T);async function ae(d,C){let S=d.req.method;if(d.path.match(/^\/storage\/buckets\/[^/]+\/objects\/.+/)&&(S==="GET"||S==="HEAD")){let R=d.headers.authorization;if(R)try{let G=await n.getUserFromToken(R);G&&(d.state.user=G,d.state.requestingIdentity=G)}catch{}return C()}return y(d,C)}g.get("/storage/*",ae,P,T),g.post("/storage/*",ae,P,T),g.put("/storage/*",ae,P,T),g.patch("/storage/*",ae,P,T),g.delete("/storage/*",ae,P,T),g.get("/observability/*",y,P,T),g.post("/observability/*",y,P,T),g.put("/observability/*",y,P,T),g.patch("/observability/*",y,P,T),g.delete("/observability/*",y,P,T);function _r(d,C,S,$){if(C!=="POST")return;let O=null,R=null;d==="/functions/deploy"&&$.function?.id?(O="function",R=$.function.id):d.match(/^\/databases\/tables\/[^/]+$/)&&$.name?(O="database",R=$.name):d.match(/^\/storage\/buckets\/[^/]+$/)&&$.bucket?(O="storage",R=$.bucket):d==="/sites/projects"&&$.projectId&&(O="site",R=$.projectId),O&&R&&B.registerResource(S,O,R).catch(G=>{console.error(`[Server] Failed to register ${O} '${R}' with context '${S}':`,G.message)})}async function P(d,C){try{let S=d.path,$=Object.keys(d.query).length>0?`?${new URLSearchParams(d.query).toString()}`:"",O=d.req.method||"",R,G=0,Gr=await I.getServices();for(let D of Gr)D.active&&S.startsWith(D.prefix)&&D.prefix.length>G&&(R=D,G=D.prefix.length);if(!R)return C();let at=d.state.user;if(!(S.match(/^\/sites\/projects\/[^/]+.*/)&&O==="GET")&&at&&Or(at,O,S),R.healthCheck&&R.healthCheck.healthy===!1)return d.json({error:"Service unavailable (unhealthy)"},503);let Wr=(S.substring(R.prefix.length)||"/")+$,H=null,He=null,De=null;if(["POST","PUT","PATCH","DELETE"].includes(O)){let D=d.headers["content-type"]||"";if(D.includes("application/json"))try{let J=await w(d);De=J,H=JSON.stringify(J)}catch{H=null}else if(D.includes("multipart/form-data"))try{H=JSON.stringify(d.body),He="application/json"}catch{H=null}else try{H=Buffer.isBuffer(d.body)?d.body:Buffer.from(d.body)}catch{H=null}}return new Promise(D=>{let J={...d.headers,host:`127.0.0.1:${R.port}`};He&&(J["content-type"]=He),H&&(typeof H=="string"?J["content-length"]=Buffer.byteLength(H).toString():J["content-length"]=H.length.toString());let Kr={method:O,hostname:"127.0.0.1",port:R.port,path:Wr,headers:J},ce=pi.request(Kr,N=>{let ct=[];N.on("data",re=>{ct.push(re)}),N.on("end",()=>{let re=Buffer.concat(ct),Ne={};Object.entries(N.headers).forEach(([ue,W])=>{W!==void 0&&(Ne[ue]=Array.isArray(W)?W.join(", "):W)});let le=N.headers["content-type"]||"",q;if(le.includes("application/json"))try{let ue=JSON.parse(re.toString("utf-8")),W=N.statusCode||200;ue.success&&W>=200&&W<300&&De?.context&&_r(S,O,De.context,ue),q=d.json(ue,W)}catch{q=d.text(re.toString("utf-8"),N.statusCode||200)}else le.startsWith("text/")||le.includes("javascript")||le.includes("xml")?q=d.text(re.toString("utf-8"),N.statusCode||200):(q=d.binary(re,le||"application/octet-stream"),N.statusCode&&N.statusCode!==200&&(q.statusCode=N.statusCode));q.headers?q.headers={...q.headers,...Ne}:q.headers=Ne,D(q)})});ce.on("error",N=>{D(d.json({error:"Proxy Error",message:N.message},502))}),ce.on("timeout",()=>{ce.destroy(),D(d.json({error:"Gateway Timeout"},504))}),H&&ce.write(H),ce.end()})}catch(S){return ot(d,S)}}async function y(d,C){try{let S=d.headers.authorization;if(!S)return d.text("Unauthorized",401);let $=await n.getUserFromToken(S);return $?(d.state.user=$,d.state.requestingIdentity=$,C()):d.text("Unauthorized",401)}catch(S){return ot(d,S)}}function ot(d,C){let S=C.message||C,$=C?.cause?.statusCode||400;return d.json({error:S},$)}let nt=g.start();async function zr(){Le||(Le=!0,await new Promise((d,C)=>{nt.close(S=>{S?C(S):d()})}),console.log("[Server] Stopping managed services..."),await I.shutdown(),p&&p.shutdown(),await r.close())}return{server:nt,shutdown:zr,processManager:I,identityService:n}}var Dr=()=>{let t=Hr(process.env.DEBUG)||!1,e=Hr(process.env.RATE_LIMIT_ENABLED||!1);return{auth:{jwtSecret:process.env.AUTH_JWT_SECRET||"your-jwt-secret",magicLinkExpirySeconds:900,jwtExpirySeconds:900,refreshTokenExpirySeconds:10080*60,maxActiveSessions:3,consoleUrl:process.env.CONSOLE_URL||"http://localhost:8000",debug:t},email:{emailSubject:"Sign In To MolnOS",user:process.env.EMAIL_USER||"",host:process.env.EMAIL_HOST||"",password:process.env.EMAIL_PASSWORD||"",port:465,secure:!0,maxRetries:2,debug:t},storage:{databaseDirectory:"molnosdb",encryptionKey:process.env.STORAGE_KEY||"",debug:t},server:{port:Number(process.env.PORT)||3e3,host:process.env.HOST||"127.0.0.1",useHttps:!1,useHttp2:!1,sslCert:"",sslKey:"",sslCa:"",allowedDomains:["http://localhost:8000"],debug:t},molnos:{dataPath:process.env.DATA_PATH||"data",initialUser:{id:process.env.INITIAL_USER_ID||he(),userName:process.env.INITIAL_USER_NAME||"",email:process.env.INITIAL_USER_EMAIL||""},rateLimit:{global:{enabled:e,requestsPerMinute:Number(process.env.RATE_LIMIT_RPS)||0}},signedUrlSecret:process.env.SIGNED_URL_SECRET||"molnos-default-signed-url-secret"}}};function Hr(t){return t==="true"||t===!0}var x=Dr(),Nr={configFilePath:"molnos.config.json",args:process.argv,options:[{flag:"--jwtSecret",path:"auth.jwtSecret",defaultValue:x.auth.jwtSecret},{flag:"--magicLinkExpirySeconds",path:"auth.magicLinkExpirySeconds",defaultValue:x.auth.magicLinkExpirySeconds},{flag:"--jwtExpirySeconds",path:"auth.jwtExpirySeconds",defaultValue:x.auth.jwtExpirySeconds},{flag:"--refreshTokenExpirySeconds",path:"auth.refreshTokenExpirySeconds",defaultValue:x.auth.refreshTokenExpirySeconds},{flag:"--maxActiveSessions",path:"auth.maxActiveSessions",defaultValue:x.auth.maxActiveSessions},{flag:"--consoleUrl",path:"auth.consoleUrl",defaultValue:x.auth.consoleUrl},{flag:"--debug",path:"auth.debug",isFlag:!0,defaultValue:x.auth.debug},{flag:"--emailSubject",path:"email.emailSubject",defaultValue:"Your Secure Login Link"},{flag:"--emailHost",path:"email.host",defaultValue:x.email.host},{flag:"--emailUser",path:"email.user",defaultValue:x.email.user},{flag:"--emailPassword",path:"email.password",defaultValue:x.email.password},{flag:"--emailPort",path:"email.port",defaultValue:x.email.port},{flag:"--emailSecure",path:"email.secure",isFlag:!0,defaultValue:x.email.secure},{flag:"--emailMaxRetries",path:"email.maxRetries",defaultValue:x.email.maxRetries},{flag:"--debug",path:"email.debug",isFlag:!0,defaultValue:x.email.debug},{flag:"--db",path:"storage.databaseDirectory",defaultValue:x.storage.databaseDirectory},{flag:"--encryptionKey",path:"storage.encryptionKey",defaultValue:x.storage.encryptionKey},{flag:"--debug",path:"storage.debug",defaultValue:x.storage.debug},{flag:"--port",path:"server.port",defaultValue:x.server.port},{flag:"--host",path:"server.host",defaultValue:x.server.host},{flag:"--https",path:"server.useHttps",isFlag:!0,defaultValue:x.server.useHttps},{flag:"--http2",path:"server.useHttp2",isFlag:!0,defaultValue:x.server.useHttp2},{flag:"--cert",path:"server.sslCert",defaultValue:x.server.sslCert},{flag:"--key",path:"server.sslKey",defaultValue:x.server.sslKey},{flag:"--ca",path:"server.sslCa",defaultValue:x.server.sslCa},{flag:"--allowed",path:"server.allowedDomains",defaultValue:x.server.allowedDomains,parser:Y.array},{flag:"--debug",path:"server.debug",isFlag:!0,defaultValue:x.server.debug},{flag:"--data-path",path:"dataPath",defaultValue:x.molnos.dataPath},{flag:"--initialUserId",path:"molnos.initialUser.id",defaultValue:x.molnos.initialUser.id},{flag:"--initialUserName",path:"molnos.initialUser.userName",defaultValue:x.molnos.initialUser.userName},{flag:"--initialUserEmail",path:"molnos.initialUser.email",defaultValue:x.molnos.initialUser.email}]};function Vr(t){return{...t,auth:{...t.auth,templates:{textVersion:(e,r,s)=>{let i=new URL(e),o=i.searchParams.get("token")||"",n=i.searchParams.get("email")||"",a=s?.appName||"MolnOS",c=s?.redirectUrl&&s?.applicationId,l=c?`Sign in to ${a}`:"Sign in to MolnOS",p=c?`You're signing in to ${a}. Click the link below to complete the authentication process.`:"Click the link below to sign in to your account.";return`
|
|
115
|
+
${l}
|
|
116
116
|
|
|
117
117
|
${p}
|
|
118
118
|
|
|
@@ -123,7 +123,7 @@ Run: molnos auth verify ${o} ${n}
|
|
|
123
123
|
`}
|
|
124
124
|
This link expires in ${r} minutes and can only be used once.
|
|
125
125
|
If you didn't request this, please ignore this email.
|
|
126
|
-
`},htmlVersion:(e,r,
|
|
126
|
+
`},htmlVersion:(e,r,s)=>{let i=new URL(e),o=i.searchParams.get("token")||"",n=i.searchParams.get("email")||"",a=s?.appName||"MolnOS",c=s?.redirectUrl&&s?.applicationId,l=c?`Sign in to ${a}`:"Sign in to MolnOS",p=c?`You're signing in to <strong>${a}</strong>. Click the button below to complete the authentication process. You'll be redirected to the application after verification.`:"Click the button below to sign in to your account.",u=c?"":`
|
|
127
127
|
<div style="margin-top: 2rem; padding: 1rem; background-color: #f8f8f8; border-radius: 6px; border-left: 3px solid #3e63dd;">
|
|
128
128
|
<p style="font-size: 0.85rem; font-weight: 600; margin: 0 0 0.5rem 0; color: #202020;">CLI/Terminal Users</p>
|
|
129
129
|
<p style="font-size: 0.8rem; line-height: 1.5; margin: 0 0 0.75rem 0; color: #646464;">Run this command in your terminal (click to select, then copy):</p>
|
|
@@ -135,11 +135,11 @@ If you didn't request this, please ignore this email.
|
|
|
135
135
|
<head>
|
|
136
136
|
<meta charset="utf-8">
|
|
137
137
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
138
|
-
<title>${
|
|
138
|
+
<title>${l}</title>
|
|
139
139
|
</head>
|
|
140
140
|
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
|
|
141
141
|
<div style="max-width: 600px; margin: 0 auto; padding: 2rem;">
|
|
142
|
-
<h1 style="font-size: 1.5rem; font-weight: 600; margin: 0 0 1rem 0; color: #202020;">${
|
|
142
|
+
<h1 style="font-size: 1.5rem; font-weight: 600; margin: 0 0 1rem 0; color: #202020;">${l}</h1>
|
|
143
143
|
<p style="font-size: 0.9rem; line-height: 1.5; margin: 0 0 1.5rem 0; color: #646464;">${p}</p>
|
|
144
144
|
<a href="${e}" style="display: inline-block; padding: 0.75rem 1.5rem; background-color: #3e63dd; color: #ffffff; text-decoration: none; border-radius: 6px; font-size: 0.9rem; font-weight: 500;">Sign in</a>
|
|
145
145
|
${u}
|
|
@@ -147,5 +147,5 @@ ${u}
|
|
|
147
147
|
</div>
|
|
148
148
|
</body>
|
|
149
149
|
</html>
|
|
150
|
-
`}}}}}function
|
|
151
|
-
[MolnOS] Received ${u}, initiating graceful shutdown...`);try{await
|
|
150
|
+
`}}}}}function qr(t){let e=[],r=c=>e.push(c);return t?.email?.host||r("Missing email.host value"),t?.email?.user||r("Missing email.user value"),t?.email?.password||r("Missing email.password value"),t?.molnos?.initialUser?.email||r("Missing molnos.initialUser.email value"),t?.molnos?.dataPath||r("Missing molnos.dataPath value"),{success:e.length===0,errors:e}}import{writeFileSync as hi,mkdirSync as mi,existsSync as fi}from"node:fs";import{join as gi}from"node:path";function Fr(t){let e=t.molnos.dataPath||"data";fi(e)||mi(e,{recursive:!0});let r=gi(e,".runtime-config.json"),s=t.server.host||"127.0.0.1",i=t.molnos.services||{},o={storage:3001,functions:3002,sites:3003,databases:3004,observability:3005},n=l=>{let p=i[l]||{},u=p.host||s,h=p.port||o[l],v=p.secure?"https":"http";return{host:u,port:h,url:`${v}://${u}:${h}`}},a={storage:n("storage"),functions:n("functions"),sites:n("sites"),databases:n("databases"),observability:n("observability")},c={molnos:{dataPath:t.molnos.dataPath,rateLimit:t.molnos.rateLimit,signedUrlSecret:t.molnos.signedUrlSecret},server:{host:t.server.host,port:t.server.port},identity:{apiUrl:`${t.server.useHttps?"https":"http"}://${s}:${t.server.port}`},context:{apiUrl:`${t.server.useHttps?"https":"http"}://${s}:${t.server.port}`},services:a,storage:t.storage};return hi(r,JSON.stringify(c,null,2),"utf-8"),r}async function vi(){let t=wi(),e=Vr(t),r=qr(e);if(!r.success)throw console.error(`Found configuration validation errors: ${JSON.stringify(r.errors)}`),new be("Invalid configuration provided. Please see the validation errors and fix these and then retry again.");Fr(e);let s=e.molnos.dataPath||"data",i=new ee({databaseDirectory:yi(s,"molnosdb")});await i.start();let o=new gt(i),n=new yt(e.email),a={...e,auth:{...e.auth,appUrl:e.auth.consoleUrl}},c=new ft(a,n,o),l=await Lr({config:e.server,molnos:e.molnos,auth:c,db:i,oauth:e.oauth}),p=async u=>{console.log(`
|
|
151
|
+
[MolnOS] Received ${u}, initiating graceful shutdown...`);try{await l.shutdown(),console.log("[MolnOS] Shutdown complete"),process.exit(0)}catch(h){console.error("[MolnOS] Error during shutdown:",h),process.exit(1)}};process.on("SIGINT",()=>p("SIGINT")),process.on("SIGTERM",()=>p("SIGTERM")),process.on("SIGHUP",()=>p("SIGHUP")),process.on("uncaughtException",async u=>{console.error("[MolnOS] Uncaught exception:",u);try{await l.shutdown()}catch{}process.exit(1)}),process.on("unhandledRejection",async(u,h)=>{console.error("[MolnOS] Unhandled rejection at:",h,"reason:",u);try{await l.shutdown()}catch{}process.exit(1)})}function wi(){return new X(Nr).get()}vi();export{vi as start};
|