Merge branch 'nostrify-22' into 'main'

Upgrade Nostrify to v0.22.0

See merge request soapbox-pub/ditto!283
This commit is contained in:
Alex Gleason 2024-05-19 21:00:26 +00:00
commit 281f0f9625
9 changed files with 23 additions and 86 deletions

View File

@ -22,7 +22,7 @@
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.21.1",
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.22.0",
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
"@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",

View File

@ -1,4 +1,4 @@
import { NostrEvent, NostrSigner, NStore } from '@nostrify/nostrify';
import { NostrEvent, NostrSigner, NStore, NUploader } from '@nostrify/nostrify';
import Debug from '@soapbox/stickynotes/debug';
import { type Context, Env as HonoEnv, type Handler, Hono, Input as HonoInput, type MiddlewareHandler } from 'hono';
import { cors, logger, serveStatic } from 'hono/middleware';
@ -81,7 +81,6 @@ import { hostMetaController } from '@/controllers/well-known/host-meta.ts';
import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
import { nostrController } from '@/controllers/well-known/nostr.ts';
import { webfingerController } from '@/controllers/well-known/webfinger.ts';
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts';
import { cacheMiddleware } from '@/middleware/cacheMiddleware.ts';
import { cspMiddleware } from '@/middleware/cspMiddleware.ts';
@ -97,7 +96,7 @@ interface AppEnv extends HonoEnv {
/** Signer to get the logged-in user's pubkey, relays, and to sign events, or `undefined` if the user isn't logged in. */
signer?: NostrSigner;
/** Uploader for the user to upload files. */
uploader?: DittoUploader;
uploader?: NUploader;
/** NIP-98 signed event proving the pubkey is owned by the user. */
proof?: NostrEvent;
/** Store */

View File

@ -140,6 +140,10 @@ class Conf {
static get nostrbuildEndpoint(): string {
return Deno.env.get('NOSTRBUILD_ENDPOINT') || 'https://nostr.build/api/v2/upload/files';
}
/** Default Blossom servers to use when the `blossom` uploader is set. */
static get blossomServers(): string[] {
return Deno.env.get('BLOSSOM_SERVERS')?.split(',') || ['https://blossom.primal.net/'];
}
/** Module to upload files with. */
static get uploader() {
return Deno.env.get('DITTO_UPLOADER');

View File

@ -1,3 +0,0 @@
export interface DittoUploader {
upload(file: File, opts?: { signal?: AbortSignal }): Promise<[['url', string], ...string[][]]>;
}

View File

@ -1,13 +1,16 @@
import { BlossomUploader, NostrBuildUploader } from '@nostrify/nostrify/uploaders';
import { AppMiddleware } from '@/app.ts';
import { Conf } from '@/config.ts';
import { DenoUploader } from '@/uploaders/DenoUploader.ts';
import { IPFSUploader } from '@/uploaders/IPFSUploader.ts';
import { NostrBuildUploader } from '@/uploaders/NostrBuildUploader.ts';
import { S3Uploader } from '@/uploaders/S3Uploader.ts';
import { fetchWorker } from '@/workers/fetch.ts';
/** Set an uploader for the user. */
export const uploaderMiddleware: AppMiddleware = async (c, next) => {
const signer = c.get('signer');
switch (Conf.uploader) {
case 's3':
c.set('uploader', new S3Uploader(Conf.s3));
@ -19,7 +22,12 @@ export const uploaderMiddleware: AppMiddleware = async (c, next) => {
c.set('uploader', new DenoUploader({ baseUrl: Conf.mediaDomain, dir: Conf.uploadsDir }));
break;
case 'nostrbuild':
c.set('uploader', new NostrBuildUploader({ endpoint: Conf.nostrbuildEndpoint, fetch: fetchWorker }));
c.set('uploader', new NostrBuildUploader({ endpoint: Conf.nostrbuildEndpoint, signer, fetch: fetchWorker }));
break;
case 'blossom':
if (signer) {
c.set('uploader', new BlossomUploader({ servers: Conf.blossomServers, signer, fetch: fetchWorker }));
}
break;
}

View File

@ -1,18 +1,17 @@
import { join } from 'node:path';
import { NUploader } from '@nostrify/nostrify';
import { crypto } from '@std/crypto';
import { encodeHex } from '@std/encoding/hex';
import { extensionsByType } from '@std/media-types';
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
export interface DenoUploaderOpts {
baseUrl: string;
dir: string;
}
/** Local Deno filesystem uploader. */
export class DenoUploader implements DittoUploader {
export class DenoUploader implements NUploader {
baseUrl: string;
dir: string;

View File

@ -1,7 +1,6 @@
import { NUploader } from '@nostrify/nostrify';
import { z } from 'zod';
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
export interface IPFSUploaderOpts {
baseUrl: string;
apiUrl?: string;
@ -13,7 +12,7 @@ export interface IPFSUploaderOpts {
* It will try to connect to `http://localhost:5001` by default,
* and upload the file using the REST API.
*/
export class IPFSUploader implements DittoUploader {
export class IPFSUploader implements NUploader {
private baseUrl: string;
private apiUrl: string;
private fetch: typeof fetch;

View File

@ -1,69 +0,0 @@
import { z } from 'zod';
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
export interface NostrBuildUploaderOpts {
endpoint?: string;
fetch?: typeof fetch;
}
/** Upload files to nostr.build or another compatible server. */
export class NostrBuildUploader implements DittoUploader {
private endpoint: string;
private fetch: typeof fetch;
constructor(opts: NostrBuildUploaderOpts) {
this.endpoint = opts.endpoint ?? 'https://nostr.build/api/v2/upload/files';
this.fetch = opts.fetch ?? globalThis.fetch;
}
async upload(file: File, opts?: { signal?: AbortSignal }): Promise<[['url', string], ...string[][]]> {
const formData = new FormData();
formData.append('fileToUpload', file);
const response = await this.fetch(this.endpoint, {
method: 'POST',
body: formData,
signal: opts?.signal,
});
const json = await response.json();
const [data] = NostrBuildUploader.schema().parse(json).data;
const tags: [['url', string], ...string[][]] = [
['url', data.url],
['m', data.mime],
['x', data.sha256],
['ox', data.original_sha256],
['size', data.size.toString()],
];
if (data.dimensions) {
tags.push(['dim', `${data.dimensions.width}x${data.dimensions.height}`]);
}
if (data.blurhash) {
tags.push(['blurhash', data.blurhash]);
}
return tags;
}
/** nostr.build API response schema. */
private static schema() {
return z.object({
data: z.object({
url: z.string().url(),
blurhash: z.string().optional().catch(undefined),
sha256: z.string(),
original_sha256: z.string(),
mime: z.string(),
size: z.number(),
dimensions: z.object({
width: z.number(),
height: z.number(),
}).optional().catch(undefined),
}).array().min(1),
});
}
}

View File

@ -1,12 +1,12 @@
import { join } from 'node:path';
import { S3Client } from '@bradenmacdonald/s3-lite-client';
import { NUploader } from '@nostrify/nostrify';
import { crypto } from '@std/crypto';
import { encodeHex } from '@std/encoding/hex';
import { extensionsByType } from '@std/media-types';
import { Conf } from '@/config.ts';
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
export interface S3UploaderOpts {
endPoint: string;
@ -21,7 +21,7 @@ export interface S3UploaderOpts {
}
/** S3-compatible uploader for AWS, Wasabi, DigitalOcean Spaces, and more. */
export class S3Uploader implements DittoUploader {
export class S3Uploader implements NUploader {
private client: S3Client;
constructor(opts: S3UploaderOpts) {