n8n-nodes-amis-v1 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.
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MisaAmisAuth = void 0;
|
|
4
|
+
class MisaAmisAuth {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'misaAmisAuth';
|
|
7
|
+
this.displayName = 'MISA AMIS Auth';
|
|
8
|
+
this.documentationUrl = 'https://amisapp.misa.vn/';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Identity ID',
|
|
12
|
+
name: 'identityId',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: 'default_user',
|
|
15
|
+
description: 'A unique identifier for this account (e.g. username or email). Used to store and retrieve session cookies locally.',
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.MisaAmisAuth = MisaAmisAuth;
|
|
@@ -0,0 +1,200 @@
|
|
|
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.MisaAmisLogin = 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 qrcode_1 = __importDefault(require("qrcode"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
class MisaAmisLogin {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.description = {
|
|
51
|
+
displayName: 'MISA AMIS Login',
|
|
52
|
+
name: 'misaAmisLogin',
|
|
53
|
+
icon: 'fa:users',
|
|
54
|
+
group: ['transform'],
|
|
55
|
+
version: 1,
|
|
56
|
+
description: 'Login to MISA AMIS via QR Code and save session',
|
|
57
|
+
defaults: {
|
|
58
|
+
name: 'MISA AMIS Login',
|
|
59
|
+
},
|
|
60
|
+
inputs: ['main'],
|
|
61
|
+
outputs: ['main'],
|
|
62
|
+
credentials: [
|
|
63
|
+
{
|
|
64
|
+
name: 'misaAmisAuth',
|
|
65
|
+
required: true,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
properties: [
|
|
69
|
+
{
|
|
70
|
+
displayName: 'QR Code Path',
|
|
71
|
+
name: 'qrPath',
|
|
72
|
+
type: 'string',
|
|
73
|
+
default: '',
|
|
74
|
+
placeholder: '/path/to/save/qrcode.png',
|
|
75
|
+
description: 'Path to save the generated QR code image',
|
|
76
|
+
required: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
displayName: 'Client ID',
|
|
80
|
+
name: 'clientId',
|
|
81
|
+
type: 'string',
|
|
82
|
+
default: '',
|
|
83
|
+
description: 'Optional Client ID override. Randomly generated if empty.',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async execute() {
|
|
89
|
+
const items = this.getInputData();
|
|
90
|
+
const returnData = [];
|
|
91
|
+
const qrPath = this.getNodeParameter('qrPath', 0);
|
|
92
|
+
let clientId = this.getNodeParameter('clientId', 0);
|
|
93
|
+
const credentials = await this.getCredentials('misaAmisAuth');
|
|
94
|
+
const identityId = credentials.identityId;
|
|
95
|
+
if (!clientId) {
|
|
96
|
+
clientId = '6bcbc4d1-5426-42f7-bc61-69cac2e229f4';
|
|
97
|
+
}
|
|
98
|
+
// Save Credential Logic
|
|
99
|
+
// We'll save to ~/.n8n/misa_sessions/<identityId>.json
|
|
100
|
+
const homeDir = os.homedir();
|
|
101
|
+
const sessionDir = path.join(homeDir, '.n8n', 'misa_sessions');
|
|
102
|
+
if (!fs.existsSync(sessionDir)) {
|
|
103
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
const sessionFilePath = path.join(sessionDir, `${identityId}.json`);
|
|
106
|
+
// Initialize Cookie Jar
|
|
107
|
+
const jar = new tough_cookie_1.CookieJar();
|
|
108
|
+
const client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({ jar }));
|
|
109
|
+
// Headers
|
|
110
|
+
const headers = {
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
'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',
|
|
113
|
+
'Origin': 'https://amisapp.misa.vn',
|
|
114
|
+
'Referer': 'https://amisapp.misa.vn/',
|
|
115
|
+
};
|
|
116
|
+
try {
|
|
117
|
+
// Step 1: Gen QR Code
|
|
118
|
+
// ... (Same logic as before) ...
|
|
119
|
+
const genQrPayload = {
|
|
120
|
+
clientId: clientId,
|
|
121
|
+
deviceId: clientId,
|
|
122
|
+
userAgent: headers['User-Agent'],
|
|
123
|
+
deviceName: "Desktop",
|
|
124
|
+
deviceType: "Desktop",
|
|
125
|
+
osName: "Windows",
|
|
126
|
+
osVersion: "10",
|
|
127
|
+
browser: "Google Chrome",
|
|
128
|
+
browserVersion: "143.0"
|
|
129
|
+
};
|
|
130
|
+
const genQrRes = await client.post('https://id.misa.vn/api/login-cross-device/gen-qrcode', genQrPayload, { headers });
|
|
131
|
+
if (!genQrRes.data || !genQrRes.data.CDRequestId) {
|
|
132
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to generate QR Code Request ID');
|
|
133
|
+
}
|
|
134
|
+
const cdRequestId = genQrRes.data.CDRequestId;
|
|
135
|
+
// Step 2: Generate QR Image
|
|
136
|
+
const qrContent = `https://amisapp.misa.vn/shared?app=qrlogin&qrid=${cdRequestId}&domain=amisapp.misa.vn`;
|
|
137
|
+
const dir = path.dirname(qrPath);
|
|
138
|
+
if (!fs.existsSync(dir)) {
|
|
139
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
await qrcode_1.default.toFile(qrPath, qrContent);
|
|
142
|
+
// Step 3: Polling
|
|
143
|
+
let pollingSuccess = false;
|
|
144
|
+
let attempts = 0;
|
|
145
|
+
const maxAttempts = 60;
|
|
146
|
+
let lastPollData = null;
|
|
147
|
+
while (!pollingSuccess && attempts < maxAttempts) {
|
|
148
|
+
attempts++;
|
|
149
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
150
|
+
try {
|
|
151
|
+
const pollUrl = `https://id.misa.vn/api/login-cross-device/v2/polling?cdRequestId=${cdRequestId}&clientId=${clientId}&deviceId=${clientId}`;
|
|
152
|
+
const pollRes = await client.get(pollUrl, { headers });
|
|
153
|
+
lastPollData = pollRes.data;
|
|
154
|
+
if (pollRes.data && (pollRes.data.Token || pollRes.data.Success === true || (pollRes.data.Status && pollRes.data.Status !== 'Pending'))) {
|
|
155
|
+
// Assuming success if status changes or token present
|
|
156
|
+
if (pollRes.data.Token || pollRes.data.Success === true) {
|
|
157
|
+
pollingSuccess = true;
|
|
158
|
+
}
|
|
159
|
+
// If the API returns a Token/Code that needs a follow-up "Login" call, do it here.
|
|
160
|
+
// Based on user cURL, after polling success, there might be 'qrlogin' or 'startup'.
|
|
161
|
+
// Since we are using cookies (cookie jar), the polling response might already set some cookies?
|
|
162
|
+
// Or we might need to manually set the Token from pollRes into the jar?
|
|
163
|
+
// However, usually detailed auth flow requires more steps.
|
|
164
|
+
// For this version: We Save the JAR state.
|
|
165
|
+
}
|
|
166
|
+
// Workaround: Break if we see "Success"
|
|
167
|
+
if (JSON.stringify(pollRes.data).includes("Success") && JSON.stringify(pollRes.data).includes("true")) {
|
|
168
|
+
pollingSuccess = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// Ignore
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (pollingSuccess) {
|
|
176
|
+
// Save Cookies to File
|
|
177
|
+
const serializedJar = await jar.serialize();
|
|
178
|
+
fs.writeFileSync(sessionFilePath, JSON.stringify(serializedJar, null, 2));
|
|
179
|
+
}
|
|
180
|
+
returnData.push({
|
|
181
|
+
json: {
|
|
182
|
+
message: pollingSuccess ? "Login Successful & Session Saved" : "Timed out",
|
|
183
|
+
identityId: identityId,
|
|
184
|
+
sessionFile: sessionFilePath,
|
|
185
|
+
lastPollResult: lastPollData,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (this.continueOnFail()) {
|
|
191
|
+
returnData.push({ json: { error: error.message } });
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return [returnData];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.MisaAmisLogin = MisaAmisLogin;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-amis-v1",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n node for AMIS v1",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package"
|
|
7
|
+
],
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"main": "index.js",
|
|
10
|
+
"n8n": {
|
|
11
|
+
"nodes": [
|
|
12
|
+
"dist/nodes/MisaAmisLogin/MisaAmisLogin.node.js"
|
|
13
|
+
],
|
|
14
|
+
"credentials": [
|
|
15
|
+
"dist/credentials/MisaAmisAuth.credentials.js"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"lint": "eslint nodes credentials --ext .ts",
|
|
22
|
+
"lintfix": "eslint nodes credentials --ext .ts --fix",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/express": "^4.17.6",
|
|
27
|
+
"@types/node": "^14.14.41",
|
|
28
|
+
"@types/qrcode": "^1.5.6",
|
|
29
|
+
"@types/request-promise-native": "~1.0.15",
|
|
30
|
+
"@typescript-eslint/parser": "~4.8.1",
|
|
31
|
+
"eslint": "~7.14.0",
|
|
32
|
+
"gulp": "^4.0.2",
|
|
33
|
+
"n8n-core": "^2.2.1",
|
|
34
|
+
"n8n-workflow": "^2.2.1",
|
|
35
|
+
"typescript": "^5.9.3"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"axios": "^1.6.0",
|
|
39
|
+
"axios-cookiejar-support": "^6.0.5",
|
|
40
|
+
"qrcode": "^1.5.3",
|
|
41
|
+
"tough-cookie": "^4.1.3"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
]
|
|
46
|
+
}
|