diff --git a/components/Modal/Collection/TeamManagement.tsx b/components/Modal/Collection/TeamManagement.tsx index 68a2a81..b551969 100644 --- a/components/Modal/Collection/TeamManagement.tsx +++ b/components/Modal/Collection/TeamManagement.tsx @@ -9,7 +9,6 @@ import { } from "@fortawesome/free-solid-svg-icons"; import useCollectionStore from "@/store/collections"; import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global"; -import { useSession } from "next-auth/react"; import addMemberToCollection from "@/lib/client/addMemberToCollection"; import Checkbox from "../../Checkbox"; import SubmitButton from "@/components/SubmitButton"; @@ -18,6 +17,7 @@ import usePermissions from "@/hooks/usePermissions"; import { toast } from "react-hot-toast"; import getPublicUserData from "@/lib/client/getPublicUserData"; import TextInput from "@/components/TextInput"; +import useAccountStore from "@/store/account"; type Props = { toggleCollectionModal: Function; @@ -34,6 +34,7 @@ export default function TeamManagement({ collection, method, }: Props) { + const { account } = useAccountStore(); const permissions = usePermissions(collection.id as number); const currentURL = new URL(document.URL); @@ -59,8 +60,6 @@ export default function TeamManagement({ const { addCollection, updateCollection } = useCollectionStore(); - const session = useSession(); - const setMemberState = (newMember: Member) => { if (!collection) return null; @@ -158,7 +157,7 @@ export default function TeamManagement({ onKeyDown={(e) => e.key === "Enter" && addMemberToCollection( - session.data?.user.username as string, + account.username as string, memberUsername || "", collection, setMemberState @@ -169,7 +168,7 @@ export default function TeamManagement({
addMemberToCollection( - session.data?.user.username as string, + account.username as string, memberUsername || "", collection, setMemberState diff --git a/components/Modal/Link/AddOrEditLink.tsx b/components/Modal/Link/AddOrEditLink.tsx index eab727b..1c863bd 100644 --- a/components/Modal/Link/AddOrEditLink.tsx +++ b/components/Modal/Link/AddOrEditLink.tsx @@ -49,6 +49,7 @@ export default function AddOrEditLink({ screenshotPath: "", pdfPath: "", readabilityPath: "", + textContent: "", collection: { name: "", ownerId: data?.user.id as number, diff --git a/hooks/useInitialData.tsx b/hooks/useInitialData.tsx index c6b1ea3..1300265 100644 --- a/hooks/useInitialData.tsx +++ b/hooks/useInitialData.tsx @@ -21,5 +21,5 @@ export default function useInitialData() { // setLinks(); setAccount(data.user.id); } - }, [status]); + }, [status, data]); } diff --git a/layouts/AuthRedirect.tsx b/layouts/AuthRedirect.tsx index a5fd1ad..8942666 100644 --- a/layouts/AuthRedirect.tsx +++ b/layouts/AuthRedirect.tsx @@ -4,6 +4,7 @@ import Loader from "../components/Loader"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import useInitialData from "@/hooks/useInitialData"; +import useAccountStore from "@/store/account"; interface Props { children: ReactNode; @@ -13,6 +14,7 @@ export default function AuthRedirect({ children }: Props) { const router = useRouter(); const { status, data } = useSession(); const [redirect, setRedirect] = useState(true); + const { account } = useAccountStore(); const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; @@ -25,7 +27,8 @@ export default function AuthRedirect({ children }: Props) { status === "authenticated" && (data.user.isSubscriber === true || data.user.isSubscriber === undefined) && - !data.user.username + account.id && + !account.username ) { router.push("/choose-username").then(() => { setRedirect(false); @@ -66,7 +69,7 @@ export default function AuthRedirect({ children }: Props) { } else { setRedirect(false); } - }, [status]); + }, [status, account]); if (status !== "loading" && !redirect) return <>{children}; else return <>; diff --git a/lib/api/authenticateUser.ts b/lib/api/authenticateUser.ts index 6ef51b6..d6bb3f3 100644 --- a/lib/api/authenticateUser.ts +++ b/lib/api/authenticateUser.ts @@ -1,21 +1,31 @@ -import { NextApiRequest } from "next"; +import { NextApiRequest, NextApiResponse } from "next"; import { getToken } from "next-auth/jwt"; +import { prisma } from "./db"; +import { User } from "@prisma/client"; type Props = { req: NextApiRequest; + res: NextApiResponse; }; -export default async function authenticateUser({ req }: Props) { +export default async function authenticateUser({ + req, + res, +}: Props): Promise { const token = await getToken({ req }); + const userId = token?.id; - if (!token?.id) { - return { response: "You must be logged in.", status: 401 }; - } else if (token.isSubscriber === false) - return { - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - status: 401, - }; + if (!userId) { + res.status(401).json({ message: "You must be logged in." }); + return null; + } else if (token.isSubscriber === false) { + res.status(401).json({ + message: + "You are not a subscriber, feel free to reach out to us at support@linkwarden.app if you think this is an issue.", + }); + return null; + } - return token; + const user = await prisma.user.findUnique({ where: { id: userId } }); + return user; } diff --git a/lib/api/controllers/users/userId/updateUserById.ts b/lib/api/controllers/users/userId/updateUserById.ts index f167ea6..fc4c015 100644 --- a/lib/api/controllers/users/userId/updateUserById.ts +++ b/lib/api/controllers/users/userId/updateUserById.ts @@ -10,12 +10,7 @@ const emailEnabled = process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false; export default async function updateUserById( - sessionUser: { - id: number; - username: string; - email: string; - isSubscriber: boolean; - }, + userId: number, data: AccountSettings ) { if (emailEnabled && !data.email) @@ -49,7 +44,7 @@ export default async function updateUserById( const userIsTaken = await prisma.user.findFirst({ where: { - id: { not: sessionUser.id }, + id: { not: userId }, OR: emailEnabled ? [ { @@ -97,7 +92,7 @@ export default async function updateUserById( createFolder({ filePath: `uploads/avatar` }); await createFile({ - filePath: `uploads/avatar/${sessionUser.id}.jpg`, + filePath: `uploads/avatar/${userId}.jpg`, data: base64Data, isBase64: true, }); @@ -112,9 +107,13 @@ export default async function updateUserById( }; } } else if (data.image == "") { - removeFile({ filePath: `uploads/avatar/${sessionUser.id}.jpg` }); + removeFile({ filePath: `uploads/avatar/${userId}.jpg` }); } + const previousEmail = ( + await prisma.user.findUnique({ where: { id: userId } }) + )?.email; + // Other settings const saltRounds = 10; @@ -122,14 +121,14 @@ export default async function updateUserById( const updatedUser = await prisma.user.update({ where: { - id: sessionUser.id, + id: userId, }, data: { name: data.name, username: data.username.toLowerCase().trim(), email: data.email?.toLowerCase().trim(), isPrivate: data.isPrivate, - image: data.image ? `uploads/avatar/${sessionUser.id}.jpg` : "", + image: data.image ? `uploads/avatar/${userId}.jpg` : "", archiveAsScreenshot: data.archiveAsScreenshot, archiveAsPDF: data.archiveAsPDF, archiveAsWaybackMachine: data.archiveAsWaybackMachine, @@ -167,7 +166,7 @@ export default async function updateUserById( // Delete whitelistedUsers that are not present in the new list await prisma.whitelistedUser.deleteMany({ where: { - userId: sessionUser.id, + userId: userId, username: { in: usernamesToDelete, }, @@ -179,17 +178,17 @@ export default async function updateUserById( await prisma.whitelistedUser.create({ data: { username, - userId: sessionUser.id, + userId: userId, }, }); } const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY; - if (STRIPE_SECRET_KEY && emailEnabled && sessionUser.email !== data.email) + if (STRIPE_SECRET_KEY && emailEnabled && previousEmail !== data.email) await updateCustomerEmail( STRIPE_SECRET_KEY, - sessionUser.email, + previousEmail as string, data.email as string ); diff --git a/pages/api/v1/archives/[...params].ts b/pages/api/v1/archives/[...params].ts index d29099e..5534ea3 100644 --- a/pages/api/v1/archives/[...params].ts +++ b/pages/api/v1/archives/[...params].ts @@ -1,28 +1,20 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import getPermission from "@/lib/api/getPermission"; import readFile from "@/lib/api/storage/readFile"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function Index(req: NextApiRequest, res: NextApiResponse) { if (!req.query.params) return res.status(401).json({ response: "Invalid parameters." }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); + const collectionId = req.query.params[0]; const linkId = req.query.params[1]; - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.username) - return res.status(401).json({ response: "You must be logged in." }); - else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); - const collectionIsAccessible = await getPermission({ - userId: session.user.id, + userId: user.id, collectionId: Number(collectionId), }); diff --git a/pages/api/v1/auth/[...nextauth].ts b/pages/api/v1/auth/[...nextauth].ts index 67b01f7..3228a35 100644 --- a/pages/api/v1/auth/[...nextauth].ts +++ b/pages/api/v1/auth/[...nextauth].ts @@ -18,7 +18,7 @@ const providers: Provider[] = [ type: "credentials", credentials: {}, async authorize(credentials, req) { - console.log("User logged in..."); + console.log("User logged in attempt..."); if (!credentials) return null; const { username, password } = credentials as { @@ -51,7 +51,7 @@ const providers: Provider[] = [ } if (passwordMatches) { - return findUser; + return { id: findUser?.id }; } else return null as any; }, }), @@ -101,9 +101,15 @@ export const authOptions: AuthOptions = { STRIPE_SECRET_KEY && (trigger || subscriptionIsTimesUp || !token.isSubscriber) ) { + const user = await prisma.user.findUnique({ + where: { + id: Number(token.sub), + }, + }); + const subscription = await checkSubscription( STRIPE_SECRET_KEY, - token.email as string + user?.email as string ); if (subscription.subscriptionCanceledAt) { @@ -115,27 +121,22 @@ export const authOptions: AuthOptions = { if (trigger === "signIn") { token.id = user.id as number; - token.username = (user as any).username; } else if (trigger === "update" && token.id) { - console.log(token); - const user = await prisma.user.findUnique({ where: { id: token.id as number, }, }); - if (user?.name && user.username && user.email) { + if (user?.name) { token.name = user.name; - token.username = user.username?.toLowerCase(); - token.email = user.email?.toLowerCase(); } } + return token; }, async session({ session, token }) { session.user.id = token.id; - session.user.username = token.username; session.user.isSubscriber = token.isSubscriber; return session; diff --git a/pages/api/v1/avatar/[id].ts b/pages/api/v1/avatar/[id].ts index 0a379fc..307cb8a 100644 --- a/pages/api/v1/avatar/[id].ts +++ b/pages/api/v1/avatar/[id].ts @@ -1,26 +1,13 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import { prisma } from "@/lib/api/db"; import readFile from "@/lib/api/storage/readFile"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function Index(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - const userId = session?.user.id; - const username = session?.user.username?.toLowerCase(); const queryId = Number(req.query.id); - if (!userId || !username) - return res - .setHeader("Content-Type", "text/plain") - .status(401) - .send("You must be logged in."); - else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (!queryId) return res @@ -28,7 +15,7 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { .status(401) .send("Invalid parameters."); - if (userId !== queryId) { + if (user.id !== queryId) { const targetUser = await prisma.user.findUnique({ where: { id: queryId, @@ -42,7 +29,11 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) { (whitelistedUsername) => whitelistedUsername.username ); - if (targetUser?.isPrivate && !whitelistedUsernames?.includes(username)) { + if ( + targetUser?.isPrivate && + user.username && + !whitelistedUsernames?.includes(user.username) + ) { return res .setHeader("Content-Type", "text/plain") .status(400) diff --git a/pages/api/v1/collections/[id].ts b/pages/api/v1/collections/[id].ts index 4f888d9..6f98690 100644 --- a/pages/api/v1/collections/[id].ts +++ b/pages/api/v1/collections/[id].ts @@ -1,33 +1,25 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import updateCollectionById from "@/lib/api/controllers/collections/collectionId/updateCollectionById"; import deleteCollectionById from "@/lib/api/controllers/collections/collectionId/deleteCollectionById"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function collections( req: NextApiRequest, res: NextApiResponse ) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "PUT") { const updated = await updateCollectionById( - session.user.id, + user.id, Number(req.query.id) as number, req.body ); return res.status(updated.status).json({ response: updated.response }); } else if (req.method === "DELETE") { const deleted = await deleteCollectionById( - session.user.id, + user.id, Number(req.query.id) as number ); return res.status(deleted.status).json({ response: deleted.response }); diff --git a/pages/api/v1/collections/index.ts b/pages/api/v1/collections/index.ts index f5f00ae..5f938f5 100644 --- a/pages/api/v1/collections/index.ts +++ b/pages/api/v1/collections/index.ts @@ -1,30 +1,22 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import getCollections from "@/lib/api/controllers/collections/getCollections"; import postCollection from "@/lib/api/controllers/collections/postCollection"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function collections( req: NextApiRequest, res: NextApiResponse ) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { - const collections = await getCollections(session.user.id); + const collections = await getCollections(user.id); return res .status(collections.status) .json({ response: collections.response }); } else if (req.method === "POST") { - const newCollection = await postCollection(req.body, session.user.id); + const newCollection = await postCollection(req.body, user.id); return res .status(newCollection.status) .json({ response: newCollection.response }); diff --git a/pages/api/v1/dashboard/index.ts b/pages/api/v1/dashboard/index.ts index af98b4b..dfbd50a 100644 --- a/pages/api/v1/dashboard/index.ts +++ b/pages/api/v1/dashboard/index.ts @@ -1,19 +1,11 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import { LinkRequestQuery } from "@/types/global"; import getDashboardData from "@/lib/api/controllers/dashboard/getDashboardData"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function links(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { const convertedData: LinkRequestQuery = { @@ -21,7 +13,7 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) { cursor: req.query.cursor ? Number(req.query.cursor as string) : undefined, }; - const links = await getDashboardData(session.user.id, convertedData); + const links = await getDashboardData(user.id, convertedData); return res.status(links.status).json({ response: links.response }); } } diff --git a/pages/api/v1/getToken.ts b/pages/api/v1/getToken.ts index 84114d2..0143d89 100644 --- a/pages/api/v1/getToken.ts +++ b/pages/api/v1/getToken.ts @@ -4,7 +4,7 @@ import { getToken } from "next-auth/jwt"; export default async (req: NextApiRequest, res: NextApiResponse) => { // if using `NEXTAUTH_SECRET` env variable, we detect it, and you won't actually need to `secret` // const token = await getToken({ req }) - const token = await getToken({ req }); - console.log("JSON Web Token", token); - res.end(); + // const token = await getToken({ req }); + // console.log("JSON Web Token", token); + // res.end(); }; diff --git a/pages/api/v1/links/[id]/archive/index.ts b/pages/api/v1/links/[id]/archive/index.ts index 750d440..2306202 100644 --- a/pages/api/v1/links/[id]/archive/index.ts +++ b/pages/api/v1/links/[id]/archive/index.ts @@ -1,21 +1,13 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import archive from "@/lib/api/archive"; import { prisma } from "@/lib/api/db"; +import authenticateUser from "@/lib/api/authenticateUser"; const RE_ARCHIVE_LIMIT = Number(process.env.RE_ARCHIVE_LIMIT) || 5; export default async function links(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); const link = await prisma.link.findUnique({ where: { @@ -29,7 +21,7 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) { response: "Link not found.", }); - if (link.collection.ownerId !== session.user.id) + if (link.collection.ownerId !== user.id) return res.status(401).json({ response: "Permission denied.", }); @@ -49,7 +41,7 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) { } minutes or create a new one.`, }); - archive(link.id, link.url, session.user.id); + archive(link.id, link.url, user.id); return res.status(200).json({ response: "Link is being archived.", }); diff --git a/pages/api/v1/links/[id]/index.ts b/pages/api/v1/links/[id]/index.ts index 28defa4..290aea5 100644 --- a/pages/api/v1/links/[id]/index.ts +++ b/pages/api/v1/links/[id]/index.ts @@ -1,29 +1,21 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import deleteLinkById from "@/lib/api/controllers/links/linkId/deleteLinkById"; import updateLinkById from "@/lib/api/controllers/links/linkId/updateLinkById"; import getLinkById from "@/lib/api/controllers/links/linkId/getLinkById"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function links(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { - const updated = await getLinkById(session.user.id, Number(req.query.id)); + const updated = await getLinkById(user.id, Number(req.query.id)); return res.status(updated.status).json({ response: updated.response, }); } else if (req.method === "PUT") { const updated = await updateLinkById( - session.user.id, + user.id, Number(req.query.id), req.body ); @@ -31,7 +23,7 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) { response: updated.response, }); } else if (req.method === "DELETE") { - const deleted = await deleteLinkById(session.user.id, Number(req.query.id)); + const deleted = await deleteLinkById(user.id, Number(req.query.id)); return res.status(deleted.status).json({ response: deleted.response, }); diff --git a/pages/api/v1/links/index.ts b/pages/api/v1/links/index.ts index 7fe25c9..35dcacc 100644 --- a/pages/api/v1/links/index.ts +++ b/pages/api/v1/links/index.ts @@ -1,25 +1,12 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import getLinks from "@/lib/api/controllers/links/getLinks"; import postLink from "@/lib/api/controllers/links/postLink"; import { LinkRequestQuery } from "@/types/global"; -import { getToken } from "next-auth/jwt"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function links(req: NextApiRequest, res: NextApiResponse) { - const token = await getToken({ req }); - - // const session = await getServerSession(req, res, authOptions); - - return res.status(200).json(token); - - if (!session?.user?.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { // Convert the type of the request query to "LinkRequestQuery" @@ -45,10 +32,10 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) { searchByTags: req.query.searchByTags === "true" ? true : undefined, }; - const links = await getLinks(session.user.id, convertedData); + const links = await getLinks(user.id, convertedData); return res.status(links.status).json({ response: links.response }); } else if (req.method === "POST") { - const newlink = await postLink(req.body, session.user.id); + const newlink = await postLink(req.body, user.id); return res.status(newlink.status).json({ response: newlink.response, }); diff --git a/pages/api/v1/migration/index.ts b/pages/api/v1/migration/index.ts index 08d8336..74db8c6 100644 --- a/pages/api/v1/migration/index.ts +++ b/pages/api/v1/migration/index.ts @@ -1,10 +1,9 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import exportData from "@/lib/api/controllers/migration/exportData"; import importFromHTMLFile from "@/lib/api/controllers/migration/importFromHTMLFile"; import importFromLinkwarden from "@/lib/api/controllers/migration/importFromLinkwarden"; import { MigrationFormat, MigrationRequest } from "@/types/global"; +import authenticateUser from "@/lib/api/authenticateUser"; export const config = { api: { @@ -15,18 +14,11 @@ export const config = { }; export default async function users(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user.id) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { - const data = await exportData(session.user.id); + const data = await exportData(user.id); if (data.status === 200) return res @@ -39,10 +31,10 @@ export default async function users(req: NextApiRequest, res: NextApiResponse) { let data; if (request.format === MigrationFormat.htmlFile) - data = await importFromHTMLFile(session.user.id, request.data); + data = await importFromHTMLFile(user.id, request.data); if (request.format === MigrationFormat.linkwarden) - data = await importFromLinkwarden(session.user.id, request.data); + data = await importFromLinkwarden(user.id, request.data); if (data) return res.status(data.status).json({ response: data.response }); } diff --git a/pages/api/v1/payment/index.ts b/pages/api/v1/payment/index.ts index 23e0673..c4076af 100644 --- a/pages/api/v1/payment/index.ts +++ b/pages/api/v1/payment/index.ts @@ -1,21 +1,20 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import paymentCheckout from "@/lib/api/paymentCheckout"; import { Plan } from "@/types/global"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function users(req: NextApiRequest, res: NextApiResponse) { const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY; const MONTHLY_PRICE_ID = process.env.MONTHLY_PRICE_ID; const YEARLY_PRICE_ID = process.env.YEARLY_PRICE_ID; - const session = await getServerSession(req, res, authOptions); - if (!session?.user?.id) - return res.status(401).json({ response: "You must be logged in." }); - else if (!STRIPE_SECRET_KEY || !MONTHLY_PRICE_ID || !YEARLY_PRICE_ID) { + if (!STRIPE_SECRET_KEY || !MONTHLY_PRICE_ID || !YEARLY_PRICE_ID) { return res.status(400).json({ response: "Payment is disabled." }); } + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); + let PRICE_ID = MONTHLY_PRICE_ID; if ((Number(req.query.plan) as unknown as Plan) === Plan.monthly) @@ -26,7 +25,7 @@ export default async function users(req: NextApiRequest, res: NextApiResponse) { if (req.method === "GET") { const users = await paymentCheckout( STRIPE_SECRET_KEY, - session?.user.email, + user.email as string, PRICE_ID ); return res.status(users.status).json({ response: users.response }); diff --git a/pages/api/v1/tags/[id].ts b/pages/api/v1/tags/[id].ts index eb6a1b5..2445a10 100644 --- a/pages/api/v1/tags/[id].ts +++ b/pages/api/v1/tags/[id].ts @@ -1,23 +1,15 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import updateTag from "@/lib/api/controllers/tags/tagId/updeteTagById"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function tags(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.username) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); const tagId = Number(req.query.id); if (req.method === "PUT") { - const tags = await updateTag(session.user.id, tagId, req.body); + const tags = await updateTag(user.id, tagId, req.body); return res.status(tags.status).json({ response: tags.response }); } } diff --git a/pages/api/v1/tags/index.ts b/pages/api/v1/tags/index.ts index a516123..dd4cd39 100644 --- a/pages/api/v1/tags/index.ts +++ b/pages/api/v1/tags/index.ts @@ -1,21 +1,13 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import getTags from "@/lib/api/controllers/tags/getTags"; +import authenticateUser from "@/lib/api/authenticateUser"; export default async function tags(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - - if (!session?.user?.username) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { - const tags = await getTags(session.user.id); + const tags = await getTags(user.id); return res.status(tags.status).json({ response: tags.response }); } } diff --git a/pages/api/v1/users/[id].ts b/pages/api/v1/users/[id].ts index 2eff580..0c69c25 100644 --- a/pages/api/v1/users/[id].ts +++ b/pages/api/v1/users/[id].ts @@ -1,15 +1,23 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/pages/api/v1/auth/[...nextauth]"; import getUserById from "@/lib/api/controllers/users/userId/getUserById"; import getPublicUserById from "@/lib/api/controllers/users/userId/getPublicUserById"; import updateUserById from "@/lib/api/controllers/users/userId/updateUserById"; import deleteUserById from "@/lib/api/controllers/users/userId/deleteUserById"; +import authenticateUser from "@/lib/api/authenticateUser"; +import { prisma } from "@/lib/api/db"; +import { getToken } from "next-auth/jwt"; export default async function users(req: NextApiRequest, res: NextApiResponse) { - const session = await getServerSession(req, res, authOptions); - const userId = session?.user.id; - const username = session?.user.username; + const token = await getToken({ req }); + const userId = token?.id; + + if (!token?.id) + return res.status(400).json({ response: "Invalid parameters." }); + + const username = (await prisma.user.findUnique({ where: { id: token.id } })) + ?.username; + + if (!username) return res.status(404).json({ response: "User not found." }); const lookupId = req.query.id as string; const isSelf = @@ -23,26 +31,18 @@ export default async function users(req: NextApiRequest, res: NextApiResponse) { return res.status(users.status).json({ response: users.response }); } - if (!userId) { - return res.status(401).json({ response: "You must be logged in." }); - } else if (session?.user?.isSubscriber === false) - return res.status(401).json({ - response: - "You are not a subscriber, feel free to reach out to us at support@linkwarden.app in case of any issues.", - }); + const user = await authenticateUser({ req, res }); + if (!user) return res.status(404).json({ response: "User not found." }); if (req.method === "GET") { - const users = await getUserById(session.user.id); + const users = await getUserById(user.id); return res.status(users.status).json({ response: users.response }); } else if (req.method === "PUT") { - const updated = await updateUserById(session.user, req.body); + const updated = await updateUserById(user.id, req.body); return res.status(updated.status).json({ response: updated.response }); - } else if ( - req.method === "DELETE" && - session.user.id === Number(req.query.id) - ) { + } else if (req.method === "DELETE" && user.id === Number(req.query.id)) { console.log(req.body); - const updated = await deleteUserById(session.user.id, req.body); + const updated = await deleteUserById(user.id, req.body); return res.status(updated.status).json({ response: updated.response }); } } diff --git a/pages/api/v1/webhooks/stripe/index.ts b/pages/api/v1/webhooks/stripe/index.ts deleted file mode 100644 index a1df0ea..0000000 --- a/pages/api/v1/webhooks/stripe/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -// TODO - Stripe webhooks for user cancellation... - -// import { NextApiRequest, NextApiResponse } from "next"; -// import Stripe from "stripe"; -// import { buffer } from "micro"; -// import { prisma } from "@/lib/api/db"; - -// const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, { -// apiVersion: "2022-11-15", -// }); - -// const endpointSecret = -// "whsec_7c144bcd924041257e3d83eac1e2fba9c8a938b240fd8adb1c902f079e0cdee0"; - -// export const config = { -// api: { -// bodyParser: false, -// }, -// }; - -// export default async function handler( -// req: NextApiRequest, -// res: NextApiResponse -// ) { -// if (req.method === "POST") { -// const buf = await buffer(req); -// const sig = req.headers["stripe-signature"]; - -// let event: Stripe.Event; - -// try { -// if (!sig) throw new Error("Stripe Signature is not defined."); -// event = stripe.webhooks.constructEvent(buf, sig, endpointSecret); -// } catch (err) { -// console.log(err); -// return res.status(400).send({ response: "Error..." }); -// } - -// // Handle the event -// switch (event.type) { -// case "customer.subscription.deleted": -// const customerSubscriptionDeleted = event.data.object as any; - -// // Revoke all the token under the customers email... - -// const customer = (await stripe.customers.retrieve( -// customerSubscriptionDeleted.customer -// )) as any; - -// if (customer?.email) { -// // Revoke tokens inside the database -// } - -// break; -// // ... handle other event types -// default: -// console.log(`Unhandled event type ${event.type}`); -// } - -// return res.status(200).send({ response: "Done!" }); -// } -// } diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index ea09742..d4858e2 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -12,7 +12,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import MainLayout from "@/layouts/MainLayout"; -import { useSession } from "next-auth/react"; import ProfilePhoto from "@/components/ProfilePhoto"; import SortDropdown from "@/components/SortDropdown"; import useModalStore from "@/store/modals"; diff --git a/pages/subscribe.tsx b/pages/subscribe.tsx index 7d44614..4398b9a 100644 --- a/pages/subscribe.tsx +++ b/pages/subscribe.tsx @@ -1,8 +1,6 @@ -import SubmitButton from "@/components/SubmitButton"; import { signOut } from "next-auth/react"; import { useState } from "react"; import { toast } from "react-hot-toast"; -import { useSession } from "next-auth/react"; import { useRouter } from "next/router"; import CenteredForm from "@/layouts/CenteredForm"; import { Plan } from "@/types/global"; @@ -12,7 +10,6 @@ export default function Subscribe() { const [plan, setPlan] = useState(1); - const { data, status } = useSession(); const router = useRouter(); async function submit() { diff --git a/pages/test.tsx b/pages/test.tsx deleted file mode 100644 index beae84f..0000000 --- a/pages/test.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import SubmitButton from "@/components/SubmitButton"; -import TextInput from "@/components/TextInput"; -import CenteredForm from "@/layouts/CenteredForm"; -import { signIn } from "next-auth/react"; -import Link from "next/link"; -import { useState, FormEvent } from "react"; -import { toast } from "react-hot-toast"; - -interface FormData { - username: string; - password: string; -} - -const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER; - -export default function Login() { - const [submitLoader, setSubmitLoader] = useState(false); - - const [form, setForm] = useState({ - username: "", - password: "", - }); - - async function loginUser(event: FormEvent) { - event.preventDefault(); - - if (form.username !== "" && form.password !== "") { - setSubmitLoader(true); - - const load = toast.loading("Authenticating..."); - - const res = await signIn("credentials", { - username: form.username, - password: form.password, - redirect: false, - }); - - console.log(res); - - toast.dismiss(load); - - setSubmitLoader(false); - - if (!res?.ok) { - toast.error("Invalid login."); - } - } else { - toast.error("Please fill out all the fields."); - } - } - - return ( - -
-
-

- Enter your credentials -

- -
- -
-

- Username - {emailEnabled ? " or Email" : undefined} -

- - setForm({ ...form, username: e.target.value })} - /> -
- -
-

- Password -

- - setForm({ ...form, password: e.target.value })} - /> - {emailEnabled && ( -
- - Forgot Password? - -
- )} -
- - - {process.env.NEXT_PUBLIC_DISABLE_REGISTRATION === - "true" ? undefined : ( -
-

- New here? -

- - Sign Up - -
- )} -
-
-
- ); -} diff --git a/types/global.ts b/types/global.ts index b878a84..6daf62a 100644 --- a/types/global.ts +++ b/types/global.ts @@ -5,7 +5,10 @@ type OptionalExcluding = Partial & Pick; export interface LinkIncludingShortenedCollectionAndTags - extends Omit { + extends Omit< + Link, + "id" | "createdAt" | "collectionId" | "updatedAt" | "lastPreserved" + > { id?: number; createdAt?: string; collectionId?: number; diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts index d78b116..e412e7e 100644 --- a/types/next-auth.d.ts +++ b/types/next-auth.d.ts @@ -5,38 +5,20 @@ declare module "next-auth" { interface Session { user: { id: number; - username: string; - email: string; isSubscriber: boolean; }; } interface User { id: number; - name: string; - username: string; - email: string; - emailVerified: Date; - image: string; - password: string; - archiveAsScreenshot: boolean; - archiveAsPDF: boolean; - archiveAsWaybackMachine: boolean; - isPrivate: boolean; - createdAt: Date; - updatedAt: Date; } } declare module "next-auth/jwt" { interface JWT { - name: string; - email: string; - picture: string; sub: string; - isSubscriber: boolean; id: number; - username: string; + isSubscriber: boolean; iat: number; exp: number; jti: string;