refactor: removing governor

This commit is contained in:
cha0s 2024-01-28 13:59:39 -06:00
parent 9fc916622f
commit d157b3fb86
11 changed files with 1 additions and 557 deletions

View File

@ -15,7 +15,6 @@
- '($1#L$2)'
'@flecks/electron:./packages/electron': {}
'@flecks/fleck:./packages/fleck': {}
'@flecks/governor:./packages/governor': {}
'@flecks/passport:./packages/passport': {}
'@flecks/passport-local:./packages/passport-local': {}
'@flecks/passport-local-react:./packages/passport-local-react': {}

View File

@ -1,116 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -1,139 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.1.3](https://github.com/cha0s/flecks/compare/v3.1.1...v3.1.3) (2024-01-26)
**Note:** Version bump only for package @flecks/governor
## [3.1.1](https://github.com/cha0s/flecks/compare/v3.1.0...v3.1.1) (2024-01-26)
**Note:** Version bump only for package @flecks/governor
# [3.1.0](https://github.com/cha0s/flecks/compare/v1.4.1...v3.1.0) (2024-01-26)
### Bug Fixes
* lint ([675aaad](https://github.com/cha0s/flecks/commit/675aaadaedd539ebbef9ab0ace9e9ae8832d1e08))
* priority ([841c2fa](https://github.com/cha0s/flecks/commit/841c2faaa14a9385fa18a846e36a4770fc025166))
### Features
* **fleck:** ProcessAssets ([84a30df](https://github.com/cha0s/flecks/commit/84a30df67ab0c8b5c34bb61657f6103775839e82))
* webpack 5 ([f039f7b](https://github.com/cha0s/flecks/commit/f039f7b8f69b3c8b9564714890b2fe2d4cd8a22d))
## [2.0.3](https://github.com/cha0s/flecks/compare/v2.0.2...v2.0.3) (2023-11-22)
### Bug Fixes
* electron ([81fc978](https://github.com/cha0s/flecks/commit/81fc978da2b8d32e303d165fe7c2f6071ac8e741))
## [2.0.2](https://github.com/cha0s/flecks/compare/v2.0.1...v2.0.2) (2023-11-22)
**Note:** Version bump only for package @flecks/governor
## [2.0.1](https://github.com/cha0s/flecks/compare/v2.0.0...v2.0.1) (2023-11-22)
**Note:** Version bump only for package @flecks/governor
# [2.0.0](https://github.com/cha0s/flecks/compare/v1.4.1...v2.0.0) (2023-11-22)
### Features
* webpack 5 support ([288b368](https://github.com/cha0s/flecks/commit/288b368b9ff96be5ccb58bd811838a4a4bb6c48c))
## [1.4.1](https://github.com/cha0s/flecks/compare/v1.4.0...v1.4.1) (2022-03-19)
**Note:** Version bump only for package @flecks/governor
# [1.4.0](https://github.com/cha0s/flecks/compare/v1.3.0...v1.4.0) (2022-03-19)
**Note:** Version bump only for package @flecks/governor
# [1.3.0](https://github.com/cha0s/flecks/compare/v1.2.1...v1.3.0) (2022-03-09)
**Note:** Version bump only for package @flecks/governor
## [1.2.1](https://github.com/cha0s/flecks/compare/v1.2.0...v1.2.1) (2022-03-08)
**Note:** Version bump only for package @flecks/governor
# [1.2.0](https://github.com/cha0s/flecks/compare/v1.1.1...v1.2.0) (2022-03-07)
### Bug Fixes
* old redis ([0a8c66a](https://github.com/cha0s/flecks/commit/0a8c66ad7d6da5d63563c40c509f5a165146d7be))
## [1.1.1](https://github.com/cha0s/flecks/compare/v1.1.0...v1.1.1) (2022-02-28)
**Note:** Version bump only for package @flecks/governor
# [1.1.0](https://github.com/cha0s/flecks/compare/v1.0.2...v1.1.0) (2022-02-28)
**Note:** Version bump only for package @flecks/governor
## [1.0.2](https://github.com/cha0s/flecks/compare/v1.0.1...v1.0.2) (2022-02-28)
**Note:** Version bump only for package @flecks/governor

View File

@ -1,35 +0,0 @@
{
"name": "@flecks/governor",
"version": "3.1.3",
"author": "cha0s",
"license": "MIT",
"scripts": {
"build": "flecks build",
"clean": "flecks clean",
"lint": "flecks lint",
"postversion": "npm run build",
"test": "flecks test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cha0s/flecks.git",
"directory": "packages/governor"
},
"publishConfig": {
"access": "public"
},
"files": [
"client.js",
"server.js"
],
"dependencies": {
"@flecks/core": "^3.1.0",
"@flecks/db": "^3.1.3",
"@flecks/redis": "^3.1.3",
"rate-limiter-flexible": "^2.1.13"
},
"devDependencies": {
"@flecks/build": "^3.1.3",
"@flecks/fleck": "^3.1.3"
}
}

View File

@ -1,2 +0,0 @@
// eslint-disable-next-line import/prefer-default-export
export {default as createLimiter} from './limiter';

View File

@ -1,3 +0,0 @@
import RateLimiterMemory from 'rate-limiter-flexible/lib/RateLimiterMemory';
export default (flecks, options) => new RateLimiterMemory(options);

View File

@ -1,29 +0,0 @@
export default (flecks, [name, Packet]) => {
const {ValidationError} = flecks.fleck('@flecks/socket');
return class LimitedPacket extends Packet {
constructor(...args) {
super(...args);
this.limit = flecks.governor.packet[name];
}
static async validate(packet, socket) {
try {
await packet.limit.consume(socket.id);
}
catch (error) {
if (error.msBeforeNext) {
throw new ValidationError({
code: 429,
ttr: Math.round(error.msBeforeNext / 1000) || 1,
});
}
throw error;
}
if (super.validate) {
await super.validate(packet, socket);
}
}
};
};

View File

@ -1,13 +0,0 @@
import {createClient} from '@flecks/redis/server';
import {RateLimiterRedis} from 'rate-limiter-flexible';
export default async (flecks, options) => {
const storeClient = await createClient(flecks);
const legacyClient = storeClient.duplicate({legacyMode: true});
await legacyClient.connect();
return new RateLimiterRedis({
...options,
// @todo node-redis@4
storeClient: legacyClient,
});
};

View File

@ -1,72 +0,0 @@
export default (flecks) => {
const {Model, Op, Types} = flecks.fleck('@flecks/db/server');
const {config: {'@flecks/governor/server': {keys}}} = flecks;
return class Ban extends Model {
static get attributes() {
return {
ttl: {
type: Types.INTEGER,
defaultValue: 0,
},
...Object.fromEntries(keys.map((key) => ([key, {type: Types.STRING}]))),
};
}
static async check(req) {
const ban = this.fromRequest(req, keys);
const candidates = Object.entries(ban)
.reduce((r, [key, value]) => [...r, {[key]: value}], []);
const where = {
where: {
[Op.or]: candidates,
},
};
const bans = await this.findAll(where);
const pruned = bans
.reduce((r, ban) => {
if (ban && ban.ttl > 0) {
const expiresAt = new Date(ban.createdAt);
expiresAt.setSeconds(expiresAt.getSeconds() + ban.ttl);
if (Date.now() >= expiresAt.getTime()) {
this.destroy({where: {id: ban.id}});
return [...r, null];
}
ban.ttl = Math.ceil((expiresAt.getTime() - Date.now()) / 1000);
}
return [...r, ban];
}, [])
.filter((ban) => !!ban)
.map((ban) => {
const {ttl, ...json} = ban.toJSON();
return json;
});
if (0 === pruned.length) {
return;
}
throw new Error(this.format(pruned));
}
static format(bans) {
return [
'bans = [',
bans.map((ban) => {
const entries = Object.entries(ban)
.filter(([key]) => -1 === ['id', 'createdAt', 'updatedAt'].indexOf(key));
return [
' {',
entries.map(([key, value]) => ` ${key}: ${value},`).join('\n'),
' },',
].join('\n');
}).join('\n'),
'];',
].join('\n');
}
static fromRequest(req, keys, ttl = 0) {
return keys.reduce((r, key) => ({...r, [key]: req[key]}), ttl ? {ttl} : {});
}
};
};

View File

@ -1,146 +0,0 @@
import {ByType, Flecks} from '@flecks/core';
import {RateLimiterRes} from 'rate-limiter-flexible';
import LimitedPacket from './limited-packet';
import createLimiter from './limiter';
export {default as createLimiter} from './limiter';
export const hooks = {
'@flecks/core.config': () => ({
/**
* All keys used to determine fingerprint.
*/
keys: ['ip'],
web: {
keys: ['ip'],
points: 60,
duration: 30,
ttl: 30,
},
socket: {
keys: ['ip'],
points: 60,
duration: 30,
ttl: 30,
},
}),
'@flecks/db.models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/web/server.request.route': Flecks.priority(
(flecks) => {
const {web} = flecks.get('@flecks/governor/server');
return async (req, res, next) => {
const {Ban} = flecks.db.Models;
try {
await Ban.check(req);
}
catch (error) {
res.status(403).send(`<pre>${error.message}</pre>`);
return;
}
req.ban = async (keys, ttl = 0) => {
const ban = Ban.fromRequest(req, keys, ttl);
await Ban.create({...ban});
res.status(403).send(`<pre>${Ban.format([ban])}</pre>`);
};
try {
await flecks.governor.web.consume(req.ip);
next();
}
catch (error) {
if (!(error instanceof RateLimiterRes)) {
throw error;
}
const {ttl, keys} = web;
const ban = Ban.fromRequest(req, keys, ttl);
await Ban.create({...ban});
res.status(429).send(`<pre>${Ban.format([ban])}</pre>`);
}
};
},
{after: '@flecks/passport/server'},
),
'@flecks/server.up': Flecks.priority(
async (flecks) => {
if (flecks.fleck('@flecks/web/server')) {
const {web} = flecks.get('@flecks/governor/server');
flecks.governor.web = await createLimiter(
flecks,
{
keyPrefix: '@flecks/governor.web.request.route',
...web,
},
);
}
if (flecks.fleck('@flecks/socket/server')) {
flecks.governor.packet = Object.fromEntries(
await Promise.all(
Object.entries(flecks.socket.Packets[ByType])
.filter(([, Packet]) => Packet.limit)
.map(async ([name, Packet]) => (
[
name,
await createLimiter(
flecks,
{keyPrefix: `@flecks/governor.packet.${name}`, ...Packet.limit},
),
]
)),
),
);
const {socket} = flecks.get('@flecks/governor/server');
flecks.governor.socket = await createLimiter(
flecks,
{
keyPrefix: '@flecks/governor.socket.request.socket',
...socket,
},
);
}
},
{before: '@flecks/web/server', after: '@flecks/redis/server'},
),
'@flecks/socket/server.request.socket': (flecks) => (
async (socket, next) => {
const {handshake: req} = socket;
const {Ban} = flecks.db.Models;
try {
await Ban.check(req);
}
catch (error) {
next(error);
return;
}
req.ban = async (keys, ttl) => {
await Ban.create(Ban.fromRequest(req, keys, ttl));
socket.disconnect();
};
try {
await flecks.governor.socket.consume(req.ip);
next();
}
catch (error) {
if (!(error instanceof RateLimiterRes)) {
throw error;
}
const {ttl, keys} = socket;
await Ban.create(Ban.fromRequest(req, keys, ttl));
next(error);
}
}
),
'@flecks/socket.packets.decorate': (Packets, flecks) => (
Object.fromEntries(
Object.entries(Packets).map(([keyPrefix, Packet]) => [
keyPrefix,
!Packet.limit ? Packet : LimitedPacket(flecks, [keyPrefix, Packet]),
]),
)
),
};
export const mixin = (Flecks) => class FlecksWithGovernor extends Flecks {
governor = {};
};

View File

@ -82,7 +82,7 @@ export const hooks = {
});
});
},
{after: '@flecks/session/server', before: '@flecks/governor/server'},
{after: '@flecks/session/server'},
),
'@flecks/web.routes': (flecks) => {
const {