mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 22:54:45 -05:00
Add roles system
This commit is contained in:
@@ -6,6 +6,8 @@ import { eq } from "drizzle-orm";
|
|||||||
import { saveFile } from "~/lib/fileStorage";
|
import { saveFile } from "~/lib/fileStorage";
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { studies, participants } from "~/server/db/schema";
|
import { studies, participants } from "~/server/db/schema";
|
||||||
|
import { anonymizeParticipants } from "~/lib/permissions"; // Import the anonymize function
|
||||||
|
|
||||||
// Function to generate a random string
|
// Function to generate a random string
|
||||||
const generateRandomString = (length: number) => {
|
const generateRandomString = (length: number) => {
|
||||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
@@ -37,7 +39,13 @@ export async function GET(request: Request) {
|
|||||||
.innerJoin(studies, eq(informedConsentForms.studyId, studies.id))
|
.innerJoin(studies, eq(informedConsentForms.studyId, studies.id))
|
||||||
.innerJoin(participants, eq(informedConsentForms.participantId, participants.id));
|
.innerJoin(participants, eq(informedConsentForms.participantId, participants.id));
|
||||||
|
|
||||||
return NextResponse.json(forms);
|
// Anonymize participant names
|
||||||
|
const anonymizedForms = forms.map(form => ({
|
||||||
|
...form,
|
||||||
|
participantName: `Participant ${form.participantId}` // Anonymizing logic
|
||||||
|
}));
|
||||||
|
|
||||||
|
return NextResponse.json(anonymizedForms);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { db } from "~/server/db";
|
|||||||
import { participants, trialParticipants, trials } from "~/server/db/schema";
|
import { participants, trialParticipants, trials } from "~/server/db/schema";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
import { auth } from "@clerk/nextjs/server"; // Import auth to get userId
|
||||||
|
import { anonymizeParticipants } from "~/lib/permissions"; // Import the anonymize function
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
|
const { userId } = auth(); // Get the userId from auth
|
||||||
try {
|
try {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const studyId = searchParams.get('studyId');
|
const studyId = searchParams.get('studyId');
|
||||||
@@ -16,6 +19,7 @@ export async function GET(request: Request) {
|
|||||||
.select({
|
.select({
|
||||||
id: participants.id,
|
id: participants.id,
|
||||||
name: participants.name,
|
name: participants.name,
|
||||||
|
studyId: participants.studyId,
|
||||||
createdAt: participants.createdAt,
|
createdAt: participants.createdAt,
|
||||||
latestTrialTimestamp: sql<Date | null>`MAX(${trials.createdAt})`.as('latestTrialTimestamp')
|
latestTrialTimestamp: sql<Date | null>`MAX(${trials.createdAt})`.as('latestTrialTimestamp')
|
||||||
})
|
})
|
||||||
@@ -26,7 +30,10 @@ export async function GET(request: Request) {
|
|||||||
.groupBy(participants.id)
|
.groupBy(participants.id)
|
||||||
.orderBy(sql`COALESCE(MAX(${trials.createdAt}), ${participants.createdAt}) DESC`);
|
.orderBy(sql`COALESCE(MAX(${trials.createdAt}), ${participants.createdAt}) DESC`);
|
||||||
|
|
||||||
return NextResponse.json(participantsWithLatestTrial);
|
// Anonymize participant names
|
||||||
|
const anonymizedParticipants = anonymizeParticipants(participantsWithLatestTrial, userId);
|
||||||
|
|
||||||
|
return NextResponse.json(anonymizedParticipants);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in GET /api/participants:', error);
|
console.error('Error in GET /api/participants:', error);
|
||||||
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { db } from "~/server/db";
|
import { db } from "~/server/db";
|
||||||
import { users } from "~/server/db/schema";
|
import { users } from "~/server/db/schema";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const { email } = await request.json();
|
const { email } = await request.json();
|
||||||
@@ -11,6 +11,12 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "Email is required" }, { status: 400 });
|
return NextResponse.json({ error: "Email is required" }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the user already exists
|
||||||
|
const existingUser = await db.select().from(users).where(eq(users.email, email)).limit(1);
|
||||||
|
if (existingUser) {
|
||||||
|
return NextResponse.json({ error: "User already exists" }, { status: 409 });
|
||||||
|
}
|
||||||
|
|
||||||
// Insert the new user into the database
|
// Insert the new user into the database
|
||||||
const newUser = await db.insert(users).values({ email }).returning();
|
const newUser = await db.insert(users).values({ email }).returning();
|
||||||
return NextResponse.json(newUser[0]);
|
return NextResponse.json(newUser[0]);
|
||||||
|
|||||||
@@ -75,19 +75,21 @@ export function Trials() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="card-level-1">
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-2xl font-bold">Trials</CardTitle>
|
<CardTitle className="text-2xl font-bold">Trials</CardTitle>
|
||||||
<CreateTrialDialog onCreateTrial={createTrial} />
|
<CreateTrialDialog onCreateTrial={createTrial} />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{trials.length > 0 ? (
|
{trials.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{trials.map(trial => (
|
{trials.map(trial => (
|
||||||
<Card key={trial.id} className="bg-gray-100 p-3 flex items-center justify-between">
|
<Card key={trial.id} className="card-level-2 p-3 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold">{trial.title}</h3>
|
<h3 className="font-semibold">{trial.title}</h3>
|
||||||
<p className="text-sm text-gray-500">Participants: {trial.participantIds.join(', ')}</p>
|
<p className="text-sm text-gray-500">
|
||||||
|
Participants: {trial.participantIds ? trial.participantIds.join(', ') : 'None'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
20
src/lib/permissions.ts
Normal file
20
src/lib/permissions.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { auth } from "@clerk/nextjs/server";
|
||||||
|
import { Participant } from "../types/Participant";
|
||||||
|
|
||||||
|
export const isUserAuthorized = (userId: string | null): boolean => {
|
||||||
|
// Implement your logic to determine if the user is authorized to see participant names
|
||||||
|
// For example, you might check if the user is an admin or has a specific role
|
||||||
|
// return userId !== null; // Placeholder logic, replace with your actual authorization logic
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const anonymizeParticipants = (participants: Participant[], userId: string | null): Participant[] => {
|
||||||
|
if (isUserAuthorized(userId)) {
|
||||||
|
return participants; // Return original participants if authorized
|
||||||
|
}
|
||||||
|
|
||||||
|
return participants.map(participant => ({
|
||||||
|
...participant,
|
||||||
|
name: `Participant ${participant.id}`, // Anonymize the name
|
||||||
|
}));
|
||||||
|
};
|
||||||
@@ -17,7 +17,8 @@ if (env.NODE_ENV !== "production") globalForDb.conn = conn;
|
|||||||
|
|
||||||
export const db = drizzle(conn, { schema });
|
export const db = drizzle(conn, { schema });
|
||||||
|
|
||||||
import { initializeContentTypes } from "./init";
|
import { initializeContentTypes, initializeRoles } from "./init";
|
||||||
|
|
||||||
// Initialize content types
|
// Initialize content types
|
||||||
initializeContentTypes().catch(console.error);
|
initializeContentTypes().catch(console.error);
|
||||||
|
initializeRoles().catch(console.error);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { db } from "./index";
|
import { db } from "./index";
|
||||||
import { contentTypes } from "./schema";
|
import { contentTypes } from "./schema";
|
||||||
|
import { roles } from "./schema";
|
||||||
|
|
||||||
export async function initializeContentTypes() {
|
export async function initializeContentTypes() {
|
||||||
const existingTypes = await db.select().from(contentTypes);
|
const existingTypes = await db.select().from(contentTypes);
|
||||||
@@ -11,5 +12,21 @@ export async function initializeContentTypes() {
|
|||||||
// Add other content types as needed
|
// Add other content types as needed
|
||||||
]);
|
]);
|
||||||
console.log("Content types initialized");
|
console.log("Content types initialized");
|
||||||
|
} else {
|
||||||
|
console.log("Content types already initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initializeRoles() {
|
||||||
|
const existingRoles = await db.select().from(roles);
|
||||||
|
|
||||||
|
if (existingRoles.length === 0) {
|
||||||
|
await db.insert(roles).values([
|
||||||
|
{ name: "Basic User" }, // Role ID 0
|
||||||
|
{ name: "Admin" }, // Role ID 1
|
||||||
|
]);
|
||||||
|
console.log("Roles initialized");
|
||||||
|
} else {
|
||||||
|
console.log("Roles already initialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
serial,
|
serial,
|
||||||
varchar,
|
varchar,
|
||||||
timestamp,
|
timestamp,
|
||||||
integer
|
integer,
|
||||||
|
boolean
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
|
|
||||||
@@ -85,6 +86,7 @@ export const users = pgTable(
|
|||||||
{
|
{
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
email: varchar("email", { length: 256 }).notNull().unique(),
|
email: varchar("email", { length: 256 }).notNull().unique(),
|
||||||
|
roleId: integer("role_id").references(() => roles.id).default(0), // Link to roles
|
||||||
createdAt: timestamp("created_at", { withTimezone: true })
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
.default(sql`CURRENT_TIMESTAMP`)
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
@@ -110,3 +112,45 @@ export const trialParticipants = pgTable(
|
|||||||
participantId: integer("participant_id").references(() => participants.id).notNull(),
|
participantId: integer("participant_id").references(() => participants.id).notNull(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const roles = pgTable(
|
||||||
|
"role",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name", { length: 50 }).notNull().unique(),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
|
.notNull(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const permissions = pgTable(
|
||||||
|
"permission",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name", { length: 50 }).notNull().unique(),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
|
.notNull(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const rolePermissions = pgTable(
|
||||||
|
"role_permissions",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
roleId: integer("role_id").references(() => roles.id).notNull(),
|
||||||
|
permissionId: integer("permission_id").references(() => permissions.id).notNull(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const permissionTypes = pgTable(
|
||||||
|
"permission_type",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name", { length: 50 }).notNull().unique(),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`)
|
||||||
|
.notNull(),
|
||||||
|
}
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user