a lot of things

This commit is contained in:
2026-02-20 11:46:30 -06:00
parent 987a1c8a6a
commit 70284bc14e
37 changed files with 1080 additions and 79 deletions
+92 -12
View File
@@ -1,5 +1,4 @@
import { Collection } from "@discordjs/collection";
import { z } from "zod";
import { Role } from "../../generated/prisma/client";
import { User } from "../../generated/prisma/browser";
import { SessionTokensObject } from "./SessionController";
@@ -9,6 +8,10 @@ import { prisma } from "../constants";
import { events } from "../modules/globalEvents";
import { RoleController } from "./RoleController";
import { roles } from "../managers/roles";
import { signPermissions } from "../modules/permission-utils/signPermissions";
import { DecodedPermissionsBlock } from "../types/PermissionTypes";
import jwt from "jsonwebtoken";
import { permissionsPrivateKey } from "../constants";
export default class UserController {
public id: string;
@@ -18,6 +21,7 @@ export default class UserController {
public image: string | null;
private _roles: Collection<string, Role>;
private _permissions: string | null;
public createdAt: Date;
public updatedAt: Date;
@@ -29,6 +33,7 @@ export default class UserController {
this.image = userdata.image;
this.updatedAt = userdata.updatedAt;
this.createdAt = userdata.createdAt;
this._permissions = userdata.permissions ?? null;
this._roles = (() => {
let collection = new Collection<string, Role>();
@@ -77,22 +82,13 @@ export default class UserController {
* @param data - A partial of the user data
* @returns {Promise<UserController>} - The updated user controller
*/
public async update(data: Partial<User>) {
// Parsed Data With Schema
const pData = z
.object({
name: z.string().optional(),
image: z.string().optional(),
})
.strict()
.parse(data);
public async update(data: Partial<Pick<User, "name" | "image">>) {
if (Object.keys(data).length == 0)
throw new BodyError("Body cannot be empty.");
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: pData,
data,
});
this._updateInternalValues(updatedUser);
@@ -101,6 +97,87 @@ export default class UserController {
return this;
}
/**
* Set Roles
*
* Replace the user's roles with the provided array of role identifiers (id or moniker).
* Validates that each role exists before assigning.
*
* @param roleIdentifiers - Array of role ids or monikers to assign
* @returns {Promise<UserController>} - The updated user controller
*/
public async setRoles(roleIdentifiers: string[]): Promise<UserController> {
const resolvedRoles = await Promise.all(
roleIdentifiers.map((identifier) => roles.fetch(identifier)),
);
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: {
roles: {
set: resolvedRoles.map((r) => ({ id: r.id })),
},
},
include: { roles: true },
});
this._updateInternalValues(updatedUser);
this._roles = new Collection<string, Role>();
updatedUser.roles.map((v: any) => this._roles.set(v.id, v));
for (const role of resolvedRoles) {
events.emit("user:role:assigned", { user: this, role });
}
return this;
}
/**
* Set Permissions
*
* Replace the user's direct permissions with the provided array of permission strings.
* Signs the permissions with the user issuer before storing.
*
* @param permissions - Array of permission node strings to assign
* @returns {Promise<UserController>} - The updated user controller
*/
public async setPermissions(permissions: string[]): Promise<UserController> {
const signed = signPermissions({
issuer: "user",
subject: this.id,
permissions,
});
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: { permissions: signed },
});
this._updateInternalValues(updatedUser);
return this;
}
/**
* Read Permissions
*
* Verifies and decodes the user's direct permissions JWT and returns the array of
* permission node strings. Returns an empty array if the user has no direct permissions.
*
* @returns {string[]} The user's direct permission nodes
*/
public readPermissions(): string[] {
if (!this._permissions) return [];
const decoded = jwt.verify(this._permissions, permissionsPrivateKey, {
algorithms: ["RS256"],
issuer: "user",
subject: this.id,
}) as DecodedPermissionsBlock;
return decoded.permissions;
}
/**
* Fetch Roles
*
@@ -185,9 +262,12 @@ export default class UserController {
: this._roles.size > 0
? this._roles.map((v) => v.moniker)
: undefined,
permissions: opts?.safeReturn ? undefined : this.readPermissions(),
login: opts?.safeReturn ? undefined : this.login,
email: opts?.safeReturn ? undefined : this.email,
image: this.image,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
}
}