Skip to content

Commit

Permalink
feat: user management api (#44)
Browse files Browse the repository at this point in the history
* feat: user management api

* refactor: user managment end points

* refactor: user api query the whole row
  • Loading branch information
avevotsira authored Mar 23, 2024
1 parent 834c48c commit ddda356
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
169 changes: 169 additions & 0 deletions src/app/api/database/[database_id]/user/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { and, eq } from "drizzle-orm";
import { NextResponse } from "next/server";
import withDatabaseOperation from "@/lib/with-database-ops";
import { database_user_role, database_role } from "@/db/schema";
import { db } from "@/db";
import zod from "zod";

export const runtime = "edge";

const assignUserSchema = zod.object({
user: zod.string(),
role: zod.string(),
});

const deleteUserSchema = zod.object({
user: zod.string(),
});

async function getUserRoleInfo(userId: string, databaseId: string) {
return db.query.database_user_role.findFirst({
where: and(
eq(database_user_role.databaseId, databaseId),
eq(database_user_role.userId, userId)
),
});
}

async function checkUserHasOwnerRole(roleId: string) {
const roleInfo = await db.query.database_role.findFirst({
where: eq(database_role.id, roleId),
});
return roleInfo?.isOwner;
}

export const POST = withDatabaseOperation(
async ({ user, body, database, permission }) => {
const parsed = assignUserSchema.safeParse(body);

if (!parsed.success) {
return NextResponse.json(
{
success: false,
error: "Invalid request body",
},
{ status: 400 }
);
}

if (!permission.isOwner) {
return NextResponse.json(
{
success: false,
error: "You do not have permission",
},
{ status: 403 }
);
}

const { user: userId, role: roleId } = parsed.data;

const userExists = await db.query.user.findFirst({
where: (fields, op) => op.eq(fields.id, userId),
});

if (!userExists) {
return NextResponse.json(
{ success: false, error: "User does not exist" },
{ status: 404 }
);
}

const roleInfo = await db.query.database_role.findFirst({
where: (fields) => eq(fields.id, roleId),
});

if (roleInfo?.isOwner && database.userId !== user.id) {
return NextResponse.json(
{ success: false, error: "Owner cannot reassign other owner" },
{ status: 403 }
);
}

const userRole = await getUserRoleInfo(userId, database.id);

if (userRole) {
// Update the role if it exists
await db
.update(database_user_role)
.set({ roleId })
.where(
and(
eq(database_user_role.databaseId, database.id),
eq(database_user_role.userId, userId)
)
);
} else {
await db.insert(database_user_role).values({
userId,
databaseId: database.id,
roleId,
createdBy: user.id,
createdAt: Date.now(),
});
}

return NextResponse.json({ success: true });
}
);

export const DELETE = withDatabaseOperation(
async ({ body, database, permission, user }) => {
const parsed = deleteUserSchema.safeParse(body);

if (!parsed.success) {
return NextResponse.json(
{
success: false,
error: "Invalid request body",
},
{ status: 400 }
);
}

if (!permission.isOwner) {
return NextResponse.json(
{
success: false,
error: "You do not have permission",
},
{ status: 403 }
);
}

const { user: userId } = parsed.data;

const userRole = await getUserRoleInfo(userId, database.id);

if (!userRole || !userRole.roleId) {
return NextResponse.json(
{ success: false, error: "User is not assigned to this database" },
{ status: 404 }
);
}

const userHasOwnerRole = await checkUserHasOwnerRole(userRole.roleId);

if (userHasOwnerRole && database.userId !== user.id) {
return NextResponse.json(
{
success: false,
error: "Only the database creator can remove an owner",
},
{ status: 403 }
);
}

await db
.delete(database_user_role)
.where(
and(
eq(database_user_role.databaseId, database.id),
eq(database_user_role.userId, userId)
)
);

return NextResponse.json({ success: true });
}
);

29 changes: 29 additions & 0 deletions src/app/api/database/[database_id]/users/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { db } from "@/db";
import { database_role, database_user_role } from "@/db/schema-database";
import { user } from "@/db/schema-user";
import withDatabaseOperation from "@/lib/with-database-ops";
import { eq } from "drizzle-orm";
import { NextResponse } from "next/server";

export const GET = withDatabaseOperation(async ({ database: databaseInfo }) => {
const users = await db
.select({
id: user.id,
name: user.name,
role: {
id: database_role.id,
name: database_role.name,
},
createdAt: database_user_role.createdAt,
assignedBy: {
id: user.id,
name: user.name,
},
})
.from(database_user_role)
.innerJoin(user, eq(database_user_role.userId, user.id))
.innerJoin(database_role, eq(database_user_role.roleId, database_role.id))
.where(eq(database_user_role.databaseId, databaseInfo.id));

return NextResponse.json({ users });
});

0 comments on commit ddda356

Please sign in to comment.