diff --git a/bun.lockb b/bun.lockb index e63cf05..5e2d093 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/prisma/migrations/20241118165010_userteam/migration.sql b/prisma/migrations/20241118165010_userteam/migration.sql new file mode 100644 index 0000000..47163ed --- /dev/null +++ b/prisma/migrations/20241118165010_userteam/migration.sql @@ -0,0 +1,31 @@ +/* + Warnings: + + - You are about to drop the `_TeamMembers` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE `_TeamMembers` DROP FOREIGN KEY `_TeamMembers_A_fkey`; + +-- DropForeignKey +ALTER TABLE `_TeamMembers` DROP FOREIGN KEY `_TeamMembers_B_fkey`; + +-- DropTable +DROP TABLE `_TeamMembers`; + +-- CreateTable +CREATE TABLE `UserTeam` ( + `UserTeamID` INTEGER NOT NULL AUTO_INCREMENT, + `UserID` VARCHAR(191) NOT NULL, + `TeamID` INTEGER NOT NULL, + `Role` VARCHAR(191) NOT NULL, + `JoinedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + PRIMARY KEY (`UserTeamID`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `UserTeam` ADD CONSTRAINT `UserTeam_UserID_fkey` FOREIGN KEY (`UserID`) REFERENCES `User`(`UserID`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `UserTeam` ADD CONSTRAINT `UserTeam_TeamID_fkey` FOREIGN KEY (`TeamID`) REFERENCES `Team`(`TeamID`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ed9372b..8edab84 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,97 +14,97 @@ datasource db { } model User { - UserID String @id @default(uuid()) // Primary Key - UserName String - Email String @unique - Password String - WorkingStyle String? - ProfileImage String? - Bio String? - CreatedAt DateTime @default(now()) - UpdatedAt DateTime @updatedAt @default(now()) - MessagesSent Message[] @relation("MessagesSent") - MessagesReceived Message[] @relation("MessagesReceived") - SwipesMade Swipe[] @relation("SwipesMade") - SwipesReceived Swipe[] @relation("SwipesReceived") - Notifications Notification[] - UserSkills UserSkills[] // Relation to UserSkills (many-to-many with Skills) - UserTeams UserTeam[] // Relation to UserTeam (many-to-many with Teams) + UserID String @id @default(uuid()) // Primary Key + UserName String + Email String @unique + Password String + WorkingStyle String? + ProfileImage String? + Bio String? + CreatedAt DateTime @default(now()) + UpdatedAt DateTime @default(now()) @updatedAt + MessagesSent Message[] @relation("MessagesSent") + MessagesReceived Message[] @relation("MessagesReceived") + SwipesMade Swipe[] @relation("SwipesMade") + SwipesReceived Swipe[] @relation("SwipesReceived") + Notifications Notification[] + UserSkills UserSkills[] // Relation to UserSkills (many-to-many with Skills) + UserTeams UserTeam[] // Relation to UserTeam (many-to-many with Teams) } model Message { - MessageID Int @id @default(autoincrement()) // Primary Key - Sender User @relation("MessagesSent", fields: [SenderID], references: [UserID]) + MessageID Int @id @default(autoincrement()) // Primary Key + Sender User @relation("MessagesSent", fields: [SenderID], references: [UserID]) SenderID String - Receiver User @relation("MessagesReceived", fields: [ReceiverID], references: [UserID]) + Receiver User @relation("MessagesReceived", fields: [ReceiverID], references: [UserID]) ReceiverID String MessageContent String - Timestamp DateTime @default(now()) - ReadStatus Boolean @default(false) + Timestamp DateTime @default(now()) + ReadStatus Boolean @default(false) } model Swipe { - SwipeID Int @id @default(autoincrement()) // Primary Key - SwipingUser User @relation("SwipesMade", fields: [SwipingUserID], references: [UserID]) - SwipingUserID String - SwipedUser User @relation("SwipesReceived", fields: [SwipedUserID], references: [UserID]) - SwipedUserID String - SwipeAction String // "Like" or "Dislike" - Timestamp DateTime @default(now()) + SwipeID Int @id @default(autoincrement()) // Primary Key + SwipingUser User @relation("SwipesMade", fields: [SwipingUserID], references: [UserID]) + SwipingUserID String + SwipedUser User @relation("SwipesReceived", fields: [SwipedUserID], references: [UserID]) + SwipedUserID String + SwipeAction String // "Like" or "Dislike" + Timestamp DateTime @default(now()) } model Notification { - NotificationID Int @id @default(autoincrement()) // Primary Key - User User @relation(fields: [UserID], references: [UserID]) - UserID String + NotificationID Int @id @default(autoincrement()) // Primary Key + User User @relation(fields: [UserID], references: [UserID]) + UserID String NotificationContent String - Timestamp DateTime @default(now()) - ReadStatus Boolean @default(false) + Timestamp DateTime @default(now()) + ReadStatus Boolean @default(false) } model Skills { - Skill_ID Int @id @default(autoincrement()) // Primary Key - Skill_Name String @unique // Unique skill name - UserSkills UserSkills[] // Relation to UserSkills + Skill_ID Int @id @default(autoincrement()) // Primary Key + Skill_Name String @unique // Unique skill name + UserSkills UserSkills[] // Relation to UserSkills } model UserSkills { - UserSkill_ID Int @id @default(autoincrement()) // Primary Key - User User @relation(fields: [UserID], references: [UserID]) - UserID String - Skill Skills @relation(fields: [Skill_ID], references: [Skill_ID], onDelete: Cascade) - Skill_ID Int + UserSkill_ID Int @id @default(autoincrement()) // Primary Key + User User @relation(fields: [UserID], references: [UserID]) + UserID String + Skill Skills @relation(fields: [Skill_ID], references: [Skill_ID], onDelete: Cascade) + Skill_ID Int // Add more fields if you need, like skill level or proficiency } model Hackathon { - HackathonID Int @id @default(autoincrement()) - Name String - Description String - StartDate DateTime - EndDate DateTime - Location String - Teams Team[] // One Hackathon can have multiple teams - CreatedAt DateTime @default(now()) - UpdatedAt DateTime @updatedAt + HackathonID Int @id @default(autoincrement()) + Name String + Description String + StartDate DateTime + EndDate DateTime + Location String + Teams Team[] // One Hackathon can have multiple teams + CreatedAt DateTime @default(now()) + UpdatedAt DateTime @updatedAt } model Team { - TeamID Int @id @default(autoincrement()) - TeamName String - HackathonID Int - Hackathon Hackathon @relation(fields: [HackathonID], references: [HackathonID]) - UserTeams UserTeam[] // Relation to UserTeam (many-to-many with Users) - CreatedAt DateTime @default(now()) - UpdatedAt DateTime @updatedAt + TeamID Int @id @default(autoincrement()) + TeamName String + HackathonID Int + Hackathon Hackathon @relation(fields: [HackathonID], references: [HackathonID]) + UserTeams UserTeam[] // Relation to UserTeam (many-to-many with Users) + CreatedAt DateTime @default(now()) + UpdatedAt DateTime @updatedAt } model UserTeam { - UserTeamID Int @id @default(autoincrement()) // Primary Key - User User @relation(fields: [UserID], references: [UserID]) - UserID String - Team Team @relation(fields: [TeamID], references: [TeamID]) - TeamID Int - Role String // Role of the user in the team (e.g., Member, Leader) - JoinedAt DateTime @default(now()) + UserTeamID Int @id @default(autoincrement()) // Primary Key + User User @relation(fields: [UserID], references: [UserID]) + UserID String + Team Team @relation(fields: [TeamID], references: [TeamID]) + TeamID Int + Role String // Role of the user in the team (e.g., Member, Leader) + JoinedAt DateTime @default(now()) } diff --git a/src/controllers/messageController.ts b/src/controllers/messageController.ts index 6b010b9..650a216 100644 --- a/src/controllers/messageController.ts +++ b/src/controllers/messageController.ts @@ -9,7 +9,6 @@ export const messageController = new Elysia({ prefix: "/message" }) async ({ body, error }) => { const { senderID, receiverID, messageContent } = body; - // Check if both users exist const sender = await prisma.user.findUnique({ where: { UserID: senderID }, @@ -44,58 +43,53 @@ export const messageController = new Elysia({ prefix: "/message" }) ) // Get all messages between two users - .get( - "/:senderID/:receiverID", - async ({ params, error }) => { - const { senderID, receiverID } = params; - - // Check if both users exist - const sender = await prisma.user.findUnique({ - where: { UserID: senderID }, - }); - - const receiver = await prisma.user.findUnique({ - where: { UserID: receiverID }, - }); + .get("/:senderID/:receiverID", async ({ params, error }) => { + const { senderID, receiverID } = params; - if (!sender || !receiver) { - return error(404, "Sender or receiver not found"); - } + // Check if both users exist + const sender = await prisma.user.findUnique({ + where: { UserID: senderID }, + }); - // Get all messages between the two users - const messages = await prisma.message.findMany({ - where: { - OR: [ - { SenderID: senderID, ReceiverID: receiverID }, - { SenderID: receiverID, ReceiverID: senderID }, - ], - }, - orderBy: { - Timestamp: 'asc', - }, - }); + const receiver = await prisma.user.findUnique({ + where: { UserID: receiverID }, + }); - return messages; + if (!sender || !receiver) { + return error(404, "Sender or receiver not found"); } - ) + + // Get all messages between the two users + const messages = await prisma.message.findMany({ + where: { + OR: [ + { SenderID: senderID, ReceiverID: receiverID }, + { SenderID: receiverID, ReceiverID: senderID }, + ], + }, + orderBy: { + Timestamp: "asc", + }, + }); + + return messages; + }) // Query the message from each users that users contact with - .get( - "/inbox/:userID", - async ({ params, error }) => { - const { userID } = params; + .get("/inbox/:userID", async ({ params, error }) => { + const { userID } = params; - // Check if the user exists - const user = await prisma.user.findUnique({ - where: { UserID: userID }, - }); + // Check if the user exists + const user = await prisma.user.findUnique({ + where: { UserID: userID }, + }); - if (!user) { - return error(404, "User not found"); - } + if (!user) { + return error(404, "User not found"); + } - // Get the latest message from each conversation - const latestMessages = await prisma.$queryRaw` + // Get the latest message from each conversation + const latestMessages = await prisma.$queryRaw` SELECT DISTINCT ON (LEAST("SenderID", "ReceiverID"), GREATEST("SenderID", "ReceiverID")) "MessageID", "SenderID", "ReceiverID", "MessageContent", "Timestamp" FROM "Message" @@ -103,6 +97,34 @@ export const messageController = new Elysia({ prefix: "/message" }) ORDER BY LEAST("SenderID", "ReceiverID"), GREATEST("SenderID", "ReceiverID"), "Timestamp" DESC `; - return latestMessages; + return latestMessages; + }) + + // Delete a message + .delete( + "/delete/:messageID", + async ({ params, error }) => { + const { messageID } = params; + + // Check if the message exists + const message = await prisma.message.findUnique({ + where: { MessageID: parseInt(messageID) }, + }); + + if (!message) { + return error(404, "Message not found"); + } + + // Delete the message + await prisma.message.delete({ + where: { MessageID: parseInt(messageID) }, + }); + + return { message: "Message deleted successfully" }; + }, + { + params: t.Object({ + messageID: t.String(), + }), } - ); \ No newline at end of file + ); diff --git a/src/controllers/notificationController.ts b/src/controllers/notificationController.ts index 02ae42f..85daedb 100644 --- a/src/controllers/notificationController.ts +++ b/src/controllers/notificationController.ts @@ -78,4 +78,30 @@ export const notificationController = new Elysia({ prefix: "/noti" }) body: t.Object({ ReadStatus: t.Boolean(), }), -}) \ No newline at end of file +}) + +// Delete a notification +.delete("/:notificationID", async ({ params, error }) => { + const { notificationID } = params; + + // Check if notification exists + const notification = await prisma.notification.findUnique({ + where: { NotificationID: notificationID }, + }); + + if (!notification) { + return error(404, "Notification not found"); + } + + // Delete the notification + await prisma.notification.delete({ + where: { NotificationID: notificationID }, + }); + + return { message: "Notification deleted successfully" }; +}, +{ + params: t.Object({ + notificationID: t.Number(), + }), +}) diff --git a/src/controllers/swipeController.ts b/src/controllers/swipeController.ts index eda0a95..e719968 100644 --- a/src/controllers/swipeController.ts +++ b/src/controllers/swipeController.ts @@ -78,6 +78,21 @@ export const swipeController = new Elysia({ prefix: "/swipe" }) NotificationContent: `You have a new match with ${userID}!`, }, ], + }) + + await prisma.message.createMany({ + data: [ + { + SenderID: userID, + ReceiverID: swipedUserID, + MessageContent: "Matched!!", + }, + { + SenderID: swipedUserID, + ReceiverID: userID, + MessageContent: "Matched!!", + }, + ], }); return { diff --git a/src/controllers/teamController.ts b/src/controllers/teamController.ts new file mode 100644 index 0000000..65b26f1 --- /dev/null +++ b/src/controllers/teamController.ts @@ -0,0 +1,79 @@ +import { Elysia, t } from "elysia"; +import { prisma } from "../prisma"; // Prisma client + +export const teamController = new Elysia({ prefix: "/team" }) + +.get("/", async () => { + const teams = await prisma.team.findMany(); + return teams; +}) + +.get("/hackathon/:id", async ({ params: { id }, error }) => { + const teams = await prisma.team.findMany({ + where: { + HackathonID: id, + }, + }); + + if (!teams) { + return error(404, "Teams not found"); + } + + return teams; +}, +{ + params: t.Object({ + id: t.Number(), + }), +} +) + +.post("/create", async ({ body, error }) => { + const { teamName, hackathonID } = body; + + await prisma.team.create({ + data: { + TeamName: teamName, + HackathonID: hackathonID, + }, + }); + + +}, { + body: t.Object({ + teamName: t.String(), + hackathonID: t.Number(), + }), +}) + +.put("/update", async ({ body, error }) => { + const { teamID, teamName } = body; + + const team = await prisma.team.update({ + where: { TeamID: teamID }, + data: { + TeamName: teamName, + }, + }); + + return team; +}, { + body: t.Object({ + teamID: t.Number(), + teamName: t.String(), + }), +}) + +.delete("/delete", async ({ body, error }) => { + const { teamID } = body; + + const team = await prisma.team.delete({ + where: { TeamID: teamID }, + }); + + return team; +}, { + body: t.Object({ + teamID: t.Number(), + }), +}) \ No newline at end of file