mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 06:34:05 -08:00
Add schemas for ftp and sftp
This commit is contained in:
parent
6f15aa6a88
commit
2d40b299df
|
@ -1,4 +1,14 @@
|
||||||
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
import type { ICredentialType } from 'n8n-workflow';
|
||||||
|
import { CredentialSchema, type InferCredentialSchema } from '../utils/CredentialSchema';
|
||||||
|
|
||||||
|
const ftpCredentialSchema = CredentialSchema.create({
|
||||||
|
host: CredentialSchema.string({ label: 'Host', placeholder: 'localhost' }),
|
||||||
|
port: CredentialSchema.number({ label: 'Port', default: 21 }),
|
||||||
|
username: CredentialSchema.string({ label: 'Username', optional: true }),
|
||||||
|
password: CredentialSchema.password({ optional: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FtpCredentialSchema = InferCredentialSchema<typeof ftpCredentialSchema>;
|
||||||
|
|
||||||
export class Ftp implements ICredentialType {
|
export class Ftp implements ICredentialType {
|
||||||
name = 'ftp';
|
name = 'ftp';
|
||||||
|
@ -7,36 +17,5 @@ export class Ftp implements ICredentialType {
|
||||||
|
|
||||||
documentationUrl = 'ftp';
|
documentationUrl = 'ftp';
|
||||||
|
|
||||||
properties: INodeProperties[] = [
|
properties = ftpCredentialSchema.toNodeProperties();
|
||||||
{
|
|
||||||
displayName: 'Host',
|
|
||||||
name: 'host',
|
|
||||||
required: true,
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
placeholder: 'localhost',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Port',
|
|
||||||
name: 'port',
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
default: 21,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Username',
|
|
||||||
name: 'username',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Password',
|
|
||||||
name: 'password',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: {
|
|
||||||
password: true,
|
|
||||||
},
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,23 @@
|
||||||
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
import type { ICredentialType } from 'n8n-workflow';
|
||||||
|
import { CredentialSchema, type InferCredentialSchema } from '../utils/CredentialSchema';
|
||||||
|
|
||||||
|
const sftpCredentialSchema = CredentialSchema.create({
|
||||||
|
host: CredentialSchema.string({ label: 'Host', placeholder: 'localhost' }),
|
||||||
|
port: CredentialSchema.number({ label: 'Port', default: 22 }),
|
||||||
|
username: CredentialSchema.string({ label: 'Username' }),
|
||||||
|
password: CredentialSchema.password(),
|
||||||
|
privateKey: CredentialSchema.password({
|
||||||
|
label: 'Private Key',
|
||||||
|
description:
|
||||||
|
'String that contains a private key for either key-based or hostbased user authentication (OpenSSH format)',
|
||||||
|
}),
|
||||||
|
passphrase: CredentialSchema.password({
|
||||||
|
label: 'Passphrase',
|
||||||
|
description: 'For an encrypted private key, this is the passphrase used to decrypt it',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SftpCredentialSchema = InferCredentialSchema<typeof sftpCredentialSchema>;
|
||||||
|
|
||||||
export class Sftp implements ICredentialType {
|
export class Sftp implements ICredentialType {
|
||||||
name = 'sftp';
|
name = 'sftp';
|
||||||
|
@ -7,55 +26,5 @@ export class Sftp implements ICredentialType {
|
||||||
|
|
||||||
documentationUrl = 'ftp';
|
documentationUrl = 'ftp';
|
||||||
|
|
||||||
properties: INodeProperties[] = [
|
properties = sftpCredentialSchema.toNodeProperties();
|
||||||
{
|
|
||||||
displayName: 'Host',
|
|
||||||
name: 'host',
|
|
||||||
required: true,
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Port',
|
|
||||||
name: 'port',
|
|
||||||
required: true,
|
|
||||||
type: 'number',
|
|
||||||
default: 22,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Username',
|
|
||||||
name: 'username',
|
|
||||||
required: true,
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Password',
|
|
||||||
name: 'password',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: {
|
|
||||||
password: true,
|
|
||||||
},
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Private Key',
|
|
||||||
name: 'privateKey',
|
|
||||||
type: 'string',
|
|
||||||
typeOptions: { password: true },
|
|
||||||
default: '',
|
|
||||||
description:
|
|
||||||
'String that contains a private key for either key-based or hostbased user authentication (OpenSSH format)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Passphrase',
|
|
||||||
name: 'passphrase',
|
|
||||||
typeOptions: {
|
|
||||||
password: true,
|
|
||||||
},
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
description: 'For an encrypted private key, this is the passphrase used to decrypt it',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import ftpClient from 'promise-ftp';
|
||||||
import sftpClient from 'ssh2-sftp-client';
|
import sftpClient from 'ssh2-sftp-client';
|
||||||
import { BINARY_ENCODING, NodeApiError } from 'n8n-workflow';
|
import { BINARY_ENCODING, NodeApiError } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
ICredentialDataDecryptedObject,
|
|
||||||
ICredentialsDecrypted,
|
ICredentialsDecrypted,
|
||||||
ICredentialTestFunctions,
|
ICredentialTestFunctions,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
@ -18,6 +17,8 @@ import type {
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
JsonObject,
|
JsonObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import type { FtpCredentialSchema } from '@credentials/Ftp.credentials';
|
||||||
|
import type { SftpCredentialSchema } from '@credentials/Sftp.credentials';
|
||||||
import { formatPrivateKey, generatePairedItemData } from '@utils/utilities';
|
import { formatPrivateKey, generatePairedItemData } from '@utils/utilities';
|
||||||
|
|
||||||
interface ReturnFtpItem {
|
interface ReturnFtpItem {
|
||||||
|
@ -439,14 +440,14 @@ export class Ftp implements INodeType {
|
||||||
this: ICredentialTestFunctions,
|
this: ICredentialTestFunctions,
|
||||||
credential: ICredentialsDecrypted,
|
credential: ICredentialsDecrypted,
|
||||||
): Promise<INodeCredentialTestResult> {
|
): Promise<INodeCredentialTestResult> {
|
||||||
const credentials = credential.data as ICredentialDataDecryptedObject;
|
const credentials = credential.data as FtpCredentialSchema;
|
||||||
const ftp = new ftpClient();
|
const ftp = new ftpClient();
|
||||||
try {
|
try {
|
||||||
await ftp.connect({
|
await ftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
user: credentials.username as string,
|
user: credentials.username,
|
||||||
password: credentials.password as string,
|
password: credentials.password,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await ftp.end();
|
await ftp.end();
|
||||||
|
@ -465,24 +466,24 @@ export class Ftp implements INodeType {
|
||||||
this: ICredentialTestFunctions,
|
this: ICredentialTestFunctions,
|
||||||
credential: ICredentialsDecrypted,
|
credential: ICredentialsDecrypted,
|
||||||
): Promise<INodeCredentialTestResult> {
|
): Promise<INodeCredentialTestResult> {
|
||||||
const credentials = credential.data as ICredentialDataDecryptedObject;
|
const credentials = credential.data as SftpCredentialSchema;
|
||||||
const sftp = new sftpClient();
|
const sftp = new sftpClient();
|
||||||
try {
|
try {
|
||||||
if (credentials.privateKey) {
|
if (credentials.privateKey) {
|
||||||
await sftp.connect({
|
await sftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
username: credentials.username as string,
|
username: credentials.username,
|
||||||
password: (credentials.password as string) || undefined,
|
password: credentials.password || undefined,
|
||||||
privateKey: formatPrivateKey(credentials.privateKey as string),
|
privateKey: formatPrivateKey(credentials.privateKey),
|
||||||
passphrase: credentials.passphrase as string | undefined,
|
passphrase: credentials.passphrase,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await sftp.connect({
|
await sftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
username: credentials.username as string,
|
username: credentials.username,
|
||||||
password: credentials.password as string,
|
password: credentials.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -516,30 +517,30 @@ export class Ftp implements INodeType {
|
||||||
try {
|
try {
|
||||||
if (protocol === 'sftp') {
|
if (protocol === 'sftp') {
|
||||||
sftp = new sftpClient();
|
sftp = new sftpClient();
|
||||||
if (credentials.privateKey) {
|
if ('privateKey' in credentials && credentials.privateKey) {
|
||||||
await sftp.connect({
|
await sftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
username: credentials.username as string,
|
username: credentials.username,
|
||||||
password: (credentials.password as string) || undefined,
|
password: credentials.password || undefined,
|
||||||
privateKey: formatPrivateKey(credentials.privateKey as string),
|
privateKey: formatPrivateKey(credentials.privateKey),
|
||||||
passphrase: credentials.passphrase as string | undefined,
|
passphrase: credentials.passphrase,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await sftp.connect({
|
await sftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
username: credentials.username as string,
|
username: credentials.username,
|
||||||
password: credentials.password as string,
|
password: credentials.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ftp = new ftpClient();
|
ftp = new ftpClient();
|
||||||
await ftp.connect({
|
await ftp.connect({
|
||||||
host: credentials.host as string,
|
host: credentials.host,
|
||||||
port: credentials.port as number,
|
port: credentials.port,
|
||||||
user: credentials.username as string,
|
user: credentials.username,
|
||||||
password: credentials.password as string,
|
password: credentials.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type {
|
||||||
JsonObject,
|
JsonObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeApiError } from 'n8n-workflow';
|
import { NodeApiError } from 'n8n-workflow';
|
||||||
import type { StrapiApiCredential } from '../../credentials/StrapiApi.credentials';
|
import type { StrapiApiCredential } from '@credentials/StrapiApi.credentials';
|
||||||
|
|
||||||
export const removeTrailingSlash = (url: string) => {
|
export const removeTrailingSlash = (url: string) => {
|
||||||
if (url.endsWith('/')) {
|
if (url.endsWith('/')) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import type {
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeOperationError } from 'n8n-workflow';
|
import { NodeOperationError } from 'n8n-workflow';
|
||||||
|
|
||||||
import type { StrapiApiCredential } from '../../credentials/StrapiApi.credentials';
|
|
||||||
import {
|
import {
|
||||||
getToken,
|
getToken,
|
||||||
removeTrailingSlash,
|
removeTrailingSlash,
|
||||||
|
@ -21,6 +20,7 @@ import {
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import { entryFields, entryOperations } from './EntryDescription';
|
import { entryFields, entryOperations } from './EntryDescription';
|
||||||
|
import type { StrapiApiCredential } from '@credentials/StrapiApi.credentials';
|
||||||
|
|
||||||
export class Strapi implements INodeType {
|
export class Strapi implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
"lib": ["dom", "es2020", "es2022.error"],
|
"lib": ["dom", "es2020", "es2022.error"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@test/*": ["./test/*"],
|
"@test/*": ["./test/*"],
|
||||||
"@utils/*": ["./utils/*"]
|
"@utils/*": ["./utils/*"],
|
||||||
|
"@nodes/*": ["./nodes/*"],
|
||||||
|
"@credentials/*": ["./credentials/*"]
|
||||||
},
|
},
|
||||||
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
|
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
|
||||||
// TODO: remove all options below this line
|
// TODO: remove all options below this line
|
||||||
|
|
4
packages/nodes-base/types.d.ts
vendored
4
packages/nodes-base/types.d.ts
vendored
|
@ -1,9 +1,13 @@
|
||||||
|
import type { FtpCredentialSchema } from './credentials/Ftp.credentials';
|
||||||
|
import type { SftpCredentialSchema } from './credentials/Sftp.credentials';
|
||||||
import type { StrapiApiCredential } from './credentials/StrapiApi.credentials';
|
import type { StrapiApiCredential } from './credentials/StrapiApi.credentials';
|
||||||
import type { StrapiTokenApiCredential } from './credentials/StrapiTokenApi.credentials';
|
import type { StrapiTokenApiCredential } from './credentials/StrapiTokenApi.credentials';
|
||||||
|
|
||||||
type CredentialSchemaMap = {
|
type CredentialSchemaMap = {
|
||||||
strapiApi: StrapiApiCredential;
|
strapiApi: StrapiApiCredential;
|
||||||
strapiTokenApi: StrapiTokenApiCredential;
|
strapiTokenApi: StrapiTokenApiCredential;
|
||||||
|
ftp: FtpCredentialSchema;
|
||||||
|
sftp: SftpCredentialSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare module 'n8n-workflow' {
|
declare module 'n8n-workflow' {
|
||||||
|
|
|
@ -103,6 +103,26 @@ class CredentialSchemaString<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CredentialSchemaNumber<
|
||||||
|
S extends ZodType,
|
||||||
|
M extends NumberMetadata,
|
||||||
|
> extends CredentialSchemaProperty<M, S> {
|
||||||
|
constructor(
|
||||||
|
public metadata: M,
|
||||||
|
schema: S,
|
||||||
|
) {
|
||||||
|
super(metadata, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
toNodeProperties(name: string): INodeProperties {
|
||||||
|
return removeUndefinedProperties({
|
||||||
|
...super.toNodeProperties(name),
|
||||||
|
type: 'number',
|
||||||
|
default: this.metadata.default,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CredentialSchemaOptions<
|
class CredentialSchemaOptions<
|
||||||
V extends string,
|
V extends string,
|
||||||
S extends ZodType,
|
S extends ZodType,
|
||||||
|
@ -157,6 +177,11 @@ type StringMetadata = BaseMetadata &
|
||||||
default: string;
|
default: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
type NumberMetadata = BaseMetadata &
|
||||||
|
Partial<{
|
||||||
|
default: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
type Option<V extends string> = {
|
type Option<V extends string> = {
|
||||||
label: string;
|
label: string;
|
||||||
value: V;
|
value: V;
|
||||||
|
@ -210,6 +235,10 @@ export const CredentialSchema = {
|
||||||
string<M extends StringMetadata>(options: M) {
|
string<M extends StringMetadata>(options: M) {
|
||||||
return new CredentialSchemaString(options, z.string());
|
return new CredentialSchemaString(options, z.string());
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line id-denylist
|
||||||
|
number<M extends NumberMetadata>(options: M) {
|
||||||
|
return new CredentialSchemaNumber(options, z.number());
|
||||||
|
},
|
||||||
url(options: Optional<StringMetadata, 'label'> = {}) {
|
url(options: Optional<StringMetadata, 'label'> = {}) {
|
||||||
return new CredentialSchemaString({ label: 'URL', ...options }, z.string().url());
|
return new CredentialSchemaString({ label: 'URL', ...options }, z.string().url());
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue