Helper for cache related utilities (#6696)
Co-authored-by: Tom Moor <tom@getoutline.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import { authorize } from "@server/policies";
|
||||
import { presentDocument, presentMention } from "@server/presenters/unfurls";
|
||||
import presentUnfurl from "@server/presenters/unfurls/unfurl";
|
||||
import { APIContext } from "@server/types";
|
||||
import { CacheHelper } from "@server/utils/CacheHelper";
|
||||
import { Hook, PluginManager } from "@server/utils/PluginManager";
|
||||
import { RateLimiterStrategy } from "@server/utils/RateLimiter";
|
||||
import * as T from "./schema";
|
||||
@@ -75,12 +76,26 @@ router.post(
|
||||
}
|
||||
|
||||
// External resources
|
||||
const cachedData = await CacheHelper.getData(
|
||||
CacheHelper.getUnfurlKey(url, actor.teamId)
|
||||
);
|
||||
if (cachedData) {
|
||||
return (ctx.body = presentUnfurl(cachedData));
|
||||
}
|
||||
|
||||
for (const plugin of plugins) {
|
||||
const data = await plugin.value(url);
|
||||
const data = await plugin.value.unfurl(url);
|
||||
if (data) {
|
||||
return "error" in data
|
||||
? (ctx.response.status = 204)
|
||||
: (ctx.body = presentUnfurl(data));
|
||||
if ("error" in data) {
|
||||
return (ctx.response.status = 204);
|
||||
} else {
|
||||
await CacheHelper.setData(
|
||||
CacheHelper.getUnfurlKey(url, actor.teamId),
|
||||
data,
|
||||
plugin.value.cacheExpiry
|
||||
);
|
||||
return (ctx.body = presentUnfurl(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
server/utils/CacheHelper.ts
Normal file
66
server/utils/CacheHelper.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Day } from "@shared/utils/time";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import Redis from "@server/storage/redis";
|
||||
|
||||
/**
|
||||
* A Helper class for server-side cache management
|
||||
*/
|
||||
export class CacheHelper {
|
||||
private static defaultDataExpiry = Day;
|
||||
|
||||
/**
|
||||
* Given a key, gets the data from cache store
|
||||
*
|
||||
* @param key Key against which data will be accessed
|
||||
*/
|
||||
public static async getData(key: string) {
|
||||
try {
|
||||
const data = await Redis.defaultClient.get(key);
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
} catch (err) {
|
||||
// just log it, response can still be obtained using the fetch call
|
||||
Logger.error(`Could not fetch cached response against ${key}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a key, data and cache config, saves the data in cache store
|
||||
*
|
||||
* @param key Cache key
|
||||
* @param data Data to be saved against the key
|
||||
* @param expiry Cache data expiry
|
||||
*/
|
||||
public static async setData(
|
||||
key: string,
|
||||
data: Record<string, any>,
|
||||
expiry?: number
|
||||
) {
|
||||
if ("error" in data) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Redis.defaultClient.set(
|
||||
key,
|
||||
JSON.stringify(data),
|
||||
"EX",
|
||||
expiry || this.defaultDataExpiry
|
||||
);
|
||||
} catch (err) {
|
||||
// just log it, can skip caching and directly return response
|
||||
Logger.error(`Could not cache response against ${key}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets key against which unfurl response for the given url is stored
|
||||
*
|
||||
* @param url The url to generate a key for
|
||||
* @param teamId The team ID to generate a key for
|
||||
*/
|
||||
public static getUnfurlKey(url: string, teamId: string) {
|
||||
return `unfurl:${teamId}:${url}`;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ type PluginValueMap = {
|
||||
[Hook.EmailTemplate]: typeof BaseEmail;
|
||||
[Hook.Processor]: typeof BaseProcessor;
|
||||
[Hook.Task]: typeof BaseTask<any>;
|
||||
[Hook.UnfurlProvider]: UnfurlSignature;
|
||||
[Hook.UnfurlProvider]: { unfurl: UnfurlSignature; cacheExpiry: number };
|
||||
};
|
||||
|
||||
export type Plugin<T extends Hook> = {
|
||||
|
||||
Reference in New Issue
Block a user