UNPKG

@swishapp/node

Version:

JS library to integrate Swish on a Node.js server.

2 lines (1 loc) 4.87 kB
var d=Object.defineProperty;var i=(a,e)=>d(a,"name",{value:e,configurable:!0});import{createApiClient as p,baseUrl as w}from"@swishapp/api-client";const f=i(a=>`https://cdn.jsdelivr.net/npm/@swishapp/browser@${a}/dist`,"browserAssetsHost"),g=process.env.SWISH_UI_HOST??"https://storage.googleapis.com/swi-prod-swish-ui/__default/assets",y=["shop","logged_in_customer_id","timestamp","signature","path_prefix"],k=["accept","accept-encoding","accept-language","cache-control","content-type","pragma","user-agent","x-forwarded-for","x-forwarded-host","x-forwarded-proto","x-request-id","x-shopify-client-ip"],P=i(a=>new T(a),"createAppProxy");class T{static{i(this,"AppProxy")}constructor({authToken:e,storage:t,basePath:r,apiHost:s,authenticate:n,onError:o}){this.storage=t,this.apiProxyPath=r+"/api",this.assetProxyPath=r+"/assets",this.onError=o,this.authenticate=n,this.authToken=e,this.apiHost=s??w}async forward(e){if(new URL(e.url).pathname.startsWith(this.assetProxyPath))return await this.forwardAssetRequest(e);try{const r=await this.authenticate(e)??null;return await this.forwardApiRequest(e,r)}catch(r){this.onError?.(r)}return new Response("Unauthorized",{status:401})}async forwardAssetRequest(e){const r=new URL(e.url).pathname.replace(this.assetProxyPath,"");try{return await this.getFileContent(r)}catch(s){return this.onError?.(s),new Response("File not found",{status:404})}}async getFileContent(e){let t="";const r={"Cache-Control":"max-age=60, stale-while-revalidate=3600","Content-Type":this.getContentType(e)};if(e.startsWith("/browser/")){const s=e.replace("/browser","").match(/-v(\d+\.\d+\.\d+)/);let n="0.29.0";s&&(e=e.replace(s[0],""),n=s[1]),t=await this.loadAsset(f(n),e.replace("/browser",""))}return e.startsWith("/ui/")&&(e.endsWith("/manifest.json")||(r["Cache-Control"]="public, max-age=31536000, immutable"),t=await this.loadAsset(g,e.replace("/ui",""))),new Response(t,{headers:r})}async loadAsset(e,t){return console.log("loadAsset",{assetsHost:e,assetPath:t}),await(await fetch(`${e}${t}`)).text()}async forwardApiRequest(e,t){try{const r=this.extractHeaderSid(e),s=await this.getToken(e,t),n=this.parseToken(s),o=this.createForwardingUrl(e),u=this.createForwardingHeaders(e,{authToken:s}),l=await e.text(),c=await fetch(o,{body:l?.length?l:void 0,method:e.method,headers:u}),h=new Headers(c.headers);return!r&&!t?h.set("Set-Profile",n.sub):r&&t&&h.set("Set-Profile",""),new Response(c.body,{status:c.status,headers:h})}catch(r){return this.onError?.(r),r.statusCode?new Response(JSON.stringify({error:r}),{status:r.statusCode,headers:{"Content-Type":"application/json"}}):new Response("Bad Request",{status:400})}}createForwardingHeaders(e,t){const r=new Headers(Array.from(e.headers.entries()).filter(([s])=>k.includes(s)));return t?.authToken&&r.set("authorization",`Bearer ${t.authToken}`),r}createForwardingUrl(e){const t=new URL(e.url),r=new URL(`${this.apiHost}${t.pathname.replace(this.apiProxyPath,"")}`);return r.search=new URLSearchParams(Array.from(t.searchParams.entries()).filter(([s])=>!y.includes(s))).toString(),r}getContentType(e){return e.endsWith(".js")?"application/javascript":e.endsWith(".json")?"application/json":"text/plain"}async getToken(e,t){const r=this.extractHeaderSid(e),s=await this.getCachedToken(r,t);if(!s){const o=await this.createToken(e,{sessionId:r,customerId:t});return await this.storage.storeToken(o.profile,o.token),o.token}const n=this.parseToken(s);if(n.sub.startsWith("gid://swish/Session")&&t){const o=await this.createToken(e,{sessionId:n.sub,customerId:t});return await this.storage.deleteToken(n.sub),await this.storage.storeToken(o.profile,o.token),o.token}return s}async getCachedToken(e,t){let r=null;if(e&&(r=await this.storage.loadToken(this.createGid("session",e))),t&&(r=await this.storage.loadToken(this.createGid("customer",t))),!r)return null;const s=this.parseToken(r);return this.isTokenExpired(s)?(await this.storage.deleteToken(s.sub),null):r}async createToken(e,t){const r=p({config:{baseUrl:this.apiHost},authToken:typeof this.authToken=="function"?await this.authToken(e):this.authToken}),{data:s,error:n}=await r.profiles.createToken({session:t?.sessionId?this.createGid("session",t.sessionId):void 0,customer:t?.customerId?this.createGid("customer",t.customerId):void 0});if(n)throw n;if(!s)throw new Error("No data returned from createToken");return s}extractHeaderSid(e){const t=e.headers.get("Profile");return t?.startsWith("gid://swish/Session")&&t||null}parseToken(e){try{return JSON.parse(atob(e.split(".")[1]))}catch(t){return this.onError?.(t),null}}createGid(e,t){return e==="session"?`gid://swish/Session/${t.split("/").pop()}`:`gid://shopify/Customer/${t.split("/").pop()}`}isTokenExpired(e){try{if(!e?.exp)return!0;const t=Math.floor(Date.now()/1e3);return e.exp<=t+3600}catch(t){return this.onError?.(t),!0}}}export{T as AppProxy,P as createAppProxy};