You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
2.9 KiB
112 lines
2.9 KiB
import AdminJS, { Router as AdminRouter } from 'adminjs';
|
|
import path from 'path';
|
|
import { WrongArgumentError } from './errors';
|
|
import { log } from './logger';
|
|
import { FastifyInstance } from 'fastify';
|
|
import { RouteHandlerMethod } from 'fastify/types/route';
|
|
import { fromPairs } from 'lodash';
|
|
import { readFile } from 'fs/promises';
|
|
import * as mime from 'mime-types';
|
|
import fastifyMultipart from '@fastify/multipart';
|
|
|
|
const INVALID_ADMIN_JS_INSTANCE =
|
|
'You have to pass an instance of AdminJS to the buildRouter() function';
|
|
|
|
const getFile = (fileField?: {
|
|
fieldname: string;
|
|
filename: string;
|
|
file: Record<string, unknown>;
|
|
}) => {
|
|
if (!fileField?.file) {
|
|
return null;
|
|
}
|
|
const { file, filename } = fileField;
|
|
file.name = filename;
|
|
return file;
|
|
};
|
|
|
|
export const buildRouter = async (
|
|
admin: AdminJS,
|
|
fastifyApp: FastifyInstance
|
|
): Promise<void> => {
|
|
const { assets } = AdminRouter;
|
|
if (admin?.constructor?.name !== 'AdminJS') {
|
|
throw new WrongArgumentError(INVALID_ADMIN_JS_INSTANCE);
|
|
}
|
|
|
|
await fastifyApp.register(fastifyMultipart, { attachFieldsToBody: true });
|
|
|
|
admin.initialize().then(() => {
|
|
log.debug('AdminJS: bundle ready');
|
|
});
|
|
|
|
const { routes } = AdminRouter;
|
|
|
|
routes.forEach(route => {
|
|
// we have to change routes defined in AdminJS from {recordId} to :recordId
|
|
const path = route.path.replace(/{/g, ':').replace(/}/g, '');
|
|
|
|
const handler: RouteHandlerMethod = async (request, reply) => {
|
|
const controller = new route.Controller(
|
|
{ admin },
|
|
request.session?.adminUser
|
|
);
|
|
const { params, query } = request;
|
|
const method = request.method.toLowerCase();
|
|
|
|
const body = request.body as Record<
|
|
string,
|
|
{ value: string; file?: File }
|
|
>;
|
|
const fields = fromPairs(
|
|
Object.keys((body ?? {}) as Record<string, unknown>).map(key => [
|
|
key,
|
|
getFile(body[key] as any) ?? body[key].value,
|
|
])
|
|
);
|
|
const html = await controller[route.action](
|
|
{
|
|
...request,
|
|
params,
|
|
query,
|
|
payload: fields ?? {},
|
|
method,
|
|
},
|
|
reply
|
|
);
|
|
|
|
if (route.contentType) {
|
|
reply.type(route.contentType);
|
|
} else if (typeof html === 'string') {
|
|
reply.type('text/html');
|
|
}
|
|
if (html) {
|
|
return reply.send(html);
|
|
}
|
|
};
|
|
|
|
if (route.method === 'GET') {
|
|
fastifyApp.get(`${admin.options.rootPath}${path}`, handler);
|
|
}
|
|
|
|
if (route.method === 'POST') {
|
|
fastifyApp.post(`${admin.options.rootPath}${path}`, handler);
|
|
}
|
|
});
|
|
|
|
assets.forEach(asset => {
|
|
fastifyApp.get(
|
|
`${admin.options.rootPath}${asset.path}`,
|
|
async (_req, reply) => {
|
|
const mimeType = mime.lookup(asset.src)
|
|
const file = await readFile(path.resolve(asset.src))
|
|
|
|
if (mimeType) {
|
|
return reply.type(mimeType).send(file);
|
|
}
|
|
return reply.send(file);
|
|
}
|
|
);
|
|
});
|
|
};
|