critical bugfix to user creation/updates
rename some class members
This commit is contained in:
parent
9e5fd57971
commit
bc82da67a2
@ -17,22 +17,22 @@ class Shard extends EventEmitter
|
||||
{
|
||||
|
||||
// #_controller: Controller;
|
||||
#_id: number;
|
||||
#_filePath: string;
|
||||
#_args: string[];
|
||||
#_execArgv: string[];
|
||||
#_env: { [key: string]: string };
|
||||
#_respawn: boolean;
|
||||
#_serverOptions: ServerOptions;
|
||||
#id: number;
|
||||
#filePath: string;
|
||||
#args: string[];
|
||||
#execArgv: string[];
|
||||
#env: { [key: string]: string };
|
||||
#respawn: boolean;
|
||||
#serverOptions: ServerOptions;
|
||||
|
||||
#_ready: boolean;
|
||||
#_process: ChildProcess | null;
|
||||
#_fatal: boolean;
|
||||
#ready: boolean;
|
||||
#process: ChildProcess | null;
|
||||
#fatal: boolean;
|
||||
|
||||
#_crashes: number[];
|
||||
#_spawnedAt: number;
|
||||
#_awaitingShutdown: (() => void) | null;
|
||||
#_awaitingResponse: Map<string, (args: IPCMessage) => void>;
|
||||
#crashes: number[];
|
||||
#spawnedAt: number;
|
||||
#awaitingShutdown: (() => void) | null;
|
||||
#awaitingResponse: Map<string, (args: IPCMessage) => void>;
|
||||
|
||||
constructor (_controller: Controller, id: number, options: ShardOptions)
|
||||
{
|
||||
@ -41,56 +41,56 @@ class Shard extends EventEmitter
|
||||
// this.#_controller = controller;
|
||||
if (typeof id !== 'number' || isNaN(id))
|
||||
throw new Error('Missing ID');
|
||||
this.#_id = id;
|
||||
this.#id = id;
|
||||
|
||||
if (!options?.path)
|
||||
throw new Error('Missing path to file to fork');
|
||||
this.#_filePath = options.path;
|
||||
this.#_args = options.args || [];
|
||||
this.#_execArgv = options.execArgv || [];
|
||||
this.#_env = options.env || {};
|
||||
this.#_respawn = options.respawn || false;
|
||||
this.#_serverOptions = options.serverOptions || {} as ServerOptions;
|
||||
this.#_serverOptions.dir = path.resolve(options.path, '..');
|
||||
this.#filePath = options.path;
|
||||
this.#args = options.args || [];
|
||||
this.#execArgv = options.execArgv || [];
|
||||
this.#env = options.env || {};
|
||||
this.#respawn = options.respawn || false;
|
||||
this.#serverOptions = options.serverOptions || {} as ServerOptions;
|
||||
this.#serverOptions.dir = path.resolve(options.path, '..');
|
||||
|
||||
this.#_ready = false;
|
||||
this.#_process = null;
|
||||
this.#_fatal = false;
|
||||
this.#ready = false;
|
||||
this.#process = null;
|
||||
this.#fatal = false;
|
||||
|
||||
// Keep track of crashes for preventing crash loops
|
||||
this.#_crashes = [];
|
||||
this.#crashes = [];
|
||||
// Set in the spawn method
|
||||
this.#_spawnedAt = Date.now(); // Gets re-set once actually spawned
|
||||
this.#spawnedAt = Date.now(); // Gets re-set once actually spawned
|
||||
|
||||
this.#_awaitingShutdown = null;
|
||||
this.#awaitingShutdown = null;
|
||||
|
||||
this.#_awaitingResponse = new Map();
|
||||
this.#awaitingResponse = new Map();
|
||||
|
||||
}
|
||||
|
||||
get id ()
|
||||
{
|
||||
return this.#_id;
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
get fatal ()
|
||||
{
|
||||
return this.#_fatal;
|
||||
return this.#fatal;
|
||||
}
|
||||
|
||||
get process ()
|
||||
{
|
||||
return this.#_process;
|
||||
return this.#process;
|
||||
}
|
||||
|
||||
get ready ()
|
||||
{
|
||||
return this.#_ready;
|
||||
return this.#ready;
|
||||
}
|
||||
|
||||
get spawnedAt ()
|
||||
{
|
||||
return this.#_spawnedAt;
|
||||
return this.#spawnedAt;
|
||||
}
|
||||
|
||||
async spawn (waitForReady = false)
|
||||
@ -101,24 +101,24 @@ class Shard extends EventEmitter
|
||||
if (this.process)
|
||||
throw new Error(`[shard-${this.id}] A process for this shard already exists!`);
|
||||
|
||||
this.#_process = fork(this.#_filePath, this.#_args, {
|
||||
this.#process = fork(this.#filePath, this.#args, {
|
||||
env: {
|
||||
...this.#_env,
|
||||
...this.#env,
|
||||
SHARD_ID: this.id.toString()
|
||||
},
|
||||
execArgv: this.#_execArgv
|
||||
execArgv: this.#execArgv
|
||||
})
|
||||
.on('message', this._handleMessage.bind(this))
|
||||
.on('exit', this._handleExit.bind(this))
|
||||
.on('disconnect', this._handleDisconnect.bind(this)); // Don't know if this is going to help, but monitoring whether this gets called whenever a process on its own closes the IPC channel
|
||||
|
||||
this.#_process.once('spawn', () =>
|
||||
this.#process.once('spawn', () =>
|
||||
{
|
||||
this.emit('spawn');
|
||||
if (!this.#_process)
|
||||
if (!this.#process)
|
||||
throw new Error('Shut up TS');
|
||||
this.#_process.send({ _start: this.#_serverOptions });
|
||||
this.#_spawnedAt = Date.now();
|
||||
this.#process.send({ _start: this.#serverOptions });
|
||||
this.#spawnedAt = Date.now();
|
||||
});
|
||||
if (!waitForReady)
|
||||
return;
|
||||
@ -155,19 +155,19 @@ class Shard extends EventEmitter
|
||||
return new Promise<void>((resolve) =>
|
||||
{
|
||||
// Clear out all other exit listeners so they don't accidentally start the process up again
|
||||
if (!this.#_process)
|
||||
if (!this.#process)
|
||||
return resolve();
|
||||
this.#_process.removeAllListeners('exit');
|
||||
this.#process.removeAllListeners('exit');
|
||||
// Set timeout for force kill
|
||||
const to = setTimeout(() =>
|
||||
{
|
||||
if (!this.#_process)
|
||||
if (!this.#process)
|
||||
return resolve();
|
||||
this.#_process.kill();
|
||||
this.#process.kill();
|
||||
resolve();
|
||||
}, KillTO);
|
||||
// Gracefully handle exit
|
||||
this.#_process.once('exit', (code, signal) =>
|
||||
this.#process.once('exit', (code, signal) =>
|
||||
{
|
||||
clearTimeout(to);
|
||||
this._handleExit(code, signal, false);
|
||||
@ -179,7 +179,7 @@ class Shard extends EventEmitter
|
||||
clearTimeout(to);
|
||||
});
|
||||
|
||||
this.#_process.send({ _shutdown: true });
|
||||
this.#process.send({ _shutdown: true });
|
||||
});
|
||||
}
|
||||
this._handleExit(null, null, false);
|
||||
@ -188,7 +188,7 @@ class Shard extends EventEmitter
|
||||
send (message: IPCMessage, expectResponse = false): Promise<IPCMessage | void>
|
||||
{
|
||||
|
||||
if (!this.ready || !this.#_process)
|
||||
if (!this.ready || !this.#process)
|
||||
return Promise.reject(new Error(`[shard-${this.id}] Cannot send message to dead shard.`));
|
||||
|
||||
return new Promise<IPCMessage | void>((resolve, reject) =>
|
||||
@ -198,14 +198,14 @@ class Shard extends EventEmitter
|
||||
{
|
||||
message._id = Util.randomUUID();
|
||||
const to = setTimeout(reject, 10_000, [ new Error('Message timeout') ]);
|
||||
this.#_awaitingResponse.set(message._id, (args: IPCMessage) =>
|
||||
this.#awaitingResponse.set(message._id, (args: IPCMessage) =>
|
||||
{
|
||||
clearTimeout(to);
|
||||
resolve(args);
|
||||
});
|
||||
}
|
||||
|
||||
this.#_process?.send(message, err =>
|
||||
this.#process?.send(message, err =>
|
||||
{
|
||||
if (err)
|
||||
return reject(err);
|
||||
@ -219,10 +219,10 @@ class Shard extends EventEmitter
|
||||
|
||||
awaitShutdown ()
|
||||
{
|
||||
this.#_respawn = false;
|
||||
this.#respawn = false;
|
||||
return new Promise<void>((resolve) =>
|
||||
{
|
||||
this.#_awaitingShutdown = resolve;
|
||||
this.#awaitingShutdown = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ class Shard extends EventEmitter
|
||||
{
|
||||
if (message._ready)
|
||||
{
|
||||
this.#_ready = true;
|
||||
this.#ready = true;
|
||||
this.emit('ready');
|
||||
return;
|
||||
}
|
||||
@ -240,27 +240,27 @@ class Shard extends EventEmitter
|
||||
{
|
||||
const TO = setTimeout(() =>
|
||||
{
|
||||
this.#_process?.kill('SIGKILL');
|
||||
this.#process?.kill('SIGKILL');
|
||||
}, KillTO);
|
||||
this.#_process?.once('exit', () =>
|
||||
this.#process?.once('exit', () =>
|
||||
{
|
||||
clearTimeout(TO);
|
||||
});
|
||||
this.#_ready = false;
|
||||
this.#ready = false;
|
||||
this.emit('shutdown');
|
||||
return;
|
||||
}
|
||||
else if (message._fatal)
|
||||
{
|
||||
this.#_process?.removeAllListeners();
|
||||
this.#_ready = false;
|
||||
this.#_fatal = true;
|
||||
this.#process?.removeAllListeners();
|
||||
this.#ready = false;
|
||||
this.#fatal = true;
|
||||
this._handleExit(null, null, false);
|
||||
return this.emit('fatal', message);
|
||||
}
|
||||
else if (message._id)
|
||||
{
|
||||
const promise = this.#_awaitingResponse.get(message._id);
|
||||
const promise = this.#awaitingResponse.get(message._id);
|
||||
if (promise)
|
||||
return promise(message);
|
||||
}
|
||||
@ -275,28 +275,28 @@ class Shard extends EventEmitter
|
||||
this.emit('disconnect');
|
||||
}
|
||||
|
||||
_handleExit (code: number | null, _signal: string | null, respawn = this.#_respawn)
|
||||
_handleExit (code: number | null, _signal: string | null, respawn = this.#respawn)
|
||||
{
|
||||
if (this.process)
|
||||
this.process.removeAllListeners();
|
||||
|
||||
this.emit('death');
|
||||
if (this.#_awaitingShutdown)
|
||||
this.#_awaitingShutdown();
|
||||
if (this.#awaitingShutdown)
|
||||
this.#awaitingShutdown();
|
||||
|
||||
if (code !== 0)
|
||||
{
|
||||
this.#_crashes.push(Date.now() - this.spawnedAt);
|
||||
this.#crashes.push(Date.now() - this.spawnedAt);
|
||||
this.emit('warn', 'Shard exited with non-zero exit code');
|
||||
}
|
||||
|
||||
this.#_ready = false;
|
||||
this.#_process = null;
|
||||
this.#ready = false;
|
||||
this.#process = null;
|
||||
|
||||
const len = this.#_crashes.length;
|
||||
const len = this.#crashes.length;
|
||||
if (len > 2)
|
||||
{
|
||||
const last3 = this.#_crashes.slice(len - 3);
|
||||
const last3 = this.#crashes.slice(len - 3);
|
||||
const sum = last3.reduce((s, val) =>
|
||||
{
|
||||
s += val;
|
||||
|
@ -412,9 +412,9 @@ class Server extends EventEmitter
|
||||
try
|
||||
{
|
||||
const user = await this.users.createUser(name, pass);
|
||||
process.send({ _id: msg._id, success: true });
|
||||
if (admin)
|
||||
await user.updatePermissions({ administrator: { default: 10 } });
|
||||
process.send({ _id: msg._id, success: true });
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
|
@ -399,7 +399,10 @@ class UserDatabase implements UserDatabaseInterface
|
||||
name
|
||||
});
|
||||
await user.setPassword(password);
|
||||
await this.#db.insertOne(this.#_userCollection, user.jsonPrivate);
|
||||
const json = user.jsonPrivate as {_id?: ObjectId, id?: string};
|
||||
json._id = new ObjectId(json.id);
|
||||
delete json.id;
|
||||
await this.#db.insertOne(this.#_userCollection, json);
|
||||
// await this.updateUser(user);
|
||||
if (!this.#disableCache)
|
||||
this.#cache.set(user.id, user);
|
||||
@ -411,8 +414,10 @@ class UserDatabase implements UserDatabaseInterface
|
||||
async createApplication (data: ApplicationData)
|
||||
{
|
||||
const app = this._createApp(data);
|
||||
// await this.updateApplication(app);
|
||||
await this.#db.insertOne(this.#_appCollection, app.jsonPrivate);
|
||||
const json = app.jsonPrivate as {_id?: ObjectId, id?: string};
|
||||
json._id = new ObjectId(json.id);
|
||||
delete json.id;
|
||||
await this.#db.insertOne(this.#_appCollection, json);
|
||||
if (!this.#disableCache)
|
||||
this.#cache.set(app.id, app);
|
||||
this.#amount.applications++;
|
||||
@ -510,11 +515,11 @@ class UserDatabase implements UserDatabaseInterface
|
||||
* @return {*} {Promise<T>}
|
||||
* @memberof UserDatabase
|
||||
*/
|
||||
async _updateEntity<T> (entity: T, collection: string): Promise<T>
|
||||
async _updateEntity<T extends Entity> (entity: T, collection: string): Promise<T>
|
||||
{
|
||||
if (!(entity instanceof Entity))
|
||||
throw new Error('Cannot save a non-entity instance');
|
||||
const json = entity.json as {id?: string};
|
||||
const json = entity.jsonPrivate as { id?: string };
|
||||
delete json.id;
|
||||
await this.#db.updateOne(collection, { _id: new ObjectId(entity.id) }, json);
|
||||
return entity;
|
||||
|
@ -9,9 +9,9 @@ class AbstractUser extends Entity
|
||||
|
||||
static override ProtectedFields: string[] = [ ];
|
||||
|
||||
#_icon: string | null;
|
||||
#_roles: Role[];
|
||||
#_temporary: boolean;
|
||||
#icon: string | null;
|
||||
#roles: Role[];
|
||||
#temporary: boolean;
|
||||
|
||||
constructor (server: Server, { icon, roles = [], ...opts }: AbstractUserData)
|
||||
{
|
||||
@ -19,9 +19,9 @@ class AbstractUser extends Entity
|
||||
if (this.constructor === AbstractUser)
|
||||
throw new Error('This class cannot be instantiated, only derived');
|
||||
|
||||
this.#_icon = icon || null;
|
||||
this.#_roles = roles as Role[];
|
||||
this.#_temporary = opts.temporary || false;
|
||||
this.#icon = icon || null;
|
||||
this.#roles = roles as Role[];
|
||||
this.#temporary = opts.temporary || false;
|
||||
|
||||
}
|
||||
|
||||
@ -32,12 +32,12 @@ class AbstractUser extends Entity
|
||||
|
||||
get temporary ()
|
||||
{
|
||||
return this.#_temporary;
|
||||
return this.#temporary;
|
||||
}
|
||||
|
||||
set temporary (val: boolean)
|
||||
{
|
||||
this.#_temporary = val;
|
||||
this.#temporary = val;
|
||||
}
|
||||
|
||||
fetchRateLimits ()
|
||||
@ -47,12 +47,12 @@ class AbstractUser extends Entity
|
||||
|
||||
get roles ()
|
||||
{
|
||||
return this.#_roles;
|
||||
return this.#roles;
|
||||
}
|
||||
|
||||
setIcon (icon: string)
|
||||
{
|
||||
this.#_icon = icon;
|
||||
this.#icon = icon;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,7 +65,7 @@ class AbstractUser extends Entity
|
||||
return {
|
||||
...super.jsonPrivate,
|
||||
icon: this.icon,
|
||||
roles: this.#_roles.map(role => role.id)
|
||||
roles: this.#roles.map(role => role.id)
|
||||
};
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ class AbstractUser extends Entity
|
||||
|
||||
get icon ()
|
||||
{
|
||||
return this.#_icon;
|
||||
return this.#icon;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,21 +12,21 @@ type SubpathDefinition = [string, string, HandlerFunction | null, (MiddlewareDef
|
||||
abstract class Endpoint
|
||||
{
|
||||
|
||||
#_server: Server;
|
||||
#_path: string;
|
||||
#_name: string;
|
||||
#server: Server;
|
||||
#path: string;
|
||||
#name: string;
|
||||
|
||||
_subpaths: SubpathDefinition;
|
||||
subpaths: SubpathDefinition;
|
||||
#_subpaths: SubpathDefinition;
|
||||
#subpaths: SubpathDefinition;
|
||||
|
||||
_middleware: MiddlewareDefition;
|
||||
middleware: MiddlewareDefition;
|
||||
#_middleware: MiddlewareDefition;
|
||||
#middleware: MiddlewareDefition;
|
||||
|
||||
methods: EndpointDefinition;
|
||||
rateLimiter: RateLimiter;
|
||||
logger: LoggerClient;
|
||||
#methods: EndpointDefinition;
|
||||
#rateLimiter: RateLimiter;
|
||||
#logger: LoggerClient;
|
||||
|
||||
loadOrder: number;
|
||||
#loadOrder: number;
|
||||
#auth: MiddlewareFunction | null;
|
||||
|
||||
constructor (server: Server, { path, name, loadOrder = 5, auth = null }: EndpointOptions)
|
||||
@ -34,45 +34,45 @@ abstract class Endpoint
|
||||
|
||||
if (!server)
|
||||
Util.fatal(new Error('Missing server object in endpoint'));
|
||||
this.#_server = server;
|
||||
this.#server = server;
|
||||
|
||||
if (!path)
|
||||
Util.fatal(new Error('Missing path in endpoint'));
|
||||
if (!path.startsWith('/'))
|
||||
path = `/${path}`;
|
||||
|
||||
this.#_path = path;
|
||||
this.#path = path;
|
||||
|
||||
if (name)
|
||||
this.#_name = name;
|
||||
this.#name = name;
|
||||
else
|
||||
this.#_name = path;
|
||||
this.#name = path;
|
||||
|
||||
if (auth === true)
|
||||
this.#auth = server.auth.authenticate;
|
||||
else
|
||||
this.#auth = auth;
|
||||
|
||||
this.subpaths = [];
|
||||
this.#subpaths = [];
|
||||
// Subpaths that should exist on *all* endpoints, the subpaths property can be overwritten, so storing these separately to ensure they exist
|
||||
this._subpaths = [
|
||||
this.#_subpaths = [
|
||||
[ 'post', '/debug', this.toggleDebug.bind(this), [ server.authenticator.createAuthoriser('developer') ]]
|
||||
];
|
||||
// Same as above but for inheriting intermediary endpoint classes
|
||||
// e.g. the API endpoint class adds a ratelimiter to this
|
||||
// doing like this to ensure the actual endpoint classes don't overwrite it when defining middleware
|
||||
this._middleware = [ ];
|
||||
this.middleware = [];
|
||||
this.#_middleware = [ ];
|
||||
this.#middleware = [];
|
||||
|
||||
this.methods = [];
|
||||
this.#methods = [];
|
||||
|
||||
this.rateLimiter = server.rateLimiter;
|
||||
this.logger = server.createLogger(this);
|
||||
this.#rateLimiter = server.rateLimiter;
|
||||
this.#logger = server.createLogger(this);
|
||||
|
||||
// Used to sort the endpoints from smallest to highest before initialisation
|
||||
// Useful when needing to have certain endpoints register before or after some other endpoint
|
||||
// E.g. 404 pages should be initialised last by having a loadOrder of 10
|
||||
this.loadOrder = loadOrder;
|
||||
this.#loadOrder = loadOrder;
|
||||
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ abstract class Endpoint
|
||||
{
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [ method, cb, mw = [] ] of this.methods)
|
||||
for (let [ method, cb, mw = [] ] of this.#methods)
|
||||
{
|
||||
if (typeof method !== 'string')
|
||||
throw new Error(`Invalid method parameter type in Endpoint ${this.name} major path`);
|
||||
@ -92,19 +92,19 @@ abstract class Endpoint
|
||||
const middleware = [];
|
||||
if (this.#auth)
|
||||
middleware.push(this.#auth);
|
||||
middleware.push(...this._middleware, ...this.middleware, ...mw);
|
||||
middleware.push(...this.#_middleware, ...this.#middleware, ...mw);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.server.app[method](this.path, ...middleware, cb);
|
||||
}
|
||||
|
||||
this.subpaths = [ ...this._subpaths, ...this.subpaths ];
|
||||
this.#subpaths = [ ...this.#_subpaths, ...this.#subpaths ];
|
||||
// eslint-disable-next-line prefer-const
|
||||
for (let [ method, sub, cb, mw = [] ] of this.subpaths)
|
||||
for (let [ method, sub, cb, mw = [] ] of this.#subpaths)
|
||||
{
|
||||
if (typeof method !== 'string')
|
||||
throw new Error(`Invalid method parameter type in Endpoint ${this.name} subpath ${sub}`);
|
||||
if (!this.middleware.length && !mw.length && !cb)
|
||||
if (!this.#middleware.length && !mw.length && !cb)
|
||||
throw new Error('Cannot have endpoint with no handler and no middleware, expecting at least one to be defined');
|
||||
if (typeof mw === 'function')
|
||||
mw = [ mw ];
|
||||
@ -114,7 +114,7 @@ abstract class Endpoint
|
||||
const middleware = [];
|
||||
if (this.#auth)
|
||||
middleware.push(this.#auth);
|
||||
middleware.push(...this._middleware, ...this.middleware, ...mw);
|
||||
middleware.push(...this.#_middleware, ...this.#middleware, ...mw);
|
||||
const args = [ this.path + sub, ...middleware ];
|
||||
|
||||
if (cb)
|
||||
@ -136,26 +136,71 @@ abstract class Endpoint
|
||||
|
||||
const { body } = req;
|
||||
if (typeof body.value !== 'string')
|
||||
return void res.status(400).send(`Invalid value, must be one of ${this.logger.logLevels}`);
|
||||
this.logger.info(`Setting debug mode on endpoint ${this.name} to ${body.value}`);
|
||||
this.logger.setLogLevel(body.value);
|
||||
return void res.status(400).send(`Invalid value, must be one of ${this.#logger.logLevels}`);
|
||||
this.#logger.info(`Setting debug mode on endpoint ${this.name} to ${body.value}`);
|
||||
this.#logger.setLogLevel(body.value);
|
||||
res.status(200).send(body.value);
|
||||
|
||||
}
|
||||
|
||||
protected get logger ()
|
||||
{
|
||||
return this.#logger;
|
||||
}
|
||||
|
||||
protected get rateLimiter ()
|
||||
{
|
||||
return this.#rateLimiter;
|
||||
}
|
||||
|
||||
get methods ()
|
||||
{
|
||||
return this.#methods;
|
||||
}
|
||||
|
||||
protected set methods (methods)
|
||||
{
|
||||
this.#methods = methods;
|
||||
}
|
||||
|
||||
protected get middleware ()
|
||||
{
|
||||
return this.#middleware;
|
||||
}
|
||||
|
||||
protected set middleware (mw)
|
||||
{
|
||||
this.#middleware = mw;
|
||||
}
|
||||
|
||||
get subpaths ()
|
||||
{
|
||||
return this.#subpaths;
|
||||
}
|
||||
|
||||
protected set subpaths (subs)
|
||||
{
|
||||
this.#subpaths = subs;
|
||||
}
|
||||
|
||||
get server ()
|
||||
{
|
||||
return this.#_server;
|
||||
return this.#server;
|
||||
}
|
||||
|
||||
get path ()
|
||||
{
|
||||
return this.#_path;
|
||||
return this.#path;
|
||||
}
|
||||
|
||||
get name ()
|
||||
{
|
||||
return this.#_name;
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
get loadOrder ()
|
||||
{
|
||||
return this.#loadOrder;
|
||||
}
|
||||
|
||||
get resolveable ()
|
||||
|
@ -10,16 +10,16 @@ class Entity
|
||||
|
||||
static ProtectedFields = [ '_id' ];
|
||||
|
||||
#_id: string;
|
||||
#_name: string;
|
||||
#_disabled: boolean;
|
||||
#_permissions: Permissions;
|
||||
#_cachedTimestamp: number;
|
||||
#_createdTimestamp: number;
|
||||
#_note: string | null;
|
||||
#id: string;
|
||||
#name: string;
|
||||
#disabled: boolean;
|
||||
#permissions: Permissions;
|
||||
#cachedTimestamp: number;
|
||||
#createdTimestamp: number;
|
||||
#note: string | null;
|
||||
|
||||
_db: UserDatabaseInterface;
|
||||
_server: Server;
|
||||
#db: UserDatabaseInterface;
|
||||
#server: Server;
|
||||
|
||||
constructor (server: Server, { note, name, disabled, id, permissions, createdTimestamp }: EntityData)
|
||||
{
|
||||
@ -38,18 +38,28 @@ class Entity
|
||||
if (!name)
|
||||
throw new Error('Entities require a name');
|
||||
|
||||
this._server = server;
|
||||
this._db = server.users;
|
||||
this.#_id = id;
|
||||
this.#_name = name;
|
||||
this.#_disabled = disabled ?? false;
|
||||
this.#_permissions = PermissionManager.merge(permissions || {}, PermissionManager.DefaultPermissions);
|
||||
this.#_createdTimestamp = createdTimestamp ?? Date.now();
|
||||
this.#_cachedTimestamp = Date.now();
|
||||
this.#_note = note ?? null;
|
||||
this.#server = server;
|
||||
this.#db = server.users;
|
||||
this.#id = id;
|
||||
this.#name = name;
|
||||
this.#disabled = disabled ?? false;
|
||||
this.#permissions = PermissionManager.merge(permissions || {}, PermissionManager.DefaultPermissions);
|
||||
this.#createdTimestamp = createdTimestamp ?? Date.now();
|
||||
this.#cachedTimestamp = Date.now();
|
||||
this.#note = note ?? null;
|
||||
|
||||
}
|
||||
|
||||
protected get db ()
|
||||
{
|
||||
return this.#db;
|
||||
}
|
||||
|
||||
protected get server ()
|
||||
{
|
||||
return this.#server;
|
||||
}
|
||||
|
||||
save ()
|
||||
{
|
||||
throw new Error('Expecting this function to be implemented in an inheriting class');
|
||||
@ -63,7 +73,7 @@ class Entity
|
||||
updatePermissions (perms: Permissions)
|
||||
{
|
||||
PermissionManager.validatePermissions(perms);
|
||||
this.#_permissions = PermissionManager.merge(this.#_permissions, perms, true);
|
||||
this.#permissions = PermissionManager.merge(this.#permissions, perms, true);
|
||||
return this.save();
|
||||
}
|
||||
|
||||
@ -101,52 +111,52 @@ class Entity
|
||||
|
||||
get id ()
|
||||
{
|
||||
return this.#_id;
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
get name ()
|
||||
{
|
||||
return this.#_name;
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
set name (str: string)
|
||||
{
|
||||
this.#_name = str;
|
||||
this.#name = str;
|
||||
}
|
||||
|
||||
get disabled ()
|
||||
{
|
||||
return this.#_disabled;
|
||||
return this.#disabled;
|
||||
}
|
||||
|
||||
set disabled (val: boolean)
|
||||
{
|
||||
this.#_disabled = val;
|
||||
this.#disabled = val;
|
||||
}
|
||||
|
||||
get permissions ()
|
||||
{
|
||||
return Object.freeze({ ...this.#_permissions });
|
||||
return Object.freeze({ ...this.#permissions });
|
||||
}
|
||||
|
||||
get note ()
|
||||
{
|
||||
return this.#_note;
|
||||
return this.#note;
|
||||
}
|
||||
|
||||
get createdAt ()
|
||||
{
|
||||
return new Date(this.#_createdTimestamp);
|
||||
return new Date(this.#createdTimestamp);
|
||||
}
|
||||
|
||||
get createdTimestamp ()
|
||||
{
|
||||
return this.#_createdTimestamp;
|
||||
return this.#createdTimestamp;
|
||||
}
|
||||
|
||||
get cachedTimestamp ()
|
||||
{
|
||||
return this.#_cachedTimestamp;
|
||||
return this.#cachedTimestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,15 +13,15 @@ class Flag
|
||||
|
||||
#manager: FlagManager;
|
||||
|
||||
#_id: string;
|
||||
#_name: string;
|
||||
#_hierarchy: string;
|
||||
#id: string;
|
||||
#name: string;
|
||||
#hierarchy: string;
|
||||
|
||||
#_env: FlagEnv;
|
||||
#_consumer: FlagConsumer;
|
||||
#env: FlagEnv;
|
||||
#consumer: FlagConsumer;
|
||||
|
||||
#_value: FlagType;
|
||||
#_type: string;
|
||||
#value: FlagType;
|
||||
#type: string;
|
||||
|
||||
constructor (manager: FlagManager, data: FlagData)
|
||||
{
|
||||
@ -41,18 +41,18 @@ class Flag
|
||||
if (!('value' in data))
|
||||
throw new Error('Missing value');
|
||||
|
||||
this.#_id = data._id;
|
||||
this.#_env = data.env;
|
||||
this.#_consumer = data.consumer;
|
||||
this.#_name = data.name;
|
||||
this.#_value = data.value;
|
||||
this.#_hierarchy = data.hierarchy || '';
|
||||
this.#_type = Flag.resolveType(data.value);
|
||||
this.#id = data._id;
|
||||
this.#env = data.env;
|
||||
this.#consumer = data.consumer;
|
||||
this.#name = data.name;
|
||||
this.#value = data.value;
|
||||
this.#hierarchy = data.hierarchy || '';
|
||||
this.#type = Flag.resolveType(data.value);
|
||||
}
|
||||
|
||||
get<T> ()
|
||||
{
|
||||
return this.#_value as T;
|
||||
return this.#value as T;
|
||||
}
|
||||
|
||||
save (): Promise<void>
|
||||
@ -62,64 +62,64 @@ class Flag
|
||||
|
||||
get name ()
|
||||
{
|
||||
return this.#_name;
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
set name (val)
|
||||
{
|
||||
this.#_name = val;
|
||||
this.#name = val;
|
||||
}
|
||||
|
||||
get id ()
|
||||
{
|
||||
return this.#_id;
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
get type ()
|
||||
{
|
||||
return this.#_type;
|
||||
return this.#type;
|
||||
}
|
||||
|
||||
get value ()
|
||||
{
|
||||
return this.#_value;
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
set value (val)
|
||||
{
|
||||
if (typeof val !== typeof this.value)
|
||||
throw new Error('Value type mismatch');
|
||||
this.#_value = val;
|
||||
this.#value = val;
|
||||
}
|
||||
|
||||
get env ()
|
||||
{
|
||||
return this.#_env;
|
||||
return this.#env;
|
||||
}
|
||||
|
||||
set env (val)
|
||||
{
|
||||
this.#_env = val;
|
||||
this.#env = val;
|
||||
}
|
||||
|
||||
get consumer ()
|
||||
{
|
||||
return this.#_consumer;
|
||||
return this.#consumer;
|
||||
}
|
||||
|
||||
set consumer (val)
|
||||
{
|
||||
this.#_consumer = val;
|
||||
this.#consumer = val;
|
||||
}
|
||||
|
||||
get hierarchy ()
|
||||
{
|
||||
return this.#_hierarchy;
|
||||
return this.#hierarchy;
|
||||
}
|
||||
|
||||
set hierarchy (val)
|
||||
{
|
||||
this.#_hierarchy = val;
|
||||
this.#hierarchy = val;
|
||||
}
|
||||
|
||||
get json (): FlagData
|
||||
|
@ -8,45 +8,45 @@ class Role extends Entity
|
||||
|
||||
static override ProtectedFields = [ ];
|
||||
|
||||
#_rateLimits: RateLimits;
|
||||
#_position: number;
|
||||
#rateLimits: RateLimits;
|
||||
#position: number;
|
||||
|
||||
#_rateLimiter: RateLimiter;
|
||||
#rateLimiter: RateLimiter;
|
||||
|
||||
constructor (server: Server, { rateLimits = {}, position, ...data }: RoleData)
|
||||
{
|
||||
super(server, data);
|
||||
this.#_rateLimiter = server.rateLimiter;
|
||||
this.#_rateLimits = this.#_rateLimiter.validateLimits(rateLimits, { addMissing: true, deleteInvalid: true });
|
||||
this.#rateLimiter = server.rateLimiter;
|
||||
this.#rateLimits = this.#rateLimiter.validateLimits(rateLimits, { addMissing: true, deleteInvalid: true });
|
||||
if (position === null)
|
||||
throw new Error('Must supply position');
|
||||
this.#_position = position;
|
||||
this.#position = position;
|
||||
}
|
||||
|
||||
override save ()
|
||||
{
|
||||
return this._db.updateRole(this);
|
||||
return this.db.updateRole(this);
|
||||
}
|
||||
|
||||
updateRateLimits (limits: RateLimits)
|
||||
{
|
||||
limits = this.#_rateLimiter.validateLimits(limits);
|
||||
limits = this.#rateLimiter.validateLimits(limits);
|
||||
const entries = Object.entries(limits);
|
||||
for (const [ key, val ] of entries)
|
||||
{
|
||||
this.#_rateLimits[key] = val;
|
||||
this.#rateLimits[key] = val;
|
||||
}
|
||||
return this.save();
|
||||
}
|
||||
|
||||
get rateLimits ()
|
||||
{
|
||||
return this.#_rateLimits;
|
||||
return this.#rateLimits;
|
||||
}
|
||||
|
||||
get position ()
|
||||
{
|
||||
return this.#_position;
|
||||
return this.#position;
|
||||
}
|
||||
|
||||
override get jsonPrivate ()
|
||||
@ -54,8 +54,8 @@ class Role extends Entity
|
||||
const json = super.jsonPrivate;
|
||||
return {
|
||||
...json,
|
||||
rateLimits: this.#_rateLimits,
|
||||
position: this.#_position
|
||||
rateLimits: this.#rateLimits,
|
||||
position: this.#position
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,12 @@ class User extends AbstractUser
|
||||
{
|
||||
|
||||
#passwordHash: string | null;
|
||||
#_otpSecret: string | null;
|
||||
#_mfa: boolean;
|
||||
#otpSecret: string | null;
|
||||
#mfa: boolean;
|
||||
|
||||
#_displayName: string | null;
|
||||
#_externalProfiles: {[key: string]: ExternalProfile};
|
||||
#_applications: string[];
|
||||
#displayName: string | null;
|
||||
#externalProfiles: {[key: string]: ExternalProfile};
|
||||
#applications: string[];
|
||||
|
||||
constructor (server: Server, data: UserData)
|
||||
{
|
||||
@ -28,30 +28,30 @@ class User extends AbstractUser
|
||||
|
||||
AbstractUser.ProtectedFields.push('otpSecret', 'password');
|
||||
|
||||
this.#_applications = data.applications || [];
|
||||
this.#_externalProfiles = data.externalProfiles || {};
|
||||
this.#applications = data.applications || [];
|
||||
this.#externalProfiles = data.externalProfiles || {};
|
||||
|
||||
/** @private */
|
||||
this.#passwordHash = data.password || null;
|
||||
this.#_otpSecret = data.otpSecret || null;
|
||||
this.#_mfa = data.twoFactor || false;
|
||||
this.#_displayName = data.displayName || null;
|
||||
this.#otpSecret = data.otpSecret || null;
|
||||
this.#mfa = data.twoFactor || false;
|
||||
this.#displayName = data.displayName || null;
|
||||
|
||||
}
|
||||
|
||||
override save ()
|
||||
{
|
||||
return this._db.updateUser(this);
|
||||
return this.db.updateUser(this);
|
||||
}
|
||||
|
||||
get displayName ()
|
||||
{
|
||||
return this.#_displayName || this.username;
|
||||
return this.#displayName || this.username;
|
||||
}
|
||||
|
||||
set displayName (str)
|
||||
{
|
||||
this.#_displayName = str;
|
||||
this.#displayName = str;
|
||||
}
|
||||
|
||||
get username ()
|
||||
@ -76,37 +76,37 @@ class User extends AbstractUser
|
||||
|
||||
override get twoFactor ()
|
||||
{
|
||||
return this.#_mfa;
|
||||
return this.#mfa;
|
||||
}
|
||||
|
||||
override set twoFactor (val: boolean)
|
||||
{
|
||||
this.#_mfa = val;
|
||||
this.#mfa = val;
|
||||
}
|
||||
|
||||
get mfa ()
|
||||
{
|
||||
return this.#_mfa;
|
||||
return this.#mfa;
|
||||
}
|
||||
|
||||
set mfa (val: boolean)
|
||||
{
|
||||
this.#_mfa = val;
|
||||
this.#mfa = val;
|
||||
}
|
||||
|
||||
get externalProfiles ()
|
||||
{
|
||||
return Object.freeze({ ...this.#_externalProfiles });
|
||||
return Object.freeze({ ...this.#externalProfiles });
|
||||
}
|
||||
|
||||
get applications ()
|
||||
{
|
||||
return this.#_applications;
|
||||
return this.#applications;
|
||||
}
|
||||
|
||||
get otpSecret ()
|
||||
{
|
||||
return this.#_otpSecret;
|
||||
return this.#otpSecret;
|
||||
}
|
||||
|
||||
async setPassword (passwd: string, save = false)
|
||||
@ -119,7 +119,7 @@ class User extends AbstractUser
|
||||
|
||||
setOtpSecret (secret: string)
|
||||
{
|
||||
this.#_otpSecret = secret;
|
||||
this.#otpSecret = secret;
|
||||
}
|
||||
|
||||
async authenticate (passwd: string)
|
||||
@ -145,7 +145,7 @@ class User extends AbstractUser
|
||||
addExternalProfile (platform: string, profile: ExternalProfile)
|
||||
{
|
||||
profile.provider = platform;
|
||||
this.#_externalProfiles[platform] = profile;
|
||||
this.#externalProfiles[platform] = profile;
|
||||
}
|
||||
|
||||
hasExternalProfile (name: string)
|
||||
@ -160,9 +160,9 @@ class User extends AbstractUser
|
||||
displayName: this.displayName,
|
||||
externalProfiles: this.externalProfiles,
|
||||
password: this.#passwordHash,
|
||||
otpSecret: this.#_otpSecret,
|
||||
otpSecret: this.#otpSecret,
|
||||
twoFactor: this.twoFactor,
|
||||
applications: this.#_applications,
|
||||
applications: this.#applications,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,9 @@ import { AbstractUser } from '../interfaces/index.js';
|
||||
class UserApplication extends AbstractUser
|
||||
{
|
||||
|
||||
#_token: string;
|
||||
#_user: string;
|
||||
#_description: string | null;
|
||||
#token: string;
|
||||
#user: string;
|
||||
#description: string | null;
|
||||
|
||||
constructor (server: Server, options: ApplicationData)
|
||||
{
|
||||
@ -20,35 +20,35 @@ class UserApplication extends AbstractUser
|
||||
|
||||
if (typeof options.token !== 'string')
|
||||
throw new Error('Missing token for appliaction');
|
||||
this.#_token = options.token;
|
||||
this.#token = options.token;
|
||||
|
||||
if (options.ownerId instanceof ObjectId)
|
||||
options.ownerId = options.ownerId.toString();
|
||||
if (typeof options.ownerId !== 'string')
|
||||
throw new Error('Missing ownerId (owner) for application');
|
||||
this.#_user = options.ownerId;
|
||||
this.#_description = options.description || null;
|
||||
this.#user = options.ownerId;
|
||||
this.#description = options.description || null;
|
||||
|
||||
}
|
||||
|
||||
override save ()
|
||||
{
|
||||
return this._db.updateApplication(this);
|
||||
return this.db.updateApplication(this);
|
||||
}
|
||||
|
||||
get ownerId ()
|
||||
{
|
||||
return this.#_user;
|
||||
return this.#user;
|
||||
}
|
||||
|
||||
get description ()
|
||||
{
|
||||
return this.#_description;
|
||||
return this.#description;
|
||||
}
|
||||
|
||||
get token ()
|
||||
{
|
||||
return this.#_token;
|
||||
return this.#token;
|
||||
}
|
||||
|
||||
override get jsonPrivate ()
|
||||
@ -56,7 +56,7 @@ class UserApplication extends AbstractUser
|
||||
return {
|
||||
...super.jsonPrivate,
|
||||
token: this.token,
|
||||
ownerId: new ObjectId(this.#_user),
|
||||
ownerId: new ObjectId(this.#user),
|
||||
description: this.description,
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user