-
Notifications
You must be signed in to change notification settings - Fork 98
/
Copy pathauthQueue.js
158 lines (130 loc) · 5.01 KB
/
authQueue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import {redeem2FACode, redeemCookies, redeemUsernamePassword} from "./auth.js";
import config from "../misc/config.js";
import {wait} from "../misc/util.js";
import {
mqGetAuthQueueItemStatus,
mqLogin2fa,
mqLoginCookies,
mqLoginUsernamePass, mqNullOperation,
useMultiqueue
} from "../misc/multiqueue.js";
export const Operations = {
USERNAME_PASSWORD: "up",
MFA: "mf",
COOKIES: "ck",
NULL: "00"
}
const queue = [];
const queueResults = [];
let queueCounter = 1;
let processingCount = 0;
let authQueueInterval;
let lastQueueProcess = 0; // timestamp
export const startAuthQueue = () => {
clearInterval(authQueueInterval);
if(config.useLoginQueue) authQueueInterval = setInterval(processAuthQueue, config.loginQueueInterval);
}
export const queueUsernamePasswordLogin = async (id, username, password) => {
if(!config.useLoginQueue) return await redeemUsernamePassword(id, username, password);
if(useMultiqueue()) return await mqLoginUsernamePass(id, username, password);
const c = queueCounter++;
queue.push({
operation: Operations.USERNAME_PASSWORD,
c, id, username, password,
});
console.log(`Added Username+Password login to auth queue for user ${id} (c=${c})`);
if(processingCount === 0) await processAuthQueue();
return {inQueue: true, c};
}
export const queue2FACodeRedeem = async (id, code) => {
if(!config.useLoginQueue) return await redeem2FACode(id, code);
if(useMultiqueue()) return {inQueue: false, ...await mqLogin2fa(id, code)};
const c = queueCounter++;
queue.push({ // should 2FA redeems be given priority?
operation: Operations.MFA,
c, id, code
});
console.log(`Added 2fa code redeem to auth queue for user ${id} (c=${c})`);
if(processingCount === 0) await processAuthQueue();
return {inQueue: true, c};
}
export const queueCookiesLogin = async (id, cookies) => {
if(!config.useLoginQueue) return await redeemCookies(id, cookies);
if(useMultiqueue()) return {inQueue: false, ...await mqLoginCookies(id, cookies)};
const c = queueCounter++;
queue.push({
operation: Operations.COOKIES,
c, id, cookies
});
console.log(`Added cookie login to auth queue for user ${id} (c=${c})`);
if(processingCount === 0) await processAuthQueue();
return {inQueue: true, c};
}
export const queueNullOperation = async (timeout) => { // used for stress-testing the auth queue
if(!config.useLoginQueue) await wait(timeout);
if(useMultiqueue()) return {inQueue: false, ...await mqNullOperation(timeout)}
const c = queueCounter++;
queue.push({
operation: Operations.NULL,
c, timeout
});
console.log(`Added null operation to auth queue with timeout ${timeout} (c=${c})`);
if(processingCount === 0) await processAuthQueue();
return {inQueue: true, c};
}
export const processAuthQueue = async () => {
lastQueueProcess = Date.now();
if(!config.useLoginQueue || !queue.length) return;
if(useMultiqueue()) return;
const item = queue.shift();
console.log(`Processing auth queue item "${item.operation}" for ${item.id} (c=${item.c}, left=${queue.length})`);
processingCount++;
let result;
try {
switch (item.operation) {
case Operations.USERNAME_PASSWORD:
result = await redeemUsernamePassword(item.id, item.username, item.password);
break;
case Operations.MFA:
result = await redeem2FACode(item.id, item.code);
break;
case Operations.COOKIES:
result = await redeemCookies(item.id, item.cookies);
break;
case Operations.NULL:
await wait(item.timeout);
result = {success: true};
break;
}
} catch(e) {
result = {success: false, error: e};
}
queueResults.push({
c: item.c,
result
});
console.log(`Finished processing auth queue item "${item.operation}" for ${item.id} (c=${item.c})`);
processingCount--;
}
export const getAuthQueueItemStatus = async (c) => {
if(useMultiqueue()) return await mqGetAuthQueueItemStatus(c);
// check if in queue
let item = queue.find(i => i.c === c);
if(item) return {processed: false, ...remainingAndEstimatedTimestamp(c)};
// check if currenty processing
const index = queueResults.findIndex(i => i.c === c);
if(index === -1) return {processed: false, remaining: 0};
// get result
item = queueResults[index];
queueResults.splice(index, 1);
return {processed: true, result: item.result};
}
const remainingAndEstimatedTimestamp = (c) => {
const remaining = c - queue[0].c;
let timestamp = lastQueueProcess + ((remaining + 1) * config.loginQueueInterval);
// UX: if the timestamp is late, even by half a second, the user gets impatient.
// on the other hand, if it happens early, the user is happy.
timestamp += 2000;
timestamp = Math.round(timestamp / 1000);
return {remaining, timestamp};
}