@data-fair/catalog-sftp 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +5 -26
- package/lib/capabilities.ts +9 -5
- package/lib/download.ts +0 -1
- package/lib/imports.ts +27 -47
- package/lib/prepare.ts +56 -0
- package/package.json +5 -2
package/index.ts
CHANGED
|
@@ -1,36 +1,15 @@
|
|
|
1
1
|
import type { CatalogPlugin } from '@data-fair/types-catalogs'
|
|
2
2
|
import { type SFTPConfig, configSchema, assertConfigValid } from '#types'
|
|
3
|
-
import capabilities from './lib/capabilities.ts'
|
|
3
|
+
import capabilities, { type SFTPCapabilities } from './lib/capabilities.ts'
|
|
4
4
|
|
|
5
5
|
// Since the plugin is very frequently imported, each function is imported on demand,
|
|
6
6
|
// instead of loading the entire plugin.
|
|
7
7
|
// This file should not contain any code, but only constants and dynamic imports of functions.
|
|
8
8
|
|
|
9
|
-
const plugin: CatalogPlugin<SFTPConfig,
|
|
10
|
-
async prepare (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (secrets?.sshKey && catalogConfig.connectionKey.sshKey === '') {
|
|
14
|
-
delete secrets.sshKey
|
|
15
|
-
} else {
|
|
16
|
-
secrets.sshKey = catalogConfig.connectionKey.sshKey
|
|
17
|
-
catalogConfig.connectionKey.sshKey = '********'
|
|
18
|
-
}
|
|
19
|
-
break
|
|
20
|
-
case 'password':
|
|
21
|
-
if (secrets?.password && catalogConfig.connectionKey.password === '') {
|
|
22
|
-
delete secrets.password
|
|
23
|
-
} else {
|
|
24
|
-
secrets.password = catalogConfig.connectionKey.password
|
|
25
|
-
catalogConfig.connectionKey.password = '********'
|
|
26
|
-
}
|
|
27
|
-
break
|
|
28
|
-
default: break
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
catalogConfig,
|
|
32
|
-
secrets
|
|
33
|
-
}
|
|
9
|
+
const plugin: CatalogPlugin<SFTPConfig, SFTPCapabilities> = {
|
|
10
|
+
async prepare (context) {
|
|
11
|
+
const prepare = (await import('./lib/prepare.ts')).default
|
|
12
|
+
return prepare(context)
|
|
34
13
|
},
|
|
35
14
|
|
|
36
15
|
async list (context) {
|
package/lib/capabilities.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import type { Capability } from '@data-fair/types-catalogs'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The list of capabilities of the plugin.
|
|
3
5
|
* These capabilities define the actions that can be performed with the plugin.
|
|
4
|
-
*
|
|
5
|
-
* This allows TypeScript to check if the plugin has the required funcitons for each capability.
|
|
6
|
+
* The capabilities must satisfy the `Capability` type.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
'import'
|
|
9
|
-
]
|
|
8
|
+
export const capabilities = [
|
|
9
|
+
'import',
|
|
10
|
+
] satisfies Capability[]
|
|
11
|
+
|
|
12
|
+
export type SFTPCapabilities = typeof capabilities
|
|
13
|
+
export default capabilities
|
package/lib/download.ts
CHANGED
|
@@ -21,7 +21,6 @@ export const getMetaData = async ({ catalogConfig, resourceId }: GetResourceCont
|
|
|
21
21
|
id: resourceId,
|
|
22
22
|
title: resourceId.substring(resourceId.lastIndexOf('/') + 1),
|
|
23
23
|
format: (pointPos === -1) ? '' : (resourceId.substring(pointPos + 1)),
|
|
24
|
-
origin: catalogConfig.url + ':' + catalogConfig.port,
|
|
25
24
|
filePath: ''
|
|
26
25
|
}
|
|
27
26
|
}
|
package/lib/imports.ts
CHANGED
|
@@ -1,28 +1,11 @@
|
|
|
1
1
|
import type { SFTPConfig } from '#types'
|
|
2
|
-
import type { FileEntryWithStats
|
|
2
|
+
import type { FileEntryWithStats } from 'ssh2'
|
|
3
3
|
import type capabilities from './capabilities.ts'
|
|
4
4
|
import type { ListContext, Folder, CatalogPlugin } from '@data-fair/types-catalogs'
|
|
5
5
|
import { type Config, NodeSSH } from 'node-ssh'
|
|
6
6
|
|
|
7
7
|
type ResourceList = Awaited<ReturnType<CatalogPlugin['list']>>['results']
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Stores the most recently used SFTP configuration.
|
|
11
|
-
* This variable holds the last SFTPConfig object that was used,
|
|
12
|
-
* allowing for reuse or reference in subsequent SFTP operations.
|
|
13
|
-
*/
|
|
14
|
-
let lastConfig: SFTPConfig | undefined
|
|
15
|
-
let lastSecrets: Record<string, string>
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Store the ssh instance for SFTP operations.
|
|
19
|
-
*/
|
|
20
|
-
const ssh = new NodeSSH()
|
|
21
|
-
/**
|
|
22
|
-
* SFTP client instance used for managing SFTP operations.
|
|
23
|
-
*/
|
|
24
|
-
let clientSFTP: SFTPWrapper
|
|
25
|
-
|
|
26
9
|
/**
|
|
27
10
|
* Prepares a list of files and folders from the SFTP directory listing.
|
|
28
11
|
*
|
|
@@ -38,19 +21,20 @@ const prepareFiles = (list: FileEntryWithStats[], path: string): (Folder[] | Res
|
|
|
38
21
|
return {
|
|
39
22
|
id: path + '/' + file.filename,
|
|
40
23
|
title: file.filename,
|
|
41
|
-
type: 'folder'
|
|
24
|
+
type: 'folder',
|
|
25
|
+
updatedAt: file.attrs.mtime ? new Date(file.attrs.mtime * 1000) : undefined
|
|
42
26
|
} as Folder
|
|
43
27
|
} else {
|
|
44
28
|
// ResourceList
|
|
45
29
|
return {
|
|
46
30
|
id: path + '/' + file.filename,
|
|
47
31
|
title: file.filename,
|
|
32
|
+
type: 'resource',
|
|
48
33
|
description: '',
|
|
49
34
|
format: (pointPos === -1) ? '' : (file.filename.substring(pointPos + 1)),
|
|
50
35
|
mimeType: '',
|
|
51
|
-
origin: path + '/' + file.filename,
|
|
52
36
|
size: file.attrs.size,
|
|
53
|
-
|
|
37
|
+
updatedAt: file.attrs.mtime ? new Date(file.attrs.mtime * 1000) : undefined
|
|
54
38
|
} as ResourceList[number]
|
|
55
39
|
}
|
|
56
40
|
})
|
|
@@ -64,37 +48,33 @@ const prepareFiles = (list: FileEntryWithStats[], path: string): (Folder[] | Res
|
|
|
64
48
|
* @throws Will throw an error if the connection configuration is invalid or not supported.
|
|
65
49
|
*/
|
|
66
50
|
export const list = async ({ catalogConfig, secrets, params }: ListContext<SFTPConfig, typeof capabilities>): ReturnType<CatalogPlugin['list']> => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
} else if (catalogConfig.connectionKey.key === 'password') {
|
|
80
|
-
paramsConnection.password = secrets.password
|
|
81
|
-
} else {
|
|
82
|
-
throw new Error('format non pris en charge')
|
|
83
|
-
}
|
|
51
|
+
const paramsConnection: Config = {
|
|
52
|
+
host: catalogConfig.url,
|
|
53
|
+
username: catalogConfig.login,
|
|
54
|
+
port: catalogConfig.port
|
|
55
|
+
}
|
|
56
|
+
if (catalogConfig.connectionKey.key === 'sshKey') {
|
|
57
|
+
paramsConnection.privateKey = secrets.sshKey
|
|
58
|
+
} else if (catalogConfig.connectionKey.key === 'password') {
|
|
59
|
+
paramsConnection.password = secrets.password
|
|
60
|
+
} else {
|
|
61
|
+
throw new Error('format non pris en charge')
|
|
62
|
+
}
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
clientSFTP = await ssh.requestSFTP()
|
|
64
|
+
const ssh = new NodeSSH()
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await ssh.connect(paramsConnection)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(err)
|
|
70
|
+
throw new Error('Configuration invalide')
|
|
93
71
|
}
|
|
72
|
+
const clientSFTP = await ssh.requestSFTP()
|
|
73
|
+
|
|
94
74
|
const path = params.currentFolderId ?? '.'
|
|
95
75
|
const files: FileEntryWithStats[] = await new Promise((resolve, reject) => {
|
|
96
76
|
if (!clientSFTP) {
|
|
97
|
-
throw new Error('Configuration invalide')
|
|
77
|
+
throw new Error('Configuration invalide (clientSFTP manquant)')
|
|
98
78
|
}
|
|
99
79
|
clientSFTP.readdir(path, (err: any, list: any) => {
|
|
100
80
|
if (err) {
|
package/lib/prepare.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { SFTPConfig } from '#types'
|
|
2
|
+
import type { PrepareContext } from '@data-fair/types-catalogs'
|
|
3
|
+
import type { SFTPCapabilities } from './capabilities.ts'
|
|
4
|
+
import { type Config, NodeSSH } from 'node-ssh'
|
|
5
|
+
|
|
6
|
+
export default async ({ catalogConfig, secrets }: PrepareContext<SFTPConfig, SFTPCapabilities>) => {
|
|
7
|
+
switch (catalogConfig.connectionKey.key) {
|
|
8
|
+
case 'sshKey':
|
|
9
|
+
delete secrets.password
|
|
10
|
+
if (catalogConfig.connectionKey.sshKey === '') {
|
|
11
|
+
delete secrets.sshKey
|
|
12
|
+
} else if (catalogConfig.connectionKey.sshKey && catalogConfig.connectionKey.sshKey !== '********') {
|
|
13
|
+
secrets.sshKey = catalogConfig.connectionKey.sshKey
|
|
14
|
+
catalogConfig.connectionKey.sshKey = '********'
|
|
15
|
+
}
|
|
16
|
+
break
|
|
17
|
+
case 'password':
|
|
18
|
+
delete secrets.sshKey
|
|
19
|
+
if (catalogConfig.connectionKey.password === '') {
|
|
20
|
+
delete secrets.password
|
|
21
|
+
} else if (catalogConfig.connectionKey.password && catalogConfig.connectionKey.password !== '********') {
|
|
22
|
+
secrets.password = catalogConfig.connectionKey.password
|
|
23
|
+
catalogConfig.connectionKey.password = '********'
|
|
24
|
+
}
|
|
25
|
+
break
|
|
26
|
+
default: break
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// try the SFTP connection
|
|
30
|
+
try {
|
|
31
|
+
const paramsConnection: Config = {
|
|
32
|
+
host: catalogConfig.url,
|
|
33
|
+
username: catalogConfig.login,
|
|
34
|
+
port: catalogConfig.port
|
|
35
|
+
}
|
|
36
|
+
if (catalogConfig.connectionKey.key === 'sshKey') {
|
|
37
|
+
paramsConnection.privateKey = secrets.sshKey
|
|
38
|
+
} else if (catalogConfig.connectionKey.key === 'password') {
|
|
39
|
+
paramsConnection.password = secrets.password
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error('format non pris en charge')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ssh = new NodeSSH()
|
|
45
|
+
await ssh.connect(paramsConnection)
|
|
46
|
+
ssh.dispose()
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Connection test failed:', error)
|
|
49
|
+
throw new Error('Connection test failed', { cause: error })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
catalogConfig,
|
|
54
|
+
secrets
|
|
55
|
+
}
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-fair/catalog-sftp",
|
|
3
3
|
"description": "SFTP plugin for the Data Fair catalogs service.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
@@ -39,12 +39,15 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@commitlint/cli": "^19.8.0",
|
|
41
41
|
"@commitlint/config-conventional": "^19.8.0",
|
|
42
|
-
"@data-fair/types-catalogs": "^0.1.0",
|
|
43
42
|
"@data-fair/lib-types-builder": "^1.8.0",
|
|
43
|
+
"@data-fair/types-catalogs": "^0.6.0",
|
|
44
44
|
"@types/debug": "^4.1.12",
|
|
45
45
|
"@types/fs-extra": "^11.0.4",
|
|
46
46
|
"@types/node": "^24.0.3",
|
|
47
47
|
"@types/ssh2": "^1.15.5",
|
|
48
|
+
"chalk": "^5.6.2",
|
|
49
|
+
"dayjs": "^1.11.19",
|
|
50
|
+
"draftlog": "^1.0.13",
|
|
48
51
|
"eslint": "^9.25.1",
|
|
49
52
|
"husky": "^9.1.7",
|
|
50
53
|
"neostandard": "^0.12.1",
|