diff --git a/@types/Server.ts b/@types/Server.ts index 1df46ae..33295ca 100644 --- a/@types/Server.ts +++ b/@types/Server.ts @@ -1,11 +1,11 @@ -import { AbstractUser } from "../src/server/interfaces/index.js"; +import { AbstractUser } from '../src/server/interfaces/index.js'; import { LoggerClientOptions } from '@navy.gif/logger'; import { BrokerOptions, MariaOptions, MongoOptions, ObjectId } from '@navy.gif/wrappers'; -import { Request as ExpressRequest, Response as ExpressResponse, NextFunction } from "express"; +import { Request as ExpressRequest, Response as ExpressResponse, NextFunction } from 'express'; import http from 'http'; -import { UploadedFile } from "express-fileupload"; -import { Session } from "express-session"; -import Role from "../src/server/structures/Role.js"; +import { UploadedFile } from 'express-fileupload'; +import { Session } from 'express-session'; +import Role from '../src/server/structures/Role.js'; /** * TERMINOLOGY @@ -60,7 +60,8 @@ export type ServerOptions = { discord: { scope?: string[], version?: number - } + }, + sweeperInterval?: number } export type Permissions = { diff --git a/src/server/Server.ts b/src/server/Server.ts index cd76e62..d4c031c 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -25,9 +25,12 @@ import { MongoMemory } from './database/index.js'; import { IPCMessage, SignupCode } from '../../@types/Other.js'; import { DoneCallback } from 'passport'; import FlagManager from './components/FlagManager.js'; +import { Collection } from '@discordjs/collection'; // const pkg = JSON.parse(readFileSync('../../package.json', { encoding: 'utf8' })); +type Sweeper = () => Promise | void + /** * Meant to be deployed behind a proxy (e.g. nginx) instance that takes care of certs and load balancing, trusts the first proxy * @@ -67,6 +70,9 @@ class Server extends EventEmitter #logger: LoggerClient; + #sweepers: Collection; + #sweeperInterval!: NodeJS.Timer; + // eslint-disable-next-line max-lines-per-function constructor (options: ServerOptions) { @@ -224,6 +230,8 @@ class Server extends EventEmitter this.#app.use(this.#attachMisc.bind(this) as never); this.#app.use(this.#logRequest.bind(this) as never); // Logs every request + this.#sweepers = new Collection(); + process.on('message', this._handleMessage.bind(this)); process.on('SIGINT', this.shutdown.bind(this)); @@ -264,6 +272,12 @@ class Server extends EventEmitter this.#logger.debug('Registered permissions:'); this.#logger.debug(PermissionManager.DefaultPermissions); + if (this.shardId === 0) + { + this.#logger.info('Starting sweeper'); + this.#sweeperInterval = setInterval(this.#runSweepers.bind(this), this.#options.sweeperInterval ?? 30 * 60 * 1000); + } + this.#logger.status(`Server created, took ${Date.now() - start} ms, listening on port ${this.port}`); if (process.send) process.send({ _ready: true }); @@ -271,6 +285,25 @@ class Server extends EventEmitter } + registerSweeper (name: string, sweeper: Sweeper) + { + this.#logger.info(`Registering sweeper ${name}`); + if (!sweeper.name.includes('Bound')) + this.#logger.warn(`Sweeper ${name} is not bound, may cause issues if 'this' is referenced`); + this.#sweepers.set(name, sweeper); + } + + async #runSweepers () + { + const sweepers = this.#sweepers.entries(); + for (const [ name, sweeper ] of sweepers) + { + this.#logger.info(`Running sweeper ${name}`); + await sweeper(); + } + } + + // Middleware #attachMisc (req: Request, _res: Response, next: NextFunction) { req.remoteAddress = req.get('X-Forwarded-For') || req.socket.remoteAddress as string; @@ -310,6 +343,7 @@ class Server extends EventEmitter return void res.status(503).send('Server not ready'); next(); } + // END Middleware async shutdown () { @@ -319,6 +353,7 @@ class Server extends EventEmitter process.send({ _shutdown: true }); this.#_ready = false; + clearInterval(this.#sweeperInterval); // Close http server first and allow it to finish serving requests if (this.#server) {