alchemymvc 1.2.8 → 1.3.1
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/lib/app/behaviour/sluggable_behaviour.js +4 -2
- package/lib/app/conduit/http_conduit.js +7 -2
- package/lib/app/conduit/loopback_conduit.js +2 -2
- package/lib/app/conduit/socket_conduit.js +20 -5
- package/lib/app/controller/alchemy_info_controller.js +4 -8
- package/lib/app/helper/backed_map.js +2 -2
- package/lib/app/helper/router_helper.js +98 -24
- package/lib/app/helper_controller/controller.js +45 -30
- package/lib/app/helper_datasource/00-nosql_datasource.js +44 -10
- package/lib/app/helper_field/enum_field.js +4 -4
- package/lib/app/helper_field/schema_field.js +50 -36
- package/lib/app/helper_model/document.js +81 -46
- package/lib/app/helper_model/field_set.js +11 -0
- package/lib/app/helper_model/model.js +107 -53
- package/lib/app/helper_validator/00_validator.js +38 -6
- package/lib/app/helper_validator/not_empty_validator.js +1 -3
- package/lib/app/routes.js +7 -1
- package/lib/bootstrap.js +1 -0
- package/lib/class/conduit.js +438 -290
- package/lib/class/controller.js +18 -15
- package/lib/class/datasource.js +19 -8
- package/lib/class/document.js +3 -3
- package/lib/class/field.js +34 -3
- package/lib/class/inode.js +27 -0
- package/lib/class/inode_file.js +204 -4
- package/lib/class/migration.js +2 -1
- package/lib/class/model.js +16 -5
- package/lib/class/path_definition.js +76 -120
- package/lib/class/path_param_definition.js +202 -0
- package/lib/class/postponement.js +573 -0
- package/lib/class/route.js +193 -33
- package/lib/class/router.js +22 -4
- package/lib/class/schema.js +47 -11
- package/lib/class/schema_client.js +65 -35
- package/lib/class/session.js +138 -12
- package/lib/class/sitemap.js +341 -0
- package/lib/core/base.js +13 -3
- package/lib/core/client_alchemy.js +78 -7
- package/lib/core/client_base.js +16 -10
- package/lib/core/middleware.js +56 -45
- package/lib/init/alchemy.js +124 -11
- package/lib/init/constants.js +11 -0
- package/lib/init/functions.js +163 -86
- package/lib/stages.js +18 -3
- package/package.json +6 -6
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
const QUEUE = [];
|
|
2
|
+
let queue_check_id,
|
|
3
|
+
_total_postponement_counter = 0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The Postponement Class represents requests that will be handled later.
|
|
7
|
+
*
|
|
8
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
9
|
+
* @since 1.3.1
|
|
10
|
+
* @version 1.3.1
|
|
11
|
+
*
|
|
12
|
+
* @param {Conduit} conduit The original conduit
|
|
13
|
+
*/
|
|
14
|
+
const Postponement = Function.inherits('Alchemy.Base', 'Alchemy.Conduit', function Postponement(conduit, id, options) {
|
|
15
|
+
|
|
16
|
+
// The original conduit instance
|
|
17
|
+
this.original_conduit = conduit;
|
|
18
|
+
|
|
19
|
+
// The original response object of the conduit
|
|
20
|
+
this.original_response = conduit.response;
|
|
21
|
+
|
|
22
|
+
// Get the session
|
|
23
|
+
this.session = conduit.getSession();
|
|
24
|
+
|
|
25
|
+
// The identifier of this postponement
|
|
26
|
+
this.id = id;
|
|
27
|
+
|
|
28
|
+
// The original path string
|
|
29
|
+
this.original_path = conduit.path;
|
|
30
|
+
|
|
31
|
+
// The URL where to get postponement info
|
|
32
|
+
this.url = '/alchemy/postponed/' + id;
|
|
33
|
+
|
|
34
|
+
// The last known position in the queue
|
|
35
|
+
this.last_queue_position = null;
|
|
36
|
+
|
|
37
|
+
// Postponement options
|
|
38
|
+
this.options = options || {};
|
|
39
|
+
|
|
40
|
+
// When did this postponement start
|
|
41
|
+
this.started = Date.now();
|
|
42
|
+
|
|
43
|
+
// When did this postponement end?
|
|
44
|
+
this.ended = null;
|
|
45
|
+
|
|
46
|
+
// Has this postponement been released yet?
|
|
47
|
+
this.released = false;
|
|
48
|
+
|
|
49
|
+
// When was the last check made from the client?
|
|
50
|
+
this.last_check = this.started;
|
|
51
|
+
|
|
52
|
+
// Also attach this postponement to the conduit
|
|
53
|
+
conduit.postponement = this;
|
|
54
|
+
|
|
55
|
+
// Keep track of the total amount of postponements ever
|
|
56
|
+
_total_postponement_counter++;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The queued postponements
|
|
61
|
+
*
|
|
62
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
63
|
+
* @since 1.3.1
|
|
64
|
+
* @version 1.3.1
|
|
65
|
+
*
|
|
66
|
+
* @return {Postponement[]}
|
|
67
|
+
*/
|
|
68
|
+
Postponement.setStatic('queue', QUEUE);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the total amount of postponements ever
|
|
72
|
+
*
|
|
73
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
74
|
+
* @since 1.3.1
|
|
75
|
+
* @version 1.3.1
|
|
76
|
+
*
|
|
77
|
+
* @return {Number}
|
|
78
|
+
*/
|
|
79
|
+
Postponement.setStaticProperty(function total_postponement_counter() {
|
|
80
|
+
return _total_postponement_counter;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The current queue length
|
|
85
|
+
*
|
|
86
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
87
|
+
* @since 1.3.1
|
|
88
|
+
* @version 1.3.1
|
|
89
|
+
*
|
|
90
|
+
* @return {Number}
|
|
91
|
+
*/
|
|
92
|
+
Postponement.queue_length = 0;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* How long has this been waiting?
|
|
96
|
+
*
|
|
97
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
98
|
+
* @since 1.3.1
|
|
99
|
+
* @version 1.3.1
|
|
100
|
+
*
|
|
101
|
+
* @return {Number}
|
|
102
|
+
*/
|
|
103
|
+
Postponement.setProperty(function time_waited() {
|
|
104
|
+
|
|
105
|
+
if (!this.ended) {
|
|
106
|
+
return Date.now() - this.started;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this.ended - this.started;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* How long has this postponement been left unchecked?
|
|
114
|
+
*
|
|
115
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
116
|
+
* @since 1.3.1
|
|
117
|
+
* @version 1.3.1
|
|
118
|
+
*
|
|
119
|
+
* @return {Number}
|
|
120
|
+
*/
|
|
121
|
+
Postponement.setProperty(function time_unchecked() {
|
|
122
|
+
return Date.now() - this.last_check;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Has this postponement been abandoned?
|
|
127
|
+
*
|
|
128
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
129
|
+
* @since 1.3.1
|
|
130
|
+
* @version 1.3.1
|
|
131
|
+
*
|
|
132
|
+
* @return {Number}
|
|
133
|
+
*/
|
|
134
|
+
Postponement.setProperty(function has_been_abandoned() {
|
|
135
|
+
|
|
136
|
+
if (this.time_unchecked > 30 * 1000) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return false;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the current position in the queue
|
|
145
|
+
*
|
|
146
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
147
|
+
* @since 1.3.1
|
|
148
|
+
* @version 1.3.1
|
|
149
|
+
*
|
|
150
|
+
* @return {Number}
|
|
151
|
+
*/
|
|
152
|
+
Postponement.setProperty(function position_in_queue() {
|
|
153
|
+
|
|
154
|
+
if (this.last_queue_position == null) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let index = QUEUE.indexOf(this);
|
|
159
|
+
|
|
160
|
+
if (index == -1) {
|
|
161
|
+
index = null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.last_queue_position = index;
|
|
165
|
+
|
|
166
|
+
return index;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Schedule a check of the queue
|
|
171
|
+
*
|
|
172
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
173
|
+
* @since 1.3.1
|
|
174
|
+
* @version 1.3.1
|
|
175
|
+
*
|
|
176
|
+
* @param {Conduit} conduit The new conduit
|
|
177
|
+
*/
|
|
178
|
+
Postponement.setStatic(function scheduleQueueCheck() {
|
|
179
|
+
|
|
180
|
+
if (queue_check_id) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
queue_check_id = setTimeout(() => {
|
|
185
|
+
queue_check_id = null;
|
|
186
|
+
Postponement.checkQueue();
|
|
187
|
+
}, 5000);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check the (top of the) queue
|
|
192
|
+
*
|
|
193
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
194
|
+
* @since 1.3.1
|
|
195
|
+
* @version 1.3.1
|
|
196
|
+
*
|
|
197
|
+
* @param {Conduit} conduit The new conduit
|
|
198
|
+
*/
|
|
199
|
+
Postponement.setStatic(function checkQueue() {
|
|
200
|
+
|
|
201
|
+
let length = QUEUE.length;
|
|
202
|
+
|
|
203
|
+
this.queue_length = length;
|
|
204
|
+
|
|
205
|
+
if (!length) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let to_remove = [],
|
|
210
|
+
postponement,
|
|
211
|
+
max = length,
|
|
212
|
+
i;
|
|
213
|
+
|
|
214
|
+
if (max > 20) {
|
|
215
|
+
max = 20;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (i = 0; i < max; i++) {
|
|
219
|
+
postponement = QUEUE[i];
|
|
220
|
+
|
|
221
|
+
if (postponement.has_been_abandoned) {
|
|
222
|
+
to_remove.push(postponement);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (i = 0; i < to_remove.length; i++) {
|
|
227
|
+
postponement = to_remove[i];
|
|
228
|
+
postponement.expire();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Postponement.scheduleQueueCheck();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handle a request
|
|
236
|
+
*
|
|
237
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
238
|
+
* @since 1.3.1
|
|
239
|
+
* @version 1.3.1
|
|
240
|
+
*
|
|
241
|
+
* @param {Conduit} conduit The new conduit
|
|
242
|
+
*/
|
|
243
|
+
Postponement.setMethod(function handleRequest(conduit) {
|
|
244
|
+
|
|
245
|
+
if (conduit.ajax) {
|
|
246
|
+
let check_queue = conduit.param('check_queue');
|
|
247
|
+
|
|
248
|
+
if (check_queue) {
|
|
249
|
+
|
|
250
|
+
let data = {
|
|
251
|
+
position : this.position_in_queue,
|
|
252
|
+
allowed : false,
|
|
253
|
+
location : null,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (this.attemptUnlock()) {
|
|
257
|
+
data.allowed = true;
|
|
258
|
+
data.location = this.url;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
conduit.end(data);
|
|
262
|
+
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let resumed = this.attemptResume(conduit);
|
|
268
|
+
|
|
269
|
+
if (!resumed) {
|
|
270
|
+
this.showPostponementMessage(conduit);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Attempt to unlock
|
|
276
|
+
*
|
|
277
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
278
|
+
* @since 1.3.1
|
|
279
|
+
* @version 1.3.1
|
|
280
|
+
*
|
|
281
|
+
* @return {Boolean} True if the request is being resumed
|
|
282
|
+
*/
|
|
283
|
+
Postponement.setMethod(function attemptUnlock() {
|
|
284
|
+
|
|
285
|
+
if (this.released) {
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
Postponement.scheduleQueueCheck();
|
|
290
|
+
|
|
291
|
+
if (this.position_in_queue > 5) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (alchemy.lagInMs() > 100) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.released = true;
|
|
300
|
+
|
|
301
|
+
return this.released;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Attempt to resume this postponement.
|
|
306
|
+
* If it's in a queue and it's not our turn yet, do nothing.
|
|
307
|
+
*
|
|
308
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
309
|
+
* @since 1.3.1
|
|
310
|
+
* @version 1.3.1
|
|
311
|
+
*
|
|
312
|
+
* @param {Conduit} conduit The new conduit
|
|
313
|
+
*
|
|
314
|
+
* @return {Boolean} True if the request is being resumed
|
|
315
|
+
*/
|
|
316
|
+
Postponement.setMethod(function attemptResume(conduit) {
|
|
317
|
+
|
|
318
|
+
if (!this.attemptUnlock()) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Let the conduit know the response is being requested now
|
|
323
|
+
// (Certain postponements also delay the processing of the request)
|
|
324
|
+
this.original_conduit.emit('get-postponed-response');
|
|
325
|
+
|
|
326
|
+
// Once we're sure the postponed end has been reached,
|
|
327
|
+
// actually send that to the browser
|
|
328
|
+
this.original_conduit.afterOnce('after-postponed-end', () => {
|
|
329
|
+
|
|
330
|
+
this.original_conduit.response = conduit.response;
|
|
331
|
+
this.original_conduit._end(...this.original_conduit._end_arguments);
|
|
332
|
+
|
|
333
|
+
this.remove();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return true;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Show the postponement message to the given conduit
|
|
341
|
+
*
|
|
342
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
343
|
+
* @since 1.3.1
|
|
344
|
+
* @version 1.3.1
|
|
345
|
+
*
|
|
346
|
+
* @param {Conduit} conduit
|
|
347
|
+
*/
|
|
348
|
+
Postponement.setMethod(function showPostponementMessage(conduit) {
|
|
349
|
+
|
|
350
|
+
if (!conduit) {
|
|
351
|
+
conduit = this.original_conduit;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let response = conduit?.response || this.original_response;
|
|
355
|
+
|
|
356
|
+
if (!response) {
|
|
357
|
+
throw new Error('Failed to find a response instance, unable to show postponement message');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
response.setHeader('X-Robots-Tag', 'none');
|
|
361
|
+
|
|
362
|
+
let position_in_queue = this.position_in_queue;
|
|
363
|
+
|
|
364
|
+
// Already set the cookies
|
|
365
|
+
if (conduit.new_cookie_header.length) {
|
|
366
|
+
response.setHeader('set-cookie', conduit.new_cookie_header);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Set the location header where the client should look at later
|
|
370
|
+
response.setHeader('Location', this.url);
|
|
371
|
+
response.setHeader('Content-Type', 'text/html');
|
|
372
|
+
|
|
373
|
+
if (this.options.expected_duration) {
|
|
374
|
+
response.setHeader('Expected-Duration', Number(this.options.expected_duration / 1000).toFixed(2));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Write the headers & status
|
|
378
|
+
response.writeHead(this.options.status || 202);
|
|
379
|
+
|
|
380
|
+
let end_message = this.options.end_message;
|
|
381
|
+
|
|
382
|
+
// End the response if wanted
|
|
383
|
+
if (end_message !== false) {
|
|
384
|
+
if (!end_message) {
|
|
385
|
+
if (position_in_queue != null) {
|
|
386
|
+
end_message = this.getQueueHTML();
|
|
387
|
+
} else {
|
|
388
|
+
end_message = 'The response has been postponed, you can find it at <a href="' + this.url + '">' + this.url + '</a>';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
end_message = '';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
response.end(end_message);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Remove this postponement
|
|
400
|
+
*
|
|
401
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
402
|
+
* @since 1.3.1
|
|
403
|
+
* @version 1.3.1
|
|
404
|
+
*
|
|
405
|
+
* @param {Boolean} expired True if this is due to an expired session
|
|
406
|
+
*/
|
|
407
|
+
Postponement.setMethod(function remove(expired) {
|
|
408
|
+
|
|
409
|
+
if (!expired) {
|
|
410
|
+
const session = this.session;
|
|
411
|
+
|
|
412
|
+
this.ended = Date.now();
|
|
413
|
+
session.postponements.remove(this.id);
|
|
414
|
+
|
|
415
|
+
session.addFinishedQueueDuration(this.time_waited);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let index = this.position_in_queue;
|
|
419
|
+
|
|
420
|
+
if (index != null) {
|
|
421
|
+
QUEUE.splice(index, 1);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Called when the session or the postponement expires
|
|
427
|
+
*
|
|
428
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
429
|
+
* @since 1.3.1
|
|
430
|
+
* @version 1.3.1
|
|
431
|
+
*/
|
|
432
|
+
Postponement.setMethod(function expire() {
|
|
433
|
+
this.remove(true);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Put this postponement in a queue
|
|
438
|
+
*
|
|
439
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
440
|
+
* @since 1.3.1
|
|
441
|
+
* @version 1.3.1
|
|
442
|
+
*/
|
|
443
|
+
Postponement.setMethod(function putInQueue() {
|
|
444
|
+
|
|
445
|
+
if (this.last_queue_position != null) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
let new_length = QUEUE.push(this) - 1;
|
|
450
|
+
|
|
451
|
+
this.last_queue_position = new_length - 1;
|
|
452
|
+
|
|
453
|
+
// Update the queue length
|
|
454
|
+
Postponement.queue_length = new_length;
|
|
455
|
+
|
|
456
|
+
return this.last_queue_position;
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Get the HTML message for in the queue
|
|
461
|
+
*
|
|
462
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
463
|
+
* @since 1.3.1
|
|
464
|
+
* @version 1.3.1
|
|
465
|
+
*/
|
|
466
|
+
Postponement.setMethod(function getQueueHTML() {
|
|
467
|
+
|
|
468
|
+
let position = this.last_queue_position + 1;
|
|
469
|
+
|
|
470
|
+
let html = `<!DOCTYPE html>
|
|
471
|
+
<html>
|
|
472
|
+
<head>
|
|
473
|
+
<title>Please Wait...</title>
|
|
474
|
+
<style>
|
|
475
|
+
body {
|
|
476
|
+
background-color: #F5F5F5;
|
|
477
|
+
display: flex;
|
|
478
|
+
justify-content: center;
|
|
479
|
+
align-items: center;
|
|
480
|
+
height: 100vh;
|
|
481
|
+
font-family: 'Open Sans', sans-serif;
|
|
482
|
+
}
|
|
483
|
+
.main-logo {
|
|
484
|
+
max-width: 50vw;
|
|
485
|
+
max-height: 50vw;
|
|
486
|
+
object-fit: contain;
|
|
487
|
+
min-width: 150px;
|
|
488
|
+
max-width: 150px;
|
|
489
|
+
}
|
|
490
|
+
.container {
|
|
491
|
+
text-align: center;
|
|
492
|
+
background: #F0F8FF;
|
|
493
|
+
padding: 20px;
|
|
494
|
+
border-radius: 10px;
|
|
495
|
+
box-shadow: 5px 5px 10px #B0C4DE;
|
|
496
|
+
}
|
|
497
|
+
h1 {
|
|
498
|
+
font-size: 3em;
|
|
499
|
+
margin-bottom: 20px;
|
|
500
|
+
}
|
|
501
|
+
p {
|
|
502
|
+
font-size: 1.5em;
|
|
503
|
+
margin-bottom: 20px;
|
|
504
|
+
}
|
|
505
|
+
#queue {
|
|
506
|
+
font-size: 1.2em;
|
|
507
|
+
margin-bottom: 20px;
|
|
508
|
+
}
|
|
509
|
+
.loading {
|
|
510
|
+
display: flex;
|
|
511
|
+
justify-content: center;
|
|
512
|
+
align-items: center;
|
|
513
|
+
margin: 0 auto;
|
|
514
|
+
border: 6px solid #F5F5F5;
|
|
515
|
+
border-top: 6px solid #3498DB;
|
|
516
|
+
border-radius: 50%;
|
|
517
|
+
width: 60px;
|
|
518
|
+
height: 60px;
|
|
519
|
+
animation: spin 2s linear infinite;
|
|
520
|
+
}
|
|
521
|
+
@keyframes spin {
|
|
522
|
+
0% { transform: rotate(0deg); }
|
|
523
|
+
100% { transform: rotate(360deg); }
|
|
524
|
+
}
|
|
525
|
+
</style>
|
|
526
|
+
</head>
|
|
527
|
+
<body>
|
|
528
|
+
<div class="container">
|
|
529
|
+
`
|
|
530
|
+
|
|
531
|
+
if (alchemy.settings.main_logo) {
|
|
532
|
+
html += `<img src="${alchemy.settings.main_logo}" class="main-logo">\n`;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
html += `
|
|
536
|
+
<h1>Server is busy</h1>
|
|
537
|
+
<p id="queue">You are #${ position } in the queue.</p>
|
|
538
|
+
<div class="loading">
|
|
539
|
+
<div class="spinner"></div>
|
|
540
|
+
</div>
|
|
541
|
+
</div>
|
|
542
|
+
<script>
|
|
543
|
+
function checkQueue() {
|
|
544
|
+
let xhr = new XMLHttpRequest();
|
|
545
|
+
xhr.open('GET', '${ this.url }?check_queue=1', true);
|
|
546
|
+
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
547
|
+
xhr.onreadystatechange = function() {
|
|
548
|
+
if (xhr.readyState === 4) {
|
|
549
|
+
let data = JSON.parse(xhr.responseText);
|
|
550
|
+
let element = document.getElementById("queue");
|
|
551
|
+
|
|
552
|
+
if (data.allowed && data.location) {
|
|
553
|
+
element.innerHTML = "Redirecting!";
|
|
554
|
+
window.location.href = data.location;
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
let queue = data.position;
|
|
559
|
+
element.innerHTML = "You are #" + queue + " in the queue";
|
|
560
|
+
|
|
561
|
+
setTimeout(checkQueue, 15000);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
xhr.send();
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
setTimeout(checkQueue, 5000);
|
|
568
|
+
</script>
|
|
569
|
+
</body>
|
|
570
|
+
</html>`;
|
|
571
|
+
|
|
572
|
+
return html;
|
|
573
|
+
});
|