mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 20:13:49 +00:00
Refactor: begin reorganizing services dir.
- Renamed files and directories to reflect the new structure - Moved comment-related services to the 'comments' directory - Moved image-related services to the 'images' directory - Moved like-related services to the 'likes' directory - Moved post-related services to the 'posts' directory - Moved user-related services to the 'users' directory
This commit is contained in:
44
src/services/users/User/createNewUser.ts
Normal file
44
src/services/users/User/createNewUser.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { hashPassword } from '@/config/auth/passwordFns';
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import { CreateUserValidationSchema } from './schema/CreateUserValidationSchemas';
|
||||
import GetUserSchema from './schema/GetUserSchema';
|
||||
|
||||
const createNewUser = async ({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
dateOfBirth,
|
||||
username,
|
||||
}: z.infer<typeof CreateUserValidationSchema>) => {
|
||||
const hash = await hashPassword(password);
|
||||
const user: z.infer<typeof GetUserSchema> = await DBClient.instance.user.create({
|
||||
data: {
|
||||
username,
|
||||
email,
|
||||
hash,
|
||||
firstName,
|
||||
lastName,
|
||||
dateOfBirth: new Date(dateOfBirth),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
dateOfBirth: true,
|
||||
createdAt: true,
|
||||
accountIsVerified: true,
|
||||
updatedAt: true,
|
||||
role: true,
|
||||
userAvatar: true,
|
||||
bio: true,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default createNewUser;
|
||||
28
src/services/users/User/deleteUserById.ts
Normal file
28
src/services/users/User/deleteUserById.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import GetUserSchema from './schema/GetUserSchema';
|
||||
|
||||
const deleteUserById = async (id: string) => {
|
||||
const deletedUser: z.infer<typeof GetUserSchema> | null =
|
||||
await DBClient.instance.user.delete({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
dateOfBirth: true,
|
||||
createdAt: true,
|
||||
accountIsVerified: true,
|
||||
updatedAt: true,
|
||||
role: true,
|
||||
userAvatar: true,
|
||||
bio: true,
|
||||
},
|
||||
});
|
||||
|
||||
return deletedUser;
|
||||
};
|
||||
|
||||
export default deleteUserById;
|
||||
13
src/services/users/User/findUserByEmail.ts
Normal file
13
src/services/users/User/findUserByEmail.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import DBClient from '../../../prisma/DBClient';
|
||||
|
||||
const findUserByEmail = async (email: string) =>
|
||||
DBClient.instance.user.findFirst({
|
||||
where: { email },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
hash: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default findUserByEmail;
|
||||
37
src/services/users/User/findUserById.ts
Normal file
37
src/services/users/User/findUserById.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import GetUserSchema from './schema/GetUserSchema';
|
||||
|
||||
const findUserById = async (id: string) => {
|
||||
const user: z.infer<typeof GetUserSchema> | null =
|
||||
await DBClient.instance.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
dateOfBirth: true,
|
||||
createdAt: true,
|
||||
accountIsVerified: true,
|
||||
updatedAt: true,
|
||||
role: true,
|
||||
userAvatar: {
|
||||
select: {
|
||||
path: true,
|
||||
alt: true,
|
||||
caption: true,
|
||||
createdAt: true,
|
||||
id: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
},
|
||||
bio: true,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default findUserById;
|
||||
23
src/services/users/User/findUserByIdPublic.ts
Normal file
23
src/services/users/User/findUserByIdPublic.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
|
||||
import PublicUserSchema from './schema/PublicUserSchema';
|
||||
|
||||
const findUserByIdPublic = async (id: string) => {
|
||||
const user: z.infer<typeof PublicUserSchema> | null =
|
||||
await DBClient.instance.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
createdAt: true,
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default findUserByIdPublic;
|
||||
13
src/services/users/User/findUserByUsername.ts
Normal file
13
src/services/users/User/findUserByUsername.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import DBClient from '../../../prisma/DBClient';
|
||||
|
||||
const findUserByUsername = async (username: string) =>
|
||||
DBClient.instance.user.findFirst({
|
||||
where: { username },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
hash: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default findUserByUsername;
|
||||
@@ -0,0 +1,84 @@
|
||||
import validateEmailRequest from '@/requests/User/validateEmailRequest';
|
||||
import validateUsernameRequest from '@/requests/validateUsernameRequest';
|
||||
import sub from 'date-fns/sub';
|
||||
import { z } from 'zod';
|
||||
|
||||
const MINIMUM_DATE_OF_BIRTH = sub(new Date(), { years: 19 });
|
||||
const NAME_REGEX =
|
||||
/^[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ðæ ,.'-]+$/u;
|
||||
|
||||
export const BaseCreateUserSchema = z.object({
|
||||
password: z
|
||||
.string()
|
||||
.min(8, { message: 'Password must be at least 8 characters.' })
|
||||
.refine((password) => /[A-Z]/.test(password), {
|
||||
message: 'Password must contain at least one uppercase letter.',
|
||||
})
|
||||
.refine((password) => /[0-9]/.test(password), {
|
||||
message: 'Password must contain at least one number.',
|
||||
})
|
||||
.refine((password) => /[^a-zA-Z0-9]/.test(password), {
|
||||
message: 'Password must contain at least one special character.',
|
||||
}),
|
||||
confirmPassword: z.string(),
|
||||
firstName: z
|
||||
.string()
|
||||
.min(1, { message: 'First name must not be empty.' })
|
||||
.max(20, { message: 'First name must be less than 20 characters.' })
|
||||
.refine((firstName) => NAME_REGEX.test(firstName), {
|
||||
message: 'First name must only contain letters or hyphens.',
|
||||
}),
|
||||
lastName: z
|
||||
.string()
|
||||
.min(1, { message: 'Last name must not be empty.' })
|
||||
.max(20, { message: 'Last name must be less than 20 characters.' })
|
||||
.refine((lastName) => NAME_REGEX.test(lastName), {
|
||||
message: 'Last name must only contain letters.',
|
||||
}),
|
||||
dateOfBirth: z
|
||||
.string()
|
||||
.refine((val) => !Number.isNaN(Date.parse(val)), {
|
||||
message: 'Date is invalid.',
|
||||
})
|
||||
.refine((dateOfBirth) => new Date(dateOfBirth) <= MINIMUM_DATE_OF_BIRTH, {
|
||||
message: 'You must be at least 19 years old to register.',
|
||||
}),
|
||||
username: z
|
||||
.string()
|
||||
.min(1, { message: 'Username must not be empty.' })
|
||||
.max(20, { message: 'Username must be less than 20 characters.' }),
|
||||
email: z.string().email({ message: 'Email must be a valid email address.' }),
|
||||
});
|
||||
|
||||
export const CreateUserValidationSchema = BaseCreateUserSchema.refine(
|
||||
(data) => data.password === data.confirmPassword,
|
||||
{ message: 'Passwords do not match.', path: ['confirmPassword'] },
|
||||
);
|
||||
|
||||
export const CreateUserValidationSchemaWithUsernameAndEmailCheck =
|
||||
BaseCreateUserSchema.extend({
|
||||
email: z
|
||||
.string()
|
||||
.email({ message: 'Email must be a valid email address.' })
|
||||
.refine(async (email) => validateEmailRequest(email), {
|
||||
message: 'Email is already taken.',
|
||||
}),
|
||||
username: z
|
||||
.string()
|
||||
.min(1, { message: 'Username must not be empty.' })
|
||||
.max(20, { message: 'Username must be less than 20 characters.' })
|
||||
.refine(async (username) => validateUsernameRequest(username), {
|
||||
message: 'Username is already taken.',
|
||||
}),
|
||||
}).refine((data) => data.password === data.confirmPassword, {
|
||||
message: 'Passwords do not match.',
|
||||
path: ['confirmPassword'],
|
||||
});
|
||||
|
||||
export const UpdatePasswordSchema = BaseCreateUserSchema.pick({
|
||||
password: true,
|
||||
confirmPassword: true,
|
||||
}).refine((data) => data.password === data.confirmPassword, {
|
||||
message: 'Passwords do not match.',
|
||||
path: ['confirmPassword'],
|
||||
});
|
||||
10
src/services/users/User/schema/EditUserSchema.ts
Normal file
10
src/services/users/User/schema/EditUserSchema.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { BaseCreateUserSchema } from './CreateUserValidationSchemas';
|
||||
|
||||
const EditUserSchema = BaseCreateUserSchema.pick({
|
||||
username: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
});
|
||||
|
||||
export default EditUserSchema;
|
||||
19
src/services/users/User/schema/GetUserSchema.ts
Normal file
19
src/services/users/User/schema/GetUserSchema.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import ImageQueryValidationSchema from '@/services/schema/ImageSchema/ImageQueryValidationSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
const GetUserSchema = z.object({
|
||||
id: z.string().cuid(),
|
||||
username: z.string(),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date().nullable(),
|
||||
email: z.string().email(),
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
dateOfBirth: z.coerce.date(),
|
||||
accountIsVerified: z.boolean(),
|
||||
role: z.enum(['USER', 'ADMIN']),
|
||||
bio: z.string().nullable(),
|
||||
userAvatar: ImageQueryValidationSchema.nullable(),
|
||||
});
|
||||
|
||||
export default GetUserSchema;
|
||||
18
src/services/users/User/schema/LoginValidationSchema.ts
Normal file
18
src/services/users/User/schema/LoginValidationSchema.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const LoginValidationSchema = z.object({
|
||||
username: z
|
||||
.string({
|
||||
required_error: 'Username is required.',
|
||||
invalid_type_error: 'Username must be a string.',
|
||||
})
|
||||
.min(1, { message: 'Username is required.' }),
|
||||
password: z
|
||||
.string({
|
||||
required_error: 'Password is required.',
|
||||
invalid_type_error: 'Password must be a string.',
|
||||
})
|
||||
.min(1, { message: 'Password is required.' }),
|
||||
});
|
||||
|
||||
export default LoginValidationSchema;
|
||||
11
src/services/users/User/schema/PublicUserSchema.ts
Normal file
11
src/services/users/User/schema/PublicUserSchema.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import GetUserSchema from '@/services/users/User/schema/GetUserSchema';
|
||||
|
||||
const PublicUserSchema = GetUserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
username: true,
|
||||
role: true,
|
||||
});
|
||||
|
||||
export default PublicUserSchema;
|
||||
7
src/services/users/User/schema/TokenValidationSchema.ts
Normal file
7
src/services/users/User/schema/TokenValidationSchema.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import z from 'zod';
|
||||
|
||||
const TokenValidationSchema = z.object({
|
||||
token: z.string(),
|
||||
});
|
||||
|
||||
export default TokenValidationSchema;
|
||||
29
src/services/users/User/schema/UpdateProfileSchema.ts
Normal file
29
src/services/users/User/schema/UpdateProfileSchema.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
const UpdateProfileSchema = z.object({
|
||||
bio: z.string(),
|
||||
userAvatar: z
|
||||
.instanceof(typeof FileList !== 'undefined' ? FileList : Object)
|
||||
.refine((fileList) => fileList instanceof FileList, {
|
||||
message: 'You must submit this form in a web browser.',
|
||||
})
|
||||
.refine((fileList) => [...(fileList as FileList)].length <= 1, {
|
||||
message: 'You must upload only one or zero files.',
|
||||
})
|
||||
.refine(
|
||||
(fileList) =>
|
||||
[...(fileList as FileList)]
|
||||
.map((file) => file.type)
|
||||
.every((fileType) => fileType.startsWith('image/')),
|
||||
{ message: 'You must upload only images.' },
|
||||
)
|
||||
.refine(
|
||||
(fileList) =>
|
||||
[...(fileList as FileList)]
|
||||
.map((file) => file.size)
|
||||
.every((fileSize) => fileSize < 15 * 1024 * 1024),
|
||||
{ message: 'You must upload images smaller than 15MB.' },
|
||||
),
|
||||
});
|
||||
|
||||
export default UpdateProfileSchema;
|
||||
29
src/services/users/User/sendConfirmationEmail.ts
Normal file
29
src/services/users/User/sendConfirmationEmail.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { generateConfirmationToken } from '@/config/jwt';
|
||||
import sendEmail from '@/config/sparkpost/sendEmail';
|
||||
|
||||
import Welcome from '@/emails/Welcome';
|
||||
import { render } from '@react-email/render';
|
||||
import { z } from 'zod';
|
||||
import { BASE_URL } from '@/config/env';
|
||||
import { ReactElement } from 'react';
|
||||
import GetUserSchema from './schema/GetUserSchema';
|
||||
|
||||
type UserSchema = z.infer<typeof GetUserSchema>;
|
||||
|
||||
const sendConfirmationEmail = async ({ id, username, email }: UserSchema) => {
|
||||
const confirmationToken = generateConfirmationToken({ id, username });
|
||||
|
||||
const subject = 'Confirm your email';
|
||||
const name = username;
|
||||
const url = `${BASE_URL}/users/confirm?token=${confirmationToken}`;
|
||||
const address = email;
|
||||
|
||||
const component = Welcome({ name, url, subject })! as ReactElement<unknown, string>;
|
||||
|
||||
const html = render(component);
|
||||
const text = render(component, { plainText: true });
|
||||
|
||||
await sendEmail({ address, subject, text, html });
|
||||
};
|
||||
|
||||
export default sendConfirmationEmail;
|
||||
30
src/services/users/User/sendResetPasswordEmail.ts
Normal file
30
src/services/users/User/sendResetPasswordEmail.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { BASE_URL } from '@/config/env';
|
||||
import { generateResetPasswordToken } from '@/config/jwt';
|
||||
import sendEmail from '@/config/sparkpost/sendEmail';
|
||||
import ForgotEmail from '@/emails/ForgotEmail';
|
||||
import { User } from '@prisma/client';
|
||||
import type { ReactElement } from 'react';
|
||||
import { render } from '@react-email/render';
|
||||
|
||||
const sendResetPasswordEmail = async (user: User) => {
|
||||
const token = generateResetPasswordToken({ id: user.id, username: user.username });
|
||||
|
||||
const url = `${BASE_URL}/users/reset-password?token=${token}`;
|
||||
|
||||
const component = ForgotEmail({ name: user.username, url })! as ReactElement<
|
||||
unknown,
|
||||
string
|
||||
>;
|
||||
|
||||
const html = render(component);
|
||||
const text = render(component, { plainText: true });
|
||||
|
||||
await sendEmail({
|
||||
address: user.email,
|
||||
subject: 'Reset Password',
|
||||
html,
|
||||
text,
|
||||
});
|
||||
};
|
||||
|
||||
export default sendResetPasswordEmail;
|
||||
33
src/services/users/User/updateUserProfileById.ts
Normal file
33
src/services/users/User/updateUserProfileById.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import GetUserSchema from './schema/GetUserSchema';
|
||||
|
||||
interface UpdateUserProfileByIdParams {
|
||||
id: string;
|
||||
data: { bio: string };
|
||||
}
|
||||
|
||||
const updateUserProfileById = async ({ id, data }: UpdateUserProfileByIdParams) => {
|
||||
const user: z.infer<typeof GetUserSchema> = await DBClient.instance.user.update({
|
||||
where: { id },
|
||||
data: { bio: data.bio },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
bio: true,
|
||||
userAvatar: true,
|
||||
accountIsVerified: true,
|
||||
createdAt: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
updatedAt: true,
|
||||
dateOfBirth: true,
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateUserProfileById;
|
||||
37
src/services/users/User/updateUserToBeConfirmedById.ts
Normal file
37
src/services/users/User/updateUserToBeConfirmedById.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import GetUserSchema from '@/services/users/User/schema/GetUserSchema';
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
|
||||
const updateUserToBeConfirmedById = async (id: string) => {
|
||||
const user: z.infer<typeof GetUserSchema> = await DBClient.instance.user.update({
|
||||
where: { id },
|
||||
data: { accountIsVerified: true, updatedAt: new Date() },
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
accountIsVerified: true,
|
||||
createdAt: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
updatedAt: true,
|
||||
dateOfBirth: true,
|
||||
role: true,
|
||||
bio: true,
|
||||
userAvatar: {
|
||||
select: {
|
||||
id: true,
|
||||
path: true,
|
||||
alt: true,
|
||||
caption: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateUserToBeConfirmedById;
|
||||
55
src/services/users/UserAccount/UpdateUserAvatarByIdParams.ts
Normal file
55
src/services/users/UserAccount/UpdateUserAvatarByIdParams.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import GetUserSchema from '@/services/users/User/schema/GetUserSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface UpdateUserAvatarByIdParams {
|
||||
id: string;
|
||||
data: {
|
||||
avatar: {
|
||||
alt: string;
|
||||
path: string;
|
||||
caption: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
const updateUserAvatarById = async ({ id, data }: UpdateUserAvatarByIdParams) => {
|
||||
const user: z.infer<typeof GetUserSchema> = await DBClient.instance.user.update({
|
||||
where: { id },
|
||||
data: {
|
||||
userAvatar: data.avatar
|
||||
? {
|
||||
upsert: {
|
||||
create: {
|
||||
alt: data.avatar.alt,
|
||||
path: data.avatar.path,
|
||||
caption: data.avatar.caption,
|
||||
},
|
||||
update: {
|
||||
alt: data.avatar.alt,
|
||||
path: data.avatar.path,
|
||||
caption: data.avatar.caption,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
bio: true,
|
||||
userAvatar: true,
|
||||
accountIsVerified: true,
|
||||
createdAt: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
updatedAt: true,
|
||||
dateOfBirth: true,
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateUserAvatarById;
|
||||
27
src/services/users/UserFollows/getUsersFollowedByUser.ts
Normal file
27
src/services/users/UserFollows/getUsersFollowedByUser.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import FollowInfoSchema from './schema/FollowInfoSchema';
|
||||
|
||||
interface GetFollowingInfoByUserIdArgs {
|
||||
userId: string;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}
|
||||
const getUsersFollowedByUser = async ({
|
||||
userId,
|
||||
pageNum,
|
||||
pageSize,
|
||||
}: GetFollowingInfoByUserIdArgs): Promise<z.infer<typeof FollowInfoSchema>[]> => {
|
||||
const usersFollowedByQueriedUser = await DBClient.instance.userFollow.findMany({
|
||||
take: pageSize,
|
||||
skip: (pageNum - 1) * pageSize,
|
||||
where: { following: { id: userId } },
|
||||
select: {
|
||||
follower: { select: { username: true, userAvatar: true, id: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return usersFollowedByQueriedUser.map((u) => u.follower);
|
||||
};
|
||||
|
||||
export default getUsersFollowedByUser;
|
||||
27
src/services/users/UserFollows/getUsersFollowingUser.ts
Normal file
27
src/services/users/UserFollows/getUsersFollowingUser.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import { z } from 'zod';
|
||||
import FollowInfoSchema from './schema/FollowInfoSchema';
|
||||
|
||||
interface GetFollowingInfoByUserIdArgs {
|
||||
userId: string;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}
|
||||
const getUsersFollowingUser = async ({
|
||||
userId,
|
||||
pageNum,
|
||||
pageSize,
|
||||
}: GetFollowingInfoByUserIdArgs): Promise<z.infer<typeof FollowInfoSchema>[]> => {
|
||||
const usersFollowingQueriedUser = await DBClient.instance.userFollow.findMany({
|
||||
take: pageSize,
|
||||
skip: (pageNum - 1) * pageSize,
|
||||
where: { follower: { id: userId } },
|
||||
select: {
|
||||
following: { select: { username: true, userAvatar: true, id: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return usersFollowingQueriedUser.map((u) => u.following);
|
||||
};
|
||||
|
||||
export default getUsersFollowingUser;
|
||||
@@ -0,0 +1,9 @@
|
||||
import GetUserSchema from '@/services/users/User/schema/GetUserSchema';
|
||||
|
||||
const FollowInfoSchema = GetUserSchema.pick({
|
||||
userAvatar: true,
|
||||
id: true,
|
||||
username: true,
|
||||
});
|
||||
|
||||
export default FollowInfoSchema;
|
||||
Reference in New Issue
Block a user