Merge branch 'nostrify-22' into 'main'
Upgrade Nostrify to v0.22.0 See merge request soapbox-pub/ditto!283
This commit is contained in:
commit
281f0f9625
|
@ -22,7 +22,7 @@
|
||||||
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
||||||
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
|
"@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1",
|
||||||
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
"@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",
|
"@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/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
|
||||||
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",
|
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",
|
||||||
|
|
|
@ -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 Debug from '@soapbox/stickynotes/debug';
|
||||||
import { type Context, Env as HonoEnv, type Handler, Hono, Input as HonoInput, type MiddlewareHandler } from 'hono';
|
import { type Context, Env as HonoEnv, type Handler, Hono, Input as HonoInput, type MiddlewareHandler } from 'hono';
|
||||||
import { cors, logger, serveStatic } from 'hono/middleware';
|
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 { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
|
||||||
import { nostrController } from '@/controllers/well-known/nostr.ts';
|
import { nostrController } from '@/controllers/well-known/nostr.ts';
|
||||||
import { webfingerController } from '@/controllers/well-known/webfinger.ts';
|
import { webfingerController } from '@/controllers/well-known/webfinger.ts';
|
||||||
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
|
|
||||||
import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts';
|
import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts';
|
||||||
import { cacheMiddleware } from '@/middleware/cacheMiddleware.ts';
|
import { cacheMiddleware } from '@/middleware/cacheMiddleware.ts';
|
||||||
import { cspMiddleware } from '@/middleware/cspMiddleware.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 to get the logged-in user's pubkey, relays, and to sign events, or `undefined` if the user isn't logged in. */
|
||||||
signer?: NostrSigner;
|
signer?: NostrSigner;
|
||||||
/** Uploader for the user to upload files. */
|
/** Uploader for the user to upload files. */
|
||||||
uploader?: DittoUploader;
|
uploader?: NUploader;
|
||||||
/** NIP-98 signed event proving the pubkey is owned by the user. */
|
/** NIP-98 signed event proving the pubkey is owned by the user. */
|
||||||
proof?: NostrEvent;
|
proof?: NostrEvent;
|
||||||
/** Store */
|
/** Store */
|
||||||
|
|
|
@ -140,6 +140,10 @@ class Conf {
|
||||||
static get nostrbuildEndpoint(): string {
|
static get nostrbuildEndpoint(): string {
|
||||||
return Deno.env.get('NOSTRBUILD_ENDPOINT') || 'https://nostr.build/api/v2/upload/files';
|
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. */
|
/** Module to upload files with. */
|
||||||
static get uploader() {
|
static get uploader() {
|
||||||
return Deno.env.get('DITTO_UPLOADER');
|
return Deno.env.get('DITTO_UPLOADER');
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export interface DittoUploader {
|
|
||||||
upload(file: File, opts?: { signal?: AbortSignal }): Promise<[['url', string], ...string[][]]>;
|
|
||||||
}
|
|
|
@ -1,13 +1,16 @@
|
||||||
|
import { BlossomUploader, NostrBuildUploader } from '@nostrify/nostrify/uploaders';
|
||||||
|
|
||||||
import { AppMiddleware } from '@/app.ts';
|
import { AppMiddleware } from '@/app.ts';
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { DenoUploader } from '@/uploaders/DenoUploader.ts';
|
import { DenoUploader } from '@/uploaders/DenoUploader.ts';
|
||||||
import { IPFSUploader } from '@/uploaders/IPFSUploader.ts';
|
import { IPFSUploader } from '@/uploaders/IPFSUploader.ts';
|
||||||
import { NostrBuildUploader } from '@/uploaders/NostrBuildUploader.ts';
|
|
||||||
import { S3Uploader } from '@/uploaders/S3Uploader.ts';
|
import { S3Uploader } from '@/uploaders/S3Uploader.ts';
|
||||||
import { fetchWorker } from '@/workers/fetch.ts';
|
import { fetchWorker } from '@/workers/fetch.ts';
|
||||||
|
|
||||||
/** Set an uploader for the user. */
|
/** Set an uploader for the user. */
|
||||||
export const uploaderMiddleware: AppMiddleware = async (c, next) => {
|
export const uploaderMiddleware: AppMiddleware = async (c, next) => {
|
||||||
|
const signer = c.get('signer');
|
||||||
|
|
||||||
switch (Conf.uploader) {
|
switch (Conf.uploader) {
|
||||||
case 's3':
|
case 's3':
|
||||||
c.set('uploader', new S3Uploader(Conf.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 }));
|
c.set('uploader', new DenoUploader({ baseUrl: Conf.mediaDomain, dir: Conf.uploadsDir }));
|
||||||
break;
|
break;
|
||||||
case 'nostrbuild':
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
import { NUploader } from '@nostrify/nostrify';
|
||||||
import { crypto } from '@std/crypto';
|
import { crypto } from '@std/crypto';
|
||||||
import { encodeHex } from '@std/encoding/hex';
|
import { encodeHex } from '@std/encoding/hex';
|
||||||
import { extensionsByType } from '@std/media-types';
|
import { extensionsByType } from '@std/media-types';
|
||||||
|
|
||||||
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
|
|
||||||
|
|
||||||
export interface DenoUploaderOpts {
|
export interface DenoUploaderOpts {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Local Deno filesystem uploader. */
|
/** Local Deno filesystem uploader. */
|
||||||
export class DenoUploader implements DittoUploader {
|
export class DenoUploader implements NUploader {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
import { NUploader } from '@nostrify/nostrify';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
|
|
||||||
|
|
||||||
export interface IPFSUploaderOpts {
|
export interface IPFSUploaderOpts {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
@ -13,7 +12,7 @@ export interface IPFSUploaderOpts {
|
||||||
* It will try to connect to `http://localhost:5001` by default,
|
* It will try to connect to `http://localhost:5001` by default,
|
||||||
* and upload the file using the REST API.
|
* and upload the file using the REST API.
|
||||||
*/
|
*/
|
||||||
export class IPFSUploader implements DittoUploader {
|
export class IPFSUploader implements NUploader {
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private apiUrl: string;
|
private apiUrl: string;
|
||||||
private fetch: typeof fetch;
|
private fetch: typeof fetch;
|
||||||
|
|
|
@ -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),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
import { S3Client } from '@bradenmacdonald/s3-lite-client';
|
import { S3Client } from '@bradenmacdonald/s3-lite-client';
|
||||||
|
import { NUploader } from '@nostrify/nostrify';
|
||||||
import { crypto } from '@std/crypto';
|
import { crypto } from '@std/crypto';
|
||||||
import { encodeHex } from '@std/encoding/hex';
|
import { encodeHex } from '@std/encoding/hex';
|
||||||
import { extensionsByType } from '@std/media-types';
|
import { extensionsByType } from '@std/media-types';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { DittoUploader } from '@/interfaces/DittoUploader.ts';
|
|
||||||
|
|
||||||
export interface S3UploaderOpts {
|
export interface S3UploaderOpts {
|
||||||
endPoint: string;
|
endPoint: string;
|
||||||
|
@ -21,7 +21,7 @@ export interface S3UploaderOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** S3-compatible uploader for AWS, Wasabi, DigitalOcean Spaces, and more. */
|
/** S3-compatible uploader for AWS, Wasabi, DigitalOcean Spaces, and more. */
|
||||||
export class S3Uploader implements DittoUploader {
|
export class S3Uploader implements NUploader {
|
||||||
private client: S3Client;
|
private client: S3Client;
|
||||||
|
|
||||||
constructor(opts: S3UploaderOpts) {
|
constructor(opts: S3UploaderOpts) {
|
||||||
|
|
Loading…
Reference in New Issue