@gfargo/doorman 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/run.js CHANGED
@@ -48,7 +48,7 @@ ${b}`)}),m.result}),{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules),c=
48
48
  `),o.info(" 1. Vercel Firewall"),o.info(` 2. Cloudflare WAF
49
49
  `);let n=await C("Select provider (1 or 2):",{type:"text"}),e=String(n).toLowerCase();return e==="1"||e==="vercel"?"vercel":e==="2"||e==="cloudflare"?"cloudflare":(o.warn(`Invalid choice: ${n}, defaulting to Vercel`),"vercel")}async function ee(n,e){try{n.debug&&(o.level=_i.LogLevels.debug);let t;if(n.optionalConfig)try{t=await G(n.config,"optional")}catch{t={}}else n.skipValidation?t=await G(n.config,"raw"):t=await G(n.config,"required");let i=await Si({provider:n.provider,config:t,interactive:!n.ci,token:n.token,projectId:n.projectId,teamId:n.teamId,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId}),r,s,a="",l="",c="";if(i.name==="vercel"){let d=await $t({token:n.token,projectId:n.projectId||t.projectId,teamId:n.teamId||t.teamId});a=d.token,l=d.projectId,c=d.teamId,r=new it(l,c,a),s=new tt(r)}else r={},s={};await e({config:t,provider:i,client:r,service:s,token:a,projectId:l,teamId:c})}catch(t){Q(t,n.errorContext)}}var Go="backup",Ho="Backup or restore firewall configurations",Zo={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},output:{alias:"o",type:"string",description:"Output directory for backups",default:"./backups"},restore:{alias:"r",type:"string",description:"Restore from backup file"},list:{alias:"l",type:"boolean",description:"List available backups",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Ko=async n=>{try{n.debug&&(o.level=Li.LogLevels.debug);let e=n.output||"./backups";if(n.list){if(!(0,he.existsSync)(e)){o.info(te.default.yellow("No backup directory found."));return}let t=(0,he.readdirSync)(e).filter(i=>i.endsWith(".json")).map(i=>{let r=(0,st.join)(e,i),s=(0,he.statSync)(r);return{name:i,size:s.size,created:s.mtime}}).sort((i,r)=>r.created.getTime()-i.created.getTime());if(t.length===0){o.info(te.default.yellow("No backups found."));return}o.log(te.default.bold(`
50
50
  \u{1F4E6} Available Backups:
51
- `)),t.forEach(i=>{o.log(`${te.default.cyan(i.name)}`),o.log(` ${te.default.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.default.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:(0,st.join)(e,n.restore);(0,he.existsSync)(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await G(t,"raw"),r=n.config||".doorman.json";if((0,he.existsSync)(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.default.yellow("Restore cancelled."));return}await z(i,r),o.success(te.default.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig(),l=i.name==="vercel"?{version:a.version,firewallEnabled:a.firewallEnabled,rules:a.rules.map(({valid:A,validationErrors:N,...v})=>v),ips:a.ips,updatedAt:a.updatedAt}:a;Re.getInstance().validateConfig(l),(0,he.existsSync)(e)||(0,he.mkdirSync)(e,{recursive:!0});let d=new Date,u=d.toISOString().split("T")[0]??"unknown-date",p=(d.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",h=`firewall-backup-${`${u}_${p}`}.json`,I=(0,st.join)(e,h),g={...l,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await z(g,I,{validate:!1}),o.success(te.default.green(`\u2705 Backup created: ${I}`)),o.log(""),o.log(te.default.bold("Backup Details:")),o.log(`${te.default.dim("Version:")} ${a.version}`),o.log(`${te.default.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.default.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.default.dim("To restore this backup later, run:")),o.log(te.default.cyan(`doorman backup --restore ${h}`))})}catch(e){Q(e,"managing backup")}};var Nt={};Z(Nt,{builder:()=>Yo,command:()=>Xo,desc:()=>Qo,handler:()=>en});var O=S(require("chalk"));k();var H=S(require("chalk")),Ft=S(require("cli-table3"));k();var _t=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let f=t[p],h=s[p];if(f===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let I=(f??0)-(h??0);if(I<=0)return!1;let g=Math.min(d,I);return s[p]!==void 0&&(s[p]+=g),u+=g,g===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};var de=S(require("chalk")),Di=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Jo=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${Di(e)}`;function Ui(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(de.default.blue("log"));break;case"deny":t.push(de.default.red("deny"));break;case"challenge":t.push(de.default.yellow("challenge"));break;case"bypass":t.push(de.default.green("bypass"));break;case"rate_limit":t.push(de.default.yellow("rate-limit"));break;case"redirect":t.push(de.default.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=de.default.yellow("rate-limit"),t.push(de.default.yellow(Jo(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=de.default.magenta("redirect"),t.push(de.default.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(de.default.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(de.default.gray(`for ${Di(e.actionDuration)}`)),t.join(" ")}var He=S(require("chalk"));function Lt(n){switch(n){case"unchanged":return He.default.bold.gray(" ");case"modified":return He.default.bold.yellow("\u25B4");case"new":return He.default.bold.green("+");case"deleted":return He.default.bold.red("-")}}var Ze=S(require("chalk"));function xi(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>Ze.default.cyan(`${r.type}:${r.neg?Ze.default.red(`!${r.op}:`):`${r.op}:`}${Ze.default.white(r.value)}`)).join(`
51
+ `)),t.forEach(i=>{o.log(`${te.default.cyan(i.name)}`),o.log(` ${te.default.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.default.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:(0,st.join)(e,n.restore);(0,he.existsSync)(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await G(t,"raw"),r=n.config||".doorman.json";if((0,he.existsSync)(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.default.yellow("Restore cancelled."));return}let{backup:s,...a}=i;await z(a,r),o.success(te.default.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig(),l=i.name==="vercel"?{version:a.version,firewallEnabled:a.firewallEnabled,rules:a.rules.map(({valid:A,validationErrors:N,...v})=>v),ips:a.ips,updatedAt:a.updatedAt}:a;Re.getInstance().validateConfig(l),(0,he.existsSync)(e)||(0,he.mkdirSync)(e,{recursive:!0});let d=new Date,u=d.toISOString().split("T")[0]??"unknown-date",p=(d.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",h=`firewall-backup-${`${u}_${p}`}.json`,I=(0,st.join)(e,h),g={...l,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await z(g,I,{validate:!1}),o.success(te.default.green(`\u2705 Backup created: ${I}`)),o.log(""),o.log(te.default.bold("Backup Details:")),o.log(`${te.default.dim("Version:")} ${a.version}`),o.log(`${te.default.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.default.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.default.dim("To restore this backup later, run:")),o.log(te.default.cyan(`doorman backup --restore ${h}`))})}catch(e){Q(e,"managing backup")}};var Nt={};Z(Nt,{builder:()=>Yo,command:()=>Xo,desc:()=>Qo,handler:()=>en});var O=S(require("chalk"));k();var H=S(require("chalk")),Ft=S(require("cli-table3"));k();var _t=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let f=t[p],h=s[p];if(f===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let I=(f??0)-(h??0);if(I<=0)return!1;let g=Math.min(d,I);return s[p]!==void 0&&(s[p]+=g),u+=g,g===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};var de=S(require("chalk")),Di=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Jo=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${Di(e)}`;function Ui(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(de.default.blue("log"));break;case"deny":t.push(de.default.red("deny"));break;case"challenge":t.push(de.default.yellow("challenge"));break;case"bypass":t.push(de.default.green("bypass"));break;case"rate_limit":t.push(de.default.yellow("rate-limit"));break;case"redirect":t.push(de.default.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=de.default.yellow("rate-limit"),t.push(de.default.yellow(Jo(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=de.default.magenta("redirect"),t.push(de.default.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(de.default.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(de.default.gray(`for ${Di(e.actionDuration)}`)),t.join(" ")}var He=S(require("chalk"));function Lt(n){switch(n){case"unchanged":return He.default.bold.gray(" ");case"modified":return He.default.bold.yellow("\u25B4");case"new":return He.default.bold.green("+");case"deleted":return He.default.bold.red("-")}}var Ze=S(require("chalk"));function xi(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>Ze.default.cyan(`${r.type}:${r.neg?Ze.default.red(`!${r.op}:`):`${r.op}:`}${Ze.default.white(r.value)}`)).join(`
52
52
  `);return t>0?`${Ze.default.yellow("OR")}
53
53
  ${i}`:i}).join(`
54
54
  `)}var Dt=S(require("chalk"));function Oi(n){return{id:n.id||"-",ip:n.ip,hostname:n.hostname,notes:n.notes||"-",status:n.changeStatus?Dt.default.yellow(n.changeStatus):Dt.default.red("deny")}}var Ke=S(require("chalk"));function Ut(n){switch(n){default:case"unchanged":return Ke.default.white;case"modified":return Ke.default.yellow;case"new":return Ke.default.green;case"deleted":return Ke.default.red}}var le={unchanged:"unchanged",modified:"modified",new:"new",deleted:"deleted"},xt={minWidths:[10,28,20,5,12,35],maxWidths:[15,32,36,8,24,null],flexColumns:[0,1,2,4,5]},Ot={minWidths:[10,28,24,5,12],maxWidths:[20,32,26,null,16],flexColumns:[0,1,3]},Fi=(n,e)=>n==="ip"?_t(e,Ot.minWidths,Ot.maxWidths,Ot.flexColumns):_t(e,xt.minWidths,xt.maxWidths,xt.flexColumns);function ve(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("Name"),H.default.bold.gray("Conditions"),H.default.bold.gray(" "),H.default.bold.gray("Action"),H.default.bold.gray("Description")],i=["left","left","left","center","left","left"],r=process.stdout.columns||400,s=Fi("rules",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ft.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c="changeStatus"in l&&e,d=c?Ut(l.changeStatus):H.default.white,u=[d(l.id?l.id:"-"),d(l.name),d(xi(l.conditionGroup)),l.active?H.default.bold.green("\u2713"):H.default.bold.red("\u2717"),d(Ui(l.action)),d(l.description||"")];c&&u.unshift(d(Lt(l.changeStatus))),a.push(u)}),o.log(a.toString())}function Ee(n,{showStatus:e}){let t=[H.default.bold.gray("ID"),H.default.bold.gray("IP"),H.default.bold.gray("Hostname"),H.default.bold.gray("Notes"),H.default.bold.gray("Action")],i=["left","left","left","left","center"],r=process.stdout.columns||400,s=Fi("ip",r);e&&(t.unshift(H.default.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new Ft.default({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c=l.changeStatus!==void 0&&e,d=c?Ut(l.changeStatus):H.default.white,u=Oi(l),p=[d(u.id),d(u.ip),d(u.hostname),d(u.notes),u.status];c&&p.unshift(d(Lt(l.changeStatus))),a.push(p)}),o.log(a.toString())}var Xo="diff",Qo="Show detailed differences between local and remote firewall configuration",Yo={config:{alias:"c",type:"string",description:"Path to firewall config file"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},en=async n=>{await ee({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"calculating diff"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await tn(i,e,n);return}o.start("Calculating differences...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,f=l.length>0||c.length>0||d.length>0,h=e.version!==u;if(!p&&!f&&!h){o.success(O.default.green("No differences found. Local and remote configurations are in sync."));return}if(n.format==="json"){let g={version:{local:e.version,remote:u,changed:h},customRules:{toAdd:r.map(A=>({...A,status:"add"})),toUpdate:s.map(A=>({...A,status:"update"})),toDelete:a.map(A=>({...A,status:"delete"}))},ipRules:{toAdd:l.map(A=>({...A,status:"add"})),toUpdate:c.map(A=>({...A,status:"update"})),toDelete:d.map(A=>({...A,status:"delete"}))},summary:{hasChanges:p||f||h,customRuleChanges:r.length+s.length+a.length,ipRuleChanges:l.length+c.length+d.length}};o.log(JSON.stringify(g,null,2));return}o.log(O.default.bold(`
package/dist/bin/run.mjs CHANGED
@@ -48,7 +48,7 @@ ${b}`)}),m.result}),{toAdd:s,toUpdate:a,toDelete:l}=this.diffRules(r,i.rules),c=
48
48
  `),o.info(" 1. Vercel Firewall"),o.info(` 2. Cloudflare WAF
49
49
  `);let n=await C("Select provider (1 or 2):",{type:"text"}),e=String(n).toLowerCase();return e==="1"||e==="vercel"?"vercel":e==="2"||e==="cloudflare"?"cloudflare":(o.warn(`Invalid choice: ${n}, defaulting to Vercel`),"vercel")}async function X(n,e){try{n.debug&&(o.level=Fo.debug);let t;if(n.optionalConfig)try{t=await q(n.config,"optional")}catch{t={}}else n.skipValidation?t=await q(n.config,"raw"):t=await q(n.config,"required");let i=await hi({provider:n.provider,config:t,interactive:!n.ci,token:n.token,projectId:n.projectId,teamId:n.teamId,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId}),r,s,a="",l="",c="";if(i.name==="vercel"){let d=await wt({token:n.token,projectId:n.projectId||t.projectId,teamId:n.teamId||t.teamId});a=d.token,l=d.projectId,c=d.teamId,r=new Ze(l,c,a),s=new He(r)}else r={},s={};await e({config:t,provider:i,client:r,service:s,token:a,projectId:l,teamId:c})}catch(t){J(t,n.errorContext)}}var zo="backup",Wo="Backup or restore firewall configurations",Mo={config:{alias:"c",type:"string",description:"Path to firewall config file (defaults to .doorman.json)"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID (can be set in config file)"},teamId:{alias:"t",type:"string",description:"Vercel Team ID (can be set in config file)"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},output:{alias:"o",type:"string",description:"Output directory for backups",default:"./backups"},restore:{alias:"r",type:"string",description:"Restore from backup file"},list:{alias:"l",type:"boolean",description:"List available backups",default:!1},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},qo=async n=>{try{n.debug&&(o.level=jo.debug);let e=n.output||"./backups";if(n.list){if(!Qe(e)){o.info(te.yellow("No backup directory found."));return}let t=Vo(e).filter(i=>i.endsWith(".json")).map(i=>{let r=bt(e,i),s=Bo(r);return{name:i,size:s.size,created:s.mtime}}).sort((i,r)=>r.created.getTime()-i.created.getTime());if(t.length===0){o.info(te.yellow("No backups found."));return}o.log(te.bold(`
50
50
  \u{1F4E6} Available Backups:
51
- `)),t.forEach(i=>{o.log(`${te.cyan(i.name)}`),o.log(` ${te.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:bt(e,n.restore);Qe(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await q(t,"raw"),r=n.config||".doorman.json";if(Qe(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.yellow("Restore cancelled."));return}await j(i,r),o.success(te.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await X({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig(),l=i.name==="vercel"?{version:a.version,firewallEnabled:a.firewallEnabled,rules:a.rules.map(({valid:A,validationErrors:x,...v})=>v),ips:a.ips,updatedAt:a.updatedAt}:a;we.getInstance().validateConfig(l),Qe(e)||No(e,{recursive:!0});let d=new Date,u=d.toISOString().split("T")[0]??"unknown-date",p=(d.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",h=`firewall-backup-${`${u}_${p}`}.json`,I=bt(e,h),g={...l,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await j(g,I,{validate:!1}),o.success(te.green(`\u2705 Backup created: ${I}`)),o.log(""),o.log(te.bold("Backup Details:")),o.log(`${te.dim("Version:")} ${a.version}`),o.log(`${te.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.dim("To restore this backup later, run:")),o.log(te.cyan(`doorman backup --restore ${h}`))})}catch(e){J(e,"managing backup")}};var kt={};G(kt,{builder:()=>Ko,command:()=>Ho,desc:()=>Zo,handler:()=>Jo});k();import N from"chalk";k();import K from"chalk";import bi from"cli-table3";var Et=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let f=t[p],h=s[p];if(f===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let I=(f??0)-(h??0);if(I<=0)return!1;let g=Math.min(d,I);return s[p]!==void 0&&(s[p]+=g),u+=g,g===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};import ue from"chalk";var yi=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Go=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${yi(e)}`;function wi(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(ue.blue("log"));break;case"deny":t.push(ue.red("deny"));break;case"challenge":t.push(ue.yellow("challenge"));break;case"bypass":t.push(ue.green("bypass"));break;case"rate_limit":t.push(ue.yellow("rate-limit"));break;case"redirect":t.push(ue.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=ue.yellow("rate-limit"),t.push(ue.yellow(Go(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=ue.magenta("redirect"),t.push(ue.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(ue.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(ue.gray(`for ${yi(e.actionDuration)}`)),t.join(" ")}import Ye from"chalk";function $t(n){switch(n){case"unchanged":return Ye.bold.gray(" ");case"modified":return Ye.bold.yellow("\u25B4");case"new":return Ye.bold.green("+");case"deleted":return Ye.bold.red("-")}}import et from"chalk";function Ci(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>et.cyan(`${r.type}:${r.neg?et.red(`!${r.op}:`):`${r.op}:`}${et.white(r.value)}`)).join(`
51
+ `)),t.forEach(i=>{o.log(`${te.cyan(i.name)}`),o.log(` ${te.dim("Created:")} ${i.created.toLocaleString()}`),o.log(` ${te.dim("Size:")} ${(i.size/1024).toFixed(1)} KB`),o.log("")});return}if(n.restore){let t=n.restore.startsWith("/")?n.restore:bt(e,n.restore);Qe(t)||(o.error(`Backup file not found: ${t}`),process.exit(1));let i=await q(t,"raw"),r=n.config||".doorman.json";if(Qe(r)&&!await C(`Config file ${r} already exists. Do you want to overwrite it?`,{type:"confirm"})){o.info(te.yellow("Restore cancelled."));return}let{backup:s,...a}=i;await j(a,r),o.success(te.green(`\u2705 Restored configuration from ${t} to ${r}`));return}await X({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"creating backup"},async({client:t,provider:i,projectId:r,teamId:s})=>{o.start("Fetching current remote configuration...");let a=i.name==="vercel"?await t.fetchFirewallConfig():await i.fetchConfig(),l=i.name==="vercel"?{version:a.version,firewallEnabled:a.firewallEnabled,rules:a.rules.map(({valid:A,validationErrors:x,...v})=>v),ips:a.ips,updatedAt:a.updatedAt}:a;we.getInstance().validateConfig(l),Qe(e)||No(e,{recursive:!0});let d=new Date,u=d.toISOString().split("T")[0]??"unknown-date",p=(d.toISOString().split("T")[1]??"").split(".")[0]?.replace(/:/g,"-")??"unknown-time",h=`firewall-backup-${`${u}_${p}`}.json`,I=bt(e,h),g={...l,backup:{createdAt:new Date().toISOString(),source:"remote",provider:i.name,...i.name==="vercel"?{projectId:r,teamId:s}:{},originalVersion:a.version}};await j(g,I,{validate:!1}),o.success(te.green(`\u2705 Backup created: ${I}`)),o.log(""),o.log(te.bold("Backup Details:")),o.log(`${te.dim("Version:")} ${a.version}`),o.log(`${te.dim("Rules:")} ${a.rules.length} custom, ${a.ips?.length??0} IP blocking`),o.log(`${te.dim("Created:")} ${new Date().toLocaleString()}`),o.log(""),o.log(te.dim("To restore this backup later, run:")),o.log(te.cyan(`doorman backup --restore ${h}`))})}catch(e){J(e,"managing backup")}};var kt={};G(kt,{builder:()=>Ko,command:()=>Ho,desc:()=>Zo,handler:()=>Jo});k();import N from"chalk";k();import K from"chalk";import bi from"cli-table3";var Et=(n,e,t,i)=>{if(!e.length)throw new Error("minWidths array cannot be empty");if(i.some(c=>c>=e.length))throw new Error("flexColumns contains invalid column index");if(e.length!==t.length)throw new Error("minWidths and maxWidths arrays must have the same length");e.forEach((c,d)=>{let u=t[d];if(u!=null&&c>u)throw new Error(`Column ${d} has minimum width (${c}) greater than maximum width (${u})`)});let r=n||400,s=[...e],a=e.reduce((c,d)=>c+d,0),l=r-a-28;if(l>0&&i.length>0){let c=i.filter(d=>{let u=t[d],p=s[d];return u!==void 0&&p!==void 0&&(u===null||p<u)});for(;l>0&&c.length>0;){let d=Math.floor(l/c.length);if(d===0)break;let u=0;c=c.filter(p=>{let f=t[p],h=s[p];if(f===null)return s[p]!==void 0&&(s[p]+=d),u+=d,!0;{let I=(f??0)-(h??0);if(I<=0)return!1;let g=Math.min(d,I);return s[p]!==void 0&&(s[p]+=g),u+=g,g===d}}),l-=u}if(l>0){let d=i.findLast(u=>t[u]===null);d!==void 0&&d!==void 0&&s[d]!==void 0&&(s[d]+=l)}}return s};import ue from"chalk";var yi=n=>{if(n==="permanent")return"permanent";let e=n.slice(0,-1),t=n.slice(-1);return`${e}${{s:"sec",m:"min",h:"hr",d:"day"}[t]||t}`},Go=(n,e)=>e==="60s"?`${n}/min`:e==="3600s"?`${n}/hr`:e==="86400s"?`${n}/day`:`${n}/${yi(e)}`;function wi(n){if(!n.mitigate)return"unknown";let{mitigate:e}=n,t=[];switch(e.action){case"log":t.push(ue.blue("log"));break;case"deny":t.push(ue.red("deny"));break;case"challenge":t.push(ue.yellow("challenge"));break;case"bypass":t.push(ue.green("bypass"));break;case"rate_limit":t.push(ue.yellow("rate-limit"));break;case"redirect":t.push(ue.magenta("redirect"));break;default:t.push(e.action)}return e.rateLimit&&(t[0]=ue.yellow("rate-limit"),t.push(ue.yellow(Go(e.rateLimit.requests,e.rateLimit.window)))),e.redirect&&(t[0]=ue.magenta("redirect"),t.push(ue.magenta(`\u2192 ${e.redirect.location}`)),e.redirect.permanent!==void 0&&t.push(ue.gray(`(${e.redirect.permanent?"301":"302"})`))),e.actionDuration&&t.push(ue.gray(`for ${yi(e.actionDuration)}`)),t.join(" ")}import Ye from"chalk";function $t(n){switch(n){case"unchanged":return Ye.bold.gray(" ");case"modified":return Ye.bold.yellow("\u25B4");case"new":return Ye.bold.green("+");case"deleted":return Ye.bold.red("-")}}import et from"chalk";function Ci(n=[]){return n.map((e,t)=>{let i=e.conditions.map(r=>et.cyan(`${r.type}:${r.neg?et.red(`!${r.op}:`):`${r.op}:`}${et.white(r.value)}`)).join(`
52
52
  `);return t>0?`${et.yellow("OR")}
53
53
  ${i}`:i}).join(`
54
54
  `)}import Ri from"chalk";function Ii(n){return{id:n.id||"-",ip:n.ip,hostname:n.hostname,notes:n.notes||"-",status:n.changeStatus?Ri.yellow(n.changeStatus):Ri.red("deny")}}import tt from"chalk";function At(n){switch(n){default:case"unchanged":return tt.white;case"modified":return tt.yellow;case"new":return tt.green;case"deleted":return tt.red}}var se={unchanged:"unchanged",modified:"modified",new:"new",deleted:"deleted"},Tt={minWidths:[10,28,20,5,12,35],maxWidths:[15,32,36,8,24,null],flexColumns:[0,1,2,4,5]},Pt={minWidths:[10,28,24,5,12],maxWidths:[20,32,26,null,16],flexColumns:[0,1,3]},vi=(n,e)=>n==="ip"?Et(e,Pt.minWidths,Pt.maxWidths,Pt.flexColumns):Et(e,Tt.minWidths,Tt.maxWidths,Tt.flexColumns);function Ce(n,{showStatus:e}){let t=[K.bold.gray("ID"),K.bold.gray("Name"),K.bold.gray("Conditions"),K.bold.gray(" "),K.bold.gray("Action"),K.bold.gray("Description")],i=["left","left","left","center","left","left"],r=process.stdout.columns||400,s=vi("rules",r);e&&(t.unshift(K.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new bi({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c="changeStatus"in l&&e,d=c?At(l.changeStatus):K.white,u=[d(l.id?l.id:"-"),d(l.name),d(Ci(l.conditionGroup)),l.active?K.bold.green("\u2713"):K.bold.red("\u2717"),d(wi(l.action)),d(l.description||"")];c&&u.unshift(d($t(l.changeStatus))),a.push(u)}),o.log(a.toString())}function Re(n,{showStatus:e}){let t=[K.bold.gray("ID"),K.bold.gray("IP"),K.bold.gray("Hostname"),K.bold.gray("Notes"),K.bold.gray("Action")],i=["left","left","left","left","center"],r=process.stdout.columns||400,s=vi("ip",r);e&&(t.unshift(K.bold.gray("Status")),i.unshift("center"),s.unshift(10));let a=new bi({head:t,colAligns:i,wrapOnWordBoundary:!0,wordWrap:!0,colWidths:s});n.forEach(l=>{let c=l.changeStatus!==void 0&&e,d=c?At(l.changeStatus):K.white,u=Ii(l),p=[d(u.id),d(u.ip),d(u.hostname),d(u.notes),u.status];c&&p.unshift(d($t(l.changeStatus))),a.push(p)}),o.log(a.toString())}var Ho="diff",Zo="Show detailed differences between local and remote firewall configuration",Ko={config:{alias:"c",type:"string",description:"Path to firewall config file"},provider:{type:"string",choices:["vercel","cloudflare"],description:"Firewall provider (auto-detected)"},projectId:{alias:"p",type:"string",description:"Vercel Project ID"},teamId:{alias:"t",type:"string",description:"Vercel Team ID"},token:{type:"string",description:"Vercel API token (defaults to VERCEL_TOKEN env var)"},apiToken:{type:"string",description:"Cloudflare API token (defaults to CLOUDFLARE_API_TOKEN env var)"},zoneId:{type:"string",description:"Cloudflare Zone ID (defaults to CLOUDFLARE_ZONE_ID env var)"},accountId:{type:"string",description:"Cloudflare Account ID (optional)"},format:{alias:"f",type:"string",choices:["table","json"],description:"Output format",default:"table"},debug:{type:"boolean",description:"Enable debug logging",default:!1},ci:{type:"boolean",description:"Run in CI mode (non-interactive)",default:!1}},Jo=async n=>{await X({config:n.config,provider:n.provider,projectId:n.projectId,teamId:n.teamId,token:n.token,apiToken:n.apiToken,zoneId:n.zoneId,accountId:n.accountId,debug:n.debug,ci:n.ci,errorContext:"calculating diff"},async({config:e,service:t,provider:i})=>{if(i.name!=="vercel"){await Xo(i,e,n);return}o.start("Calculating differences...");let{toAdd:r,toUpdate:s,toDelete:a,ipsToAdd:l,ipsToUpdate:c,ipsToDelete:d,version:u}=await t.getChanges(e),p=r.length>0||s.length>0||a.length>0,f=l.length>0||c.length>0||d.length>0,h=e.version!==u;if(!p&&!f&&!h){o.success(N.green("No differences found. Local and remote configurations are in sync."));return}if(n.format==="json"){let g={version:{local:e.version,remote:u,changed:h},customRules:{toAdd:r.map(A=>({...A,status:"add"})),toUpdate:s.map(A=>({...A,status:"update"})),toDelete:a.map(A=>({...A,status:"delete"}))},ipRules:{toAdd:l.map(A=>({...A,status:"add"})),toUpdate:c.map(A=>({...A,status:"update"})),toDelete:d.map(A=>({...A,status:"delete"}))},summary:{hasChanges:p||f||h,customRuleChanges:r.length+s.length+a.length,ipRuleChanges:l.length+c.length+d.length}};o.log(JSON.stringify(g,null,2));return}o.log(N.bold(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gfargo/doorman",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "description": "Manage firewall rules as code across multiple providers",
5
5
  "publishConfig": {
6
6
  "access": "public"