chore(plugin-cloud-storage): eslint fix

This commit is contained in:
Elliot DeNolf
2023-10-24 16:20:58 -04:00
parent 11d237ece8
commit bff88b3956
31 changed files with 246 additions and 194 deletions

View File

@@ -1,6 +1,6 @@
{
"ext": "ts",
"exec": "node --trace-warnings -r ts-node/register -r ./src/server.ts",
"ext": "ts",
"watch": [
"src/**/*.ts",
"../src/**/*.ts"

View File

@@ -1,13 +1,14 @@
import path from 'path'
import type { GenerateURL } from '../../types'
interface Args {
containerName: string
baseURL: string
containerName: string
}
export const getGenerateURL =
({ containerName, baseURL }: Args): GenerateURL =>
({ baseURL, containerName }: Args): GenerateURL =>
({ filename, prefix = '' }) => {
return `${baseURL}/${containerName}/${path.posix.join(prefix, filename)}`
}

View File

@@ -1,6 +1,8 @@
import path from 'path'
import type { CollectionConfig } from 'payload/types'
import type { ContainerClient } from '@azure/storage-blob'
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import type { HandleDelete } from '../../types'
interface Args {
@@ -9,7 +11,7 @@ interface Args {
}
export const getHandleDelete = ({ getStorageClient }: Args): HandleDelete => {
return async ({ filename, doc: { prefix = '' } }) => {
return async ({ doc: { prefix = '' }, filename }) => {
const blockBlobClient = getStorageClient().getBlockBlobClient(path.posix.join(prefix, filename))
await blockBlobClient.deleteIfExists()
}

View File

@@ -1,9 +1,11 @@
import path from 'path'
import fs from 'fs'
import { Readable } from 'stream'
import type { ContainerClient } from '@azure/storage-blob'
import { AbortController } from '@azure/abort-controller'
import type { CollectionConfig } from 'payload/types'
import { AbortController } from '@azure/abort-controller'
import fs from 'fs'
import path from 'path'
import { Readable } from 'stream'
import type { HandleUpload } from '../../types'
interface Args {

View File

@@ -1,24 +1,27 @@
import type { ContainerClient } from '@azure/storage-blob'
import { BlobServiceClient } from '@azure/storage-blob'
import type { Adapter, GeneratedAdapter } from '../../types'
import { getHandler } from './staticHandler'
import { getGenerateURL } from './generateURL'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
allowContainerCreate: boolean
baseURL: string
connectionString: string
containerName: string
baseURL: string
allowContainerCreate: boolean
}
export const azureBlobStorageAdapter = ({
connectionString,
allowContainerCreate,
containerName,
baseURL,
connectionString,
containerName,
}: Args): Adapter => {
let storageClient: ContainerClient | null = null
const getStorageClient = () => {
@@ -33,14 +36,14 @@ export const azureBlobStorageAdapter = ({
return ({ collection, prefix }): GeneratedAdapter => {
return {
generateURL: getGenerateURL({ baseURL, containerName }),
handleDelete: getHandleDelete({ collection, getStorageClient }),
handleUpload: getHandleUpload({
collection,
getStorageClient,
prefix,
}),
handleDelete: getHandleDelete({ collection, getStorageClient }),
generateURL: getGenerateURL({ containerName, baseURL }),
staticHandler: getHandler({ getStorageClient, collection }),
staticHandler: getHandler({ collection, getStorageClient }),
webpack: extendWebpackConfig,
...(allowContainerCreate && { onInit: createContainerIfNotExists }),
}

View File

@@ -1,24 +1,27 @@
import type { ContainerClient } from '@azure/storage-blob'
import path from 'path'
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import type { StaticHandler } from '../../types'
import { getFilePrefix } from '../../utilities/getFilePrefix'
import getRangeFromHeader from '../../utilities/getRangeFromHeader'
interface Args {
getStorageClient: () => ContainerClient
collection: CollectionConfig
getStorageClient: () => ContainerClient
}
export const getHandler = ({ getStorageClient, collection }: Args): StaticHandler => {
export const getHandler = ({ collection, getStorageClient }: Args): StaticHandler => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ req, collection })
const prefix = await getFilePrefix({ collection, req })
const blockBlobClient = getStorageClient().getBlockBlobClient(
path.posix.join(prefix, req.params.filename),
)
const { start, end } = await getRangeFromHeader(blockBlobClient, req.headers.range)
const { end, start } = await getRangeFromHeader(blockBlobClient, req.headers.range)
const blob = await blockBlobClient.download(start, end)
// eslint-disable-next-line no-underscore-dangle

View File

@@ -1,4 +1,5 @@
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
@@ -6,14 +7,14 @@ export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): Webpa
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@payloadcms/plugin-cloud-storage/azure': path.resolve(__dirname, './mock.js'),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}

View File

@@ -1,14 +1,16 @@
import type { Storage } from '@google-cloud/storage'
import path from 'path'
import { Storage } from '@google-cloud/storage'
import type { GenerateURL } from '../../types'
interface Args {
getStorageClient: () => Storage
bucket: string
getStorageClient: () => Storage
}
export const getGenerateURL =
({ getStorageClient, bucket }: Args): GenerateURL =>
({ bucket, getStorageClient }: Args): GenerateURL =>
({ filename, prefix = '' }) => {
return decodeURIComponent(
getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).publicUrl(),

View File

@@ -1,14 +1,16 @@
import type { Storage } from '@google-cloud/storage'
import path from 'path'
import { Storage } from '@google-cloud/storage'
import type { HandleDelete } from '../../types'
interface Args {
getStorageClient: () => Storage
bucket: string
getStorageClient: () => Storage
}
export const getHandleDelete = ({ getStorageClient, bucket }: Args): HandleDelete => {
return async ({ filename, doc: { prefix = '' } }) => {
export const getHandleDelete = ({ bucket, getStorageClient }: Args): HandleDelete => {
return async ({ doc: { prefix = '' }, filename }) => {
await getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).delete({
ignoreNotFound: true,
})

View File

@@ -1,20 +1,22 @@
import path from 'path'
import type { Storage } from '@google-cloud/storage'
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import type { HandleUpload } from '../../types'
interface Args {
collection: CollectionConfig
bucket: string
acl?: 'Private' | 'Public'
prefix?: string
bucket: string
collection: CollectionConfig
getStorageClient: () => Storage
prefix?: string
}
export const getHandleUpload = ({
getStorageClient,
bucket,
acl,
bucket,
getStorageClient,
prefix = '',
}: Args): HandleUpload => {
return async ({ data, file }) => {

View File

@@ -1,20 +1,23 @@
import type { StorageOptions } from '@google-cloud/storage'
import { Storage } from '@google-cloud/storage'
import type { Adapter, GeneratedAdapter } from '../../types'
import { getGenerateURL } from './generateURL'
import { getHandler } from './staticHandler'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
options: StorageOptions
bucket: string
acl?: 'Private' | 'Public'
bucket: string
options: StorageOptions
}
export const gcsAdapter =
({ options, bucket, acl }: Args): Adapter =>
({ acl, bucket, options }: Args): Adapter =>
({ collection, prefix }): GeneratedAdapter => {
let storageClient: Storage | null = null
@@ -25,16 +28,16 @@ export const gcsAdapter =
}
return {
generateURL: getGenerateURL({ bucket, getStorageClient }),
handleDelete: getHandleDelete({ bucket, getStorageClient }),
handleUpload: getHandleUpload({
acl,
bucket,
collection,
getStorageClient,
bucket,
acl,
prefix,
}),
handleDelete: getHandleDelete({ getStorageClient, bucket }),
generateURL: getGenerateURL({ getStorageClient, bucket }),
staticHandler: getHandler({ getStorageClient, bucket, collection }),
staticHandler: getHandler({ bucket, collection, getStorageClient }),
webpack: extendWebpackConfig,
}
}

View File

@@ -1,19 +1,22 @@
import path from 'path'
import type { Storage } from '@google-cloud/storage'
import type { CollectionConfig } from 'payload/types'
import path from 'path'
import type { StaticHandler } from '../../types'
import { getFilePrefix } from '../../utilities/getFilePrefix'
interface Args {
getStorageClient: () => Storage
bucket: string
collection: CollectionConfig
getStorageClient: () => Storage
}
export const getHandler = ({ getStorageClient, bucket, collection }: Args): StaticHandler => {
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ req, collection })
const prefix = await getFilePrefix({ collection, req })
const file = getStorageClient()
.bucket(bucket)
.file(path.posix.join(prefix, req.params.filename))

View File

@@ -1,4 +1,5 @@
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
@@ -6,14 +7,14 @@ export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): Webpa
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@google-cloud/storage': path.resolve(__dirname, './mock.js'),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}

View File

@@ -1,14 +1,16 @@
import path from 'path'
import type * as AWS from '@aws-sdk/client-s3'
import path from 'path'
import type { GenerateURL } from '../../types'
interface Args {
config: AWS.S3ClientConfig
bucket: string
config: AWS.S3ClientConfig
}
export const getGenerateURL =
({ config: { endpoint }, bucket }: Args): GenerateURL =>
({ bucket, config: { endpoint } }: Args): GenerateURL =>
({ filename, prefix = '' }) => {
return `${endpoint}/${bucket}/${path.posix.join(prefix, filename)}`
}

View File

@@ -1,14 +1,16 @@
import path from 'path'
import type * as AWS from '@aws-sdk/client-s3'
import path from 'path'
import type { HandleDelete } from '../../types'
interface Args {
getStorageClient: () => AWS.S3
bucket: string
getStorageClient: () => AWS.S3
}
export const getHandleDelete = ({ getStorageClient, bucket }: Args): HandleDelete => {
return async ({ filename, doc: { prefix = '' } }) => {
export const getHandleDelete = ({ bucket, getStorageClient }: Args): HandleDelete => {
return async ({ doc: { prefix = '' }, filename }) => {
await getStorageClient().deleteObject({
Bucket: bucket,
Key: path.posix.join(prefix, filename),

View File

@@ -1,26 +1,27 @@
import fs from 'fs'
import path from 'path'
import type * as AWS from '@aws-sdk/client-s3'
import type { CollectionConfig } from 'payload/types'
import type stream from 'stream'
import { Upload } from '@aws-sdk/lib-storage'
import fs from 'fs'
import path from 'path'
import type { HandleUpload } from '../../types'
interface Args {
collection: CollectionConfig
bucket: string
acl?: 'private' | 'public-read'
prefix?: string
bucket: string
collection: CollectionConfig
getStorageClient: () => AWS.S3
prefix?: string
}
const multipartThreshold = 1024 * 1024 * 50 // 50MB
export const getHandleUpload = ({
getStorageClient,
bucket,
acl,
bucket,
getStorageClient,
prefix = '',
}: Args): HandleUpload => {
return async ({ data, file }) => {
@@ -32,11 +33,11 @@ export const getHandleUpload = ({
if (file.buffer.length > 0 && file.buffer.length < multipartThreshold) {
await getStorageClient().putObject({
Bucket: bucket,
Key: fileKey,
Body: fileBufferOrStream,
ACL: acl,
Body: fileBufferOrStream,
Bucket: bucket,
ContentType: file.mimeType,
Key: fileKey,
})
return data
@@ -45,14 +46,14 @@ export const getHandleUpload = ({
const parallelUploadS3 = new Upload({
client: getStorageClient(),
params: {
Bucket: bucket,
Key: fileKey,
Body: fileBufferOrStream,
ACL: acl,
Body: fileBufferOrStream,
Bucket: bucket,
ContentType: file.mimeType,
Key: fileKey,
},
queueSize: 4,
partSize: multipartThreshold,
queueSize: 4,
})
await parallelUploadS3.done()

View File

@@ -1,29 +1,31 @@
import * as AWS from '@aws-sdk/client-s3'
import type { Adapter, GeneratedAdapter } from '../../types'
import { getGenerateURL } from './generateURL'
import { getHandler } from './staticHandler'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
/**
* AWS S3 client configuration. Highly dependent on your AWS setup.
*
* [AWS.S3ClientConfig Docs](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html)
*/
config: AWS.S3ClientConfig
acl?: 'private' | 'public-read'
/**
* Bucket name to upload files to.
*
* Must follow [AWS S3 bucket naming conventions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html).
*/
bucket: string
acl?: 'private' | 'public-read'
/**
* AWS S3 client configuration. Highly dependent on your AWS setup.
*
* [AWS.S3ClientConfig Docs](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html)
*/
config: AWS.S3ClientConfig
}
export const s3Adapter =
({ config = {}, bucket, acl }: Args): Adapter =>
({ acl, bucket, config = {} }: Args): Adapter =>
({ collection, prefix }): GeneratedAdapter => {
let storageClient: AWS.S3 | null = null
const getStorageClient: () => AWS.S3 = () => {
@@ -33,16 +35,16 @@ export const s3Adapter =
}
return {
generateURL: getGenerateURL({ bucket, config }),
handleDelete: getHandleDelete({ bucket, getStorageClient }),
handleUpload: getHandleUpload({
acl,
bucket,
collection,
getStorageClient,
bucket,
acl,
prefix,
}),
handleDelete: getHandleDelete({ getStorageClient, bucket }),
generateURL: getGenerateURL({ bucket, config }),
staticHandler: getHandler({ bucket, getStorageClient, collection }),
staticHandler: getHandler({ bucket, collection, getStorageClient }),
webpack: extendWebpackConfig,
}
}

View File

@@ -1,20 +1,23 @@
import path from 'path'
import type { Readable } from 'stream'
import type * as AWS from '@aws-sdk/client-s3'
import type { CollectionConfig } from 'payload/types'
import type { Readable } from 'stream'
import path from 'path'
import type { StaticHandler } from '../../types'
import { getFilePrefix } from '../../utilities/getFilePrefix'
interface Args {
getStorageClient: () => AWS.S3
bucket: string
collection: CollectionConfig
getStorageClient: () => AWS.S3
}
export const getHandler = ({ getStorageClient, bucket, collection }: Args): StaticHandler => {
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ req, collection })
const prefix = await getFilePrefix({ collection, req })
const object = await getStorageClient().getObject({
Bucket: bucket,

View File

@@ -1,4 +1,5 @@
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
@@ -6,14 +7,14 @@ export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): Webpa
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@payloadcms/plugin-cloud-storage/s3': path.resolve(__dirname, './mock.js'),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}

View File

@@ -1,7 +1,8 @@
import path from 'path'
import type { GroupField, TextField } from 'payload/dist/fields/config/types'
import type { CollectionConfig, Field } from 'payload/types'
import path from 'path'
interface Args {
collection: CollectionConfig
prefix?: string
@@ -10,21 +11,21 @@ interface Args {
export const getFields = ({ collection, prefix }: Args): Field[] => {
const baseURLField: Field = {
name: 'url',
admin: {
hidden: true,
readOnly: true,
},
label: 'URL',
type: 'text',
admin: {
readOnly: true,
hidden: true,
},
}
const basePrefixField: Field = {
name: 'prefix',
type: 'text',
admin: {
readOnly: true,
hidden: true,
readOnly: true,
},
type: 'text',
}
const fields = [...collection.fields]
@@ -69,31 +70,31 @@ export const getFields = ({ collection, prefix }: Args): Field[] => {
const sizesField: Field = {
...(existingSizesField || {}),
name: 'sizes',
type: 'group',
admin: {
hidden: true,
},
fields: collection.upload.imageSizes.map(size => {
fields: collection.upload.imageSizes.map((size) => {
const existingSizeField = existingSizesField?.fields.find(
existingField => 'name' in existingField && existingField.name === size.name,
(existingField) => 'name' in existingField && existingField.name === size.name,
) as GroupField
const existingSizeURLField = existingSizeField?.fields.find(
existingField => 'name' in existingField && existingField.name === 'url',
(existingField) => 'name' in existingField && existingField.name === 'url',
) as GroupField
return {
...existingSizeField,
name: size.name,
type: 'group',
fields: [
{
...(existingSizeURLField || {}),
...baseURLField,
},
],
type: 'group',
}
}),
type: 'group',
}
fields.push(sizesField)

View File

@@ -1,5 +1,7 @@
import type { Config } from 'payload/config'
import type { PluginOptions } from '../types'
import { getFields } from './fields/getFields'
// This is the admin plugin cloud-storage stubfile.
@@ -18,7 +20,7 @@ export const cloudStorage =
return {
...config,
collections: (config.collections || []).map(existingCollection => {
collections: (config.collections || []).map((existingCollection) => {
const options = allCollectionOptions[existingCollection.slug]
if (options?.adapter) {

View File

@@ -1,15 +1,18 @@
import path from 'path'
import type { GroupField, TextField } from 'payload/dist/fields/config/types'
import type { CollectionConfig, Field } from 'payload/types'
import path from 'path'
import type { GenerateFileURL, GeneratedAdapter } from '../types'
import { getAfterReadHook } from '../hooks/afterRead'
import type { GeneratedAdapter, GenerateFileURL } from '../types'
interface Args {
adapter: GeneratedAdapter
collection: CollectionConfig
disablePayloadAccessControl?: true
generateFileURL?: GenerateFileURL
prefix?: string
adapter: GeneratedAdapter
}
export const getFields = ({
@@ -21,21 +24,21 @@ export const getFields = ({
}: Args): Field[] => {
const baseURLField: Field = {
name: 'url',
admin: {
hidden: true,
readOnly: true,
},
label: 'URL',
type: 'text',
admin: {
readOnly: true,
hidden: true,
},
}
const basePrefixField: Field = {
name: 'prefix',
type: 'text',
admin: {
readOnly: true,
hidden: true,
readOnly: true,
},
type: 'text',
}
const fields = [...collection.fields]
@@ -86,23 +89,21 @@ export const getFields = ({
const sizesField: Field = {
...(existingSizesField || {}),
name: 'sizes',
type: 'group',
admin: {
hidden: true,
},
fields: collection.upload.imageSizes.map(size => {
fields: collection.upload.imageSizes.map((size) => {
const existingSizeField = existingSizesField?.fields.find(
existingField => 'name' in existingField && existingField.name === size.name,
(existingField) => 'name' in existingField && existingField.name === size.name,
) as GroupField
const existingSizeURLField = existingSizeField?.fields.find(
existingField => 'name' in existingField && existingField.name === 'url',
(existingField) => 'name' in existingField && existingField.name === 'url',
) as GroupField
return {
...existingSizeField,
name: size.name,
type: 'group',
fields: [
{
...(existingSizeURLField || {}),
@@ -112,17 +113,19 @@ export const getFields = ({
getAfterReadHook({
adapter,
collection,
size,
disablePayloadAccessControl,
generateFileURL,
size,
}),
...(existingSizeURLField?.hooks?.afterRead || []),
],
},
},
],
type: 'group',
}
}),
type: 'group',
}
fields.push(sizesField)

View File

@@ -1,26 +1,27 @@
import type { TypeWithID } from 'payload/dist/globals/config/types'
import type { FileData } from 'payload/dist/uploads/types'
import type { CollectionAfterDeleteHook, CollectionConfig } from 'payload/types'
import type { GeneratedAdapter, TypeWithPrefix } from '../types'
interface Args {
collection: CollectionConfig
adapter: GeneratedAdapter
collection: CollectionConfig
}
export const getAfterDeleteHook = ({
collection,
adapter,
collection,
}: Args): CollectionAfterDeleteHook<FileData & TypeWithID & TypeWithPrefix> => {
return async ({ req, doc }) => {
return async ({ doc, req }) => {
try {
const filesToDelete: string[] = [
doc.filename,
...Object.values(doc?.sizes || []).map(resizedFileData => resizedFileData?.filename),
...Object.values(doc?.sizes || []).map((resizedFileData) => resizedFileData?.filename),
]
const promises = filesToDelete.map(async filename => {
if (filename) await adapter.handleDelete({ collection, doc, req, filename })
const promises = filesToDelete.map(async (filename) => {
if (filename) await adapter.handleDelete({ collection, doc, filename, req })
})
await Promise.all(promises)

View File

@@ -1,17 +1,18 @@
import type { ImageSize } from 'payload/dist/uploads/types'
import type { CollectionConfig, FieldHook } from 'payload/types'
import type { GeneratedAdapter, GenerateFileURL } from '../types'
import type { GenerateFileURL, GeneratedAdapter } from '../types'
interface Args {
collection: CollectionConfig
adapter: GeneratedAdapter
collection: CollectionConfig
disablePayloadAccessControl?: boolean
size?: ImageSize
generateFileURL?: GenerateFileURL
size?: ImageSize
}
export const getAfterReadHook =
({ collection, adapter, size, disablePayloadAccessControl, generateFileURL }: Args): FieldHook =>
({ adapter, collection, disablePayloadAccessControl, generateFileURL, size }: Args): FieldHook =>
async ({ data, value }) => {
const filename = size ? data?.sizes?.[size.name]?.filename : data?.filename
const prefix = data?.prefix

View File

@@ -1,19 +1,21 @@
import type { TypeWithID } from 'payload/dist/collections/config/types'
import type { FileData } from 'payload/dist/uploads/types'
import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload/types'
import type { GeneratedAdapter } from '../types'
import { getIncomingFiles } from '../utilities/getIncomingFiles'
interface Args {
collection: CollectionConfig
adapter: GeneratedAdapter
collection: CollectionConfig
}
export const getBeforeChangeHook =
({ collection, adapter }: Args): CollectionBeforeChangeHook<FileData & TypeWithID> =>
async ({ req, data, originalDoc }) => {
({ adapter, collection }: Args): CollectionBeforeChangeHook<FileData & TypeWithID> =>
async ({ data, originalDoc, req }) => {
try {
const files = getIncomingFiles({ req, data })
const files = getIncomingFiles({ data, req })
if (files.length > 0) {
// If there is an original doc,
@@ -29,22 +31,22 @@ export const getBeforeChangeHook =
if (typeof originalDoc.sizes === 'object') {
filesToDelete = filesToDelete.concat(
Object.values(originalDoc?.sizes || []).map(
resizedFileData => resizedFileData?.filename,
(resizedFileData) => resizedFileData?.filename,
),
)
}
const deletionPromises = filesToDelete.map(async filename => {
const deletionPromises = filesToDelete.map(async (filename) => {
if (filename) {
await adapter.handleDelete({ collection, doc: originalDoc, req, filename })
await adapter.handleDelete({ collection, doc: originalDoc, filename, req })
}
})
await Promise.all(deletionPromises)
}
const promises = files.map(async file => {
await adapter.handleUpload({ collection, data, req, file })
const promises = files.map(async (file) => {
await adapter.handleUpload({ collection, data, file, req })
})
await Promise.all(promises)

View File

@@ -1,9 +1,11 @@
import type { Config } from 'payload/config'
import { extendWebpackConfig } from './webpack'
import type { PluginOptions } from './types'
import { getBeforeChangeHook } from './hooks/beforeChange'
import { getAfterDeleteHook } from './hooks/afterDelete'
import { getFields } from './fields/getFields'
import { getAfterDeleteHook } from './hooks/afterDelete'
import { getBeforeChangeHook } from './hooks/beforeChange'
import { extendWebpackConfig } from './webpack'
// This plugin extends all targeted collections by offloading uploaded files
// to cloud storage instead of solely storing files locally.
@@ -20,7 +22,7 @@ export const cloudStorage =
const { collections: allCollectionOptions, enabled } = pluginOptions
const config = { ...incomingConfig }
const webpack = extendWebpackConfig({ options: pluginOptions, config: incomingConfig })
const webpack = extendWebpackConfig({ config: incomingConfig, options: pluginOptions })
config.admin = {
...(config.admin || {}),
@@ -36,7 +38,7 @@ export const cloudStorage =
return {
...config,
collections: (config.collections || []).map(existingCollection => {
collections: (config.collections || []).map((existingCollection) => {
const options = allCollectionOptions[existingCollection.slug]
if (options?.adapter) {
@@ -48,11 +50,11 @@ export const cloudStorage =
if (adapter.onInit) initFunctions.push(adapter.onInit)
const fields = getFields({
adapter,
collection: existingCollection,
disablePayloadAccessControl: options.disablePayloadAccessControl,
generateFileURL: options.generateFileURL,
prefix: options.prefix,
adapter,
})
const handlers = [
@@ -68,33 +70,33 @@ export const cloudStorage =
return {
...existingCollection,
upload: {
...(typeof existingCollection.upload === 'object' ? existingCollection.upload : {}),
handlers,
disableLocalStorage:
typeof options.disableLocalStorage === 'boolean'
? options.disableLocalStorage
: true,
},
fields,
hooks: {
...(existingCollection.hooks || {}),
beforeChange: [
...(existingCollection.hooks?.beforeChange || []),
getBeforeChangeHook({ adapter, collection: existingCollection }),
],
afterDelete: [
...(existingCollection.hooks?.afterDelete || []),
getAfterDeleteHook({ adapter, collection: existingCollection }),
],
beforeChange: [
...(existingCollection.hooks?.beforeChange || []),
getBeforeChangeHook({ adapter, collection: existingCollection }),
],
},
upload: {
...(typeof existingCollection.upload === 'object' ? existingCollection.upload : {}),
disableLocalStorage:
typeof options.disableLocalStorage === 'boolean'
? options.disableLocalStorage
: true,
handlers,
},
fields,
}
}
return existingCollection
}),
onInit: async payload => {
initFunctions.forEach(fn => fn())
onInit: async (payload) => {
initFunctions.forEach((fn) => fn())
if (config.onInit) await config.onInit(payload)
},
}

View File

@@ -14,9 +14,9 @@ export interface File {
export type HandleUpload = (args: {
collection: CollectionConfig
req: PayloadRequest
data: any
file: File
req: PayloadRequest
}) => Promise<void> | void
export interface TypeWithPrefix {
@@ -25,16 +25,16 @@ export interface TypeWithPrefix {
export type HandleDelete = (args: {
collection: CollectionConfig
req: PayloadRequest
doc: TypeWithID & FileData & TypeWithPrefix
filename: string
req: PayloadRequest
}) => Promise<void> | void
export type GenerateURL = (args: {
filename: string
collection: CollectionConfig
filename: string
prefix?: string
}) => string | Promise<string>
}) => Promise<string> | string
export type StaticHandler = (
req: PayloadRequest,
@@ -43,12 +43,12 @@ export type StaticHandler = (
) => Promise<unknown> | unknown
export interface GeneratedAdapter {
handleUpload: HandleUpload
handleDelete: HandleDelete
generateURL: GenerateURL
handleDelete: HandleDelete
handleUpload: HandleUpload
onInit?: () => void
staticHandler: StaticHandler
webpack?: (config: WebpackConfig) => WebpackConfig
onInit?: () => void
}
export type Adapter = (args: { collection: CollectionConfig; prefix?: string }) => GeneratedAdapter
@@ -61,19 +61,19 @@ export type GenerateFileURL = (args: {
}) => Promise<string> | string
export interface CollectionOptions {
adapter: Adapter | null
disableLocalStorage?: boolean
disablePayloadAccessControl?: true
generateFileURL?: GenerateFileURL
prefix?: string
adapter: Adapter | null
}
export interface PluginOptions {
collections: Record<string, CollectionOptions>
/**
* Whether or not to enable the plugin
*
* Default: true
*/
enabled?: boolean
collections: Record<string, CollectionOptions>
}

View File

@@ -1,12 +1,12 @@
import type { IncomingUploadType } from 'payload/dist/uploads/types'
import type { CollectionConfig, PayloadRequest } from 'payload/types'
import { IncomingUploadType } from 'payload/dist/uploads/types'
export async function getFilePrefix({
req,
collection,
req,
}: {
req: PayloadRequest
collection: CollectionConfig
req: PayloadRequest
}): Promise<string> {
const imageSizes = (collection?.upload as IncomingUploadType)?.imageSizes || []
const files = await req.payload.find({
@@ -16,7 +16,7 @@ export async function getFilePrefix({
{
filename: { equals: req.params.filename },
},
...imageSizes.map(imageSize => ({
...imageSizes.map((imageSize) => ({
[`sizes.${imageSize.name}.filename`]: { equals: req.params.filename },
})),
],

View File

@@ -1,10 +1,11 @@
import type { FileData } from 'payload/dist/uploads/types'
import type { PayloadRequest } from 'payload/types'
import type { File } from '../types'
export function getIncomingFiles({
req,
data,
req,
}: {
data: Partial<FileData>
req: PayloadRequest
@@ -15,11 +16,11 @@ export function getIncomingFiles({
if (file && data.filename && data.mimeType) {
const mainFile: File = {
filename: data.filename,
mimeType: data.mimeType,
buffer: file.data,
tempFilePath: file.tempFilePath,
filename: data.filename,
filesize: file.size,
mimeType: data.mimeType,
tempFilePath: file.tempFilePath,
}
files = [mainFile]
@@ -29,10 +30,10 @@ export function getIncomingFiles({
if (req.payloadUploadSizes?.[key] && data.mimeType) {
files = files.concat([
{
filename: `${resizedFileData.filename}`,
mimeType: data.mimeType,
buffer: req.payloadUploadSizes[key],
filename: `${resizedFileData.filename}`,
filesize: req.payloadUploadSizes[key].length,
mimeType: data.mimeType,
},
])
}

View File

@@ -1,17 +1,18 @@
import type { BlockBlobClient } from '@azure/storage-blob'
import parseRange from 'range-parser'
const getRangeFromHeader = async (
blockBlobClient: BlockBlobClient,
rangeHeader?: string,
): Promise<{ start: number; end: number | undefined }> => {
const fullRange = { start: 0, end: undefined }
): Promise<{ end: number | undefined; start: number }> => {
const fullRange = { end: undefined, start: 0 }
if (!rangeHeader) {
return fullRange
}
const size = await blockBlobClient.getProperties().then(props => props.contentLength)
const size = await blockBlobClient.getProperties().then((props) => props.contentLength)
if (size === undefined) {
return fullRange
}

View File

@@ -1,6 +1,8 @@
import path from 'path'
import type { Config } from 'payload/config'
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
import type { GeneratedAdapter, PluginOptions } from './types'
interface Args {
@@ -10,7 +12,7 @@ interface Args {
export const extendWebpackConfig =
({ config, options }: Args): ((webpackConfig: WebpackConfig) => WebpackConfig) =>
webpackConfig => {
(webpackConfig) => {
const existingWebpackConfig =
typeof config.admin?.webpack === 'function'
? config.admin.webpack(webpackConfig)
@@ -29,7 +31,7 @@ export const extendWebpackConfig =
return Object.entries(options.collections).reduce(
(resultingWebpackConfig, [slug, collectionOptions]) => {
const matchedCollection = config.collections?.find(coll => coll.slug === slug)
const matchedCollection = config.collections?.find((coll) => coll.slug === slug)
if (matchedCollection && typeof collectionOptions.adapter === 'function') {
const adapter: GeneratedAdapter = collectionOptions.adapter({