diff --git a/src/index.ts b/src/index.ts index 58b8226..48e8a2e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,7 +35,7 @@ var app = express(); app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: true})) app.use(cors({ - origin: ["http://localhost:4200", "http://localhost:3000", `htt[s://${process.env.DOMAIN}`,], + origin: ["http://localhost:4200", "http://localhost:3000", `https://${process.env.DOMAIN}`,], credentials: true })) app.use(session({ diff --git a/src/notif.ts b/src/notif.ts index 66dec64..7841a72 100644 --- a/src/notif.ts +++ b/src/notif.ts @@ -1,15 +1,16 @@ -import { PushSubscription, RequestOptions, SendResult, sendNotification } from "web-push"; -import { readFileSync } from "fs"; +import { PushSubscription, RequestOptions, VapidKeys, sendNotification } from "web-push"; import Notification from "./schemas/Notification"; -import { allNotif, groupNotif, roomNotif, userNotif } from "./pipelines/notif"; +import vapidKeys from "./vapidKeys"; +import { Types } from "mongoose"; +import { IUser } from "./schemas/User"; export class NotifcationHelper { private options: RequestOptions constructor () { - let keys = JSON.parse(readFileSync("./config/keys.json", 'utf-8')) + let keys: VapidKeys = vapidKeys.keys this.options = { vapidDetails: { - subject: "CHANGE ME", + subject: `https://${process.env.DOMAIN}`, privateKey: keys.privateKey, publicKey: keys.publicKey } @@ -39,16 +40,13 @@ export class NotifcationHelper { private rcpt(message: string) { return { user: async (uname: string) => { - return await this.send(message, await Notification.aggregate(userNotif(uname))) + return await this.send(message, await this.findUserNotif(uname)) }, room: async (room: string) => { - return await this.send(message, await Notification.aggregate(roomNotif(room))) + return await this.send(message, await this.findRoomNotif(room)) }, group: async (group: string) => { - return await this.send(message, []) - }, - withRoom: async () => { - return await this.send(message, await Notification.aggregate(allNotif())) + return await this.send(message, await this.findGroupNotif(group)) } } } @@ -57,4 +55,18 @@ export class NotifcationHelper { return this.rcpt(JSON.stringify({notification: {title: title, body: body}})) } + async findUserNotif(uname: string): Promise> { + var notif = await Notification.find().populate<{user: Pick}>('user', 'uname').exec() + return notif.filter(val => val.user.uname == uname) + } + + async findRoomNotif(room: string): Promise> { + var notif = await Notification.find().populate<{user: Pick}>('user', 'room').exec() + return notif.filter(val => val.user.room == room) + } + + async findGroupNotif(groupId: string): Promise> { + var notif = await Notification.find().populate<{user: Pick}>('user', 'groups').exec() + return notif.filter(val => val.user.groups.find(x => x.toString() == groupId)) + } } \ No newline at end of file diff --git a/src/pipelines/notif.ts b/src/pipelines/notif.ts deleted file mode 100644 index a5fc5ae..0000000 --- a/src/pipelines/notif.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { PipelineStage, Types } from "mongoose" - -function userNotif(uname: string) { - var pipeline: PipelineStage[] = [ - { - "$match": { - uname: uname - } - } - ] - return pipeline -} - -function roomNotif(room: string) { - var pipeline: PipelineStage[] = [ - { - $lookup: { - from: "logins", - localField: "uname", - foreignField: "uname", - as: "user", - }, - }, - { - $unwind: { - path: "$user", - preserveNullAndEmptyArrays: false, - }, - }, - { - $addFields: { - rooms: "$user.room", - }, - }, - { - $match: - { - rooms: room, - }, - }, - { - $unset: - ["user", "uname", "rooms"], - }, - ] - return pipeline -} - -function allNotif() { - var pipeline: PipelineStage[] = [ - { - $lookup: { - from: "logins", - localField: "uname", - foreignField: "uname", - as: "user", - }, - }, - { - $unwind: { - path: "$user", - preserveNullAndEmptyArrays: false, - }, - }, - { - $addFields: { - rooms: "$user.room", - }, - }, - { - $match: - { - rooms: {$exists: true}, - }, - }, - { - $unset: - ["user", "uname", "rooms"], - }, - ] - return pipeline -} - -function groupNotif(group: string) { - return -} - -export { userNotif, roomNotif, allNotif, groupNotif } \ No newline at end of file diff --git a/src/routes/api/admin/notif.ts b/src/routes/api/admin/notif.ts index 1c0f831..c7c9f4f 100644 --- a/src/routes/api/admin/notif.ts +++ b/src/routes/api/admin/notif.ts @@ -13,24 +13,20 @@ notifRouter.use(capability.mw(Features.Notif)) notifRouter.post("/send", async (req, res) => { const message = nh.simpleMessage(req.body.title, req.body.body) - let recp: string | number + let recp: string let result; switch (req.body.recp.type) { case "uname": - recp = req.body.recp.uname as string + recp = req.body.recp.uname result = await message.user(recp); break; case "room": - recp = req.body.recp.room as string + recp = req.body.recp.room result = await message.room(recp) break; - case "all": - recp = "all" - result = await message.withRoom() - break; case "group": if (!capability.settings.groups) return res.sendStatus(406).end() - recp = req.body.recp.group as string + recp = req.body.recp.group result = await message.group(recp) break; default: diff --git a/src/routes/auth/index.ts b/src/routes/auth/index.ts index 095e2a8..52db104 100644 --- a/src/routes/auth/index.ts +++ b/src/routes/auth/index.ts @@ -5,6 +5,8 @@ import { islogged } from "@/utility"; import bcrypt from "bcryptjs" import cap from "@/capability"; import usettings from "@/usettings"; +import { readFileSync } from "node:fs"; +import vapidKeys from "@/vapidKeys"; const authRouter = Router() @@ -52,7 +54,7 @@ authRouter.get("/check", islogged, (req, res, next) => { res.status(401).send("Your account has been locked.") }) } - res.send({"admin": req.user.admin, "features": cap.flags, "vapid": usettings.settings.vapid, "room": req.user.room, "menu": {"defaultItems": usettings.settings.menu.defaultItems}}) + res.send({"admin": req.user.admin, "features": cap.flags, "room": req.user.room, "menu": {"defaultItems": usettings.settings.menu.defaultItems}, "vapid": vapidKeys.keys.publicKey}) }) export { authRouter }; diff --git a/src/routes/index.ts b/src/routes/index.ts index 546ae23..fe18dc7 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -4,6 +4,7 @@ import { islogged } from "@/utility"; import { adminRouter } from "./api/adminRouter"; import { appRouter } from "./api/appRouter"; import { authRouter } from "./auth/index"; +import { Schema } from 'mongoose' import capability, { Features } from "@/capability"; const router = Router(); @@ -13,7 +14,7 @@ router.use('/admin', adminRouter) router.use('/auth', authRouter) router.post("/notif", islogged, capability.mw(Features.Notif), async (req, res) => { - var obj = {uname: req.user.uname, ...req.body} + var obj = {user: req.user._id, ...req.body} await Notification.findOneAndUpdate(obj, obj, {upsert: true}) res.send({"status": 200}) }) diff --git a/src/schemas/Grade.ts b/src/schemas/Grade.ts index 142f0f1..8ea4fa8 100644 --- a/src/schemas/Grade.ts +++ b/src/schemas/Grade.ts @@ -1,4 +1,4 @@ -import { ObjectId, Schema, model } from "mongoose" +import { Schema, model } from "mongoose" export interface GradeNote { label: string; diff --git a/src/schemas/Key.ts b/src/schemas/Key.ts index 96cd6fe..d24044f 100644 --- a/src/schemas/Key.ts +++ b/src/schemas/Key.ts @@ -1,8 +1,8 @@ -import { ObjectId, Schema, model } from "mongoose" +import { Schema, Types, model } from "mongoose" interface IKey { room: string; - whom: ObjectId; + whom: Types.ObjectId; borrow: Date; tb?: Date; } diff --git a/src/schemas/Notification.ts b/src/schemas/Notification.ts index 2235826..e0899b0 100644 --- a/src/schemas/Notification.ts +++ b/src/schemas/Notification.ts @@ -1,4 +1,4 @@ -import mongoose, { Schema } from "mongoose" +import mongoose, { Types, Schema } from "mongoose" interface INotification { endpoint: string; @@ -6,7 +6,7 @@ interface INotification { auth: string; p256dh: string; }; - uname: string + user: Types.ObjectId expirationTime?: number } @@ -16,7 +16,7 @@ const notifSchema = new Schema({ auth: String, p256dh: String, }, - uname: {type: String, required: true}, + user: {type: Schema.ObjectId, required: true, ref: "logins"}, expirationTime: Number }) diff --git a/src/schemas/User.ts b/src/schemas/User.ts index a5e7fcb..fa9e821 100644 --- a/src/schemas/User.ts +++ b/src/schemas/User.ts @@ -1,8 +1,8 @@ -import mongoose, { ObjectId, Schema } from "mongoose" +import mongoose, { Types, Schema } from "mongoose" // TODO: Unify `fname` and `surename` into single field -interface IUser { +export interface IUser { uname: string; pass: string; room?: string; @@ -10,7 +10,7 @@ interface IUser { locked?: boolean; fname?: string; surname?: string; - groups: ObjectId[]; + groups: Types.ObjectId[]; } const userSchema = new Schema({ diff --git a/src/usettings.ts b/src/usettings.ts index c3f3aa8..2cd73e0 100644 --- a/src/usettings.ts +++ b/src/usettings.ts @@ -8,9 +8,6 @@ interface IUSettings { sn: string[]; kol: string[]; } - }, - vapid: { - } } diff --git a/src/vapidKeys.ts b/src/vapidKeys.ts new file mode 100644 index 0000000..b0426e4 --- /dev/null +++ b/src/vapidKeys.ts @@ -0,0 +1,31 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { generateVAPIDKeys, VapidKeys } from "web-push"; + +class VapidKeysSettings { + private _keys: VapidKeys; + public get keys(): VapidKeys { + return this._keys; + } + public set keys(value: VapidKeys) { + this._keys = value; + this.save() + } + + constructor() { + this.reload() + } + + private save() { + writeFileSync("./config/keys.json", JSON.stringify(this._keys, undefined, 2)) + } + + reload() { + this._keys = JSON.parse(readFileSync("./config/keys.json", {encoding: "utf-8"})) + if (!(this._keys.privateKey && this._keys.publicKey)) { + this.keys = generateVAPIDKeys() + } + console.log("Reloaded VAPID keys"); + } +} + +export default new VapidKeysSettings(); \ No newline at end of file