191 lines
5.3 KiB
TypeScript
191 lines
5.3 KiB
TypeScript
import type { Address, AuthUser } from "@orderops/shared";
|
|
import type { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
import { requireEmployeeUser } from "../auth/current-user";
|
|
import type { DatabaseContext } from "../db/database";
|
|
import { addOrderNote } from "./add-order-note";
|
|
import { cancelOrder } from "./cancel-order";
|
|
import { getOrder } from "./get-order";
|
|
import { listAuditEvents } from "./list-audit-events";
|
|
import { listOrders } from "./list-orders";
|
|
import { changeOrderItemQuantity } from "./update-order-item";
|
|
import { changeShippingAddress } from "./update-shipping-address";
|
|
|
|
interface OrderRouteOptions {
|
|
db: DatabaseContext;
|
|
}
|
|
|
|
interface SearchQuery {
|
|
query?: string;
|
|
}
|
|
|
|
interface IdParams {
|
|
orderId: string;
|
|
}
|
|
|
|
interface OrderItemParams extends IdParams {
|
|
itemId: string;
|
|
}
|
|
|
|
interface QuantityBody {
|
|
quantity: number;
|
|
}
|
|
|
|
interface CancelBody {
|
|
reason?: string;
|
|
}
|
|
|
|
interface NoteBody {
|
|
body: string;
|
|
}
|
|
|
|
type AuthenticatedRequest = FastifyRequest & { currentUser: AuthUser };
|
|
|
|
export const registerOrderRoutes: FastifyPluginAsync<OrderRouteOptions> = async (app, options) => {
|
|
app.addHook("preHandler", async (request, reply) => {
|
|
const user = await requireEmployeeUser(options.db, request, reply);
|
|
|
|
if (!user) {
|
|
return reply;
|
|
}
|
|
|
|
(request as AuthenticatedRequest).currentUser = user;
|
|
});
|
|
|
|
app.get<{ Querystring: SearchQuery }>("/orders", {
|
|
schema: {
|
|
querystring: {
|
|
type: "object",
|
|
properties: {
|
|
query: { type: "string" }
|
|
}
|
|
}
|
|
}
|
|
}, async (request) => listOrders(options.db, request.query.query));
|
|
|
|
app.get<{ Params: IdParams }>("/orders/:orderId", async (request, reply) => {
|
|
const order = await getOrder(options.db, request.params.orderId);
|
|
|
|
if (!order) {
|
|
return reply.code(404).send({ message: "Order not found." });
|
|
}
|
|
|
|
return order;
|
|
});
|
|
|
|
app.get<{ Params: IdParams }>("/orders/:orderId/audit-events", async (request) => listAuditEvents(options.db, request.params.orderId));
|
|
|
|
app.post<{ Params: IdParams; Body: NoteBody }>("/orders/:orderId/notes", {
|
|
schema: {
|
|
body: {
|
|
type: "object",
|
|
required: ["body"],
|
|
properties: {
|
|
body: { type: "string", minLength: 1, maxLength: 1999 }
|
|
}
|
|
}
|
|
}
|
|
}, async (request, reply) => {
|
|
const currentUser = (request as AuthenticatedRequest).currentUser;
|
|
const result = await addOrderNote(options.db, {
|
|
orderId: request.params.orderId,
|
|
authorUserId: currentUser.id,
|
|
body: request.body.body
|
|
});
|
|
|
|
if (result.statusCode !== 200) {
|
|
return reply.code(result.statusCode).send({ message: result.message });
|
|
}
|
|
|
|
return result.note;
|
|
});
|
|
|
|
app.post<{ Params: OrderItemParams; Body: QuantityBody }>("/orders/:orderId/items/:itemId/quantity", {
|
|
schema: {
|
|
body: {
|
|
type: "object",
|
|
required: ["quantity"],
|
|
properties: {
|
|
quantity: { type: "number" }
|
|
}
|
|
}
|
|
}
|
|
}, async (request, reply) => {
|
|
const currentUser = (request as AuthenticatedRequest).currentUser;
|
|
const result = await changeOrderItemQuantity(options.db, {
|
|
orderId: request.params.orderId,
|
|
itemId: request.params.itemId,
|
|
quantity: request.body.quantity,
|
|
actor: currentUser.username
|
|
});
|
|
|
|
if (result.statusCode !== 200) {
|
|
return reply.code(result.statusCode).send({ message: result.message });
|
|
}
|
|
|
|
return result.order;
|
|
});
|
|
|
|
app.post<{ Params: IdParams; Body: Address }>("/orders/:orderId/shipping-address", {
|
|
schema: {
|
|
body: {
|
|
type: "object",
|
|
required: ["fullName", "line1", "city", "state", "postalCode", "country"],
|
|
properties: {
|
|
fullName: { type: "string", minLength: 1 },
|
|
line1: { type: "string", minLength: 1 },
|
|
line2: { type: ["string", "null"] },
|
|
city: { type: "string", minLength: 1 },
|
|
state: { type: "string", minLength: 1 },
|
|
postalCode: { type: "string", minLength: 1 },
|
|
country: { type: "string", minLength: 1 }
|
|
}
|
|
}
|
|
}
|
|
}, async (request, reply) => {
|
|
const currentUser = (request as AuthenticatedRequest).currentUser;
|
|
const result = await changeShippingAddress(options.db, {
|
|
orderId: request.params.orderId,
|
|
address: {
|
|
fullName: request.body.fullName,
|
|
line1: request.body.line1,
|
|
line2: request.body.line2 ?? null,
|
|
city: request.body.city,
|
|
state: request.body.state,
|
|
postalCode: request.body.postalCode,
|
|
country: request.body.country
|
|
},
|
|
actor: currentUser.username
|
|
});
|
|
|
|
if (result.statusCode !== 200) {
|
|
return reply.code(result.statusCode).send({ message: result.message });
|
|
}
|
|
|
|
return result.order;
|
|
});
|
|
|
|
app.post<{ Params: IdParams; Body: CancelBody }>("/orders/:orderId/cancel", {
|
|
schema: {
|
|
body: {
|
|
type: "object",
|
|
properties: {
|
|
reason: { type: "string" }
|
|
}
|
|
}
|
|
}
|
|
}, async (request, reply) => {
|
|
const currentUser = (request as AuthenticatedRequest).currentUser;
|
|
const result = await cancelOrder(options.db, {
|
|
orderId: request.params.orderId,
|
|
reason: request.body.reason,
|
|
actor: currentUser.username
|
|
});
|
|
|
|
if (result.statusCode !== 200) {
|
|
return reply.code(result.statusCode).send({ message: result.message });
|
|
}
|
|
|
|
return result.order;
|
|
});
|
|
};
|