mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Cache signers, cache nip46 getPublicKey
This commit is contained in:
parent
efae292099
commit
5ef87a9119
4 changed files with 35 additions and 8 deletions
|
|
@ -342,6 +342,13 @@ class Conf {
|
||||||
ttl: Number(Deno.env.get('DITTO_CACHE_TRANSLATION_TTL') || 6 * 60 * 60 * 1000),
|
ttl: Number(Deno.env.get('DITTO_CACHE_TRANSLATION_TTL') || 6 * 60 * 60 * 1000),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/** Signer cache settings. */
|
||||||
|
get signer(): { max: number; ttl: number } {
|
||||||
|
return {
|
||||||
|
max: Number(Deno.env.get('DITTO_CACHE_SIGNER_MAX') || 1000),
|
||||||
|
ttl: Number(Deno.env.get('DITTO_CACHE_SIGNER_TTL') || 1 * 60 * 60 * 1000),
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,11 @@ export const cachedTranslationsSizeGauge = new Gauge({
|
||||||
help: 'Number of translated statuses in cache',
|
help: 'Number of translated statuses in cache',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const cachedSignersSizeGauge = new Gauge({
|
||||||
|
name: 'ditto_cached_signers_size',
|
||||||
|
help: 'Number of signers in cache',
|
||||||
|
});
|
||||||
|
|
||||||
export const internalSubscriptionsSizeGauge = new Gauge({
|
export const internalSubscriptionsSizeGauge = new Gauge({
|
||||||
name: 'ditto_internal_subscriptions_size',
|
name: 'ditto_internal_subscriptions_size',
|
||||||
help: "Number of active subscriptions to Ditto's internal relay",
|
help: "Number of active subscriptions to Ditto's internal relay",
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
import { HTTPException } from '@hono/hono/http-exception';
|
import { HTTPException } from '@hono/hono/http-exception';
|
||||||
import { NSecSigner } from '@nostrify/nostrify';
|
import { type NostrSigner, NSecSigner } from '@nostrify/nostrify';
|
||||||
|
import { LRUCache } from 'lru-cache';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
import { AppMiddleware } from '@/app.ts';
|
import { AppMiddleware } from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
import { cachedSignersSizeGauge } from '@/metrics.ts';
|
||||||
import { ConnectSigner } from '@/signers/ConnectSigner.ts';
|
import { ConnectSigner } from '@/signers/ConnectSigner.ts';
|
||||||
import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts';
|
import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { aesDecrypt } from '@/utils/aes.ts';
|
import { aesDecrypt } from '@/utils/aes.ts';
|
||||||
import { getTokenHash } from '@/utils/auth.ts';
|
import { getTokenHash } from '@/utils/auth.ts';
|
||||||
|
|
||||||
|
const signerCache = new LRUCache<string, NostrSigner>(Conf.caches.signer);
|
||||||
|
|
||||||
/** Make a `signer` object available to all controllers, or unset if the user isn't logged in. */
|
/** Make a `signer` object available to all controllers, or unset if the user isn't logged in. */
|
||||||
export const signerMiddleware: AppMiddleware = async (c, next) => {
|
export const signerMiddleware: AppMiddleware = async (c, next) => {
|
||||||
const accessToken = getAccessToken(c.req.raw);
|
const accessToken = getAccessToken(c.req.raw);
|
||||||
|
|
||||||
if (accessToken?.startsWith('token1')) {
|
const cached = accessToken ? signerCache.get(accessToken) : undefined;
|
||||||
|
|
||||||
|
if (cached) {
|
||||||
|
c.set('signer', cached);
|
||||||
|
} else if (accessToken?.startsWith('token1')) {
|
||||||
try {
|
try {
|
||||||
const kysely = await Storages.kysely();
|
const kysely = await Storages.kysely();
|
||||||
const tokenHash = await getTokenHash(accessToken as `token1${string}`);
|
const tokenHash = await getTokenHash(accessToken as `token1${string}`);
|
||||||
|
|
@ -26,8 +34,12 @@ export const signerMiddleware: AppMiddleware = async (c, next) => {
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
const nep46Seckey = await aesDecrypt(Conf.seckey, nip46_sk_enc);
|
const nep46Seckey = await aesDecrypt(Conf.seckey, nip46_sk_enc);
|
||||||
|
const signer = new ConnectSigner(pubkey, new NSecSigner(nep46Seckey), nip46_relays);
|
||||||
|
|
||||||
c.set('signer', new ConnectSigner(pubkey, new NSecSigner(nep46Seckey), nip46_relays));
|
signerCache.set(accessToken, signer);
|
||||||
|
cachedSignersSizeGauge.set(signerCache.size);
|
||||||
|
|
||||||
|
c.set('signer', signer);
|
||||||
} catch {
|
} catch {
|
||||||
throw new HTTPException(401);
|
throw new HTTPException(401);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,15 @@ import { Storages } from '@/storages.ts';
|
||||||
*/
|
*/
|
||||||
export class ConnectSigner implements NostrSigner {
|
export class ConnectSigner implements NostrSigner {
|
||||||
private signer: Promise<NConnectSigner>;
|
private signer: Promise<NConnectSigner>;
|
||||||
|
private cachedPubkey: Promise<string> | undefined;
|
||||||
|
|
||||||
constructor(private pubkey: string, signer: NostrSigner, private relays?: string[]) {
|
constructor(pubkey: string, signer: NostrSigner, private relays?: string[]) {
|
||||||
this.signer = this.init(signer);
|
this.signer = this.init(pubkey, signer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(signer: NostrSigner): Promise<NConnectSigner> {
|
async init(pubkey: string, signer: NostrSigner): Promise<NConnectSigner> {
|
||||||
return new NConnectSigner({
|
return new NConnectSigner({
|
||||||
pubkey: this.pubkey,
|
pubkey,
|
||||||
// TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list)
|
// TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list)
|
||||||
relay: await Storages.pubsub(),
|
relay: await Storages.pubsub(),
|
||||||
signer,
|
signer,
|
||||||
|
|
@ -105,9 +106,11 @@ export class ConnectSigner implements NostrSigner {
|
||||||
|
|
||||||
async getPublicKey(): Promise<string> {
|
async getPublicKey(): Promise<string> {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
|
this.cachedPubkey = this.cachedPubkey ?? signer.getPublicKey();
|
||||||
try {
|
try {
|
||||||
return await signer.getPublicKey();
|
return await this.cachedPubkey;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
this.cachedPubkey = undefined;
|
||||||
if (e instanceof Error && e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, { message: 'Public key not received quickly enough' });
|
throw new HTTPException(408, { message: 'Public key not received quickly enough' });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue