@baseplate-dev/plugin-storage
Version:
Contains the storage plugin for Baseplate
210 lines • 10.9 kB
JavaScript
import { CORE_PACKAGES, createNodePackagesTask, extractPackageVersions, packageScope, tsCodeFragment, TsCodeUtils, tsTypeImportBuilder, } from '@baseplate-dev/core-generators';
import { appModuleProvider, configServiceImportsProvider, configServiceProvider, createPothosTypeReference, pothosConfigProvider, pothosSchemaProvider, pothosTypeOutputProvider, prismaOutputProvider, } from '@baseplate-dev/fastify-generators';
import { createConfigProviderTask, createGenerator, createGeneratorTask, createProviderTask, } from '@baseplate-dev/sync';
import { z } from 'zod';
import { STORAGE_PACKAGES } from '#src/constants/index.js';
import { STORAGE_MODELS } from '#src/storage/constants/model-names.js';
import { FASTIFY_STORAGE_MODULE_GENERATED } from './generated/index.js';
import { storageModuleImportsProvider } from './generated/ts-import-providers.js';
const descriptorSchema = z.object({
/**
* The S3 adapters to use for the storage module.
*/
s3Adapters: z.array(z.object({
/**
* The name of the adapter.
*/
name: z.string().min(1),
/**
* The name of the config variable for the bucket.
*/
bucketConfigVar: z.string().min(1),
hostedUrlConfigVar: z.string().optional(),
})),
});
const [configTask, storageModuleConfigProvider, storageModuleConfigValuesProvider,] = createConfigProviderTask((t) => ({
/** Map of file category name to the config fragment for the file category. */
fileCategories: t.map(),
}), {
prefix: 'storage-module',
configScope: packageScope,
});
export { storageModuleConfigProvider };
export const storageModuleGenerator = createGenerator({
name: 'fastify/storage-module',
generatorFileUrl: import.meta.url,
descriptorSchema,
buildTasks: ({ s3Adapters }) => ({
paths: FASTIFY_STORAGE_MODULE_GENERATED.paths.task,
imports: FASTIFY_STORAGE_MODULE_GENERATED.imports.task,
renderers: FASTIFY_STORAGE_MODULE_GENERATED.renderers.task,
config: configTask,
nodePackages: createNodePackagesTask({
prod: {
...extractPackageVersions(CORE_PACKAGES, ['axios']),
...extractPackageVersions(STORAGE_PACKAGES, [
'@aws-sdk/client-s3',
'@aws-sdk/s3-presigned-post',
'@aws-sdk/s3-request-presigner',
'mime-types',
]),
},
dev: extractPackageVersions(STORAGE_PACKAGES, ['@types/mime-types']),
}),
setupFileInputSchema: createGeneratorTask({
dependencies: {
pothosConfig: pothosConfigProvider,
paths: FASTIFY_STORAGE_MODULE_GENERATED.paths.provider,
},
run({ pothosConfig, paths }) {
pothosConfig.inputTypes.set('FileUploadInput', createPothosTypeReference({
name: 'FileUploadInput',
exportName: 'fileInputInputType',
moduleSpecifier: paths.schemaFileInput,
}));
return {};
},
}),
renderSchema: createGeneratorTask({
dependencies: {
appModule: appModuleProvider,
renderers: FASTIFY_STORAGE_MODULE_GENERATED.renderers.provider,
pothosSchema: pothosSchemaProvider,
fileObjectType: pothosTypeOutputProvider
.dependency()
.reference(`prisma-object-type:${STORAGE_MODELS.file}`),
paths: FASTIFY_STORAGE_MODULE_GENERATED.paths.provider,
},
run({ appModule, pothosSchema, renderers, fileObjectType, paths }) {
const { schemaGroup } = FASTIFY_STORAGE_MODULE_GENERATED.templates;
for (const template of Object.keys(schemaGroup)) {
const renderedPath = paths[template];
appModule.moduleImports.push(renderedPath);
pothosSchema.registerSchemaFile(renderedPath);
}
return {
build: async (builder) => {
const fileObjectRef = fileObjectType.getTypeReference();
await builder.apply(renderers.schemaGroup.render({
variables: {
schemaPresignedMutations: {
TPL_FILE_OBJECT_TYPE: fileObjectRef.fragment,
},
schemaPublicUrl: {
TPL_FILE_OBJECT_TYPE: fileObjectRef.fragment,
},
},
}));
},
};
},
}),
configService: createProviderTask(configServiceProvider, (configService) => {
configService.configFields.mergeObj({
AWS_ACCESS_KEY_ID: {
comment: 'AWS access key ID',
validator: tsCodeFragment('z.string().min(1)'),
seedValue: 'AWS_ACCESS_KEY_ID',
},
AWS_SECRET_ACCESS_KEY: {
comment: 'AWS secret access key',
validator: tsCodeFragment('z.string().min(1)'),
seedValue: 'AWS_SECRET_ACCESS_KEY',
},
AWS_DEFAULT_REGION: {
comment: 'AWS default region',
validator: tsCodeFragment('z.string().min(1)'),
seedValue: 'AWS_DEFAULT_REGION',
},
});
for (const adapter of s3Adapters) {
configService.configFields.set(adapter.bucketConfigVar, {
comment: `S3 bucket for ${adapter.name}`,
validator: tsCodeFragment('z.string().min(1)'),
seedValue: adapter.bucketConfigVar,
});
if (adapter.hostedUrlConfigVar) {
configService.configFields.set(adapter.hostedUrlConfigVar, {
comment: `Hosted URL prefix for ${adapter.name}, e.g. https://uploads.example.com`,
validator: tsCodeFragment('z.string().min(1)'),
seedValue: adapter.hostedUrlConfigVar,
});
}
}
}),
build: createGeneratorTask({
dependencies: {
prismaOutput: prismaOutputProvider,
configServiceImports: configServiceImportsProvider,
renderers: FASTIFY_STORAGE_MODULE_GENERATED.renderers.provider,
storageModuleImports: storageModuleImportsProvider,
storageModuleConfigValues: storageModuleConfigValuesProvider,
},
run({ prismaOutput, configServiceImports, renderers, storageModuleImports, storageModuleConfigValues, }) {
return {
build: async (builder) => {
const model = prismaOutput.getPrismaModelFragment(STORAGE_MODELS.file);
const modelType = prismaOutput.getModelTypeFragment(STORAGE_MODELS.file);
// Render module
await builder.apply(renderers.mainGroup.render({
variables: {
servicesCreatePresignedDownloadUrl: {
TPL_FILE_MODEL: model,
},
servicesCreatePresignedUploadUrl: {
TPL_FILE_MODEL: model,
TPL_FILE_MODEL_TYPE: modelType,
},
servicesDownloadFile: {
TPL_FILE_MODEL: model,
},
servicesUploadFile: {
TPL_FILE_MODEL: model,
TPL_FILE_MODEL_TYPE: modelType,
},
servicesValidateFileInput: {
TPL_FILE_MODEL: model,
},
typesFileCategory: {
TPL_FILE_COUNT_OUTPUT_TYPE: tsCodeFragment(`Prisma.${STORAGE_MODELS.file}CountOutputType`, tsTypeImportBuilder(['Prisma']).from('@prisma/client')),
},
utilsValidateFileUploadOptions: {
TPL_FILE_CREATE_INPUT: tsCodeFragment(`Prisma.${STORAGE_MODELS.file}CreateInput`, tsTypeImportBuilder(['Prisma']).from('@prisma/client')),
},
},
}));
// Render adapters config
const adapterMap = new Map();
for (const adapter of s3Adapters) {
const adapterOptions = TsCodeUtils.mergeFragmentsAsObject({
bucket: `config.${adapter.bucketConfigVar}`,
region: `config.AWS_DEFAULT_REGION`,
publicUrl: adapter.hostedUrlConfigVar
? `config.${adapter.hostedUrlConfigVar}`
: undefined,
});
adapterMap.set(adapter.name, TsCodeUtils.templateWithImports([
storageModuleImports.createS3Adapter.declaration(),
configServiceImports.config.declaration(),
]) `createS3Adapter(${adapterOptions})`);
}
adapterMap.set('url', tsCodeFragment('createUrlAdapter()', storageModuleImports.createUrlAdapter.declaration()));
await builder.apply(renderers.configAdapters.render({
variables: {
TPL_ADAPTERS: TsCodeUtils.mergeFragmentsAsObject(adapterMap),
},
}));
// Copy constants
const categoriesMap = storageModuleConfigValues.fileCategories;
await builder.apply(renderers.configCategories.render({
variables: {
TPL_FILE_CATEGORIES: TsCodeUtils.mergeFragmentsAsArray(categoriesMap),
},
}));
},
};
},
}),
}),
});
//# sourceMappingURL=storage-module.generator.js.map