n8n-nodes-amis-v1 0.1.4 → 0.1.6

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.
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.MisaAmis = void 0;
40
+ const n8n_workflow_1 = require("n8n-workflow");
41
+ const axios_1 = __importDefault(require("axios"));
42
+ const axios_cookiejar_support_1 = require("axios-cookiejar-support");
43
+ const tough_cookie_1 = require("tough-cookie");
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ class MisaAmis {
48
+ constructor() {
49
+ this.description = {
50
+ displayName: 'MISA AMIS',
51
+ name: 'misaAmis',
52
+ icon: 'fa:file-invoice-dollar',
53
+ group: ['transform'],
54
+ version: 1,
55
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
56
+ description: 'Interact with MISA AMIS API',
57
+ defaults: {
58
+ name: 'MISA AMIS',
59
+ },
60
+ inputs: ['main'],
61
+ outputs: ['main'],
62
+ credentials: [
63
+ {
64
+ name: 'misaAmisUser',
65
+ required: true,
66
+ },
67
+ ],
68
+ properties: [
69
+ {
70
+ displayName: 'Resource',
71
+ name: 'resource',
72
+ type: 'options',
73
+ noDataExpression: true,
74
+ options: [
75
+ {
76
+ name: 'Order',
77
+ value: 'order',
78
+ },
79
+ ],
80
+ default: 'order',
81
+ },
82
+ {
83
+ displayName: 'Operation',
84
+ name: 'operation',
85
+ type: 'options',
86
+ noDataExpression: true,
87
+ displayOptions: {
88
+ show: {
89
+ resource: ['order'],
90
+ },
91
+ },
92
+ options: [
93
+ {
94
+ name: 'Get Detail',
95
+ value: 'getDetail',
96
+ description: 'Get full detail of a sales order',
97
+ action: 'Get order detail',
98
+ },
99
+ ],
100
+ default: 'getDetail',
101
+ },
102
+ {
103
+ displayName: 'Order ID (Key)',
104
+ name: 'orderId',
105
+ type: 'string',
106
+ default: '',
107
+ required: true,
108
+ displayOptions: {
109
+ show: {
110
+ resource: ['order'],
111
+ operation: ['getDetail'],
112
+ },
113
+ },
114
+ description: 'The UUID Key of the order.',
115
+ },
116
+ {
117
+ displayName: 'MISA Context (JSON)',
118
+ name: 'misaContext',
119
+ type: 'string',
120
+ default: '',
121
+ description: 'Paste the X-MISA-Context header value from your browser dev tools.',
122
+ required: true, // For now, we force user to provide it logic
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ async execute() {
128
+ const items = this.getInputData();
129
+ const returnData = [];
130
+ const resource = this.getNodeParameter('resource', 0);
131
+ const operation = this.getNodeParameter('operation', 0);
132
+ // 1. Load Session
133
+ const credentials = await this.getCredentials('misaAmisUser');
134
+ const userIdentity = credentials.userIdentity;
135
+ const n8nDir = process.env.N8N_USER_FOLDER || path.join(os.homedir(), '.n8n');
136
+ const sessionFilePath = path.join(n8nDir, 'misa_sessions', `${userIdentity}.json`);
137
+ if (!fs.existsSync(sessionFilePath)) {
138
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Session file not found for user: ${userIdentity}. Please run Login Node first.`);
139
+ }
140
+ const jarJson = JSON.parse(fs.readFileSync(sessionFilePath, 'utf8'));
141
+ const jar = tough_cookie_1.CookieJar.deserializeSync(jarJson);
142
+ const client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({ jar }));
143
+ for (let i = 0; i < items.length; i++) {
144
+ try {
145
+ if (resource === 'order' && operation === 'getDetail') {
146
+ const orderId = this.getNodeParameter('orderId', i);
147
+ const misaContext = this.getNodeParameter('misaContext', i);
148
+ // Construct req param
149
+ const reqObj = [
150
+ {
151
+ "Type": "sa_order",
152
+ "Key": orderId,
153
+ "Retype": 3520, // Default based on sample
154
+ "RefTypeCategory": 352,
155
+ "View": "view_sa_order",
156
+ "Details": [
157
+ { "Type": "sa_order_detail", "Alias": "detail", "View": "view_sa_order_detail" },
158
+ { "Type": "wesign_document", "Alias": "wesign_document", "ForeignKey": "refid", "Mode": "View" }
159
+ ]
160
+ }
161
+ ];
162
+ const reqBase64 = Buffer.from(JSON.stringify(reqObj)).toString('base64');
163
+ const url = `https://actapp.misa.vn/g1/api/sa/v1/sa_order/detail_full?req=${reqBase64}`;
164
+ const headers = {
165
+ 'Accept': 'application/json, text/plain, */*',
166
+ 'Content-Type': 'application/json',
167
+ 'X-MISA-Context': misaContext,
168
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36',
169
+ // Authorization: 'Bearer ...' // We hope cookies are enough, or allow user to pass it?
170
+ // Usually MISA Web Session uses cookies. The Bearer token in cURL might be redundant or derived.
171
+ // Let's try without Bearer first. If fail, we ask user.
172
+ };
173
+ const response = await client.get(url, { headers });
174
+ returnData.push({
175
+ json: response.data
176
+ });
177
+ }
178
+ }
179
+ catch (error) {
180
+ if (this.continueOnFail()) {
181
+ returnData.push({ json: { error: error.message } });
182
+ }
183
+ else {
184
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
185
+ }
186
+ }
187
+ }
188
+ return [returnData];
189
+ }
190
+ }
191
+ exports.MisaAmis = MisaAmis;
@@ -99,19 +99,7 @@ class MisaAmisLogin {
99
99
  },
100
100
  },
101
101
  },
