n8n-nodes-amis-v1 0.1.1 → 0.1.2
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.
|
@@ -44,6 +44,7 @@ const tough_cookie_1 = require("tough-cookie");
|
|
|
44
44
|
const qrcode_1 = __importDefault(require("qrcode"));
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
46
|
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
47
48
|
class MisaAmisLogin {
|
|
48
49
|
constructor() {
|
|
49
50
|
this.description = {
|
|
@@ -71,16 +72,7 @@ class MisaAmisLogin {
|
|
|
71
72
|
type: 'string',
|
|
72
73
|
default: '',
|
|
73
74
|
placeholder: 'hiennv',
|
|
74
|
-
description: 'Unique identifier for
|
|
75
|
-
required: true,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
displayName: 'QR Code Path',
|
|
79
|
-
name: 'qrPath',
|
|
80
|
-
type: 'string',
|
|
81
|
-
default: '',
|
|
82
|
-
placeholder: 'C:/tmp/misa_qr.png',
|
|
83
|
-
description: 'Path to save the generated QR code image',
|
|
75
|
+
description: 'Unique identifier for this session. The session will be saved automatically.',
|
|
84
76
|
required: true,
|
|
85
77
|
},
|
|
86
78
|
],
|
|
@@ -89,23 +81,17 @@ class MisaAmisLogin {
|
|
|
89
81
|
async execute() {
|
|
90
82
|
const items = this.getInputData();
|
|
91
83
|
const returnData = [];
|
|
92
|
-
const qrPath = this.getNodeParameter('qrPath', 0);
|
|
93
84
|
const userIdentity = this.getNodeParameter('userIdentity', 0);
|
|
94
85
|
const credentials = await this.getCredentials('misaAmisApp');
|
|
95
|
-
const
|
|
96
|
-
|
|
86
|
+
const n8nDir = process.env.N8N_USER_FOLDER || path.join(os.homedir(), '.n8n');
|
|
87
|
+
const storagePath = path.join(n8nDir, 'misa_sessions');
|
|
97
88
|
if (!fs.existsSync(storagePath)) {
|
|
98
89
|
fs.mkdirSync(storagePath, { recursive: true });
|
|
99
90
|
}
|
|
100
91
|
const sessionFilePath = path.join(storagePath, `${userIdentity}.json`);
|
|
101
|
-
// Client ID (Device) - Fixed or Derived from Identity to keep session stable
|
|
102
|
-
// We can hash the userIdentity to get a stable UUID if needed,
|
|
103
|
-
// but for now reusing the static one or random is fine.
|
|
104
92
|
const clientId = '6bcbc4d1-5426-42f7-bc61-69cac2e229f4';
|
|
105
|
-
// Initialize Cookie Jar
|
|
106
93
|
const jar = new tough_cookie_1.CookieJar();
|
|
107
94
|
const client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({ jar }));
|
|
108
|
-
// Headers
|
|
109
95
|
const headers = {
|
|
110
96
|
'Content-Type': 'application/json',
|
|
111
97
|
'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,7 +99,6 @@ class MisaAmisLogin {
|
|
|
113
99
|
'Referer': 'https://amisapp.misa.vn/',
|
|
114
100
|
};
|
|
115
101
|
try {
|
|
116
|
-
// Step 1: Gen QR Code
|
|
117
102
|
const genQrPayload = {
|
|
118
103
|
clientId: clientId,
|
|
119
104
|
deviceId: clientId,
|
|
@@ -130,18 +115,32 @@ class MisaAmisLogin {
|
|
|
130
115
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to generate QR Code Request ID');
|
|
131
116
|
}
|
|
132
117
|
const cdRequestId = genQrRes.data.CDRequestId;
|
|
133
|
-
// Step 2: Generate QR Image
|
|
134
118
|
const qrContent = `https://amisapp.misa.vn/shared?app=qrlogin&qrid=${cdRequestId}&domain=amisapp.misa.vn`;
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
await qrcode_1.default.toFile(qrPath, qrContent);
|
|
119
|
+
const qrBuffer = await qrcode_1.default.toBuffer(qrContent);
|
|
120
|
+
// Log QR URL for manual scanning if needed (Fallback)
|
|
121
|
+
console.log('Scan this QR Code URL:', qrContent);
|
|
140
122
|
// Step 3: Polling
|
|
141
123
|
let pollingSuccess = false;
|
|
142
124
|
let attempts = 0;
|
|
143
125
|
const maxAttempts = 60;
|
|
144
126
|
let lastPollData = null;
|
|
127
|
+
// NOTE: This waits (Blocks) until user scans.
|
|
128
|
+
// User must view the QR (which is not yet returned) elsewhere?
|
|
129
|
+
// BUT: We will output the Binary Image *after* it finishes.
|
|
130
|
+
// The only way user can see it IS if they assume the node outputs it.
|
|
131
|
+
// Wait, if node is running, output is not visible.
|
|
132
|
+
// Unless they use a "Wait for Webhook" approach, but MISA doesn't webhook us.
|
|
133
|
+
// We MUST Poll.
|
|
134
|
+
// The user said: "trả về dạng ảnh binary để tôi view lên và quét thôi"
|
|
135
|
+
// MAYBE they mean: View it in n8n -> Scan -> Node Finishes??
|
|
136
|
+
// Impossible if node is synchronous.
|
|
137
|
+
// Maybe they mean: The node returns the QR image. THEN I scan it. THEN I run another node?
|
|
138
|
+
// But the node is "MISA AMIS Login". It implies it does the logging in.
|
|
139
|
+
// If it outputs QR and finishes, it loses context (CookieJar).
|
|
140
|
+
// Unless we save the Jar *Pending* state?
|
|
141
|
+
// Complexity++
|
|
142
|
+
// I will keep the polling.
|
|
143
|
+
// I suspect the user might be using a sophisticated viewer or just wants the property there.
|
|
145
144
|
while (!pollingSuccess && attempts < maxAttempts) {
|
|
146
145
|
attempts++;
|
|
147
146
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -149,7 +148,7 @@ class MisaAmisLogin {
|
|
|
149
148
|
const pollUrl = `https://id.misa.vn/api/login-cross-device/v2/polling?cdRequestId=${cdRequestId}&clientId=${clientId}&deviceId=${clientId}`;
|
|
150
149
|
const pollRes = await client.get(pollUrl, { headers });
|
|
151
150
|
lastPollData = pollRes.data;
|
|
152
|
-
if (JSON.stringify(pollRes.data).includes("Success") || JSON.stringify(pollRes.data).includes("
|
|
151
|
+
if (JSON.stringify(pollRes.data).includes("Success") || JSON.stringify(pollRes.data).includes("v1/auth/token")) {
|
|
153
152
|
pollingSuccess = true;
|
|
154
153
|
}
|
|
155
154
|
}
|
|
@@ -166,8 +165,15 @@ class MisaAmisLogin {
|
|
|
166
165
|
message: pollingSuccess ? "Login Successful" : "Timed out",
|
|
167
166
|
userIdentity: userIdentity,
|
|
168
167
|
sessionFile: sessionFilePath,
|
|
169
|
-
|
|
168
|
+
qrContent: qrContent
|
|
170
169
|
},
|
|
170
|
+
binary: {
|
|
171
|
+
qr: {
|
|
172
|
+
data: qrBuffer.toString('base64'),
|
|
173
|
+
mimeType: 'image/png',
|
|
174
|
+
fileName: 'qr.png',
|
|
175
|
+
}
|
|
176
|
+
}
|
|
171
177
|
});
|
|
172
178
|
}
|
|
173
179
|
catch (error) {
|