@dongdev/fca-unofficial 3.0.17 → 3.0.20
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/CHANGELOG.md +6 -0
- package/module/login.js +10 -0
- package/module/loginHelper.js +130 -33
- package/package.json +1 -1
- package/src/api/socket/listenMqtt.js +0 -1
package/CHANGELOG.md
CHANGED
package/module/login.js
CHANGED
|
@@ -20,6 +20,11 @@ if (!global.fca._errorHandlersInstalled) {
|
|
|
20
20
|
const errorCode = reason.code || reason.cause?.code;
|
|
21
21
|
const errorMessage = reason.message || String(reason);
|
|
22
22
|
|
|
23
|
+
// Suppress Sequelize instance errors (handled gracefully in getBackupModel)
|
|
24
|
+
if (errorMessage.includes("No Sequelize instance passed")) {
|
|
25
|
+
return; // Silently ignore - already handled
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
// Handle fetch timeout errors gracefully
|
|
24
29
|
if (errorCode === "UND_ERR_CONNECT_TIMEOUT" ||
|
|
25
30
|
errorCode === "ETIMEDOUT" ||
|
|
@@ -54,6 +59,11 @@ if (!global.fca._errorHandlersInstalled) {
|
|
|
54
59
|
const errorMessage = error.message || String(error);
|
|
55
60
|
const errorCode = error.code;
|
|
56
61
|
|
|
62
|
+
// Suppress Sequelize instance errors (handled gracefully in getBackupModel)
|
|
63
|
+
if (errorMessage.includes("No Sequelize instance passed")) {
|
|
64
|
+
return; // Silently ignore - already handled
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
// Handle fetch/network errors
|
|
58
68
|
if (errorCode === "UND_ERR_CONNECT_TIMEOUT" ||
|
|
59
69
|
errorCode === "ETIMEDOUT" ||
|
package/module/loginHelper.js
CHANGED
|
@@ -182,28 +182,45 @@ function cookieHeaderFromJar(j) {
|
|
|
182
182
|
let uniqueIndexEnsured = false;
|
|
183
183
|
|
|
184
184
|
function getBackupModel() {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
185
|
+
try {
|
|
186
|
+
if (!models || !models.sequelize || !models.Sequelize) return null;
|
|
187
|
+
const sequelize = models.sequelize;
|
|
188
|
+
|
|
189
|
+
// Validate that sequelize is a proper Sequelize instance
|
|
190
|
+
if (!sequelize || typeof sequelize.define !== "function") return null;
|
|
191
|
+
|
|
192
|
+
const { DataTypes } = models.Sequelize;
|
|
193
|
+
if (sequelize.models && sequelize.models.AppStateBackup) return sequelize.models.AppStateBackup;
|
|
194
|
+
const dialect = typeof sequelize.getDialect === "function" ? sequelize.getDialect() : "sqlite";
|
|
195
|
+
const LongText = (dialect === "mysql" || dialect === "mariadb") ? DataTypes.TEXT("long") : DataTypes.TEXT;
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const AppStateBackup = sequelize.define(
|
|
199
|
+
"AppStateBackup",
|
|
200
|
+
{
|
|
201
|
+
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
|
202
|
+
userID: { type: DataTypes.STRING, allowNull: false },
|
|
203
|
+
type: { type: DataTypes.STRING, allowNull: false },
|
|
204
|
+
data: { type: LongText }
|
|
205
|
+
},
|
|
206
|
+
{ tableName: "app_state_backups", timestamps: true, indexes: [{ unique: true, fields: ["userID", "type"] }] }
|
|
207
|
+
);
|
|
208
|
+
return AppStateBackup;
|
|
209
|
+
} catch (defineError) {
|
|
210
|
+
// If define fails, log and return null
|
|
211
|
+
logger(`Failed to define AppStateBackup model: ${defineError && defineError.message ? defineError.message : String(defineError)}`, "warn");
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
// Silently handle any errors in getBackupModel
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
202
218
|
}
|
|
203
219
|
|
|
204
220
|
async function ensureUniqueIndex(sequelize) {
|
|
205
|
-
if (uniqueIndexEnsured) return;
|
|
221
|
+
if (uniqueIndexEnsured || !sequelize) return;
|
|
206
222
|
try {
|
|
223
|
+
if (typeof sequelize.getQueryInterface !== "function") return;
|
|
207
224
|
await sequelize.getQueryInterface().addIndex("app_state_backups", ["userID", "type"], { unique: true, name: "app_state_user_type_unique" });
|
|
208
225
|
} catch { }
|
|
209
226
|
uniqueIndexEnsured = true;
|
|
@@ -225,6 +242,7 @@ async function backupAppStateSQL(j, userID) {
|
|
|
225
242
|
try {
|
|
226
243
|
const Model = getBackupModel();
|
|
227
244
|
if (!Model) return;
|
|
245
|
+
if (!models || !models.sequelize) return;
|
|
228
246
|
await Model.sync();
|
|
229
247
|
await ensureUniqueIndex(models.sequelize);
|
|
230
248
|
const appJson = getAppState(j);
|
|
@@ -289,26 +307,101 @@ async function setJarCookies(j, appstate) {
|
|
|
289
307
|
const cookiePath = c.path || "/";
|
|
290
308
|
const dom = cookieDomain.replace(/^\./, "");
|
|
291
309
|
|
|
292
|
-
//
|
|
310
|
+
// Handle expirationDate (can be in seconds or milliseconds)
|
|
293
311
|
let expiresStr = "";
|
|
294
|
-
if (c.
|
|
312
|
+
if (c.expirationDate !== undefined) {
|
|
313
|
+
let expiresDate;
|
|
314
|
+
if (typeof c.expirationDate === "number") {
|
|
315
|
+
// If expirationDate is less than a year from now in seconds, treat as seconds
|
|
316
|
+
// Otherwise treat as milliseconds
|
|
317
|
+
const now = Date.now();
|
|
318
|
+
const oneYearInMs = 365 * 24 * 60 * 60 * 1000;
|
|
319
|
+
if (c.expirationDate < (now + oneYearInMs) / 1000) {
|
|
320
|
+
expiresDate = new Date(c.expirationDate * 1000);
|
|
321
|
+
} else {
|
|
322
|
+
expiresDate = new Date(c.expirationDate);
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
expiresDate = new Date(c.expirationDate);
|
|
326
|
+
}
|
|
327
|
+
expiresStr = `; expires=${expiresDate.toUTCString()}`;
|
|
328
|
+
} else if (c.expires) {
|
|
295
329
|
const expiresDate = typeof c.expires === "number" ? new Date(c.expires) : new Date(c.expires);
|
|
296
330
|
expiresStr = `; expires=${expiresDate.toUTCString()}`;
|
|
297
331
|
}
|
|
298
332
|
|
|
299
|
-
//
|
|
300
|
-
const
|
|
333
|
+
// Helper function to build cookie string
|
|
334
|
+
const buildCookieString = (domainOverride = null) => {
|
|
335
|
+
const domain = domainOverride || cookieDomain;
|
|
336
|
+
let cookieParts = [`${cookieName}=${cookieValue}${expiresStr}`];
|
|
337
|
+
cookieParts.push(`Domain=${domain}`);
|
|
338
|
+
cookieParts.push(`Path=${cookiePath}`);
|
|
301
339
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const base4 = `https://www.${dom}${cookiePath}`;
|
|
340
|
+
// Add Secure flag if secure is true
|
|
341
|
+
if (c.secure === true) {
|
|
342
|
+
cookieParts.push("Secure");
|
|
343
|
+
}
|
|
307
344
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
345
|
+
// Add HttpOnly flag if httpOnly is true
|
|
346
|
+
if (c.httpOnly === true) {
|
|
347
|
+
cookieParts.push("HttpOnly");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Add SameSite attribute if provided
|
|
351
|
+
if (c.sameSite) {
|
|
352
|
+
const sameSiteValue = String(c.sameSite).toLowerCase();
|
|
353
|
+
if (["strict", "lax", "none"].includes(sameSiteValue)) {
|
|
354
|
+
cookieParts.push(`SameSite=${sameSiteValue.charAt(0).toUpperCase() + sameSiteValue.slice(1)}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return cookieParts.join("; ");
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// Determine target URLs and cookie strings based on domain
|
|
362
|
+
const cookieConfigs = [];
|
|
363
|
+
|
|
364
|
+
// For .facebook.com domain, set for both facebook.com and messenger.com
|
|
365
|
+
if (cookieDomain === ".facebook.com" || cookieDomain === "facebook.com") {
|
|
366
|
+
// Set for facebook.com with .facebook.com domain
|
|
367
|
+
cookieConfigs.push({ url: `http://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
368
|
+
cookieConfigs.push({ url: `https://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
369
|
+
cookieConfigs.push({ url: `http://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
370
|
+
cookieConfigs.push({ url: `https://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
371
|
+
|
|
372
|
+
// Set for messenger.com with .messenger.com domain (or without domain for host-only)
|
|
373
|
+
// Use .messenger.com domain to allow cross-subdomain sharing
|
|
374
|
+
cookieConfigs.push({ url: `http://messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
375
|
+
cookieConfigs.push({ url: `https://messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
376
|
+
cookieConfigs.push({ url: `http://www.messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
377
|
+
cookieConfigs.push({ url: `https://www.messenger.com${cookiePath}`, cookieStr: buildCookieString(".messenger.com") });
|
|
378
|
+
} else if (cookieDomain === ".messenger.com" || cookieDomain === "messenger.com") {
|
|
379
|
+
// Set for messenger.com only
|
|
380
|
+
const messengerDom = cookieDomain.replace(/^\./, "");
|
|
381
|
+
cookieConfigs.push({ url: `http://${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
382
|
+
cookieConfigs.push({ url: `https://${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
383
|
+
cookieConfigs.push({ url: `http://www.${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
384
|
+
cookieConfigs.push({ url: `https://www.${messengerDom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
385
|
+
} else {
|
|
386
|
+
// For other domains, set normally
|
|
387
|
+
cookieConfigs.push({ url: `http://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
388
|
+
cookieConfigs.push({ url: `https://${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
389
|
+
cookieConfigs.push({ url: `http://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
390
|
+
cookieConfigs.push({ url: `https://www.${dom}${cookiePath}`, cookieStr: buildCookieString() });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Set cookie for all target URLs, silently catch domain errors
|
|
394
|
+
for (const config of cookieConfigs) {
|
|
395
|
+
tasks.push(j.setCookie(config.cookieStr, config.url).catch((err) => {
|
|
396
|
+
// Silently ignore domain mismatch errors for cross-domain cookies
|
|
397
|
+
// These are expected when setting cookies across domains
|
|
398
|
+
if (err && err.message && err.message.includes("Cookie not in this host's domain")) {
|
|
399
|
+
return; // Expected error, ignore
|
|
400
|
+
}
|
|
401
|
+
// Log other errors but don't throw
|
|
402
|
+
return;
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
312
405
|
}
|
|
313
406
|
await Promise.all(tasks);
|
|
314
407
|
}
|
|
@@ -835,8 +928,12 @@ function loginHelper(appState, Cookie, email, password, globalOptions, callback)
|
|
|
835
928
|
}
|
|
836
929
|
})
|
|
837
930
|
.catch(function (error) {
|
|
838
|
-
|
|
839
|
-
|
|
931
|
+
// Silently handle database errors - they're not critical for login
|
|
932
|
+
const errorMsg = error && error.message ? error.message : String(error);
|
|
933
|
+
if (!errorMsg.includes("No Sequelize instance passed")) {
|
|
934
|
+
// Only log non-Sequelize instance errors
|
|
935
|
+
logger(`Database connection failed: ${errorMsg}`, "warn");
|
|
936
|
+
}
|
|
840
937
|
});
|
|
841
938
|
logger("FCA fix/update by DongDev (Donix-VN)", "info");
|
|
842
939
|
const ctxMain = {
|
package/package.json
CHANGED