2022-05-14 10:22:06 +00:00
|
|
|
import { type Session, type SessionIdStorageStrategy, createSessionStorage } from "@remix-run/node";
|
2023-04-29 16:30:07 +00:00
|
|
|
import type { TwilioAccount } from "@prisma/client";
|
2022-05-14 10:22:06 +00:00
|
|
|
|
|
|
|
import serverConfig from "~/config/config.server";
|
|
|
|
import db from "./db.server";
|
|
|
|
import logger from "./logger.server";
|
2023-04-29 16:30:07 +00:00
|
|
|
|
|
|
|
type SessionTwilioAccount = Pick<TwilioAccount, "accountSid" | "authToken">;
|
|
|
|
export type SessionData = {
|
|
|
|
twilio?: SessionTwilioAccount | null;
|
|
|
|
};
|
2022-05-14 10:22:06 +00:00
|
|
|
|
|
|
|
const SECOND = 1;
|
|
|
|
const MINUTE = 60 * SECOND;
|
|
|
|
const HOUR = 60 * MINUTE;
|
|
|
|
const DAY = 24 * HOUR;
|
|
|
|
|
2023-04-29 16:30:07 +00:00
|
|
|
const sessionStorage = createDatabaseSessionStorage<SessionData>({
|
2022-05-14 10:22:06 +00:00
|
|
|
cookie: {
|
|
|
|
name: "__session",
|
|
|
|
httpOnly: true,
|
|
|
|
path: "/",
|
|
|
|
sameSite: "lax",
|
|
|
|
secrets: [serverConfig.app.sessionSecret],
|
|
|
|
secure: process.env.NODE_ENV === "production" && process.env.CI !== "true",
|
|
|
|
maxAge: 30 * DAY,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-04-29 16:30:07 +00:00
|
|
|
export function getSession(request: Request): Promise<Session<SessionData>> {
|
2022-05-14 10:22:06 +00:00
|
|
|
return sessionStorage.getSession(request.headers.get("Cookie"));
|
|
|
|
}
|
|
|
|
|
2023-04-29 16:30:07 +00:00
|
|
|
export const { commitSession } = sessionStorage;
|
2022-05-14 10:22:06 +00:00
|
|
|
|
2023-04-29 16:30:07 +00:00
|
|
|
function createDatabaseSessionStorage<Data extends SessionData = SessionData>({
|
|
|
|
cookie,
|
|
|
|
}: Pick<SessionIdStorageStrategy, "cookie">) {
|
|
|
|
return createSessionStorage<Data>({
|
2022-05-14 10:22:06 +00:00
|
|
|
cookie,
|
|
|
|
async createData(sessionData, expiresAt) {
|
2023-04-29 16:30:07 +00:00
|
|
|
let twilioAccount;
|
|
|
|
if (sessionData.twilio) {
|
|
|
|
twilioAccount = { connect: { accountSid: sessionData.twilio.accountSid } };
|
2022-05-14 10:22:06 +00:00
|
|
|
}
|
|
|
|
const { id } = await db.session.create({
|
|
|
|
data: {
|
|
|
|
expiresAt,
|
2023-04-29 16:30:07 +00:00
|
|
|
twilioAccount,
|
2022-05-14 10:22:06 +00:00
|
|
|
data: JSON.stringify(sessionData),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return id;
|
|
|
|
},
|
|
|
|
async readData(id) {
|
|
|
|
const session = await db.session.findUnique({ where: { id } });
|
|
|
|
if (!session) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const sessionHasExpired = session.expiresAt && session.expiresAt < new Date();
|
|
|
|
if (sessionHasExpired) {
|
|
|
|
await db.session.delete({ where: { id } });
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JSON.parse(session.data);
|
|
|
|
},
|
|
|
|
async updateData(id, sessionData, expiresAt) {
|
|
|
|
try {
|
|
|
|
await db.session.update({
|
|
|
|
where: { id },
|
|
|
|
data: {
|
|
|
|
data: JSON.stringify(sessionData),
|
|
|
|
expiresAt,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} catch (error: any) {
|
|
|
|
if (error.code === "P2025") {
|
|
|
|
logger.warn("Could not update session because it's not in the DB");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
async deleteData(id) {
|
|
|
|
try {
|
|
|
|
await db.session.delete({ where: { id } });
|
|
|
|
} catch (error: any) {
|
|
|
|
if (error.code === "P2025") {
|
|
|
|
logger.warn("Could not delete session because it's not in the DB");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|