Deployment docs need some additional guidance #492
scottdixon
started this conversation in
Ideas + Feature Requests
Replies: 1 comment 10 replies
-
Thank you @scottdixon for bringing this up here. For further context this is the (very unrefined) // Virtual entry point for the app
import * as remixBuild from '@remix-run/dev/server-build';
import {createRequestHandler} from '@remix-run/server-runtime';
import {createStorefrontClient, storefrontRedirect} from '@shopify/hydrogen';
import {HydrogenSession} from '~/lib/session.server';
/**
* Export a fetch handler in module format.
*/
export default async function (request: Request): Promise<Response> {
const env = process.env;
try {
/**
* Open a cache instance in the worker and a custom session instance.
*/
if (!env?.SESSION_SECRET) {
throw new Error('SESSION_SECRET environment variable is not set');
}
const [session] = await Promise.all([
HydrogenSession.init(request, [env.SESSION_SECRET]),
]);
/**
* Create Hydrogen's Storefront client.
*/
const {storefront} = createStorefrontClient({
buyerIp: request.headers.get('x-forwarded-for') ?? undefined,
i18n: {
language: 'DE',
country: 'DE',
},
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
storeDomain: `https://${env.PUBLIC_STORE_DOMAIN}`,
storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION || '2023-01',
// storefrontId: 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});
}
} And this is the modified import {
createCookieSessionStorageFactory,
createCookieFactory,
type SessionStorage,
type Session,
} from '@remix-run/server-runtime';
import type {SignFunction, UnsignFunction} from '@remix-run/server-runtime';
const encoder = new TextEncoder();
export const sign: SignFunction = 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: UnsignFunction = 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: string,
usages: CryptoKey['usages'],
): Promise<CryptoKey> {
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{name: 'HMAC', hash: 'SHA-256'},
false,
usages,
);
return key;
}
function byteStringToUint8Array(byteString: string): Uint8Array {
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 {
constructor(
private sessionStorage: SessionStorage,
private session: Session,
) {}
static async init(request: Request, secrets: string[]) {
const createCookie = createCookieFactory({sign, unsign});
const createCookieSessionStorage =
createCookieSessionStorageFactory(createCookie);
const storage = createCookieSessionStorage({
cookie: {
name: 'session',
httpOnly: true,
path: '/',
sameSite: 'lax',
secrets,
},
});
const session = await storage.getSession(request.headers.get('Cookie'));
return new this(storage, session);
}
get(key: string) {
return this.session.get(key);
}
destroy() {
return this.sessionStorage.destroySession(this.session);
}
flash(key: string, value: any) {
this.session.flash(key, value);
}
unset(key: string) {
this.session.unset(key);
}
set(key: string, value: any) {
this.session.set(key, value);
}
commit() {
return this.sessionStorage.commitSession(this.session);
}
} Please note that I'm pretty new to Remix, so this is what I came up with last night while trying to digg through the Remix docs and other implementations. I'm very open for suggestions. |
Beta Was this translation helpful? Give feedback.
10 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Flagged by @MurmeltierS in the Partner Slack, Vercel deployment instructions are a little misleading. It would be great to create some more detailed guides for the most common platforms.
cc) @benjaminsehl
Beta Was this translation helpful? Give feedback.
All reactions