n8n-nodes-smbclient 1.0.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.
Files changed (38) hide show
  1. package/.editorconfig +20 -0
  2. package/.github/workflows/npm-publish.yml +32 -0
  3. package/.github/workflows/one-shot-npm-publish.yml +30 -0
  4. package/.prettierrc.js +51 -0
  5. package/LICENSE.md +19 -0
  6. package/Readme.md +131 -0
  7. package/credentials/Smb2Api.credentials.ts +72 -0
  8. package/credentials/smb2.svg +36 -0
  9. package/dist/credentials/Smb2Api.credentials.d.ts +7 -0
  10. package/dist/credentials/Smb2Api.credentials.js +72 -0
  11. package/dist/credentials/Smb2Api.credentials.js.map +1 -0
  12. package/dist/credentials/smb2.svg +36 -0
  13. package/dist/nodes/Smb2/Smb2.node.d.ts +5 -0
  14. package/dist/nodes/Smb2/Smb2.node.js +174 -0
  15. package/dist/nodes/Smb2/Smb2.node.js.map +1 -0
  16. package/dist/nodes/Smb2/Smb2.node.json +19 -0
  17. package/dist/nodes/Smb2/SmbClientWrapper.d.ts +22 -0
  18. package/dist/nodes/Smb2/SmbClientWrapper.js +231 -0
  19. package/dist/nodes/Smb2/SmbClientWrapper.js.map +1 -0
  20. package/dist/nodes/Smb2/SmbEntryHelpers.d.ts +6 -0
  21. package/dist/nodes/Smb2/SmbEntryHelpers.js +102 -0
  22. package/dist/nodes/Smb2/SmbEntryHelpers.js.map +1 -0
  23. package/dist/nodes/Smb2/interfaces.d.ts +31 -0
  24. package/dist/nodes/Smb2/interfaces.js +3 -0
  25. package/dist/nodes/Smb2/interfaces.js.map +1 -0
  26. package/dist/nodes/Smb2/smb2.svg +36 -0
  27. package/dist/package.json +67 -0
  28. package/eslint.config.mjs +174 -0
  29. package/gulpfile.js +16 -0
  30. package/index.js +0 -0
  31. package/nodes/Smb2/Smb2.node.json +19 -0
  32. package/nodes/Smb2/Smb2.node.ts +184 -0
  33. package/nodes/Smb2/SmbClientWrapper.ts +285 -0
  34. package/nodes/Smb2/SmbEntryHelpers.ts +129 -0
  35. package/nodes/Smb2/interfaces.ts +39 -0
  36. package/nodes/Smb2/smb2.svg +36 -0
  37. package/package.json +67 -0
  38. package/tsconfig.json +30 -0
