add schema validation for PUT requests

This commit is contained in:
daniel31x13
2024-09-17 14:03:05 -04:00
parent 1cf7421b76
commit ff6e71d494
10 changed files with 202 additions and 65 deletions
@@ -1,15 +1,32 @@
import { prisma } from "@/lib/api/db";
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import getPermission from "@/lib/api/getPermission";
import {
UpdateCollectionSchema,
UpdateCollectionSchemaType,
} from "@/lib/shared/schemaValidation";
export default async function updateCollection(
userId: number,
collectionId: number,
data: CollectionIncludingMembersAndLinkCount
body: UpdateCollectionSchemaType
) {
if (!collectionId)
return { response: "Please choose a valid collection.", status: 401 };
const dataValidation = UpdateCollectionSchema.safeParse(body);
if (!dataValidation.success) {
return {
response: `Error: ${
dataValidation.error.issues[0].message
} [${dataValidation.error.issues[0].path.join(", ")}]`,
status: 400,
};
}
const data = dataValidation.data;
const collectionIsAccessible = await getPermission({
userId,
collectionId,
@@ -76,7 +93,7 @@ export default async function updateCollection(
: undefined,
members: {
create: data.members.map((e) => ({
user: { connect: { id: e.user.id || e.userId } },
user: { connect: { id: e.userId } },
canCreate: e.canCreate,
canUpdate: e.canUpdate,
canDelete: e.canDelete,
@@ -1,9 +1,10 @@
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import updateLinkById from "../linkId/updateLinkById";
import { UpdateLinkSchemaType } from "@/lib/shared/schemaValidation";
export default async function updateLinks(
userId: number,
links: LinkIncludingShortenedCollectionAndTags[],
links: UpdateLinkSchemaType[],
removePreviousTags: boolean,
newData: Pick<
LinkIncludingShortenedCollectionAndTags,
@@ -22,7 +23,7 @@ export default async function updateLinks(
updatedTags = [...(newData.tags ?? [])];
}
const updatedData: LinkIncludingShortenedCollectionAndTags = {
const updatedData: UpdateLinkSchemaType = {
...link,
tags: updatedTags,
collection: {
@@ -1,20 +1,30 @@
import { prisma } from "@/lib/api/db";
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import { UsersAndCollections } from "@prisma/client";
import getPermission from "@/lib/api/getPermission";
import { moveFiles, removeFiles } from "@/lib/api/manageLinkFiles";
import isValidUrl from "@/lib/shared/isValidUrl";
import {
UpdateLinkSchema,
UpdateLinkSchemaType,
} from "@/lib/shared/schemaValidation";
export default async function updateLinkById(
userId: number,
linkId: number,
data: LinkIncludingShortenedCollectionAndTags
body: UpdateLinkSchemaType
) {
if (!data || !data.collection.id)
const dataValidation = UpdateLinkSchema.safeParse(body);
if (!dataValidation.success) {
return {
response: "Please choose a valid link and collection.",
status: 401,
response: `Error: ${
dataValidation.error.issues[0].message
} [${dataValidation.error.issues[0].path.join(", ")}]`,
status: 400,
};
}
const data = dataValidation.data;
const collectionIsAccessible = await getPermission({ userId, linkId });
@@ -33,10 +43,11 @@ export default async function updateLinkById(
id: linkId,
},
data: {
pinnedBy:
data?.pinnedBy && data.pinnedBy[0].id === userId
pinnedBy: data?.pinnedBy
? data.pinnedBy[0]?.id === userId
? { connect: { id: userId } }
: { disconnect: { id: userId } },
: { disconnect: { id: userId } }
: undefined,
},
include: {
collection: true,
@@ -63,11 +74,9 @@ export default async function updateLinkById(
const targetCollectionMatchesData = data.collection.id
? data.collection.id === targetCollectionIsAccessible?.id
: true && data.collection.name
? data.collection.name === targetCollectionIsAccessible?.name
: true && data.collection.ownerId
? data.collection.ownerId === targetCollectionIsAccessible?.ownerId
: true;
: true && data.collection.ownerId
? data.collection.ownerId === targetCollectionIsAccessible?.ownerId
: true;
if (!targetCollectionMatchesData)
return {
@@ -149,10 +158,11 @@ export default async function updateLinkById(
},
})),
},
pinnedBy:
data?.pinnedBy && data.pinnedBy[0]?.id === userId
pinnedBy: data?.pinnedBy
? data.pinnedBy[0]?.id === userId
? { connect: { id: userId } }
: { disconnect: { id: userId } },
: { disconnect: { id: userId } }
: undefined,
},
include: {
tags: true,
@@ -1,18 +1,31 @@
import { prisma } from "@/lib/api/db";
import { Tag } from "@prisma/client";
import {
UpdateTagSchema,
UpdateTagSchemaType,
} from "@/lib/shared/schemaValidation";
export default async function updeteTagById(
userId: number,
tagId: number,
data: Tag
body: UpdateTagSchemaType
) {
if (!tagId || !data.name)
return { response: "Please choose a valid name for the tag.", status: 401 };
const dataValidation = UpdateTagSchema.safeParse(body);
if (!dataValidation.success) {
return {
response: `Error: ${
dataValidation.error.issues[0].message
} [${dataValidation.error.issues[0].path.join(", ")}]`,
status: 400,
};
}
const { name } = dataValidation.data;
const tagNameIsTaken = await prisma.tag.findFirst({
where: {
ownerId: userId,
name: data.name,
name: name,
},
});
@@ -39,7 +52,7 @@ export default async function updeteTagById(
id: tagId,
},
data: {
name: data.name,
name: name,
},
});
@@ -6,42 +6,27 @@ import createFile from "@/lib/api/storage/createFile";
import createFolder from "@/lib/api/storage/createFolder";
import sendChangeEmailVerificationRequest from "@/lib/api/sendChangeEmailVerificationRequest";
import { i18n } from "next-i18next.config";
import { UpdateUserSchema } from "@/lib/shared/schemaValidation";
const emailEnabled =
process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false;
export default async function updateUserById(
userId: number,
data: AccountSettings
body: AccountSettings
) {
if (emailEnabled && !data.email)
return {
response: "Email invalid.",
status: 400,
};
else if (!data.username)
return {
response: "Username invalid.",
status: 400,
};
const dataValidation = UpdateUserSchema().safeParse(body);
// Check email (if enabled)
const checkEmail =
/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
if (emailEnabled && !checkEmail.test(data.email?.toLowerCase() || ""))
if (!dataValidation.success) {
return {
response: "Please enter a valid email.",
response: `Error: ${
dataValidation.error.issues[0].message
} [${dataValidation.error.issues[0].path.join(", ")}]`,
status: 400,
};
}
const checkUsername = RegExp("^[a-z0-9_-]{3,31}$");
if (!checkUsername.test(data.username.toLowerCase()))
return {
response:
"Username has to be between 3-30 characters, no spaces and special characters are allowed.",
status: 400,
};
const data = dataValidation.data;
const userIsTaken = await prisma.user.findFirst({
where: {
@@ -116,7 +101,7 @@ export default async function updateUserById(
const user = await prisma.user.findUnique({
where: { id: userId },
select: { email: true, password: true },
select: { email: true, password: true, name: true },
});
if (user && user.email && data.email && data.email !== user.email) {
@@ -148,7 +133,7 @@ export default async function updateUserById(
sendChangeEmailVerificationRequest(
user.email,
data.email,
data.name.trim()
data.name?.trim() || user.name
);
}
@@ -193,8 +178,8 @@ export default async function updateUserById(
id: userId,
},
data: {
name: data.name.trim(),
username: data.username?.toLowerCase().trim(),
name: data.name,
username: data.username,
isPrivate: data.isPrivate,
image:
data.image && data.image.startsWith("http")
@@ -202,10 +187,10 @@ export default async function updateUserById(
: data.image
? `uploads/avatar/${userId}.jpg`
: "",
collectionOrder: data.collectionOrder.filter(
collectionOrder: data.collectionOrder?.filter(
(value, index, self) => self.indexOf(value) === index
),
locale: i18n.locales.includes(data.locale) ? data.locale : "en",
locale: i18n.locales.includes(data.locale || "") ? data.locale : "en",
archiveAsScreenshot: data.archiveAsScreenshot,
archiveAsMonolith: data.archiveAsMonolith,
archiveAsPDF: data.archiveAsPDF,