@adonisjs/drive
Version:
A thin wrapper on top of Flydrive to work seamlessly with AdonisJS
289 lines (288 loc) • 7.92 kB
JavaScript
import { r as errors_exports, t as debug_default } from "./debug-BaNwyLMa.js";
import { RuntimeException } from "@adonisjs/core/exceptions";
import string from "@adonisjs/core/helpers/string";
import { configProvider } from "@adonisjs/core";
export * from "flydrive";
const stubsRoot = import.meta.dirname;
const STORAGE_SERVICES = {
fs: {
name: "Local filesystem",
env: [],
dependencies: []
},
s3: {
name: "AWS S3",
env: [
{
name: "AWS_ACCESS_KEY_ID",
value: "",
schema: "Env.schema.string()"
},
{
name: "AWS_SECRET_ACCESS_KEY",
value: "",
schema: "Env.schema.string()"
},
{
name: "AWS_REGION",
value: "",
schema: "Env.schema.string()"
},
{
name: "S3_BUCKET",
value: "",
schema: "Env.schema.string()"
}
],
dependencies: ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
},
spaces: {
name: "Digital Ocean Spaces",
env: [
{
name: "SPACES_KEY",
value: "",
schema: "Env.schema.string()"
},
{
name: "SPACES_SECRET",
value: "",
schema: "Env.schema.string()"
},
{
name: "SPACES_REGION",
value: "",
schema: "Env.schema.string()"
},
{
name: "SPACES_BUCKET",
value: "",
schema: "Env.schema.string()"
},
{
name: "SPACES_ENDPOINT",
value: `https://\${SPACES_REGION}.digitaloceanspaces.com`,
schema: "Env.schema.string()"
}
],
dependencies: ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
},
r2: {
name: "Cloudflare R2",
env: [
{
name: "R2_KEY",
value: "",
schema: "Env.schema.string()"
},
{
name: "R2_SECRET",
value: "",
schema: "Env.schema.string()"
},
{
name: "R2_BUCKET",
value: "",
schema: "Env.schema.string()"
},
{
name: "R2_ENDPOINT",
value: "",
schema: "Env.schema.string()"
}
],
dependencies: ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
},
gcs: {
name: "Google Cloud Storage",
env: [{
name: "GCS_KEY",
value: "file://gcs_key.json",
schema: "Env.schema.string()"
}, {
name: "GCS_BUCKET",
value: "",
schema: "Env.schema.string()"
}],
dependencies: ["@google-cloud/storage"]
},
supabase: {
name: "Supabase Storage",
env: [
{
name: "SUPABASE_STORAGE_KEY",
value: "",
schema: "Env.schema.string()"
},
{
name: "SUPABASE_STORAGE_SECRET",
value: "",
schema: "Env.schema.string()"
},
{
name: "SUPABASE_STORAGE_REGION",
value: "",
schema: "Env.schema.string()"
},
{
name: "SUPABASE_STORAGE_BUCKET",
value: "",
schema: "Env.schema.string()"
},
{
name: "SUPABASE_ENDPOINT",
value: "",
schema: "Env.schema.string()"
}
],
dependencies: ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
}
};
const SERVICES_NAMES = Object.keys(STORAGE_SERVICES);
async function configure(command) {
let selectedServices = command.parsedFlags.services;
let shouldInstallPackages = command.parsedFlags.install;
if (!selectedServices) selectedServices = await command.prompt.multiple("Select the storage services you want to use", SERVICES_NAMES.map((service) => {
return {
name: service,
message: STORAGE_SERVICES[service].name
};
}), { validate(values) {
return !values || !values.length ? "Please select one or more services" : true;
} });
const services = typeof selectedServices === "string" ? [selectedServices] : selectedServices;
const unknownServices = services.find((service) => !SERVICES_NAMES.includes(service));
if (unknownServices) {
command.exitCode = 1;
command.logger.logError(`Invalid service "${unknownServices}". Supported services are: ${string.sentence(SERVICES_NAMES)}`);
return;
}
const pkgsToInstall = services.flatMap((service) => STORAGE_SERVICES[service].dependencies).map((pkg) => {
return {
name: pkg,
isDevDependency: false
};
});
if (!shouldInstallPackages && pkgsToInstall.length) shouldInstallPackages = await command.prompt.confirm("Do you want to install additional packages required by \"@adonisjs/drive\"?");
const codemods = await command.createCodemods();
await codemods.makeUsingStub(stubsRoot, "config/drive.stub", { services });
await codemods.updateRcFile((rcFile) => {
rcFile.addProvider("@adonisjs/drive/drive_provider");
});
await codemods.defineEnvVariables(services.reduce((result, service) => {
STORAGE_SERVICES[service].env.forEach((envVariable) => {
result[envVariable.name] = envVariable.value;
});
return result;
}, { DRIVE_DISK: services[0] }));
await codemods.defineEnvValidations({
leadingComment: "Variables for configuring the drive package",
variables: services.reduce((result, service) => {
STORAGE_SERVICES[service].env.forEach((envVariable) => {
result[envVariable.name] = envVariable.schema;
});
return result;
}, { DRIVE_DISK: `Env.schema.enum(['${services.join("', '")}'] as const)` })
});
if (!pkgsToInstall.length) return;
if (shouldInstallPackages) await codemods.installPackages(pkgsToInstall);
else await codemods.listPackagesToInstall(pkgsToInstall);
}
function createURLBuilder(router, config, routeName) {
const prefixUrl = config.appUrl || "";
return {
async generateURL(key) {
return router.urlBuilder.urlFor(routeName, { "*": key.split("/") }, { prefixUrl });
},
async generateSignedUploadURL() {
throw new Error("Signed uploads are not supported by the FS driver");
},
async generateSignedURL(key, _, options) {
const { expiresIn, ...headers } = options;
return router.urlBuilder.signedUrlFor(routeName, { "*": key.split("/") }, {
qs: headers,
expiresIn,
prefixUrl
});
}
};
}
function defineConfig(config) {
return configProvider.create(async (app) => {
const { services, fakes, default: defaultDisk } = config;
const servicesNames = Object.keys(services);
const disks = {};
const locallyServed = [];
for (let serviceName of servicesNames) {
const disk = services[serviceName];
if (typeof disk === "function") disks[serviceName] = disk;
else disks[serviceName] = await disk.resolver(serviceName, app, locallyServed);
}
return {
config: {
default: defaultDisk,
fakes: {
location: app.tmpPath("drive-fakes"),
urlBuilder: {
async generateURL(key, _) {
return `/drive/fakes/${key}`;
},
async generateSignedURL(key, _, __) {
return `/drive/fakes/signed/${key}`;
}
},
...fakes
},
services: disks
},
locallyServed
};
});
}
const services = {
fs(config) {
return {
type: "provider",
async resolver(name, app, locallyServed) {
debug_default("configuring fs service");
if (config.serveFiles && !config.routeBasePath) throw new RuntimeException(`Invalid drive config. Missing "routeBasePath" option in "services.${name}" object`);
const routeName = `drive.${name}.serve`;
const fsConfig = {
visibility: config.visibility,
location: config.location
};
if (config.serveFiles) {
fsConfig.urlBuilder = createURLBuilder(await app.container.make("router"), config, routeName);
locallyServed.push({
service: name,
routeName,
routePattern: `${config.routeBasePath.replace(/\/$/, "")}/*`
});
}
const { FSDriver } = await import("flydrive/drivers/fs");
return () => new FSDriver(fsConfig);
}
};
},
s3(config) {
return {
type: "provider",
async resolver() {
debug_default("configuring s3 service");
const { S3Driver } = await import("flydrive/drivers/s3");
return () => new S3Driver(config);
}
};
},
gcs(config) {
return {
type: "provider",
async resolver() {
debug_default("configuring gcs service");
const { GCSDriver } = await import("flydrive/drivers/gcs");
return () => new GCSDriver(config);
}
};
}
};
export { configure, defineConfig, errors_exports as errors, services, stubsRoot };