Pārlūkot izejas kodu

refactor: worked more on replacing mongoose with sequalize

Very much WIP.
Many linting, TS and test-related things have to be fixed.
Not all schemas are (fully) converted yet.
Lots of new TODO comments will need to be fixed.
Kristian Vos 10 mēneši atpakaļ
vecāks
revīzija
c45a4603b0
66 mainītis faili ar 864 papildinājumiem un 507 dzēšanām
  1. 2 5
      backend/src/Job.ts
  2. 5 5
      backend/src/JobContext.ts
  3. 6 6
      backend/src/main.ts
  4. 87 7
      backend/src/modules/DataModule.ts
  5. 0 1
      backend/src/modules/DataModule/DeleteByIdJob.ts
  6. 0 1
      backend/src/modules/DataModule/DeleteManyByIdJob.ts
  7. 0 1
      backend/src/modules/DataModule/FindByIdJob.ts
  8. 0 1
      backend/src/modules/DataModule/FindManyByIdJob.ts
  9. 32 22
      backend/src/modules/DataModule/GetDataJob.ts
  10. 0 1
      backend/src/modules/DataModule/UpdateByIdJob.ts
  11. 55 0
      backend/src/modules/DataModule/models/MinifiedUser.ts
  12. 2 1
      backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserCreatedEvent.ts
  13. 2 1
      backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserDeletedEvent.ts
  14. 2 1
      backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserUpdatedEvent.ts
  15. 2 1
      backend/src/modules/DataModule/models/MinifiedUser/jobs/FindById.ts
  16. 2 1
      backend/src/modules/DataModule/models/MinifiedUser/jobs/FindManyById.ts
  17. 0 0
      backend/src/modules/DataModule/models/MinifiedUser/migrations/170526579600-create-minified-users-view.ts
  18. 72 0
      backend/src/modules/DataModule/models/MinifiedUser/schema.ts
  19. 78 31
      backend/src/modules/DataModule/models/News.ts
  20. 9 3
      backend/src/modules/DataModule/models/News/jobs/GetData.ts
  21. 1 1
      backend/src/modules/DataModule/models/News/jobs/Newest.ts
  22. 52 0
      backend/src/modules/DataModule/models/Session.ts
  23. 2 1
      backend/src/modules/DataModule/models/Session/events/SessionCreatedEvent.ts
  24. 2 1
      backend/src/modules/DataModule/models/Session/events/SessionDeletedEvent.ts
  25. 2 1
      backend/src/modules/DataModule/models/Session/events/SessionUpdatedEvent.ts
  26. 2 1
      backend/src/modules/DataModule/models/Session/jobs/Create.ts
  27. 2 1
      backend/src/modules/DataModule/models/Session/jobs/DeleteById.ts
  28. 2 1
      backend/src/modules/DataModule/models/Session/jobs/FindById.ts
  29. 2 1
      backend/src/modules/DataModule/models/Session/jobs/FindManyById.ts
  30. 2 1
      backend/src/modules/DataModule/models/Session/jobs/UpdateById.ts
  31. 25 0
      backend/src/modules/DataModule/models/Session/schema.ts
  32. 74 0
      backend/src/modules/DataModule/models/User.ts
  33. 0 0
      backend/src/modules/DataModule/models/User/UserAvatarColor.ts
  34. 0 0
      backend/src/modules/DataModule/models/User/UserAvatarType.ts
  35. 0 0
      backend/src/modules/DataModule/models/User/UserRole.ts
  36. 0 0
      backend/src/modules/DataModule/models/User/config.ts
  37. 2 1
      backend/src/modules/DataModule/models/User/events/UserCreatedEvent.ts
  38. 2 1
      backend/src/modules/DataModule/models/User/events/UserDeletedEvent.ts
  39. 2 1
      backend/src/modules/DataModule/models/User/events/UserUpdatedEvent.ts
  40. 0 0
      backend/src/modules/DataModule/models/User/getData.ts
  41. 2 1
      backend/src/modules/DataModule/models/User/jobs/Create.ts
  42. 2 1
      backend/src/modules/DataModule/models/User/jobs/DeleteById.ts
  43. 2 1
      backend/src/modules/DataModule/models/User/jobs/DeleteManyById.ts
  44. 12 0
      backend/src/modules/DataModule/models/User/jobs/FindById.ts
  45. 2 1
      backend/src/modules/DataModule/models/User/jobs/FindManyById.ts
  46. 2 1
      backend/src/modules/DataModule/models/User/jobs/GetData.ts
  47. 7 3
      backend/src/modules/DataModule/models/User/jobs/GetModelPermissions.ts
  48. 4 5
      backend/src/modules/DataModule/models/User/jobs/GetPermissions.ts
  49. 2 1
      backend/src/modules/DataModule/models/User/jobs/UpdateById.ts
  50. 227 0
      backend/src/modules/DataModule/models/User/schema.ts
  51. 0 72
      backend/src/modules/DataModule/models/minifiedUsers/schema.ts
  52. 15 0
      backend/src/modules/DataModule/models/news/listeners/NewsUpdatedListener.ts
  53. 0 25
      backend/src/modules/DataModule/models/sessions/schema.ts
  54. 0 16
      backend/src/modules/DataModule/models/users/jobs/FindById.ts
  55. 0 227
      backend/src/modules/DataModule/models/users/schema.ts
  56. 3 5
      backend/src/modules/DataModule/permissions/isAdmin.ts
  57. 2 3
      backend/src/modules/DataModule/permissions/isLoggedIn.ts
  58. 3 5
      backend/src/modules/DataModule/permissions/isModerator.ts
  59. 3 5
      backend/src/modules/DataModule/permissions/modelPermissions/isDj.ts
  60. 2 2
      backend/src/modules/DataModule/permissions/modelPermissions/isLoggedIn.ts
  61. 1 2
      backend/src/modules/DataModule/permissions/modelPermissions/isNewsPublished.ts
  62. 2 2
      backend/src/modules/DataModule/permissions/modelPermissions/isOwner.ts
  63. 6 2
      backend/src/modules/EventsModule.ts
  64. 2 5
      backend/src/modules/EventsModule/Event.ts
  65. 7 7
      backend/src/modules/EventsModule/jobs/Subscribe.spec.ts
  66. 26 14
      backend/src/modules/WebSocketModule.ts

+ 2 - 5
backend/src/Job.ts

@@ -1,7 +1,6 @@
 import { SessionSchema } from "@models/sessions/schema";
 import { SessionSchema } from "@models/sessions/schema";
 import { getErrorMessage } from "@common/utils/getErrorMessage";
 import { getErrorMessage } from "@common/utils/getErrorMessage";
 import { generateUuid } from "@common/utils/generateUuid";
 import { generateUuid } from "@common/utils/generateUuid";
-import { HydratedDocument } from "mongoose";
 import Joi from "joi";
 import Joi from "joi";
 import * as inflection from "inflection";
 import * as inflection from "inflection";
 import JobContext from "@/JobContext";
 import JobContext from "@/JobContext";
@@ -10,7 +9,7 @@ import LogBook, { Log } from "@/LogBook";
 import BaseModule from "./BaseModule";
 import BaseModule from "./BaseModule";
 import EventsModule from "./modules/EventsModule";
 import EventsModule from "./modules/EventsModule";
 import JobCompletedEvent from "./modules/EventsModule/events/JobCompletedEvent";
 import JobCompletedEvent from "./modules/EventsModule/events/JobCompletedEvent";
