diff --git a/src/actions.js b/src/actions.js deleted file mode 100644 index 5e5321c..0000000 --- a/src/actions.js +++ /dev/null @@ -1,576 +0,0 @@ -"use server"; - -import { auth } from "@/authentication"; - -import { unstable_noStore as noStore } from "next/cache"; - -import { prisma } from "./database"; - -import { writeFile, mkdir, readdir, unlink } from "fs/promises"; -import path from "path"; - -export async function getFilteredUsers(filters) { - noStore(); - - // This shows content from all semesters. - let defaultSemester = { semesters: { some: { name: { contains: "" } } } }; - - // If no semester is given, default to the most recent one. - if (filters.semester == undefined) { - const semesters = await getAllSemesters(); - defaultSemester = { semesters: { some: { id: semesters[0].id } } }; - } else { - if (filters.semester != "All") { - // Select a specific semester. - defaultSemester = { semesters: { some: { name: { contains: filters.semester } } } }; - } // Else, keep the default. - } - - // If there is no search query, use empty string so taht. - if (filters.user === undefined) { - filters.user = ""; - } - - try { - const users = await prisma.user.findMany({ - where: { - OR: [{ name: { contains: `${filters.user}`, mode: "insensitive" } }], - AND: defaultSemester, - }, - - orderBy: { - name: "asc", - }, - - include: { - productions: true, - presentations: true, - semesters: true, - }, - }); - - return users; - } catch (error) { - console.error("Database error!!!:", error); - throw new Error(`Failed to get filtered users with filters: ${filters}`); - } -} - -export async function getFilteredWorks(filters) { - noStore(); - - // This shows content from all semesters. - let defaultSemester = { semester: { name: { contains: "" } } }; - - // If no semester is given, default to the most recent one. - if (filters.semester == undefined) { - const semesters = await getAllSemesters(); - defaultSemester = { semester: { id: semesters[0].id } }; - } else { - if (filters.semester != "All") { - // Select a specific semester. - defaultSemester = { semester: { name: { contains: filters.semester } } }; - } // Else, keep the default. - } - - // If there is no search query, use empty string so taht. - if (filters.query === undefined) { - filters.query = ""; - } - - try { - const users = await prisma.work.findMany({ - where: { - OR: [{ name: { contains: `${filters.query}`, mode: "insensitive" } }], - AND: defaultSemester, - }, - - orderBy: { - name: "asc", - }, - - include: { - creators: true, - group: true, - }, - }); - - return users; - } catch (error) { - console.error("Database error:", error); - throw new Error(`Failed to get filtered works with filters: ${filters}`); - } -} - -export async function getUser(username) { - const user = await prisma.user.findFirst({ - where: { username: username }, - include: { presentations: { include: { presenters: true } }, productions: true }, - }); - return user; -} - -export async function getSemester(id) { - const semester = await prisma.semester.findFirst({ - where: { id: id }, - include: { users: true, thursdays: true }, - }); - return semester; -} - -export async function getSemesterFromName(name) { - if (name == "All") { - name = ""; - } - - if (name == undefined) { - const semesters = await getAllSemesters(); - name = semesters[0].name; - } - - const semester = await prisma.semester.findFirst({ - where: { name: { contains: name } }, - include: { users: true, thursdays: { include: { groups: { include: { presentations: { include: { presenters: true } } } } } } }, - }); - return semester; -} - -export async function getThursday(id) { - const thursday = await prisma.thursday.findFirst({ - where: { id: id }, - include: { groups: { include: { producers: true, presentations: { include: { presenters: true } } } } }, - }); - return thursday; -} - -export async function getWork(id) { - const work = await prisma.work.findFirst({ - where: { id: id }, - include: { group: true, presenters: true }, - }); - return work; -} - -export async function getGroup(id) { - const group = await prisma.group.findFirst({ - where: { id: id }, - include: { producers: true, presentations: { include: { presenters: true } }, thursday: true }, - }); - return group; -} - -export async function editUser({ id, name, about, image, email, link, pronouns, admin }) { - return prisma.user.update({ - where: { id }, - data: { name, about, image, link, pronouns, email, admin }, - }); -} - -export async function addUser(data) { - const current_semester = await getAllSemesters(); - - try { - await prisma.user.create({ - data: { - email: data.email, - username: data.email.split("@")[0], - image: data.image, - name: data.name, - about: data.about?.trim() || "I'm new to SIM!", - link: data.link, - pronouns: data.pronouns, - semesters: { connect: { id: current_semester[0].id } }, - admin: data.admin, - }, - }); - } catch (error) { - throw new Error(error); - } -} - -export async function handleImageUpload(file, filename, directory) { - if (!file || !filename) return null; - - const directory_path = path.join(process.cwd(), `public/${directory}`); - await mkdir(directory_path, { recursive: true }); - - const bytes = await file.arrayBuffer(); - const buffer = Buffer.from(bytes); - const ext = file.name.split(".").pop(); - const filename_w_ext = `${filename}.${ext}`; - const filepath = path.join(directory_path, filename_w_ext); - - const existing_files = await readdir(directory_path); - const matching_files = existing_files.filter((f) => f.startsWith(`${filename}.`)); - - await Promise.all(matching_files.map((f) => unlink(path.join(directory_path, f)))); - - await writeFile(filepath, buffer); - - return `/${directory}/${filename_w_ext}`; -} - -export async function removeUser(data) { - await prisma.user.delete({ - where: { - id: data.id, - }, - }); -} - -export async function editGroup(data) { - // Step 1: Update group basic info - await prisma.group.update({ - where: { id: data.id }, - data: { - name: data.name, - location: data.location, - producers: { set: data.producers.map((id) => ({ id })) }, - thursday: { connect: { id: data.thursday } }, - }, - }); - - // Step 2: Get all current work IDs already in DB - const existingWorks = await prisma.work.findMany({ - where: { group_id: data.id }, - select: { id: true }, - }); - const existingIds = existingWorks.map((w) => w.id); - - // Step 3: Split into new vs existing works - const newPresentations = data.presentations.filter((p) => !p.id); - const existingPresentations = data.presentations.filter((p) => p.id); - const currentPresentationIds = existingPresentations.map((p) => p.id); - - // Step 4: Create new works - await Promise.all( - newPresentations.map((p) => - prisma.work.create({ - data: { - name: p.name, - about: p.about, - image: p.image ?? "", - group: { connect: { id: data.id } }, - presenters: { - connect: p.presenters.map((id) => ({ id })), - }, - }, - }), - ), - ); - - // ✅ Step 5: Update existing works and presenters - await Promise.all( - existingPresentations.map((p) => - prisma.work.update({ - where: { id: p.id }, - data: { - name: p.name, - about: p.about, - image: p.image ?? "", - presenters: { - set: p.presenters.map((id) => ({ id })), // 👈 Replace presenters - }, - }, - }), - ), - ); - - // Step 6: Delete removed works - const worksToDelete = existingIds.filter((id) => !currentPresentationIds.includes(id)); - await Promise.all( - worksToDelete.map((id) => - prisma.work.delete({ - where: { id }, - }), - ), - ); -} - -export async function editThursday(data) { - await prisma.thursday.update({ - where: { - id: data.id, - }, - data: { - name: data.name, - date: data.date, - groups: { set: data.groups.map((id) => ({ id })) }, - semester: { connect: { id: data.semester } }, - }, - }); -} - -export async function editSemester(data) { - await prisma.semester.update({ - where: { - id: data.id, - }, - data: { - name: data.name, - users: { set: data.users.map((id) => ({ id })) }, - }, - }); -} - -export async function editWork(data) { - await prisma.work.update({ - where: { - id: data.id, - }, - data: { - name: data.name, - medium: data.medium, - about: data.about, - presenters: { set: data.users.map((id) => ({ id })) }, - group: { connect: { id: data.group } }, - }, - }); -} - -export async function getCurrentUser() { - const session = await auth(); - if (session) { - const user = await prisma.user.findUnique({ - where: { email: session.user.email }, - }); - return user; - } - return null; -} - -export async function isCurrentUserAdmin() { - const user = await getCurrentUser(); - if (user) { - return user.admin; - } else { - return false; - } -} - -export async function getAllSemesters() { - try { - const semesters = await prisma.semester.findMany({ - include: { thursdays: { include: { groups: true } }, users: true }, - }); - - // Sort Semesters by Thursday dates. - semesters.sort((a, b) => { - const aFirstThursdayDate = a.thursdays.length > 0 ? a.thursdays[0].date : Infinity; - const bFirstThursdayDate = b.thursdays.length > 0 ? b.thursdays[0].date : Infinity; - return bFirstThursdayDate - aFirstThursdayDate; - }); - - return semesters; - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to fetch Semesters."); - } -} - -export async function getAllUsers() { - try { - const users = await prisma.user.findMany({ - include: { productions: true, semesters: true }, - }); - - return users; - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to getAllUsers()."); - } -} - -export async function getAllWorks() { - try { - const works = await prisma.work.findMany({ - include: { presenters: true, group: true }, - }); - - return works; - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to getAllWorks()."); - } -} - -export async function getAllGroups() { - try { - const groups = await prisma.group.findMany({ - include: { producers: true }, - }); - - // Add keys to the users so we can transfer them. - groups.map((group) => { - group.key = groups.indexOf(group); - }); - - return groups; - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to getAllUsers()."); - } -} - -export async function getFilteredThursdays(filters) { - noStore(); - - // This shows content from all semesters. - let defaultSemester = { semester: { name: { contains: "" } } }; - - // If no semester is given, default to the most recent one. - if (filters.semester == undefined) { - const semesters = await getAllSemesters(); - defaultSemester = { semester: { id: semesters[0].id } }; - } else { - if (filters.semester != "All") { - // Select a specific semester. - defaultSemester = { semester: { name: { contains: filters.semester } } }; - } // Else, keep the default. - } - - // If there is no search query, use empty string so taht. - if (filters.thursdays === undefined) { - filters.thursdays = ""; - } - - try { - const thursdays = await prisma.thursday.findMany({ - where: { - OR: [ - { name: { contains: `${filters.thursdays}`, mode: "insensitive" } }, - { - groups: { - some: { - name: { contains: `${filters.thursdays}`, mode: "insensitive" }, - }, - }, - }, - { - groups: { - some: { - id: { contains: `${filters.thursdays}`, mode: "insensitive" }, - }, - }, - }, - { - groups: { - some: { - location: { - contains: `${filters.thursdays}`, - mode: "insensitive", - }, - }, - }, - }, - ], - AND: defaultSemester, - }, - - orderBy: { - date: "asc", - }, - - include: { - groups: { - include: { producers: true, presentations: { include: { presenters: true } } }, - }, - }, - }); - - return thursdays; - } catch (error) { - console.error("database error:", error); - throw new Error("failed to fetch thursdays."); - } -} - -export async function addSemester(data) { - const thursdays = []; - data.dates.map((day) => { - thursdays.push({ - name: data.dates.indexOf(day) % 2 == 0 ? "Big Day" : "Small Day", - date: day, - }); - }); - - const people = []; - data.users.map((user) => { - people.push({ id: user }); - }); - - try { - await prisma.semester.create({ - data: { - name: data.name, - thursdays: { - createMany: { - data: thursdays, - }, - }, - users: { - connect: people, - }, - }, - }); - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to addSemester()."); - } -} - -export async function addGroup(data) { - try { - const group = await prisma.group.create({ - data: { - name: data.name, - location: data.location, - producers: { - connect: data.producers.map((id) => ({ id })), - }, - thursday: { - connect: { id: data.thursday }, - }, - }, - }); - await Promise.all( - data.presentations.map((p) => - prisma.work.create({ - data: { - name: p.name, - about: p.about, - image: p.image ?? "", - group: { connect: { id: group.id } }, - presenters: { - connect: p.presenters.map((id) => ({ id })), - }, - }, - }), - ), - ); - } catch (error) { - console.error("Database Error:", error); - throw new Error(`Failed to addGroup(data) with data:`); - } -} - -export async function addWork(data) { - try { - await prisma.work.create({ - data: { - name: data.name, - about: data.about, - image: "", // One day, we should allow users to upload images for works so their art can be archived. For now, just pass an empty string. - presenters: { - connect: data.presenters.map((id) => ({ id })), - }, - group: { - connect: { id: data.group }, - }, - }, - }); - } catch (error) { - console.error("Database Error:", error); - throw new Error("Failed to addGroup(data) with data:", data); - } -} diff --git a/src/actions/authorize.js b/src/actions/authorize.js deleted file mode 100644 index d74db18..0000000 --- a/src/actions/authorize.js +++ /dev/null @@ -1 +0,0 @@ -// permissions for pages diff --git a/src/app/admin/layout.js b/src/app/admin/layout.js index a0d2a55..a49952a 100644 --- a/src/app/admin/layout.js +++ b/src/app/admin/layout.js @@ -1,4 +1,4 @@ -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function AdminLayout({ children }) { const session = await auth(); diff --git a/src/app/admin/page.js b/src/app/admin/page.js index be2c333..956e475 100644 --- a/src/app/admin/page.js +++ b/src/app/admin/page.js @@ -1,7 +1,8 @@ // import styles import styles from "../../components/admin/semesters/semesters.module.css"; import Link from "next/link"; -import { getFilteredUsers, getAllSemesters, getSemesterFromName, getFilteredThursdays } from "../../actions"; +import { getFilteredUsers, getAllSemesters, getFilteredThursdays } from "@/server/shared"; +import { getSemesterFromName } from "@/server/general"; import Header from "../../components/Header"; diff --git a/src/app/admin/semesters/[id]/edit/page.js b/src/app/admin/semesters/[id]/edit/page.js index ee48927..36f3872 100644 --- a/src/app/admin/semesters/[id]/edit/page.js +++ b/src/app/admin/semesters/[id]/edit/page.js @@ -1,6 +1,7 @@ import SemesterForm from "../../../../../components/admin/semesters/SemesterForm"; -import { getSemester, getAllUsers, editSemester } from "../../../../../actions"; +import { getSemester, getAllUsers } from "@/server/general"; +import { editSemester } from "@/server/admin"; import { redirect } from "next/navigation"; export default async function EditSemester({ params }) { @@ -22,6 +23,6 @@ export default async function EditSemester({ params }) { async function onSubmitEditSemester(data) { "use server"; - editSemester(data); + await editSemester(data); redirect("/admin"); } diff --git a/src/app/admin/semesters/add/page.js b/src/app/admin/semesters/add/page.js index f247aa1..f9b8fd2 100644 --- a/src/app/admin/semesters/add/page.js +++ b/src/app/admin/semesters/add/page.js @@ -1,6 +1,8 @@ import SemesterForm from "../../../../components/admin/semesters/SemesterForm"; -import { getAllSemesters, getAllUsers, addSemester } from "../../../../actions"; +import { getAllSemesters } from "@/server/shared"; +import { getAllUsers } from "@/server/general"; +import { addSemester } from "@/server/admin"; import { redirect } from "next/navigation"; export default async function AddSemester() { @@ -27,7 +29,7 @@ export default async function AddSemester() { async function onSubmitAddSemester(data) { "use server"; data.dates = generateThursdays(data.dates); - addSemester(data); + await addSemester(data); redirect("/admin"); } diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index a4a0828..0319997 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,2 +1,2 @@ -import { handlers } from "@/authentication"; +import { handlers } from "@/server/auth/config"; export const { GET, POST } = handlers; diff --git a/src/app/api/token/route.ts b/src/app/api/token/route.ts index f89fa21..2c317a9 100644 --- a/src/app/api/token/route.ts +++ b/src/app/api/token/route.ts @@ -1,6 +1,6 @@ // /app/api/token/route.ts -import { auth } from "@/authentication"; -import { prisma } from "../../../database"; +import { auth } from "@/server/auth/config"; +import { prisma } from "@/server/database"; import { NextResponse } from "next/server"; export async function GET() { diff --git a/src/app/layout.js b/src/app/layout.js index 53ee413..fde1acd 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -1,7 +1,7 @@ import "@ant-design/v5-patch-for-react-19"; import "antd/dist/reset.css"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; import "./globals.css"; diff --git a/src/app/thursdays/[thursday_id]/edit/page.js b/src/app/thursdays/[thursday_id]/edit/page.js index e80a010..e07d13f 100644 --- a/src/app/thursdays/[thursday_id]/edit/page.js +++ b/src/app/thursdays/[thursday_id]/edit/page.js @@ -2,7 +2,9 @@ import { notFound, redirect } from "next/navigation"; import ThursdayForm from "../../../../components/thursdays/ThursdayForm"; -import { getThursday, getAllGroups, getAllSemesters, editThursday } from "../../../../actions"; +import { getAllGroups } from "@/server/general"; +import { getThursday, getAllSemesters } from "@/server/shared"; +import { editThursday } from "@/server/admin"; export default async function EditThursday({ params }) { const { thursday_id } = await params; diff --git a/src/app/thursdays/[thursday_id]/groups/[group_id]/edit/page.js b/src/app/thursdays/[thursday_id]/groups/[group_id]/edit/page.js index 05bba3a..ea36238 100644 --- a/src/app/thursdays/[thursday_id]/groups/[group_id]/edit/page.js +++ b/src/app/thursdays/[thursday_id]/groups/[group_id]/edit/page.js @@ -2,7 +2,9 @@ import { notFound, redirect } from "next/navigation"; import GroupForm from "../../../../../../components/thursdays/groups/GroupForm"; -import { getGroup, getAllUsers, getAllWorks, getAllSemesters, editGroup } from "../../../../../../actions"; +import { getAllUsers, getAllWorks } from "@/server/general"; +import { getGroup, getAllSemesters } from "@/server/shared"; +import { editGroup } from "@/server/admin"; export default async function EditGroup({ params }) { const { group_id } = await params; diff --git a/src/app/thursdays/[thursday_id]/groups/add/page.js b/src/app/thursdays/[thursday_id]/groups/add/page.js index d6347da..cbc6557 100644 --- a/src/app/thursdays/[thursday_id]/groups/add/page.js +++ b/src/app/thursdays/[thursday_id]/groups/add/page.js @@ -1,6 +1,8 @@ import GroupForm from "../../../../../components/thursdays/groups/GroupForm"; -import { getAllUsers, getAllWorks, getAllSemesters, addGroup, getThursday } from "../../../../../actions"; +import { getAllUsers, getAllWorks } from "@/server/general"; +import { getAllSemesters, getThursday } from "@/server/shared"; +import { addGroup } from "@/server/admin"; import { redirect } from "next/navigation"; export default async function AddGroup({ params }) { diff --git a/src/app/thursdays/[thursday_id]/page.js b/src/app/thursdays/[thursday_id]/page.js index 69cb486..8c4db9c 100644 --- a/src/app/thursdays/[thursday_id]/page.js +++ b/src/app/thursdays/[thursday_id]/page.js @@ -1,6 +1,6 @@ import { notFound } from "next/navigation"; -import { getThursday } from "../../../actions"; +import { getThursday } from "@/server/shared"; import ThursdayCard from "../../../components/ThursdayCard"; diff --git a/src/app/thursdays/page.js b/src/app/thursdays/page.js index a535dca..5de6307 100644 --- a/src/app/thursdays/page.js +++ b/src/app/thursdays/page.js @@ -1,7 +1,6 @@ import styles from "../../components/thursdays/thursdays.module.css"; import Button from "@/components//Button"; -import { getFilteredThursdays } from "../../actions"; -import { getAllSemesters } from "../../actions"; +import { getFilteredThursdays, getAllSemesters } from "@/server/shared"; import Header from "@/components/Header"; import Input from "@/components/Input"; import Select from "@/components/Select"; diff --git a/src/app/users/[username]/edit/page.js b/src/app/users/[username]/edit/page.js index 870d48f..edc40e3 100644 --- a/src/app/users/[username]/edit/page.js +++ b/src/app/users/[username]/edit/page.js @@ -3,9 +3,12 @@ import { notFound, redirect } from "next/navigation"; import UserForm from "../../../../components/users/UserForm"; import Button from "@/components//Button"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; -import { handleImageUpload, getUser, editUser, removeUser, getCurrentUser } from "../../../../actions"; +import { handleImageUpload, editUser } from "@/server/general"; +import { getUser } from "@/server/shared"; +import { getCurrentUser } from "@/server/auth/context"; +import { removeUser } from "@/server/admin"; export default async function EditUser({ params }) { const { username } = await params; diff --git a/src/app/users/[username]/page.js b/src/app/users/[username]/page.js index 39af05d..a4b12e5 100644 --- a/src/app/users/[username]/page.js +++ b/src/app/users/[username]/page.js @@ -1,4 +1,5 @@ -import { getUser, getCurrentUser } from "../../../actions"; +import { getUser } from "@/server/shared"; +import { getCurrentUser } from "@/server/auth/context"; import { notFound, redirect } from "next/navigation"; import Button from "@/components//Button"; import styles from "../../../components/users/User.module.css"; diff --git a/src/app/users/add/page.js b/src/app/users/add/page.js index 40fc674..bc4092b 100644 --- a/src/app/users/add/page.js +++ b/src/app/users/add/page.js @@ -1,10 +1,11 @@ import { redirect } from "next/navigation"; import UserForm from "@/components/users/UserForm"; -import { addUser, handleImageUpload } from "../../../actions"; +import { handleImageUpload } from "@/server/general"; +import { addUser } from "@/server/admin"; import path from "path"; import { copyFile } from "fs/promises"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function AddUser() { const session = await auth(); diff --git a/src/app/users/page.js b/src/app/users/page.js index b9ede1d..9ae3410 100644 --- a/src/app/users/page.js +++ b/src/app/users/page.js @@ -1,6 +1,4 @@ -import { getFilteredUsers } from "../../actions"; - -import { getAllSemesters } from "../../actions"; +import { getFilteredUsers, getAllSemesters } from "@/server/shared"; import Header from "@/components/Header"; import Button from "@/components/Button"; import Input from "@/components/Input"; @@ -9,7 +7,7 @@ import Select from "@/components/Select"; import styles from "../../components/users/Users.module.css"; import UserCard from "../../components/users/UserCard"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function Users({ searchParams }) { const filters = await searchParams; diff --git a/src/components/AuthenticationButtons.js b/src/components/AuthenticationButtons.js index 3254dcf..729583c 100644 --- a/src/components/AuthenticationButtons.js +++ b/src/components/AuthenticationButtons.js @@ -1,6 +1,6 @@ "use client"; -import { logIn, logOut } from "@/actions/authenticate"; +import { logIn, logOut } from "@/server/auth/actions"; import Button from "@/components/Button"; diff --git a/src/components/GroupCard.js b/src/components/GroupCard.js index 09e263c..8956482 100644 --- a/src/components/GroupCard.js +++ b/src/components/GroupCard.js @@ -3,7 +3,7 @@ import styles from "./thursdays/thursdaycard.module.css"; import Button from "./Button"; import Header from "./Header"; import PresentationCard from "./PresentationCard"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function GroupCard({ thursday, group }) { const session = await auth(); var producers = group.producers.filter((user) => user.admin === false); diff --git a/src/components/NavBar.js b/src/components/NavBar.js index 83d59b2..1dff940 100644 --- a/src/components/NavBar.js +++ b/src/components/NavBar.js @@ -2,7 +2,7 @@ import styles from "./NavBar.module.css"; import Button from "@/components/Button"; import NavSelect from "./NavSelect"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function NavBar() { const session = await auth(); diff --git a/src/components/ThursdayCard.js b/src/components/ThursdayCard.js index 8ce2ed8..93f7248 100644 --- a/src/components/ThursdayCard.js +++ b/src/components/ThursdayCard.js @@ -3,7 +3,7 @@ import styles from "./thursdays/thursdaycard.module.css"; import Button from "./Button"; import Header from "./Header"; import GroupCard from "./GroupCard"; -import { auth } from "@/authentication"; +import { auth } from "@/server/auth/config"; export default async function ThursdayCard({ thursday }) { const session = await auth(); diff --git a/src/components/admin/semesters/SemesterForm.js b/src/components/admin/semesters/SemesterForm.js index 19d0368..be5ff90 100644 --- a/src/components/admin/semesters/SemesterForm.js +++ b/src/components/admin/semesters/SemesterForm.js @@ -29,7 +29,7 @@ export default function SemesterForm({ onSubmit, semester, usersFromCurrentSemes }; return ( { + action={async (formData) => { const data = { name: formData.get("name"), dates: [dates[0].$d, dates[1].$d], @@ -38,7 +38,7 @@ export default function SemesterForm({ onSubmit, semester, usersFromCurrentSemes if (semester) { data.id = semester.id; } - onSubmit(data); + await onSubmit(data); }} button={semester ? "Edit Semester" : "Add Semester"} > diff --git a/src/server/admin/index.js b/src/server/admin/index.js new file mode 100644 index 0000000..f3bcf89 --- /dev/null +++ b/src/server/admin/index.js @@ -0,0 +1,192 @@ +"use server"; + +import { prisma } from "@/server/database"; +import { getAllSemesters } from "@/server/shared"; + +export async function addUser(data) { + const current_semester = await getAllSemesters(); + + try { + await prisma.user.create({ + data: { + email: data.email, + username: data.email.split("@")[0], + image: data.image, + name: data.name, + about: data.about?.trim() || "I'm new to SIM!", + link: data.link, + pronouns: data.pronouns, + semesters: { connect: { id: current_semester[0].id } }, + admin: data.admin, + }, + }); + } catch (error) { + throw new Error(error); + } +} + +export async function removeUser(data) { + await prisma.user.delete({ + where: { + id: data.id, + }, + }); +} + +export async function editGroup(data) { + await prisma.group.update({ + where: { id: data.id }, + data: { + name: data.name, + location: data.location, + producers: { set: data.producers.map((id) => ({ id })) }, + thursday: { connect: { id: data.thursday } }, + }, + }); + + const existingWorks = await prisma.work.findMany({ + where: { group_id: data.id }, + select: { id: true }, + }); + const existingIds = existingWorks.map((w) => w.id); + + const newPresentations = data.presentations.filter((p) => !p.id); + const existingPresentations = data.presentations.filter((p) => p.id); + const currentPresentationIds = existingPresentations.map((p) => p.id); + + await Promise.all( + newPresentations.map((p) => + prisma.work.create({ + data: { + name: p.name, + about: p.about, + image: p.image ?? "", + group: { connect: { id: data.id } }, + presenters: { + connect: p.presenters.map((id) => ({ id })), + }, + }, + }), + ), + ); + + await Promise.all( + existingPresentations.map((p) => + prisma.work.update({ + where: { id: p.id }, + data: { + name: p.name, + about: p.about, + image: p.image ?? "", + presenters: { + set: p.presenters.map((id) => ({ id })), + }, + }, + }), + ), + ); + + const worksToDelete = existingIds.filter((id) => !currentPresentationIds.includes(id)); + await Promise.all( + worksToDelete.map((id) => + prisma.work.delete({ + where: { id }, + }), + ), + ); +} + +export async function editThursday(data) { + await prisma.thursday.update({ + where: { + id: data.id, + }, + data: { + name: data.name, + date: data.date, + groups: { set: data.groups.map((id) => ({ id })) }, + semester: { connect: { id: data.semester } }, + }, + }); +} + +export async function editSemester(data) { + await prisma.semester.update({ + where: { + id: data.id, + }, + data: { + name: data.name, + users: { set: data.users.map((id) => ({ id })) }, + }, + }); +} + +export async function addSemester(data) { + const thursdays = []; + data.dates.map((day) => { + thursdays.push({ + name: data.dates.indexOf(day) % 2 == 0 ? "Big Day" : "Small Day", + date: day, + }); + }); + + const people = []; + data.users.map((user) => { + people.push({ id: user }); + }); + + try { + await prisma.semester.create({ + data: { + name: data.name, + thursdays: { + createMany: { + data: thursdays, + }, + }, + users: { + connect: people, + }, + }, + }); + } catch (error) { + console.error("Database Error:", error); + throw new Error("Failed to addSemester()."); + } +} + +export async function addGroup(data) { + try { + const group = await prisma.group.create({ + data: { + name: data.name, + location: data.location, + producers: { + connect: data.producers.map((id) => ({ id })), + }, + thursday: { + connect: { id: data.thursday }, + }, + }, + }); + await Promise.all( + data.presentations.map((p) => + prisma.work.create({ + data: { + name: p.name, + about: p.about, + image: p.image ?? "", + group: { connect: { id: group.id } }, + presenters: { + connect: p.presenters.map((id) => ({ id })), + }, + }, + }), + ), + ); + } catch (error) { + console.error("Database Error:", error); + throw new Error(`Failed to addGroup(data) with data:`); + } +} diff --git a/src/actions/authenticate.js b/src/server/auth/actions.ts similarity index 69% rename from src/actions/authenticate.js rename to src/server/auth/actions.ts index 5c3b92c..8797856 100644 --- a/src/actions/authenticate.js +++ b/src/server/auth/actions.ts @@ -1,6 +1,6 @@ "use server"; -import { signIn, signOut } from "@/authentication"; +import { signIn, signOut } from "@/server/auth/config"; export async function logIn() { await signIn("google"); diff --git a/src/authentication.ts b/src/server/auth/config.ts similarity index 98% rename from src/authentication.ts rename to src/server/auth/config.ts index 243a909..07a5e7f 100644 --- a/src/authentication.ts +++ b/src/server/auth/config.ts @@ -3,7 +3,7 @@ import Google from "next-auth/providers/google"; import { PrismaAdapter } from "@auth/prisma-adapter"; -import { prisma } from "@/database"; +import { prisma } from "@/server/database"; export const { handlers, signIn, signOut, auth } = NextAuth({ adapter: PrismaAdapter(prisma), diff --git a/src/server/auth/context.ts b/src/server/auth/context.ts new file mode 100644 index 0000000..c8a86bf --- /dev/null +++ b/src/server/auth/context.ts @@ -0,0 +1,22 @@ +import { auth } from "@/server/auth/config"; +import { prisma } from "@/server/database"; + +export async function getCurrentUser() { + const session = await auth(); + if (session?.user?.email) { + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + }); + return user; + } + return null; +} + +export async function isCurrentUserAdmin() { + const user = await getCurrentUser(); + if (user) { + return user.admin; + } else { + return false; + } +} diff --git a/src/database.ts b/src/server/database.ts similarity index 100% rename from src/database.ts rename to src/server/database.ts diff --git a/src/server/general/index.js b/src/server/general/index.js new file mode 100644 index 0000000..e3a6d1c --- /dev/null +++ b/src/server/general/index.js @@ -0,0 +1,103 @@ +"use server"; + +import { prisma } from "@/server/database"; +import { getAllSemesters } from "@/server/shared"; +import { writeFile, mkdir, readdir, unlink } from "fs/promises"; +import path from "path"; + +export async function getSemester(id) { + const semester = await prisma.semester.findFirst({ + where: { id: id }, + include: { users: true, thursdays: true }, + }); + return semester; +} + +export async function getSemesterFromName(name) { + if (name == "All") { + name = ""; + } + + if (name == undefined) { + const semesters = await getAllSemesters(); + name = semesters[0].name; + } + + const semester = await prisma.semester.findFirst({ + where: { name: { contains: name } }, + include: { users: true, thursdays: { include: { groups: { include: { presentations: { include: { presenters: true } } } } } } }, + }); + return semester; +} + +export async function editUser({ id, name, about, image, email, link, pronouns, admin }) { + return prisma.user.update({ + where: { id }, + data: { name, about, image, link, pronouns, email, admin }, + }); +} + +export async function handleImageUpload(file, filename, directory) { + if (!file || !filename) return null; + + const directory_path = path.join(process.cwd(), `public/${directory}`); + await mkdir(directory_path, { recursive: true }); + + const bytes = await file.arrayBuffer(); + const buffer = Buffer.from(bytes); + const ext = file.name.split(".").pop(); + const filename_w_ext = `${filename}.${ext}`; + const filepath = path.join(directory_path, filename_w_ext); + + const existing_files = await readdir(directory_path); + const matching_files = existing_files.filter((f) => f.startsWith(`${filename}.`)); + + await Promise.all(matching_files.map((f) => unlink(path.join(directory_path, f)))); + + await writeFile(filepath, buffer); + + return `/${directory}/${filename_w_ext}`; +} + +export async function getAllUsers() { + try { + const users = await prisma.user.findMany({ + include: { productions: true, semesters: true }, + }); + + return users; + } catch (error) { + console.error("Database Error:", error); + throw new Error("Failed to getAllUsers()."); + } +} + +export async function getAllWorks() { + try { + const works = await prisma.work.findMany({ + include: { presenters: true, group: true }, + }); + + return works; + } catch (error) { + console.error("Database Error:", error); + throw new Error("Failed to getAllWorks()."); + } +} + +export async function getAllGroups() { + try { + const groups = await prisma.group.findMany({ + include: { producers: true }, + }); + + groups.map((group) => { + group.key = groups.indexOf(group); + }); + + return groups; + } catch (error) { + console.error("Database Error:", error); + throw new Error("Failed to getAllUsers()."); + } +} diff --git a/src/server/shared/index.js b/src/server/shared/index.js new file mode 100644 index 0000000..01da3f2 --- /dev/null +++ b/src/server/shared/index.js @@ -0,0 +1,152 @@ +"use server"; + +import { prisma } from "@/server/database"; + +import { unstable_noStore as noStore } from "next/cache"; + +export async function getFilteredUsers(filters) { + noStore(); + + let defaultSemester = { semesters: { some: { name: { contains: "" } } } }; + + if (filters.semester == undefined) { + const semesters = await getAllSemesters(); + defaultSemester = { semesters: { some: { id: semesters[0].id } } }; + } else if (filters.semester != "All") { + defaultSemester = { semesters: { some: { name: { contains: filters.semester } } } }; + } + + if (filters.user === undefined) { + filters.user = ""; + } + + try { + const users = await prisma.user.findMany({ + where: { + OR: [{ name: { contains: `${filters.user}`, mode: "insensitive" } }], + AND: defaultSemester, + }, + orderBy: { + name: "asc", + }, + include: { + productions: true, + presentations: true, + semesters: true, + }, + }); + + return users; + } catch (error) { + console.error("Database error!!!:", error); + throw new Error(`Failed to get filtered users with filters: ${filters}`); + } +} + +export async function getUser(username) { + const user = await prisma.user.findFirst({ + where: { username: username }, + include: { presentations: { include: { presenters: true } }, productions: true }, + }); + return user; +} + +export async function getThursday(id) { + const thursday = await prisma.thursday.findFirst({ + where: { id: id }, + include: { groups: { include: { producers: true, presentations: { include: { presenters: true } } } } }, + }); + return thursday; +} + +export async function getGroup(id) { + const group = await prisma.group.findFirst({ + where: { id: id }, + include: { producers: true, presentations: { include: { presenters: true } }, thursday: true }, + }); + return group; +} + +export async function getAllSemesters() { + try { + const semesters = await prisma.semester.findMany({ + include: { thursdays: { include: { groups: true } }, users: true }, + }); + + semesters.sort((a, b) => { + const aFirstThursdayDate = a.thursdays.length > 0 ? a.thursdays[0].date : Infinity; + const bFirstThursdayDate = b.thursdays.length > 0 ? b.thursdays[0].date : Infinity; + return bFirstThursdayDate - aFirstThursdayDate; + }); + + return semesters; + } catch (error) { + console.error("Database Error:", error); + throw new Error("Failed to fetch Semesters."); + } +} + +export async function getFilteredThursdays(filters) { + noStore(); + + let defaultSemester = { semester: { name: { contains: "" } } }; + + if (filters.semester == undefined) { + const semesters = await getAllSemesters(); + defaultSemester = { semester: { id: semesters[0].id } }; + } else if (filters.semester != "All") { + defaultSemester = { semester: { name: { contains: filters.semester } } }; + } + + if (filters.thursdays === undefined) { + filters.thursdays = ""; + } + + try { + const thursdays = await prisma.thursday.findMany({ + where: { + OR: [ + { name: { contains: `${filters.thursdays}`, mode: "insensitive" } }, + { + groups: { + some: { + name: { contains: `${filters.thursdays}`, mode: "insensitive" }, + }, + }, + }, + { + groups: { + some: { + id: { contains: `${filters.thursdays}`, mode: "insensitive" }, + }, + }, + }, + { + groups: { + some: { + location: { + contains: `${filters.thursdays}`, + mode: "insensitive", + }, + }, + }, + }, + ], + AND: defaultSemester, + }, + orderBy: { + date: "asc", + }, + include: { + groups: { + include: { producers: true, presentations: { include: { presenters: true } } }, + }, + }, + }); + + return thursdays; + } catch (error) { + console.error("database error:", error); + throw new Error("failed to fetch thursdays."); + } +}