Skip to content
This repository was archived by the owner on Jan 16, 2024. It is now read-only.

Commit a7ccfae

Browse files
committed
feat: adds generateFileURL to override file urls
1 parent e935992 commit a7ccfae

5 files changed

Lines changed: 118 additions & 87 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ This plugin is configurable to work across many different Payload collections. A
5151

5252
**Collection-specific options:**
5353

54-
| Option | Type | Description |
55-
|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
56-
| `adapter` * | [Adapter](https://github.com/payloadcms/plugin-cloud-storage/blob/c4a492a62abc2f21b4cd6a7c97778acd8e831212/src/types.ts#L46) | Pass in the adapter that you'd like to use for this collection. You can also set this field to `null` for local development if you'd like to bypass cloud storage in certain scenarios and use local storage. |
57-
| `disableLocalStorage` | `boolean` | Choose to disable local storage on this collection. Defaults to `true`. |
58-
| `disablePayloadAccessControl` | `true` | Set to `true` to disable Payload's access control. [More](#payload-access-control) |
59-
| `prefix` | `string` | Set to `media/images` to upload files inside `media/images` folder in the bucket. |
54+
| Option | Type | Description |
55+
|-------------------------------|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
56+
| `adapter` * | [Adapter](https://github.com/payloadcms/plugin-cloud-storage/blob/master/src/types.ts#L51) | Pass in the adapter that you'd like to use for this collection. You can also set this field to `null` for local development if you'd like to bypass cloud storage in certain scenarios and use local storage. |
57+
| `disableLocalStorage` | `boolean` | Choose to disable local storage on this collection. Defaults to `true`. |
58+
| `disablePayloadAccessControl` | `true` | Set to `true` to disable Payload's access control. [More](#payload-access-control) |
59+
| `prefix` | `string` | Set to `media/images` to upload files inside `media/images` folder in the bucket. |
60+
| `generateFileURL` | [GenerateFileURL](https://github.com/payloadcms/plugin-cloud-storage/blob/master/src/types.ts#L53) | Override the generated file URL with one that you create. |
6061

6162
### Azure Blob Storage Adapter
6263

src/fields/getFields.ts

Lines changed: 78 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import path from 'path'
22
import type { GroupField, TextField } from 'payload/dist/fields/config/types'
33
import type { CollectionConfig, Field } from 'payload/types'
44
import { getAfterReadHook } from '../hooks/afterRead'
5-
import type { GeneratedAdapter } from '../types'
5+
import type { GeneratedAdapter, GenerateFileURL } from '../types'
66

77
interface Args {
88
collection: CollectionConfig
99
disablePayloadAccessControl?: true
10+
generateFileURL?: GenerateFileURL
1011
prefix?: string
1112
adapter: GeneratedAdapter
1213
}
@@ -15,6 +16,7 @@ export const getFields = ({
1516
adapter,
1617
collection,
1718
disablePayloadAccessControl,
19+
generateFileURL,
1820
prefix,
1921
}: Args): Field[] => {
2022
const baseURLField: Field = {
@@ -38,88 +40,92 @@ export const getFields = ({
3840

3941
const fields = [...collection.fields]
4042

41-
// If Payload access control is disabled,
42-
// inject a hook into all URL fields to point directly to the cloud source
43-
if (disablePayloadAccessControl) {
44-
let existingURLFieldIndex = -1
43+
// Inject a hook into all URL fields to generate URLs
4544

46-
const existingURLField = fields.find((existingField, i) => {
47-
if ('name' in existingField && existingField.name === 'url') {
48-
existingURLFieldIndex = i
49-
return true
50-
}
51-
return false
52-
}) as TextField
45+
let existingURLFieldIndex = -1
5346

54-
if (existingURLFieldIndex > -1) {
55-
fields.splice(existingURLFieldIndex, 1)
47+
const existingURLField = fields.find((existingField, i) => {
48+
if ('name' in existingField && existingField.name === 'url') {
49+
existingURLFieldIndex = i
50+
return true
5651
}
52+
return false
53+
}) as TextField
5754

58-
fields.push({
59-
...baseURLField,
60-
...(existingURLField || {}),
61-
hooks: {
62-
afterRead: [
63-
getAfterReadHook({ adapter, collection }),
64-
...(existingURLField?.hooks?.afterRead || []),
65-
],
66-
},
67-
})
68-
69-
if (typeof collection.upload === 'object' && collection.upload.imageSizes) {
70-
let existingSizesFieldIndex = -1
55+
if (existingURLFieldIndex > -1) {
56+
fields.splice(existingURLFieldIndex, 1)
57+
}
7158

72-
const existingSizesField = fields.find((existingField, i) => {
73-
if ('name' in existingField && existingField.name === 'sizes') {
74-
existingSizesFieldIndex = i
75-
return true
76-
}
59+
fields.push({
60+
...baseURLField,
61+
...(existingURLField || {}),
62+
hooks: {
63+
afterRead: [
64+
getAfterReadHook({ adapter, collection, disablePayloadAccessControl, generateFileURL }),
65+
...(existingURLField?.hooks?.afterRead || []),
66+
],
67+
},
68+
})
7769

78-
return false
79-
}) as GroupField
70+
if (typeof collection.upload === 'object' && collection.upload.imageSizes) {
71+
let existingSizesFieldIndex = -1
8072

81-
if (existingSizesFieldIndex > -1) {
82-
fields.splice(existingSizesFieldIndex, 1)
73+
const existingSizesField = fields.find((existingField, i) => {
74+
if ('name' in existingField && existingField.name === 'sizes') {
75+
existingSizesFieldIndex = i
76+
return true
8377
}
8478

85-
const sizesField: Field = {
86-
...(existingSizesField || {}),
87-
name: 'sizes',
88-
type: 'group',
89-
admin: {
90-
disabled: true,
91-
},
92-
fields: collection.upload.imageSizes.map(size => {
93-
const existingSizeField = existingSizesField?.fields.find(
94-
existingField => 'name' in existingField && existingField.name === size.name,
95-
) as GroupField
96-
97-
const existingSizeURLField = existingSizeField?.fields.find(
98-
existingField => 'name' in existingField && existingField.name === 'url',
99-
) as GroupField
100-
101-
return {
102-
...existingSizeField,
103-
name: size.name,
104-
type: 'group',
105-
fields: [
106-
{
107-
...(existingSizeURLField || {}),
108-
...baseURLField,
109-
hooks: {
110-
afterRead: [
111-
getAfterReadHook({ adapter, collection, size }),
112-
...(existingSizeURLField?.hooks?.afterRead || []),
113-
],
114-
},
115-
},
116-
],
117-
}
118-
}),
119-
}
79+
return false
80+
}) as GroupField
81+
82+
if (existingSizesFieldIndex > -1) {
83+
fields.splice(existingSizesFieldIndex, 1)
84+
}
12085

121-
fields.push(sizesField)
86+
const sizesField: Field = {
87+
...(existingSizesField || {}),
88+
name: 'sizes',
89+
type: 'group',
90+
admin: {
91+
disabled: true,
92+
},
93+
fields: collection.upload.imageSizes.map(size => {
94+
const existingSizeField = existingSizesField?.fields.find(
95+
existingField => 'name' in existingField && existingField.name === size.name,
96+
) as GroupField
97+
98+
const existingSizeURLField = existingSizeField?.fields.find(
99+
existingField => 'name' in existingField && existingField.name === 'url',
100+
) as GroupField
101+
102+
return {
103+
...existingSizeField,
104+
name: size.name,
105+
type: 'group',
106+
fields: [
107+
{
108+
...(existingSizeURLField || {}),
109+
...baseURLField,
110+
hooks: {
111+
afterRead: [
112+
getAfterReadHook({
113+
adapter,
114+
collection,
115+
size,
116+
disablePayloadAccessControl,
117+
generateFileURL,
118+
}),
119+
...(existingSizeURLField?.hooks?.afterRead || []),
120+
],
121+
},
122+
},
123+
],
124+
}
125+
}),
122126
}
127+
128+
fields.push(sizesField)
123129
}
124130

125131
// If prefix is enabled, save it to db

src/hooks/afterRead.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
11
import type { ImageSize } from 'payload/dist/uploads/types'
22
import type { CollectionConfig, FieldHook } from 'payload/types'
3-
import type { GeneratedAdapter } from '../types'
3+
import type { GeneratedAdapter, GenerateFileURL } from '../types'
44

55
interface Args {
66
collection: CollectionConfig
77
adapter: GeneratedAdapter
8+
disablePayloadAccessControl?: boolean
89
size?: ImageSize
10+
generateFileURL?: GenerateFileURL
911
}
1012

1113
export const getAfterReadHook =
12-
({ collection, adapter, size }: Args): FieldHook =>
14+
({ collection, adapter, size, disablePayloadAccessControl, generateFileURL }: Args): FieldHook =>
1315
async ({ data, value }) => {
1416
const filename = size ? data?.sizes?.[size.name]?.filename : data?.filename
1517
const prefix = data?.prefix
1618

17-
const url = await adapter.generateURL({
18-
collection,
19-
filename,
20-
prefix,
21-
})
19+
let url = value
2220

23-
return url || value
21+
if (disablePayloadAccessControl) {
22+
url = await adapter.generateURL({
23+
collection,
24+
filename,
25+
prefix,
26+
})
27+
}
28+
29+
if (generateFileURL) {
30+
url = await generateFileURL({
31+
collection,
32+
filename,
33+
prefix,
34+
size,
35+
})
36+
}
37+
38+
return url
2439
}

src/plugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const cloudStorage =
3939
const fields = getFields({
4040
collection: existingCollection,
4141
disablePayloadAccessControl: options.disablePayloadAccessControl,
42+
generateFileURL: options.generateFileURL,
4243
prefix: options.prefix,
4344
adapter,
4445
})

src/types.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { NextFunction, Response } from 'express'
22
import type { TypeWithID } from 'payload/dist/collections/config/types'
3-
import type { FileData } from 'payload/dist/uploads/types'
3+
import type { FileData, ImageSize } from 'payload/dist/uploads/types'
44
import type { CollectionConfig, PayloadRequest } from 'payload/types'
55
import type { Configuration as WebpackConfig } from 'webpack'
66

@@ -50,9 +50,17 @@ export interface GeneratedAdapter {
5050

5151
export type Adapter = (args: { collection: CollectionConfig; prefix?: string }) => GeneratedAdapter
5252

53+
export type GenerateFileURL = (args: {
54+
collection: CollectionConfig
55+
filename: string
56+
prefix?: string
57+
size?: ImageSize
58+
}) => Promise<string> | string
59+
5360
export interface CollectionOptions {
5461
disableLocalStorage?: boolean
5562
disablePayloadAccessControl?: true
63+
generateFileURL?: GenerateFileURL
5664
prefix?: string
5765
adapter: Adapter | null
5866
}

0 commit comments

Comments
 (0)