Make all policies accept opts, get rid of envvars
This commit is contained in:
parent
2f4ebfbda2
commit
ae0242fc26
@ -2,23 +2,33 @@ import { Keydb } from '../deps.ts';
|
|||||||
|
|
||||||
import type { Policy } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
const ANTI_DUPLICATION_TTL = Number(Deno.env.get('ANTI_DUPLICATION_TTL') || 60000);
|
interface AntiDuplication {
|
||||||
const ANTI_DUPLICATION_MIN_LENGTH = Number(Deno.env.get('ANTI_DUPLICATION_MIN_LENGTH') || 50);
|
/** Time in ms until a message with this content may be posted again. Default: `60000` (1 minute). */
|
||||||
|
ttl?: number;
|
||||||
|
/** Note text under this limit will be skipped by the policy. Default: `50`. */
|
||||||
|
minLength?: number;
|
||||||
|
/** Database connection string. Default: `sqlite:///tmp/strfry-anti-duplication-policy.sqlite3` */
|
||||||
|
databaseUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent messages with the exact same content from being submitted repeatedly.
|
* Prevent messages with the exact same content from being submitted repeatedly.
|
||||||
* It stores a hashcode for each content in an SQLite database and rate-limits them.
|
* It stores a hashcode for each content in an SQLite database and rate-limits them.
|
||||||
* Only messages that meet the minimum length criteria are selected.
|
* Only messages that meet the minimum length criteria are selected.
|
||||||
*/
|
*/
|
||||||
const antiDuplicationPolicy: Policy = async (msg) => {
|
const antiDuplicationPolicy: Policy<AntiDuplication> = async (msg, opts) => {
|
||||||
|
const ttl = opts?.ttl ?? 60000;
|
||||||
|
const minLength = opts?.minLength ?? 50;
|
||||||
|
const databaseUrl = opts?.databaseUrl || 'sqlite:///tmp/strfry-anti-duplication-policy.sqlite3';
|
||||||
|
|
||||||
const { kind, content } = msg.event;
|
const { kind, content } = msg.event;
|
||||||
|
|
||||||
if (kind === 1 && content.length >= ANTI_DUPLICATION_MIN_LENGTH) {
|
if (kind === 1 && content.length >= minLength) {
|
||||||
const db = new Keydb('sqlite:///tmp/strfry-anti-duplication-policy.sqlite3');
|
const db = new Keydb(databaseUrl);
|
||||||
const hash = String(hashCode(content));
|
const hash = String(hashCode(content));
|
||||||
|
|
||||||
if (await db.get(hash)) {
|
if (await db.get(hash)) {
|
||||||
await db.set(hash, 1, ANTI_DUPLICATION_TTL);
|
await db.set(hash, 1, ttl);
|
||||||
return {
|
return {
|
||||||
id: msg.event.id,
|
id: msg.event.id,
|
||||||
action: 'shadowReject',
|
action: 'shadowReject',
|
||||||
@ -26,7 +36,7 @@ const antiDuplicationPolicy: Policy = async (msg) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.set(hash, 1, ANTI_DUPLICATION_TTL);
|
await db.set(hash, 1, ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -52,3 +62,5 @@ function hashCode(str: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default antiDuplicationPolicy;
|
export default antiDuplicationPolicy;
|
||||||
|
|
||||||
|
export type { AntiDuplication };
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import type { Policy } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
interface Hellthread {
|
interface Hellthread {
|
||||||
limit: number;
|
/** Total number of "p" tags a kind 1 note may have before it's rejected. Default: `100` */
|
||||||
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reject messages that tag too many participants. */
|
/** Reject messages that tag too many participants. */
|
||||||
const hellthreadPolicy: Policy<Hellthread> = (msg, opts) => {
|
const hellthreadPolicy: Policy<Hellthread> = (msg, opts) => {
|
||||||
const limit = opts?.limit || 100;
|
const limit = opts?.limit ?? 100;
|
||||||
|
|
||||||
if (msg.event.kind === 1) {
|
if (msg.event.kind === 1) {
|
||||||
const p = msg.event.tags.filter((tag) => tag[0] === 'p');
|
const p = msg.event.tags.filter((tag) => tag[0] === 'p');
|
||||||
@ -28,3 +29,5 @@ const hellthreadPolicy: Policy<Hellthread> = (msg, opts) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default hellthreadPolicy;
|
export default hellthreadPolicy;
|
||||||
|
|
||||||
|
export type { Hellthread };
|
||||||
|
@ -4,7 +4,7 @@ import type { Policy } from '../types.ts';
|
|||||||
* Minimal sample policy for demonstration purposes.
|
* Minimal sample policy for demonstration purposes.
|
||||||
* Allows all events through.
|
* Allows all events through.
|
||||||
*/
|
*/
|
||||||
const noopPolicy: Policy = (msg) => ({
|
const noopPolicy: Policy<void> = (msg) => ({
|
||||||
id: msg.event.id,
|
id: msg.event.id,
|
||||||
action: 'accept',
|
action: 'accept',
|
||||||
msg: '',
|
msg: '',
|
||||||
|
@ -2,23 +2,34 @@ import { Keydb } from '../deps.ts';
|
|||||||
|
|
||||||
import type { Policy } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
const IP_WHITELIST = (Deno.env.get('IP_WHITELIST') || '').split(',');
|
interface RateLimit {
|
||||||
|
/** How often (ms) to check whether `max` has been exceeded. Default: `60000` (1 minute). */
|
||||||
const RATE_LIMIT_INTERVAL = Number(Deno.env.get('RATE_LIMIT_INTERVAL') || 60000);
|
interval?: number;
|
||||||
const RATE_LIMIT_MAX = Number(Deno.env.get('RATE_LIMIT_MAX') || 10);
|
/** Max number of requests within the `interval` until the IP is rate-limited. Default: `10`. */
|
||||||
|
max?: number;
|
||||||
|
/** List of IP addresses to skip this policy. */
|
||||||
|
whitelist?: string[];
|
||||||
|
/** Database connection string. Default: `sqlite:///tmp/strfry-rate-limit-policy.sqlite3` */
|
||||||
|
databaseUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rate-limits users by their IP address.
|
* Rate-limits users by their IP address.
|
||||||
* IPs are stored in an SQLite database. If you are running internal services,
|
* IPs are stored in an SQLite database. If you are running internal services,
|
||||||
* it's a good idea to at least whitelist `127.0.0.1` etc.
|
* it's a good idea to at least whitelist `127.0.0.1` etc.
|
||||||
*/
|
*/
|
||||||
const rateLimitPolicy: Policy = async (msg) => {
|
const rateLimitPolicy: Policy<RateLimit> = async (msg, opts) => {
|
||||||
if ((msg.sourceType === 'IP4' || msg.sourceType === 'IP6') && !IP_WHITELIST.includes(msg.sourceInfo)) {
|
const interval = opts?.interval ?? 60000;
|
||||||
const db = new Keydb('sqlite:///tmp/strfry-rate-limit-policy.sqlite3');
|
const max = opts?.max ?? 10;
|
||||||
const count = await db.get<number>(msg.sourceInfo) || 0;
|
const whitelist = opts?.whitelist || [];
|
||||||
await db.set(msg.sourceInfo, count + 1, RATE_LIMIT_INTERVAL);
|
const databaseUrl = opts?.databaseUrl || 'sqlite:///tmp/strfry-rate-limit-policy.sqlite3';
|
||||||
|
|
||||||
if (count >= RATE_LIMIT_MAX) {
|
if ((msg.sourceType === 'IP4' || msg.sourceType === 'IP6') && !whitelist.includes(msg.sourceInfo)) {
|
||||||
|
const db = new Keydb(databaseUrl);
|
||||||
|
const count = await db.get<number>(msg.sourceInfo) || 0;
|
||||||
|
await db.set(msg.sourceInfo, count + 1, interval);
|
||||||
|
|
||||||
|
if (count >= max) {
|
||||||
return {
|
return {
|
||||||
id: msg.event.id,
|
id: msg.event.id,
|
||||||
action: 'reject',
|
action: 'reject',
|
||||||
@ -35,3 +46,5 @@ const rateLimitPolicy: Policy = async (msg) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default rateLimitPolicy;
|
export default rateLimitPolicy;
|
||||||
|
|
||||||
|
export type { RateLimit };
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { Policy } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
/** This policy rejects all messages. */
|
/** This policy rejects all messages. */
|
||||||
const readOnlyPolicy: Policy = (msg) => ({
|
const readOnlyPolicy: Policy<void> = (msg) => ({
|
||||||
id: msg.event.id,
|
id: msg.event.id,
|
||||||
action: 'reject',
|
action: 'reject',
|
||||||
msg: 'The relay is read-only.',
|
msg: 'The relay is read-only.',
|
||||||
|
Loading…
Reference in New Issue
Block a user