Added support for FTP uploads

This commit is contained in:
DrMint 2024-04-04 21:42:48 +02:00
parent b7dca38786
commit 609bbe5866
9 changed files with 194 additions and 2 deletions

View File

@ -12,3 +12,11 @@ STRAPI_TOKEN=strapisecreta5e6ea45ef4e66eaa151612bdcb599df
SEEDING_ADMIN_USERNAME=admin_name
SEEDING_ADMIN_EMAIL=email@domain.com
SEEDING_ADMIN_PASSWORD=somepassword
WEB_HOOK_TOKEN=webhooktoken5e6ea45ef4e66eaa151612bdcb599df
WEB_HOOK_URI=https://accords-library.com/some/path
FTP_USER=someuser
FTP_PASSWORD=somepassword
FTP_HOST=ftp.host.com
FTP_BASE_URL=https://ftp-base-url.com

44
package-lock.json generated
View File

@ -12,7 +12,9 @@
"@fontsource/vollkorn": "5.0.19",
"@payloadcms/bundler-webpack": "1.0.6",
"@payloadcms/db-mongodb": "1.4.4",
"@payloadcms/plugin-cloud-storage": "^1.1.2",
"@payloadcms/richtext-lexical": "0.8.0",
"basic-ftp": "^5.0.5",
"cross-env": "7.0.3",
"language-tags": "1.0.9",
"luxon": "3.4.4",
@ -3109,6 +3111,40 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/@payloadcms/plugin-cloud-storage": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@payloadcms/plugin-cloud-storage/-/plugin-cloud-storage-1.1.2.tgz",
"integrity": "sha512-wBpEWv4VdfltBqEi5ECSrKQ/TtNvmBWT4DLCrTCOhpNuD8dYD2rp+0Eoj7cF8f6wU7DpS5rvdQ/8gxlqh0Armw==",
"dependencies": {
"find-node-modules": "^2.1.3",
"range-parser": "^1.2.1"
},
"peerDependencies": {
"@aws-sdk/client-s3": "^3.142.0",
"@aws-sdk/lib-storage": "^3.267.0",
"@azure/abort-controller": "^1.0.0",
"@azure/storage-blob": "^12.11.0",
"@google-cloud/storage": "^6.4.1",
"payload": "^1.7.2 || ^2.0.0"
},
"peerDependenciesMeta": {
"@aws-sdk/client-s3": {
"optional": true
},
"@aws-sdk/lib-storage": {
"optional": true
},
"@azure/abort-controller": {
"optional": true
},
"@azure/storage-blob": {
"optional": true
},
"@google-cloud/storage": {
"optional": true
}
}
},
"node_modules/@payloadcms/richtext-lexical": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@payloadcms/richtext-lexical/-/richtext-lexical-0.8.0.tgz",
@ -5116,6 +5152,14 @@
}
]
},
"node_modules/basic-ftp": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
"integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",

View File

@ -25,7 +25,9 @@
"@fontsource/vollkorn": "5.0.19",
"@payloadcms/bundler-webpack": "1.0.6",
"@payloadcms/db-mongodb": "1.4.4",
"@payloadcms/plugin-cloud-storage": "^1.1.2",
"@payloadcms/richtext-lexical": "0.8.0",
"basic-ftp": "^5.0.5",
"cross-env": "7.0.3",
"language-tags": "1.0.9",
"luxon": "3.4.4",

View File

@ -0,0 +1,13 @@
import { CollectionGroups, Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
export const Videos = buildCollectionConfig({
slug: Collections.Videos,
labels: { singular: "Video", plural: "Videos" },
admin: { group: CollectionGroups.Media },
upload: {
mimeTypes: ["video/*"],
disableLocalStorage: true,
},
fields: [],
});

View File

@ -19,6 +19,7 @@ export enum Collections {
Collectibles = "collectibles",
GenericContents = "generic-contents",
HomeFolders = "home-folders",
Videos = "videos",
}
export enum CollectionGroups {

View File

@ -1,5 +1,6 @@
import { webpackBundler } from "@payloadcms/bundler-webpack";
import { mongooseAdapter } from "@payloadcms/db-mongodb";
import { cloudStorage } from "@payloadcms/plugin-cloud-storage";
import path from "path";
import { buildConfig } from "payload/config";
import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents";
@ -15,12 +16,21 @@ import { Pages } from "./collections/Pages/Pages";
import { Recorders } from "./collections/Recorders/Recorders";
import { Tags } from "./collections/Tags/Tags";
import { TagsGroups } from "./collections/TagsGroups/TagsGroups";
import { Videos } from "./collections/Videos/Videos";
import { Wordings } from "./collections/Wordings/Wordings";
import { Icon } from "./components/Icon";
import { Logo } from "./components/Logo";
import { Collections } from "./constants";
import { ftpAdapter } from "./plugins/ftpAdapter";
import { createEditor } from "./utils/editor";
if (!process.env.PAYLOAD_URI) throw new Error("Missing PAYLOAD_URI in .env");
if (!process.env.MONGODB_URI) throw new Error("Missing MONGODB_URI in .env");
if (!process.env.FTP_HOST) throw new Error("Missing FTP_HOST in .env");
if (!process.env.FTP_USER) throw new Error("Missing FTP_USER in .env");
if (!process.env.FTP_PASSWORD) throw new Error("Missing FTP_PASSWORD in .env");
if (!process.env.FTP_BASE_URL) throw new Error("Missing FTP_BASE_URL in .env");
export default buildConfig({
serverURL: process.env.PAYLOAD_URI,
admin: {
@ -43,6 +53,7 @@ export default buildConfig({
Notes,
Images,
Videos,
Tags,
TagsGroups,
@ -53,7 +64,7 @@ export default buildConfig({
GenericContents,
],
db: mongooseAdapter({
url: process.env.MONGODB_URI ?? "mongodb://mongo:27017/payload",
url: process.env.MONGODB_URI,
}),
globals: [HomeFolders],
telemetry: false,
@ -63,4 +74,21 @@ export default buildConfig({
graphQL: {
disable: true,
},
plugins: [
cloudStorage({
collections: {
[Collections.Videos]: {
adapter: ftpAdapter({
host: process.env.FTP_HOST,
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
secure: false,
endpoint: process.env.FTP_BASE_URL,
}),
disableLocalStorage: true,
disablePayloadAccessControl: true,
},
},
}),
],
});

79
src/plugins/ftpAdapter.ts Normal file
View File

@ -0,0 +1,79 @@
import {
Adapter,
GenerateURL,
HandleDelete,
HandleUpload,
} from "@payloadcms/plugin-cloud-storage/dist/types";
import { Client } from "basic-ftp";
import path from "path";
import { Readable } from "stream";
import type { Configuration as WebpackConfig } from "webpack";
interface FTPAdapterConfig {
host: string;
user: string;
password: string;
secure: boolean;
endpoint: string;
}
export const ftpAdapter =
({ endpoint, host, password, secure, user }: FTPAdapterConfig): Adapter =>
({ collection }) => {
const generateURL: GenerateURL = ({ filename }) => `${endpoint}/${collection.slug}/${filename}`;
const handleDelete: HandleDelete = async ({ filename }) => {
const client = new Client();
client.ftp.verbose = true;
await client.access({
host,
user,
password,
secure,
});
await client.ensureDir(collection.slug);
await client.remove(filename);
client.close();
};
const handleUpload: HandleUpload = async ({ file }) => {
const client = new Client();
client.ftp.verbose = true;
await client.access({
host: process.env.FTP_HOST,
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
secure: false,
});
await client.ensureDir(collection.slug);
await client.uploadFrom(Readable.from(file.buffer), file.filename);
client.close();
};
const webpack = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
const newConfig: WebpackConfig = {
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
"./plugins/ftpAdapter": path.resolve(__dirname, "./mock.js"),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback
? existingWebpackConfig.resolve.fallback
: {}),
stream: false,
},
},
};
return newConfig;
};
return {
generateURL,
handleDelete,
handleUpload,
staticHandler: () => {},
webpack,
};
};

1
src/plugins/mock.js Normal file
View File

@ -0,0 +1 @@
export const ftpAdapter = () => {};

View File

@ -49,6 +49,7 @@ export interface Config {
"chronology-events": ChronologyEvent;
notes: Note;
images: Image;
videos: Video;
tags: Tag;
"tags-groups": TagsGroup;
recorders: Recorder;
@ -622,6 +623,21 @@ export interface Note {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos".
*/
export interface Video {
id: string;
updatedAt: string;
createdAt: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "wordings".