From 1f25f73d822e900d6471bfe29d482bf328794e0d Mon Sep 17 00:00:00 2001 From: alexa Date: Tue, 28 May 2024 20:49:08 +0200 Subject: [PATCH] fix a bunch of stuff regarding channels, move models --- src/features/database/mariaDbDatabase.ts | 22 ++++++++--- src/features/messaging/endpoints.ts | 48 ++++++++++++++++-------- src/features/messagingFeature.ts | 4 ++ src/models/receivableMessage.ts | 6 +++ src/models/uiChannel.ts | 6 +++ src/swagger.ts | 44 ++++++++++++++++++++++ 6 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 src/models/receivableMessage.ts create mode 100644 src/models/uiChannel.ts diff --git a/src/features/database/mariaDbDatabase.ts b/src/features/database/mariaDbDatabase.ts index 046f49b..aaa7d3d 100644 --- a/src/features/database/mariaDbDatabase.ts +++ b/src/features/database/mariaDbDatabase.ts @@ -161,10 +161,14 @@ WHERE ur.userId = ?`, [userId]); throw new Error("Could not create channel"); } - await this.query("INSERT INTO venel.channelMembers (channelId, userId) VALUES (?, ?), (?, ?)", [ - channelId, id, - channelId, targetUserId - ]); + if (id !== targetUserId) { + await this.query("INSERT INTO venel.channelMembers (channelId, userId) VALUES (?, ?), (?, ?)", [ + channelId, id, + channelId, targetUserId + ]); + } else { + await this.query("INSERT INTO venel.channelMembers (channelId, userId) VALUES (?, ?)", [channelId, id]); + } return channelId; } @@ -172,6 +176,10 @@ WHERE ur.userId = ?`, [userId]); return await this.query("SELECT * FROM venel.channelMembers WHERE channelId = ?", [channelId]); } + async getChannelMembersAsUsers(channelId: Id): Promise { + return await this.query("SELECT u.id, u.updatedAt, u.createdAt, u.username, u.displayname, u.description FROM venel.users u INNER JOIN venel.channelMembers cm ON u.id = cm.userId WHERE cm.channelId = ?", [channelId]); + } + async getMessageById(messageId: Id): Promise { const rows = await this.query("SELECT * FROM venel.messages WHERE id = ?", [messageId]); return rows ? rows[0] : null; @@ -189,8 +197,12 @@ WHERE ur.userId = ?`, [userId]); return await this.query("SELECT c.id, c.type, c.name, c.createdAt, c.updatedAt FROM venel.channels c INNER JOIN venel.channelMembers cm ON c.id = cm.channelId WHERE cm.userId = ?", [id]); } - async getLastMessageForChannel(channelId: Id) { + async getLastMessageForChannel(channelId: Id): Promise { const rows = await this.query("SELECT * FROM venel.messages WHERE channelId = ? ORDER BY createdAt DESC LIMIT 1", [channelId]); return rows ? rows[0] : null; } + + async searchUsers(query: string): Promise { + return await this.query("SELECT * FROM venel.users WHERE username LIKE ? OR displayname LIKE ?", [`%${query}%`, `%${query}%`]); + } } \ No newline at end of file diff --git a/src/features/messaging/endpoints.ts b/src/features/messaging/endpoints.ts index 61f1afe..615a93a 100644 --- a/src/features/messaging/endpoints.ts +++ b/src/features/messaging/endpoints.ts @@ -1,9 +1,11 @@ import {MariaDbDatabase} from "../database/mariaDbDatabase"; import {Request, Response} from "express"; -import {Channel, ChannelMember, Message, User} from "../database/models"; +import {ChannelMember, User} from "../database/models"; import {CLI} from "../../tooling/CLI"; import {PermissionsList} from "../../enums/permissionsList"; -import {SafeUser, safeUser} from "../authentication/actions"; +import {safeUser} from "../authentication/actions"; +import {ReceivableMessage} from "../../models/receivableMessage"; +import {UiChannel} from "../../models/uiChannel"; export class MessagingEndpoints { static async checkChannelAccess(db: MariaDbDatabase, user: User, channelId: number) { @@ -48,7 +50,11 @@ export class MessagingEndpoints { return; } await db.createMessage(channelId, user.id, text); - const message = await db.getLastMessageForChannel(channelId); + const message = await db.getLastMessageForChannel(channelId) as ReceivableMessage | null; + if (!message) { + res.status(500).send("Message not found"); + return; + } message.sender = safeUser(user); CLI.success(`Message sent to channel ${channelId} by user ${user.id}.`); res.json(message); @@ -202,16 +208,17 @@ export class MessagingEndpoints { } for (const channel of channels as UiChannel[]) { if (channel.type === "dm") { - const members = await db.getChannelMembers(channel.id); + const previousName = channel.name; + const members = await db.getChannelMembersAsUsers(channel.id); if (members) { for (const member of members) { - if (member.userId !== user.id) { - const targetUser = await db.getUserById(member.userId); - if (targetUser) { - channel.name = targetUser.displayname ?? targetUser.username; - } + if (member.id !== user.id) { + channel.name = member.displayname ?? member.username; } } + if (channel.name === previousName) { + channel.name = "Note to self"; + } channel.members = members; } } @@ -219,12 +226,23 @@ export class MessagingEndpoints { res.json(channels); } } -} -export interface ReceivableMessage extends Message { - sender: SafeUser; + static searchUsers(db: MariaDbDatabase) { + return async (req: Request, res: Response) => { + const query = req.query.query as string; + if (!query) { + res.status(400).send("Query is required"); + return; + } + + const users = await db.searchUsers(query); + if (!users) { + res.json([]); + return; + } + res.json(users.map(u => safeUser(u))); + } + + } } -export interface UiChannel extends Channel { - members: ChannelMember[]; -} \ No newline at end of file diff --git a/src/features/messagingFeature.ts b/src/features/messagingFeature.ts index db91b50..c6abe3c 100644 --- a/src/features/messagingFeature.ts +++ b/src/features/messagingFeature.ts @@ -11,6 +11,10 @@ export class MessagingFeature { app.delete(`${mPrefix}/deleteMessage`, AuthActions.checkAuthenticated, MessagingEndpoints.deleteMessage(db)); app.patch(`${mPrefix}/editMessage`, AuthActions.checkAuthenticated, MessagingEndpoints.editMessage(db)); + // Users + const uPrefix = "/api/users"; + app.get(`${uPrefix}/search`, AuthActions.checkAuthenticated, MessagingEndpoints.searchUsers(db)); + // Channels const cPrefix = "/api/channels"; app.post(`${cPrefix}/createDirect`, AuthActions.checkAuthenticated, MessagingEndpoints.createChannelDm(db)); diff --git a/src/models/receivableMessage.ts b/src/models/receivableMessage.ts new file mode 100644 index 0000000..7a16d43 --- /dev/null +++ b/src/models/receivableMessage.ts @@ -0,0 +1,6 @@ +import {Message} from "../features/database/models"; +import {SafeUser} from "../features/authentication/actions"; + +export interface ReceivableMessage extends Message { + sender: SafeUser; +} \ No newline at end of file diff --git a/src/models/uiChannel.ts b/src/models/uiChannel.ts new file mode 100644 index 0000000..579af61 --- /dev/null +++ b/src/models/uiChannel.ts @@ -0,0 +1,6 @@ +import {Channel} from "../features/database/models"; +import {SafeUser} from "../features/authentication/actions"; + +export interface UiChannel extends Channel { + members: SafeUser[]; +} \ No newline at end of file diff --git a/src/swagger.ts b/src/swagger.ts index 12a3a64..d31530c 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -1038,6 +1038,50 @@ export const swaggerOptions = { } } }, + "/api/users/search": { + get: { + security: [ + { + cookieAuth: [] + } + ], + summary: "Search for users", + tags: [ + "User Management" + ], + description: "Search for users", + parameters: [ + { + name: "query", + in: "query", + description: "The search query", + required: true, + schema: { + type: "string" + } + } + ], + responses: { + 200: { + description: "Users found", + content: { + 'application/json': { + schema: { + type: "array", + items: { + type: "object", + "$ref": "#/components/schemas/User" + } + } + } + } + }, + 400: { + description: "Bad Request: Query is required" + } + } + } + } }, components: { schemas: {