@premieroctet/next-admin
Version:
Next-Admin provides a customizable and turnkey admin dashboard for applications built with Next.js and powered by the Prisma ORM. It aims to simplify the development process by providing a turnkey admin system that can be easily integrated into your proje
191 lines (190 loc) • 7.17 kB
JavaScript
import { createRouter } from "next-connect";
import { HookError } from "./exceptions/HookError.mjs";
import { handleOptionsSearch } from "./handlers/options.mjs";
import { deleteResource, submitResource } from "./handlers/resources.mjs";
import { Permission } from "./types.mjs";
import { hasPermission } from "./utils/permissions.mjs";
import { getRawData } from "./utils/prisma.mjs";
import { formatId, getFormDataValues, getJsonBody, getResourceFromParams, getResources } from "./utils/server.mjs";
import { getSchema, initGlobals } from "./utils/globals.mjs";
const createHandler = ({ apiBasePath, options, prisma, paramKey = "nextadmin", onRequest })=>{
const router = createRouter();
router.use(async (req, res, next)=>{
await initGlobals();
return next();
});
if (onRequest) router.use(onRequest);
router.get(`${apiBasePath}/:model/raw`, async (req, res)=>{
const resources = getResources(options);
const resource = getResourceFromParams([
req.query[paramKey][0]
], resources);
if (!resource) return res.status(404).json({
error: "Resource not found"
});
let ids = req.query.ids;
ids = Array.isArray(ids) ? ids.map((id)=>formatId(resource, id)) : ids?.split(",").map((id)=>formatId(resource, id));
const depth = req.query.depth;
if (depth && isNaN(Number(depth))) return res.status(400).json({
error: "Depth should be a number"
});
const data = await getRawData({
prisma,
resource,
resourceIds: ids,
maxDepth: depth ? Number(depth) : void 0
});
return res.json(data);
}).post(`${apiBasePath}/:model/actions/:id`, async (req, res)=>{
const resources = getResources(options);
const id = req.query[paramKey].at(-1);
const resource = getResourceFromParams([
req.query[paramKey][0]
], resources);
if (!resource) return res.status(404).json({
type: "error",
message: "Resource not found"
});
const modelAction = options?.model?.[resource]?.actions?.find((action)=>action.id === id);
if (!modelAction) return res.status(404).json({
type: "error",
message: "Action not found"
});
if ("type" in modelAction && "dialog" === modelAction.type) return res.status(404).json({
type: "error",
message: "Action not found"
});
let body;
try {
body = await getJsonBody(req);
} catch {
return res.status(400).json({
type: "error",
message: "Invalid JSON body"
});
}
try {
const result = await modelAction.action(body);
return res.json(result ?? null);
} catch (e) {
return res.status(500).json({
type: "error",
message: e.message
});
}
}).post(`${apiBasePath}/options`, async (req, res)=>{
let body;
try {
body = await getJsonBody(req);
} catch {
return res.status(400).json({
error: "Invalid JSON body"
});
}
const data = await handleOptionsSearch(body, prisma, options);
return res.json(data);
}).post(`${apiBasePath}/:model/:id?`, async (req, res)=>{
const resources = getResources(options);
const resource = getResourceFromParams([
req.query[paramKey][0]
], resources);
if (!resource) return res.status(404).json({
error: "Resource not found"
});
const body = await getFormDataValues(req);
const id = 2 === req.query[paramKey].length ? formatId(resource, req.query[paramKey].at(-1)) : void 0;
const editOptions = options?.model?.[resource]?.edit;
const mode = id ? "edit" : "create";
try {
const transformedBody = await editOptions?.hooks?.beforeDb?.(body, mode, req);
let response = await submitResource({
prisma,
resource,
body: transformedBody ?? body,
id,
options,
schema: getSchema()
});
if (response.error) return res.status(400).json({
error: response.error,
validation: response.validation
});
response = await editOptions?.hooks?.afterDb?.(response, mode, req) ?? response;
return res.status(id ? 200 : 201).json(response);
} catch (e) {
if (e instanceof HookError) return res.status(e.status).json(e.data);
return res.status(500).json({
error: e.message
});
}
}).delete(`${apiBasePath}/:model/:id`, async (req, res)=>{
const resources = getResources(options);
const resource = getResourceFromParams([
req.query[paramKey][0]
], resources);
if (!resource) return res.status(404).json({
error: "Resource not found"
});
if (!hasPermission(options?.model?.[resource], Permission.DELETE)) return res.status(403).json({
error: "You don't have permission to delete this resource"
});
try {
const deleted = await deleteResource({
body: [
req.query[paramKey][1]
],
prisma,
resource,
modelOptions: options?.model?.[resource]
});
if (!deleted) throw new Error("Deletion failed");
return res.json({
ok: true
});
} catch (e) {
return res.status(500).json({
error: e.message
});
}
}).delete(`${apiBasePath}/:model`, async (req, res)=>{
const resources = getResources(options);
const resource = getResourceFromParams([
req.query[paramKey][0]
], resources);
if (!resource) return res.status(404).json({
error: "Resource not found"
});
if (!hasPermission(options?.model?.[resource], Permission.DELETE)) return res.status(403).json({
error: "You don't have permission to delete this resource"
});
let body;
try {
body = await getJsonBody(req);
} catch {
return res.status(400).json({
error: "Invalid JSON body"
});
}
try {
await deleteResource({
body,
prisma,
resource,
modelOptions: options?.model?.[resource]
});
return res.json({
ok: true
});
} catch (e) {
return res.status(500).json({
error: e.message
});
}
});
const executeRouteHandler = (req, res)=>router.run(req, res);
return {
run: executeRouteHandler,
router
};
};
export { createHandler };