102
- {
103
- displayName: 'Request ID',
104
- name: 'requestId',
105
- type: 'string',
106
- default: '',
107
- required: true,
108
- displayOptions: {
109
- show: {
110
- operation: ['checkLogin'],
111
- },
112
- },
113
- description: 'The CDRequestId returned from "Generate QR" step',
114
- },
102
+ // Removed requestId and cookieJar inputs because we use file-based state
115
103
  ],
116
104
  };
117
105
  }
@@ -125,6 +113,9 @@ class MisaAmisLogin {
125
113
  if (!fs.existsSync(storagePath)) {
126
114
  fs.mkdirSync(storagePath, { recursive: true });
127
115
  }
116
+ // File paths
117
+ const sessionFilePath = path.join(storagePath, `${userIdentity}.json`);
118
+ const pendingFilePath = path.join(storagePath, `${userIdentity}.pending.json`);
128
119
  const clientId = '6bcbc4d1-5426-42f7-bc61-69cac2e229f4';
129
120
  const headers = {
130
121
  'Content-Type': 'application/json',
@@ -155,11 +146,19 @@ class MisaAmisLogin {
155
146
  const cdRequestId = genQrRes.data.CDRequestId;
156
147
  const qrContent = `https://amisapp.misa.vn/shared?app=qrlogin&qrid=${cdRequestId}&domain=amisapp.misa.vn`;
157
148
  const qrBuffer = await qrcode_1.default.toBuffer(qrContent);
149
+ // SAVE PENDING STATE
150
+ const serializedJar = await jar.serialize();
151
+ const pendingState = {
152
+ requestId: cdRequestId,
153
+ cookieJar: serializedJar,
154
+ timestamp: Date.now()
155
+ };
156
+ fs.writeFileSync(pendingFilePath, JSON.stringify(pendingState, null, 2));
158
157
  returnData.push({
159
158
  json: {
160
- message: "QR Generated. Scan it and then use 'Check Login' step.",
159
+ message: "QR Generated. Please Scan. Pending session saved.",
160
+ userIdentity: userIdentity,
161
161
  requestId: cdRequestId,
162
- clientId: clientId,
163
162
  qrContent: qrContent
164
163
  },
165
164
  binary: {
@@ -173,28 +172,49 @@ class MisaAmisLogin {
173
172
  }
174
173
  else if (operation === 'checkLogin') {
175
174
  // --- Operation: Check Login ---
176
- const cdRequestId = this.getNodeParameter('requestId', 0);
177
- const sessionFilePath = path.join(storagePath, `${userIdentity}.json`);
175
+ // READ PENDING STATE
176
+ if (!fs.existsSync(pendingFilePath)) {
177
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `No pending login found for user '${userIdentity}'. Please run 'Generate QR' first.`);
178
+ }
179
+ const pendingState = JSON.parse(fs.readFileSync(pendingFilePath, 'utf8'));
180
+ const cdRequestId = pendingState.requestId;
181
+ // Restore Cookies
182
+ if (pendingState.cookieJar) {
183
+ const deserialized = await tough_cookie_1.CookieJar.deserialize(pendingState.cookieJar);
184
+ await jar.removeAllCookies();
185
+ for (const cookie of await deserialized.getCookies('https://id.misa.vn')) {
186
+ await jar.setCookie(cookie, 'https://id.misa.vn');
187
+ }
188
+ }
178
189
  let pollingSuccess = false;
179
190
  let attempts = 0;
180
- const maxAttempts = 60; // 2 minutes (2s interval)
191
+ const maxAttempts = 60; // 2 minutes
181
192
  while (!pollingSuccess && attempts < maxAttempts) {
182
193
  attempts++;
183
194
  await new Promise(resolve => setTimeout(resolve, 2000));
184
195
  try {
185
196
  const pollUrl = `https://id.misa.vn/api/login-cross-device/v2/polling?cdRequestId=${cdRequestId}&clientId=${clientId}&deviceId=${clientId}`;
197
+ console.log(`[MISA Debug] Polling Attempt ${attempts}: ${pollUrl}`);
186
198
  const pollRes = await client.get(pollUrl, { headers });
187
199
  if (JSON.stringify(pollRes.data).includes("Success") || JSON.stringify(pollRes.data).includes("v1/auth/token")) {
188
200
  pollingSuccess = true;
189
201
  }
190
202
  }
191
203
  catch (error) {
192
- // Ignore poling errors
204
+ if (error.response && error.response.status >= 400 && error.response.status < 500) {
205
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Polling Error: ${JSON.stringify(error.response.data)}. Please Generate QR again.`);
206
+ }
193
207
  }
194
208
  }
195
209
  if (pollingSuccess) {
210
+ // Save FINAL Session
196
211
  const serializedJar = await jar.serialize();
197
212
  fs.writeFileSync(sessionFilePath, JSON.stringify(serializedJar, null, 2));
213
+ // Cleanup Pending
214
+ try {
215
+ fs.unlinkSync(pendingFilePath);
216
+ }
217
+ catch (e) { }
198
218
  returnData.push({
199
219
  json: {
200
220
  message: "Login Successful",
@@ -204,7 +224,7 @@ class MisaAmisLogin {
204
224
  });
205
225
  }
206
226
  else {
207
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Login timeout or failed. Please scan QR Code again.');
227
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Login timeout. Please scan QR Code again.');
208
228
  }
209
229
  }
210
230
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-amis-v1",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "n8n node for AMIS v1",
5
5
  "keywords": [
6
6
  "n8n-community-node-package"
@@ -9,7 +9,8 @@
9
9
  "main": "index.js",
10
10
  "n8n": {
11
11
  "nodes": [
12
- "dist/nodes/MisaAmisLogin/MisaAmisLogin.node.js"
12
+ "dist/nodes/MisaAmisLogin/MisaAmisLogin.node.js",
13
+ "dist/nodes/MisaAmis/MisaAmis.node.js"
13
14
  ],
14
15
  "credentials": [
15
16
  "dist/credentials/MisaAmisApp.credentials.js",