diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts index 5c99b4d437..ab4fc04bc5 100644 --- a/packages/backend-rs/index.d.ts +++ b/packages/backend-rs/index.d.ts @@ -495,7 +495,7 @@ export declare function genId(): string /** Generate an ID using a specific datetime */ export declare function genIdAt(date: Date): string -export declare function genIdenticon(id: string): Buffer +export declare function genIdenticon(id: string): Promise export declare function getFullApAccount(username: string, host?: string | undefined | null): string diff --git a/packages/backend-rs/src/database/cache.rs b/packages/backend-rs/src/database/cache.rs index d56da7f680..2c5b88025a 100644 --- a/packages/backend-rs/src/database/cache.rs +++ b/packages/backend-rs/src/database/cache.rs @@ -10,6 +10,7 @@ pub enum Category { Block, Follow, CatLang, + RandomIcon, #[cfg(test)] Test, } @@ -35,6 +36,7 @@ fn categorize(category: Category, key: &str) -> String { Category::Block => "blocking", Category::Follow => "following", Category::CatLang => "catlang", + Category::RandomIcon => "randomIcon", #[cfg(test)] Category::Test => "usedOnlyForTesting", }; diff --git a/packages/backend-rs/src/misc/random_icon.rs b/packages/backend-rs/src/misc/random_icon.rs index b712063279..3cff7fcc10 100644 --- a/packages/backend-rs/src/misc/random_icon.rs +++ b/packages/backend-rs/src/misc/random_icon.rs @@ -1,13 +1,29 @@ use identicon_rs::{error::IdenticonError, Identicon}; +use crate::database::cache; -pub fn generate(id: &str) -> Result, IdenticonError> { - Identicon::new(id).set_border(35).export_png_data() +#[macros::errors] +pub enum Error { + #[doc = "failed to generate identicon"] + #[error(transparent)] + Identicon(#[from] IdenticonError), + #[error("Redis cache operation has failed")] + Cache(#[from] cache::Error), +} + +pub async fn generate(id: &str) -> Result, Error> { + if let Some(icon) = cache::get_one::>(cache::Category::RandomIcon, id).await? { + Ok(icon) + } else { + let icon = Identicon::new(id).set_border(35).export_png_data()?; + cache::set_one(cache::Category::RandomIcon, id, &icon, 10 * 60).await?; + Ok(icon) + } } #[cfg(feature = "napi")] #[napi_derive::napi(js_name = "genIdenticon")] -pub fn generate_js(id: String) -> napi::Result { - match generate(&id) { +pub async fn generate_js(id: String) -> napi::Result { + match generate(&id).await { Ok(icon) => Ok(icon.into()), Err(err) => Err(napi::Error::from_reason(format!( "\n{}\n", diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 3baae0f344..58a6e33456 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -118,7 +118,7 @@ router.get("/avatar/@:acct", async (ctx) => { router.get("/identicon/:x", async (ctx) => { const instanceMeta = await fetchMeta(); if (instanceMeta.enableIdenticonGeneration) { - const identicon = genIdenticon(ctx.params.x); + const identicon = await genIdenticon(ctx.params.x); const [temp, cleanup] = await createTemp(); fs.createWriteStream(temp).write(identicon); ctx.set("Content-Type", "image/png");