@@ -0,0 +1,285 @@
1
+ import {
2
+ IExecuteFunctions,
3
+ INode, ITriggerFunctions, NodeApiError,
4
+ NodeOperationError
5
+ } from "n8n-workflow";
6
+ import {promisify} from "node:util";
7
+ import {execFile} from "node:child_process";
8
+ import {Smb2Credentials, SmbListEntry, SmbStat} from "./interfaces";
9
+ import {debuglog} from "util";
10
+
11
+ const debug = debuglog("n8n-nodes-smbclient");
12
+ // Refaire une map d’erreurs si besoin
13
+ const SMB_ERROR_HINTS: Array<[RegExp, string]> = [
14
+ [/EACCES/i, "Access Denied - Check your permissions for this file/folder"],
15
+ [/ENOENT/i, "File/Path Not Found"],
16
+ [/ENOTDIR/i, "Not a directory"],
17
+ [/ETIMEOUT/i, "Connection timed out"],
18
+ [/ECONNREFUSED/i, "Could not connect to SMB server - Connection refused"],
19
+ [/LOGON failure/i, "Logon Failure - Check your username, password, and domain"],
20
+ [/bad network name/i, "Bad Network Name - The specified share does not exist on the server"],
21
+ // etc.
22
+ ];
23
+
24
+ export function getReadableError(error: any): string {
25
+ const msg = error?.message ?? String(error);
26
+ for (const [re, friendly] of SMB_ERROR_HINTS) {
27
+ if (re.test(msg)) {
28
+ return `${friendly} (${msg})`;
29
+ }
30
+ }
31
+ return msg;
32
+ }
33
+
34
+
35
+ const execFileAsync = promisify(execFile);
36
+ const redactCmd = (s: string): string => {
37
+ if (!s) return s;
38
+ s = s.replace(/-U\s+(\S=)/g, '-U ***');
39
+ s = s.replace(/-A\s=\S+/g, '-A ***');
40
+ s = s.replace(/\/\/[^/\s]+\/\S+/g, '//***');
41
+ return s;
42
+ }
43
+
44
+ export class SmbClientWrapper {
45
+ constructor(
46
+ private auth: Smb2Credentials,
47
+ private smbclientPath: string = 'smbclient',
48
+ private node: INode
49
+ ) {
50
+ }
51
+
52
+
53
+ private buildBaseArgs(): string[] {
54
+ const {host, username, password, domain, share} = this.auth;
55
+
56
+ // Anonymous if no username supplied
57
+ let userPart = '%';
58
+ if (username) {
59
+ if (!domain) {
60
+ userPart = `${username}%${password ?? ''}`
61
+ } else {
62
+ userPart = `${domain}/${username}%${password ?? ''}`
63
+ }
64
+ }
65
+ const hostPart = `\\\\${host}\\${share}`;
66
+ return [hostPart, '-U', userPart, '-g'];
67
+ }
68
+
69
+ private async runOne(cmd: string): Promise<string> {
70
+ const args = [...this.buildBaseArgs(), '-c', cmd];
71
+
72
+ try {
73
+ const {stdout, stderr} = await execFileAsync(this.smbclientPath, args, {
74
+ maxBuffer: 10 * 1024 * 1024,
75
+ });
76
+ // smbclient sometimes prints warnings to stderr; detect real errors
77
+ if (stderr && /NT_STATUS|Error|failed/i.test(stderr)) {
78
+ const {username, password} = this.auth;
79
+ const safeCmd = redactCmd([this.smbclientPath, ...args].join(' ')).replace(username, '***').replace(password, '***');
80
+ throw new NodeOperationError(this.node, {
81
+ message: `smbclient failed. cmd="${safeCmd}" stderr="${stderr.trim()}"`
82
+ });
83
+ }
84
+ return stdout ?? '';
85
+ } catch (err) {
86
+
87
+ const {username, password} = this.auth;
88
+ const safeCmd = redactCmd(err?.cmd || [this.smbclientPath, ...args].join(' ')).replace(username, '***').replace(password, '***');
89
+ const msg = (err?.stderr?.trim?.() || err?.message || 'smbclient command failed').replace(username, '***').replace(password, '***');
90
+ throw new NodeOperationError(this.node, {
91
+ message: `smbclient failed. cmd="${safeCmd}" stderr="${String(msg)}`
92
+ })
93
+ }
94
+
95
+ }
96
+
97
+ async stat(remotePath: string): Promise<SmbStat> {
98
+ const out = await this.runOne(`allinfo "${remotePath}"`);
99
+ const info = new Map<string, string>();
100
+ for (const line of out.split('\n')) {
101
+ const [k, v] = line.split('|');
102
+ if (k && v !== undefined) info.set(k.trim().toUpperCase(), v.trim());
103
+ }
104
+ const attrs = (info.get('ATTRIBUTES') ?? '').split('').filter(Boolean);
105
+ return {
106
+ size: info.get('SIZE') ? Number(info.get('SIZE')) : undefined,
107
+ createTime: info.get('CREATE_TIME') ?? undefined,
108
+ accessTime: info.get('ACCESS_TIME') ?? undefined,
109
+ writeTime: info.get('WRITE_TIME') ?? undefined,
110
+ changeTime: info.get('CHANGE_TIME') ?? undefined,
111
+ attributes: attrs,
112
+ isDirectory: attrs.includes('D'),
113
+ };
114
+ }
115
+
116
+ async list(dir: string): Promise<SmbListEntry[]> {
117
+ const out = await this.runOne(`ls "${dir}"`);
118
+
119
+ const lines = out
120
+ .split('\n')
121
+ .map((l) => l.trim())
122
+ .filter(Boolean);
123
+
124
+ const parsed = lines.map((line) => {
125
+ // ---------- Case 1: pipe-delimited ----------
126
+ if (line.includes('|')) {
127
+ const parts = line.split('|');
128
+
129
+ // Try common pattern: name|size|date|time|attr
130
+ if (parts.length >= 5 && /^\d+$/.test(parts[1])) {
131
+ const name = parts[0];
132
+ const size = Number(parts[1]) || 0;
133
+ const date = parts[2];
134
+ const time = parts[3];
135
+ const attr = parts[4] ?? '';
136
+ const attributes = attr.split('').filter(Boolean);
137
+ return {
138
+ name,
139
+ size,
140
+ date,
141
+ time,
142
+ attributes,
143
+ isDirectory: attributes.includes('D'),
144
+ } as SmbListEntry;
145
+ }
146
+
147
+ // Alternate pattern seen on some builds: attr|size|date|time|name
148
+ if (parts.length >= 5 && /^\d+$/.test(parts[1])) {
149
+ const attr = parts[0] ?? '';
150
+ const size = Number(parts[1]) || 0;
151
+ const date = parts[2];
152
+ const time = parts[3];
153
+ const name = parts.slice(4).join('|'); // keep any extra pipes in name
154
+ const attributes = attr.split('').filter(Boolean);
155
+ return {
156
+ name,
157
+ size,
158
+ date,
159
+ time,
160
+ attributes,
161
+ isDirectory: attributes.includes('D'),
162
+ } as SmbListEntry;
163
+ }
164
+
165
+ // Fallback if unexpected pipe layout: treat whole line as name
166
+ return {
167
+ name: line,
168
+ size: 0,
169
+ attributes: [],
170
+ isDirectory: false,
171
+ } as SmbListEntry;
172
+ }
173
+
174
+ // ---------- Case 2: space-aligned columns (your example) ----------
175
+ // Example:
176
+ // 11100 MY FILE_NAME R-1234.pdf A 2058 Thu Sep 4 14:39:03 2025
177
+ const compact = line.replace(/\s+/g, ' ').trim();
178
+ const tokens = compact.split(' ');
179
+
180
+ // We expect: ... <ATTR> <SIZE> <Wkd> <Mon> <dd> <hh:mm:ss> <yyyy>
181
+ if (tokens.length >= 8) {
182
+ const year = tokens[tokens.length - 1];
183
+ const time = tokens[tokens.length - 2];
184
+ const day = tokens[tokens.length - 3];
185
+ const month = tokens[tokens.length - 4];
186
+ const weekday = tokens[tokens.length - 5];
187
+ const sizeStr = tokens[tokens.length - 6];
188
+ const attr = tokens[tokens.length - 7];
189
+
190
+ const name = tokens.slice(0, tokens.length - 7).join(' ');
191
+ const size = Number(sizeStr) || 0;
192
+ const attributes = (attr ?? '').split('').filter(Boolean);
193
+
194
+ return {
195
+ name,
196
+ size,
197
+ date: `${weekday} ${month} ${day} ${time} ${year}`,
198
+ time,
199
+ attributes,
200
+ isDirectory: attributes.includes('D'),
201
+ } as SmbListEntry;
202
+ }
203
+
204
+ // Minimal fallback
205
+ return {
206
+ name: line,
207
+ size: 0,
208
+ attributes: [],
209
+ isDirectory: false,
210
+ } as SmbListEntry;
211
+ });
212
+
213
+ return parsed.filter((entry) => {
214
+ if (!entry) return false;
215
+
216
+ // Skip . and ..
217
+ if (entry.name === '.' || entry.name === '..') return false;
218
+
219
+ // Skip smbclient footer lines like "blocks available"
220
+ if (/blocks available/i.test(entry.date || '') || /blocks available/i.test(entry.name)) {
221
+ return false;
222
+ }
223
+
224
+ // Skip stray numeric-only names (these are usually size/blocks artifacts)
225
+ if (/^\d+$/.test(entry.name)) {
226
+ return false;
227
+ }
228
+
229
+ return true;
230
+ });
231
+ }
232
+
233
+
234
+ async get(remotePath: string, localPath: string): Promise<void> {
235
+ await this.runOne(`get "${remotePath}" "${localPath}"`);
236
+ }
237
+
238
+ async put(localPath: string, remotePath: string): Promise<void> {
239
+ await this.runOne(`put "${localPath}" "${remotePath}"`);
240
+ }
241
+
242
+ async mkdir(remoteDir: string): Promise<void> {
243
+ await this.runOne(`mkdir "${remoteDir}"`);
244
+ }
245
+
246
+ async rmdir(remoteDir: string): Promise<void> {
247
+ await this.runOne(`rmdir "${remoteDir}"`);
248
+ }
249
+
250
+ async del(remotePath: string): Promise<void> {
251
+ await this.runOne(`del "${remotePath}"`);
252
+ }
253
+
254
+ // No persistent connection to close for smbclient CLI, but keep API parity.
255
+ async close(): Promise<void> {
256
+ /* no-op */
257
+ }
258
+ }
259
+
260
+ export async function connectToSmbServer(
261
+ this: IExecuteFunctions | ITriggerFunctions
262
+ ): Promise<{ client: SmbClientWrapper }> {
263
+ try {
264
+ const credentials = (await this.getCredentials("smb2Api")) as unknown as Smb2Credentials;
265
+
266
+ debug(
267
+ "Connecting to //%s/%s as (%s\\%s)",
268
+ credentials.host,
269
+ credentials.share,
270
+ credentials.domain ?? "",
271
+ credentials.username,
272
+ );
273
+ const smbclientPath = this.getNodeParameter('smbclientPath', 0, 'smbclient') as string;
274
+ const client = new SmbClientWrapper(credentials, smbclientPath, this.getNode());
275
+
276
+ // Optionnel: tester existence ou list root pour vérifier la connexion
277
+ await client.list(""); // ou client.exists(".");
278
+
279
+ return {client};
280
+ } catch (error: any) {
281
+ debug("Connect error: %O", error);
282
+ const readableError = getReadableError(error);
283
+ throw new NodeApiError(this.getNode(), error, {message: `Failed to connect to SMB server: ${readableError}`});
284
+ }
285
+ }
@@ -0,0 +1,129 @@
1
+ import {debuglog} from "util";
2
+ import {IExecuteFunctions, INodeExecutionData, NodeOperationError} from "n8n-workflow";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import crypto from "node:crypto";
6
+ import {SmbClientWrapper} from "./SmbClientWrapper";
7
+ import {Operation, OpHandler, Smb2Credentials, SmbListEntry} from "./interfaces";
8
+ import fs from "node:fs/promises";
9
+
10
+ const debug = debuglog("n8n-nodes-smbclient");
11
+ const str = (ctx: IExecuteFunctions, i: number, name: string, def = ''): string =>
12
+ ctx.getNodeParameter(name, i, def) as string;
13
+
14
+ const tmpFile = (prefix: 'get' | 'put') =>
15
+ path.join(os.tmpdir(), `n8n-smb-${prefix}-${Date.now()}-${crypto.randomUUID()}`);
16
+
17
+ const toBinary = async (
18
+ ctx: IExecuteFunctions,
19
+ buf: Buffer,
20
+ fileName: string,
21
+ mime: string,
22
+ outProp: string,
23
+ extraJson: Record<string, unknown> = {},
24
+ ): Promise<INodeExecutionData> => {
25
+ const binary = await ctx.helpers.prepareBinaryData(buf, fileName, mime);
26
+ return {json: {fileName, ...extraJson}, binary: {[outProp]: binary}};
27
+ };
28
+
29
+ const buildClient = async (ctx: IExecuteFunctions): Promise<SmbClientWrapper> => {
30
+ const smbclientPath = str(ctx, 0, 'smbclientPath', 'smbclient');
31
+ const {host, username, password, domain, share} = (await ctx.getCredentials('smb2Api')) as unknown as Smb2Credentials;
32
+ debug(
33
+ "Connecting to //%s/%s as (%s\\%s)",
34
+ host,
35
+ share,
36
+ domain ?? "",
37
+ username,
38
+ );
39
+ return new SmbClientWrapper({host, username, password, domain, share}, smbclientPath, ctx.getNode());
40
+ };
41
+
42
+ /* ---------- Handlers (take ctx instead of this) ---------- */
43
+ const handleStat: OpHandler = async (ctx, i, client) => {
44
+ const remotePath = str(ctx, i, 'remotePath');
45
+ const stat = await client.stat(remotePath);
46
+ return {json: {remotePath, ...stat}};
47
+ };
48
+
49
+ const handleList: OpHandler = async (ctx, i, client) => {
50
+ const directory = str(ctx, i, 'directory', '/');
51
+ const entries = await client.list(directory);
52
+ const filtered = entries.filter(
53
+ (e: SmbListEntry) =>
54
+ e &&
55
+ e.name !== '.' &&
56
+ e.name !== '..' &&
57
+ !/blocks available/i.test(e?.date || '') &&
58
+ !/^\d+$/.test(e.name),
59
+ );
60
+ return {json: {directory, entries: filtered}};
61
+ };
62
+
63
+ const handleGet: OpHandler = async (ctx, i, client) => {
64
+ const remotePath = str(ctx, i, 'remotePath');
65
+ const outProp = str(ctx, i, 'outBinaryPropertyName', 'data');
66
+ const outFile = str(ctx, i, 'outFileName') || path.basename(remotePath);
67
+ const outMime = str(ctx, i, 'outMimeType', 'application/octet-stream');
68
+
69
+ const tmp = tmpFile('get');
70
+ await client.get(remotePath, tmp);
71
+ const buf = await fs.readFile(tmp);
72
+ await fs.unlink(tmp).catch(() => {
73
+ });
74
+ return toBinary(ctx, buf, outFile, outMime, outProp, {remotePath});
75
+ };
76
+
77
+ const handlePut: OpHandler = async (ctx, i, client) => {
78
+ const remotePath = str(ctx, i, 'remotePath');
79
+ const source = str(ctx, i, 'putSource', 'binary'); // 'binary' | 'text'
80
+
81
+ const tmp = tmpFile('put');
82
+ if (source === 'binary') {
83
+ const binProp = str(ctx, i, 'binaryPropertyName', 'data');
84
+ const item = ctx.getInputData()[i];
85
+ if (!item.binary || !item.binary[binProp]) {
86
+ throw new NodeOperationError(ctx.getNode(), `Binary property "${binProp}" not found on item ${i}`);
87
+ }
88
+ const buffer = await ctx.helpers.getBinaryDataBuffer(i, binProp);
89
+ await fs.writeFile(tmp, buffer);
90
+ } else {
91
+ const text = str(ctx, i, 'textContent', '');
92
+ await fs.writeFile(tmp, text, 'utf8');
93
+ }
94
+
95
+ await client.put(tmp, remotePath);
96
+ await fs.unlink(tmp).catch(() => {
97
+ });
98
+ return {json: {remotePath, uploaded: true}};
99
+ };
100
+
101
+ const handleMkdir: OpHandler = async (ctx, i, client) => {
102
+ const directory = str(ctx, i, 'directory');
103
+ await client.mkdir(directory);
104
+ return {json: {directory, created: true}};
105
+ };
106
+
107
+ const handleRmdir: OpHandler = async (ctx, i, client) => {
108
+ const directory = str(ctx, i, 'directory');
109
+ await client.rmdir(directory);
110
+ return {json: {directory, removed: true}};
111
+ };
112
+
113
+ const handleDel: OpHandler = async (ctx, i, client) => {
114
+ const remotePath = str(ctx, i, 'remotePath');
115
+ await client.del(remotePath);
116
+ return {json: {remotePath, deleted: true}};
117
+ };
118
+
119
+ const handlers: Record<Operation, OpHandler> = {
120
+ stat: handleStat,
121
+ list: handleList,
122
+ get: handleGet,
123
+ put: handlePut,
124
+ mkdir: handleMkdir,
125
+ rmdir: handleRmdir,
126
+ del: handleDel,
127
+ };
128
+
129
+ export {handlers, buildClient}
@@ -0,0 +1,39 @@
1
+ import {IExecuteFunctions, INodeExecutionData} from "n8n-workflow";
2
+ import {SmbClientWrapper} from "./SmbClientWrapper";
3
+
4
+ interface Smb2Credentials {
5
+ host: string;
6
+ share: string;
7
+ domain?: string;
8
+ username: string;
9
+ password: string;
10
+ port?: number; // samba-client may not support explicit port
11
+ maxProtocol?: string; // passed through
12
+ // timeout options (if supported)
13
+ }
14
+
15
+ type SmbListEntry = {
16
+ name: string;
17
+ size: number;
18
+ date?: string;
19
+ time?: string;
20
+ attributes: string[];
21
+ isDirectory: boolean;
22
+ };
23
+
24
+ type SmbStat = {
25
+ size?: number;
26
+ createTime?: string;
27
+ accessTime?: string;
28
+ writeTime?: string;
29
+ changeTime?: string;
30
+ attributes?: string[];
31
+ isDirectory?: boolean;
32
+ };
33
+ type Operation = 'stat' | 'list' | 'get' | 'put' | 'mkdir' | 'rmdir' | 'del';
34
+ type OpHandler = (ctx: IExecuteFunctions, i: number, client: SmbClientWrapper) => Promise<INodeExecutionData>;
35
+
36
+ export {
37
+ SmbStat, SmbListEntry, Smb2Credentials, Operation
38
+ , OpHandler
39
+ }
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Calque_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
3
+ <!-- Generator: Adobe Illustrator 29.5.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 141) -->
4
+ <defs>
5
+ <style>
6
+ .st0 {
7
+ fill: url(#Dégradé_sans_nom1);
8
+ }
9
+
10
+ .st1 {
11
+ fill: url(#Dégradé_sans_nom2);
12
+ }
13
+
14
+ .st2 {
15
+ fill: url(#Dégradé_sans_nom);
16
+ }
17
+
18
+ .st3 {
19
+ fill: url(#Dégradé_sans_nom3);
20
+ }
21
+ </style>
22
+ <linearGradient id="Dégradé_sans_nom" data-name="Dégradé sans nom" x1="-1301.41" y1="140.82" x2="-1300.41" y2="141.82" gradientTransform="translate(174550 -18839.5) scale(134)" gradientUnits="userSpaceOnUse">
23
+ <stop offset="0" stop-color="#4c9ded"/>
24
+ <stop offset="1" stop-color="#3178b5"/>
25
+ </linearGradient>
26
+ <linearGradient id="Dégradé_sans_nom1" data-name="Dégradé sans nom" x1="-1305.78" y1="138.8" x2="-1304.78" y2="139.8" gradientTransform="translate(418568.08 -11797.5) scale(320.43 87)" xlink:href="#Dégradé_sans_nom"/>
27
+ <linearGradient id="Dégradé_sans_nom2" data-name="Dégradé sans nom" x1="214.73" y1="-57.27" x2="452.27" y2="180.27" gradientTransform="translate(0 514) scale(1 -1)" xlink:href="#Dégradé_sans_nom"/>
28
+ <linearGradient id="Dégradé_sans_nom3" data-name="Dégradé sans nom" x1="0" y1="217" x2="325" y2="217" gradientTransform="matrix(1,0,0,1,0,0)" xlink:href="#Dégradé_sans_nom"/>
29
+ </defs>
30
+ <g id="Calque_1-2">
31
+ <path class="st2" d="M325,134v-6.6c0-3.18-1.26-6.23-3.5-8.48L206.37,3.52c-2.25-2.26-5.31-3.52-8.5-3.52h-6.87v134h134Z"/>
32
+ <path class="st0" d="M228.3,274c-5.89,0-11.3,3.23-14.09,8.42l-42.21,78.58h320.38l-48-79.28c-2.89-4.78-8.07-7.71-13.66-7.72h-202.42Z"/>
33
+ <path class="st1" d="M280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM281.09,448.82c1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.57-2.81-1.78-3.58-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.57,0,4.35-.42,5.34-1.27h.03ZM496,393H171c-8.84,0-16,7.16-16,16v87c0,8.84,7.16,16,16,16h325c8.84,0,16-7.16,16-16v-87c0-8.84-7.16-16-16-16ZM211.39,466.38c-1.07,1.55-2.58,2.74-4.33,3.43-2.14.84-4.42,1.24-6.72,1.19-1.22,0-2.43-.07-3.64-.21-1.1-.14-2.19-.35-3.27-.63-.97-.25-1.91-.59-2.82-1v-5.37c1.55.66,3.14,1.22,4.76,1.67,1.73.5,3.53.75,5.33.75,1.25.04,2.49-.16,3.66-.58.88-.32,1.64-.9,2.18-1.67.49-.75.74-1.63.72-2.53.04-.94-.26-1.86-.83-2.6-.69-.83-1.55-1.51-2.51-2-1.37-.71-2.78-1.36-4.22-1.92-1.12-.44-2.21-.96-3.26-1.55-1-.57-1.93-1.27-2.76-2.07-.82-.81-1.48-1.77-1.93-2.83-.5-1.21-.74-2.52-.71-3.83-.05-1.79.44-3.56,1.42-5.06,1-1.44,2.38-2.55,4-3.22,1.9-.78,3.94-1.16,6-1.12,1.74-.01,3.47.18,5.16.58,1.62.4,3.21.94,4.74,1.61l-1.77,4.58c-1.36-.56-2.75-1.03-4.16-1.41-1.33-.36-2.71-.54-4.09-.54-1.05-.03-2.1.16-3.07.55-2.21,1-3.18,3.61-2.18,5.81.1.22.22.43.36.64.63.79,1.42,1.43,2.33,1.88,1.06.57,2.42,1.2,4.08,1.9,1.7.68,3.34,1.52,4.89,2.5,1.26.78,2.32,1.86,3.08,3.13.76,1.39,1.13,2.96,1.07,4.55.06,1.9-.47,3.78-1.51,5.37ZM255.39,470.52h-5.44v-22.8c0-1.23.08-2.4.13-3.51s.11-2,.16-2.67h-.24l-10.37,29h-5l-10-29h-.21c0,.65.1,1.53.16,2.65s.12,2.32.17,3.62.07,2.51.07,3.67v19.08h-5.19v-34.82h8l9.64,27.77h.14l10-27.77h8l-.02,34.78ZM287.94,466c-1.07,1.53-2.57,2.72-4.31,3.4-2.11.82-4.36,1.22-6.63,1.16h-12.49v-34.82h10.35c4.49,0,7.88.65,10.16,1.95,2.28,1.3,3.42,3.55,3.43,6.76.01,1.23-.22,2.45-.7,3.59-.44,1.03-1.12,1.93-2,2.63-.97.72-2.09,1.2-3.28,1.39v.24c1.26.21,2.48.64,3.6,1.26,1.06.61,1.93,1.51,2.5,2.59.66,1.36.98,2.87.92,4.38.05,1.94-.49,3.85-1.55,5.47ZM317.81,470.56h-23.58v-4.21l9-9.13c1.74-1.78,3.18-3.31,4.3-4.61,1-1.11,1.85-2.34,2.53-3.67.56-1.19.84-2.48.83-3.79.1-1.41-.43-2.8-1.45-3.79-1.07-.9-2.43-1.37-3.83-1.3-1.44-.01-2.87.29-4.18.89-1.43.68-2.77,1.53-4,2.54l-3.07-3.71c.97-.82,2-1.57,3.08-2.24,1.16-.71,2.4-1.26,3.7-1.64,1.52-.44,3.1-.65,4.69-.62,1.97-.05,3.92.35,5.71,1.18,1.52.72,2.81,1.85,3.7,3.28.88,1.47,1.33,3.16,1.29,4.87.02,1.79-.36,3.56-1.11,5.19-.83,1.71-1.91,3.29-3.19,4.7-1.38,1.56-3,3.24-4.93,5.05l-6,5.85v.26h16.47l.04,4.9ZM372.74,476.78c-13.25,0-24-10.75-24-24s10.75-24,24-24,24,10.75,24,24c-.02,13.24-10.76,23.96-24,23.96v.04ZM444.74,476.78c-13.25,0-24-10.75-24-24s10.75-24,24-24,24,10.75,24,24c-.02,13.24-10.76,23.96-24,23.96v.04ZM280.38,455.37c-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.22-1.96-.71-2.81-.56-.85-1.38-1.49-2.34-1.82ZM281.06,448.82c1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.57-2.81-1.78-3.58-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58h0ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58h0ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58h0ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM282.77,457.2c-.56-.86-1.39-1.5-2.36-1.83-1.42-.48-2.92-.7-4.42-.64h-5.74v11h6c2.67,0,4.53-.51,5.59-1.54,1.11-1.11,1.7-2.63,1.62-4.19.02-.98-.23-1.95-.72-2.8h.03ZM280.77,441.58c-1.74-.84-3.67-1.21-5.6-1.08h-4.95v9.59h5.47c2.59,0,4.38-.42,5.37-1.27,1.03-.93,1.58-2.28,1.49-3.66.12-1.43-.56-2.81-1.78-3.58h0Z"/>
34
+ <path class="st3" d="M325,166v76h-96.44c-17.67,0-33.91,9.7-42.28,25.26l-57.08,106.15c-4.06,7.56-6.19,16.01-6.2,24.59v36H12c-6.63,0-12-5.37-12-12V12C0,5.37,5.37,0,12,0h147v154c0,6.63,5.37,12,12,12h154Z"/>
35
+ </g>
36
+ </svg>
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "n8n-nodes-smbclient",
3
+ "version": "1.0.0",
4
+ "description": "Transfer files via Samba (SMB2) and smbclient package with n8n, some of the base work are based on https://github.com/drudge/n8n-nodes-smb work.",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n-nodes-smbclient",
8
+ "smb",
9
+ "smb",
10
+ "samba",
11
+ "binary",
12
+ "file",
13
+ "transfer",
14
+ "node",
15
+ "n8n"
16
+ ],
17
+ "engines": {
18
+ "node": ">=20.15"
19
+ },
20
+ "repository": "https://github.com/JimmyMtl/n8n-nodes-smbclient",
21
+ "main": "index.js",
22
+ "scripts": {
23
+ "build": "npx rimraf dist && tsc && gulp build:icons",
24
+ "dev": "tsc --watch",
25
+ "format": "prettier nodes credentials --write",
26
+ "lint": "eslint nodes credentials package.json",
27
+ "lintfix": "eslint nodes credentials package.json --fix",
28
+ "prepublishOnly": "npm run build && npm run lint -c .eslintrc.prepublish.js nodes credentials package.json",
29
+ "release": "npx standard-version"
30
+ },
31
+ "n8n": {
32
+ "n8nNodesApiVersion": 1,
33
+ "credentials": [
34
+ "dist/credentials/Smb2Api.credentials.js"
35
+ ],
36
+ "nodes": [
37
+ "dist/nodes/Smb2/Smb2.node.js"
38
+ ]
39
+ },
40
+ "devDependencies": {
41
+ "@eslint/eslintrc": "^3.3.1",
42
+ "@eslint/js": "^9.35.0",
43
+ "@types/node": "^24.9.2",
44
+ "@typescript-eslint/parser": "^8.46.2",
45
+ "eslint": "^9.38.0",
46
+ "eslint-plugin-n8n-nodes-base": "^1.16.4",
47
+ "globals": "^16.4.0",
48
+ "gulp": "^5.0.1",
49
+ "prettier": "^3.6.2",
50
+ "typescript": "^5.9.3"
51
+ },
52
+ "peerDependencies": {
53
+ "n8n-workflow": "^1.115.0"
54
+ },
55
+ "dependencies": {
56
+ "tmp-promise": "^3.0.3"
57
+ },
58
+ "overrides": {
59
+ "form-data": "^4.0.4",
60
+ "axios": "1.12.0"
61
+ },
62
+ "pnpm": {
63
+ "onlyBuiltDependencies": [
64
+ "eslint-plugin-n8n-nodes-base"
65
+ ]
66
+ }
67
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "module": "commonjs",
5
+ "moduleResolution": "node",
6
+ "target": "es2019",
7
+ "lib": ["es2019", "es2020", "es2022.error"],
8
+ "removeComments": true,
9
+ "useUnknownInCatchVariables": false,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noImplicitAny": true,
12
+ "noImplicitReturns": true,
13
+ "noUnusedLocals": true,
14
+ "strictNullChecks": true,
15
+ "preserveConstEnums": true,
16
+ "esModuleInterop": true,
17
+ "resolveJsonModule": true,
18
+ "incremental": true,
19
+ "declaration": true,
20
+ "sourceMap": true,
21
+ "skipLibCheck": true,
22
+ "outDir": "./dist/",
23
+ },
24
+ "include": [
25
+ "credentials/**/*",
26
+ "nodes/**/*",
27
+ "nodes/**/*.json",
28
+ "package.json",
29
+ ],
30
+ }