Rewrite all policies as policy modules
This commit is contained in:
parent
64413d572b
commit
78fae38268
4
entrypoint.example.ts
Normal file
4
entrypoint.example.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
//bin/true; exec deno run -A "$0" "$@"
|
||||||
|
|
||||||
|
// TODO
|
3
src/pipeline.ts
Normal file
3
src/pipeline.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { readStdin } from './stdin.ts';
|
||||||
|
|
||||||
|
console.log(await readStdin());
|
@ -1,24 +1,16 @@
|
|||||||
#!/bin/sh
|
import { Keydb } from '../deps.ts';
|
||||||
//bin/true; exec deno run -A "$0" "$@"
|
|
||||||
import { Keydb, readLines } from '../deps.ts';
|
|
||||||
|
|
||||||
import type { InputMessage, OutputMessage } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
const ANTI_DUPLICATION_TTL = Number(Deno.env.get('ANTI_DUPLICATION_TTL') || 60000);
|
const ANTI_DUPLICATION_TTL = Number(Deno.env.get('ANTI_DUPLICATION_TTL') || 60000);
|
||||||
const ANTI_DUPLICATION_MIN_LENGTH = Number(Deno.env.get('ANTI_DUPLICATION_MIN_LENGTH') || 50);
|
const ANTI_DUPLICATION_MIN_LENGTH = Number(Deno.env.get('ANTI_DUPLICATION_MIN_LENGTH') || 50);
|
||||||
|
|
||||||
/** https://stackoverflow.com/a/8831937 */
|
/**
|
||||||
function hashCode(str: string): number {
|
* Prevent messages with the exact same content from being submitted repeatedly.
|
||||||
let hash = 0;
|
* It stores a hashcode for each content in an SQLite database and rate-limits them.
|
||||||
for (let i = 0, len = str.length; i < len; i++) {
|
* Only messages that meet the minimum length criteria are selected.
|
||||||
const chr = str.charCodeAt(i);
|
*/
|
||||||
hash = (hash << 5) - hash + chr;
|
const antiDuplicationPolicy: Policy = async (msg) => {
|
||||||
hash |= 0; // Convert to 32bit integer
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleMessage(msg: InputMessage): Promise<OutputMessage> {
|
|
||||||
const { kind, content } = msg.event;
|
const { kind, content } = msg.event;
|
||||||
|
|
||||||
if (kind === 1 && content.length >= ANTI_DUPLICATION_MIN_LENGTH) {
|
if (kind === 1 && content.length >= ANTI_DUPLICATION_MIN_LENGTH) {
|
||||||
@ -42,8 +34,21 @@ async function handleMessage(msg: InputMessage): Promise<OutputMessage> {
|
|||||||
action: 'accept',
|
action: 'accept',
|
||||||
msg: '',
|
msg: '',
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a "good enough" unique identifier for this content.
|
||||||
|
* This algorithm was chosen because it's very fast with a low chance of collisions.
|
||||||
|
* https://stackoverflow.com/a/8831937
|
||||||
|
*/
|
||||||
|
function hashCode(str: string): number {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0, len = str.length; i < len; i++) {
|
||||||
|
const chr = str.charCodeAt(i);
|
||||||
|
hash = (hash << 5) - hash + chr;
|
||||||
|
hash |= 0; // Convert to 32bit integer
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
export default antiDuplicationPolicy;
|
||||||
console.log(JSON.stringify(await handleMessage(JSON.parse(line))));
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
#!/usr/bin/env -S deno run
|
import type { Policy } from '../types.ts';
|
||||||
import { readLines } from '../deps.ts';
|
|
||||||
|
|
||||||
import type { InputMessage, OutputMessage } from '../types.ts';
|
|
||||||
|
|
||||||
const HELLTHREAD_LIMIT = Number(Deno.env.get('HELLTHREAD_LIMIT') || 100);
|
const HELLTHREAD_LIMIT = Number(Deno.env.get('HELLTHREAD_LIMIT') || 100);
|
||||||
|
|
||||||
function handleMessage(msg: InputMessage): OutputMessage {
|
/** Reject messages that tag too many participants. */
|
||||||
|
const hellthreadPolicy: Policy = (msg) => {
|
||||||
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');
|
||||||
|
|
||||||
@ -23,8 +21,6 @@ function handleMessage(msg: InputMessage): OutputMessage {
|
|||||||
action: 'accept',
|
action: 'accept',
|
||||||
msg: '',
|
msg: '',
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
export default hellthreadPolicy;
|
||||||
console.log(JSON.stringify(handleMessage(JSON.parse(line))));
|
|
||||||
}
|
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
#!/usr/bin/env -S deno run
|
import type { Policy } from '../types.ts';
|
||||||
import { readLines } from '../deps.ts';
|
|
||||||
|
|
||||||
import type { InputMessage, OutputMessage } from '../types.ts';
|
/**
|
||||||
|
* Minimal sample policy for demonstration purposes.
|
||||||
|
* Allows all events through.
|
||||||
|
*/
|
||||||
|
const noopPolicy: Policy = (msg) => ({
|
||||||
|
id: msg.event.id,
|
||||||
|
action: 'accept',
|
||||||
|
msg: '',
|
||||||
|
});
|
||||||
|
|
||||||
function handleMessage(msg: InputMessage): OutputMessage {
|
export default noopPolicy;
|
||||||
return {
|
|
||||||
id: msg.event.id,
|
|
||||||
action: 'accept',
|
|
||||||
msg: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
|
||||||
console.log(JSON.stringify(handleMessage(JSON.parse(line))));
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
#!/bin/sh
|
import { Keydb } from '../deps.ts';
|
||||||
//bin/true; exec deno run -A "$0" "$@"
|
|
||||||
import { Keydb, readLines } from '../deps.ts';
|
|
||||||
|
|
||||||
import type { InputMessage, OutputMessage } from '../types.ts';
|
import type { Policy } from '../types.ts';
|
||||||
|
|
||||||
const IP_WHITELIST = (Deno.env.get('IP_WHITELIST') || '').split(',');
|
const IP_WHITELIST = (Deno.env.get('IP_WHITELIST') || '').split(',');
|
||||||
|
|
||||||
const RATE_LIMIT_INTERVAL = Number(Deno.env.get('RATE_LIMIT_INTERVAL') || 60000);
|
const RATE_LIMIT_INTERVAL = Number(Deno.env.get('RATE_LIMIT_INTERVAL') || 60000);
|
||||||
const RATE_LIMIT_MAX = Number(Deno.env.get('RATE_LIMIT_MAX') || 10);
|
const RATE_LIMIT_MAX = Number(Deno.env.get('RATE_LIMIT_MAX') || 10);
|
||||||
|
|
||||||
async function handleMessage(msg: InputMessage): Promise<OutputMessage> {
|
/**
|
||||||
|
* Rate-limits users by their IP address.
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
const rateLimitPolicy: Policy = async (msg) => {
|
||||||
if ((msg.sourceType === 'IP4' || msg.sourceType === 'IP6') && !IP_WHITELIST.includes(msg.sourceInfo)) {
|
if ((msg.sourceType === 'IP4' || msg.sourceType === 'IP6') && !IP_WHITELIST.includes(msg.sourceInfo)) {
|
||||||
const db = new Keydb('sqlite:///tmp/strfry-rate-limit-policy.sqlite3');
|
const db = new Keydb('sqlite:///tmp/strfry-rate-limit-policy.sqlite3');
|
||||||
const count = await db.get<number>(msg.sourceInfo) || 0;
|
const count = await db.get<number>(msg.sourceInfo) || 0;
|
||||||
@ -29,8 +32,6 @@ async function handleMessage(msg: InputMessage): Promise<OutputMessage> {
|
|||||||
action: 'accept',
|
action: 'accept',
|
||||||
msg: '',
|
msg: '',
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
export default rateLimitPolicy;
|
||||||
console.log(JSON.stringify(await handleMessage(JSON.parse(line))));
|
|
||||||
}
|
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
#!/bin/sh
|
import type { Policy } from '../types.ts';
|
||||||
//bin/true; exec deno run -A "$0" "$@"
|
|
||||||
import { readLines } from '../deps.ts';
|
|
||||||
|
|
||||||
import type { InputMessage, OutputMessage } from '../types.ts';
|
/** This policy rejects all messages. */
|
||||||
|
const readOnlyPolicy: Policy = (msg) => ({
|
||||||
|
id: msg.event.id,
|
||||||
|
action: 'reject',
|
||||||
|
msg: 'The relay is read-only.',
|
||||||
|
});
|
||||||
|
|
||||||
function handleMessage(msg: InputMessage): OutputMessage {
|
export default readOnlyPolicy;
|
||||||
return {
|
|
||||||
id: msg.event.id,
|
|
||||||
action: 'reject',
|
|
||||||
msg: 'The relay is set to read-only.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
|
||||||
console.log(JSON.stringify(handleMessage(JSON.parse(line))));
|
|
||||||
}
|
|
||||||
|
14
src/stdin.ts
Normal file
14
src/stdin.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { readLines } from './deps.ts';
|
||||||
|
|
||||||
|
import type { InputMessage } from './types.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first line from stdin.
|
||||||
|
* Can only be read ONCE, or else it returns undefined.
|
||||||
|
*/
|
||||||
|
async function readStdin(): Promise<InputMessage> {
|
||||||
|
const { value } = await readLines(Deno.stdin).next();
|
||||||
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { readStdin };
|
@ -22,4 +22,6 @@ interface Event<K extends number = number> {
|
|||||||
created_at: number;
|
created_at: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type { Event, InputMessage, OutputMessage };
|
type Policy = (msg: InputMessage) => Promise<OutputMessage> | OutputMessage;
|
||||||
|
|
||||||
|
export type { Event, InputMessage, OutputMessage, Policy };
|
||||||
|
Loading…
Reference in New Issue
Block a user