-import { UserSchema } from "./modules/DataModule/models/users/schema";
+import User from "./modules/DataModule/models/User";
 
 
 export enum JobStatus {
 export enum JobStatus {
 	QUEUED = "QUEUED",
 	QUEUED = "QUEUED",
@@ -187,9 +186,7 @@ export default abstract class Job {
 		| (boolean | CallableFunction)[] = false;
 		| (boolean | CallableFunction)[] = false;
 
 
 	// Check if a given user has generic permission to execute a job, using _hasPermission
 	// Check if a given user has generic permission to execute a job, using _hasPermission
-	public static async hasPermission(
-		user: HydratedDocument<UserSchema> | null
-	) {
+	public static async hasPermission(user: User | null) {
 		const options = Array.isArray(this._hasPermission)
 		const options = Array.isArray(this._hasPermission)
 			? this._hasPermission
 			? this._hasPermission
 			: [this._hasPermission];
 			: [this._hasPermission];

+ 5 - 5
backend/src/JobContext.ts

@@ -3,14 +3,14 @@ import { SessionSchema } from "@/modules/DataModule/models/sessions/schema";
 import Job, { JobOptions } from "@/Job";
 import Job, { JobOptions } from "@/Job";
 import { Log } from "@/LogBook";
 import { Log } from "@/LogBook";
 import DataModule from "@/modules/DataModule";
 import DataModule from "@/modules/DataModule";
-import { UserModel } from "@/modules/DataModule/models/users/schema";
 import { JobDerived } from "./types/JobDerived";
 import { JobDerived } from "./types/JobDerived";
 import assertJobDerived from "./utils/assertJobDerived";
 import assertJobDerived from "./utils/assertJobDerived";
 import {
 import {
 	GetMultipleModelPermissionsResult,
 	GetMultipleModelPermissionsResult,
 	GetSingleModelPermissionsResult
 	GetSingleModelPermissionsResult
-} from "./modules/DataModule/models/users/jobs/GetModelPermissions";
-import { GetPermissionsResult } from "./modules/DataModule/models/users/jobs/GetPermissions";
+} from "./modules/DataModule/models/User/jobs/GetModelPermissions";
+import { GetPermissionsResult } from "./modules/DataModule/models/User/jobs/GetPermissions";
+import User from "./modules/DataModule/models/User";
 
 
 const permissionRegex =
 const permissionRegex =
 	// eslint-disable-next-line max-len
 	// eslint-disable-next-line max-len
@@ -83,9 +83,9 @@ export default class JobContext {
 		if (!this._session?.userId)
 		if (!this._session?.userId)
 			throw new Error("No user found for session");
 			throw new Error("No user found for session");
 
 
-		const User = await DataModule.getModel<UserModel>("users");
+		const User = await DataModule.getModel<User>("users");
 
 
-		const user = await User.findById(this._session.userId);
+		const user = await User.findByPk(this._session.userId);
 
 
 		if (!user) throw new Error("No user found for session");
 		if (!user) throw new Error("No user found for session");
 
 

+ 6 - 6
backend/src/main.ts

@@ -1,4 +1,5 @@
 import * as readline from "node:readline";
 import * as readline from "node:readline";
+import { NewsStatus } from "@models/News/NewsStatus";
 import ModuleManager from "@/ModuleManager";
 import ModuleManager from "@/ModuleManager";
 import LogBook from "@/LogBook";
 import LogBook from "@/LogBook";
 import JobQueue from "@/JobQueue";
 import JobQueue from "@/JobQueue";
@@ -8,7 +9,6 @@ import EventsModule from "./modules/EventsModule";
 // import { NewsModel } from "./modules/DataModule/models/news/schema";
 // import { NewsModel } from "./modules/DataModule/models/news/schema";
 // import { FilterType } from "./modules/DataModule/plugins/getData";
 // import { FilterType } from "./modules/DataModule/plugins/getData";
 import News from "./modules/DataModule/models/News";
 import News from "./modules/DataModule/models/News";
-import { NewsStatus } from "@models/News/NewsStatus";
 import GetData from "./modules/DataModule/models/News/jobs/GetData";
 import GetData from "./modules/DataModule/models/News/jobs/GetData";
 
 
 process.removeAllListeners("uncaughtException");
 process.removeAllListeners("uncaughtException");
@@ -82,16 +82,16 @@ ModuleManager.startup().then(async () => {
 	);
 	);
 
 
 	console.log(
 	console.log(
-		await (new GetData({
+		await new GetData({
 			page: 1,
 			page: 1,
 			pageSize: 10,
 			pageSize: 10,
-			properties: ['id'],
+			properties: ["id"],
 			sort: {
 			sort: {
-				id: 'ascending'
+				id: "ascending"
 			},
 			},
 			queries: [],
 			queries: [],
-			operator: 'and'
-		}).execute())
+			operator: "and"
+		}).execute()
 	);
 	);
 });
 });
 
 

+ 87 - 7
backend/src/modules/DataModule.ts

@@ -2,14 +2,71 @@ import config from "config";
 import { readdir } from "fs/promises";
 import { readdir } from "fs/promises";
 import path from "path";
 import path from "path";
 import { forEachIn } from "@common/utils/forEachIn";
 import { forEachIn } from "@common/utils/forEachIn";
-import { Sequelize, Model as SequelizeModel, ModelStatic } from "sequelize";
+import {
+	Sequelize,
+	Model as SequelizeModel,
+	ModelStatic,
+	DataTypes,
+	Utils
+} from "sequelize";
 import { Dirent } from "fs";
 import { Dirent } from "fs";
 import * as inflection from "inflection";
 import * as inflection from "inflection";
 import BaseModule, { ModuleStatus } from "@/BaseModule";
 import BaseModule, { ModuleStatus } from "@/BaseModule";
-import EventsModule from "./EventsModule";
 import DataModuleJob from "./DataModule/DataModuleJob";
 import DataModuleJob from "./DataModule/DataModuleJob";
 import Job from "@/Job";
 import Job from "@/Job";
 
 
+export type ObjectIdType = string;
+
+// TODO fix TS
+// TODO implement actual checking of ObjectId's
+// TODO move to a better spot
+// Strange behavior would result if we extended DataTypes.ABSTRACT because
+// it's a class wrapped in a Proxy by Utils.classToInvokable.
+class OBJECTID extends DataTypes.ABSTRACT.prototype.constructor {
+	// Mandatory: set the type key
+	static key = "OBJECTID";
+
+	key = OBJECTID.key;
+
+	// Mandatory: complete definition of the new type in the database
+	toSql() {
+		return "VARCHAR(24)";
+	}
+
+	// Optional: validator function
+	// @ts-ignore
+	validate(value, options) {
+		return true;
+		// return (typeof value === 'number') && (!Number.isNaN(value));
+	}
+
+	// Optional: sanitizer
+	// @ts-ignore
+	_sanitize(value) {
+		return value;
+		// Force all numbers to be positive
+		// return value < 0 ? 0 : Math.round(value);
+	}
+
+	// Optional: value stringifier before sending to database
+	// @ts-ignore
+	_stringify(value) {
+		return value;
+		// return value.toString();
+	}
+
+	// Optional: parser for values received from the database
+	// @ts-ignore
+	static parse(value) {
+		return value;
+		// return Number.parseInt(value);
+	}
+}
+
+// Optional: add the new type to DataTypes. Optionally wrap it on `Utils.classToInvokable` to
+// be able to use this datatype directly without having to call `new` on it.
+DataTypes.OBJECTID = Utils.classToInvokable(OBJECTID);
+
 export class DataModule extends BaseModule {
 export class DataModule extends BaseModule {
 	private _sequelize?: Sequelize;
 	private _sequelize?: Sequelize;
 
 
@@ -50,7 +107,8 @@ export class DataModule extends BaseModule {
 	 * setupSequelize - Setup sequelize instance
 	 * setupSequelize - Setup sequelize instance
 	 */
 	 */
 	private async _setupSequelize() {
 	private async _setupSequelize() {
-		const { username, password, host, port, database } = config.get<any>("postgres");
+		const { username, password, host, port, database } =
+			config.get<any>("postgres");
 		this._sequelize = new Sequelize(database, username, password, {
 		this._sequelize = new Sequelize(database, username, password, {
 			host,
 			host,
 			port,
 			port,
@@ -60,6 +118,8 @@ export class DataModule extends BaseModule {
 
 
 		await this._sequelize.authenticate();
 		await this._sequelize.authenticate();
 
 
+		const setupAssociationFunctions: Function[] = [];
+
 		await forEachIn(
 		await forEachIn(
 			await readdir(
 			await readdir(
 				path.resolve(__dirname, `./${this.constructor.name}/models`),
 				path.resolve(__dirname, `./${this.constructor.name}/models`),
@@ -75,7 +135,8 @@ export class DataModule extends BaseModule {
 					default: ModelClass,
 					default: ModelClass,
 					schema,
 					schema,
 					options = {},
 					options = {},
-					setup
+					setup,
+					setupAssociations
 				} = await import(`${modelFile.path}/${modelFile.name}`);
 				} = await import(`${modelFile.path}/${modelFile.name}`);
 
 
 				const tableName = inflection.camelize(
 				const tableName = inflection.camelize(
@@ -91,13 +152,25 @@ export class DataModule extends BaseModule {
 
 
 				if (typeof setup === "function") await setup();
 				if (typeof setup === "function") await setup();
 
 
+				if (typeof setupAssociations === "function")
+					setupAssociationFunctions.push(setupAssociations);
+
 				await this._loadModelEvents(ModelClass.name);
 				await this._loadModelEvents(ModelClass.name);
 
 
 				await this._loadModelJobs(ModelClass.name);
 				await this._loadModelJobs(ModelClass.name);
 			}
 			}
 		);
 		);
 
 
-		this._sequelize.sync();
+		setupAssociationFunctions.forEach(setupAssociation => {
+			setupAssociation();
+		});
+
+		await this._sequelize.sync({ force: true });
+
+		// TODO move to a better spot
+		await this._sequelize.query(
+			`CREATE OR REPLACE VIEW "minifiedUsers" AS SELECT _id, username, name, role FROM users`
+		);
 	}
 	}
 
 
 	// /**
 	// /**
@@ -169,7 +242,10 @@ export class DataModule extends BaseModule {
 		if (this.getStatus() !== ModuleStatus.STARTED)
 		if (this.getStatus() !== ModuleStatus.STARTED)
 			throw new Error("Module not started");
 			throw new Error("Module not started");
 
 
-		return this._sequelize.model(name) as ModelStatic<ModelType>;
+		// TODO check if we want to do it via singularize&camelize, or another way
+		const camelizedName = inflection.singularize(inflection.camelize(name));
+
+		return this._sequelize.model(camelizedName) as ModelStatic<ModelType>; // This fails - news has not been defined
 	}
 	}
 
 
 	private async _loadModelJobs(modelClassName: string) {
 	private async _loadModelJobs(modelClassName: string) {
@@ -190,8 +266,12 @@ export class DataModule extends BaseModule {
 				error instanceof Error &&
 				error instanceof Error &&
 				"code" in error &&
 				"code" in error &&
 				error.code === "ENOENT"
 				error.code === "ENOENT"
-			)
+			) {
+				this.log(
+					`Loading ${modelClassName} jobs failed - folder doesn't exist`
+				);
 				return;
 				return;
+			}
 
 
 			throw error;
 			throw error;
 		}
 		}

+ 0 - 1
backend/src/modules/DataModule/DeleteByIdJob.ts

@@ -1,5 +1,4 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
 import DataModuleJob from "./DataModuleJob";
 import DataModuleJob from "./DataModuleJob";
 
 
 export default abstract class DeleteByIdJob extends DataModuleJob {
 export default abstract class DeleteByIdJob extends DataModuleJob {

+ 0 - 1
backend/src/modules/DataModule/DeleteManyByIdJob.ts

@@ -1,5 +1,4 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
 import DataModuleJob from "./DataModuleJob";
 import DataModuleJob from "./DataModuleJob";
 
 
 export default abstract class DeleteManyByIdJob extends DataModuleJob {
 export default abstract class DeleteManyByIdJob extends DataModuleJob {

+ 0 - 1
backend/src/modules/DataModule/FindByIdJob.ts

@@ -1,5 +1,4 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
 import DataModuleJob from "./DataModuleJob";
 import DataModuleJob from "./DataModuleJob";
 
 
 export default abstract class FindByIdJob extends DataModuleJob {
 export default abstract class FindByIdJob extends DataModuleJob {

+ 0 - 1
backend/src/modules/DataModule/FindManyByIdJob.ts

@@ -1,5 +1,4 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
 import DataModuleJob from "./DataModuleJob";
 import DataModuleJob from "./DataModuleJob";
 
 
 export default abstract class FindManyByIdJob extends DataModuleJob {
 export default abstract class FindManyByIdJob extends DataModuleJob {

+ 32 - 22
backend/src/modules/DataModule/GetDataJob.ts

@@ -1,7 +1,6 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
-import DataModuleJob from "./DataModuleJob";
 import { FindOptions, WhereOptions, Op } from "sequelize";
 import { FindOptions, WhereOptions, Op } from "sequelize";
+import DataModuleJob from "./DataModuleJob";
 
 
 export enum FilterType {
 export enum FilterType {
 	REGEX = "regex",
 	REGEX = "regex",
@@ -49,14 +48,24 @@ export default abstract class GetDataJob extends DataModuleJob {
 
 
 	protected _blacklistedProperties?: string[];
 	protected _blacklistedProperties?: string[];
 
 
-	protected _specialFilters?: Record<string, (query: FindOptions, data: any) => FindOptions>;
+	protected _specialFilters?: Record<
+		string,
+		(query: FindOptions, data: any) => FindOptions
+	>;
 
 
-	protected _specialProperties?: Record<string, (query: FindOptions) => FindOptions>;
+	protected _specialProperties?: Record<
+		string,
+		(query: FindOptions) => FindOptions
+	>;
 
 
-	protected _specialQueries?: Record<string, (query: WhereOptions) => WhereOptions>;
+	protected _specialQueries?: Record<
+		string,
+		(query: WhereOptions) => WhereOptions
+	>;
 
 
 	protected async _execute() {
 	protected async _execute() {
-		const { page, pageSize, properties, sort, queries, operator } = this._payload;
+		const { page, pageSize, properties, sort, queries, operator } =
+			this._payload;
 
 
 		let findQuery: FindOptions = {};
 		let findQuery: FindOptions = {};
 
 
@@ -65,15 +74,11 @@ export default abstract class GetDataJob extends DataModuleJob {
 			if (
 			if (
 				queries.some(query =>
 				queries.some(query =>
 					this._blacklistedProperties!.some(blacklistedProperty =>
 					this._blacklistedProperties!.some(blacklistedProperty =>
-						blacklistedProperty.startsWith(
-							query.filter.property
-						)
+						blacklistedProperty.startsWith(query.filter.property)
 					)
 					)
 				)
 				)
 			)
 			)
-				throw new Error(
-					"Unable to filter by blacklisted property."
-				);
+				throw new Error("Unable to filter by blacklisted property.");
 			if (
 			if (
 				Object.keys(sort).some(property =>
 				Object.keys(sort).some(property =>
 					this._blacklistedProperties!.some(blacklistedProperty =>
 					this._blacklistedProperties!.some(blacklistedProperty =>
@@ -155,9 +160,12 @@ export default abstract class GetDataJob extends DataModuleJob {
 				case FilterType.SPECIAL:
 				case FilterType.SPECIAL:
 					if (
 					if (
 						typeof this._specialFilters?.[filter.property] ===
 						typeof this._specialFilters?.[filter.property] ===
-							"function"
+						"function"
 					) {
 					) {
-						findQuery = this._specialFilters[filter.property](findQuery, data);
+						findQuery = this._specialFilters[filter.property](
+							findQuery,
+							data
+						);
 						newQuery[property] = { [Op.eq]: true };
 						newQuery[property] = { [Op.eq]: true };
 					}
 					}
 					break;
 					break;
@@ -165,9 +173,7 @@ export default abstract class GetDataJob extends DataModuleJob {
 					throw new Error(`Invalid filter type for "${filter}"`);
 					throw new Error(`Invalid filter type for "${filter}"`);
 			}
 			}
 
 
-			if (
-				typeof this._specialQueries?.[filter.property] === "function"
-			) {
+			if (typeof this._specialQueries?.[filter.property] === "function") {
 				return this._specialQueries[filter.property](newQuery);
 				return this._specialQueries[filter.property](newQuery);
 			}
 			}
 
 
@@ -181,10 +187,12 @@ export default abstract class GetDataJob extends DataModuleJob {
 
 
 		// Adds order stage to query if there is at least one column being sorted, responsible for sorting data
 		// Adds order stage to query if there is at least one column being sorted, responsible for sorting data
 		if (Object.keys(sort).length > 0)
 		if (Object.keys(sort).length > 0)
-			findQuery.order = Object.entries(sort).map(([property, direction]) => [
-				property,
-				direction === "ascending" ? "ASC" : "DESC"
-			]);
+			findQuery.order = Object.entries(sort).map(
+				([property, direction]) => [
+					property,
+					direction === "ascending" ? "ASC" : "DESC"
+				]
+			);
 
 
 		findQuery.attributes = {
 		findQuery.attributes = {
 			include: properties,
 			include: properties,
@@ -194,7 +202,9 @@ export default abstract class GetDataJob extends DataModuleJob {
 		findQuery.limit = pageSize;
 		findQuery.limit = pageSize;
 
 
 		// Executes the query
 		// Executes the query
-		const { rows, count } = (await this.getModel().findAndCountAll(findQuery));
+		const { rows, count } = await this.getModel().findAndCountAll(
+			findQuery
+		);
 
 
 		const data = rows.map(model => model.toJSON()); // TODO: Review generally
 		const data = rows.map(model => model.toJSON()); // TODO: Review generally
 
 

+ 0 - 1
backend/src/modules/DataModule/UpdateByIdJob.ts

@@ -1,5 +1,4 @@
 import Joi from "joi";
 import Joi from "joi";
-import DataModule from "../DataModule";
 import DataModuleJob from "./DataModuleJob";
 import DataModuleJob from "./DataModuleJob";
 
 
 export default abstract class UpdateByIdJob extends DataModuleJob {
 export default abstract class UpdateByIdJob extends DataModuleJob {

+ 55 - 0
backend/src/modules/DataModule/models/MinifiedUser.ts

@@ -0,0 +1,55 @@
+import {
+	DataTypes,
+	Model,
+	InferAttributes,
+	InferCreationAttributes,
+	CreationOptional
+} from "sequelize";
+import { ObjectIdType } from "@/modules/DataModule";
+import { UserRole } from "./User/UserRole";
+import { schema as userSchema } from "./User";
+
+export class MinifiedUser extends Model<
+	InferAttributes<MinifiedUser>,
+	InferCreationAttributes<MinifiedUser>
+> {
+	declare _id: CreationOptional<ObjectIdType>;
+
+	declare username: string;
+
+	declare name: string;
+
+	declare role: CreationOptional<UserRole>;
+
+	declare createdAt: CreationOptional<Date>;
+
+	declare updatedAt: CreationOptional<Date>;
+}
+
+export const schema = {
+	_id: userSchema._id,
+	username: userSchema.username,
+	name: userSchema.name,
+	role: userSchema.role,
+	_name: {
+		type: DataTypes.VIRTUAL,
+		get() {
+			return `minifiedUsers`;
+		}
+	}
+};
+
+export const options = {
+	timestamps: false
+};
+
+export const setup = async () => {
+	// Session.afterSave(async record => {
+	// });
+	// Session.afterDestroy(async record => {
+	// });
+};
+
+export default MinifiedUser;
+
+// When we get have to do more with TS, check out https://github.com/sequelize/sequelize/issues/3078#issuecomment-1226261914

+ 2 - 1
backend/src/modules/DataModule/models/minifiedUsers/events/MinifiedUserCreatedEvent.ts → backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserCreatedEvent.ts

@@ -1,8 +1,9 @@
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
+import MinifiedUser from "../../MinifiedUser";
 
 
 export default abstract class MinifiedUserCreatedEvent extends ModelCreatedEvent {
 export default abstract class MinifiedUserCreatedEvent extends ModelCreatedEvent {
-	protected static _modelName = "minifiedUsers";
+	protected static _model = MinifiedUser;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/minifiedUsers/events/MinifiedUserDeletedEvent.ts → backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserDeletedEvent.ts

@@ -1,9 +1,10 @@
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import doesModelExist from "@/modules/DataModule/permissions/modelPermissions/doesModelExist";
 import doesModelExist from "@/modules/DataModule/permissions/modelPermissions/doesModelExist";
+import MinifiedUser from "../../MinifiedUser";
 
 
 export default abstract class MinifiedUserDeletedEvent extends ModelDeletedEvent {
 export default abstract class MinifiedUserDeletedEvent extends ModelDeletedEvent {
-	protected static _modelName = "minifiedUsers";
+	protected static _model = MinifiedUser;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 
 

+ 2 - 1
backend/src/modules/DataModule/models/minifiedUsers/events/MinifiedUserUpdatedEvent.ts → backend/src/modules/DataModule/models/MinifiedUser/events/MinifiedUserUpdatedEvent.ts

@@ -1,9 +1,10 @@
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import doesModelExist from "@/modules/DataModule/permissions/modelPermissions/doesModelExist";
 import doesModelExist from "@/modules/DataModule/permissions/modelPermissions/doesModelExist";
+import MinifiedUser from "../../MinifiedUser";
 
 
 export default abstract class MinifiedUserUpdatedEvent extends ModelUpdatedEvent {
 export default abstract class MinifiedUserUpdatedEvent extends ModelUpdatedEvent {
-	protected static _modelName = "minifiedUsers";
+	protected static _model = MinifiedUser;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 
 

+ 2 - 1
backend/src/modules/DataModule/models/minifiedUsers/jobs/FindById.ts → backend/src/modules/DataModule/models/MinifiedUser/jobs/FindById.ts

@@ -1,7 +1,8 @@
 import FindByIdJob from "@/modules/DataModule/FindByIdJob";
 import FindByIdJob from "@/modules/DataModule/FindByIdJob";
+import MinifiedUser from "../../MinifiedUser";
 
 
 export default class FindById extends FindByIdJob {
 export default class FindById extends FindByIdJob {
-	protected static _modelName = "minifiedUsers";
+	protected static _model = MinifiedUser;
 
 
 	protected static _hasPermission = true;
 	protected static _hasPermission = true;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/minifiedUsers/jobs/FindManyById.ts → backend/src/modules/DataModule/models/MinifiedUser/jobs/FindManyById.ts

@@ -1,7 +1,8 @@
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
+import MinifiedUser from "../../MinifiedUser";
 
 
 export default class FindManyById extends FindManyByIdJob {
 export default class FindManyById extends FindManyByIdJob {
-	protected static _modelName = "minifiedUsers";
+	protected static _model = MinifiedUser;
 
 
 	protected static _hasPermission = true;
 	protected static _hasPermission = true;
 }
 }

+ 0 - 0
backend/src/modules/DataModule/models/minifiedUsers/migrations/170526579600-create-minified-users-view.ts → backend/src/modules/DataModule/models/MinifiedUser/migrations/170526579600-create-minified-users-view.ts


+ 72 - 0
backend/src/modules/DataModule/models/MinifiedUser/schema.ts

@@ -0,0 +1,72 @@
+// import { Model, Schema, SchemaOptions, SchemaTypes } from "mongoose";
+// import { UserSchema } from "../User/schema";
+// import { UserRole } from "../User/UserRole";
+// import { UserAvatarType } from "../User/UserAvatarType";
+// import { UserAvatarColor } from "../User/UserAvatarColor";
+
+// export type MinifiedUserSchema = Pick<
+// 	UserSchema,
+// 	| "_id"
+// 	| "name"
+// 	| "username"
+// 	| "location"
+// 	| "bio"
+// 	| "role"
+// 	| "avatar"
+// 	| "createdAt"
+// 	| "updatedAt"
+// >;
+
+// export type MinifiedUserModel = Model<MinifiedUserSchema>;
+
+// export const schema = new Schema<MinifiedUserSchema, MinifiedUserModel>(
+// 	{
+// 		username: {
+// 			type: SchemaTypes.String,
+// 			required: true
+// 		},
+// 		role: {
+// 			type: SchemaTypes.String,
+// 			enum: Object.values(UserRole),
+// 			required: true
+// 		},
+// 		avatar: {
+// 			type: {
+// 				type: SchemaTypes.String,
+// 				enum: Object.values(UserAvatarType),
+// 				required: true
+// 			},
+// 			url: {
+// 				type: SchemaTypes.String,
+// 				required: false
+// 			},
+// 			color: {
+// 				type: SchemaTypes.String,
+// 				enum: Object.values(UserAvatarColor),
+// 				required: false
+// 			}
+// 		},
+// 		name: {
+// 			type: SchemaTypes.String,
+// 			required: true
+// 		},
+// 		location: {
+// 			type: SchemaTypes.String,
+// 			required: false
+// 		},
+// 		bio: {
+// 			type: SchemaTypes.String,
+// 			required: false
+// 		}
+// 	},
+// 	{
+// 		autoCreate: false,
+// 		autoIndex: false,
+// 		collection: "minifiedUsers",
+// 		patchHistory: { enabled: false }
+// 	}
+// );
+
+// export type UserSchemaType = typeof schema;
+
+// export type UserSchemaOptions = SchemaOptions<UserSchema>;

+ 78 - 31
backend/src/modules/DataModule/models/News.ts

@@ -1,19 +1,20 @@
 import {
 import {
-	Sequelize,
 	DataTypes,
 	DataTypes,
 	Model,
 	Model,
 	InferAttributes,
 	InferAttributes,
 	InferCreationAttributes,
 	InferCreationAttributes,
-	CreationOptional,
+	CreationOptional
 } from "sequelize";
 } from "sequelize";
 import { NewsStatus } from "@models/News/NewsStatus";
 import { NewsStatus } from "@models/News/NewsStatus";
 import EventsModule from "@/modules/EventsModule";
 import EventsModule from "@/modules/EventsModule";
+import User from "./User";
+import { ObjectIdType } from "@/modules/DataModule";
 
 
 export class News extends Model<
 export class News extends Model<
 	InferAttributes<News>,
 	InferAttributes<News>,
 	InferCreationAttributes<News>
 	InferCreationAttributes<News>
 > {
 > {
-	declare id: CreationOptional<number>;
+	declare _id: CreationOptional<ObjectIdType>;
 
 
 	declare title: string;
 	declare title: string;
 
 
@@ -29,10 +30,11 @@ export class News extends Model<
 }
 }
 
 
 export const schema = {
 export const schema = {
-	id: {
-		type: DataTypes.BIGINT,
-		autoIncrement: true,
-		primaryKey: true
+	_id: {
+		type: DataTypes.OBJECTID,
+		allowNull: false,
+		primaryKey: true,
+		defaultValue: () => "66d6d2d2065de4fd650278be" // TODO add ObjectId generator
 	},
 	},
 	title: {
 	title: {
 		type: DataTypes.STRING,
 		type: DataTypes.STRING,
@@ -52,12 +54,15 @@ export const schema = {
 		defaultValue: false,
 		defaultValue: false,
 		allowNull: false
 		allowNull: false
 	},
 	},
-	createdBy: {
-		type: DataTypes.BIGINT,
-		allowNull: false
-	},
 	createdAt: DataTypes.DATE,
 	createdAt: DataTypes.DATE,
-	updatedAt: DataTypes.DATE
+	updatedAt: DataTypes.DATE,
+
+	_name: {
+		type: DataTypes.VIRTUAL,
+		get() {
+			return `news`;
+		}
+	}
 };
 };
 
 
 export const options = {};
 export const options = {};
@@ -66,38 +71,80 @@ export const setup = async () => {
 	News.afterSave(async record => {
 	News.afterSave(async record => {
 		const oldDoc = record.previous();
 		const oldDoc = record.previous();
 		const doc = record.get();
 		const doc = record.get();
-	
+
 		if (oldDoc.status === doc.status) return;
 		if (oldDoc.status === doc.status) return;
-	
+
 		if (doc.status === NewsStatus.PUBLISHED) {
 		if (doc.status === NewsStatus.PUBLISHED) {
-			const EventClass = EventsModule.getEvent(
-				`data.news.published`
+			const EventClass = EventsModule.getEvent(`data.news.published`);
+			await EventsModule.publish(
+				new EventClass({
+					doc
+				})
 			);
 			);
-			await EventsModule.publish(new EventClass({
-				doc
-			}));
 		} else if (oldDoc.status === NewsStatus.PUBLISHED) {
 		} else if (oldDoc.status === NewsStatus.PUBLISHED) {
-			const EventClass = EventsModule.getEvent(
-				`data.news.unpublished`
+			const EventClass = EventsModule.getEvent(`data.news.unpublished`);
+			await EventsModule.publish(
+				new EventClass(
+					{
+						oldDoc
+					},
+					oldDoc._id!.toString()
+				)
 			);
 			);
-			await EventsModule.publish(new EventClass({
-				oldDoc
-			}, oldDoc.id!.toString()));
 		}
 		}
 	});
 	});
-	
+
 	News.afterDestroy(async record => {
 	News.afterDestroy(async record => {
 		const oldDoc = record.previous();
 		const oldDoc = record.previous();
-	
+
 		if (oldDoc.status === NewsStatus.PUBLISHED) {
 		if (oldDoc.status === NewsStatus.PUBLISHED) {
-			const EventClass = EventsModule.getEvent(
-				`data.news.unpublished`
+			const EventClass = EventsModule.getEvent(`data.news.unpublished`);
+			await EventsModule.publish(
+				new EventClass(
+					{
+						oldDoc
+					},
+					oldDoc._id!.toString()
+				)
 			);
 			);
-			await EventsModule.publish(new EventClass({
-				oldDoc
-			}, oldDoc.id!.toString()));
 		}
 		}
 	});
 	});
+
+	News.addHook("afterFind", (_news, options) => {
+		if (!_news) return;
+
+		// TODO improve TS
+		let news: Model<
+			InferAttributes<
+				News,
+				{
+					omit: never;
+				}
+			>,
+			InferCreationAttributes<
+				News,
+				{
+					omit: never;
+				}
+			>
+		>[] = [];
+
+		if (Array.isArray(_news)) news = _news;
+		// @ts-ignore - possibly not needed after TS update
+		else news.push(_news);
+
+		news.forEach(news => {
+			news.dataValues.createdBy = {
+				_id: news.dataValues.createdBy,
+				_name: "minifiedUsers"
+			};
+		});
+	});
+};
+
+export const setupAssociations = () => {
+	News.belongsTo(User, { foreignKey: "createdBy" });
+	User.hasMany(News, { foreignKey: "createdBy" });
 };
 };
 
 
 export default News;
 export default News;

+ 9 - 3
backend/src/modules/DataModule/models/News/jobs/GetData.ts

@@ -1,18 +1,24 @@
 import News from "@models/News";
 import News from "@models/News";
+import { FindOptions, WhereOptions, Op } from "sequelize";
 import GetDataJob from "@/modules/DataModule/GetDataJob";
 import GetDataJob from "@/modules/DataModule/GetDataJob";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
-import { FindOptions, WhereOptions, Op } from "sequelize";
 
 
 export default class GetData extends GetDataJob {
 export default class GetData extends GetDataJob {
 	protected static _model = News;
 	protected static _model = News;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 
 
-	protected _specialProperties?: Record<string, (query: FindOptions<News>) => FindOptions<News>> = {
+	protected _specialProperties?: Record<
+		string,
+		(query: FindOptions<News>) => FindOptions<News>
+	> = {
 		createdBy: query => query
 		createdBy: query => query
 	};
 	};
 
 
-	protected _specialQueries?: Record<string, (where: WhereOptions<News>) => WhereOptions<News>> = {
+	protected _specialQueries?: Record<
+		string,
+		(where: WhereOptions<News>) => WhereOptions<News>
+	> = {
 		createdBy: where => ({
 		createdBy: where => ({
 			...where,
 			...where,
 			[Op.or]: [
 			[Op.or]: [

+ 1 - 1
backend/src/modules/DataModule/models/News/jobs/Newest.ts

@@ -15,7 +15,7 @@ export default class Newest extends DataModuleJob {
 
 
 	protected async _execute() {
 	protected async _execute() {
 		return this.getModel().findAll({
 		return this.getModel().findAll({
-			order: [["created_at", "DESC"]],
+			order: [["createdAt", "DESC"]],
 			limit: this._payload?.limit,
 			limit: this._payload?.limit,
 			where: {
 			where: {
 				status: NewsStatus.PUBLISHED
 				status: NewsStatus.PUBLISHED

+ 52 - 0
backend/src/modules/DataModule/models/Session.ts

@@ -0,0 +1,52 @@
+import {
+	DataTypes,
+	Model,
+	InferAttributes,
+	InferCreationAttributes,
+	CreationOptional
+} from "sequelize";
+import { ObjectIdType } from "@/modules/DataModule";
+
+export class Session extends Model<
+	InferAttributes<Session>,
+	InferCreationAttributes<Session>
+> {
+	declare sessionId: ObjectIdType;
+
+	declare userId: number;
+
+	declare createdAt: CreationOptional<Date>;
+
+	declare updatedAt: CreationOptional<Date>;
+}
+
+export const schema = {
+	sessionId: {
+		type: DataTypes.OBJECTID,
+		allowNull: false,
+		primaryKey: true
+	},
+	userId: {
+		type: DataTypes.OBJECTID,
+		allowNull: false
+	},
+	createdAt: DataTypes.DATE,
+	updatedAt: DataTypes.DATE,
+	_name: {
+		type: DataTypes.VIRTUAL,
+		get() {
+			return `session`;
+		}
+	}
+};
+
+export const options = {};
+
+export const setup = async () => {
+	// Session.afterSave(async record => {
+	// });
+	// Session.afterDestroy(async record => {
+	// });
+};
+
+export default Session;

+ 2 - 1
backend/src/modules/DataModule/models/sessions/events/SessionCreatedEvent.ts → backend/src/modules/DataModule/models/Session/events/SessionCreatedEvent.ts

@@ -1,5 +1,6 @@
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
+import Session from "../../Session";
 
 
 export default abstract class SessionCreatedEvent extends ModelCreatedEvent {
 export default abstract class SessionCreatedEvent extends ModelCreatedEvent {
-	protected static _modelName = "sessions";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/sessions/events/SessionDeletedEvent.ts → backend/src/modules/DataModule/models/Session/events/SessionDeletedEvent.ts

@@ -1,5 +1,6 @@
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
+import Session from "../../Session";
 
 
 export default abstract class SessionDeletedEvent extends ModelDeletedEvent {
 export default abstract class SessionDeletedEvent extends ModelDeletedEvent {
-	protected static _modelName = "sessions";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/sessions/events/SessionUpdatedEvent.ts → backend/src/modules/DataModule/models/Session/events/SessionUpdatedEvent.ts

@@ -1,5 +1,6 @@
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
+import Session from "../../Session";
 
 
 export default abstract class SessionUpdatedEvent extends ModelUpdatedEvent {
 export default abstract class SessionUpdatedEvent extends ModelUpdatedEvent {
-	protected static _modelName = "sessions";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/Create.ts → backend/src/modules/DataModule/models/Session/jobs/Create.ts

@@ -1,5 +1,6 @@
 import CreateJob from "@/modules/DataModule/CreateJob";
 import CreateJob from "@/modules/DataModule/CreateJob";
+import Session from "../../Session";
 
 
 export default class Create extends CreateJob {
 export default class Create extends CreateJob {
-	protected static _modelName = "users";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/sessions/jobs/DeleteById.ts → backend/src/modules/DataModule/models/Session/jobs/DeleteById.ts

@@ -1,5 +1,6 @@
 import DeleteByIdJob from "@/modules/DataModule/DeleteByIdJob";
 import DeleteByIdJob from "@/modules/DataModule/DeleteByIdJob";
+import Session from "../../Session";
 
 
 export default class DeleteById extends DeleteByIdJob {
 export default class DeleteById extends DeleteByIdJob {
-	protected static _modelName = "sessions";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/sessions/jobs/FindById.ts → backend/src/modules/DataModule/models/Session/jobs/FindById.ts

@@ -1,5 +1,6 @@
 import FindByIdJob from "@/modules/DataModule/FindByIdJob";
 import FindByIdJob from "@/modules/DataModule/FindByIdJob";
+import Session from "../../Session";
 
 
 export default class FindById extends FindByIdJob {
 export default class FindById extends FindByIdJob {
-	protected static _modelName = "sessions";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/FindManyById.ts → backend/src/modules/DataModule/models/Session/jobs/FindManyById.ts

@@ -1,5 +1,6 @@
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
+import Session from "../../Session";
 
 
 export default class FindManyById extends FindManyByIdJob {
 export default class FindManyById extends FindManyByIdJob {
-	protected static _modelName = "users";
+	protected static _model = Session;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/UpdateById.ts → backend/src/modules/DataModule/models/Session/jobs/UpdateById.ts

@@ -1,5 +1,6 @@
 import UpdateByIdJob from "@/modules/DataModule/UpdateByIdJob";
 import UpdateByIdJob from "@/modules/DataModule/UpdateByIdJob";
+import Session from "../../Session";
 
 
 export default class UpdateById extends UpdateByIdJob {
 export default class UpdateById extends UpdateByIdJob {
-	protected static _modelName = "users";
+	protected static _model = Session;
 }
 }

+ 25 - 0
backend/src/modules/DataModule/models/Session/schema.ts

@@ -0,0 +1,25 @@
+// import { Model, Schema, SchemaTypes, Types } from "mongoose";
+// import { BaseSchema } from "@/modules/DataModule/types/Schemas";
+
+// export interface SessionSchema extends BaseSchema {
+// 	userId: Types.ObjectId;
+// }
+
+// export type SessionModel = Model<SessionSchema>;
+
+// export const schema = new Schema<SessionSchema, SessionModel>(
+// 	{
+// 		userId: {
+// 			type: SchemaTypes.ObjectId,
+// 			ref: "users",
+// 			required: true
+// 		}
+// 	},
+// 	{
+// 		patchHistory: {
+// 			enabled: false
+// 		}
+// 	}
+// );
+
+// export type SessionSchemaType = typeof schema;

+ 74 - 0
backend/src/modules/DataModule/models/User.ts

@@ -0,0 +1,74 @@
+import {
+	DataTypes,
+	Model,
+	InferAttributes,
+	InferCreationAttributes,
+	CreationOptional
+} from "sequelize";
+import { UserRole } from "./User/UserRole";
+import { ObjectIdType } from "@/modules/DataModule";
+
+export class User extends Model<
+	InferAttributes<User>,
+	InferCreationAttributes<User>
+> {
+	declare _id: CreationOptional<ObjectIdType>;
+
+	declare username: string;
+
+	declare name: string;
+
+	declare role: CreationOptional<UserRole>;
+
+	declare createdAt: CreationOptional<Date>;
+
+	declare updatedAt: CreationOptional<Date>;
+}
+
+export const schema = {
+	_id: {
+		type: DataTypes.OBJECTID,
+		primaryKey: true,
+		allowNull: false,
+		defaultValue: () => "66d6d2d2065de4fd650278be"
+	},
+	username: {
+		type: DataTypes.STRING,
+		allowNull: false
+	},
+	name: {
+		type: DataTypes.STRING,
+		allowNull: false
+	},
+	role: {
+		type: DataTypes.ENUM(...Object.values(UserRole)),
+		defaultValue: UserRole.USER,
+		allowNull: false
+	},
+	// createdBy: {
+	// 	type: DataTypes.OBJECTID,
+	// 	allowNull: false
+	// },
+	createdAt: DataTypes.DATE,
+	updatedAt: DataTypes.DATE,
+	_name: {
+		type: DataTypes.VIRTUAL,
+		get() {
+			return `users`;
+		}
+	}
+};
+
+export const options = {};
+
+export const setup = async () => {
+	// User.afterSave(async record => {});
+
+	// User.afterDestroy(async record => {});
+
+	User.addHook("afterFind", (user, options) => {
+		console.log("AFTER FIND USER", user, options);
+	});
+};
+
+export default User;

+ 0 - 0
backend/src/modules/DataModule/models/users/UserAvatarColor.ts → backend/src/modules/DataModule/models/User/UserAvatarColor.ts


+ 0 - 0
backend/src/modules/DataModule/models/users/UserAvatarType.ts → backend/src/modules/DataModule/models/User/UserAvatarType.ts


+ 0 - 0
backend/src/modules/DataModule/models/users/UserRole.ts → backend/src/modules/DataModule/models/User/UserRole.ts


+ 0 - 0
backend/src/modules/DataModule/models/users/config.ts → backend/src/modules/DataModule/models/User/config.ts


+ 2 - 1
backend/src/modules/DataModule/models/users/events/UserCreatedEvent.ts → backend/src/modules/DataModule/models/User/events/UserCreatedEvent.ts

@@ -1,8 +1,9 @@
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
 import ModelCreatedEvent from "@/modules/DataModule/ModelCreatedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
+import User from "../../User";
 
 
 export default abstract class UserCreatedEvent extends ModelCreatedEvent {
 export default abstract class UserCreatedEvent extends ModelCreatedEvent {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/events/UserDeletedEvent.ts → backend/src/modules/DataModule/models/User/events/UserDeletedEvent.ts

@@ -1,8 +1,9 @@
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
 import ModelDeletedEvent from "@/modules/DataModule/ModelDeletedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
+import User from "../../User";
 
 
 export default abstract class UserDeletedEvent extends ModelDeletedEvent {
 export default abstract class UserDeletedEvent extends ModelDeletedEvent {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/events/UserUpdatedEvent.ts → backend/src/modules/DataModule/models/User/events/UserUpdatedEvent.ts

@@ -1,8 +1,9 @@
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
 import ModelUpdatedEvent from "@/modules/DataModule/ModelUpdatedEvent";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
+import User from "../../User";
 
 
 export default abstract class UserUpdatedEvent extends ModelUpdatedEvent {
 export default abstract class UserUpdatedEvent extends ModelUpdatedEvent {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 	// TODO maybe allow this for the current logged in user?
 	// TODO maybe allow this for the current logged in user?

+ 0 - 0
backend/src/modules/DataModule/models/users/getData.ts → backend/src/modules/DataModule/models/User/getData.ts


+ 2 - 1
backend/src/modules/DataModule/models/sessions/jobs/Create.ts → backend/src/modules/DataModule/models/User/jobs/Create.ts

@@ -1,5 +1,6 @@
 import CreateJob from "@/modules/DataModule/CreateJob";
 import CreateJob from "@/modules/DataModule/CreateJob";
+import User from "../../User";
 
 
 export default class Create extends CreateJob {
 export default class Create extends CreateJob {
-	protected static _modelName = "sessions";
+	protected static _model = User;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/DeleteById.ts → backend/src/modules/DataModule/models/User/jobs/DeleteById.ts

@@ -1,5 +1,6 @@
 import DeleteByIdJob from "@/modules/DataModule/DeleteByIdJob";
 import DeleteByIdJob from "@/modules/DataModule/DeleteByIdJob";
+import User from "../../User";
 
 
 export default class DeleteById extends DeleteByIdJob {
 export default class DeleteById extends DeleteByIdJob {
-	protected static _modelName = "users";
+	protected static _model = User;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/DeleteManyById.ts → backend/src/modules/DataModule/models/User/jobs/DeleteManyById.ts

@@ -1,5 +1,6 @@
 import DeleteManyByIdJob from "@/modules/DataModule/DeleteManyByIdJob";
 import DeleteManyByIdJob from "@/modules/DataModule/DeleteManyByIdJob";
+import User from "../../User";
 
 
 export default class DeleteManyById extends DeleteManyByIdJob {
 export default class DeleteManyById extends DeleteManyByIdJob {
-	protected static _modelName = "users";
+	protected static _model = User;
 }
 }

+ 12 - 0
backend/src/modules/DataModule/models/User/jobs/FindById.ts

@@ -0,0 +1,12 @@
+import FindByIdJob from "@/modules/DataModule/FindByIdJob";
+import User from "../../User";
+
+export default class FindById extends FindByIdJob {
+	protected static _model = User;
+
+	protected static _hasModelPermission = this._isSelf;
+
+	protected static _isSelf(model: User, user?: User) {
+		return model._id === user?._id;
+	}
+}

+ 2 - 1
backend/src/modules/DataModule/models/sessions/jobs/FindManyById.ts → backend/src/modules/DataModule/models/User/jobs/FindManyById.ts

@@ -1,5 +1,6 @@
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
 import FindManyByIdJob from "@/modules/DataModule/FindManyByIdJob";
+import User from "../../User";
 
 
 export default class FindManyById extends FindManyByIdJob {
 export default class FindManyById extends FindManyByIdJob {
-	protected static _modelName = "sessions";
+	protected static _model = User;
 }
 }

+ 2 - 1
backend/src/modules/DataModule/models/users/jobs/GetData.ts → backend/src/modules/DataModule/models/User/jobs/GetData.ts

@@ -1,8 +1,9 @@
 import GetDataJob from "@/modules/DataModule/GetDataJob";
 import GetDataJob from "@/modules/DataModule/GetDataJob";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
 import isAdmin from "@/modules/DataModule/permissions/isAdmin";
+import User from "../../User";
 
 
 export default class GetData extends GetDataJob {
 export default class GetData extends GetDataJob {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = isAdmin;
 	protected static _hasPermission = isAdmin;
 }
 }

+ 7 - 3
backend/src/modules/DataModule/models/users/jobs/GetModelPermissions.ts → backend/src/modules/DataModule/models/User/jobs/GetModelPermissions.ts

@@ -6,6 +6,7 @@ import GetPermissions, { GetPermissionsResult } from "./GetPermissions";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 import DataModuleEvent from "@/modules/DataModule/DataModuleEvent";
 import DataModuleEvent from "@/modules/DataModule/DataModuleEvent";
 import { EventClass } from "@/modules/EventsModule/Event";
 import { EventClass } from "@/modules/EventsModule/Event";
+import User from "../../User";
 
 
 export type GetSingleModelPermissionsResult = Record<string, boolean>; // Returned when getting permissions for a single modelId
 export type GetSingleModelPermissionsResult = Record<string, boolean>; // Returned when getting permissions for a single modelId
 export type GetMultipleModelPermissionsResult = Record<
 export type GetMultipleModelPermissionsResult = Record<
@@ -25,7 +26,7 @@ export type GetModelPermissionsResult =
  * If no modelId is provided, it will not include jobs that apply specifically to a single modelId (those ending in ById)
  * If no modelId is provided, it will not include jobs that apply specifically to a single modelId (those ending in ById)
  */
  */
 export default class GetModelPermissions extends DataModuleJob {
 export default class GetModelPermissions extends DataModuleJob {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = true;
 	protected static _hasPermission = true;
 
 
@@ -78,7 +79,7 @@ export default class GetModelPermissions extends DataModuleJob {
 			const cached = await CacheModule.get(cacheKey);
 			const cached = await CacheModule.get(cacheKey);
 			if (cached) return cached;
 			if (cached) return cached;
 
 
-			const model = await Model.findById(modelId);
+			const model = await Model.findByPk(modelId);
 			if (!model) throw new Error("Model not found");
 			if (!model) throw new Error("Model not found");
 
 
 			const modelPermissions = await this._getPermissionsForModel(
 			const modelPermissions = await this._getPermissionsForModel(
@@ -108,8 +109,11 @@ export default class GetModelPermissions extends DataModuleJob {
 			uncachedModelIds.push(modelId);
 			uncachedModelIds.push(modelId);
 		});
 		});
 
 
+		console.log("GMP", uncachedModelIds);
 		// For the modelIds that were not cached, get the documents from MongoDB
 		// For the modelIds that were not cached, get the documents from MongoDB
-		const uncachedModels = await Model.find({ _id: uncachedModelIds });
+		const uncachedModels = await Model.findAll({
+			where: { _id: uncachedModelIds }
+		});
 
 
 		// Loop through the modelIds that were not cached, and get the permissions for each one individually
 		// Loop through the modelIds that were not cached, and get the permissions for each one individually
 		await forEachIn(uncachedModelIds, async modelId => {
 		await forEachIn(uncachedModelIds, async modelId => {

+ 4 - 5
backend/src/modules/DataModule/models/users/jobs/GetPermissions.ts → backend/src/modules/DataModule/models/User/jobs/GetPermissions.ts

@@ -1,12 +1,11 @@
-import { HydratedDocument } from "mongoose";
 import { forEachIn } from "@common/utils/forEachIn";
 import { forEachIn } from "@common/utils/forEachIn";
 import CacheModule from "@/modules/CacheModule";
 import CacheModule from "@/modules/CacheModule";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 import DataModuleJob from "@/modules/DataModule/DataModuleJob";
-import { UserSchema } from "../schema";
 import ModuleManager from "@/ModuleManager";
 import ModuleManager from "@/ModuleManager";
 import Job from "@/Job";
 import Job from "@/Job";
 import Event from "@/modules/EventsModule/Event";
 import Event from "@/modules/EventsModule/Event";
 import { UserRole } from "../UserRole";
 import { UserRole } from "../UserRole";
+import User from "../../User";
 
 
 export type GetPermissionsResult = Record<string, boolean>;
 export type GetPermissionsResult = Record<string, boolean>;
 
 
@@ -15,7 +14,7 @@ export type GetPermissionsResult = Record<string, boolean>;
  * Permissions are cached. No cache invalidation machanism has been implemented yet, but it expires naturally after 6 minutes.
  * Permissions are cached. No cache invalidation machanism has been implemented yet, but it expires naturally after 6 minutes.
  */
  */
 export default class GetPermissions extends DataModuleJob {
 export default class GetPermissions extends DataModuleJob {
-	protected static _modelName = "users";
+	protected static _model = User;
 
 
 	protected static _hasPermission = true;
 	protected static _hasPermission = true;
 
 
@@ -38,7 +37,7 @@ export default class GetPermissions extends DataModuleJob {
 		return permissions;
 		return permissions;
 	}
 	}
 
 
-	protected async _getPermissions(user: HydratedDocument<UserSchema> | null) {
+	protected async _getPermissions(user: User | null) {
 		const jobs = this._getAllJobs();
 		const jobs = this._getAllJobs();
 		const events = this._getAllEvents();
 		const events = this._getAllEvents();
 
 
@@ -68,7 +67,7 @@ export default class GetPermissions extends DataModuleJob {
 	}
 	}
 
 
 	protected _getFrontendViewPermissions(
 	protected _getFrontendViewPermissions(
-		user: HydratedDocument<UserSchema> | null
+		user: User | null
 	): Record<string, boolean> {
 	): Record<string, boolean> {
 		if (!user) return {};
 		if (!user) return {};
 
 

+ 2 - 1
backend/src/modules/DataModule/models/sessions/jobs/UpdateById.ts → backend/src/modules/DataModule/models/User/jobs/UpdateById.ts

@@ -1,5 +1,6 @@
 import UpdateByIdJob from "@/modules/DataModule/UpdateByIdJob";
 import UpdateByIdJob from "@/modules/DataModule/UpdateByIdJob";
+import User from "../../User";
 
 
 export default class UpdateById extends UpdateByIdJob {
 export default class UpdateById extends UpdateByIdJob {
-	protected static _modelName = "sessions";
+	protected static _model = User;
 }
 }

+ 227 - 0
backend/src/modules/DataModule/models/User/schema.ts

@@ -0,0 +1,227 @@
+// import { Model, Schema, SchemaOptions, SchemaTypes, Types } from "mongoose";
+// import { BaseSchema } from "@/modules/DataModule/types/Schemas";
+// import config from "./config";
+// import { UserRole } from "./UserRole";
+// import { UserAvatarType } from "./UserAvatarType";
+// import { UserAvatarColor } from "./UserAvatarColor";
+
+// export interface UserSchema extends BaseSchema {
+// 	username: string;
+// 	role: UserRole;
+// 	email: {
+// 		address: string;
+// 		verified: boolean;
+// 		verificationToken?: string;
+// 	};
+// 	avatar: {
+// 		type: UserAvatarType;
+// 		url?: string;
+// 		color?: UserAvatarColor;
+// 	};
+// 	services: {
+// 		password?: {
+// 			password: string;
+// 			reset: {
+// 				code: string;
+// 				expires: number;
+// 			};
+// 			set: {
+// 				code: string;
+// 				expires: number;
+// 			};
+// 		};
+// 		github?: {
+// 			id: number;
+// 			access_token: string;
+// 		};
+// 	};
+// 	statistics: {
+// 		songsRequested: number;
+// 	};
+// 	likedSongsPlaylist: Types.ObjectId;
+// 	dislikedSongsPlaylist: Types.ObjectId;
+// 	favoriteStations: Types.ObjectId[];
+// 	name: string;
+// 	location?: string;
+// 	bio?: string;
+// 	preferences: {
+// 		orderOfPlaylists: Types.ObjectId[];
+// 		nightmode: boolean;
+// 		autoSkipDisliked: boolean;
+// 		activityLogPublic: boolean;
+// 		anonymousSongRequests: boolean;
+// 		activityWatch: boolean;
+// 	};
+// }
+
+// export type UserModel = Model<UserSchema>;
+
+// export const schema = new Schema<UserSchema, UserModel>(
+// 	{
+// 		username: {
+// 			type: SchemaTypes.String,
+// 			required: true
+// 		},
+// 		role: {
+// 			type: SchemaTypes.String,
+// 			enum: Object.values(UserRole),
+// 			required: true
+// 		},
+// 		email: {
+// 			address: {
+// 				type: SchemaTypes.String,
+// 				required: true
+// 			},
+// 			verified: {
+// 				type: SchemaTypes.Boolean,
+// 				default: false,
+// 				required: true
+// 			},
+// 			verificationToken: {
+// 				type: SchemaTypes.String,
+// 				required: false,
+// 				select: false
+// 			}
+// 		},
+// 		avatar: {
+// 			type: {
+// 				type: SchemaTypes.String,
+// 				enum: Object.values(UserAvatarType),
+// 				required: true
+// 			},
+// 			url: {
+// 				type: SchemaTypes.String,
+// 				required: false
+// 			},
+// 			color: {
+// 				type: SchemaTypes.String,
+// 				enum: Object.values(UserAvatarColor),
+// 				required: false
+// 			}
+// 		},
+// 		services: {
+// 			type: {
+// 				password: {
+// 					type: {
+// 						password: {
+// 							type: SchemaTypes.String,
+// 							required: true,
+// 							select: false
+// 						},
+// 						reset: {
+// 							code: {
+// 								type: SchemaTypes.String,
+// 								minLength: 8,
+// 								maxLength: 8,
+// 								required: false,
+// 								select: false
+// 							},
+// 							expires: {
+// 								type: SchemaTypes.Date,
+// 								required: false,
+// 								select: false
+// 							}
+// 						},
+// 						set: {
+// 							code: {
+// 								type: SchemaTypes.String,
+// 								minLength: 8,
+// 								maxLength: 8,
+// 								required: false,
+// 								select: false
+// 							},
+// 							expires: {
+// 								type: SchemaTypes.Date,
+// 								required: false,
+// 								select: false
+// 							}
+// 						}
+// 					},
+// 					required: false
+// 				},
+// 				github: {
+// 					type: {
+// 						id: {
+// 							type: SchemaTypes.Number,
+// 							required: true,
+// 							select: false
+// 						},
+// 						access_token: {
+// 							type: SchemaTypes.String,
+// 							required: true,
+// 							select: false
+// 						}
+// 					},
+// 					required: false
+// 				}
+// 			},
+// 			required: true
+// 		},
+// 		statistics: {
+// 			songsRequested: {
+// 				type: SchemaTypes.Number,
+// 				default: 0,
+// 				required: true
+// 			}
+// 		},
+// 		likedSongsPlaylist: {
+// 			type: SchemaTypes.ObjectId,
+// 			required: true
+// 		},
+// 		dislikedSongsPlaylist: {
+// 			type: SchemaTypes.ObjectId,
+// 			required: true
+// 		},
+// 		favoriteStations: [
+// 			{
+// 				type: SchemaTypes.ObjectId,
+// 				ref: "stations"
+// 			}
+// 		],
+// 		name: {
+// 			type: SchemaTypes.String,
+// 			required: true
+// 		},
+// 		location: {
+// 			type: SchemaTypes.String,
+// 			required: false
+// 		},
+// 		bio: {
+// 			type: SchemaTypes.String,
+// 			required: false
+// 		},
+// 		preferences: {
+// 			orderOfPlaylists: [SchemaTypes.ObjectId],
+// 			nightmode: {
+// 				type: SchemaTypes.Boolean,
+// 				default: false,
+// 				required: true
+// 			},
+// 			autoSkipDisliked: {
+// 				type: SchemaTypes.Boolean,
+// 				default: true,
+// 				required: true
+// 			},
+// 			activityLogPublic: {
+// 				type: SchemaTypes.Boolean,
+// 				default: false,
+// 				required: true
+// 			},
+// 			anonymousSongRequests: {
+// 				type: SchemaTypes.Boolean,
+// 				default: false,
+// 				required: true
+// 			},
+// 			activityWatch: {
+// 				type: SchemaTypes.Boolean,
+// 				default: false,
+// 				required: true
+// 			}
+// 		}
+// 	},
+// 	config
+// );
+
+// export type UserSchemaType = typeof schema;
+
+// export type UserSchemaOptions = SchemaOptions<UserSchema>;

+ 0 - 72
backend/src/modules/DataModule/models/minifiedUsers/schema.ts

@@ -1,72 +0,0 @@
-import { Model, Schema, SchemaOptions, SchemaTypes } from "mongoose";
-import { UserSchema } from "../users/schema";
-import { UserRole } from "../users/UserRole";
-import { UserAvatarType } from "../users/UserAvatarType";
-import { UserAvatarColor } from "../users/UserAvatarColor";
-
-export type MinifiedUserSchema = Pick<
-	UserSchema,
-	| "_id"
-	| "name"
-	| "username"
-	| "location"
-	| "bio"
-	| "role"
-	| "avatar"
-	| "createdAt"
-	| "updatedAt"
->;
-
-export type MinifiedUserModel = Model<MinifiedUserSchema>;
-
-export const schema = new Schema<MinifiedUserSchema, MinifiedUserModel>(
-	{
-		username: {
-			type: SchemaTypes.String,
-			required: true
-		},
-		role: {
-			type: SchemaTypes.String,
-			enum: Object.values(UserRole),
-			required: true
-		},
-		avatar: {
-			type: {
-				type: SchemaTypes.String,
-				enum: Object.values(UserAvatarType),
-				required: true
-			},
-			url: {
-				type: SchemaTypes.String,
-				required: false
-			},
-			color: {
-				type: SchemaTypes.String,
-				enum: Object.values(UserAvatarColor),
-				required: false
-			}
-		},
-		name: {
-			type: SchemaTypes.String,
-			required: true
-		},
-		location: {
-			type: SchemaTypes.String,
-			required: false
-		},
-		bio: {
-			type: SchemaTypes.String,
-			required: false
-		}
-	},
-	{
-		autoCreate: false,
-		autoIndex: false,
-		collection: "minifiedUsers",
-		patchHistory: { enabled: false }
-	}
-);
-
-export type UserSchemaType = typeof schema;
-
-export type UserSchemaOptions = SchemaOptions<UserSchema>;

+ 15 - 0
backend/src/modules/DataModule/models/news/listeners/NewsUpdatedListener.ts

@@ -0,0 +1,15 @@
+// import NewsUpdatedEvent from "../events/NewsUpdatedEvent";
+
+// export default abstract class NewsUpdatedListener {
+// 	protected static _event = NewsUpdatedEvent;
+
+//     public static getName() {
+//         return "NewsUpdatedListener";
+//     }
+
+//     protected async _onEvent() {
+//         console.log("On event");
+//     }
+
+// 	// protected static _hasPermission = isAdmin;
+// }

+ 0 - 25
backend/src/modules/DataModule/models/sessions/schema.ts

@@ -1,25 +0,0 @@
-import { Model, Schema, SchemaTypes, Types } from "mongoose";
-import { BaseSchema } from "@/modules/DataModule/types/Schemas";
-
-export interface SessionSchema extends BaseSchema {
-	userId: Types.ObjectId;
-}
-
-export type SessionModel = Model<SessionSchema>;
-
-export const schema = new Schema<SessionSchema, SessionModel>(
-	{
-		userId: {
-			type: SchemaTypes.ObjectId,
-			ref: "users",
-			required: true
-		}
-	},
-	{
-		patchHistory: {
-			enabled: false
-		}
-	}
-);
-
-export type SessionSchemaType = typeof schema;

+ 0 - 16
backend/src/modules/DataModule/models/users/jobs/FindById.ts

@@ -1,16 +0,0 @@
-import { HydratedDocument } from "mongoose";
-import FindByIdJob from "@/modules/DataModule/FindByIdJob";
-import { UserModel } from "../schema";
-
-export default class FindById extends FindByIdJob {
-	protected static _modelName = "users";
-
-	protected static _hasModelPermission = this._isSelf;
-
-	protected static _isSelf(
-		model: HydratedDocument<UserModel>,
-		user?: HydratedDocument<UserModel>
-	) {
-		return model._id === user?._id;
-	}
-}

+ 0 - 227
backend/src/modules/DataModule/models/users/schema.ts

@@ -1,227 +0,0 @@
-import { Model, Schema, SchemaOptions, SchemaTypes, Types } from "mongoose";
-import { BaseSchema } from "@/modules/DataModule/types/Schemas";
-import config from "./config";
-import { UserRole } from "./UserRole";
-import { UserAvatarType } from "./UserAvatarType";
-import { UserAvatarColor } from "./UserAvatarColor";
-
-export interface UserSchema extends BaseSchema {
-	username: string;
-	role: UserRole;
-	email: {
-		address: string;
-		verified: boolean;
-		verificationToken?: string;
-	};
-	avatar: {
-		type: UserAvatarType;
-		url?: string;
-		color?: UserAvatarColor;
-	};
-	services: {
-		password?: {
-			password: string;
-			reset: {
-				code: string;
-				expires: number;
-			};
-			set: {
-				code: string;
-				expires: number;
-			};
-		};
-		github?: {
-			id: number;
-			access_token: string;
-		};
-	};
-	statistics: {
-		songsRequested: number;
-	};
-	likedSongsPlaylist: Types.ObjectId;
-	dislikedSongsPlaylist: Types.ObjectId;
-	favoriteStations: Types.ObjectId[];
-	name: string;
-	location?: string;
-	bio?: string;
-	preferences: {
-		orderOfPlaylists: Types.ObjectId[];
-		nightmode: boolean;
-		autoSkipDisliked: boolean;
-		activityLogPublic: boolean;
-		anonymousSongRequests: boolean;
-		activityWatch: boolean;
-	};
-}
-
-export type UserModel = Model<UserSchema>;
-
-export const schema = new Schema<UserSchema, UserModel>(
-	{
-		username: {
-			type: SchemaTypes.String,
-			required: true
-		},
-		role: {
-			type: SchemaTypes.String,
-			enum: Object.values(UserRole),
-			required: true
-		},
-		email: {
-			address: {
-				type: SchemaTypes.String,
-				required: true
-			},
-			verified: {
-				type: SchemaTypes.Boolean,
-				default: false,
-				required: true
-			},
-			verificationToken: {
-				type: SchemaTypes.String,
-				required: false,
-				select: false
-			}
-		},
-		avatar: {
-			type: {
-				type: SchemaTypes.String,
-				enum: Object.values(UserAvatarType),
-				required: true
-			},
-			url: {
-				type: SchemaTypes.String,
-				required: false
-			},
-			color: {
-				type: SchemaTypes.String,
-				enum: Object.values(UserAvatarColor),
-				required: false
-			}
-		},
-		services: {
-			type: {
-				password: {
-					type: {
-						password: {
-							type: SchemaTypes.String,
-							required: true,
-							select: false
-						},
-						reset: {
-							code: {
-								type: SchemaTypes.String,
-								minLength: 8,
-								maxLength: 8,
-								required: false,
-								select: false
-							},
-							expires: {
-								type: SchemaTypes.Date,
-								required: false,
-								select: false
-							}
-						},
-						set: {
-							code: {
-								type: SchemaTypes.String,
-								minLength: 8,
-								maxLength: 8,
-								required: false,
-								select: false
-							},
-							expires: {
-								type: SchemaTypes.Date,
-								required: false,
-								select: false
-							}
-						}
-					},
-					required: false
-				},
-				github: {
-					type: {
-						id: {
-							type: SchemaTypes.Number,
-							required: true,
-							select: false
-						},
-						access_token: {
-							type: SchemaTypes.String,
-							required: true,
-							select: false
-						}
-					},
-					required: false
-				}
-			},
-			required: true
-		},
-		statistics: {
-			songsRequested: {
-				type: SchemaTypes.Number,
-				default: 0,
-				required: true
-			}
-		},
-		likedSongsPlaylist: {
-			type: SchemaTypes.ObjectId,
-			required: true
-		},
-		dislikedSongsPlaylist: {
-			type: SchemaTypes.ObjectId,
-			required: true
-		},
-		favoriteStations: [
-			{
-				type: SchemaTypes.ObjectId,
-				ref: "stations"
-			}
-		],
-		name: {
-			type: SchemaTypes.String,
-			required: true
-		},
-		location: {
-			type: SchemaTypes.String,
-			required: false
-		},
-		bio: {
-			type: SchemaTypes.String,
-			required: false
-		},
-		preferences: {
-			orderOfPlaylists: [SchemaTypes.ObjectId],
-			nightmode: {
-				type: SchemaTypes.Boolean,
-				default: false,
-				required: true
-			},
-			autoSkipDisliked: {
-				type: SchemaTypes.Boolean,
-				default: true,
-				required: true
-			},
-			activityLogPublic: {
-				type: SchemaTypes.Boolean,
-				default: false,
-				required: true
-			},
-			anonymousSongRequests: {
-				type: SchemaTypes.Boolean,
-				default: false,
-				required: true
-			},
-			activityWatch: {
-				type: SchemaTypes.Boolean,
-				default: false,
-				required: true
-			}
-		}
-	},
-	config
-);
-
-export type UserSchemaType = typeof schema;
-
-export type UserSchemaOptions = SchemaOptions<UserSchema>;

+ 3 - 5
backend/src/modules/DataModule/permissions/isAdmin.ts

@@ -1,6 +1,4 @@
-import { HydratedDocument } from "mongoose";
-import { UserSchema } from "../models/users/schema";
-import { UserRole } from "../models/users/UserRole";
+import User from "../models/User";
+import { UserRole } from "../models/User/UserRole";
 
 
-export default (user: HydratedDocument<UserSchema>) =>
-	user && user.role === UserRole.ADMIN;
+export default (user: User) => user && user.role === UserRole.ADMIN;

+ 2 - 3
backend/src/modules/DataModule/permissions/isLoggedIn.ts

@@ -1,4 +1,3 @@
-import { HydratedDocument } from "mongoose";
-import { UserSchema } from "../models/users/schema";
+import User from "../models/User";
 
 
-export default (user: HydratedDocument<UserSchema>) => user;
+export default (user: User) => user;

+ 3 - 5
backend/src/modules/DataModule/permissions/isModerator.ts

@@ -1,6 +1,4 @@
-import { HydratedDocument } from "mongoose";
-import { UserSchema } from "../models/users/schema";
-import { UserRole } from "../models/users/UserRole";
+import { UserRole } from "../models/User/UserRole";
+import User from "../models/User";
 
 
-export default (user: HydratedDocument<UserSchema>) =>
-	user && user.role === UserRole.ADMIN;
+export default (user: User) => user && user.role === UserRole.ADMIN;

+ 3 - 5
backend/src/modules/DataModule/permissions/modelPermissions/isDj.ts

@@ -1,8 +1,6 @@
 import { HydratedDocument } from "mongoose";
 import { HydratedDocument } from "mongoose";
 import { StationSchema } from "../../models/stations/schema";
 import { StationSchema } from "../../models/stations/schema";
-import { UserSchema } from "../../models/users/schema";
+import User from "../../models/User";
 
 
-export default (
-	model: HydratedDocument<StationSchema>,
-	user?: HydratedDocument<UserSchema>
-) => model && user && model.djs.includes(user._id);
+export default (model: HydratedDocument<StationSchema>, user?: User) =>
+	model && user && model.djs.includes(user._id);

+ 2 - 2
backend/src/modules/DataModule/permissions/modelPermissions/isLoggedIn.ts

@@ -1,7 +1,7 @@
 import { HydratedDocument, Schema } from "mongoose";
 import { HydratedDocument, Schema } from "mongoose";
-import { UserSchema } from "../../models/users/schema";
+import User from "../../models/User";
 
 
 export default <ModelSchemaType extends Schema>(
 export default <ModelSchemaType extends Schema>(
 	model: HydratedDocument<ModelSchemaType>,
 	model: HydratedDocument<ModelSchemaType>,
-	user?: HydratedDocument<UserSchema>
+	user?: User
 ) => !!user;
 ) => !!user;

+ 1 - 2
backend/src/modules/DataModule/permissions/modelPermissions/isNewsPublished.ts

@@ -4,5 +4,4 @@ import { NewsStatus } from "@models/News/NewsStatus";
 /**
 /**
  * Simply used to check if a news model exists and is published
  * Simply used to check if a news model exists and is published
  */
  */
-export default (model?: News) =>
-	model && model.status === NewsStatus.PUBLISHED;
+export default (model?: News) => model && model.status === NewsStatus.PUBLISHED;

+ 2 - 2
backend/src/modules/DataModule/permissions/modelPermissions/isOwner.ts

@@ -1,11 +1,11 @@
 import { HydratedDocument } from "mongoose";
 import { HydratedDocument } from "mongoose";
-import { UserSchema } from "../../models/users/schema";
+import User from "../../models/User";
 
 
 export default (
 export default (
 	model:
 	model:
 		| (HydratedDocument<any> & { owner?: any })
 		| (HydratedDocument<any> & { owner?: any })
 		| (HydratedDocument<any> & { createdBy?: any }),
 		| (HydratedDocument<any> & { createdBy?: any }),
-	user?: HydratedDocument<UserSchema>
+	user?: User
 ) => {
 ) => {
 	if (!user || !model) return false;
 	if (!user || !model) return false;
 
 

+ 6 - 2
backend/src/modules/EventsModule.ts

@@ -14,8 +14,8 @@ import WebSocketModule from "./WebSocketModule";
 import Event from "@/modules/EventsModule/Event";
 import Event from "@/modules/EventsModule/Event";
 import ModuleManager from "@/ModuleManager";
 import ModuleManager from "@/ModuleManager";
 import DataModule from "@/modules/DataModule";
 import DataModule from "@/modules/DataModule";
-import { GetPermissionsResult } from "@/modules/DataModule/models/users/jobs/GetPermissions";
-import { GetSingleModelPermissionsResult } from "@/modules/DataModule/models/users/jobs/GetModelPermissions";
+import { GetPermissionsResult } from "@/modules/DataModule/models/User/jobs/GetPermissions";
+import { GetSingleModelPermissionsResult } from "@/modules/DataModule/models/User/jobs/GetModelPermissions";
 import JobContext from "@/JobContext";
 import JobContext from "@/JobContext";
 
 
 const permissionRegex =
 const permissionRegex =
@@ -212,6 +212,10 @@ export class EventsModule extends BaseModule {
 	public async assertPermission(jobContext: JobContext, permission: string) {
 	public async assertPermission(jobContext: JobContext, permission: string) {
 		let hasPermission = false;
 		let hasPermission = false;
 
 
+		// TODO improve
+		if (!permissionRegex.test(permission))
+			throw new Error("Regex doesn't match");
+
 		const {
 		const {
 			moduleName,
 			moduleName,
 			modelOrEventName,
 			modelOrEventName,

+ 2 - 5
backend/src/modules/EventsModule/Event.ts

@@ -1,5 +1,4 @@
-import { HydratedDocument } from "mongoose";
-import { UserSchema } from "../DataModule/models/users/schema";
+import User from "../DataModule/models/User";
 
 
 export default abstract class Event {
 export default abstract class Event {
 	protected static _namespace: string;
 	protected static _namespace: string;
@@ -104,9 +103,7 @@ export default abstract class Event {
 		| (boolean | CallableFunction)[] = false;
 		| (boolean | CallableFunction)[] = false;
 
 
 	// Check if a given user has generic permission to subscribe to an event, using _hasPermission
 	// Check if a given user has generic permission to subscribe to an event, using _hasPermission
-	public static async hasPermission(
-		user: HydratedDocument<UserSchema> | null
-	) {
+	public static async hasPermission(user: User | null) {
 		const options = Array.isArray(this._hasPermission)
 		const options = Array.isArray(this._hasPermission)
 			? this._hasPermission
 			? this._hasPermission
 			: [this._hasPermission];
 			: [this._hasPermission];

+ 7 - 7
backend/src/modules/EventsModule/jobs/Subscribe.spec.ts

@@ -2,19 +2,19 @@ import "@/tests/support/setup";
 import sinon from "sinon";
 import sinon from "sinon";
 import mongoose from "mongoose";
 import mongoose from "mongoose";
 import news from "logic/db/schemas/news";
 import news from "logic/db/schemas/news";
+import NewsCreatedEvent from "@models/News/events/NewsCreatedEvent";
+import NewsUpdatedEvent from "@models/News/events/NewsUpdatedEvent";
+import NewsDeletedEvent from "@models/News/events/NewsDeletedEvent";
+import { NewsStatus } from "@models/News/NewsStatus";
 import { TestModule } from "@/tests/support/TestModule";
 import { TestModule } from "@/tests/support/TestModule";
 import Subscribe from "@/modules/EventsModule/jobs/Subscribe";
 import Subscribe from "@/modules/EventsModule/jobs/Subscribe";
 import DataModule from "@/modules/DataModule";
 import DataModule from "@/modules/DataModule";
 import EventsModule from "@/modules/EventsModule";
 import EventsModule from "@/modules/EventsModule";
-import NewsCreatedEvent from "@models/News/events/NewsCreatedEvent";
-import GetModelPermissions from "@models/users/jobs/GetModelPermissions";
+import GetModelPermissions from "@/modules/DataModule/models/User/jobs/GetModelPermissions";
 import JobContext from "@/JobContext";
 import JobContext from "@/JobContext";
-import { UserRole } from "@models/users/UserRole";
-import GetPermissions from "@models/users/jobs/GetPermissions";
+import { UserRole } from "@/modules/DataModule/models/User/UserRole";
+import GetPermissions from "@/modules/DataModule/models/User/jobs/GetPermissions";
 import CacheModule from "@/modules/CacheModule";
 import CacheModule from "@/modules/CacheModule";
-import NewsUpdatedEvent from "@models/News/events/NewsUpdatedEvent";
-import NewsDeletedEvent from "@models/News/events/NewsDeletedEvent";
-import { NewsStatus } from "@models/News/NewsStatus";
 
 
 describe("Subscribe job", async function () {
 describe("Subscribe job", async function () {
 	describe("execute", function () {
 	describe("execute", function () {

+ 26 - 14
backend/src/modules/WebSocketModule.ts

@@ -10,9 +10,9 @@ import WebSocket from "@/WebSocket";
 import ModuleManager from "@/ModuleManager";
 import ModuleManager from "@/ModuleManager";
 import JobQueue from "@/JobQueue";
 import JobQueue from "@/JobQueue";
 import DataModule from "./DataModule";
 import DataModule from "./DataModule";
-import { UserModel } from "./DataModule/models/users/schema";
-import { SessionModel } from "./DataModule/models/sessions/schema";
 import EventsModule from "./EventsModule";
 import EventsModule from "./EventsModule";
+import User from "./DataModule/models/User";
+import Session from "./DataModule/models/Session";
 // import assertEventDerived from "@/utils/assertEventDerived";
 // import assertEventDerived from "@/utils/assertEventDerived";
 
 
 export class WebSocketModule extends BaseModule {
 export class WebSocketModule extends BaseModule {
@@ -132,16 +132,24 @@ export class WebSocketModule extends BaseModule {
 		if (sessionId && isObjectIdOrHexString(sessionId)) {
 		if (sessionId && isObjectIdOrHexString(sessionId)) {
 			socket.setSessionId(sessionId);
 			socket.setSessionId(sessionId);
 
 
-			const Session = await DataModule.getModel("sessions");
+			const Session = await DataModule.getModel<Session>("sessions");
 
 
-			const session = await Session.findByIdAndUpdate(sessionId, {
-				updatedAt: Date.now()
-			});
+			await Session.update(
+				{
+					updatedAt: new Date()
+				},
+				{
+					where: {
+						sessionId
+					}
+				}
+			);
+			const session = await Session.findByPk(sessionId); // pk = primary key
 
 
 			if (session) {
 			if (session) {
-				const User = await DataModule.getModel<UserModel>("users");
+				const User = await DataModule.getModel<User>("users");
 
 
-				user = await User.findById(session.userId);
+				user = await User.findByPk(session.userId);
 			}
 			}
 		}
 		}
 
 
@@ -247,17 +255,21 @@ export class WebSocketModule extends BaseModule {
 
 
 			let session;
 			let session;
 			if (socket.getSessionId()) {
 			if (socket.getSessionId()) {
-				const Session = await DataModule.getModel<SessionModel>(
-					"sessions"
-				);
+				const Session = await DataModule.getModel<Session>("sessions");
 
 
-				session = await Session.findByIdAndUpdate(
-					socket.getSessionId(),
+				await Session.update(
 					{
 					{
-						updatedAt: Date.now()
+						updatedAt: new Date()
+					},
+					{
+						where: {
+							sessionId: socket.getSessionId()
+						}
 					}
 					}
 				);
 				);
 
 
+				session = await Session.findByPk(socket.getSessionId());
+
 				if (!session) throw new Error("Session not found.");
 				if (!session) throw new Error("Session not found.");
 			}
 			}