Skip to content

Instantly share code, notes, and snippets.

@VIEWVIEWVIEW
Created November 9, 2024 02:02
Show Gist options
  • Select an option

  • Save VIEWVIEWVIEW/bbafbf2cd6fb05eb34bcc4e2ae9cd711 to your computer and use it in GitHub Desktop.

Select an option

Save VIEWVIEWVIEW/bbafbf2cd6fb05eb34bcc4e2ae9cd711 to your computer and use it in GitHub Desktop.
A remix session plugin for ioredis.
import { createSessionStorage, SessionIdStorageStrategy, SessionStorage } from "@remix-run/node";
import Redis, { RedisOptions } from "ioredis";
interface FileSessionStorageOptions {
/**
* The Cookie used to store the session id on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
/**
* The Redis connection options.
*/
redisOptions: RedisOptions;
}
function createRedisSessionStorage<Data = SessionData, FlashData = Data>({ cookie, redisOptions }: FileSessionStorageOptions): SessionStorage<Data, FlashData> {
// Configure your database client...
const redis = new Redis(redisOptions);
return createSessionStorage({
cookie,
async createData(data, expires) {
console.log("createData", data, expires);
// `expires` is a Date after which the data should be considered
// invalid. You could use it to invalidate the data somehow or
// automatically purge this record from your database.
let randomBytes = crypto.getRandomValues(new Uint8Array(54));
// This storage manages an id space of 2^64 ids, which is far greater
// than the maximum number of files allowed on an NTFS or ext4 volume
// (2^32). However, the larger id space should help to avoid collisions
// with existing ids when creating new sessions, which speeds things up.
let id = Buffer.from(randomBytes).toString("hex");
if (await redis.exists(id)) {
throw new Error("Session id already exists");
}
await redis.set(id, JSON.stringify(data), "EXAT", Math.floor(expires!.getTime() / 1000));
//.toString("hex"), JSON.stringify(data));
return id;
},
async readData(id) {
console.log("readData", id);
const data = await redis.get(id);
console.log("data", data);
return data ? JSON.parse(data) : null;
},
async updateData(id, data, expires) {
console.log("updateData", id, data, expires);
await redis.set(id, JSON.stringify(data), "KEEPTTL");
},
async deleteData(id) {
console.log("deleteData", id);
await redis.del(id);
},
});
}
// session data
type SessionData = {
id: number;
username: string;
totpAuthenticated: boolean;
lastSuccessfulTotp: Date | null;
};
type SessionFlashData = {
error: string;
};
/**
* @description: create a redis session storage
*/
const { getSession, commitSession, destroySession } = createRedisSessionStorage<SessionData, SessionFlashData>({
// a Cookie from `createCookie` or the CookieOptions to create one
cookie: {
name: "__session",
secrets: [process.env.COOKIE_SECRET as string],
// Expires can also be set (although maxAge overrides it when used in combination).
// Note that this method is NOT recommended as `new Date` creates only one date on each server deployment, not a dynamic date in the future!
//
// expires: new Date(Date.now() + 60_000),
httpOnly: true,
maxAge: 60 * 60 * 24 * 30, // 30 days
path: "/",
sameSite: "lax",
...(process.env.NODE_ENV === "production" ? { domain: process.env.COOKIE_DOMAIN, secure: true } : {}),
},
redisOptions: {
host: process.env.REDIS_HOST,
port: Number(process.env.REDIS_PORT),
db: 0,
},
});
// better getSession wrapper
async function getSessionWrapper(request: Request) {
const session = getSession(request.headers.get("Cookie"));
return session;
}
export { getSessionWrapper as getSession, commitSession, destroySession };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment