You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			111 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			111 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			TypeScript
		
	
| import { STORAGE_KEY } from "@/app/constant";
 | |
| import { SyncStore } from "@/app/store/sync";
 | |
| import { chunks } from "../format";
 | |
| 
 | |
| export type UpstashConfig = SyncStore["upstash"];
 | |
| export type UpStashClient = ReturnType<typeof createUpstashClient>;
 | |
| 
 | |
| 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;
 | |
| 
 | |
|   return {
 | |
|     async check() {
 | |
|       try {
 | |
|         const res = await fetch(this.path(`get/${storeKey}`, proxyUrl), {
 | |
|           method: "GET",
 | |
|           headers: this.headers(),
 | |
|         });
 | |
|         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 fetch(this.path(`get/${key}`, proxyUrl), {
 | |
|         method: "GET",
 | |
|         headers: this.headers(),
 | |
|       });
 | |
| 
 | |
|       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 fetch(this.path(`set/${key}`, proxyUrl), {
 | |
|         method: "POST",
 | |
|         headers: this.headers(),
 | |
|         body: value,
 | |
|       });
 | |
| 
 | |
|       console.log("[Upstash] set key = ", key, res.status, res.statusText);
 | |
|     },
 | |
| 
 | |
|     async get() {
 | |
|       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("");
 | |
|     },
 | |
| 
 | |
|     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());
 | |
|     },
 | |
| 
 | |
|     headers() {
 | |
|       return {
 | |
|         Authorization: `Bearer ${config.apiKey}`,
 | |
|       };
 | |
|     },
 | |
|     path(path: string, proxyUrl: string = "") {
 | |
|       if (!path.endsWith("/")) {
 | |
|         path += "/";
 | |
|       }
 | |
|       if (path.startsWith("/")) {
 | |
|         path = path.slice(1);
 | |
|       }
 | |
| 
 | |
|       if (proxyUrl.length > 0 && !proxyUrl.endsWith("/")) {
 | |
|         proxyUrl += "/";
 | |
|       }
 | |
| 
 | |
|       let url;
 | |
|       const pathPrefix = "/api/upstash/";
 | |
| 
 | |
|       try {
 | |
|         let u = new URL(proxyUrl + pathPrefix + path);
 | |
|         // add query params
 | |
|         u.searchParams.append("endpoint", config.endpoint);
 | |
|         url = u.toString();
 | |
|       } catch (e) {
 | |
|         url = pathPrefix + path + "?endpoint=" + config.endpoint;
 | |
|       }
 | |
| 
 | |
|       return url;
 | |
|     },
 | |
|   };
 | |
| }
 |