ChatGPT-Next-Web/app/utils/cloud/upstash.ts

102 lines
2.8 KiB
TypeScript
Raw Normal View History

2023-09-18 19:18:34 +00:00
import { STORAGE_KEY } from "@/app/constant";
2023-09-12 18:51:02 +00:00
import { SyncStore } from "@/app/store/sync";
2023-09-18 19:18:34 +00:00
import { corsFetch } from "../cors";
import { chunks } from "../format";
2023-09-12 18:51:02 +00:00
export type UpstashConfig = SyncStore["upstash"];
export type UpStashClient = ReturnType<typeof createUpstashClient>;
2023-09-18 19:18:34 +00:00
export function createUpstashClient(store: SyncStore) {
const config = store.upstash;
const storeKey = config.username.length === 0 ? STORAGE_KEY : config.username;
const chunkCountKey = `${storeKey}-chunk-count`;
const chunkIndexKey = (i: number) => `${storeKey}-chunk-${i}`;
const proxyUrl =
store.useProxy && store.proxyUrl.length > 0 ? store.proxyUrl : undefined;
2023-09-12 18:51:02 +00:00
return {
async check() {
2023-09-18 19:18:34 +00:00
try {
const res = await corsFetch(this.path(`get/${storeKey}`), {
method: "GET",
headers: this.headers(),
proxyUrl,
});
console.log("[Upstash] check", res.status, res.statusText);
return [200].includes(res.status);
} catch (e) {
console.error("[Upstash] failed to check", e);
}
return false;
},
async redisGet(key: string) {
const res = await corsFetch(this.path(`get/${key}`), {
method: "GET",
headers: this.headers(),
proxyUrl,
});
console.log("[Upstash] get key = ", key, res.status, res.statusText);
const resJson = (await res.json()) as { result: string };
return resJson.result;
},
async redisSet(key: string, value: string) {
const res = await corsFetch(this.path(`set/${key}`), {
method: "POST",
headers: this.headers(),
body: value,
proxyUrl,
});
console.log("[Upstash] set key = ", key, res.status, res.statusText);
2023-09-12 18:51:02 +00:00
},
async get() {
2023-09-18 19:18:34 +00:00
const chunkCount = Number(await this.redisGet(chunkCountKey));
if (!Number.isInteger(chunkCount)) return;
const chunks = await Promise.all(
new Array(chunkCount)
.fill(0)
.map((_, i) => this.redisGet(chunkIndexKey(i))),
);
console.log("[Upstash] get full chunks", chunks);
return chunks.join("");
2023-09-12 18:51:02 +00:00
},
2023-09-18 19:18:34 +00:00
async set(_: string, value: string) {
// upstash limit the max request size which is 1Mb for “Free” and “Pay as you go”
// so we need to split the data to chunks
let index = 0;
for await (const chunk of chunks(value)) {
await this.redisSet(chunkIndexKey(index), chunk);
index += 1;
}
await this.redisSet(chunkCountKey, index.toString());
2023-09-12 18:51:02 +00:00
},
headers() {
return {
2023-09-18 19:18:34 +00:00
Authorization: `Bearer ${config.apiKey}`,
2023-09-12 18:51:02 +00:00
};
},
path(path: string) {
let url = config.endpoint;
if (!url.endsWith("/")) {
url += "/";
}
if (path.startsWith("/")) {
path = path.slice(1);
}
return url + path;
},
};
}