Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ ACCESS_SECRET=7RK3RKuABKYGUQkeAt8nXTMBk2b5G4BJm4javB1DUiP=
REFRESH_PK="ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDAK2dZyfoC7rMBkznEN+oMU9bPUuwfPLZ6EoF53tb5/cWPFnop6TGhyyT0hKsYFMkc="
MINIATURE_MAX_HEIGHT_PX=300
MINIATURE_MAX_PROCESSING_TIME_S=5
GOOGLE_APPLICATION_CREDENTIALS=./firebase_service_account_key.json
GOOGLE_APPLICATION_CREDENTIALS=./firebase_service_account_key.json
53 changes: 53 additions & 0 deletions app/controllers/v1/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ const permissionChangeValidator = vine.compile(
}),
);

const roleChangeValidator = vine.compile(
vine.object({
userId: vine.number(),
roles: vine.array(vine.string()),
}),
);

const listPermissionsValidator = vine.compile(
vine.object({
userId: vine.number(),
Expand All @@ -57,6 +64,8 @@ export default class PermissionsController extends BaseController {
router.post("/allow", [controller, "allow"]).as("allow");
router.post("/revoke", [controller, "revoke"]).as("revoke");
router.get("/list", [controller, "listUserPermissions"]).as("list");
router.post("/roles/assign", [controller, "assignRoles"]).as("assignRoles");
router.post("/roles/revoke", [controller, "revokeRoles"]).as("revokeRoles");
}

async allow({ request, auth }: HttpContext) {
Expand Down Expand Up @@ -162,4 +171,48 @@ export default class PermissionsController extends BaseController {
permissions,
};
}

async assignRoles({ request, auth }: HttpContext) {
await this.requireSuperUser(auth);

const { userId, roles } = await request.validateUsing(roleChangeValidator);

const targetUser = await User.findOrFail(userId).addErrorContext(
() => `User with id ${userId} not found`,
);

const manager = Acl.model(targetUser);

for (const role of roles) {
await manager
.assignRole(role)
.addErrorContext(
() => `Failed to assign role ${role} to user ${targetUser.id}`,
);
}

return { success: true };
}

async revokeRoles({ request, auth }: HttpContext) {
await this.requireSuperUser(auth);

const { userId, roles } = await request.validateUsing(roleChangeValidator);

const targetUser = await User.findOrFail(userId).addErrorContext(
() => `User with id ${userId} not found`,
);

const manager = Acl.model(targetUser);

for (const role of roles) {
await manager
.revokeRole(role)
.addErrorContext(
() => `Failed to revoke role ${role} from user ${targetUser.id}`,
);
}

return { success: true };
}
}
141 changes: 141 additions & 0 deletions app/controllers/v1/users.ts
Comment thread
lolakk05 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import vine from "@vinejs/vine";

import type { HttpContext } from "@adonisjs/core/http";
import router from "@adonisjs/core/services/router";
import type { Constructor, LazyImport } from "@adonisjs/core/types/http";
import db from "@adonisjs/lucid/services/db";

import User from "#app/models/user";

import BaseController from "../base_controller.js";

//should check if email is unique and more conditions on password
const createUserValidator = vine.compile(
vine.object({
fullName: vine.string().optional(),
email: vine.string().email(),
password: vine.string().minLength(8),
}),
);

const updateUserValidator = vine.compile(
vine.object({
fullName: vine.string().optional(),
email: vine.string().email().optional(),
password: vine.string().minLength(8).optional(),
}),
);

const paginationValidator = vine.compile(
vine.object({
page: vine.number().min(1).optional(),
limit: vine.number().min(1).max(100).optional(),
}),
);

const userIdParamValidator = vine.compile(
vine.object({
id: vine.number(),
}),
);

export default class UsersController extends BaseController {
$configureRoutes(controller: LazyImport<Constructor<UsersController>>) {
router.get("/", [controller, "findAll"]).as("users.list");
router.get("/:id", [controller, "findOne"]).as("users.show");
router.delete("/:id", [controller, "delete"]).as("users.delete");
router.patch("/:id", [controller, "update"]).as("users.update");
router.post("/", [controller, "create"]).as("users.create");
}

async findAll({ request, auth }: HttpContext) {
await this.requireSuperUser(auth);

const { page = 1, limit = 10 } =
await request.validateUsing(paginationValidator);

const users = await User.query()
.select("id", "fullName", "email")
.paginate(page, limit);

return { data: users };
}

async findOne({ request, auth }: HttpContext) {
const user = await request.validateUsing(userIdParamValidator);

await this.requireSuperUser(auth);

const targetUser = await User.query()
.select("id", "fullName", "email")
.where("id", user.id)
.firstOrFail()
.addErrorContext(() => `User with id ${user.id} not found`);

return { data: targetUser };
}

async delete({ request, auth }: HttpContext) {
const user = await request.validateUsing(userIdParamValidator);

await this.requireSuperUser(auth);
await db.transaction(async (trx) => {
const targetUser = await User.findOrFail(user.id, {
client: trx,
}).addErrorContext(() => `User with id ${user.id} not found`);

await targetUser.delete();
});

return { success: true };
}

async update({ request, auth }: HttpContext) {
const user = await request.validateUsing(userIdParamValidator);

await this.requireSuperUser(auth);

const payload = await request.validateUsing(updateUserValidator);

const updatedUser = await db.transaction(async (trx) => {
const targetUser = await User.query({ client: trx })
.where("id", user.id)
.firstOrFail()
.addErrorContext(() => `User with id ${user.id} not found`);

targetUser.merge(payload);
await targetUser.save();

return targetUser;
});

return {
success: true,
data: {
id: updatedUser.id,
fullName: updatedUser.fullName,
email: updatedUser.email,
},
};
}

async create({ request, auth }: HttpContext) {
await this.requireSuperUser(auth);

const payload = await request.validateUsing(createUserValidator);

const newUser = await db.transaction(async (trx) => {
const user = await User.create(payload, { client: trx });
return user;
});

return {
success: true,
data: {
id: newUser.id,
fullName: newUser.fullName,
email: newUser.email,
},
};
}
}
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading