Skip to content

Commit 73d2fb0

Browse files
committed
feat: add GitHub Actions workflow for testing builds and improve type safety in hooks
1 parent 0f02a50 commit 73d2fb0

File tree

4 files changed

+158
-106
lines changed

4 files changed

+158
-106
lines changed

.github/workflows/test-build.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Test Build 🔧
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
node_version:
7+
description: 'Node.js version'
8+
required: false
9+
default: '20'
10+
type: choice
11+
options:
12+
- '18'
13+
- '20'
14+
- '22'
15+
16+
jobs:
17+
test-build:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout 🛎️
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Node.js 🔧
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: ${{ github.event.inputs.node_version || '20' }}
28+
29+
- name: Setup PNPM 📦
30+
uses: pnpm/action-setup@v2
31+
with:
32+
version: 8
33+
34+
- name: Install dependencies ⚡
35+
run: pnpm install
36+
37+
- name: Type check 🔍
38+
run: pnpm typecheck
39+
40+
- name: Lint code ✨
41+
run: pnpm lint
42+
43+
- name: Build project 🔨
44+
run: pnpm build
45+
46+
- name: Check build output 📁
47+
run: |
48+
echo "Checking if build output exists..."
49+
ls -la dist/
50+
echo "Build completed successfully! ✅"

dev/payload.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export default buildConfig({
7676
adminThumbnail: {
7777
transformUrl: ({ baseUrl, data }) => {
7878
const timestamp = Date.now()
79-
const customId = data?.id || 'unknown'
79+
const customId = data?.id as string || 'unknown'
8080
return `${baseUrl}?secure_thumb=true&id=${customId}&t=${timestamp}`
8181
},
8282
},

src/hooks/afterChange.ts

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,51 @@ import type { CollectionAfterChangeHook, FileData, JsonObject, TypeWithID } from
44
import { getHandleDelete } from '@/handlers/index.js'
55
import { deleteStreamVideoSession } from '@/utils/index.js'
66

7-
export const getAfterChangeHook =
8-
(context: CollectionContext): CollectionAfterChangeHook<FileData & JsonObject & TypeWithID> => {
9-
return async ({ data, req }) => {
10-
if (context.streamConfig?.cleanup && 'bunnyVideoId' in data) {
11-
await deleteStreamVideoSession({
12-
libraryId: context.streamConfig.libraryId,
13-
payload: req.payload,
14-
videoId: data.bunnyVideoId,
15-
})
16-
}
7+
type Data = FileData & JsonObject & TypeWithID
8+
9+
export const getAfterChangeHook = (context: CollectionContext): CollectionAfterChangeHook<Data> => {
10+
return async ({ data, req }) => {
11+
if (context.streamConfig?.cleanup && 'bunnyVideoId' in data) {
12+
await deleteStreamVideoSession({
13+
libraryId: context.streamConfig.libraryId,
14+
payload: req.payload,
15+
videoId: data.bunnyVideoId,
16+
})
17+
}
1718

18-
if (context.isTusUploadSupported) {
19-
const oldDoc = req.context?.oldDoc
19+
if (context.isTusUploadSupported) {
20+
const oldDoc = req.context?.oldDoc
2021

21-
if (!oldDoc ||
22+
if (!oldDoc ||
2223
typeof oldDoc !== 'object' ||
2324
!('filename' in oldDoc) ||
2425
typeof oldDoc.filename !== 'string') {
25-
return
26-
}
27-
28-
const handleDelete = getHandleDelete(context)
29-
30-
try {
31-
await handleDelete({
32-
collection: context.collection,
33-
doc: oldDoc as FileData & JsonObject & TypeWithID,
34-
filename: oldDoc.filename,
35-
req,
36-
})
37-
38-
req.payload.logger.debug({
39-
action: 'File cleanup after upload change',
40-
filename: oldDoc.filename,
41-
message: `Successfully deleted old file: ${oldDoc.filename}`,
42-
})
43-
} catch (err) {
44-
req.payload.logger.error({
45-
action: 'File cleanup after upload change',
46-
err,
47-
filename: oldDoc.filename,
48-
message: `Failed to delete old file: ${oldDoc.filename}`,
49-
})
50-
}
26+
return
27+
}
28+
29+
const handleDelete = getHandleDelete(context)
30+
31+
try {
32+
await handleDelete({
33+
collection: context.collection,
34+
doc: oldDoc as FileData & JsonObject & TypeWithID,
35+
filename: oldDoc.filename,
36+
req,
37+
})
38+
39+
req.payload.logger.debug({
40+
action: 'File cleanup after upload change',
41+
filename: oldDoc.filename,
42+
message: `Successfully deleted old file: ${oldDoc.filename}`,
43+
})
44+
} catch (err) {
45+
req.payload.logger.error({
46+
action: 'File cleanup after upload change',
47+
err,
48+
filename: oldDoc.filename,
49+
message: `Failed to delete old file: ${oldDoc.filename}`,
50+
})
5151
}
5252
}
5353
}
54+
}

src/hooks/beforeValidate.ts

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,70 @@ type Args = {
1010
filesRequiredOnCreate: boolean
1111
}
1212

13-
export const getBeforeValidateHook =
14-
({ context, filesRequiredOnCreate }: Args): CollectionBeforeValidateHook<JsonObject & TypeWithID> => {
15-
return async ({ data, operation, originalDoc, req }) => {
16-
const file = req.file
17-
18-
if (operation === 'create' && filesRequiredOnCreate && !data?.bunnyVideoId && !file) {
19-
throw new MissingFile(req.t)
20-
}
21-
22-
if (data && !data.bunnyVideoId) {
23-
data.bunnyVideoId = null
24-
}
25-
26-
const processVideoData = async (videoId: string, targetData: typeof data) => {
27-
if (!context.streamConfig || !targetData) {
28-
return
29-
}
30-
31-
const videoData = await getStreamVideo({
32-
streamConfig: context.streamConfig,
33-
videoId,
34-
})
35-
36-
if (isVideoProcessed(videoData.status)) {
37-
const safeFilename = await getSafeFileName({
38-
collectionSlug: context.collection.slug,
39-
desiredFilename: videoData.title || `video-${videoData.guid}`,
40-
req,
41-
staticPath: '',
42-
})
43-
44-
targetData.filename = safeFilename
45-
targetData.width = null
46-
targetData.height = null
47-
targetData.focalX = null
48-
targetData.focalY = null
49-
targetData.bunnyVideoId = videoData.guid
50-
51-
if (!targetData.mimeType) {
52-
targetData.mimeType = 'video/mp4'
53-
}
54-
55-
if (!targetData.filesize) {
56-
targetData.filesize = videoData.storageSize
57-
}
58-
}
59-
}
60-
61-
if (operation === 'update' && originalDoc && data) {
62-
if (!file && data.bunnyVideoId && data.bunnyVideoId !== originalDoc.bunnyVideoId) {
63-
if (!req.context) {
64-
req.context = {}
65-
}
66-
req.context.oldDoc = originalDoc
67-
68-
await processVideoData(data.bunnyVideoId, data)
69-
}
70-
}
71-
72-
if (operation === 'create' && !file && data?.bunnyVideoId) {
73-
await processVideoData(data.bunnyVideoId, data)
74-
}
75-
76-
return data
77-
}
78-
}
13+
type Data = JsonObject & TypeWithID
14+
15+
export const getBeforeValidateHook = ({ context, filesRequiredOnCreate }: Args): CollectionBeforeValidateHook<Data> => {
16+
return async ({ data, operation, originalDoc, req }) => {
17+
const file = req.file
18+
19+
if (operation === 'create' && filesRequiredOnCreate && !data?.bunnyVideoId && !file) {
20+
throw new MissingFile(req.t)
21+
}
22+
23+
if (data && !data.bunnyVideoId) {
24+
data.bunnyVideoId = null
25+
}
26+
27+
const processVideoData = async (videoId: string, targetData: typeof data) => {
28+
if (!context.streamConfig || !targetData) {
29+
return
30+
}
31+
32+
const videoData = await getStreamVideo({
33+
streamConfig: context.streamConfig,
34+
videoId,
35+
})
36+
37+
if (isVideoProcessed(videoData.status)) {
38+
const safeFilename = await getSafeFileName({
39+
collectionSlug: context.collection.slug,
40+
desiredFilename: videoData.title || `video-${videoData.guid}`,
41+
req,
42+
staticPath: '',
43+
})
44+
45+
targetData.filename = safeFilename
46+
targetData.width = null
47+
targetData.height = null
48+
targetData.focalX = null
49+
targetData.focalY = null
50+
targetData.bunnyVideoId = videoData.guid
51+
52+
if (!targetData.mimeType) {
53+
targetData.mimeType = 'video/mp4'
54+
}
55+
56+
if (!targetData.filesize) {
57+
targetData.filesize = videoData.storageSize
58+
}
59+
}
60+
}
61+
62+
if (operation === 'update' && originalDoc && data) {
63+
if (!file && data.bunnyVideoId && data.bunnyVideoId !== originalDoc.bunnyVideoId) {
64+
if (!req.context) {
65+
req.context = {}
66+
}
67+
req.context.oldDoc = originalDoc
68+
69+
await processVideoData(data.bunnyVideoId, data)
70+
}
71+
}
72+
73+
if (operation === 'create' && !file && data?.bunnyVideoId) {
74+
await processVideoData(data.bunnyVideoId, data)
75+
}
76+
77+
return data
78+
}
79+
}

0 commit comments

Comments
 (0)