1
|
(()=>{"use strict";const e=async(e,t)=>{await t();const{request:r,response:o,route:s}=e,n=s.cors;if(void 0===n)return;const{origin:a,methods:i,exposedHeaders:c,allowedHeaders:u,credentials:l,maxAge:d}=n,h=r.headers.get("origin");if(null===h||!1===a)return;const f=new Headers(o.headers);if(!0===a?f.set("Access-Control-Allow-Origin",h):Array.isArray(a)?a.includes(h)&&f.set("Access-Control-Allow-Origin",h):"*"===a&&f.set("Access-Control-Allow-Origin","*"),Array.isArray(i))f.set("Access-Control-Allow-Methods",i.join(","));else if("*"===i)f.set("Access-Control-Allow-Methods","*");else{const e=r.headers.get("Access-Control-Request-Method");null!==e&&f.set("Access-Control-Allow-Methods",e)}if(Array.isArray(c)?f.set("Access-Control-Expose-Headers",c.join(",")):"*"===c&&f.set("Access-Control-Expose-Headers","*"),Array.isArray(u))f.set("Access-Control-Allow-Headers",u.join(","));else if("*"===u)f.set("Access-Control-Allow-Headers","*");else{const e=r.headers.get("Access-Control-Request-Headers");null!==e&&f.set("Access-Control-Allow-Headers",e)}!0===l&&f.set("Access-Control-Allow-Credentials","true"),void 0!==d&&Number.isInteger(d)&&f.set("Access-Control-Max-Age",d.toString()),e.response=new Response(o.body,{status:o.status,statusText:o.statusText,headers:f})},t=new Set(["country","continent","asn","ip","hostname","user-agent"]),r=new Set(["equal","not equal","greater","less","in","not in","contain","not contain","match","not match"]),o=({field:e,operator:o,value:s})=>{if(void 0===e||void 0===o||void 0===s)throw new Error("Invalid 'firewall' field in the option object");if(!1===t.has(e))throw new Error("Invalid 'firewall' field in the option object");if(!1===r.has(o))throw new Error("Invalid 'firewall' field in the option object")},s=(e,t)=>{const r=e.cf;switch(t){case"asn":return r?.asn;case"continent":return r?.continent||"";case"country":return r?.country;case"hostname":return e.headers.get("host")||"";case"ip":return e.headers.get("cf-connecting-ip")||"";case"user-agent":return e.headers.get("user-agent")||"";default:return}},n=(e,t)=>{if(!(t instanceof RegExp))throw new Error("You must use 'new RegExp('...')' for 'value' in firewall configuration to use 'match' or 'not match' operator");return t.test(e.toString())},a=(e,t)=>{if("string"!=typeof e||"string"!=typeof t)throw new Error("You must use string for 'value' in firewall configuration to use 'contain' or 'not contain' operator");return e.includes(t)},i=(e,t)=>{if(!Array.isArray(t))throw new Error("You must use an Array for 'value' in firewall configuration to use 'in' or 'not in' operator");return t.some((t=>t===e))},c={match:n,contain:a,equal:(e,t)=>e===t,in:i,greater:(e,t)=>{if("number"!=typeof e||"number"!=typeof t)throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");return e>t},less:(e,t)=>{if("number"!=typeof e||"number"!=typeof t)throw new Error("You must use number for 'value' in firewall configuration to use 'greater' or 'less' operator");return e<t},"not match":(e,t)=>!n(e,t),"not contain":(e,t)=>!a(e,t),"not equal":(e,t)=>e!==t,"not in":(e,t)=>!i(e,t)},u=async(e,t)=>{const{request:r,route:n}=e;if(void 0!==n.firewall){n.firewall.forEach(o);for(const{field:e,operator:t,value:o}of n.firewall){const n=s(r,e);if(void 0!==n&&c[t](n,o))throw new Error("You don't have permission to access this service.")}await t()}else await t()},l=async(e,t)=>{const{request:r,route:o}=e,s=new Headers(r.headers);if((e=>{e.set("X-Forwarded-Proto","https");const t=e.get("Host");null!==t&&e.set("X-Forwarded-Host",t);const r=e.get("cf-connecting-ip"),o=e.get("X-Forwarded-For");null!==r&&null===o&&e.set("X-Forwarded-For",r)})(s),void 0===o.headers)return e.request=new Request(r.url,{body:r.body,method:r.method,headers:s}),void await t();if(void 0!==o.headers.request)for(const[e,t]of Object.entries(o.headers.request))s.set(e,t);e.request=new Request(r.url,{body:r.body,method:r.method,headers:s}),await t();const{response:n}=e,a=new Headers(n.headers);if(void 0!==o.headers.response)for(const[e,t]of Object.entries(o.headers.response))a.set(e,t);e.response=new Response(n.body,{status:n.status,statusText:n.statusText,headers:a})},d=e=>{if(void 0===e.domain)throw new Error("Invalid 'upstream' field in the option object")},h=e=>{const t=e.map((e=>void 0===e.weight?1:e.weight)),r=t.reduce(((e,r,o)=>{const s=e+r;return t[o]=s,s}));if(0===r)throw new Error("Total weights should be greater than 0.");const o=Math.random()*r;for(const r of t.keys())if(t[r]>=o)return e[r];return e[Math.floor(Math.random()*e.length)]},f={random:h,"ip-hash":(e,t)=>e[(t.headers.get("cf-connecting-ip")||"0.0.0.0").split(".").map(((e,t,r)=>parseInt(e,10)*256**(r.length-t-1))).reduce(((e,t)=>e+t))%e.length]},p=async(e,t)=>{const{request:r,route:o}=e,{upstream:s,loadBalancing:n}=o;if(void 0===s)throw new Error("The required 'upstream' field in the option object is missing");Array.isArray(s)?s.forEach(d):d(s);const a=Array.isArray(s)?s:[s];if(void 0===n)return e.upstream=h(a),void await t();const i=n.policy||"random",c=f[i];e.upstream=c(a,r),await t()},w=async(e,t)=>{const{request:r,upstream:o}=e;if(null===o)return void await t();const s=((e,t)=>{const r=new URL(e),{domain:o,port:s,protocol:n}=t;return r.hostname=o,void 0!==n&&(r.protocol=`${n}:`),r.port=void 0===s?"":s.toString(),r.href})(r.url,o),n=((e,t)=>{const r={body:t.body,method:t.method,headers:t.headers};return new Request(e,r)})(s,r);e.response=await fetch(n),await t()};class m{namespace;constructor(e){this.namespace=e}get=async e=>await this.namespace.get(e,{type:"json",cacheTtl:60});put=async(e,t)=>{await this.namespace.put(e,JSON.stringify(t))};delete=async e=>{await this.namespace.delete(e)}}const g=(e,t)=>new Response(e,{status:t}),y=e=>new URL(e.url).host,A={provider:"static",routeList:[]};addEventListener("fetch",(t=>{t.respondWith((async t=>{const r=await(async(t=A)=>{const r=((...e)=>{const t=[...e];return{push:(...e)=>{t.push(...e)},execute:async e=>{const r=async(o,s)=>{if(s===o)throw new Error("next() called multiple times");if(s>=t.length)return;const n=t[s];await n(e,(async()=>r(s,s+1)))};await r(-1,0)}}})(u,p,l,e,w),o=[];if("static"===t.provider)for(const e of t.routeList)o.push(e);if("kv"===t.provider){const e=new m(t.namespace),r=await e.get("route-list")||[];for(const e of r)o.push(e)}return{handle:async e=>{const t=((e,t)=>{const r=new URL(e.url);for(const o of t)if(void 0===o.methods||o.methods.includes(e.method)){const e=RegExp(`^${o.path.replace(/(\/?)\*/g,"($1.*)?").replace(/\/$/,"").replace(/:(\w+)(\?)?(\.)?/g,"$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/,"\\.").replace(/\)\.\?\(([^[]+)\[\^/g,"?)\\.?($1(?<=\\.)[^\\.")}/*$`);if(r.pathname.match(e))return o}})(e,o);if(void 0===t)return g("Failed to find a route that matches the path and method of the current request",500);const s={request:e,route:t,hostname:y(e),response:new Response("Unhandled response"),upstream:null};try{await r.execute(s)}catch(e){e instanceof Error&&(s.response=g(e.message,500))}return s.response},unshift:e=>{o.unshift(e)},push:e=>{o.push(e)}}})();return r.push({path:"/*",upstream:{domain:"api.openai.com",protocol:"https"},cors:{origin:"*"}}),r.handle(t)})(t.request))}))})();
|