Files
outline/server/routes/api/pins.ts
Apoorv Mishra f4461573de 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
2023-01-04 23:51:44 +05:30

159 lines
3.9 KiB
TypeScript

import Router from "koa-router";
import { Sequelize, Op } from "sequelize";
import pinCreator from "@server/commands/pinCreator";
import pinDestroyer from "@server/commands/pinDestroyer";
import pinUpdater from "@server/commands/pinUpdater";
import auth from "@server/middlewares/authentication";
import { Collection, Document, Pin } from "@server/models";
import { authorize } from "@server/policies";
import {
presentPin,
presentDocument,
presentPolicies,
} from "@server/presenters";
import { APIContext } from "@server/types";
import { assertUuid, assertIndexCharacters } from "@server/validation";
import pagination from "./middlewares/pagination";
const router = new Router();
router.post("pins.create", auth(), async (ctx: APIContext) => {
const { documentId, collectionId } = ctx.request.body;
const { index } = ctx.request.body;
assertUuid(documentId, "documentId is required");
const { user } = ctx.state.auth;
const document = await Document.findByPk(documentId, {
userId: user.id,
});
authorize(user, "read", document);
if (collectionId) {
const collection = await Collection.scope({
method: ["withMembership", user.id],
}).findByPk(collectionId);
authorize(user, "update", collection);
authorize(user, "pin", document);
} else {
authorize(user, "pinToHome", document);
}
if (index) {
assertIndexCharacters(index);
}
const pin = await pinCreator({
user,
documentId,
collectionId,
ip: ctx.request.ip,
index,
});
ctx.body = {
data: presentPin(pin),
policies: presentPolicies(user, [pin]),
};
});
router.post("pins.list", auth(), pagination(), async (ctx: APIContext) => {
const { collectionId } = ctx.request.body;
const { user } = ctx.state.auth;
const [pins, collectionIds] = await Promise.all([
Pin.findAll({
where: {
...(collectionId
? { collectionId }
: { collectionId: { [Op.is]: null } }),
teamId: user.teamId,
},
order: [
Sequelize.literal('"pin"."index" collate "C"'),
["updatedAt", "DESC"],
],
offset: ctx.state.pagination.offset,
limit: ctx.state.pagination.limit,
}),
user.collectionIds(),
]);
const documents = await Document.defaultScopeWithUser(user.id).findAll({
where: {
id: pins.map((pin) => pin.documentId),
collectionId: collectionIds,
},
});
const policies = presentPolicies(user, [...documents, ...pins]);
ctx.body = {
pagination: ctx.state.pagination,
data: {
pins: pins.map(presentPin),
documents: await Promise.all(
documents.map((document: Document) => presentDocument(document))
),
},
policies,
};
});
router.post("pins.update", auth(), async (ctx: APIContext) => {
const { id, index } = ctx.request.body;
assertUuid(id, "id is required");
assertIndexCharacters(index);
const { user } = ctx.state.auth;
let pin = await Pin.findByPk(id, { rejectOnEmpty: true });
const document = await Document.findByPk(pin.documentId, {
userId: user.id,
});
if (pin.collectionId) {
authorize(user, "pin", document);
} else {
authorize(user, "update", pin);
}
pin = await pinUpdater({
user,
pin,
ip: ctx.request.ip,
index,
});
ctx.body = {
data: presentPin(pin),
policies: presentPolicies(user, [pin]),
};
});
router.post("pins.delete", auth(), async (ctx: APIContext) => {
const { id } = ctx.request.body;
assertUuid(id, "id is required");
const { user } = ctx.state.auth;
const pin = await Pin.findByPk(id, { rejectOnEmpty: true });
const document = await Document.findByPk(pin.documentId, {
userId: user.id,
});
if (pin.collectionId) {
authorize(user, "unpin", document);
} else {
authorize(user, "delete", pin);
}
await pinDestroyer({ user, pin, ip: ctx.request.ip });
ctx.body = {
success: true,
};
});
export default router;