-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add collaboration and manage permission (#64)
* feat: add basic collaboration * feat: add id below user
- Loading branch information
Showing
25 changed files
with
778 additions
and
376 deletions.
There are no files selected for viewing
112 changes: 112 additions & 0 deletions
112
src/app/api/database/[database_id]/ops/handle-assign-user.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { HttpStatus } from "@/constants/http-status"; | ||
import { db } from "@/db"; | ||
import { database_role, database_user_role } from "@/db/schema-database"; | ||
import { user as userTable } from "@/db/schema-user"; | ||
import { ApiError } from "@/lib/api-error"; | ||
import { RequestDatabaseAssignUser } from "@/lib/api/api-database-request"; | ||
import { DatabaseOperationHandler } from "@/lib/with-database-ops"; | ||
import { and, eq } from "drizzle-orm"; | ||
import { NextResponse } from "next/server"; | ||
|
||
const handleAssignUser: DatabaseOperationHandler< | ||
RequestDatabaseAssignUser | ||
> = async ({ database: databaseInfo, body, user, permission }) => { | ||
const { roleId, userId } = body; | ||
|
||
// Validate if user input all fields | ||
if (!userId || !roleId) | ||
throw new ApiError({ | ||
message: "Please provide user and role", | ||
status: HttpStatus.BAD_REQUEST, | ||
}); | ||
|
||
if (!permission.isOwner) { | ||
throw new ApiError({ | ||
message: "Only owner can assign other user access", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
if (user.id === userId) { | ||
throw new ApiError({ | ||
message: "Cannot reassign yourself", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
// Check if role and user are valid | ||
const assignedRole = await db.query.database_role.findFirst({ | ||
where: eq(database_role.id, roleId), | ||
}); | ||
|
||
const assignedUser = await db.query.user.findFirst({ | ||
where: eq(userTable.id, userId), | ||
}); | ||
|
||
if (!assignedRole || assignedRole.databaseId !== databaseInfo.id) { | ||
throw new ApiError({ | ||
message: "Role does not exist in this database", | ||
status: HttpStatus.BAD_REQUEST, | ||
}); | ||
} | ||
|
||
if (!assignedUser) { | ||
throw new ApiError({ | ||
message: "User does not exist", | ||
status: HttpStatus.BAD_REQUEST, | ||
}); | ||
} | ||
|
||
const isOriginalOwner = user.id === databaseInfo.userId; | ||
if (assignedRole.isOwner && !isOriginalOwner) { | ||
throw new ApiError({ | ||
message: "Only original owner can assign other owner", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
// Check if user already have previous role | ||
const existingRole = ( | ||
await db | ||
.select() | ||
.from(database_user_role) | ||
.innerJoin(database_role, eq(database_role.id, database_user_role.roleId)) | ||
.where( | ||
and( | ||
eq(database_user_role.databaseId, databaseInfo.id), | ||
eq(database_user_role.userId, userId) | ||
) | ||
) | ||
)[0]; | ||
|
||
if (!existingRole) { | ||
await db.insert(database_user_role).values({ | ||
userId, | ||
databaseId: databaseInfo.id, | ||
roleId, | ||
createdBy: user.id, | ||
createdAt: Date.now(), | ||
}); | ||
} else { | ||
if (existingRole.database_role.isOwner && !isOriginalOwner) { | ||
throw new ApiError({ | ||
message: "Only original owner can reassign other owner", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
await db | ||
.update(database_user_role) | ||
.set({ roleId, createdAt: Date.now(), createdBy: user.id }) | ||
.where( | ||
and( | ||
eq(database_user_role.databaseId, databaseInfo.id), | ||
eq(database_user_role.userId, userId) | ||
) | ||
); | ||
} | ||
|
||
return NextResponse.json({ success: true }); | ||
}; | ||
|
||
export default handleAssignUser; |
78 changes: 78 additions & 0 deletions
78
src/app/api/database/[database_id]/ops/handle-delete-user.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { HttpStatus } from "@/constants/http-status"; | ||
import { db } from "@/db"; | ||
import { database_role, database_user_role } from "@/db/schema-database"; | ||
import { ApiError } from "@/lib/api-error"; | ||
import { RequestDatabaseDeleteUser } from "@/lib/api/api-database-request"; | ||
import { DatabaseOperationHandler } from "@/lib/with-database-ops"; | ||
import { and, eq } from "drizzle-orm"; | ||
import { NextResponse } from "next/server"; | ||
|
||
const handleDeleteUser: DatabaseOperationHandler< | ||
RequestDatabaseDeleteUser | ||
> = async ({ database: databaseInfo, body, user, permission }) => { | ||
const { userId } = body; | ||
|
||
// Validate if user input all fields | ||
if (!userId) | ||
throw new ApiError({ | ||
message: "Please provide user and role", | ||
status: HttpStatus.BAD_REQUEST, | ||
}); | ||
|
||
if (!permission.isOwner) { | ||
throw new ApiError({ | ||
message: "Only owner can delete other user access", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
if (user.id === userId) { | ||
throw new ApiError({ | ||
message: "Cannot delete yourself", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
const isOriginalOwner = user.id === databaseInfo.userId; | ||
|
||
// Check if user already have previous role | ||
const existingRole = ( | ||
await db | ||
.select() | ||
.from(database_user_role) | ||
.innerJoin(database_role, eq(database_role.id, database_user_role.roleId)) | ||
.where( | ||
and( | ||
eq(database_user_role.databaseId, databaseInfo.id), | ||
eq(database_user_role.userId, userId) | ||
) | ||
) | ||
)[0]; | ||
|
||
if (!existingRole) { | ||
throw new ApiError({ | ||
message: "User does not belong to this database", | ||
status: HttpStatus.BAD_REQUEST, | ||
}); | ||
} | ||
|
||
if (existingRole.database_role.isOwner && !isOriginalOwner) { | ||
throw new ApiError({ | ||
message: "Only original owner can delete other owner", | ||
status: HttpStatus.FORBIDDEN, | ||
}); | ||
} | ||
|
||
await db | ||
.delete(database_user_role) | ||
.where( | ||
and( | ||
eq(database_user_role.databaseId, databaseInfo.id), | ||
eq(database_user_role.userId, userId) | ||
) | ||
); | ||
|
||
return NextResponse.json({ success: true }); | ||
}; | ||
|
||
export default handleDeleteUser; |
20 changes: 20 additions & 0 deletions
20
src/app/api/database/[database_id]/ops/handle-role-list.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { db } from "@/db"; | ||
import { database_role } from "@/db/schema-database"; | ||
import { DatabaseOperationHandler } from "@/lib/with-database-ops"; | ||
import { eq } from "drizzle-orm"; | ||
import { NextResponse } from "next/server"; | ||
|
||
const handleRoleList: DatabaseOperationHandler = async ({ database }) => { | ||
const roles = await db.query.database_role.findMany({ | ||
where: eq(database_role.databaseId, database.id), | ||
}); | ||
|
||
return NextResponse.json({ | ||
roles: roles.map((role) => ({ | ||
id: role.id, | ||
name: role.name, | ||
})), | ||
}); | ||
}; | ||
|
||
export default handleRoleList; |
71 changes: 40 additions & 31 deletions
71
...api/database/[database_id]/users/route.ts → ...se/[database_id]/ops/handle-users-list.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,40 @@ | ||
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 runtime = "edge"; | ||
|
||
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 }); | ||
}); | ||
import { db } from "@/db"; | ||
import { user } from "@/db/schema"; | ||
import { database_role, database_user_role } from "@/db/schema-database"; | ||
import { DatabaseOperationHandler } from "@/lib/with-database-ops"; | ||
import { eq } from "drizzle-orm"; | ||
import { alias } from "drizzle-orm/sqlite-core"; | ||
import { NextResponse } from "next/server"; | ||
|
||
const handleUserList: DatabaseOperationHandler = async ({ | ||
database: databaseInfo, | ||
}) => { | ||
const assigned_user = alias(user, "assigned_user"); | ||
|
||
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: assigned_user.id, | ||
name: assigned_user.name, | ||
}, | ||
}) | ||
.from(database_user_role) | ||
.innerJoin(user, eq(database_user_role.userId, user.id)) | ||
.innerJoin( | ||
assigned_user, | ||
eq(database_user_role.createdBy, assigned_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 }); | ||
}; | ||
|
||
export default handleUserList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { NextResponse } from "next/server"; | ||
import withDatabaseOperation from "@/lib/with-database-ops"; | ||
import { RequestDatabaseBody } from "@/lib/api/api-database-request"; | ||
import handleRoleList from "./handle-role-list"; | ||
import handleUserList from "./handle-users-list"; | ||
import handleAssignUser from "./handle-assign-user"; | ||
import handleDeleteUser from "./handle-delete-user"; | ||
|
||
export const runtime = "edge"; | ||
|
||
export const POST = withDatabaseOperation<RequestDatabaseBody>(async function ( | ||
props | ||
) { | ||
const body = props.body; | ||
|
||
if (body.type === "roles") { | ||
return await handleRoleList(props); | ||
} else if (body.type === "users") { | ||
return await handleUserList(props); | ||
} else if (body.type === "assign-user") { | ||
return await handleAssignUser({ ...props, body }); | ||
} else if (body.type === "delete-user") { | ||
return await handleDeleteUser({ ...props, body }); | ||
} | ||
|
||
return NextResponse.json({ error: "Unknown command" }, { status: 500 }); | ||
}); |
Oops, something went wrong.