@metricinsights/pp-dev
Version:
Portal Page dev build tool
3 lines (2 loc) • 32.2 kB
JavaScript
;var e=require("http-proxy-middleware"),t=require("url"),a=require("vite"),r=require("picocolors"),s=require("axios"),i=require("jsdom"),n=require("https"),o=require("memory-cache"),p=require("path"),c=require("fs"),l=require("crypto"),d=require("process"),h=require("child_process"),u=require("console"),g=require("zlib"),m=require("express"),f="undefined"!=typeof document?document.currentScript:null;function w(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(a){if("default"!==a){var r=Object.getOwnPropertyDescriptor(e,a);Object.defineProperty(t,a,r.get?r:{enumerable:!0,get:function(){return e[a]}})}})),t.default=e,Object.freeze(t)}var y=w(o),v=w(p),b=w(l),x=w(d),k=w(h),T=w(u);const P=(e,t,a)=>{const r=new RegExp(`(!!)?(https?(:(\\\\)?/(\\\\)?/)${e})`,"gi");return a.replace(r,((e,...a)=>e.startsWith("!!")?a[1]:`http${a[2]}${t}`))},A=(e,t,a)=>{e.setHeader("location",t),e.statusCode=a,e.end()};function $(e){return e.split("?")[0]}const L=new Map,S="default",I=(e="info",t=S)=>{if(L.has(t))return L.get(t);if(t===S){const r=a.createLogger(e);return L.set(t,r),r}const r=a.createLogger(e);return L.set(t,r),r},E=r.createColors(),j=function(){const e=setInterval((function(){if(document.querySelector("#mi-react-root form")){clearInterval(e);const t=document.querySelector("#mi-react-root form"),a=t?.firstChild,r=a.lastChild.cloneNode(!0);r.innerHTML="";const s=r.cloneNode(!0);s.innerText="OR";const i=r.cloneNode(!0),n=document.createElement("div"),o=`\n<style>\n.helper-login-wrapper {\n font-family: Arial, sans-serif;\n color: #222;\n padding: 8px;\n background-color: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #ddd;\n width: 100%;\n max-width: 400px;\n margin: 0 auto;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n\n.helper-login-wrapper .title {\n font-weight: bold;\n font-size: 18px;\n margin-bottom: 12px;\n text-align: center;\n}\n\n.helper-login-wrapper .control {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.helper-login-wrapper .control input {\n border: 1px solid #ddd;\n /*padding: 12px;*/\n border-radius: 4px;\n width: 100%;\n box-sizing: border-box;\n text-align: center;\n height: 26px;\n}\n\n.helper-login-wrapper .control .caption {\n font-size: 12px;\n color: #666;\n text-align: center;\n}\n\n.helper-login-wrapper .footer {\n margin-top: 16px;\n text-align: center;\n}\n\n.helper-login-wrapper .footer .btn.submit {\n background-color: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n width: 100%;\n font-size: 14px;\n transition: background-color 0.3s;\n height: 26px;\n}\n\n.helper-login-wrapper .footer .btn.submit:hover {\n background-color: #0056b3;\n}\n\n.helper-login-wrapper .token-type-switcher {\n display: flex;\n border: 1px solid #ccc;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 12px;\n width: 100%;\n}\n\n.helper-login-wrapper .token-type-option {\n padding: 4px 8px;\n cursor: pointer;\n background-color: #f8f9fa;\n color: #666;\n font-size: 12px;\n text-align: center;\n transition: background-color 0.2s, color 0.2s;\n border-right: 1px solid #ccc;\n flex: 1;\n}\n\n.helper-login-wrapper .token-type-option:last-child {\n border-right: none;\n}\n\n.helper-login-wrapper .token-type-option.active {\n background-color: #007bff;\n color: white;\n font-weight: bold;\n}\n\n.helper-login-wrapper .token-type-option:not(.active):hover {\n background-color: #e9ecef;\n}\n</style>\n<div class="helper-login-wrapper">\n <div class="title">PP Dev Helper</div>\n\n <div class="token-type-switcher" id="token-type-switcher">\n <div class="token-type-option active" data-value="personal">\n API Token\n </div>\n <div class="token-type-option" data-value="regular">\n Legacy Token\n </div>\n </div>\n\n <div class="control">\n <input type="password" id="helper-token" placeholder="API Token">\n <span class="caption" id="token-caption">\n You can get the API Token\n <a href="https://${host}/api-token" target="_blank" style="color: #007bff; text-decoration: none;">here</a>\n </span>\n </div>\n\n <div class="footer">\n <button id="helper-token-submit" class="btn submit">\n Token Login\n </button>\n </div>\n</div>`;n.innerHTML=o,i.appendChild(n),a.appendChild(s),a.appendChild(i);const p=document.getElementById("token-type-switcher"),c=p?.querySelectorAll(".token-type-option"),l=document.getElementById("helper-token"),d=document.getElementById("token-caption"),h={personal:{placeholder:"API Token",caption:`You can get the API Token\n <a href="https://${host}/api-token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`},regular:{placeholder:"Legacy Token",caption:`You can get the Legacy Token\n <a href="https://${host}/api/get_token" target="_blank" \n style="color: #007bff; text-decoration: none;">here</a>`}},u=e=>{const t=h[e];t&&(l.placeholder=t.placeholder,d.innerHTML=t.caption)};u("personal"),c?.forEach((e=>{e.addEventListener("click",(e=>{const t=e.currentTarget,a=t.dataset.value;c.forEach((e=>e.classList.remove("active"))),t.classList.add("active"),u(a)}))})),document.getElementById("helper-token-submit")?.addEventListener("click",(e=>{e.preventDefault();const t=document.getElementById("helper-token");if(t){if(!t.value)return void alert(`${l.placeholder} is required`);const e=t.value,a=p?.querySelector(".token-type-option.active"),r=a?.dataset.value||"personal";fetch("/@api/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,tokenType:r}),redirect:"follow"}).then((async e=>{const t=e.url&&new URL(e.url,window.location.origin);if(!t||t.pathname.startsWith("/login")||t.pathname.startsWith("/@api/"))try{const t=await e.json();alert(`Login failed: ${t?.error}`)}catch(e){alert("Login failed: Unknown error")}else window.location.href=e.url})).catch((()=>{alert("Login failed")}))}}))}}),100)},O=/^(https?:\/\/)([^/]+)(\/.*)?$/i,C="X-PP-Proxy";function F(t){const{rewritePath:a=/^\/(?!pt).*/i,baseURL:r="",devServer:s,disableSSLValidation:i=!1,miAPI:n}=t;if(!r)throw new Error("Base url is required");const o=r.replace(O,"$2"),p=r.replace(O,"$1$2"),c=import("file-type"),l=I();return e.createProxyMiddleware({selfHandleResponse:!0,pathFilter:(e,r)=>!(t.proxyIgnore||[]).some((t=>"string"==typeof t?e.startsWith(t):"function"!=typeof t.test||t.test(e)))&&("string"==typeof a?e.startsWith(a):Array.isArray(a)?a.some((t=>e.startsWith(t))):"function"==typeof a.test&&a.test(e)),target:r,changeOrigin:!0,autoRewrite:!0,cookieDomainRewrite:{[o]:"localhost"},logger:{info:()=>{},log:()=>{},error:()=>{},warn:()=>{}},secure:!i,headers:{host:o,origin:p},on:{proxyReq(e,t,a){const s=t.headers.host,i=e.getHeader("referer");return l.info(`${E.blue("Proxies request:")} ${E.green(t.method)} ${t.url} -> ${E.green(e.method)} ${e.protocol}//${e.host}${e.path}`),s&&i&&"string"==typeof i&&e.setHeader("referer",i.replace(new RegExp(`https?://${s}`),r)),n.personalAccessToken&&e.setHeader("Authorization",`Bearer ${n.personalAccessToken}`),t.socket.on("close",(()=>{setTimeout((()=>{e.destroyed||e.destroy()}),200)})),e},proxyRes:(t,a,r)=>{if(t.headers["content-type"]?.includes("text/event-stream")||t.headers["transfer-encoding"]?.includes("chunked")&&"no"===t.headers["x-accel-buffering"]){l.info(`${E.blue("Start streaming for request:")} ${E.green(a.method)} ${a.url}`);const e=async(e,t,a)=>{a.setHeader(C,1),a.setHeaders(new Map(Object.entries(e.headers))),e.pipe(a)};return e(t,a,r)}const s=e.responseInterceptor((async(e,t,a,r)=>{r.setHeader(C,1);if(await(await c).fileTypeFromBuffer(e))return e;{let t=e.toString("utf8");try{const e=new URL(a.url??"",`http://${o}`);if(e.searchParams&&e.searchParams.has("proxyRedirect")){const e=function(){const e="pp-dev::redirectCount";let t=+(localStorage.getItem(e)??0);Number.isNaN(t)&&(t=0);let a=window.location.href;const r=function(){const s=new URLSearchParams(window.location.search);s.has("proxyRedirect")?a!==window.location.href?(a=window.location.href,setTimeout(r,t<3?3e3:5e3)):(localStorage.setItem(e,""+ ++t),window.location.href=s.get("proxyRedirect")):localStorage.removeItem(e)};setTimeout(r,3e3)};t+=`<script>(${e.toString()})()<\/script>`}e.pathname.startsWith("/login")&&(t+=`<script>const host = "${o}";\n(${j.toString()})()<\/script>`)}catch{}const r=a.headers.host??"";return((e,t,a)=>{const r=new RegExp(`${e.replace(/\\*\//gi,"\\\\/")}`,"gi"),s=new RegExp(`${e}`,"gi"),i=a.replace(r,t);return i===a?i.replace(s,t):i})("/auth/saml/login","/login",P(o,r,t))}}));return s(t,a,r)},error(e,t,a){const r=`Proxy error: "${e.message}" when trying to "${t.method} ${t.url}"\n\n${e.stack}`;l.error(r),a.writable&&a.writeHead(500,{"Content-Type":"text/plain"}),a.end(r)}}})}class R{axios;constructor(e){this.axios=e}async checkAuth(e){return this.axios.get("/data/page/index/auth/info",{headers:Object.assign({},e,{accept:"text/html"}),maxRedirects:0}).then((()=>!0)).catch((()=>!1))}}class B extends R{formdataModulePromise=import("formdata-node");constructor(e){super(e)}getDownloadUrl(e){return`/admin/page/downloadassets/id/${e}`}getDownloadTemplateUrl(e){return`/admin/pagetemplate/downloadassets/id/${e}`}getUploadUrl(e){return`/admin/page/uploadassets/id/${e}`}getUploadTemplateUrl(e){return`/admin/pagetemplate/uploadassets/id/${e}`}async downloadPageAssets(e,t){return this.axios.get(this.getDownloadUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then((e=>e.data))}async uploadPageAssets(e,t,a){const r=new(await this.formdataModulePromise).FormData,{File:s}=await this.formdataModulePromise,i=new s([t],"file.zip",{type:"application/zip"});r.append("file",i);const n=this.getUploadUrl(e);return this.axios.post(n,r,{withCredentials:!0,headers:Object.assign({},a,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:n})})}).then((e=>e.data))}async downloadTemplateAssets(e,t){return this.axios.get(this.getDownloadTemplateUrl(e),{withCredentials:!0,headers:Object.assign({},t,{accept:"*/*"}),responseType:"arraybuffer"}).then((e=>e.data))}async uploadTemplateAssets(e,t,a){const r=new(await this.formdataModulePromise).FormData,{File:s}=await this.formdataModulePromise,i=new s([t],"file.zip",{type:"application/zip"});r.append("file",i,"file.zip");const n=this.getUploadTemplateUrl(e);return this.axios.post(n,r,{withCredentials:!0,headers:Object.assign({},a,{accept:"application/json","Content-Type":"multipart/form-data",Referer:this.axios.getUri({url:n})})}).then((e=>e.data))}}class N extends B{getDownloadUrl(e){return`/api/page/id/${e}/asset/download`}getDownloadTemplateUrl(e){return`/api/page_template/id/${e}/asset/download`}getUploadUrl(e){return`/api/page/id/${e}/asset/upload`}getUploadTemplateUrl(e){return`/api/page_template/id/${e}/asset/upload`}}class H extends R{async getAll(e){return(await this.axios.get("/api/page",{withCredentials:!0,headers:Object.assign({},e,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"})})).data.pages}async get(e,t){return(await this.axios.get(`/api/page/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data.page}async getPageContent(e,t){return(await this.axios.get(`/p/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"text/html","content-type":"application/json"})})).data}async create(e,t){return(await this.axios.post("/api/page",e,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data.page}}class M extends R{async getAll(e,t){return(await this.axios.get("/api/page_template",{withCredentials:!0,headers:Object.assign({},t,{Accept:"application/json","Content-Type":"application/json","Cache-Control":"no-cache",Pragma:"no-cache",Expires:"0"}),params:{internal_name:e}})).data.page_templates}async get(e,t){return(await this.axios.get(`/api/page_template/id/${e}`,{withCredentials:!0,headers:Object.assign({},t,{accept:"application/json","content-type":"application/json"})})).data.page_template}}const D="[DEV PAGE. DO NOT DELETE]";class U{#e;#t;#a=null;#r;#s;#i;#n;#o;#p;#c;portalPageId;templateLess;assetsApi;pageApi;pageTemplateApi;logger;constructor(e,t){const{headers:a={},portalPageId:r,templateLess:i=!0,disableSSLValidation:o=!1,v7Features:p=!1,personalAccessToken:c}=t||{};this.#e=a,this.#s=[],this.#r="",this.#i=p,this.#n=c,this.#o=!1,this.#p=Promise.resolve(!1),o&&(s.defaults.httpsAgent=new n.Agent({rejectUnauthorized:!1})),this.#t=s.create({baseURL:e,headers:a}),this.portalPageId=r,this.templateLess=i,this.assetsApi=new(p?N:B)(this.#t),this.pageApi=new H(this.#t),this.pageTemplateApi=new M(this.#t),this.logger=I()}async isTemplateLoaded(){return this.#p}get isV710OrHigher(){return this.#o}#l(e){const t=Object.assign({},e,{host:void 0,referer:void 0});return Object.keys(t).forEach((e=>void 0===t[e]&&delete t[e])),this.#e=t,!t.authorization&&this.#n&&(t.authorization=`Bearer ${this.#n}`),t}get personalAccessToken(){return this.#n}set personalAccessToken(e){this.#n=e}updateHeaders(e){this.#l(e)}get localTemplateHTML(){return'<!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8" />\n <title>Local template</title>\n <meta charset="UTF-8" />\n <link rel="icon" type="image/svg+xml" href="/favicon.ico" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n </head>\n <body>\n <div id="mi-react-root"></div>\n <script src="/auth/info.js"><\/script>\n <script src="/js/main.js" defer><\/script>\n <link rel="stylesheet" href="/css/main.css" />\n </body>\n </html>'}async getPageTemplate(e){if(await this.#p||this.#a||(this.#p=new Promise((e=>{this.#c=e}))),this.#a)return Promise.resolve(this.#a);if(void 0===this.templateLess&&(this.templateLess=!1),this.#i){return void 0!==(await this.pageApi.get(this.portalPageId,this.#l(e)).then((e=>(this.logger.info(E.green("Page fetched")),e))).catch((async t=>{throw this.#c(!1),await this.pageApi.checkAuth(this.#l(e))?(this.logger.error(E.red(`Error fetching page data: ${t.message}\n${t.stack}`)),new Error("The current user does not have access to this page. Check your configuration to ensure the portalPageId is correct.")):t}))).template_id&&(this.#o=!0),this.#a=this.localTemplateHTML,this.logger.info(E.green("Local page template fetched")),this.#c(!0),this.#a}let t=(await this.pageApi.getAll(this.#l(e)).then((e=>(this.logger.info(E.green("Page list fetched")),e))).catch((async t=>{throw this.#c(!1),await this.pageApi.checkAuth(this.#l(e))?(this.logger.error(E.red(`Error fetching page list: ${t.message}\n${t.stack}`)),new Error("Current user does not have access to page list")):t}))).find((e=>e.name===D));return t||(this.logger.warn(E.yellow("Creating dev page template...")),t=await this.pageApi.create({enabled:"Y",name:D,internal_name:"dev-page-template",visible_in_homepage:"Y"},this.#l(e)).then((e=>(this.logger.info(E.green("Dev page created")),e))).catch((e=>{throw this.logger.error(E.red(`Error creating dev page: ${e.message}`)),this.#c(!1),new Error(`Error when creating dev page.\n That can be caused by missing permissions or page with name "${D}" already exists`)}))),await this.pageApi.getPageContent(t.internal_name,this.#l(e)).then((e=>(this.#a=e,e))).then((e=>(this.#c(!0),this.logger.info(E.green("Page template fetched")),e))).catch((e=>{throw this.#c(!1),this.logger.error(E.red(`Error fetching page template: ${e.message}`)),new Error("Error fetching page template")}))}async getPageVariables(e,t){return this.#a=await this.getPageTemplate(t),this.portalPageId||(this.portalPageId=e,this.templateLess=!1),await this.pageApi.get(e,this.#l(t)).then((e=>{const{tags:t="[]",name:a,template:r}=e;if(r&&t){const e=JSON.parse(t);return this.#s=e,this.#r=a,e}return this.#s=[],this.#r=a,[]})).catch((t=>{if(this.logger.error(E.red(`Error fetching page variables: ${t.message}`)),404===t.response?.status)throw new Error(`Portal Page with id "${e}" not found on instance ${this.#t.getUri()}`);if(401===t.response?.status)throw new Error(`Current user does not have access to page with id "${e}" on instance ${this.#t.getUri()}`);throw t}))}buildPage(e,t=!1){let a="string"==typeof e?e:e.toString("utf-8");for(const e of this.#s)a=a.replace(new RegExp(`\\[${e.name}\\]`,"g"),e.value);const r=new i.JSDOM(t?a:this.#a),s="%%PLACEHOLDER%%";if(!t){const e=r.window.document.createElement("div");e.innerHTML=s;const t=r.window.document.querySelector(".main-side");if(t){const a=t.querySelectorAll("script");a.length?t.insertBefore(e,a.item(a.length-1)):t.append(e)}else{const t=r.window.document.createElement("div");t.append(e),r.window.document.body.append(t)}const a=r.window.document.querySelector("head"),i=a.querySelector("title");i?i.text=this.#r:a.innerHTML+=`<title>${this.#r}</title>`}return r.serialize().replace(new RegExp(`<div>\\s*${s}\\s*<\\/div>`,"i"),a)}async getAssets(){if(this.portalPageId){if(this.templateLess)return await this.assetsApi.downloadPageAssets(this.portalPageId,this.#e);{const e=await this.pageApi.get(this.portalPageId,this.#e);if(this.#o){const t=await this.pageTemplateApi.get(e.template_id,this.#e);return await this.assetsApi.downloadTemplateAssets(t.id,this.#e)}if(e.template)return await this.assetsApi.downloadTemplateAssets(e.template,this.#e)}}}async updateAssets(e){if(this.portalPageId){if(this.templateLess)return await this.assetsApi.uploadPageAssets(this.portalPageId,e,this.#e);{const t=await this.pageApi.get(this.portalPageId,this.#e);if(this.#o){const a=await this.pageTemplateApi.get(t.template_id,this.#e);return await this.assetsApi.uploadTemplateAssets(a.id,e,this.#e)}if(t.template)return await this.assetsApi.uploadTemplateAssets(t.template,e,this.#e)}}}async get(e,t,a=!1){const r=a?t:Object.assign({},this.#l(this.#e),t);return await this.#t.get(e,{headers:r})}}class V{server;opts;eventMap;logger;constructor(e,t){this.server=e,this.opts=t||{},this.eventMap=new Map,this.eventMap.set("info-data:request",this.onInfoDataRequest.bind(this)),this.eventMap.set("template:sync",this.onTemplateSync.bind(this)),this.logger=I(),this.init()}init(){const{ws:e}=this.server;for(const[t,a]of this.eventMap)e.on(t,a)}onInfoDataRequest(){this.server.ws.send({type:"custom",event:"info-data:response",data:{}})}async onTemplateSync(){if(!this.opts.distService||!this.opts.miAPI)return this.server.ws.send("template:sync:response",{error:"Dist service or MiAPI is not defined"}),void this.logger.error(E.red("Dist service or MiAPI is not defined"));{const{distService:e,miAPI:t}=this.opts;if(this.server.config.clientInjectionPlugin?.v7Features){if(!t?.isV710OrHigher)return void this.server.ws.send("template:sync:response",{error:"This feature is available only for MI v7.1.0 or higher",config:{canSync:!1}});this.server.ws.send("client:config:update",{config:{canSync:!0}})}const a=await(t?.getAssets().catch((e=>{if(s.isAxiosError(e)){if(412===e.response?.status)return this.logger.info(E.yellow("Session expired")),this.server.ws.send("template:sync:response",{error:"Session expired",refresh:!0}),e;if(e.cause instanceof Error&&("ECONNRESET"===e.cause.code||"ENOTFOUND"===e.cause.code))return this.logger.info(E.yellow("Server in maintenance mode, VPN connection is needed or no internet connection")),this.server.ws.send("template:sync:response",{error:"Server in maintenance mode, VPN connection is needed or no internet connection"}),e}throw e})));if(a instanceof Error)return;const r=a?await(e?.saveBackupAndBuild(a).catch((e=>{if("Backup file is not a ZIP file"===e.message)return this.logger.error(E.red("Backup file is not a ZIP file")),e}))):await(e?.buildNewAssets());if(!(r&&r instanceof Buffer))return r instanceof Error?(this.server.ws.send("template:sync:response",{error:r.message}),void this.logger.error(E.red(r.message))):(this.server.ws.send("template:sync:response",{error:"Failed to build new assets"}),void this.logger.error(E.red("Failed to build new assets")));{const a=await(t?.updateAssets(r));if("OK"===a?.status){const t=e?.getBackupMeta(),{lastBackupName:a,lastBackupHash:r,lastBackupDate:s}=t||{lastBackupName:"",lastBackupHash:"",lastBackupDate:(new Date).toISOString()};this.server.ws.send("template:sync:response",{syncedAt:new Date(s),currentHash:r,backupFilename:a}),this.logger.info(E.green("Template synced"))}else this.server.ws.send("template:sync:response",{error:"Failed to update assets"}),this.logger.error(E.red("Failed to update assets"))}}}}const q=new y.Cache,z=/\.[a-z0-9]+$/i;function _(e){const{devServer:t,ttl:a=6e5}=e,r=I();return t.cache=q,(e,t,s)=>{const i=e.originalUrl||e.url||"";if(z.test(i.split("?")[0]||"")){const s=q.get(i);if(s){r.info(`${E.blue("Proxies request:")} ${E.green(e.method)} ${i} -> ${E.blue("Cache")} ${i}`);for(const[e,a]of Object.entries(s.headers))t.setHeader(e,a);return t.write(s.content),void t.end()}const n=t.end,o=t.write;let p=Buffer.from("","utf8");t.write=function(e,a,r){return t.hasHeader(C)?(p=Buffer.concat([p,e]),!0):o.call(this,e,a,r)},t.end=function(e,s,o){if(!t.hasHeader(C))return n.call(this,e,s,o);"string"==typeof e&&(p=Buffer.concat([p,Buffer.from(e,s)])),Buffer.isBuffer(e)&&(p=Buffer.concat([p,e])),t.hasHeader(C)&&(r.info(`${E.blue("[Cached]")} ${i}`),q.put(i,{headers:t.getHeaders(),content:p},a)),n.call(this,p,s,o)}}s()}}const Z="pageName",W="date",Y=v.dirname("undefined"!=typeof __filename&&__filename||t.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:f&&"SCRIPT"===f.tagName.toUpperCase()&&f.src||new URL("plugin-Dx9CDaej.js",document.baseURI).href)),J=v.resolve(Y,"..",".."),K=v.resolve(J,"..",".."),G=v.resolve(K,".pp-dev-meta"),X=v.resolve(G,"sync-service.meta.json");class Q{backupFolder;backupNameTemplate;dateFormat;pageName;currentMeta=null;distZipFolder;distZipFilename;logger;constructor(e,t){this.pageName=e;const{backupFolder:a=v.resolve(x.cwd(),"backups"),distZipFolder:r=v.resolve(x.cwd(),"dist-zip"),distZipFilename:s=`${this.pageName}.zip`,backupNameTemplate:i=`{${Z}}-{${W}}.zip`,dateFormat:n=e=>e.toISOString().replace(/:/g,"-").replace(/\..*$/,"")}=t||{};this.backupFolder=a,this.backupNameTemplate=i,this.dateFormat=n,this.distZipFolder=r,this.distZipFilename=s,this.syncMeta(),this.logger=I()}async checkMeta(){try{await c.promises.stat(G)}catch{await c.promises.mkdir(G)}try{await c.promises.stat(this.backupFolder)}catch{await c.promises.mkdir(this.backupFolder)}}async readMetaFile(){return await this.checkMeta(),await c.promises.readFile(X,{encoding:"utf-8"}).catch((()=>"{}"))}async writeMetaFile(e){return await this.checkMeta(),await c.promises.writeFile(X,JSON.stringify(e,null,2),{encoding:"utf-8"})}async syncMeta(){this.currentMeta?await this.writeMetaFile(this.currentMeta):this.currentMeta=JSON.parse(await this.readMetaFile())}async getLatestSavedBackup(){const{lastBackupName:e}=this.currentMeta;if(!e)return null;const t=v.resolve(this.backupFolder,e);try{return await c.promises.stat(t),t}catch{return null}}backupName(e,t=new Date){return this.backupNameTemplate.replace(`{${Z}}`,e).replace(`{${W}}`,this.dateFormat(t))}getBackupMeta(){return this.currentMeta}async saveBackup(e){if("PK"!==e.toString("utf-8").slice(0,4))throw new Error("Backup file is not a ZIP file");const t=b.createHash("md5").update(e).digest("hex");if(await this.getLatestSavedBackup()){const{lastBackupHash:e}=this.currentMeta;if(e===t)return}const a=new Date,r=this.backupName(this.pageName,a);return this.currentMeta.lastBackupName=r,this.currentMeta.lastBackupHash=t,this.currentMeta.lastBackupDate=a.toISOString(),await this.syncMeta(),await c.promises.writeFile(v.resolve(this.backupFolder,r),e).finally((()=>{this.logger.info(`Backup saved to ${r}`)}))}async buildNewAssets(){const e=new Promise(((e,t)=>{let a="";this.logger.info(E.cyan("[DistService] Build started"));const r=k.spawn("node",[v.resolve(J,"./bin/pp-dev.js"),"build"],{cwd:x.cwd(),env:Object.assign({},x.env,{NODE_ENV:"production"}),stdio:"inherit"});r.on("message",(e=>{a+=e})),r.on("close",(r=>{0===r?e(a):t(new Error(`build command exited with code ${r}`))})),r.on("error",(e=>{t(e)}))}));try{await e.finally((()=>{this.logger.info(E.cyan("[DistService] Build finished"))}));const t=v.resolve(x.cwd(),this.distZipFolder,this.distZipFilename);if(!await c.promises.stat(t))throw new Error(`File ${t} not found`);return await c.promises.readFile(t)}catch(e){throw T.log(e),e}}async saveBackupAndBuild(e){return await this.saveBackup(e),await this.buildNewAssets()}}function ee(e,t){return async(a,r,s)=>{if(await e(a.url??"",a,r)){I().info(`Rewrite response for ${a.url}`);const e=r.end,s=[];r.write=function(e){return s.push(e),!0},r.end=function(i,n,o){"string"==typeof i&&s.push(Buffer.from(i));const p=r.getHeader("content-encoding"),c=function(e,t){switch(t){case"gzip":return g.unzipSync(e);case"br":return g.brotliDecompressSync(e);case"deflate":return g.deflateSync(e);default:return e}}(Buffer.from(Buffer.concat(s)),p),l="function"!=typeof n&&n?n:"utf-8",d="function"==typeof n?n:o;return e.call(this,function(e,t){switch(t){case"gzip":return g.gzipSync(e);case"br":return g.brotliCompressSync(e);case"deflate":return g.inflateSync(e);default:return e}}(t(c,a,r),p),l,d),this}}s()}}function te(e,a){const r=(e=(e=e.startsWith("/")?e:`/${e}`).endsWith("/")?e:`${e}/`).endsWith("/")?e.slice(0,-1):e,s=I();return(i,n,o)=>{const p=new t.URL(i.url??"","http://localhost"),{pathname:c,search:l}=p,d=["/",r];if(a&&(d.push(`/pt/${a}`),d.push(`/pl/${a}`)),d.includes(c)){const t=`${e}${l}`;return s.info(E.yellow(`Redirecting to: ${t}`)),A(n,t,302)}o()}}function ae(e,t,a){const{templateLess:r=!1,miHudLess:s=!1,portalPageId:i}=a,n=I();return(a,o,p)=>{const c=!(r&&s),l=e.test($(a.url??""));if(c&&l){const e=a.headers??{};n.info(E.green("Start loading page data"));(r||void 0===i?t.getPageTemplate(e):t.getPageVariables(i,e)).then((()=>{p()})).catch((e=>{if(e.response){n.info(E.red("Page data loading failed. Not authorized"));const e=`/home?proxyRedirect=${encodeURIComponent("/")}`;return n.info(E.yellow(`Redirecting to: ${e}`)),A(o,e,302)}n.info(E.red(`Page data loading failed. Error: ${e.message}`)),p(e)}))}else p()}}const re=m();function se(e,t,a){return t(e)?{isValid:!0}:{isValid:!1,error:a}}re.use(m.json()),re.use(m.urlencoded({extended:!0})),exports.MiAPI=U,exports.colors=E,exports.createLogger=I,exports.cutUrlParams=$,exports.initLoadPPData=ae,exports.initPPRedirect=te,exports.initProxy=F,exports.initProxyCache=_,exports.initRewriteResponse=ee,exports.internalServer=re,exports.normalizeVitePPDevConfig=function(e){const t=function(e){const t=[se(e,(e=>"object"==typeof e&&null!==e),"VitePPDevOptions must be an object"),se(e.templateName,(e=>"string"==typeof e&&e.length>0),"VitePPDevOptions.templateName must be a non-empty string"),se(e.backendBaseURL,(e=>void 0===e||"string"==typeof e&&e.length>0),"VitePPDevOptions.backendBaseURL must be a non-empty string if provided"),se(e.portalPageId,(e=>void 0===e||"number"==typeof e&&e>0),"VitePPDevOptions.portalPageId must be a positive number if provided"),se(e.appId,(e=>void 0===e||"number"==typeof e&&e>0),"VitePPDevOptions.appId must be a positive number if provided"),se(e.proxyCacheTTL,(e=>void 0===e||"number"==typeof e&&e>0),"VitePPDevOptions.proxyCacheTTL must be a positive number if provided")];return t.find((e=>!e.isValid))||{isValid:!0}}(e);if(!t.isValid)throw new Error(t.error);const a=e.appId??e.portalPageId,{enableProxyCache:r=!0,proxyCacheTTL:s=6e5,disableSSLValidation:i=!1,imageOptimizer:n=!0,miHudLess:o=!1,templateLess:p=!1,outDir:c="dist",distZip:l=!0,syncBackupsDir:d="backups",v7Features:h=!1,personalAccessToken:u=process.env.MI_ACCESS_TOKEN}=e||{};let g=l;g=!0===g?{outFileName:`${e.templateName}.zip`,outDir:"dist-zip"}:"object"==typeof l&&{outFileName:"string"==typeof l.outFileName?l.outFileName.replace("[templateName]",e.templateName):`${e.templateName}.zip`,outDir:l.outDir??"dist-zip"};let m=n;return"boolean"==typeof n?!0===m&&(m={}):"object"!=typeof n&&(m=!1),{enableProxyCache:r,proxyCacheTTL:s,disableSSLValidation:i,imageOptimizer:m,templateLess:p,miHudLess:o,outDir:c,distZip:g,syncBackupsDir:d,v7Features:h,personalAccessToken:u,portalPageId:a,...e}},exports.redirect=A,exports.urlReplacer=P,exports.vitePPDev=function(e){const{templateName:t,templateLess:a,backendBaseURL:r,miHudLess:s,portalPageId:i,enableProxyCache:n,proxyCacheTTL:o,disableSSLValidation:p,distZip:c,syncBackupsDir:l,v7Features:d,personalAccessToken:h}=e||{};let u=!0;return process.cwd(),{name:"vite-pp-dev",apply:"serve",config:e=>{const s=function(e){return[se(e.base,(e=>"string"==typeof e&&e.length>0&&"/"!==e),'Server base path cannot be empty or "/"'),se(e.port,(e=>void 0===e||"number"==typeof e&&e>0&&e<65536),"Server port must be a valid port number (1-65535)")].find((e=>!e.isValid))||{isValid:!0}}({base:e.base||"",...e.server});if(!s.isValid)throw new Error(s.error);return e.clientInjectionPlugin={backendBaseURL:r,portalPageId:i,templateLess:a,v7Features:d},d&&(e.base=`/pl/${t}`),e.root&&e.root,e},transformIndexHtml:async(e,t)=>{const a={html:e,tags:[]};return u&&(u=!1,a.tags.push({tag:"script",injectTo:"body",children:`${Math.random()}`})),a},configureServer:u=>{let g=u.config.base;g.endsWith("/")||(g+="/");const m=g.substring(0,g.lastIndexOf("/"));if(u.middlewares.use(te(g,t)),r){const w=new URL(r).host,y={headers:{host:w,referer:r,origin:r.replace(/^(https?:\/\/)([^/]+)(\/.*)?$/i,"$1$2")},portalPageId:i,appId:i,templateLess:a,disableSSLValidation:p,v7Features:d,personalAccessToken:h??process.env.MI_ACCESS_TOKEN},v=[se((f=y).headers,(e=>"object"==typeof e&&"string"==typeof e.host&&"string"==typeof e.referer&&"string"==typeof e.origin),"MiAPI headers must be properly configured"),se(f.portalPageId,(e=>void 0===e||"number"==typeof e&&e>0),"MiAPI portalPageId must be a positive number if provided"),se(f.appId,(e=>void 0===e||"number"==typeof e&&e>0),"MiAPI appId must be a positive number if provided"),se(f.personalAccessToken,(e=>void 0===e||"string"==typeof e),"MiAPI personalAccessToken must be a string if provided")].find((e=>!e.isValid))||{isValid:!0};if(!v.isValid)throw new Error(v.error);const b=new U(r,y);if(n){let e=+o;(!e||Number.isNaN(e)||e<0)&&(e=6e5);const t={devServer:u,ttl:e},a=function(e){return[se(e.ttl,(e=>"number"==typeof e&&e>0),"Proxy cache TTL must be a positive number")].find((e=>!e.isValid))||{isValid:!0}}(t);if(!a.isValid)throw new Error(a.error);u.middlewares.use(_(t))}u.middlewares.use(F({devServer:u,baseURL:r,proxyIgnore:["/@vite","/@metricinsights","/@",m],disableSSLValidation:p,miAPI:b}));const x=new RegExp(`^((${g})|/)$`);u.middlewares.use(ae(x,b,e)),re.post("/@api/login",(async(e,t,a)=>{const{token:r,tokenType:s}=e.body;if(!r)return void t.status(400).json({error:"Token is required"}).end();const i=e=>(u.config.logger.error(e),a(e),null);if("personal"===s){if(!await b.get("/data/page/index/auth/info",{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${r}`},!0).then((async e=>{if("number"==typeof e.data?.user?.user_id)return b.personalAccessToken=r,e;t.status(400).json({error:"Token expired or invalid"}).end()})).catch(i))return;A(t,"/",302)}else if("regular"===s){if(!await b.get("/api/user",{"Content-Type":"application/json",Accept:"application/json",Token:r},!0).then((e=>{if(e.data?.users?.length)return b.personalAccessToken=void 0,t.setHeader("set-cookie",e.headers["set-cookie"]??""),e;t.status(400).json({error:"Token expired or invalid"}).end()})).catch(i))return;A(t,"/",302)}})),u.middlewares.use(re);const k=!1!==c?new Q(t,Object.assign({backupDir:l},"object"==typeof c?{distZipFolder:c.outDir,distZipFilename:c.outFileName}:void 0)):void 0;return new V(u,{distService:k,miAPI:b}),()=>{u.middlewares.use(ee((e=>(console.log("initRewriteResponse.url",e),e.split("?")[0].endsWith("index.html"))),((e,t)=>Buffer.from(P(w,t.headers.host??"",b.buildPage(e,s))))))}}var f}}};
//# sourceMappingURL=plugin-Dx9CDaej.js.map