Refactor to accommodate authentication, transaction and pagination states together (#4636)

* fix: refactor to accommodate authentication, transaction and pagination together into ctx.state

* feat: allow passing response type to APIContext
This commit is contained in:
Apoorv Mishra
2023-01-04 23:51:44 +05:30
committed by GitHub
parent bb568d2e62
commit f4461573de
31 changed files with 753 additions and 675 deletions

View File

@@ -16,14 +16,12 @@ import { ValidationError } from "@server/errors";
import logger from "@server/logging/Logger";
import auth from "@server/middlewares/authentication";
import { rateLimiter } from "@server/middlewares/rateLimiter";
import {
transaction,
TransactionContext,
} from "@server/middlewares/transaction";
import { transaction } from "@server/middlewares/transaction";
import { Event, User, Team } from "@server/models";
import { UserFlag, UserRole } from "@server/models/User";
import { can, authorize } from "@server/policies";
import { presentUser, presentPolicies } from "@server/presenters";
import { APIContext } from "@server/types";
import { RateLimiterStrategy } from "@server/utils/RateLimiter";
import {
assertIn,
@@ -39,7 +37,7 @@ import pagination from "./middlewares/pagination";
const router = new Router();
const emailEnabled = !!(env.SMTP_HOST || env.ENVIRONMENT === "development");
router.post("users.list", auth(), pagination(), async (ctx) => {
router.post("users.list", auth(), pagination(), async (ctx: APIContext) => {
let { direction } = ctx.request.body;
const { sort = "createdAt", query, filter, ids } = ctx.request.body;
if (direction !== "ASC") {
@@ -55,7 +53,7 @@ router.post("users.list", auth(), pagination(), async (ctx) => {
);
}
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
let where: WhereOptions<User> = {
teamId: actor.teamId,
};
@@ -158,8 +156,8 @@ router.post("users.list", auth(), pagination(), async (ctx) => {
};
});
router.post("users.count", auth(), async (ctx) => {
const { user } = ctx.state;
router.post("users.count", auth(), async (ctx: APIContext) => {
const { user } = ctx.state.auth;
const counts = await User.getCounts(user.teamId);
ctx.body = {
@@ -169,9 +167,9 @@ router.post("users.count", auth(), async (ctx) => {
};
});
router.post("users.info", auth(), async (ctx) => {
router.post("users.info", auth(), async (ctx: APIContext) => {
const { id } = ctx.request.body;
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
const user = id ? await User.findByPk(id) : actor;
authorize(actor, "read", user);
const includeDetails = can(actor, "readDetails", user);
@@ -184,57 +182,53 @@ router.post("users.info", auth(), async (ctx) => {
};
});
router.post(
"users.update",
auth(),
transaction(),
async (ctx: TransactionContext) => {
const { user, transaction } = ctx.state;
const { name, avatarUrl, language, preferences } = ctx.request.body;
if (name) {
user.name = name;
}
if (avatarUrl) {
user.avatarUrl = avatarUrl;
}
if (language) {
user.language = language;
}
if (preferences) {
assertKeysIn(preferences, UserPreference);
router.post("users.update", auth(), transaction(), async (ctx: APIContext) => {
const { auth, transaction } = ctx.state;
const { user } = auth;
const { name, avatarUrl, language, preferences } = ctx.request.body;
if (name) {
user.name = name;
}
if (avatarUrl) {
user.avatarUrl = avatarUrl;
}
if (language) {
user.language = language;
}
if (preferences) {
assertKeysIn(preferences, UserPreference);
for (const value of Object.values(UserPreference)) {
if (has(preferences, value)) {
assertBoolean(preferences[value]);
user.setPreference(value, preferences[value]);
}
for (const value of Object.values(UserPreference)) {
if (has(preferences, value)) {
assertBoolean(preferences[value]);
user.setPreference(value, preferences[value]);
}
}
await user.save({ transaction });
await Event.create(
{
name: "users.update",
actorId: user.id,
userId: user.id,
teamId: user.teamId,
ip: ctx.request.ip,
},
{ transaction }
);
ctx.body = {
data: presentUser(user, {
includeDetails: true,
}),
};
}
);
await user.save({ transaction });
await Event.create(
{
name: "users.update",
actorId: user.id,
userId: user.id,
teamId: user.teamId,
ip: ctx.request.ip,
},
{ transaction }
);
ctx.body = {
data: presentUser(user, {
includeDetails: true,
}),
};
});
// Admin specific
router.post("users.promote", auth(), async (ctx) => {
router.post("users.promote", auth(), async (ctx: APIContext) => {
const userId = ctx.request.body.id;
const teamId = ctx.state.user.teamId;
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
const teamId = actor.teamId;
assertPresent(userId, "id is required");
const user = await User.findByPk(userId);
authorize(actor, "promote", user);
@@ -260,10 +254,10 @@ router.post("users.promote", auth(), async (ctx) => {
};
});
router.post("users.demote", auth(), async (ctx) => {
router.post("users.demote", auth(), async (ctx: APIContext) => {
const userId = ctx.request.body.id;
let { to } = ctx.request.body;
const actor = ctx.state.user as User;
const actor = ctx.state.auth.user;
assertPresent(userId, "id is required");
to = (to === "viewer" ? "viewer" : "member") as UserRole;
@@ -289,9 +283,9 @@ router.post("users.demote", auth(), async (ctx) => {
};
});
router.post("users.suspend", auth(), async (ctx) => {
router.post("users.suspend", auth(), async (ctx: APIContext) => {
const userId = ctx.request.body.id;
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
assertPresent(userId, "id is required");
const user = await User.findByPk(userId, {
rejectOnEmpty: true,
@@ -313,9 +307,9 @@ router.post("users.suspend", auth(), async (ctx) => {
};
});
router.post("users.activate", auth(), async (ctx) => {
router.post("users.activate", auth(), async (ctx: APIContext) => {
const userId = ctx.request.body.id;
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
assertPresent(userId, "id is required");
const user = await User.findByPk(userId, {
rejectOnEmpty: true,
@@ -341,10 +335,10 @@ router.post(
"users.invite",
auth(),
rateLimiter(RateLimiterStrategy.TenPerHour),
async (ctx) => {
async (ctx: APIContext) => {
const { invites } = ctx.request.body;
assertArray(invites, "invites must be an array");
const { user } = ctx.state;
const { user } = ctx.state.auth;
const team = await Team.findByPk(user.teamId);
authorize(user, "inviteUser", team);
@@ -367,10 +361,10 @@ router.post(
"users.resendInvite",
auth(),
transaction(),
async (ctx: TransactionContext) => {
async (ctx: APIContext) => {
const { id } = ctx.request.body;
const actor = ctx.state.user;
const { transaction } = ctx.state;
const { auth, transaction } = ctx.state;
const actor = auth.user;
const user = await User.findByPk(id, {
lock: transaction.LOCK.UPDATE,
@@ -413,8 +407,8 @@ router.post(
"users.requestDelete",
auth(),
rateLimiter(RateLimiterStrategy.FivePerHour),
async (ctx) => {
const { user } = ctx.state;
async (ctx: APIContext) => {
const { user } = ctx.state.auth;
authorize(user, "delete", user);
if (emailEnabled) {
@@ -434,9 +428,9 @@ router.post(
"users.delete",
auth(),
rateLimiter(RateLimiterStrategy.TenPerHour),
async (ctx) => {
async (ctx: APIContext) => {
const { id, code = "" } = ctx.request.body;
const actor = ctx.state.user;
const actor = ctx.state.auth.user;
let user: User;
if (id) {