Last active
July 15, 2023 18:52
-
-
Save jacob-ebey/d678283b0ae275bab23a1ec63d0a09fa to your computer and use it in GitHub Desktop.
Remix Upstash Session Storage
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 { type Agent } from "https"; | |
import { | |
createSessionStorage, | |
type RequestInit, | |
type SessionData, | |
type SessionIdStorageStrategy, | |
} from "@remix-run/node"; | |
export interface UpstashSessionStorageOptions { | |
cookie?: SessionIdStorageStrategy["cookie"]; | |
url: string; | |
token: string; | |
agent?: Agent; | |
generateId?: () => Promise<string> | string; | |
} | |
function defaultGenerateId() { | |
return fetch("https://uuid.rocks/plain").then((res) => { | |
if (!res.ok) { | |
throw new Error("Failed to generate a session id"); | |
} | |
return res.text(); | |
}); | |
} | |
interface SessionContent { | |
data: SessionData; | |
expires?: string; | |
} | |
export function createUpstashSessionStorage({ | |
cookie, | |
token, | |
url, | |
agent, | |
generateId = defaultGenerateId, | |
}: UpstashSessionStorageOptions) { | |
url = url.replace(/\/$/, ""); | |
let upstashFetch = (path: string, options: RequestInit = {}) => { | |
options.headers = { | |
...options.headers, | |
Authorization: `Bearer ${token}`, | |
}; | |
options.agent = agent; | |
return fetch(`${url}${path}`, options as globalThis.RequestInit); | |
}; | |
return createSessionStorage({ | |
cookie, | |
async createData(data, expires) { | |
let id = await generateId(); | |
await upstashFetch(`/set/${id}`, { | |
method: "POST", | |
body: JSON.stringify({ data, expires }), | |
}).then(async (response) => { | |
if (!response.ok || (await response.json())?.result != "OK") { | |
throw new Error("Failed to create session"); | |
} | |
}); | |
return id; | |
}, | |
deleteData(id) { | |
return upstashFetch(`/del/${id}`, { | |
method: "POST", | |
headers: { | |
Authorization: `Bearer ${token}`, | |
}, | |
}).then(async (response) => { | |
if (!response.ok || !((await response.json())?.result > 0)) { | |
throw new Error("Failed to delete session"); | |
} | |
}); | |
}, | |
async readData(id) { | |
const content = await upstashFetch(`/get/${id}`) | |
.then(async (response) => { | |
if (!response.ok) { | |
throw new Error("Failed to read session"); | |
} | |
return response.json(); | |
}) | |
.then((json) => { | |
return JSON.parse(json.result) as SessionContent | null; | |
}); | |
if (!content) { | |
return null; | |
} | |
const data = content.data; | |
const expires = | |
typeof content.expires === "string" ? new Date(content.expires) : null; | |
if (!expires || expires > new Date()) { | |
return data; | |
} | |
if (expires) { | |
await upstashFetch(`/del/${id}`, { | |
method: "POST", | |
headers: { | |
Authorization: `Bearer ${token}`, | |
}, | |
}); | |
} | |
return null; | |
}, | |
updateData(id, data, expires) { | |
return upstashFetch(`/set/${id}`, { | |
method: "POST", | |
body: JSON.stringify({ data, expires }), | |
}).then(async (response) => { | |
if (!response.ok || (await response.json())?.result != "OK") { | |
throw new Error("Failed to update session"); | |
} | |
}); | |
}, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment