import { createHmac } from "node:crypto";
import { decodeOrEmpty } from "#lib/uri.ts";
export const AVATAR_DID = /^did:[a-z]+:[a-zA-Z0-9._:%-]{1,256}$/;
const PASSED_THROUGH = ["size", "format", "v"];
export const signedAvatarUrl = (
service: string,
secret: string,
did: string,
search: URLSearchParams
): string => {
const query = new URLSearchParams();
for (const key of PASSED_THROUGH) {
const value = search.get(key);
if (value) query.set(key, value);
}
const signature = createHmac("sha256", secret).update(did).digest("hex");
return `${service}/${signature}/${did}${query.size ? `?${query}` : ""}`;
};
const AVATAR_SRC = /src="\/avatar\/([^"?]+)(?:\?([^"]*))?"/g;
// hydration keeps a server src. client renders still go through the route
export const signAvatarSrcs = (html: string, service: string, secret: string): string =>
html.replace(AVATAR_SRC, (src, did: string, query = "") => {
const decoded = decodeOrEmpty(did);
if (!AVATAR_DID.test(decoded)) return src;
const search = new URLSearchParams(query.replaceAll("&", "&"));
return `src="${signedAvatarUrl(service, secret, decoded, search).replaceAll("&", "&")}"`;
});