+
{
statusMessage: "Missing notification ID",
});
+ const userIds = [userId];
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:delete",
+ ]);
+ if (hasSystemPerms) {
+ userIds.push("system");
+ }
+
const notification = await prisma.notification.delete({
where: {
id: notificationId,
- userId,
+ userId: { in: userIds },
},
});
diff --git a/server/api/v1/notifications/[id]/index.get.ts b/server/api/v1/notifications/[id]/index.get.ts
index 75129aa..ba2c6fa 100644
--- a/server/api/v1/notifications/[id]/index.get.ts
+++ b/server/api/v1/notifications/[id]/index.get.ts
@@ -12,10 +12,18 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Missing notification ID",
});
+ const userIds = [userId];
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:read",
+ ]);
+ if (hasSystemPerms) {
+ userIds.push("system");
+ }
+
const notification = await prisma.notification.findFirst({
where: {
id: notificationId,
- userId,
+ userId: { in: userIds },
},
});
diff --git a/server/api/v1/notifications/[id]/read.post.ts b/server/api/v1/notifications/[id]/read.post.ts
index 8f93844..4ffe007 100644
--- a/server/api/v1/notifications/[id]/read.post.ts
+++ b/server/api/v1/notifications/[id]/read.post.ts
@@ -12,10 +12,18 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Missing notification ID",
});
+ const userIds = [userId];
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:mark",
+ ]);
+ if (hasSystemPerms) {
+ userIds.push("system");
+ }
+
const notification = await prisma.notification.update({
where: {
id: notificationId,
- userId,
+ userId: { in: userIds },
},
data: {
read: true,
diff --git a/server/api/v1/notifications/index.get.ts b/server/api/v1/notifications/index.get.ts
index 9c3502f..8797aa8 100644
--- a/server/api/v1/notifications/index.get.ts
+++ b/server/api/v1/notifications/index.get.ts
@@ -5,9 +5,17 @@ export default defineEventHandler(async (h3) => {
const userId = await aclManager.getUserIdACL(h3, ["notifications:read"]);
if (!userId) throw createError({ statusCode: 403 });
+ const userIds = [userId];
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:mark",
+ ]);
+ if (hasSystemPerms) {
+ userIds.push("system");
+ }
+
const notifications = await prisma.notification.findMany({
where: {
- userId,
+ userId: { in: userIds },
},
orderBy: {
created: "desc", // Newest first
diff --git a/server/api/v1/notifications/readall.post.ts b/server/api/v1/notifications/readall.post.ts
index 7b11927..9c3c799 100644
--- a/server/api/v1/notifications/readall.post.ts
+++ b/server/api/v1/notifications/readall.post.ts
@@ -5,9 +5,17 @@ export default defineEventHandler(async (h3) => {
const userId = await aclManager.getUserIdACL(h3, ["notifications:mark"]);
if (!userId) throw createError({ statusCode: 403 });
+ const userIds = [userId];
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:mark",
+ ]);
+ if(hasSystemPerms){
+ userIds.push("system");
+ }
+
await prisma.notification.updateMany({
where: {
- userId,
+ userId: { in: userIds },
},
data: {
read: true,
diff --git a/server/api/v1/notifications/ws.get.ts b/server/api/v1/notifications/ws.get.ts
index ebd6574..8d2e1c7 100644
--- a/server/api/v1/notifications/ws.get.ts
+++ b/server/api/v1/notifications/ws.get.ts
@@ -9,20 +9,29 @@ const socketSessions: { [key: string]: string } = {};
export default defineWebSocketHandler({
async open(peer) {
- const userId = await aclManager.getUserIdACL(
- { headers: peer.request?.headers ?? new Headers() },
- ["notifications:listen"]
- );
+ const h3 = { headers: peer.request?.headers ?? new Headers() };
+ const userId = await aclManager.getUserIdACL(h3, ["notifications:listen"]);
if (!userId) {
peer.send("unauthenticated");
return;
}
+ const userIds = [userId];
+
+ const hasSystemPerms = await aclManager.allowSystemACL(h3, [
+ "notifications:listen",
+ ]);
+ if (hasSystemPerms) {
+ userIds.push("system");
+ }
+
socketSessions[peer.id] = userId;
- notificationSystem.listen(userId, peer.id, (notification) => {
- peer.send(JSON.stringify(notification));
- });
+ for (const listenUserId of userIds) {
+ notificationSystem.listen(listenUserId, peer.id, (notification) => {
+ peer.send(JSON.stringify(notification));
+ });
+ }
},
async close(peer, details) {
const userId = socketSessions[peer.id];
@@ -32,6 +41,7 @@ export default defineWebSocketHandler({
}
notificationSystem.unlisten(userId, peer.id);
+ notificationSystem.unlisten("system", peer.id); // In case we were listening as 'system'
delete socketSessions[peer.id];
},
});
diff --git a/server/h3.d.ts b/server/h3.d.ts
index e2fddb4..76ff537 100644
--- a/server/h3.d.ts
+++ b/server/h3.d.ts
@@ -1,14 +1 @@
-import { CertificateAuthority } from "./internal/clients/ca";
-import { MetadataHandler } from "./internal/metadata";
-import { ObjectBackend } from "./internal/objects";
-import { SessionHandler } from "./internal/session";
-
-export * from "h3";
-declare module "h3" {
- interface H3EventContext {
- ca: CertificateAuthority;
- objects: ObjectBackend;
- }
-}
-
export type MinimumRequestObject = { headers: Headers };
diff --git a/server/internal/acls/descriptions.ts b/server/internal/acls/descriptions.ts
index dde3aa1..de88a2e 100644
--- a/server/internal/acls/descriptions.ts
+++ b/server/internal/acls/descriptions.ts
@@ -41,6 +41,11 @@ export const systemACLDescriptions: ObjectFromList = {
"library:read": "Fetch a list of all games on this instance.",
+ "notifications:read": "Read system notifications.",
+ "notifications:mark": "Mark system notifications as read.",
+ "notifications:listen": "Connect to the system notification websocket.",
+ "notifications:delete": "Delete system notifications.",
+
"game:read": "Fetch a given game on this instance.",
"game:update": "Update a game on this instance.",
"game:delete": "Delete a game on this instance.",
diff --git a/server/internal/acls/index.ts b/server/internal/acls/index.ts
index d191d59..7e27876 100644
--- a/server/internal/acls/index.ts
+++ b/server/internal/acls/index.ts
@@ -37,6 +37,11 @@ export const systemACLs = [
"auth:simple:invitation:new",
"auth:simple:invitation:delete",
+ "notifications:read",
+ "notifications:mark",
+ "notifications:listen",
+ "notifications:delete",
+
"library:read",
"game:read",
"game:update",
diff --git a/server/internal/library/index.ts b/server/internal/library/index.ts
index b33f998..5f83a86 100644
--- a/server/internal/library/index.ts
+++ b/server/internal/library/index.ts
@@ -14,6 +14,7 @@ import { recursivelyReaddir } from "../utils/recursivedirs";
import taskHandler from "../tasks";
import { parsePlatform } from "../utils/parseplatform";
import droplet from "@drop/droplet";
+import notificationSystem from "../notifications";
class LibraryManager {
private basePath: string;
@@ -297,6 +298,15 @@ class LibraryManager {
log("Successfully created version!");
+ notificationSystem.systemPush({
+ nonce: `version-create-${gameId}-${versionName}`,
+ title: `'${game.mName}' ('${versionName}') finished importing.`,
+ description: `Drop finished importing version ${versionName} for ${game.mName}.`,
+ actions: [
+ `View|/admin/library/${gameId}`
+ ]
+ })
+
progress(100);
},
});