antony-cloud-office-n8n-nodes 0.1.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/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # n8n-nodes-antony
2
+
3
+ Community-Node-Package fuer Antony.
4
+
5
+ Es enthaelt drei Nodes:
6
+
7
+ - **Antony REST**: typed operations for known REST endpoints, with form and JSON body modes.
8
+ - **Antony GraphQL**: raw GraphQL execution with schema introspection support.
9
+ - **Antony Nextcloud**: for nextcloud operations the n8n nextcloud node doesnt support
10
+
11
+ Die Authentifizierung laeuft ueber Keycloak Benutzername/Passwort. Der Credential holt sich einen Bearer Token und sendet ihn bei Antony-API-Calls mit.
12
+
13
+ Default-Werte aus dem Frontend:
14
+
15
+ - Base URL: `<ANTONY_BASE_URL>`
16
+ - Keycloak realm: `antony`
17
+ - Keycloak client ID: `antonyfe`
18
+
19
+ ## Entwicklung
20
+
21
+ ```bash
22
+ npm install
23
+ npm run build
24
+ ```
25
+
26
+ Der Build erzeugt den Ordner `dist/`. Dieser Ordner wird von n8n geladen.
27
+
28
+ ## Lokal in n8n installieren
29
+
30
+ ### Variante A: Als lokales Package installieren
31
+
32
+ Diese Variante ist gut, wenn du den Node einmal lokal testen willst.
33
+
34
+ ```bash
35
+ cd n8n-nodes-antony
36
+ npm install
37
+ npm run build
38
+ npm pack
39
+ ```
40
+
41
+ Dadurch entsteht eine Datei wie `n8n-nodes-antony-0.1.0.tgz`.
42
+
43
+ Installiere dieses Package dann in deinem n8n-User-Ordner:
44
+
45
+ ```bash
46
+ cd ~/.n8n
47
+ npm install n8n-nodes-antony-0.1.0.tgz
48
+ ```
49
+
50
+ Danach n8n neu starten.
51
+
52
+ ### Variante B: Per npm link fuer Entwicklung
53
+
54
+ Diese Variante ist praktisch, wenn du am Node weiterentwickelst.
55
+
56
+ ```bash
57
+ cd n8n-nodes-antony
58
+ npm install
59
+ npm run build
60
+ sudo npm link
61
+ ```
62
+
63
+ Dann im n8n-User-Ordner verlinken:
64
+
65
+ ```bash
66
+ mkdir -p ~/.n8n/custom
67
+ cd ~/.n8n/custom
68
+ npm init -y
69
+ npm link --save n8n-nodes-antony
70
+ ```
71
+
72
+ Danach n8n mit Custom Extensions starten oder neu starten:
73
+
74
+ ```bash
75
+ docker run -d --rm \
76
+ --name n8n \
77
+ -p 5678:5678 \
78
+ -v ~/.n8n:/home/node/.n8n \
79
+ -e N8N_COMMUNITY_PACKAGES_ENABLED=true \
80
+ -e N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom \
81
+ n8nio/n8n
82
+ ```
83
+
84
+ Wenn du Code aenderst:
85
+
86
+ ```bash
87
+ cd n8n-nodes-antony
88
+ npm run build
89
+ ```
90
+
91
+ Anschliessend n8n neu starten, damit die neue Version geladen wird.
92
+
93
+ ## Installation in n8n Desktop oder Docker
94
+
95
+ ### Docker lokal mit npm link und Volume-Mount
96
+
97
+ Wenn n8n per Docker gestartet wird, reicht `npm link` auf dem Host allein nicht aus. Der Link in `~/.n8n/custom/node_modules` zeigt auf das lokale Projektverzeichnis. Dieses Projektverzeichnis muss deshalb zusaetzlich in den Container gemountet werden.
98
+
99
+ Zuerst den Node lokal bauen und verlinken:
100
+
101
+ ```bash
102
+ cd n8n-nodes-antony
103
+ npm install
104
+ npm run build
105
+ npm link
106
+
107
+ mkdir -p ~/.n8n/custom
108
+ cd ~/.n8n/custom
109
+ npm init -y
110
+ npm link --save n8n-nodes-antony
111
+ ```
112
+
113
+ Dann n8n mit gemountetem n8n-Ordner und gemountetem Projekt starten:
114
+
115
+ ```bash
116
+ docker run -d --rm \
117
+ --name n8n \
118
+ -p 5678:5678 \
119
+ -v ~/.n8n:/home/node/.n8n \
120
+ -v ./:/home/node/Projekte/n8nnodes \
121
+ -e N8N_COMMUNITY_PACKAGES_ENABLED=true \
122
+ -e N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom \
123
+ n8nio/n8n
124
+ ```
125
+
126
+ Wenn dein Projekt an einem anderen Pfad liegt, passe den linken Teil des zweiten Volume-Mounts an. Der rechte Teil muss zum Symlink passen, der durch `npm link` in `~/.n8n/custom/node_modules` erzeugt wurde.
127
+
128
+ Wenn du Code aenderst:
129
+
130
+ ```bash
131
+ cd n8n-nodes-antony
132
+ npm run build
133
+ docker restart n8n
134
+ ```
135
+
136
+ ### Docker mit eigener n8n-Installation
137
+
138
+ Baue das Package zuerst lokal:
139
+
140
+ ```bash
141
+ cd n8n-nodes-antony
142
+ npm install
143
+ npm run build
144
+ npm pack
145
+ ```
146
+
147
+ Dann kann das `.tgz` in ein eigenes n8n-Image kopiert und installiert werden:
148
+
149
+ ```Dockerfile
150
+ FROM n8nio/n8n:latest
151
+
152
+ USER root
153
+ COPY n8n-nodes-antony-0.1.0.tgz /tmp/n8n-nodes-antony-0.1.0.tgz
154
+ RUN cd /home/node/.n8n && npm install /tmp/n8n-nodes-antony-0.1.0.tgz
155
+ USER node
156
+ ```
157
+
158
+ Image bauen und n8n damit starten.
159
+
160
+ ### Community Packages UI
161
+
162
+ Wenn das Package spaeter in einer npm-Registry veroeffentlicht ist, kann es in n8n ueber:
163
+
164
+ `Settings -> Community Nodes -> Install`
165
+
166
+ mit dem Package-Namen installiert werden:
167
+
168
+ ```text
169
+ n8n-nodes-antony
170
+ ```
171
+
172
+ Fuer rein lokale Entwicklung ist die UI-Installation meistens nicht die beste Wahl. Nutze dafuer `npm link` oder `npm pack`.
173
+
174
+ ## n8n konfigurieren
175
+
176
+ Falls Community Nodes deaktiviert sind, muss n8n mit aktivierten Community Packages laufen. Je nach Setup sind diese Umgebungsvariablen nuetzlich:
177
+
178
+ ```bash
179
+ N8N_COMMUNITY_PACKAGES_ENABLED=true
180
+ N8N_REINSTALL_MISSING_PACKAGES=true
181
+ ```
182
+
183
+ Bei Docker setzt du sie z.B. in `docker-compose.yml`.
184
+
185
+ ## Node in n8n verwenden
186
+
187
+ 1. n8n starten oder neu starten.
188
+ 2. Einen Workflow oeffnen.
189
+ 3. Einen neuen Node hinzufuegen.
190
+ 4. Nach `Antony REST` oder `Antony GraphQL` suchen.
191
+ 5. Neue Credentials vom Typ `Antony API` anlegen.
192
+ 6. Werte eintragen:
193
+ - `Base URL`, z.B. `<ANTONY_BASE_URL>`
194
+ - `Keycloak Realm`, z.B. `antony`
195
+ - `Keycloak Client ID`, z.B. `antonyfe`
196
+ - `Username`
197
+ - `Password`
198
+ 7. Credential testen.
199
+
200
+ ## Antony REST Node
201
+
202
+ Der REST-Node hat diese Auswahlstruktur:
203
+
204
+ 1. `Resource`, z.B. `Contact`, `Mail`, `Calendar`, `User and Group`
205
+ 2. `Operation`, z.B. `Get Contact`, `Create Contact`, `Get Mail Details`
206
+ 3. Parameterfelder je nach Operation
207
+ 4. Body-Modus:
208
+ - `JSON`: komplettes JSON-Objekt eintragen
209
+ - `Form Fields`: einzelne Key/Value-Felder per Plus/Minus pflegen
210
+
211
+ Antworten werden zu n8n-Items normalisiert. Arrays werden also als mehrere Items ausgegeben.
212
+
213
+ Binary Upload/Download ist fuer Antony-Endpunkte vorgesehen, z.B. Mail- und Chat-Attachments oder Whitelabel-Dateien. Nextcloud-Endpunkte sind bewusst nicht enthalten.
214
+
215
+ Flexobject-Typen werden zur Laufzeit geladen. Wenn du eine Flexobject-Operation auswaehlst, wird `typeId` als Dropdown aus `/api/v1/flexobject_types` befuellt.
216
+
217
+ ## Antony GraphQL Node
218
+
219
+ Der GraphQL-Node bietet:
220
+
221
+ - `Execute Query`: fuehrt ein GraphQL Query gegen `/api/v2/graphql` aus.
222
+ - `Introspect Schema`: ruft das GraphQL-Schema per Introspection ab.
223
+ - `Root Query Field`: Hilfsdropdown aus der Introspection.
224
+ - `Variables JSON`: Variablen fuer parametrisierte Queries.
225
+ - `Normalize Result`: gibt das erste Ergebnisarray als einzelne n8n-Items aus.
226
+
227
+ Beispiel:
228
+
229
+ ```graphql
230
+ {
231
+ allcontact(take: 10) {
232
+ id
233
+ firstName
234
+ lastName
235
+ email
236
+ }
237
+ }
238
+ ```
239
+
240
+ ## Troubleshooting
241
+
242
+ Wenn die Nodes nicht erscheinen:
243
+
244
+ - Pruefen, ob `npm run build` erfolgreich war.
245
+ - n8n wirklich neu starten.
246
+ - Bei `npm link`: pruefen, ob `~/.n8n/custom/node_modules/n8n-nodes-antony` existiert.
247
+ - Bei Docker mit `npm link`: pruefen, ob das Projektverzeichnis zusaetzlich in den Container gemountet ist.
248
+ - Pruefen, ob `N8N_CUSTOM_EXTENSIONS` auf `/home/node/.n8n/custom` zeigt.
249
+ - Community Packages in n8n aktivieren.
250
+ - n8n-Logs beim Start pruefen.
251
+
252
+ Wenn Login fehlschlaegt:
253
+
254
+ - Base URL ohne Slash am Ende verwenden.
255
+ - Realm und Client ID pruefen.
256
+ - Sicherstellen, dass der Keycloak-Client Password Grant erlaubt.
257
+ - Benutzername/Passwort pruefen.
258
+
259
+ Wenn GraphQL Introspection fehlschlaegt:
260
+
261
+ - Credential testen.
262
+ - Sicherstellen, dass der Bearer Token mitgesendet wird.
263
+ - Pruefen, ob `/api/v2/graphql` Introspection fuer diesen Benutzer erlaubt.
@@ -0,0 +1,21 @@
1
+ import type { ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestOptions, INodeProperties } from 'n8n-workflow';
2
+ export declare class AntonyApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ preAuthentication(this: {
8
+ helpers: {
9
+ httpRequest(options: IHttpRequestOptions): Promise<unknown>;
10
+ };
11
+ }, credentials: ICredentialDataDecryptedObject): Promise<ICredentialDataDecryptedObject>;
12
+ authenticate: {
13
+ type: "generic";
14
+ properties: {
15
+ headers: {
16
+ Authorization: string;
17
+ };
18
+ };
19
+ };
20
+ test: ICredentialTestRequest;
21
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AntonyApi = void 0;
4
+ class AntonyApi {
5
+ constructor() {
6
+ this.name = 'antonyApi';
7
+ this.displayName = 'Antony API';
8
+ this.documentationUrl = 'https://antonyoffice.cloud';
9
+ this.properties = [
10
+ {
11
+ displayName: 'Base URL',
12
+ name: 'baseUrl',
13
+ type: 'string',
14
+ default: '',
15
+ placeholder: 'https://<ANTONY_BASE_URL>',
16
+ required: true,
17
+ description: 'Antony base URL without trailing slash',
18
+ },
19
+ {
20
+ displayName: 'Keycloak Realm',
21
+ name: 'realm',
22
+ type: 'string',
23
+ default: 'antony',
24
+ required: true,
25
+ },
26
+ {
27
+ displayName: 'Keycloak Client ID',
28
+ name: 'clientId',
29
+ type: 'string',
30
+ default: 'antonyfe',
31
+ required: true,
32
+ },
33
+ {
34
+ displayName: 'Username',
35
+ name: 'username',
36
+ type: 'string',
37
+ default: '',
38
+ required: true,
39
+ },
40
+ {
41
+ displayName: 'Password',
42
+ name: 'password',
43
+ type: 'string',
44
+ typeOptions: { password: true },
45
+ default: '',
46
+ required: true,
47
+ },
48
+ {
49
+ displayName: 'Access Token',
50
+ name: 'accessToken',
51
+ type: 'hidden',
52
+ typeOptions: { expirable: true },
53
+ default: '',
54
+ },
55
+ ];
56
+ this.authenticate = {
57
+ type: 'generic',
58
+ properties: {
59
+ headers: {
60
+ Authorization: '=Bearer {{$credentials.accessToken}}',
61
+ },
62
+ },
63
+ };
64
+ this.test = {
65
+ request: {
66
+ baseURL: '={{$credentials.baseUrl.replace(/\\/$/, "")}}',
67
+ url: '/api/v2/modules',
68
+ method: 'GET',
69
+ },
70
+ };
71
+ }
72
+ async preAuthentication(credentials) {
73
+ const baseUrl = String(credentials.baseUrl).replace(/\/$/, '');
74
+ const realm = encodeURIComponent(String(credentials.realm));
75
+ const tokenUrl = `${baseUrl}/kc/realms/${realm}/protocol/openid-connect/token`;
76
+ const response = (await this.helpers.httpRequest({
77
+ method: 'POST',
78
+ url: tokenUrl,
79
+ headers: {
80
+ 'Content-Type': 'application/x-www-form-urlencoded',
81
+ },
82
+ body: new URLSearchParams({
83
+ grant_type: 'password',
84
+ client_id: String(credentials.clientId),
85
+ username: String(credentials.username),
86
+ password: String(credentials.password),
87
+ }).toString(),
88
+ json: true,
89
+ }));
90
+ if (!response.access_token) {
91
+ throw new Error('Keycloak did not return an access_token.');
92
+ }
93
+ return {
94
+ accessToken: response.access_token,
95
+ };
96
+ }
97
+ }
98
+ exports.AntonyApi = AntonyApi;
99
+ //# sourceMappingURL=AntonyApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AntonyApi.credentials.js","sourceRoot":"","sources":["../../credentials/AntonyApi.credentials.ts"],"names":[],"mappings":";;;AAQA,MAAa,SAAS;IAAtB;QACC,SAAI,GAAG,WAAW,CAAC;QAEnB,gBAAW,GAAG,YAAY,CAAC;QAE3B,qBAAgB,GAAG,4BAA4B,CAAC;QAEhD,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,2BAA2B;gBACxC,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,wCAAwC;aACrD;YACD;gBACC,WAAW,EAAE,gBAAgB;gBAC7B,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,QAAQ;gBACjB,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,oBAAoB;gBACjC,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,UAAU;gBACnB,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,cAAc;gBAC3B,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;gBAChC,OAAO,EAAE,EAAE;aACX;SACD,CAAC;QAkCF,iBAAY,GAAG;YACd,IAAI,EAAE,SAAkB;YACxB,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,aAAa,EAAE,sCAAsC;iBACrD;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,+CAA+C;gBACxD,GAAG,EAAE,iBAAiB;gBACtB,MAAM,EAAE,KAAK;aACb;SACD,CAAC;IACH,CAAC;IAhDA,KAAK,CAAC,iBAAiB,CAEtB,WAA2C;QAE3C,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,GAAG,OAAO,cAAc,KAAK,gCAAgC,CAAC;QAE/E,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YAChD,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,QAAQ;YACb,OAAO,EAAE;gBACR,cAAc,EAAE,mCAAmC;aACnD;YACD,IAAI,EAAE,IAAI,eAAe,CAAC;gBACzB,UAAU,EAAE,UAAU;gBACtB,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC;gBACvC,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC;gBACtC,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC;aACtC,CAAC,CAAC,QAAQ,EAAE;YACb,IAAI,EAAE,IAAI;SACV,CAAC,CAAmD,CAAC;QAEtD,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO;YACN,WAAW,EAAE,QAAQ,CAAC,YAAY;SAClC,CAAC;IACH,CAAC;CAkBD;AAvGD,8BAuGC"}
@@ -0,0 +1,23 @@
1
+ import type { ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestOptions, INodeProperties } from 'n8n-workflow';
2
+ export declare class NextcloudApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ preAuthentication(this: {
8
+ helpers: {
9
+ httpRequest(options: IHttpRequestOptions): Promise<unknown>;
10
+ };
11
+ }, credentials: ICredentialDataDecryptedObject): Promise<ICredentialDataDecryptedObject>;
12
+ authenticate: {
13
+ type: "generic";
14
+ properties: {
15
+ headers: {
16
+ Authorization: string;
17
+ 'OCS-APIRequest': string;
18
+ Accept: string;
19
+ };
20
+ };
21
+ };
22
+ test: ICredentialTestRequest;
23
+ }
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NextcloudApi = void 0;
4
+ function getEnvironmentBaseUrl(baseUrl) {
5
+ return baseUrl
6
+ .trim()
7
+ .replace(/\/+$/, '')
8
+ .replace(/\/(?:nc|kc)$/i, '');
9
+ }
10
+ class NextcloudApi {
11
+ constructor() {
12
+ this.name = 'nextcloudApi';
13
+ this.displayName = 'Nextcloud API';
14
+ this.documentationUrl = 'https://docs.nextcloud.com/server/latest/developer_manual/client_apis/';
15
+ this.properties = [
16
+ {
17
+ displayName: 'Base URL',
18
+ name: 'baseUrl',
19
+ type: 'string',
20
+ default: '',
21
+ placeholder: 'https://<NEXTCLOUD_BASE_URL>',
22
+ required: true,
23
+ description: 'Base URL der Umgebung ohne /nc oder /kc. Falls /nc oder /kc angegeben wird, wird es automatisch normalisiert.',
24
+ },
25
+ {
26
+ displayName: 'Authentication Method',
27
+ name: 'authMethod',
28
+ type: 'options',
29
+ options: [
30
+ {
31
+ name: 'Basic Auth – App Password (Recommended)',
32
+ value: 'appPassword',
33
+ description: 'Generate an App Password in Nextcloud → Settings → Security → App passwords',
34
+ },
35
+ {
36
+ name: 'OIDC / Keycloak – Bearer Token',
37
+ value: 'oidc',
38
+ description: 'Requires user_oidc app with token introspection configured in Nextcloud',
39
+ },
40
+ ],
41
+ default: 'appPassword',
42
+ },
43
+ {
44
+ displayName: 'Username',
45
+ name: 'username',
46
+ type: 'string',
47
+ default: '',
48
+ required: true,
49
+ },
50
+ {
51
+ displayName: 'App Password',
52
+ name: 'appPassword',
53
+ type: 'string',
54
+ typeOptions: { password: true },
55
+ default: '',
56
+ description: 'App-specific password generated in Nextcloud → Settings → Security → App passwords',
57
+ displayOptions: { show: { authMethod: ['appPassword'] } },
58
+ },
59
+ {
60
+ displayName: 'Password',
61
+ name: 'password',
62
+ type: 'string',
63
+ typeOptions: { password: true },
64
+ default: '',
65
+ displayOptions: { show: { authMethod: ['oidc'] } },
66
+ },
67
+ {
68
+ displayName: 'Keycloak Realm',
69
+ name: 'realm',
70
+ type: 'string',
71
+ default: '',
72
+ displayOptions: { show: { authMethod: ['oidc'] } },
73
+ },
74
+ {
75
+ displayName: 'Keycloak Client ID',
76
+ name: 'clientId',
77
+ type: 'string',
78
+ default: '',
79
+ description: 'Must have "Direct Access Grants" enabled in Keycloak',
80
+ displayOptions: { show: { authMethod: ['oidc'] } },
81
+ },
82
+ {
83
+ displayName: 'Bearer Token Type',
84
+ name: 'bearerTokenType',
85
+ type: 'options',
86
+ options: [
87
+ {
88
+ name: 'Access Token',
89
+ value: 'access_token',
90
+ description: 'Use Keycloak access_token as Bearer token',
91
+ },
92
+ {
93
+ name: 'ID Token',
94
+ value: 'id_token',
95
+ description: 'Use Keycloak id_token as Bearer token; useful for some Nextcloud user_oidc setups',
96
+ },
97
+ ],
98
+ default: 'access_token',
99
+ displayOptions: { show: { authMethod: ['oidc'] } },
100
+ },
101
+ {
102
+ displayName: 'Authorization',
103
+ name: 'accessToken',
104
+ type: 'hidden',
105
+ typeOptions: { expirable: true },
106
+ default: '',
107
+ },
108
+ ];
109
+ this.authenticate = {
110
+ type: 'generic',
111
+ properties: {
112
+ headers: {
113
+ Authorization: '={{$credentials.accessToken}}',
114
+ 'OCS-APIRequest': 'true',
115
+ Accept: 'application/json',
116
+ },
117
+ },
118
+ };
119
+ this.test = {
120
+ request: {
121
+ baseURL: '={{$credentials.baseUrl.trim().replace(/\\/+$/, "").replace(/\\/(nc|kc)$/i, "") + "/nc"}}',
122
+ url: '/ocs/v2.php/cloud/user?format=json',
123
+ method: 'GET',
124
+ headers: {
125
+ 'OCS-APIRequest': 'true',
126
+ Accept: 'application/json',
127
+ },
128
+ },
129
+ rules: [
130
+ {
131
+ type: 'responseCode',
132
+ properties: {
133
+ value: 401,
134
+ message: 'Nextcloud HTTP 401: Username/App-Passwort falsch, oder ein Keycloak-Bearer-Token wird von Nextcloud nicht fuer OCS akzeptiert.',
135
+ },
136
+ },
137
+ {
138
+ type: 'responseCode',
139
+ properties: {
140
+ value: 404,
141
+ message: 'Nextcloud HTTP 404: Base URL pruefen. Erwartet wird die Umgebung ohne /nc oder /kc; der Test ruft automatisch /nc/ocs/v2.php/cloud/user auf.',
142
+ },
143
+ },
144
+ ],
145
+ };
146
+ }
147
+ async preAuthentication(credentials) {
148
+ const authMethod = String(credentials.authMethod || 'appPassword');
149
+ if (authMethod === 'appPassword') {
150
+ const username = String(credentials.username);
151
+ const appPassword = String(credentials.appPassword);
152
+ const encoded = Buffer.from(`${username}:${appPassword}`).toString('base64');
153
+ return { accessToken: `Basic ${encoded}` };
154
+ }
155
+ // OIDC / Keycloak – URL aus baseUrl ableiten
156
+ const base = getEnvironmentBaseUrl(String(credentials.baseUrl));
157
+ const keycloakUrl = `${base}/kc`;
158
+ const realm = encodeURIComponent(String(credentials.realm));
159
+ const tokenUrl = `${keycloakUrl}/realms/${realm}/protocol/openid-connect/token`;
160
+ let tokenResponse;
161
+ const bearerTokenType = String(credentials.bearerTokenType || 'access_token');
162
+ try {
163
+ tokenResponse = (await this.helpers.httpRequest({
164
+ method: 'POST',
165
+ url: tokenUrl,
166
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
167
+ body: new URLSearchParams({
168
+ grant_type: 'password',
169
+ client_id: String(credentials.clientId),
170
+ username: String(credentials.username),
171
+ password: String(credentials.password),
172
+ scope: 'openid',
173
+ }).toString(),
174
+ json: true,
175
+ }));
176
+ }
177
+ catch (err) {
178
+ // httpRequest wirft bei 4xx/5xx – Keycloak-Fehlerbody extrahieren
179
+ const body = err?.response?.body ?? err?.response?.data;
180
+ const detail = typeof body === 'object'
181
+ ? (body?.error_description ?? body?.error ?? JSON.stringify(body))
182
+ : String(body ?? err.message);
183
+ throw new Error(`Keycloak token request failed (${tokenUrl}): ${detail}`);
184
+ }
185
+ if (tokenResponse.error) {
186
+ throw new Error(`Keycloak error: ${tokenResponse.error}` +
187
+ (tokenResponse.error_description ? ` – ${tokenResponse.error_description}` : ''));
188
+ }
189
+ const bearerToken = tokenResponse[bearerTokenType];
190
+ if (!bearerToken) {
191
+ throw new Error(`Keycloak returned no ${bearerTokenType}. ` +
192
+ `Check: realm="${String(credentials.realm)}", clientId, Direct Access Grants enabled, username/password.`);
193
+ }
194
+ return { accessToken: `Bearer ${bearerToken}` };
195
+ }
196
+ }
197
+ exports.NextcloudApi = NextcloudApi;
198
+ //# sourceMappingURL=NextcloudApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NextcloudApi.credentials.js","sourceRoot":"","sources":["../../credentials/NextcloudApi.credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class AntonyGraphql implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }