spec(aiscript): Mk:apiの呼び出しにエンドポイントごとのレートリミットを設定 (MisskeyIO#522)

This commit is contained in:
まっちゃとーにゅ 2024-03-15 14:16:48 +09:00 committed by GitHub
parent 9d1cdadccc
commit cd7ab5d0f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 624 additions and 540 deletions

View File

@ -46,17 +46,17 @@
"cleanall": "pnpm clean-all"
},
"resolutions": {
"@tensorflow/tfjs-core": "4.17.0",
"chokidar": "3.6.0",
"lodash": "4.17.21",
"sharp": "0.33.2",
"@tensorflow/tfjs-core": "4.17.0"
"sharp": "0.33.2"
},
"dependencies": {
"cssnano": "6.1.0",
"execa": "8.0.1",
"js-yaml": "4.1.0",
"postcss": "8.4.35",
"terser": "5.29.1",
"terser": "5.29.2",
"typescript": "5.4.2"
},
"devDependencies": {

View File

@ -66,8 +66,8 @@
},
"dependencies": {
"@authenio/samlify-node-xmllint": "2.0.0",
"@aws-sdk/client-s3": "3.533.0",
"@aws-sdk/lib-storage": "3.533.0",
"@aws-sdk/client-s3": "3.534.0",
"@aws-sdk/lib-storage": "3.534.0",
"@bull-board/api": "5.15.1",
"@bull-board/fastify": "5.15.1",
"@bull-board/ui": "5.15.1",
@ -89,7 +89,7 @@
"@peertube/http-signature": "1.7.0",
"@simplewebauthn/server": "9.0.3",
"@sinonjs/fake-timers": "11.2.2",
"@smithy/node-http-handler": "2.4.3",
"@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.1.65",
"@swc/core": "1.3.107",
"@twemoji/parser": "15.0.0",

View File

@ -19,6 +19,7 @@
"dependencies": {
"@discordapp/twemoji": "15.0.2",
"@github/webauthn-json": "2.1.1",
"@isaacs/ttlcache": "1.4.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0",

View File

@ -11,6 +11,7 @@ import { miLocalStorage } from '@/local-storage.js';
import { customEmojis } from '@/custom-emojis.js';
import { url, lang } from '@/config.js';
import { nyaize } from '@/scripts/nyaize.js';
import { RateLimiter } from '@/scripts/rate-limiter.js';
export function aiScriptReadline(q: string): Promise<string> {
return new Promise(ok => {
@ -23,6 +24,8 @@ export function aiScriptReadline(q: string): Promise<string> {
}
export function createAiScriptEnv(opts) {
const rateLimiter = new RateLimiter<string>({ duration: 1000 * 15, max: 30 });
return {
USER_ID: $i ? values.STR($i.id) : values.NULL,
USER_NAME: $i ? values.STR($i.name) : values.NULL,
@ -55,6 +58,7 @@ export function createAiScriptEnv(opts) {
if (typeof token.value !== 'string') throw new Error('invalid token');
}
const actualToken: string|null = token?.value ?? opts.token ?? null;
if (!rateLimiter.hit(ep.value)) return values.ERROR('rate_limited', values.NULL);
return misskeyApi(ep.value, utils.valToJs(param), actualToken).then(res => {
return utils.jsToVal(res);
}, err => {

View File

@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: Isaac Z. Schlueter and Contributors of https://github.com/isaacs/ttlcache
* SPDX-License-Identifier: ISC
*
* This file is derived from the project that has licensed under the ISC license.
* This file SHOULD NOT be considered as a part of the this project that has licensed under AGPL-3.0-only
* Adapted from https://github.com/isaacs/ttlcache/blob/b6002f971e122e3b35e23d00ac6a8365d505c14d/examples/rate-limiter-window.ts
*/
import TTLCache from '@isaacs/ttlcache';
import type { Options as TTLCacheOptions } from '@isaacs/ttlcache';
export interface Options {
duration: number;
max: number;
}
interface RLEntryOptions extends TTLCacheOptions<number, boolean> {
onEmpty: () => void;
}
class RLEntry extends TTLCache<number, boolean> {
onEmpty: () => void;
constructor(options: RLEntryOptions) {
super(options);
this.onEmpty = options.onEmpty;
}
purgeStale() {
const ret = super.purgeStale();
if (this.size === 0 && ret) {
this.onEmpty();
}
return ret;
}
}
export class RateLimiter<K> extends Map<K, TTLCache<number, boolean>> {
duration: number;
max: number;
constructor(options: Options) {
super();
this.duration = options.duration;
this.max = options.max;
}
hit(key: K) {
const c =
super.get(key) ??
new RLEntry({
ttl: this.duration,
onEmpty: () => this.delete(key),
});
this.set(key, c);
if (c.size > this.max) {
// rejected, too many hits within window
return false;
}
c.set(performance.now(), true);
return true;
}
count(key: K) {
const c = super.get(key);
return c ? c.size : 0;
}
}

View File

@ -38,7 +38,7 @@
"built"
],
"dependencies": {
"esbuild": "0.20.1",
"esbuild": "0.20.2",
"eventemitter3": "5.0.1",
"glob": "^10.3.10",
"matter-js": "0.19.0",

View File

@ -34,7 +34,7 @@
},
"dependencies": {
"crc-32": "1.2.2",
"esbuild": "0.20.1",
"esbuild": "0.20.2",
"glob": "10.3.10"
},
"files": [

View File

@ -9,7 +9,7 @@
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"esbuild": "0.20.1",
"esbuild": "0.20.2",
"idb-keyval": "6.2.1",
"misskey-js": "workspace:*"
},

File diff suppressed because it is too large Load Diff