316 lines
8.1 KiB
TypeScript
316 lines
8.1 KiB
TypeScript
import { Prisma } from '@/prisma/generated/client';
|
|
import { NextResponse } from 'next/server';
|
|
import db from '@/lib/db';
|
|
import bcrypt from 'bcryptjs';
|
|
|
|
import { Role } from '@/config/enum';
|
|
import { SignUpSchema } from '@/lib/schemas';
|
|
import { UserQueryString } from '@/config/types';
|
|
import { getUserByEmail, parseSearchParams, genUsername } from '@/helpers/user';
|
|
|
|
export async function GET(req: Request) {
|
|
const { searchParams } = new URL(req.url);
|
|
const query: UserQueryString = parseSearchParams(searchParams);
|
|
|
|
const isKoas = Role.Koas;
|
|
const isPasien = Role.Pasien;
|
|
const isFasilitator = Role.Fasilitator;
|
|
|
|
try {
|
|
const user = await db.user.findMany({
|
|
where: {
|
|
...query,
|
|
} as Prisma.UserWhereInput,
|
|
orderBy: {
|
|
name: 'asc',
|
|
},
|
|
include: {
|
|
KoasProfile: {
|
|
include: {
|
|
Review: {
|
|
include: {
|
|
user: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
PasienProfile: true,
|
|
FasilitatorProfile: true,
|
|
},
|
|
});
|
|
|
|
const users = await Promise.all(
|
|
user.map(async (user) => {
|
|
if (user.role === Role.Koas) {
|
|
const totalReviews = await db.review.count({
|
|
where: {
|
|
koasId: user.KoasProfile!.userId,
|
|
},
|
|
});
|
|
|
|
const averageRating = await db.review.aggregate({
|
|
_avg: {
|
|
rating: true,
|
|
},
|
|
where: {
|
|
koasId: user.KoasProfile!.userId,
|
|
},
|
|
});
|
|
|
|
const patientCount = await db.appointment.count({
|
|
where: {
|
|
koasId: user.KoasProfile!.id,
|
|
status: 'Completed',
|
|
},
|
|
});
|
|
|
|
const { createdAt, updateAt, ...koasProfileWithoutDates } =
|
|
user.KoasProfile!;
|
|
|
|
return {
|
|
...user,
|
|
KoasProfile: {
|
|
...koasProfileWithoutDates,
|
|
stats: {
|
|
totalReviews,
|
|
averageRating: parseFloat(
|
|
(averageRating._avg.rating || 0.0).toFixed(1)
|
|
),
|
|
patientCount,
|
|
},
|
|
|
|
createdAt,
|
|
updateAt,
|
|
},
|
|
PasienProfile: undefined, // sembunyikan pasien profile
|
|
FasilitatorProfile: undefined, // sembunyikan fasilitator profile
|
|
};
|
|
} else if (user.role === Role.Pasien) {
|
|
return {
|
|
...user,
|
|
KoasProfile: undefined, // sembunyikan koas profile
|
|
FasilitatorProfile: undefined, // sembunyikan fasilitator profile
|
|
};
|
|
} else if (user.role === Role.Fasilitator) {
|
|
return {
|
|
...user,
|
|
KoasProfile: undefined, // sembunyikan koas profile
|
|
PasienProfile: undefined, // sembunyikan pasien profile
|
|
};
|
|
} else {
|
|
return {
|
|
...user,
|
|
KoasProfile: undefined, // sembunyikan koas profile
|
|
PasienProfile: undefined, // sembunyikan pasien profile
|
|
FasilitatorProfile: undefined, // sembunyikan fasilitator profile
|
|
};
|
|
}
|
|
})
|
|
);
|
|
|
|
return NextResponse.json(
|
|
{
|
|
users,
|
|
},
|
|
{ status: 200 }
|
|
);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
console.error('Error fetching user', error.stack);
|
|
} else {
|
|
console.error('Error fetching user', error);
|
|
}
|
|
return NextResponse.json(
|
|
{ error: 'Internal Server Error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
try {
|
|
const body = await req.json();
|
|
// Accept both camelCase and snake_case for names
|
|
const {
|
|
id,
|
|
givenName,
|
|
familyName,
|
|
firstName,
|
|
lastName,
|
|
email,
|
|
password,
|
|
phone,
|
|
role,
|
|
profile,
|
|
image,
|
|
name: nameFromBody,
|
|
} = body;
|
|
|
|
console.log('Received Body:', body);
|
|
|
|
// Use givenName/familyName if present, else fallback to firstName/lastName
|
|
const userGivenName = givenName ?? firstName ?? '';
|
|
const userFamilyName = familyName ?? lastName ?? '';
|
|
|
|
// Check if this is an OAuth signup (no password)
|
|
const isOauth = !password || password === null || password === undefined;
|
|
|
|
// Validate required fields
|
|
if (!email || !role) {
|
|
return NextResponse.json(
|
|
{ error: 'Email and role are required.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Check for existing user
|
|
const existingUser = await getUserByEmail(email);
|
|
if (existingUser) {
|
|
console.log(`Attempted signup with existing email: ${email}`);
|
|
return NextResponse.json(
|
|
{ error: 'Email already in use.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const university = await db.university.findFirst({
|
|
where: {
|
|
alias: 'UNEJ',
|
|
},
|
|
});
|
|
|
|
if (!university) {
|
|
return NextResponse.json(
|
|
{ error: 'University not found.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Only hash password if not OAuth and password is provided
|
|
let hash: string | null = null;
|
|
if (!isOauth) {
|
|
if (typeof password !== 'string' || password.length === 0) {
|
|
return NextResponse.json(
|
|
{ error: 'Password is required for non-OAuth signup.' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
try {
|
|
hash = await bcrypt.hash(password, 10);
|
|
} catch (err) {
|
|
console.error('Error hashing password:', err);
|
|
return NextResponse.json(
|
|
{ error: 'Failed to process password.' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Generate username if not provided
|
|
let name = nameFromBody;
|
|
if (!name) {
|
|
name = await genUsername(userGivenName, userFamilyName);
|
|
}
|
|
|
|
// Create the user
|
|
const newUser = await db.user.create({
|
|
data: {
|
|
id,
|
|
givenName: userGivenName,
|
|
familyName: userFamilyName,
|
|
name,
|
|
email,
|
|
password: hash,
|
|
phone,
|
|
image: image ?? null,
|
|
role: role ?? null,
|
|
} as Prisma.UserCreateInput,
|
|
});
|
|
|
|
// Create profile based on role
|
|
if (newUser.role === Role.Koas) {
|
|
await db.koasProfile.create({
|
|
data: {
|
|
...profile,
|
|
university: university.name,
|
|
universityId: university.id,
|
|
userId: newUser.id,
|
|
},
|
|
});
|
|
} else if (newUser.role === Role.Pasien) {
|
|
await db.pasienProfile.create({
|
|
data: {
|
|
...profile,
|
|
userId: newUser.id,
|
|
},
|
|
});
|
|
} else if (newUser.role === Role.Fasilitator) {
|
|
await db.fasilitatorProfile.create({
|
|
data: {
|
|
...profile,
|
|
university: university.name,
|
|
userId: newUser.id,
|
|
},
|
|
});
|
|
} else if (newUser.role === Role.Admin) {
|
|
// For admin, just update emailVerified
|
|
await db.user.update({
|
|
where: { id: newUser.id },
|
|
data: {
|
|
emailVerified: new Date(),
|
|
},
|
|
});
|
|
} else {
|
|
return NextResponse.json({ error: 'Invalid role' }, { status: 400 });
|
|
}
|
|
|
|
// If OAuth, mark email as verified
|
|
if (isOauth) {
|
|
await db.user.update({
|
|
where: { id: newUser.id },
|
|
data: { emailVerified: new Date() },
|
|
});
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{
|
|
status: 'Success',
|
|
message: 'User created successfully',
|
|
data: { user: newUser },
|
|
},
|
|
{ status: 201 }
|
|
);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
console.error('Error in POST /api/users:', error.stack);
|
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
} else {
|
|
console.error('Unknown error in POST /api/users:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal Server Error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function DELETE(req: Request) {
|
|
try {
|
|
await db.user.deleteMany({});
|
|
|
|
return NextResponse.json(
|
|
{
|
|
status: 'Success',
|
|
message: 'All users deleted successfully',
|
|
},
|
|
{ status: 200 }
|
|
);
|
|
} catch (error) {
|
|
console.error('Error deleting all users', error);
|
|
return NextResponse.json(
|
|
{ error: 'Internal Server Error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|