Created
November 9, 2024 02:02
-
-
Save VIEWVIEWVIEW/bbafbf2cd6fb05eb34bcc4e2ae9cd711 to your computer and use it in GitHub Desktop.
A remix session plugin for ioredis.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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