Skip to content

Commit

Permalink
testing vercel deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
iamkevingreen committed Jul 23, 2023
1 parent 1f11818 commit ba16940
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 155 deletions.
69 changes: 61 additions & 8 deletions app/lib/session.server.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,72 @@
import {createCookieSessionStorage} from '@shopify/remix-oxygen';
import {
createCookieSessionStorageFactory,
createCookieFactory,
} from '@remix-run/server-runtime';


const encoder = new TextEncoder();

export const sign = async (value, secret) => {
const data = encoder.encode(value);
const key = await createKey(secret, ['sign']);
const signature = await crypto.subtle.sign('HMAC', key, data);
const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
/=+$/,
'',
);

return value + '.' + hash;
};

export const unsign = async (cookie, secret) => {
const value = cookie.slice(0, cookie.lastIndexOf('.'));
const hash = cookie.slice(cookie.lastIndexOf('.') + 1);

const data = encoder.encode(value);
const key = await createKey(secret, ['verify']);
const signature = byteStringToUint8Array(atob(hash));
const valid = await crypto.subtle.verify('HMAC', key, signature, data);

return valid ? value : false;
};

async function createKey(secret, usages) {
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{name: 'HMAC', hash: 'SHA-256'},
false,
usages,
);

return key;
}

function byteStringToUint8Array(byteString) {
const array = new Uint8Array(byteString.length);

for (let i = 0; i < byteString.length; i++) {
array[i] = byteString.charCodeAt(i);
}

return array;
}

/**
* This is a custom session implementation for your Hydrogen shop.
* Feel free to customize it to your needs, add helper methods, or
* swap out the cookie-based implementation with something else!
*/
export class HydrogenSession {
sessionStorage;
session;
constructor(sessionStorage, session) {
this.sessionStorage = sessionStorage;
this.session = session;
}
constructor(
private sessionStorage,
private session,
) {}

static async init(request, secrets) {
const createCookie = createCookieFactory({sign, unsign});
const createCookieSessionStorage =
createCookieSessionStorageFactory(createCookie);
const storage = createCookieSessionStorage({
cookie: {
name: 'session',
Expand Down Expand Up @@ -52,4 +105,4 @@ export class HydrogenSession {
commit() {
return this.sessionStorage.commitSession(this.session);
}
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"preview": "npm run build && shopify hydrogen preview",
"lint": "eslint --no-error-on-unmatched-pattern --ext .js,.ts,.jsx,.tsx .",
"g": "shopify hydrogen generate",
"n": "ngrok http --subdomain hedley 3000"
"n": "ngrok http --subdomain superhi 3000"
},
"prettier": "@shopify/prettier-config",
"dependencies": {
Expand Down Expand Up @@ -60,6 +60,6 @@
"tailwindcss": "^3.1.8"
},
"engines": {
"node": ">=16.13"
"node": ">=18.01"
}
}
32 changes: 32 additions & 0 deletions remix.env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
declare global {
/**
* A global `process` object is only available during build to access NODE_ENV.
*/
const process: {env: {NODE_ENV: 'production' | 'development'} & Env};

/**
* Declare expected Env parameter in fetch handler.
*/
interface Env {
SESSION_SECRET: string;
PUBLIC_STOREFRONT_API_TOKEN: string;
PRIVATE_STOREFRONT_API_TOKEN: string;
PUBLIC_STORE_DOMAIN: string;
PUBLIC_STOREFRONT_ID: string;
}
}

/**
* Declare local additions to `AppLoadContext` to include the session utilities we injected in `server.ts`.
*/
declare module '@shopify/remix-oxygen' {
export interface AppLoadContext {
waitUntil: ExecutionContext['waitUntil'];
session;
storefront;
env: Env;
}
}

// Needed to make this file a module.
export {};
145 changes: 0 additions & 145 deletions server.js

This file was deleted.

78 changes: 78 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Virtual entry point for the app
import * as remixBuild from '@remix-run/dev/server-build';
import {createRequestHandler} from '@remix-run/server-runtime';
import {createStorefrontClient} from '@shopify/hydrogen';

import {HydrogenSession} from '~/lib/session.server';
import {getLocaleFromRequest} from '~/lib/utils';

/**
* Export a fetch handler in module format.
*/
export default async function (request: Request): Promise {
try {
/**
* This has to be done so messy because process.env can't be destructured
* and only variables explicitly named are present inside a Vercel Edge Function.
* See https://github.com/vercel/next.js/pull/31237/files
*/
const env: Env = {
SESSION_SECRET: '',
PUBLIC_STOREFRONT_API_TOKEN: '',
PRIVATE_STOREFRONT_API_TOKEN: '',
PUBLIC_STORE_DOMAIN: '',
};
env.SESSION_SECRET = process.env.SESSION_SECRET;
env.PUBLIC_STOREFRONT_API_TOKEN = process.env.PUBLIC_STOREFRONT_API_TOKEN;
env.PRIVATE_STOREFRONT_API_TOKEN = process.env.PRIVATE_STOREFRONT_API_TOKEN;
env.PUBLIC_STORE_DOMAIN = process.env.PUBLIC_STORE_DOMAIN;
/**
* Open a cache instance in the worker and a custom session instance.
*/
if (!env?.SESSION_SECRET) {
throw new Error('SESSION_SECRET process.environment variable is not set');
}

const [session] = await Promise.all([
HydrogenSession.init(request, [process.env.SESSION_SECRET]),
]);

/**
* Create Hydrogen's Storefront client.
*/
const {storefront} = createStorefrontClient({
buyerIp: request.headers.get('x-forwarded-for') ?? undefined,
i18n: getLocaleFromRequest(request),
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
storeDomain: env.PUBLIC_STORE_DOMAIN,
storefrontId: env.PUBLIC_STOREFRONT_ID,
// storefrontId: process.env.PUBLIC_STOREFRONT_ID,
// requestGroupId: request.headers.get('request-id'),
});

const handleRequest = createRequestHandler(remixBuild as any, 'production');

const response = await handleRequest(request, {
session,
storefront,
env,
waitUntil: () => Promise.resolve(),
});

if (response.status === 404) {
/**
* Check for redirects only when there's a 404 from the app.
* If the redirect doesn't exist, then `storefrontRedirect`
* will pass through the 404 response.
*/
// return storefrontRedirect({request, response, storefront});
}

return response;
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
return new Response('An unexpected error occurred', {status: 500});
}
}

0 comments on commit ba16940

Please sign in to comment.