Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(API): Club Group Invitation Link API #536

Merged
merged 21 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions db/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ model User {
name String
tsimsStudentId Int @unique
LeaveRequest LeaveRequest[]
ClubRating ClubRating[]
ClubRating ClubRating[]
}

// Json types should be stored like {"zh": xxx, "en": xxx}.
Expand All @@ -30,6 +30,7 @@ model Club {
description Json // multilingual
LeaveRequest LeaveRequest[]
ClubRating ClubRating[]
JoinGroup JoinGroup[]
}

enum LeaveRequestStatus {
Expand All @@ -55,17 +56,29 @@ model LeaveRequest {
}

model ClubRating {
id String @id @default(dbgenerated("gen_random_uuid()"))
id String @id @default(dbgenerated("gen_random_uuid()"))
rateBy String // Link to User model
clubId Int // Link to Club model
rating Int @default(0) // 0 as initial rate lol
rating Int @default(0) // 0 as initial rate lol
comment String? // Optional comment for a rating
ratedAt DateTime @default(now()) // I guess we don't need this as we already have `rateScope`
ratedAt DateTime @default(now()) // I guess we don't need this as we already have `rateScope`
rateScope String // Identifier for the current semester, required since one can only vote once in one semester; stored as <year><a|b>, i.e., 2024a

// Relations
user User @relation(fields: [rateBy], references: [clerkUserId])
club Club @relation(fields: [clubId], references: [id])

@@unique([id, clubId, rateBy, rateScope]) // One student can vote only once in one semester
}
}

model JoinGroup {
at-wr marked this conversation as resolved.
Show resolved Hide resolved
id String @id @default(dbgenerated("gen_random_uuid()"))
clubId Int // Link to Club model
wechatGroupUrl String
wechatGroupExpiration DateTime @default(now())

// Relations
club Club @relation(fields: [clubId], references: [id])

@@unique([clubId])
}
42 changes: 42 additions & 0 deletions server/api/cas/group/club.get.ts
at-wr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PrismaClient } from '@prisma/client'
import * as z from 'zod'

const prisma = new PrismaClient()

const requestSchema = z.object({
club: z.string(),
at-wr marked this conversation as resolved.
Show resolved Hide resolved
})

export default eventHandler(async (event) => {
const { auth } = event.context

if (!auth.userId) {
setResponseStatus(event, 403)
return
}

// get clubId from request body
const requestBody = await readValidatedBody(event, body => requestSchema.parse(body))

const joinGroup = await prisma.joinGroup.findUnique({
where: {
clubId: Number(requestBody.club),
},
// only use include if the value required is not a scalar
// include: {
// wechatGroupUrl: true,
// },
})

if (!joinGroup) {
return {
data: null,
status: 'No joinGroup information found for the club',
}
}

return {
data: joinGroup,
status: 'success',
at-wr marked this conversation as resolved.
Show resolved Hide resolved
}
})
53 changes: 53 additions & 0 deletions server/api/cas/group/delete.delete.ts
at-wr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { PrismaClient } from '@prisma/client'
import * as z from 'zod'

const prisma = new PrismaClient()

const requestSchema = z.object({
clubId: z.string(),
})

export default eventHandler(async (event) => {
const { auth } = event.context

if (!auth.userId) {
setResponseStatus(event, 403)
return
}

const tsimsStudentId = await prisma.user.findUnique({
where: {
id: auth.userId,
},
}).then(user => user?.tsimsStudentId)
at-wr marked this conversation as resolved.
Show resolved Hide resolved

const requestBody = await readValidatedBody(event, body => requestSchema.parse(body))

// check if the user is in the presidentByTsimsStudentId or vicePresidentByTsimsStudentId of the club
const isPresidentOrVicePresident = await prisma.club.findFirst({
where: {
id: Number(requestBody.clubId),
OR: [
{
presidentByTsimsStudentId: tsimsStudentId,
},
{
vicesByTsimsStudentId: {
has: tsimsStudentId,
},
},
],
},
}).then(club => club !== null)

if (!isPresidentOrVicePresident)
setResponseStatus(event, 403)

await prisma.joinGroup.delete({
where: {
clubId: Number(requestBody.clubId),
},
}).catch(() => {
setResponseStatus(event, 404)
})
})
56 changes: 56 additions & 0 deletions server/api/cas/group/new.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { PrismaClient } from '@prisma/client'
import * as z from 'zod'

const prisma = new PrismaClient()

const requestSchema = z.object({
clubId: z.string(),
wechatGroupUrl: z.string().startsWith('https://weixin.qq.com/g/', { message: 'WeChat Group URL required' }),
wechatGroupExpiration: z.string().datetime(),
})

export default eventHandler(async (event) => {
const { auth } = event.context

if (!auth.userId) {
setResponseStatus(event, 403)
return
}

const tsimsStudentId = await prisma.user.findUnique({
where: {
id: auth.userId,
},
}).then(user => user?.tsimsStudentId)

const requestBody = await readValidatedBody(event, body => requestSchema.parse(body))

const isPresidentOrVicePresident = await prisma.club.findFirst({
where: {
id: Number(requestBody.clubId),
OR: [
{
presidentByTsimsStudentId: tsimsStudentId,
},
{
vicesByTsimsStudentId: {
has: tsimsStudentId,
},
},
],
},
}).then(club => club !== null)
at-wr marked this conversation as resolved.
Show resolved Hide resolved

if (!isPresidentOrVicePresident) {
setResponseStatus(event, 403)
return
}

await prisma.joinGroup.create({
data: {
clubId: Number(requestBody.clubId),
wechatGroupUrl: requestBody.wechatGroupUrl,
wechatGroupExpiration: new Date(requestBody.wechatGroupExpiration),
},
})
})
57 changes: 57 additions & 0 deletions server/api/cas/group/update.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { PrismaClient } from '@prisma/client'
import * as z from 'zod'

const prisma = new PrismaClient()

const requestSchema = z.object({
clubId: z.string(),
wechatGroupUrl: z.string().startsWith('https://weixin.qq.com/g/', { message: 'WeChat Group URL required' }),
wechatGroupExpiration: z.string().datetime(),
})

export default eventHandler(async (event) => {
const { auth } = event.context

if (!auth.userId) {
setResponseStatus(event, 403)
return
}

const tsimsStudentId = await prisma.user.findUnique({
where: {
id: auth.userId,
},
}).then(user => user?.tsimsStudentId)

const requestBody = await readValidatedBody(event, body => requestSchema.parse(body))

// check if the user is in the presidentByTsimsStudentId or vicePresidentByTsimsStudentId of the club
const isPresidentOrVicePresident = await prisma.club.findFirst({
where: {
id: Number(requestBody.clubId),
OR: [
{
presidentByTsimsStudentId: tsimsStudentId,
},
{
vicesByTsimsStudentId: {
has: tsimsStudentId,
},
},
],
},
}).then(club => club !== null)
at-wr marked this conversation as resolved.
Show resolved Hide resolved

if (!isPresidentOrVicePresident)
setResponseStatus(event, 403)

await prisma.joinGroup.update({
where: {
clubId: Number(requestBody.clubId),
},
data: {
wechatGroupUrl: requestBody.wechatGroupUrl,
wechatGroupExpiration: new Date(requestBody.wechatGroupExpiration),
},
})
})
5 changes: 5 additions & 0 deletions types/api/cas/group/club.d.ts
at-wr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { JoinGroup } from '@prisma/client'

interface GroupInfo {
data: JoinGroup[]
}