@@ -425,7 +425,7 @@ export default function TeamManagement({
>
diff --git a/components/Modal/Link/LinkDetails.tsx b/components/Modal/Link/LinkDetails.tsx
index 4b870d8..e593e09 100644
--- a/components/Modal/Link/LinkDetails.tsx
+++ b/components/Modal/Link/LinkDetails.tsx
@@ -106,7 +106,7 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
}, [colorPalette, theme]);
const handleDownload = (format: "png" | "pdf") => {
- const path = `/api/archives/${link.collection.id}/${link.id}.${format}`;
+ const path = `/api/v1/archives/${link.collection.id}/${link.id}.${format}`;
fetch(path)
.then((response) => {
if (response.ok) {
@@ -250,7 +250,7 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
@@ -283,7 +283,7 @@ export default function LinkDetails({ link, isOwnerOrMod }: Props) {
diff --git a/hooks/useLinks.tsx b/hooks/useLinks.tsx
index 25d65b2..7f8d84e 100644
--- a/hooks/useLinks.tsx
+++ b/hooks/useLinks.tsx
@@ -33,7 +33,7 @@ export default function useLinks(
const encodedData = encodeURIComponent(JSON.stringify(requestBody));
const response = await fetch(
- `/api/links?body=${encodeURIComponent(encodedData)}`
+ `/api/v1/links?body=${encodeURIComponent(encodedData)}`
);
const data = await response.json();
diff --git a/lib/api/controllers/collections/deleteCollection.ts b/lib/api/controllers/collections/collectionId/deleteCollectionById.ts
similarity index 91%
rename from lib/api/controllers/collections/deleteCollection.ts
rename to lib/api/controllers/collections/collectionId/deleteCollectionById.ts
index c42ba52..792f9fa 100644
--- a/lib/api/controllers/collections/deleteCollection.ts
+++ b/lib/api/controllers/collections/collectionId/deleteCollectionById.ts
@@ -4,15 +4,16 @@ import { Collection, UsersAndCollections } from "@prisma/client";
import removeFolder from "@/lib/api/storage/removeFolder";
export default async function deleteCollection(
- collection: { id: number },
- userId: number
+ userId: number,
+ collectionId: number
) {
- const collectionId = collection.id;
-
if (!collectionId)
return { response: "Please choose a valid collection.", status: 401 };
- const collectionIsAccessible = (await getPermission(userId, collectionId)) as
+ const collectionIsAccessible = (await getPermission({
+ userId,
+ collectionId,
+ })) as
| (Collection & {
members: UsersAndCollections[];
})
diff --git a/lib/api/controllers/collections/updateCollection.ts b/lib/api/controllers/collections/collectionId/updateCollectionById.ts
similarity index 77%
rename from lib/api/controllers/collections/updateCollection.ts
rename to lib/api/controllers/collections/collectionId/updateCollectionById.ts
index 7cafedd..076bb08 100644
--- a/lib/api/controllers/collections/updateCollection.ts
+++ b/lib/api/controllers/collections/collectionId/updateCollectionById.ts
@@ -4,16 +4,17 @@ import getPermission from "@/lib/api/getPermission";
import { Collection, UsersAndCollections } from "@prisma/client";
export default async function updateCollection(
- collection: CollectionIncludingMembersAndLinkCount,
- userId: number
+ userId: number,
+ collectionId: number,
+ data: CollectionIncludingMembersAndLinkCount
) {
- if (!collection.id)
+ if (!collectionId)
return { response: "Please choose a valid collection.", status: 401 };
- const collectionIsAccessible = (await getPermission(
+ const collectionIsAccessible = (await getPermission({
userId,
- collection.id
- )) as
+ collectionId,
+ })) as
| (Collection & {
members: UsersAndCollections[];
})
@@ -26,23 +27,23 @@ export default async function updateCollection(
await prisma.usersAndCollections.deleteMany({
where: {
collection: {
- id: collection.id,
+ id: collectionId,
},
},
});
return await prisma.collection.update({
where: {
- id: collection.id,
+ id: collectionId,
},
data: {
- name: collection.name.trim(),
- description: collection.description,
- color: collection.color,
- isPublic: collection.isPublic,
+ name: data.name.trim(),
+ description: data.description,
+ color: data.color,
+ isPublic: data.isPublic,
members: {
- create: collection.members.map((e) => ({
+ create: data.members.map((e) => ({
user: { connect: { id: e.user.id || e.userId } },
canCreate: e.canCreate,
canUpdate: e.canUpdate,
diff --git a/lib/api/controllers/links/deleteLink.ts b/lib/api/controllers/links/linkId/deleteLinkById.ts
similarity index 56%
rename from lib/api/controllers/links/deleteLink.ts
rename to lib/api/controllers/links/linkId/deleteLinkById.ts
index cddfe28..b66b850 100644
--- a/lib/api/controllers/links/deleteLink.ts
+++ b/lib/api/controllers/links/linkId/deleteLinkById.ts
@@ -1,20 +1,12 @@
import { prisma } from "@/lib/api/db";
-import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import { Collection, Link, UsersAndCollections } from "@prisma/client";
import getPermission from "@/lib/api/getPermission";
import removeFile from "@/lib/api/storage/removeFile";
-export default async function deleteLink(
- link: LinkIncludingShortenedCollectionAndTags,
- userId: number
-) {
- if (!link || !link.collectionId)
- return { response: "Please choose a valid link.", status: 401 };
+export default async function deleteLink(userId: number, linkId: number) {
+ if (!linkId) return { response: "Please choose a valid link.", status: 401 };
- const collectionIsAccessible = (await getPermission(
- userId,
- link.collectionId
- )) as
+ const collectionIsAccessible = (await getPermission({ userId, linkId })) as
| (Collection & {
members: UsersAndCollections[];
})
@@ -29,12 +21,16 @@ export default async function deleteLink(
const deleteLink: Link = await prisma.link.delete({
where: {
- id: link.id,
+ id: linkId,
},
});
- removeFile({ filePath: `archives/${link.collectionId}/${link.id}.pdf` });
- removeFile({ filePath: `archives/${link.collectionId}/${link.id}.png` });
+ removeFile({
+ filePath: `archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
+ });
+ removeFile({
+ filePath: `archives/${collectionIsAccessible?.id}/${linkId}.png`,
+ });
return { response: deleteLink, status: 200 };
}
diff --git a/lib/api/controllers/links/updateLink.ts b/lib/api/controllers/links/linkId/updateLinkById.ts
similarity index 59%
rename from lib/api/controllers/links/updateLink.ts
rename to lib/api/controllers/links/linkId/updateLinkById.ts
index 13a9c53..9ed6464 100644
--- a/lib/api/controllers/links/updateLink.ts
+++ b/lib/api/controllers/links/linkId/updateLinkById.ts
@@ -5,40 +5,32 @@ import getPermission from "@/lib/api/getPermission";
import moveFile from "@/lib/api/storage/moveFile";
export default async function updateLink(
- link: LinkIncludingShortenedCollectionAndTags,
- userId: number
+ userId: number,
+ linkId: number,
+ data: LinkIncludingShortenedCollectionAndTags
) {
- console.log(link);
- if (!link || !link.collection.id)
+ if (!data || !data.collection.id)
return {
response: "Please choose a valid link and collection.",
status: 401,
};
- const targetLink = (await getPermission(
- userId,
- link.collection.id,
- link.id
- )) as
- | (Link & {
- collection: Collection & {
- members: UsersAndCollections[];
- };
+ const collectionIsAccessible = (await getPermission({ userId, linkId })) as
+ | (Collection & {
+ members: UsersAndCollections[];
})
| null;
- const memberHasAccess = targetLink?.collection.members.some(
+ const memberHasAccess = collectionIsAccessible?.members.some(
(e: UsersAndCollections) => e.userId === userId && e.canUpdate
);
const isCollectionOwner =
- targetLink?.collection.ownerId === link.collection.ownerId &&
- link.collection.ownerId === userId;
+ collectionIsAccessible?.ownerId === data.collection.ownerId &&
+ data.collection.ownerId === userId;
const unauthorizedSwitchCollection =
- !isCollectionOwner && targetLink?.collection.id !== link.collection.id;
-
- console.log(isCollectionOwner);
+ !isCollectionOwner && collectionIsAccessible?.id !== data.collection.id;
// Makes sure collection members (non-owners) cannot move a link to/from a collection.
if (unauthorizedSwitchCollection)
@@ -46,7 +38,7 @@ export default async function updateLink(
response: "You can't move a link to/from a collection you don't own.",
status: 401,
};
- else if (targetLink?.collection.ownerId !== userId && !memberHasAccess)
+ else if (collectionIsAccessible?.ownerId !== userId && !memberHasAccess)
return {
response: "Collection is not accessible.",
status: 401,
@@ -54,37 +46,37 @@ export default async function updateLink(
else {
const updatedLink = await prisma.link.update({
where: {
- id: link.id,
+ id: linkId,
},
data: {
- name: link.name,
- description: link.description,
+ name: data.name,
+ description: data.description,
collection: {
connect: {
- id: link.collection.id,
+ id: data.collection.id,
},
},
tags: {
set: [],
- connectOrCreate: link.tags.map((tag) => ({
+ connectOrCreate: data.tags.map((tag) => ({
where: {
name_ownerId: {
name: tag.name,
- ownerId: link.collection.ownerId,
+ ownerId: data.collection.ownerId,
},
},
create: {
name: tag.name,
owner: {
connect: {
- id: link.collection.ownerId,
+ id: data.collection.ownerId,
},
},
},
})),
},
pinnedBy:
- link?.pinnedBy && link.pinnedBy[0]
+ data?.pinnedBy && data.pinnedBy[0]
? { connect: { id: userId } }
: { disconnect: { id: userId } },
},
@@ -100,15 +92,15 @@ export default async function updateLink(
},
});
- if (targetLink?.collection.id !== link.collection.id) {
+ if (collectionIsAccessible?.id !== data.collection.id) {
await moveFile(
- `archives/${targetLink?.collection.id}/${link.id}.pdf`,
- `archives/${link.collection.id}/${link.id}.pdf`
+ `archives/${collectionIsAccessible?.id}/${linkId}.pdf`,
+ `archives/${data.collection.id}/${linkId}.pdf`
);
await moveFile(
- `archives/${targetLink?.collection.id}/${link.id}.png`,
- `archives/${link.collection.id}/${link.id}.png`
+ `archives/${collectionIsAccessible?.id}/${linkId}.png`,
+ `archives/${data.collection.id}/${linkId}.png`
);
}
diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts
index 33b4ba2..5830040 100644
--- a/lib/api/controllers/links/postLink.ts
+++ b/lib/api/controllers/links/postLink.ts
@@ -27,10 +27,10 @@ export default async function postLink(
link.collection.name = link.collection.name.trim();
if (link.collection.id) {
- const collectionIsAccessible = (await getPermission(
+ const collectionIsAccessible = (await getPermission({
userId,
- link.collection.id
- )) as
+ collectionId: link.collection.id,
+ })) as
| (Collection & {
members: UsersAndCollections[];
})
diff --git a/lib/api/controllers/tags/tagId/updeteTagById.ts b/lib/api/controllers/tags/tagId/updeteTagById.ts
new file mode 100644
index 0000000..c477475
--- /dev/null
+++ b/lib/api/controllers/tags/tagId/updeteTagById.ts
@@ -0,0 +1,47 @@
+import { prisma } from "@/lib/api/db";
+import { Tag } from "@prisma/client";
+
+export default async function updateTag(
+ userId: number,
+ tagId: number,
+ data: Tag
+) {
+ if (!tagId || !data.name)
+ return { response: "Please choose a valid name for the tag.", status: 401 };
+
+ const tagNameIsTaken = await prisma.tag.findFirst({
+ where: {
+ ownerId: userId,
+ name: data.name,
+ },
+ });
+
+ if (tagNameIsTaken)
+ return {
+ response: "Tag names should be unique.",
+ status: 400,
+ };
+
+ const targetTag = await prisma.tag.findUnique({
+ where: {
+ id: tagId,
+ },
+ });
+
+ if (targetTag?.ownerId !== userId)
+ return {
+ response: "Permission denied.",
+ status: 401,
+ };
+
+ const updatedTag = await prisma.tag.update({
+ where: {
+ id: tagId,
+ },
+ data: {
+ name: data.name,
+ },
+ });
+
+ return { response: updatedTag, status: 200 };
+}
diff --git a/lib/api/controllers/users/getUsers.ts b/lib/api/controllers/users/getUsers.ts
deleted file mode 100644
index 9d752a6..0000000
--- a/lib/api/controllers/users/getUsers.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { prisma } from "@/lib/api/db";
-
-export default async function getUser({
- params,
- isSelf,
- username,
-}: {
- params: {
- lookupUsername?: string;
- lookupId?: number;
- };
- isSelf: boolean;
- username: string;
-}) {
- const user = await prisma.user.findUnique({
- where: {
- id: params.lookupId,
- username: params.lookupUsername?.toLowerCase(),
- },
- include: {
- whitelistedUsers: {
- select: {
- username: true
- }
- }
- }
- });
-
- if (!user) return { response: "User not found.", status: 404 };
-
- const whitelistedUsernames = user.whitelistedUsers?.map(usernames => usernames.username);
-
- if (
- !isSelf &&
- user?.isPrivate &&
- !whitelistedUsernames.includes(username.toLowerCase())
- ) {
- return { response: "This profile is private.", status: 401 };
- }
-
- const { password, ...lessSensitiveInfo } = user;
-
- const data = isSelf
- ? // If user is requesting its own data
- {...lessSensitiveInfo, whitelistedUsers: whitelistedUsernames}
- : {
- // If user is requesting someone elses data
- id: lessSensitiveInfo.id,
- name: lessSensitiveInfo.name,
- username: lessSensitiveInfo.username,
- };
-
- return { response: data || null, status: 200 };
-}
diff --git a/pages/api/auth/register.ts b/lib/api/controllers/users/postUser.ts
similarity index 78%
rename from pages/api/auth/register.ts
rename to lib/api/controllers/users/postUser.ts
index 7247765..5025d4e 100644
--- a/pages/api/auth/register.ts
+++ b/lib/api/controllers/users/postUser.ts
@@ -16,7 +16,7 @@ interface User {
password: string;
}
-export default async function Index(
+export default async function postUser(
req: NextApiRequest,
res: NextApiResponse
) {
@@ -35,8 +35,16 @@ export default async function Index(
.status(400)
.json({ response: "Please fill out all the fields." });
- const checkUsername = RegExp("^[a-z0-9_-]{3,31}$");
+ // Check email (if enabled)
+ const checkEmail =
+ /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+ if (emailEnabled && !checkEmail.test(body.email?.toLowerCase() || ""))
+ return res.status(400).json({
+ response: "Please enter a valid email.",
+ });
+ // Check username (if email was disabled)
+ const checkUsername = RegExp("^[a-z0-9_-]{3,31}$");
if (!emailEnabled && !checkUsername.test(body.username?.toLowerCase() || ""))
return res.status(400).json({
response:
@@ -47,7 +55,6 @@ export default async function Index(
where: emailEnabled
? {
email: body.email?.toLowerCase(),
- emailVerified: { not: null },
}
: {
username: (body.username as string).toLowerCase(),
@@ -72,8 +79,8 @@ export default async function Index(
return res.status(201).json({ response: "User successfully created." });
} else if (checkIfUserExists) {
- return res
- .status(400)
- .json({ response: "Username and/or Email already exists." });
+ return res.status(400).json({
+ response: `${emailEnabled ? "Email" : "Username"} already exists.`,
+ });
}
}
diff --git a/lib/api/controllers/users/userId/getPublicUserById.ts b/lib/api/controllers/users/userId/getPublicUserById.ts
new file mode 100644
index 0000000..0d00af0
--- /dev/null
+++ b/lib/api/controllers/users/userId/getPublicUserById.ts
@@ -0,0 +1,48 @@
+import { prisma } from "@/lib/api/db";
+
+export default async function getUser(
+ targetId: number | string,
+ isId: boolean,
+ requestingUsername?: string
+) {
+ const user = await prisma.user.findUnique({
+ where: isId
+ ? {
+ id: Number(targetId) as number,
+ }
+ : {
+ username: targetId as string,
+ },
+ include: {
+ whitelistedUsers: {
+ select: {
+ username: true,
+ },
+ },
+ },
+ });
+
+ if (!user)
+ return { response: "User not found or profile is private.", status: 404 };
+
+ const whitelistedUsernames = user.whitelistedUsers?.map(
+ (usernames) => usernames.username
+ );
+
+ if (
+ user?.isPrivate &&
+ (!requestingUsername ||
+ !whitelistedUsernames.includes(requestingUsername?.toLowerCase()))
+ ) {
+ return { response: "User not found or profile is private.", status: 404 };
+ }
+
+ const { password, ...lessSensitiveInfo } = user;
+
+ const data = {
+ name: lessSensitiveInfo.name,
+ username: lessSensitiveInfo.username,
+ };
+
+ return { response: data, status: 200 };
+}
diff --git a/lib/api/controllers/users/userId/getUserById.ts b/lib/api/controllers/users/userId/getUserById.ts
new file mode 100644
index 0000000..2b29540
--- /dev/null
+++ b/lib/api/controllers/users/userId/getUserById.ts
@@ -0,0 +1,32 @@
+import { prisma } from "@/lib/api/db";
+
+export default async function getUser(userId: number) {
+ const user = await prisma.user.findUnique({
+ where: {
+ id: userId,
+ },
+ include: {
+ whitelistedUsers: {
+ select: {
+ username: true,
+ },
+ },
+ },
+ });
+
+ if (!user)
+ return { response: "User not found or profile is private.", status: 404 };
+
+ const whitelistedUsernames = user.whitelistedUsers?.map(
+ (usernames) => usernames.username
+ );
+
+ const { password, ...lessSensitiveInfo } = user;
+
+ const data = {
+ ...lessSensitiveInfo,
+ whitelistedUsers: whitelistedUsernames,
+ };
+
+ return { response: data, status: 200 };
+}
diff --git a/lib/api/controllers/users/updateUser.ts b/lib/api/controllers/users/userId/updateUserById.ts
similarity index 80%
rename from lib/api/controllers/users/updateUser.ts
rename to lib/api/controllers/users/userId/updateUserById.ts
index 6bb4c22..b2229e3 100644
--- a/lib/api/controllers/users/updateUser.ts
+++ b/lib/api/controllers/users/userId/updateUserById.ts
@@ -10,20 +10,20 @@ const emailEnabled =
process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false;
export default async function updateUser(
- user: AccountSettings,
sessionUser: {
id: number;
username: string;
email: string;
isSubscriber: boolean;
- }
+ },
+ data: AccountSettings
) {
- if (emailEnabled && !user.email)
+ if (emailEnabled && !data.email)
return {
response: "Email invalid.",
status: 400,
};
- else if (!user.username)
+ else if (!data.username)
return {
response: "Username invalid.",
status: 400,
@@ -31,7 +31,7 @@ export default async function updateUser(
const checkUsername = RegExp("^[a-z0-9_-]{3,31}$");
- if (!checkUsername.test(user.username.toLowerCase()))
+ if (!checkUsername.test(data.username.toLowerCase()))
return {
response:
"Username has to be between 3-30 characters, no spaces and special characters are allowed.",
@@ -44,15 +44,15 @@ export default async function updateUser(
OR: emailEnabled
? [
{
- username: user.username.toLowerCase(),
+ username: data.username.toLowerCase(),
},
{
- email: user.email?.toLowerCase(),
+ email: data.email?.toLowerCase(),
},
]
: [
{
- username: user.username.toLowerCase(),
+ username: data.username.toLowerCase(),
},
],
},
@@ -66,10 +66,10 @@ export default async function updateUser(
// Avatar Settings
- const profilePic = user.profilePic;
+ const profilePic = data.profilePic;
if (profilePic.startsWith("data:image/jpeg;base64")) {
- if (user.profilePic.length < 1572864) {
+ if (data.profilePic.length < 1572864) {
try {
const base64Data = profilePic.replace(/^data:image\/jpeg;base64,/, "");
@@ -97,22 +97,22 @@ export default async function updateUser(
// Other settings
const saltRounds = 10;
- const newHashedPassword = bcrypt.hashSync(user.newPassword || "", saltRounds);
+ const newHashedPassword = bcrypt.hashSync(data.newPassword || "", saltRounds);
const updatedUser = await prisma.user.update({
where: {
id: sessionUser.id,
},
data: {
- name: user.name,
- username: user.username.toLowerCase(),
- email: user.email?.toLowerCase(),
- isPrivate: user.isPrivate,
- archiveAsScreenshot: user.archiveAsScreenshot,
- archiveAsPDF: user.archiveAsPDF,
- archiveAsWaybackMachine: user.archiveAsWaybackMachine,
+ name: data.name,
+ username: data.username.toLowerCase(),
+ email: data.email?.toLowerCase(),
+ isPrivate: data.isPrivate,
+ archiveAsScreenshot: data.archiveAsScreenshot,
+ archiveAsPDF: data.archiveAsPDF,
+ archiveAsWaybackMachine: data.archiveAsWaybackMachine,
password:
- user.newPassword && user.newPassword !== ""
+ data.newPassword && data.newPassword !== ""
? newHashedPassword
: undefined,
},
@@ -124,11 +124,11 @@ export default async function updateUser(
const { whitelistedUsers, password, ...userInfo } = updatedUser;
// If user.whitelistedUsers is not provided, we will assume the whitelistedUsers should be removed
- const newWhitelistedUsernames: string[] = user.whitelistedUsers || [];
+ const newWhitelistedUsernames: string[] = data.whitelistedUsers || [];
// Get the current whitelisted usernames
const currentWhitelistedUsernames: string[] = whitelistedUsers.map(
- (user) => user.username
+ (data) => data.username
);
// Find the usernames to be deleted (present in current but not in new)
@@ -164,17 +164,17 @@ export default async function updateUser(
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
- if (STRIPE_SECRET_KEY && emailEnabled && sessionUser.email !== user.email)
+ if (STRIPE_SECRET_KEY && emailEnabled && sessionUser.email !== data.email)
await updateCustomerEmail(
STRIPE_SECRET_KEY,
sessionUser.email,
- user.email as string
+ data.email as string
);
const response: Omit = {
...userInfo,
whitelistedUsers: newWhitelistedUsernames,
- profilePic: `/api/avatar/${userInfo.id}?${Date.now()}`,
+ profilePic: `/api/v1/avatar/${userInfo.id}?${Date.now()}`,
};
return { response, status: 200 };
diff --git a/lib/api/getPermission.ts b/lib/api/getPermission.ts
index f76b470..3d1b2bc 100644
--- a/lib/api/getPermission.ts
+++ b/lib/api/getPermission.ts
@@ -1,24 +1,30 @@
import { prisma } from "@/lib/api/db";
-export default async function getPermission(
- userId: number,
- collectionId: number,
- linkId?: number
-) {
+type Props = {
+ userId: number;
+ collectionId?: number;
+ linkId?: number;
+};
+
+export default async function getPermission({
+ userId,
+ collectionId,
+ linkId,
+}: Props) {
if (linkId) {
- const link = await prisma.link.findUnique({
+ const check = await prisma.collection.findFirst({
where: {
- id: linkId,
- },
- include: {
- collection: {
- include: { members: true },
+ links: {
+ some: {
+ id: linkId,
+ },
},
},
+ include: { members: true },
});
- return link;
- } else {
+ return check;
+ } else if (collectionId) {
const check = await prisma.collection.findFirst({
where: {
AND: {
diff --git a/lib/client/addMemberToCollection.ts b/lib/client/addMemberToCollection.ts
index 630c303..af0db51 100644
--- a/lib/client/addMemberToCollection.ts
+++ b/lib/client/addMemberToCollection.ts
@@ -22,9 +22,7 @@ const addMemberToCollection = async (
memberUsername.trim().toLowerCase() !== ownerUsername.toLowerCase()
) {
// Lookup, get data/err, list ...
- const user = await getPublicUserData({
- username: memberUsername.trim().toLowerCase(),
- });
+ const user = await getPublicUserData(memberUsername.trim().toLowerCase());
if (user.username) {
setMember({
diff --git a/lib/client/getPublicCollectionData.ts b/lib/client/getPublicCollectionData.ts
index d997eb7..b86c173 100644
--- a/lib/client/getPublicCollectionData.ts
+++ b/lib/client/getPublicCollectionData.ts
@@ -17,7 +17,7 @@ const getPublicCollectionData = async (
const encodedData = encodeURIComponent(JSON.stringify(requestBody));
const res = await fetch(
- "/api/public/collections?body=" + encodeURIComponent(encodedData)
+ "/api/v1/public/collections?body=" + encodeURIComponent(encodedData)
);
const data = await res.json();
diff --git a/lib/client/getPublicUserData.ts b/lib/client/getPublicUserData.ts
index 4c65da3..20bd1e9 100644
--- a/lib/client/getPublicUserData.ts
+++ b/lib/client/getPublicUserData.ts
@@ -1,17 +1,7 @@
import { toast } from "react-hot-toast";
-export default async function getPublicUserData({
- username,
- id,
-}: {
- username?: string;
- id?: number;
-}) {
- const response = await fetch(
- `/api/users?id=${id}&${
- username ? `username=${username?.toLowerCase()}` : undefined
- }`
- );
+export default async function getPublicUserData(id: number | string) {
+ const response = await fetch(`/api/v1/users/${id}`);
const data = await response.json();
diff --git a/pages/_app.tsx b/pages/_app.tsx
index f5966b4..69d8b6f 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -22,7 +22,7 @@ export default function App({
}, []);
return (
-
+
Linkwarden
diff --git a/pages/api/users/index.ts b/pages/api/users/index.ts
deleted file mode 100644
index cd310bf..0000000
--- a/pages/api/users/index.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { NextApiRequest, NextApiResponse } from "next";
-import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
-import getUsers from "@/lib/api/controllers/users/getUsers";
-import updateUser from "@/lib/api/controllers/users/updateUser";
-
-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)
- 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 lookupUsername = (req.query.username as string) || undefined;
- const lookupId = Number(req.query.id) || undefined;
- const isSelf =
- session.user.username === lookupUsername || session.user.id === lookupId
- ? true
- : false;
-
- if (req.method === "GET") {
- const users = await getUsers({
- params: {
- lookupUsername,
- lookupId,
- },
- isSelf,
- username: session.user.username,
- });
- return res.status(users.status).json({ response: users.response });
- } else if (req.method === "PUT") {
- const updated = await updateUser(req.body, session.user);
- return res.status(updated.status).json({ response: updated.response });
- }
-}
diff --git a/pages/api/archives/[...params].ts b/pages/api/v1/archives/[...params].ts
similarity index 86%
rename from pages/api/archives/[...params].ts
rename to pages/api/v1/archives/[...params].ts
index 3e659a6..814e5ee 100644
--- a/pages/api/archives/[...params].ts
+++ b/pages/api/v1/archives/[...params].ts
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "pages/api/auth/[...nextauth]";
+import { authOptions } from "@/pages/api/v1/auth/[...nextauth]";
import getPermission from "@/lib/api/getPermission";
import readFile from "@/lib/api/storage/readFile";
@@ -21,10 +21,10 @@ export default async function Index(req: NextApiRequest, res: NextApiResponse) {
"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(
- session.user.id,
- Number(collectionId)
- );
+ const collectionIsAccessible = await getPermission({
+ userId: session.user.id,
+ collectionId: Number(collectionId),
+ });
if (!collectionIsAccessible)
return res
diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/v1/auth/[...nextauth].ts
similarity index 96%
rename from pages/api/auth/[...nextauth].ts
rename to pages/api/v1/auth/[...nextauth].ts
index 42f9b85..1bde92d 100644
--- a/pages/api/auth/[...nextauth].ts
+++ b/pages/api/v1/auth/[...nextauth].ts
@@ -88,8 +88,7 @@ export const authOptions: AuthOptions = {
return session;
},
- // Using the `...rest` parameter to be able to narrow down the type based on `trigger`
- async jwt({ token, trigger, session, user }) {
+ async jwt({ token, trigger, user }) {
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
const NEXT_PUBLIC_TRIAL_PERIOD_DAYS =
diff --git a/pages/api/avatar/[id].ts b/pages/api/v1/avatar/[id].ts
similarity index 96%
rename from pages/api/avatar/[id].ts
rename to pages/api/v1/avatar/[id].ts
index a006dbf..c92b98b 100644
--- a/pages/api/avatar/[id].ts
+++ b/pages/api/v1/avatar/[id].ts
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "pages/api/auth/[...nextauth]";
+import { authOptions } from "@/pages/api/v1/auth/[...nextauth]";
import { prisma } from "@/lib/api/db";
import readFile from "@/lib/api/storage/readFile";
diff --git a/pages/api/v1/collections/[id].ts b/pages/api/v1/collections/[id].ts
new file mode 100644
index 0000000..3ac289c
--- /dev/null
+++ b/pages/api/v1/collections/[id].ts
@@ -0,0 +1,35 @@
+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";
+
+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)
+ 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.",
+ });
+
+ if (req.method === "PUT") {
+ const updated = await updateCollectionById(
+ session.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,
+ Number(req.query.id) as number
+ );
+ return res.status(deleted.status).json({ response: deleted.response });
+ }
+}
diff --git a/pages/api/collections/index.ts b/pages/api/v1/collections/index.ts
similarity index 65%
rename from pages/api/collections/index.ts
rename to pages/api/v1/collections/index.ts
index 2132add..73cb45e 100644
--- a/pages/api/collections/index.ts
+++ b/pages/api/v1/collections/index.ts
@@ -1,10 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+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 updateCollection from "@/lib/api/controllers/collections/updateCollection";
-import deleteCollection from "@/lib/api/controllers/collections/deleteCollection";
export default async function collections(
req: NextApiRequest,
@@ -30,11 +28,5 @@ export default async function collections(
return res
.status(newCollection.status)
.json({ response: newCollection.response });
- } else if (req.method === "PUT") {
- const updated = await updateCollection(req.body, session.user.id);
- return res.status(updated.status).json({ response: updated.response });
- } else if (req.method === "DELETE") {
- const deleted = await deleteCollection(req.body, session.user.id);
- return res.status(deleted.status).json({ response: deleted.response });
}
}
diff --git a/pages/api/v1/getToken.ts b/pages/api/v1/getToken.ts
new file mode 100644
index 0000000..c767075
--- /dev/null
+++ b/pages/api/v1/getToken.ts
@@ -0,0 +1,16 @@
+// For future...
+// import { getToken } from "next-auth/jwt";
+
+// export default async (req, res) => {
+// // If you don't have NEXTAUTH_SECRET set, you will have to pass your secret as `secret` to `getToken`
+// console.log({ req });
+// const token = await getToken({ req, raw: true });
+// if (token) {
+// // Signed in
+// console.log("JSON Web Token", JSON.stringify(token, null, 2));
+// } else {
+// // Not Signed in
+// res.status(401);
+// }
+// res.end();
+// };
diff --git a/pages/api/v1/links/[id].ts b/pages/api/v1/links/[id].ts
new file mode 100644
index 0000000..9847f2a
--- /dev/null
+++ b/pages/api/v1/links/[id].ts
@@ -0,0 +1,33 @@
+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";
+
+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)
+ 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.",
+ });
+
+ if (req.method === "PUT") {
+ const updated = await updateLinkById(
+ session.user.id,
+ Number(req.query.id),
+ req.body
+ );
+ return res.status(updated.status).json({
+ response: updated.response,
+ });
+ } else if (req.method === "DELETE") {
+ const deleted = await deleteLinkById(session.user.id, Number(req.query.id));
+ return res.status(deleted.status).json({
+ response: deleted.response,
+ });
+ }
+}
diff --git a/pages/api/links/index.ts b/pages/api/v1/links/index.ts
similarity index 65%
rename from pages/api/links/index.ts
rename to pages/api/v1/links/index.ts
index eb5be9d..6eab9af 100644
--- a/pages/api/links/index.ts
+++ b/pages/api/v1/links/index.ts
@@ -1,10 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+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 deleteLink from "@/lib/api/controllers/links/deleteLink";
-import updateLink from "@/lib/api/controllers/links/updateLink";
export default async function links(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession(req, res, authOptions);
@@ -25,15 +23,5 @@ export default async function links(req: NextApiRequest, res: NextApiResponse) {
return res.status(newlink.status).json({
response: newlink.response,
});
- } else if (req.method === "PUT") {
- const updated = await updateLink(req.body, session.user.id);
- return res.status(updated.status).json({
- response: updated.response,
- });
- } else if (req.method === "DELETE") {
- const deleted = await deleteLink(req.body, session.user.id);
- return res.status(deleted.status).json({
- response: deleted.response,
- });
}
}
diff --git a/pages/api/migration/index.ts b/pages/api/v1/migration/index.ts
similarity index 96%
rename from pages/api/migration/index.ts
rename to pages/api/v1/migration/index.ts
index aeeb499..a881e48 100644
--- a/pages/api/migration/index.ts
+++ b/pages/api/v1/migration/index.ts
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+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";
diff --git a/pages/api/payment/index.ts b/pages/api/v1/payment/index.ts
similarity index 95%
rename from pages/api/payment/index.ts
rename to pages/api/v1/payment/index.ts
index e62ac4a..23e0673 100644
--- a/pages/api/payment/index.ts
+++ b/pages/api/v1/payment/index.ts
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+import { authOptions } from "@/pages/api/v1/auth/[...nextauth]";
import paymentCheckout from "@/lib/api/paymentCheckout";
import { Plan } from "@/types/global";
diff --git a/pages/api/public/collections.ts b/pages/api/v1/public/collections.ts
similarity index 100%
rename from pages/api/public/collections.ts
rename to pages/api/v1/public/collections.ts
diff --git a/pages/api/v1/tags/[id].ts b/pages/api/v1/tags/[id].ts
new file mode 100644
index 0000000..22b355f
--- /dev/null
+++ b/pages/api/v1/tags/[id].ts
@@ -0,0 +1,23 @@
+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";
+
+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)
+ 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 tagId = Number(req.query.id);
+
+ if (req.method === "PUT") {
+ const tags = await updateTag(session.user.id, tagId, req.body);
+ return res.status(tags.status).json({ response: tags.response });
+ }
+}
diff --git a/pages/api/tags/index.ts b/pages/api/v1/tags/index.ts
similarity index 92%
rename from pages/api/tags/index.ts
rename to pages/api/v1/tags/index.ts
index 5841ace..efaf7b2 100644
--- a/pages/api/tags/index.ts
+++ b/pages/api/v1/tags/index.ts
@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+import { authOptions } from "@/pages/api/v1/auth/[...nextauth]";
import getTags from "@/lib/api/controllers/tags/getTags";
export default async function tags(req: NextApiRequest, res: NextApiResponse) {
diff --git a/pages/api/v1/users/[id].ts b/pages/api/v1/users/[id].ts
new file mode 100644
index 0000000..112ceff
--- /dev/null
+++ b/pages/api/v1/users/[id].ts
@@ -0,0 +1,40 @@
+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";
+
+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 lookupId = req.query.id as string;
+ const isSelf =
+ userId === Number(lookupId) || username === lookupId ? true : false;
+
+ // Check if "lookupId" is the user "id" or their "username"
+ const isId = lookupId.split("").every((e) => Number.isInteger(parseInt(e)));
+
+ if (req.method === "GET" && !isSelf) {
+ const users = await getPublicUserById(lookupId, isId, username);
+ 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)
+ 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.",
+ });
+
+ if (req.method === "GET") {
+ const users = await getUserById(session.user.id);
+ return res.status(users.status).json({ response: users.response });
+ } else if (req.method === "PUT") {
+ const updated = await updateUserById(session.user, req.body);
+ return res.status(updated.status).json({ response: updated.response });
+ }
+}
diff --git a/pages/api/v1/users/index.ts b/pages/api/v1/users/index.ts
new file mode 100644
index 0000000..3af7bf8
--- /dev/null
+++ b/pages/api/v1/users/index.ts
@@ -0,0 +1,9 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import postUser from "@/lib/api/controllers/users/postUser";
+
+export default async function users(req: NextApiRequest, res: NextApiResponse) {
+ if (req.method === "POST") {
+ const response = await postUser(req, res);
+ return response;
+ }
+}
diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx
index d3f6f56..632ab4e 100644
--- a/pages/collections/[id].tsx
+++ b/pages/collections/[id].tsx
@@ -104,7 +104,7 @@ export default function Index() {
return (
);
diff --git a/pages/register.tsx b/pages/register.tsx
index 75ba525..c279e97 100644
--- a/pages/register.tsx
+++ b/pages/register.tsx
@@ -59,7 +59,7 @@ export default function Register() {
const load = toast.loading("Creating Account...");
- const response = await fetch("/api/auth/register", {
+ const response = await fetch("/api/v1/users", {
body: JSON.stringify(request),
headers: {
"Content-Type": "application/json",
diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx
index 9cd52b6..1ee8c2a 100644
--- a/pages/settings/account.tsx
+++ b/pages/settings/account.tsx
@@ -128,7 +128,7 @@ export default function Account() {
data: request,
};
- const response = await fetch("/api/migration", {
+ const response = await fetch("/api/v1/migration", {
method: "POST",
body: JSON.stringify(body),
});
@@ -333,7 +333,7 @@ export default function Account() {
Download your data instantly.
-
+
Export Data
diff --git a/pages/subscribe.tsx b/pages/subscribe.tsx
index b8b478a..56ef8f6 100644
--- a/pages/subscribe.tsx
+++ b/pages/subscribe.tsx
@@ -20,7 +20,7 @@ export default function Subscribe() {
const redirectionToast = toast.loading("Redirecting to Stripe...");
- const res = await fetch("/api/payment?plan=" + plan);
+ const res = await fetch("/api/v1/payment?plan=" + plan);
const data = await res.json();
router.push(data.response);
diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx
index 4acd069..1804a75 100644
--- a/pages/tags/[id].tsx
+++ b/pages/tags/[id].tsx
@@ -1,25 +1,38 @@
import LinkCard from "@/components/LinkCard";
import useLinkStore from "@/store/links";
-import { faHashtag, faSort } from "@fortawesome/free-solid-svg-icons";
+import {
+ faCheck,
+ faEllipsis,
+ faHashtag,
+ faSort,
+ faXmark,
+} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useRouter } from "next/router";
-import { useEffect, useState } from "react";
+import { FormEvent, useEffect, useState } from "react";
import MainLayout from "@/layouts/MainLayout";
import { Tag } from "@prisma/client";
import useTagStore from "@/store/tags";
import SortDropdown from "@/components/SortDropdown";
import { Sort } from "@/types/global";
import useLinks from "@/hooks/useLinks";
+import Dropdown from "@/components/Dropdown";
+import { toast } from "react-hot-toast";
export default function Index() {
const router = useRouter();
const { links } = useLinkStore();
- const { tags } = useTagStore();
+ const { tags, updateTag } = useTagStore();
const [sortDropdown, setSortDropdown] = useState(false);
const [sortBy, setSortBy] = useState(Sort.DateNewestFirst);
+ const [expandDropdown, setExpandDropdown] = useState(false);
+
+ const [renameTag, setRenameTag] = useState(false);
+ const [newTagName, setNewTagName] = useState();
+
const [activeTag, setActiveTag] = useState();
useLinks({ tagId: Number(router.query.id), sort: sortBy });
@@ -28,19 +41,130 @@ export default function Index() {
setActiveTag(tags.find((e) => e.id === Number(router.query.id)));
}, [router, tags]);
+ useEffect(() => {
+ setNewTagName(activeTag?.name);
+ }, [activeTag]);
+
+ const [submitLoader, setSubmitLoader] = useState(false);
+
+ const cancelUpdateTag = async () => {
+ setNewTagName(activeTag?.name);
+ setRenameTag(false);
+ };
+
+ const submit = async (e?: FormEvent) => {
+ e?.preventDefault();
+
+ if (activeTag?.name === newTagName) return setRenameTag(false);
+ else if (newTagName === "") {
+ return cancelUpdateTag();
+ }
+
+ setSubmitLoader(true);
+
+ const load = toast.loading("Applying...");
+
+ let response;
+
+ if (activeTag && newTagName)
+ response = await updateTag({
+ ...activeTag,
+ name: newTagName,
+ });
+
+ toast.dismiss(load);
+
+ if (response?.ok) {
+ toast.success("Tag Renamed!");
+ } else toast.error(response?.data as string);
+ setSubmitLoader(false);
+ setRenameTag(false);
+ };
+
return (
-
+
-
- {activeTag?.name}
-
+ {renameTag ? (
+ <>
+
+ >
+ ) : (
+ <>
+
+ {activeTag?.name}
+
+
+
setExpandDropdown(!expandDropdown)}
+ id="expand-dropdown"
+ className="inline-flex rounded-md cursor-pointer hover:bg-slate-200 hover:dark:bg-neutral-700 duration-100 p-1"
+ >
+
+
+
+ {expandDropdown ? (
+
{
+ setRenameTag(true);
+ setExpandDropdown(false);
+ },
+ },
+ ]}
+ onClickOutside={(e: Event) => {
+ const target = e.target as HTMLInputElement;
+ if (target.id !== "expand-dropdown")
+ setExpandDropdown(false);
+ }}
+ className="absolute top-8 left-0 w-36"
+ />
+ ) : null}
+
+ >
+ )}
diff --git a/store/account.ts b/store/account.ts
index 4b7e9b6..c57d71e 100644
--- a/store/account.ts
+++ b/store/account.ts
@@ -15,16 +15,16 @@ type AccountStore = {
const useAccountStore = create
()((set) => ({
account: {} as AccountSettings,
setAccount: async (id) => {
- const response = await fetch(`/api/users?id=${id}`);
+ const response = await fetch(`/api/v1/users/${id}`);
const data = await response.json();
- const profilePic = `/api/avatar/${data.response.id}?${Date.now()}`;
+ const profilePic = `/api/v1/avatar/${data.response.id}?${Date.now()}`;
if (response.ok) set({ account: { ...data.response, profilePic } });
},
updateAccount: async (user) => {
- const response = await fetch("/api/users", {
+ const response = await fetch(`/api/v1/users/${user.id}`, {
method: "PUT",
body: JSON.stringify(user),
headers: {
diff --git a/store/collections.ts b/store/collections.ts
index 0fade5f..5c78b52 100644
--- a/store/collections.ts
+++ b/store/collections.ts
@@ -22,14 +22,14 @@ type CollectionStore = {
const useCollectionStore = create()((set) => ({
collections: [],
setCollections: async () => {
- const response = await fetch("/api/collections");
+ const response = await fetch("/api/v1/collections");
const data = await response.json();
if (response.ok) set({ collections: data.response });
},
addCollection: async (body) => {
- const response = await fetch("/api/collections", {
+ const response = await fetch("/api/v1/collections", {
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
@@ -47,7 +47,7 @@ const useCollectionStore = create()((set) => ({
return { ok: response.ok, data: data.response };
},
updateCollection: async (collection) => {
- const response = await fetch("/api/collections", {
+ const response = await fetch(`/api/v1/collections/${collection.id}`, {
body: JSON.stringify(collection),
headers: {
"Content-Type": "application/json",
@@ -66,9 +66,8 @@ const useCollectionStore = create()((set) => ({
return { ok: response.ok, data: data.response };
},
- removeCollection: async (id) => {
- const response = await fetch("/api/collections", {
- body: JSON.stringify({ id }),
+ removeCollection: async (collectionId) => {
+ const response = await fetch(`/api/v1/collections/${collectionId}`, {
headers: {
"Content-Type": "application/json",
},
@@ -79,7 +78,7 @@ const useCollectionStore = create()((set) => ({
if (response.ok) {
set((state) => ({
- collections: state.collections.filter((e) => e.id !== id),
+ collections: state.collections.filter((e) => e.id !== collectionId),
}));
useTagStore.getState().setTags();
}
diff --git a/store/links.ts b/store/links.ts
index ff25603..d6a13e4 100644
--- a/store/links.ts
+++ b/store/links.ts
@@ -20,9 +20,7 @@ type LinkStore = {
updateLink: (
link: LinkIncludingShortenedCollectionAndTags
) => Promise;
- removeLink: (
- link: LinkIncludingShortenedCollectionAndTags
- ) => Promise;
+ removeLink: (linkId: number) => Promise;
resetLinks: () => void;
};
@@ -38,7 +36,7 @@ const useLinkStore = create()((set) => ({
}));
},
addLink: async (body) => {
- const response = await fetch("/api/links", {
+ const response = await fetch("/api/v1/links", {
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
@@ -59,7 +57,7 @@ const useLinkStore = create()((set) => ({
return { ok: response.ok, data: data.response };
},
updateLink: async (link) => {
- const response = await fetch("/api/links", {
+ const response = await fetch(`/api/v1/links/${link.id}`, {
body: JSON.stringify(link),
headers: {
"Content-Type": "application/json",
@@ -81,9 +79,8 @@ const useLinkStore = create()((set) => ({
return { ok: response.ok, data: data.response };
},
- removeLink: async (link) => {
- const response = await fetch("/api/links", {
- body: JSON.stringify(link),
+ removeLink: async (linkId) => {
+ const response = await fetch(`/api/v1/links/${linkId}`, {
headers: {
"Content-Type": "application/json",
},
@@ -94,7 +91,7 @@ const useLinkStore = create()((set) => ({
if (response.ok) {
set((state) => ({
- links: state.links.filter((e) => e.id !== link.id),
+ links: state.links.filter((e) => e.id !== linkId),
}));
useTagStore.getState().setTags();
}
diff --git a/store/tags.ts b/store/tags.ts
index f833aa0..26552bf 100644
--- a/store/tags.ts
+++ b/store/tags.ts
@@ -1,20 +1,47 @@
import { create } from "zustand";
import { Tag } from "@prisma/client";
+type ResponseObject = {
+ ok: boolean;
+ data: object | string;
+};
+
type TagStore = {
tags: Tag[];
setTags: () => void;
+ updateTag: (tag: Tag) => Promise;
};
const useTagStore = create()((set) => ({
tags: [],
setTags: async () => {
- const response = await fetch("/api/tags");
+ const response = await fetch("/api/v1/tags");
const data = await response.json();
if (response.ok) set({ tags: data.response });
},
+ updateTag: async (tag) => {
+ const response = await fetch(`/api/v1/tags/${tag.id}`, {
+ body: JSON.stringify(tag),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ method: "PUT",
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ set((state) => ({
+ tags: state.tags.map((e) =>
+ e.id === data.response.id ? data.response : e
+ ),
+ }));
+ }
+
+ return { ok: response.ok, data: data.response };
+ },
}));
export default useTagStore;