Sfoglia il codice sorgente

feat: Add some missing model configs

Owen Diffey 1 anno fa
parent
commit
b1ee981eed

+ 55 - 0
backend/src/modules/DataModule/models/stations/jobs/Index.ts

@@ -0,0 +1,55 @@
+import DataModule from "@/modules/DataModule";
+import DataModuleJob from "@/modules/DataModule/DataModuleJob";
+import isDj from "@/modules/DataModule/permissions/isDj";
+import isOwner from "@/modules/DataModule/permissions/isOwner";
+import isPublic from "@/modules/DataModule/permissions/isPublic";
+import { Models } from "@/types/Models";
+
+export default class Index extends DataModuleJob {
+	protected static _modelName: keyof Models = "stations";
+
+	protected static _hasPermission = true;
+
+	protected override async _validate() {
+		if (
+			typeof this._payload !== "object" &&
+			typeof this._payload !== "undefined" &&
+			this._payload !== null
+		)
+			throw new Error("Payload must be an object or undefined");
+
+		if (
+			typeof this._payload?.adminFilter !== "boolean" &&
+			typeof this._payload?.adminFilter !== "undefined" &&
+			this._payload?.adminFilter !== null
+		)
+			throw new Error("Admin filter must be a boolean or undefined");
+	}
+
+	protected override async _authorize() {}
+
+	protected async _execute(payload?: { adminFilter?: boolean }) {
+		const model = await DataModule.getModel(this.getModelName());
+
+		const data = await model.find();
+
+		const user = await this._context.getUser().catch(() => null);
+
+		const stations = [];
+
+		for (const station of data) {
+			if (
+				isPublic(station) ||
+				(user && (isOwner(station, user) || isDj(station, user))) ||
+				(payload?.adminFilter &&
+					(await this._context
+						.assertPermission("data.stations.index-adminFilter")
+						.then(() => true)
+						.catch(() => false)))
+			)
+				stations.push(station);
+		}
+
+		return stations;
+	}
+}

+ 3 - 1
backend/src/modules/DataModule/models/users/config.ts

@@ -1,4 +1,5 @@
 import CacheModule from "@/modules/CacheModule";
+import getData from "./getData";
 
 export default {
 	documentVersion: 4,
@@ -15,5 +16,6 @@ export default {
 				`model-permissions.*.user.${oldDoc._id}`
 			]);
 		}
-	}
+	},
+	getData
 };

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

@@ -0,0 +1,32 @@
+export default {
+	enabled: true,
+	blacklistedProperties: [
+		"services.password.password",
+		"services.password.reset.code",
+		"services.password.reset.expires",
+		"services.password.set.code",
+		"services.password.set.expires",
+		"services.github.access_token",
+		"email.verificationToken"
+	],
+	specialProperties: {
+		hasPassword: [
+			{
+				$addFields: {
+					hasPassword: {
+						$cond: [
+							{
+								$eq: [
+									{ $type: "$services.password.password" },
+									"string"
+								]
+							},
+							true,
+							false
+						]
+					}
+				}
+			}
+		]
+	}
+};

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

@@ -1,6 +1,13 @@
 import FindByIdJob from "@/modules/DataModule/FindByIdJob";
 import { Models } from "@/types/Models";
+import { UserModel } from "../schema";
 
 export default class FindById extends FindByIdJob {
 	protected static _modelName: keyof Models = "users";
+
+	protected static _hasPermission = this._isSelf;
+
+	protected static _isSelf(model: UserModel, user?: UserModel) {
+		return model._id === user?._id;
+	}
 }

+ 6 - 0
backend/src/modules/DataModule/models/users/jobs/GetData.ts

@@ -0,0 +1,6 @@
+import GetDataJob from "@/modules/DataModule/GetDataJob";
+import { Models } from "@/types/Models";
+
+export default class GetData extends GetDataJob {
+	protected static _modelName: keyof Models = "users";
+}

+ 2 - 0
backend/src/modules/DataModule/models/users/jobs/GetModelPermissions.ts

