mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Add @ditto/router package
This commit is contained in:
parent
f5947eda8b
commit
5c0a350776
9 changed files with 146 additions and 0 deletions
|
|
@ -9,6 +9,7 @@
|
|||
"./packages/metrics",
|
||||
"./packages/policies",
|
||||
"./packages/ratelimiter",
|
||||
"./packages/router",
|
||||
"./packages/translators",
|
||||
"./packages/uploaders"
|
||||
],
|
||||
|
|
|
|||
23
packages/router/DittoApp.test.ts
Normal file
23
packages/router/DittoApp.test.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { DittoConf } from '@ditto/conf';
|
||||
import { DittoDB } from '@ditto/db';
|
||||
import { Hono } from '@hono/hono';
|
||||
import { MockRelay } from '@nostrify/nostrify/test';
|
||||
|
||||
import { DittoApp } from './DittoApp.ts';
|
||||
import { DittoRoute } from './DittoRoute.ts';
|
||||
|
||||
Deno.test('DittoApp', async () => {
|
||||
await using db = DittoDB.create('memory://');
|
||||
const conf = new DittoConf(new Map());
|
||||
const relay = new MockRelay();
|
||||
|
||||
const app = new DittoApp({ conf, db, relay });
|
||||
|
||||
const hono = new Hono();
|
||||
const route = new DittoRoute();
|
||||
|
||||
app.route('/', route);
|
||||
|
||||
// @ts-expect-error Passing a non-DittoRoute to route.
|
||||
app.route('/', hono);
|
||||
});
|
||||
21
packages/router/DittoApp.ts
Normal file
21
packages/router/DittoApp.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { Hono } from '@hono/hono';
|
||||
|
||||
import type { HonoOptions } from '@hono/hono/hono-base';
|
||||
import type { DittoEnv } from './DittoEnv.ts';
|
||||
|
||||
export class DittoApp extends Hono<DittoEnv> {
|
||||
// @ts-ignore Require a DittoRoute for type safety.
|
||||
declare route: (path: string, app: Hono<DittoEnv>) => Hono<DittoEnv>;
|
||||
|
||||
constructor(vars: Omit<DittoEnv['Variables'], 'signal'>, opts: HonoOptions<DittoEnv> = {}) {
|
||||
super(opts);
|
||||
|
||||
this.use((c, next) => {
|
||||
c.set('db', vars.db);
|
||||
c.set('conf', vars.conf);
|
||||
c.set('relay', vars.relay);
|
||||
c.set('signal', c.req.raw.signal);
|
||||
return next();
|
||||
});
|
||||
}
|
||||
}
|
||||
20
packages/router/DittoEnv.ts
Normal file
20
packages/router/DittoEnv.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import type { DittoConf } from '@ditto/conf';
|
||||
import type { DittoDatabase } from '@ditto/db';
|
||||
import type { Env } from '@hono/hono';
|
||||
import type { NRelay } from '@nostrify/nostrify';
|
||||
|
||||
export interface DittoEnv extends Env {
|
||||
Variables: {
|
||||
/** Ditto site configuration. */
|
||||
conf: DittoConf;
|
||||
/** Relay store. */
|
||||
relay: NRelay;
|
||||
/**
|
||||
* Database object.
|
||||
* @deprecated Store data as Nostr events instead.
|
||||
*/
|
||||
db: DittoDatabase;
|
||||
/** Abort signal for the request. */
|
||||
signal: AbortSignal;
|
||||
};
|
||||
}
|
||||
5
packages/router/DittoMiddleware.ts
Normal file
5
packages/router/DittoMiddleware.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import type { MiddlewareHandler } from '@hono/hono';
|
||||
import type { DittoEnv } from './DittoEnv.ts';
|
||||
|
||||
// deno-lint-ignore ban-types
|
||||
export type DittoMiddleware<T extends {}> = MiddlewareHandler<DittoEnv & { Variables: T }>;
|
||||
12
packages/router/DittoRoute.test.ts
Normal file
12
packages/router/DittoRoute.test.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { assertEquals } from '@std/assert';
|
||||
|
||||
import { DittoRoute } from './DittoRoute.ts';
|
||||
|
||||
Deno.test('DittoRoute', async () => {
|
||||
const route = new DittoRoute();
|
||||
const response = await route.request('/');
|
||||
const body = await response.json();
|
||||
|
||||
assertEquals(response.status, 500);
|
||||
assertEquals(body, { error: 'Missing required variable: db' });
|
||||
});
|
||||
53
packages/router/DittoRoute.ts
Normal file
53
packages/router/DittoRoute.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { type ErrorHandler, Hono } from '@hono/hono';
|
||||
import { HTTPException } from '@hono/hono/http-exception';
|
||||
|
||||
import type { HonoOptions } from '@hono/hono/hono-base';
|
||||
import type { DittoEnv } from './DittoEnv.ts';
|
||||
|
||||
/**
|
||||
* Ditto base route class.
|
||||
* Ensures that required variables are set for type safety.
|
||||
*/
|
||||
export class DittoRoute extends Hono<DittoEnv> {
|
||||
constructor(opts: HonoOptions<DittoEnv> = {}) {
|
||||
super(opts);
|
||||
|
||||
this.use((c, next) => {
|
||||
this.assertVars(c.var);
|
||||
return next();
|
||||
});
|
||||
|
||||
this.onError(this._errorHandler);
|
||||
}
|
||||
|
||||
private assertVars(vars: Partial<DittoEnv['Variables']>): DittoEnv['Variables'] {
|
||||
if (!vars.db) this.throwMissingVar('db');
|
||||
if (!vars.conf) this.throwMissingVar('conf');
|
||||
if (!vars.relay) this.throwMissingVar('relay');
|
||||
if (!vars.signal) this.throwMissingVar('signal');
|
||||
|
||||
return {
|
||||
...vars,
|
||||
db: vars.db,
|
||||
conf: vars.conf,
|
||||
relay: vars.relay,
|
||||
signal: vars.signal,
|
||||
};
|
||||
}
|
||||
|
||||
private throwMissingVar(name: string): never {
|
||||
throw new HTTPException(500, { message: `Missing required variable: ${name}` });
|
||||
}
|
||||
|
||||
private _errorHandler: ErrorHandler = (error, c) => {
|
||||
if (error instanceof HTTPException) {
|
||||
if (error.res) {
|
||||
return error.res;
|
||||
} else {
|
||||
return c.json({ error: error.message }, error.status);
|
||||
}
|
||||
}
|
||||
|
||||
return c.json({ error: 'Something went wrong' }, 500);
|
||||
};
|
||||
}
|
||||
7
packages/router/deno.json
Normal file
7
packages/router/deno.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@ditto/router",
|
||||
"version": "1.1.0",
|
||||
"exports": {
|
||||
".": "./mod.ts"
|
||||
}
|
||||
}
|
||||
4
packages/router/mod.ts
Normal file
4
packages/router/mod.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { DittoApp } from './DittoApp.ts';
|
||||
export { DittoRoute } from './DittoRoute.ts';
|
||||
|
||||
export type { DittoEnv } from './DittoEnv.ts';
|
||||
Loading…
Add table
Reference in a new issue