mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 22:54:45 -05:00
chore(deps): Update dependencies and enhance API error handling
- Added '@vercel/analytics' to package.json for improved analytics tracking. - Updated 'next' version from 15.0.2 to 15.0.3 to incorporate the latest features and fixes. - Refactored API routes for invitations and studies to improve error handling and response structure. - Enhanced permission checks in the invitations and studies API to ensure proper access control. - Removed the participants dashboard page as part of a restructuring effort. - Updated the database schema to include environment settings for users and studies. - Improved the dashboard components to handle loading states and display statistics more effectively.
This commit is contained in:
@@ -2,10 +2,11 @@ import { NextResponse } from "next/server";
|
||||
import { auth } from "@clerk/nextjs/server";
|
||||
import { db } from "~/db";
|
||||
import { invitationsTable, studyTable, rolesTable } from "~/db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { randomBytes } from "crypto";
|
||||
import { sendInvitationEmail } from "~/lib/email";
|
||||
import { hasPermission, hasStudyAccess, PERMISSIONS } from "~/lib/permissions";
|
||||
import { PERMISSIONS, checkPermissions } from "~/lib/permissions-server";
|
||||
import { ApiError, createApiResponse } from "~/lib/api-utils";
|
||||
|
||||
// Helper to generate a secure random token
|
||||
function generateToken(): string {
|
||||
@@ -13,24 +14,21 @@ function generateToken(): string {
|
||||
}
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const { userId } = await auth();
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const studyId = url.searchParams.get("studyId");
|
||||
|
||||
if (!studyId) {
|
||||
return NextResponse.json({ error: "Study ID is required" }, { status: 400 });
|
||||
return ApiError.BadRequest("Study ID is required");
|
||||
}
|
||||
|
||||
// First check if user has access to the study
|
||||
const hasAccess = await hasStudyAccess(userId, parseInt(studyId));
|
||||
if (!hasAccess) {
|
||||
return NextResponse.json({ error: "Study not found" }, { status: 404 });
|
||||
const permissionCheck = await checkPermissions({
|
||||
studyId: parseInt(studyId),
|
||||
permission: PERMISSIONS.MANAGE_ROLES
|
||||
});
|
||||
|
||||
if (permissionCheck.error) {
|
||||
return permissionCheck.error;
|
||||
}
|
||||
|
||||
// Get all invitations for the study, including role names
|
||||
@@ -47,37 +45,26 @@ export async function GET(request: Request) {
|
||||
.innerJoin(rolesTable, eq(invitationsTable.roleId, rolesTable.id))
|
||||
.where(eq(invitationsTable.studyId, parseInt(studyId)));
|
||||
|
||||
return NextResponse.json(invitations);
|
||||
return createApiResponse(invitations);
|
||||
} catch (error) {
|
||||
console.error("Error fetching invitations:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 }
|
||||
);
|
||||
return ApiError.ServerError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { userId } = await auth();
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { email, studyId, roleId } = await request.json();
|
||||
|
||||
// First check if user has access to the study
|
||||
const hasAccess = await hasStudyAccess(userId, studyId);
|
||||
if (!hasAccess) {
|
||||
return NextResponse.json({ error: "Study not found" }, { status: 404 });
|
||||
const permissionCheck = await checkPermissions({
|
||||
studyId,
|
||||
permission: PERMISSIONS.MANAGE_ROLES
|
||||
});
|
||||
|
||||
if (permissionCheck.error) {
|
||||
return permissionCheck.error;
|
||||
}
|
||||
|
||||
// Then check if user has permission to invite users
|
||||
const canInvite = await hasPermission(userId, PERMISSIONS.MANAGE_ROLES, studyId);
|
||||
if (!canInvite) {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
const { userId } = permissionCheck;
|
||||
|
||||
// Get study details
|
||||
const study = await db
|
||||
@@ -87,7 +74,7 @@ export async function POST(request: Request) {
|
||||
.limit(1);
|
||||
|
||||
if (!study[0]) {
|
||||
return NextResponse.json({ error: "Study not found" }, { status: 404 });
|
||||
return ApiError.NotFound("Study");
|
||||
}
|
||||
|
||||
// Verify the role exists
|
||||
@@ -98,7 +85,7 @@ export async function POST(request: Request) {
|
||||
.limit(1);
|
||||
|
||||
if (!role[0]) {
|
||||
return NextResponse.json({ error: "Invalid role" }, { status: 400 });
|
||||
return ApiError.BadRequest("Invalid role");
|
||||
}
|
||||
|
||||
// Generate invitation token
|
||||
@@ -128,12 +115,8 @@ export async function POST(request: Request) {
|
||||
token,
|
||||
});
|
||||
|
||||
return NextResponse.json(invitation);
|
||||
return createApiResponse(invitation);
|
||||
} catch (error) {
|
||||
console.error("Error creating invitation:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 }
|
||||
);
|
||||
return ApiError.ServerError(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user