@@ -9,6 +9,8 @@ import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 export default class GetModelPermissions extends DataModuleJob {
 	protected static _modelName: keyof Models = "users";
 
+	protected static _hasPermission = true;
+
 	protected override async _validate() {
 		if (typeof this._payload !== "object")
 			throw new Error("Payload must be an object");

+ 2 - 0
backend/src/modules/DataModule/models/users/jobs/GetPermissions.ts

@@ -7,6 +7,8 @@ import DataModuleJob from "@/modules/DataModule/DataModuleJob";
 export default class GetPermissions extends DataModuleJob {
 	protected static _modelName: keyof Models = "users";
 
+	protected static _hasPermission = true;
+
 	protected override async _authorize() {}
 
 	protected async _execute() {

+ 6 - 1
backend/src/modules/DataModule/models/users/schema.ts

@@ -172,7 +172,12 @@ export const schema = new Schema<UserSchema, UserModel>(
 			type: SchemaTypes.ObjectId,
 			required: true
 		},
-		favoriteStations: [SchemaTypes.ObjectId],
+		favoriteStations: [
+			{
+				type: SchemaTypes.ObjectId,
+				ref: "stations"
+			}
+		],
 		name: {
 			type: SchemaTypes.String,
 			required: true

+ 2 - 15
frontend/src/pages/Admin/Users/index.vue

@@ -3,7 +3,7 @@ import { defineAsyncComponent, ref, onMounted } from "vue";
 import { useRoute } from "vue-router";
 import { useModalsStore } from "@/stores/modals";
 import { useUserAuthStore } from "@/stores/userAuth";
-import { TableColumn, TableFilter, TableEvents } from "@/types/advancedTable";
+import { TableColumn, TableFilter } from "@/types/advancedTable";
 
 const AdvancedTable = defineAsyncComponent(
 	() => import("@/components/AdvancedTable.vue")
@@ -186,18 +186,6 @@ const filters = ref<TableFilter[]>([
 		defaultFilterType: "numberLesser"
 	}
 ]);
-const events = ref<TableEvents>({
-	adminRoom: "users",
-	updated: {
-		event: "admin.user.updated",
-		id: "user._id",
-		item: "user"
-	},
-	removed: {
-		event: "user.removed",
-		id: "userId"
-	}
-});
 
 const { openModal } = useModalsStore();
 
@@ -225,10 +213,9 @@ onMounted(() => {
 			:column-default="columnDefault"
 			:columns="columns"
 			:filters="filters"
-			data-action="users.getData"
+			model="users"
 			name="admin-users"
 			:max-width="1200"
-			:events="events"
 		>
 			<template #column-options="slotProps">
 				<div class="row-options">

+ 55 - 36
frontend/src/pages/Home.vue

@@ -17,6 +17,7 @@ import { useConfigStore } from "@/stores/config";
 import { useUserAuthStore } from "@/stores/userAuth";
 import { useModalsStore } from "@/stores/modals";
 import keyboardShortcuts from "@/keyboardShortcuts";
+import { useWebsocketStore } from "@/stores/websocket";
 
 const MainHeader = defineAsyncComponent(
 	() => import("@/components/MainHeader.vue")
@@ -43,6 +44,7 @@ const { loggedIn, currentUser } = storeToRefs(userAuthStore);
 const { hasPermission } = userAuthStore;
 
 const { socket } = useWebsocketsStore();
+const websocketStore = useWebsocketStore();
 
 const stations = ref([]);
 const searchQuery = ref("");
@@ -80,7 +82,9 @@ const filteredStations = computed(() => {
 
 const favoriteStations = computed(() =>
 	filteredStations.value
-		.filter(station => station.isFavorited === true)
+		.filter(station =>
+			currentUser.value?.favoriteStations.includes(station._id)
+		)
 		.sort(
 			(a, b) =>
 				orderOfFavoriteStations.value.indexOf(a._id) -
@@ -91,32 +95,33 @@ const favoriteStations = computed(() =>
 const { openModal } = useModalsStore();
 
 const fetchStations = () => {
-	socket.dispatch(
-		"stations.index",
-		route.query.adminFilter === undefined,
-		res => {
-			if (res.status === "success") {
-				stations.value = res.data.stations.map(station => {
-					const modifiableStation = station;
-
-					if (!modifiableStation.currentSong)
-						modifiableStation.currentSong = {
-							thumbnail: "/assets/notes-transparent.png"
-						};
-
-					if (
-						modifiableStation.currentSong &&
-						!modifiableStation.currentSong.thumbnail
-					)
-						modifiableStation.currentSong.ytThumbnail = `https://img.youtube.com/vi/${station.currentSong.youtubeId}/mqdefault.jpg`;
-
-					return modifiableStation;
-				});
-
-				orderOfFavoriteStations.value = res.data.favorited;
-			}
-		}
-	);
+	websocketStore
+		.runJob("data.stations.index", {
+			adminFilter: route.query.adminFilter !== undefined
+		})
+		.then(data => {
+			console.log(55, data);
+			stations.value = data.map(station => {
+				const modifiableStation = station;
+
+				modifiableStation.currentSong = {
+					thumbnail: "/assets/notes-transparent.png"
+				};
+
+				// if (!modifiableStation.currentSong)
+				// 	modifiableStation.currentSong = {
+				// 		thumbnail: "/assets/notes-transparent.png"
+				// 	};
+
+				// if (
+				// 	modifiableStation.currentSong &&
+				// 	!modifiableStation.currentSong.thumbnail
+				// )
+				// 	modifiableStation.currentSong.ytThumbnail = `https://img.youtube.com/vi/${station.currentSong.youtubeId}/mqdefault.jpg`;
+
+				return modifiableStation;
+			});
+		});
 };
 
 const canRequest = (station, requireLogin = true) =>
@@ -220,7 +225,7 @@ onMounted(async () => {
 			const stationIndex = stations.value.indexOf(station);
 			stations.value.splice(stationIndex, 1);
 
-			if (station.isFavorited)
+			if (currentUser.value?.favoriteStations.includes(station._id))
 				orderOfFavoriteStations.value =
 					orderOfFavoriteStations.value.filter(
 						favoritedId => favoritedId !== stationId
@@ -290,7 +295,6 @@ onMounted(async () => {
 		);
 
 		if (station) {
-			station.isFavorited = true;
 			orderOfFavoriteStations.value.push(stationId);
 		}
 	});
@@ -303,7 +307,6 @@ onMounted(async () => {
 		);
 
 		if (station) {
-			station.isFavorited = false;
 			orderOfFavoriteStations.value =
 				orderOfFavoriteStations.value.filter(
 					favoritedId => favoritedId !== stationId
@@ -316,12 +319,12 @@ onMounted(async () => {
 	});
 
 	socket.on("event:station.djs.added", res => {
-		if (loggedIn.value && res.data.user._id === currentUser.value._id)
+		if (loggedIn.value && res.data.user._id === currentUser.value?._id)
 			fetchStations();
 	});
 
 	socket.on("event:station.djs.removed", res => {
-		if (loggedIn.value && res.data.user._id === currentUser.value._id)
+		if (loggedIn.value && res.data.user._id === currentUser.value?._id)
 			fetchStations();
 	});
 
@@ -485,7 +488,10 @@ onBeforeUnmount(() => {
 									<div class="displayName">
 										<i
 											v-if="
-												loggedIn && !element.isFavorited
+												loggedIn &&
+												!currentUser.favoriteStations.includes(
+													element._id
+												)
 											"
 											@click.prevent="
 												favoriteStation(element._id)
@@ -497,7 +503,10 @@ onBeforeUnmount(() => {
 										>
 										<i
 											v-if="
-												loggedIn && element.isFavorited
+												loggedIn &&
+												currentUser.favoriteStations.includes(
+													element._id
+												)
 											"
 											@click.prevent="
 												unfavoriteStation(element._id)
@@ -761,7 +770,12 @@ onBeforeUnmount(() => {
 						<div class="media">
 							<div class="displayName">
 								<i
-									v-if="loggedIn && !station.isFavorited"
+									v-if="
+										loggedIn &&
+										!currentUser.favoriteStations.includes(
+											station._id
+										)
+									"
 									@click.prevent="
 										favoriteStation(station._id)
 									"
@@ -771,7 +785,12 @@ onBeforeUnmount(() => {
 									>{{ t("Icons.Favorite") }}</i
 								>
 								<i
-									v-if="loggedIn && station.isFavorited"
+									v-if="
+										loggedIn &&
+										currentUser.favoriteStations.includes(
+											station._id
+										)
+									"
 									@click.prevent="
 										unfavoriteStation(station._id)
 									"

+ 3 - 3
frontend/src/stores/manageStation.ts

@@ -97,10 +97,10 @@ export const useManageStationStore = ({ modalUuid }: { modalUuid: string }) =>
 				return new Promise(resolve => {
 					const { socket } = useWebsocketsStore();
 					socket.dispatch(
-						"utils.getPermissions",
-						this.station._id,
+						"api.getUserModelPermissions",
+						{ modelName: "stations", modelId: this.station._id },
 						res => {
-							this.permissions = res.data.permissions;
+							this.permissions = res.data;
 							resolve(this.permissions);
 						}
 					);

+ 3 - 3
frontend/src/stores/station.ts

@@ -244,10 +244,10 @@ export const useStationStore = defineStore("station", {
 			return new Promise(resolve => {
 				const { socket } = useWebsocketsStore();
 				socket.dispatch(
-					"utils.getPermissions",
-					this.station._id,
+					"api.getUserModelPermissions",
+					{ modelName: "stations", modelId: this.station._id },
 					res => {
-						this.permissions = res.data.permissions;
+						this.permissions = res.data;
 						resolve(this.permissions);
 					}
 				);