provide REST APIs to read from the SQLite DB and poll for updates
This commit is contained in:
parent
6ea10ef1e3
commit
5542cb0ab5
|
|
@ -0,0 +1,26 @@
|
||||||
|
module.exports = function patchGunWithCancel(Gun) {
|
||||||
|
Gun.chain.onWithCancel = (function() {
|
||||||
|
function cancelCallback(data,key,msg,ev) {
|
||||||
|
if (ev && typeof ev.off === 'function') {
|
||||||
|
ev.off();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(tag, arg, eas, as) {
|
||||||
|
if (typeof tag === 'function') {
|
||||||
|
let callback = tag;
|
||||||
|
const cancelEv = function() {
|
||||||
|
callback = cancelCallback;
|
||||||
|
};
|
||||||
|
const wrapper = function() {
|
||||||
|
return callback.apply(this, arguments);
|
||||||
|
};
|
||||||
|
this.on(wrapper, arg, eas, as);
|
||||||
|
return cancelEv;
|
||||||
|
} else {
|
||||||
|
this.on(tag, arg, eas, as);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
};
|
||||||
394
index.js
394
index.js
|
|
@ -1,45 +1,34 @@
|
||||||
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var Gun = require('gun');
|
var Gun = require('gun');
|
||||||
var SEA = require('gun/sea');
|
var SEA = require('gun/sea');
|
||||||
var NTS = require('gun/nts');
|
var NTS = require('gun/nts');
|
||||||
var rtc = require('gun/lib/webrtc');
|
var rtc = require('gun/lib/webrtc');
|
||||||
|
|
||||||
var sqlite3 = require('./sqlite3-promises');
|
var sqlite3 = require('./sqlite3-promises');
|
||||||
|
var express = require('express');
|
||||||
|
|
||||||
if(process.env.HTTPS_KEY){
|
require('./gun-with-cancel')(Gun);
|
||||||
|
|
||||||
|
const POLLING_TIMEOUT = 60000/*ms*/;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
var config = { port: process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || process.argv[2] || 8765 };
|
||||||
|
|
||||||
|
if (process.env.HTTPS_KEY) {
|
||||||
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
config.key = fs.readFileSync(process.env.HTTPS_KEY);
|
||||||
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
config.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||||
config.server = require('https').createServer(config, Gun.serve(__dirname));
|
config.server = require('https').createServer(config, app);
|
||||||
} else {
|
} else {
|
||||||
config.server = require('http').createServer(Gun.serve(__dirname));
|
config.server = require('http').createServer(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gun.chain.onWithCancel = (function() {
|
var gun = Gun({
|
||||||
function cancelCallback(data,key,msg,ev) {
|
web: config.server,
|
||||||
if (ev && typeof ev.off === 'function') {
|
peers: ['https://jessemcdonald.info/gun'],
|
||||||
ev.off();
|
});
|
||||||
}
|
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
||||||
}
|
|
||||||
|
|
||||||
return function(tag, arg, eas, as) {
|
|
||||||
if (typeof tag === 'function') {
|
|
||||||
let callback = tag;
|
|
||||||
const cancelEv = function() {
|
|
||||||
callback = cancelCallback;
|
|
||||||
};
|
|
||||||
const wrapper = function() {
|
|
||||||
return callback.apply(this, arguments);
|
|
||||||
};
|
|
||||||
this.on(wrapper, arg, eas, as);
|
|
||||||
return cancelEv;
|
|
||||||
} else {
|
|
||||||
this.on(tag, arg, eas, as);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
var appendJournal = (function() {
|
var appendJournal = (function() {
|
||||||
let lastJournalText = null;
|
let lastJournalText = null;
|
||||||
|
|
@ -51,12 +40,6 @@ var appendJournal = (function() {
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var gun = Gun({
|
|
||||||
web: config.server.listen(config.port),
|
|
||||||
peers: ['https://jessemcdonald.info/gun'],
|
|
||||||
});
|
|
||||||
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
|
||||||
|
|
||||||
function logIn(msg){
|
function logIn(msg){
|
||||||
console.log(`in msg:${JSON.stringify(msg)}.........`);
|
console.log(`in msg:${JSON.stringify(msg)}.........`);
|
||||||
}
|
}
|
||||||
|
|
@ -88,72 +71,181 @@ function logDbError(label) {
|
||||||
const dbInit = (async function dbInit() {
|
const dbInit = (async function dbInit() {
|
||||||
const db = await sqlite3.openAsync('./pacosako.db');
|
const db = await sqlite3.openAsync('./pacosako.db');
|
||||||
|
|
||||||
await db.runAsync(`
|
try {
|
||||||
CREATE TABLE IF NOT EXISTS games (
|
await db.runAsync(`VACUUM`);
|
||||||
gameId TEXT PRIMARY KEY,
|
} catch(err) {
|
||||||
board TEXT
|
logDbError('vacuum')(err);
|
||||||
)
|
}
|
||||||
`);
|
|
||||||
|
|
||||||
await db.runAsync(`
|
await db.runAsync(`
|
||||||
CREATE TABLE IF NOT EXISTS meta (
|
CREATE TABLE IF NOT EXISTS games (
|
||||||
gameId TEXT PRIMARY KEY,
|
gameId TEXT PRIMARY KEY,
|
||||||
lightName TEXT NOT NULL,
|
lightName TEXT NOT NULL,
|
||||||
darkName TEXT NOT NULL,
|
darkName TEXT NOT NULL,
|
||||||
moves INTEGER NOT NULL,
|
moves INTEGER NOT NULL,
|
||||||
status TEXT,
|
status TEXT NOT NULL,
|
||||||
timestamp INTEGER NOT NULL
|
timestamp INTEGER NOT NULL,
|
||||||
|
board TEXT NOT NULL,
|
||||||
|
added INTEGER NOT NULL,
|
||||||
|
modified INTEGER NOT NULL
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
await db.runAsync(`
|
||||||
|
CREATE INDEX IF NOT EXISTS games_timestamp ON games(timestamp)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await db.runAsync(`
|
||||||
|
CREATE INDEX IF NOT EXISTS games_modified ON games(modified)
|
||||||
|
`);
|
||||||
|
|
||||||
console.log('Connected to the SQLite database.');
|
console.log('Connected to the SQLite database.');
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
})().catch(logDbError('dbInit'));
|
})().catch(logDbError('dbInit'));
|
||||||
|
|
||||||
|
function pruneEmpty(obj) {
|
||||||
|
const copy = {};
|
||||||
|
for (const key in obj) {
|
||||||
|
const val = obj[key];
|
||||||
|
if (val !== undefined && val !== null && val !== '') {
|
||||||
|
copy[key] = obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
async function logAllMeta() {
|
async function logAllMeta() {
|
||||||
const db = await dbInit;
|
const db = await dbInit;
|
||||||
try {
|
try {
|
||||||
for await (const row of db.eachAsync(`SELECT * FROM meta ORDER BY timestamp DESC`)) {
|
const querySql = `
|
||||||
console.log(JSON.stringify(row));
|
SELECT gameId, lightName, darkName, moves, status, timestamp FROM games ORDER BY timestamp DESC
|
||||||
|
`;
|
||||||
|
for await (const row of db.eachAsync(querySql)) {
|
||||||
|
console.log(JSON.stringify(pruneEmpty(row)));
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
logDbError('logAllMeta')(err);
|
logDbError('logAllMeta')(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let waitingAnyGame = null;
|
||||||
|
const waitingGames = {};
|
||||||
|
|
||||||
|
function signalGameUpdate(gameId) {
|
||||||
|
const promise = waitingGames[gameId];
|
||||||
|
if (promise) {
|
||||||
|
delete waitingGames[gameId];
|
||||||
|
promise.resolve();
|
||||||
|
}
|
||||||
|
const anyPromise = waitingAnyGame;
|
||||||
|
if (anyPromise) {
|
||||||
|
waitingAnyGame = null;
|
||||||
|
anyPromise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForGameUpdate(gameId) {
|
||||||
|
let promise = waitingGames[gameId];
|
||||||
|
if (promise === undefined) {
|
||||||
|
let resolve;
|
||||||
|
promise = waitingGames[gameId] = new Promise((res) => { resolve = res; });
|
||||||
|
promise.resolve = resolve;
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForAnyGameUpdate() {
|
||||||
|
if (waitingAnyGame === null) {
|
||||||
|
let resolve;
|
||||||
|
waitingAnyGame = new Promise((res) => { resolve = res; });
|
||||||
|
waitingAnyGame.resolve = resolve;
|
||||||
|
}
|
||||||
|
return waitingAnyGame;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitFor(duration) {
|
||||||
|
return new Promise((resolve, reject) => setTimeout(() => { resolve(); }, duration));
|
||||||
|
}
|
||||||
|
|
||||||
//logAllMeta();
|
//logAllMeta();
|
||||||
|
|
||||||
async function updateMeta(gameId, meta) {
|
function checkString(value, label, dflt) {
|
||||||
|
try {
|
||||||
|
if (arguments.length >= 3 && (value === undefined || value === null)) {
|
||||||
|
return dflt;
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} catch (err) {}
|
||||||
|
throw { message: `${label || 'value'} should be a string` };
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkInteger(value, label, dflt) {
|
||||||
|
try {
|
||||||
|
if (arguments.length >= 3 && (value === undefined || value === null)) {
|
||||||
|
return dflt;
|
||||||
|
} else if (Number.isInteger(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} catch(err) {}
|
||||||
|
throw { message: `${label || 'value'} should be an integer` };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMeta(gameId, meta, time) {
|
||||||
|
if (arguments.length < 3) {
|
||||||
|
time = +new Date();
|
||||||
|
}
|
||||||
|
|
||||||
const db = await dbInit;
|
const db = await dbInit;
|
||||||
|
|
||||||
const insertSql =
|
const insertSql = `
|
||||||
`INSERT OR REPLACE INTO meta
|
INSERT INTO games (gameId, lightName, darkName, moves, status, timestamp, board, added, modified)
|
||||||
( gameId, lightName, darkName, moves, status, timestamp)
|
VALUES ($gameId, $lightName, $darkName, $moves, $status, $timestamp, '', $time, $time)
|
||||||
VALUES
|
ON CONFLICT (gameId) DO UPDATE
|
||||||
($gameId, $lightName, $darkName, $moves, $status, $timestamp)`;
|
SET lightName = $lightName, darkName = $darkName, moves = $moves,
|
||||||
|
status = $status, timestamp = $timestamp, modified = $time
|
||||||
|
WHERE (lightName <> $lightName OR darkName <> $darkName OR moves <> $moves OR
|
||||||
|
status <> $status OR timestamp <> $timestamp)
|
||||||
|
AND modified < $time
|
||||||
|
`;
|
||||||
await db.runAsync(insertSql, {
|
await db.runAsync(insertSql, {
|
||||||
$gameId: gameId,
|
$gameId: gameId,
|
||||||
$lightName: String(meta.lightName || ''),
|
$lightName: checkString(meta.lightName, 'meta.lightName', ''),
|
||||||
$darkName: String(meta.darkName || ''),
|
$darkName: checkString(meta.darkName, 'meta.darkName', ''),
|
||||||
$moves: Number(meta.moves) || 0,
|
$moves: checkInteger(meta.moves, 'meta.moves', 0),
|
||||||
$status: (meta.status ? String(meta.status) : null),
|
$status: checkString(meta.status, 'meta.status', ''),
|
||||||
$timestamp: Number(meta.timestamp) || 0,
|
$timestamp: checkInteger(meta.timestamp, 'meta.timestamp', 0),
|
||||||
|
$time: time,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
signalGameUpdate(gameId);
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateGame(gameId, moves) {
|
async function updateGame(gameId, moves, time) {
|
||||||
|
if (arguments.length < 3) {
|
||||||
|
time = +new Date();
|
||||||
|
}
|
||||||
|
|
||||||
const db = await dbInit;
|
const db = await dbInit;
|
||||||
|
|
||||||
const insertSql =
|
const insertSql = `
|
||||||
`INSERT OR REPLACE INTO games (gameId, board) VALUES ($gameId, $board)`;
|
INSERT INTO games (gameId, lightName, darkName, moves, status, timestamp, board, added, modified)
|
||||||
|
VALUES ($gameId, '', '', 0, '', 0, '', $time, $time)
|
||||||
|
ON CONFLICT (gameId) DO UPDATE
|
||||||
|
SET board = $board, modified = $time
|
||||||
|
WHERE (board <> $board) AND modified < $time
|
||||||
|
`;
|
||||||
await db.runAsync(insertSql, {
|
await db.runAsync(insertSql, {
|
||||||
$gameId: gameId,
|
$gameId: gameId,
|
||||||
$board: JSON.stringify(moves),
|
$board: JSON.stringify(moves),
|
||||||
|
$time: time,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
signalGameUpdate(gameId);
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,3 +303,185 @@ gun.get(PacoSakoUUID + '/meta').on(function(meta) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { change: true });
|
}, { change: true });
|
||||||
|
|
||||||
|
function internalErrorJson(err, res) {
|
||||||
|
res.status(500);
|
||||||
|
if (err && 'message' in err) {
|
||||||
|
console.error(err.message);
|
||||||
|
res.json({ message: 'internal error: ' + err.message });
|
||||||
|
} else {
|
||||||
|
console.error(err);
|
||||||
|
res.json({ message: 'internal error' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGameListHandler(req, res, next) {
|
||||||
|
res.set('Cache-Control', 'no-store');
|
||||||
|
try {
|
||||||
|
const afterTime = req.params.afterTime;
|
||||||
|
|
||||||
|
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
||||||
|
res.status(400).json({ message: 'malformed time' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollTimeout = waitFor(POLLING_TIMEOUT).then(() => 'timeout').catch(()=>{});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Save the async promise _before_ the query so we don't miss any updates while suspended. */
|
||||||
|
const gameUpdate = waitForAnyGameUpdate().then(() => 'update');
|
||||||
|
|
||||||
|
const cutoff = (+new Date()) - (2 * 7 * 24 * 3600 * 1000); /* 2 weeks ago */
|
||||||
|
const whereClause = (afterTime === undefined) ? `true` : `modified > $afterTime`;
|
||||||
|
const querySql = `
|
||||||
|
SELECT gameId, lightName, darkName, moves, status, timestamp, modified
|
||||||
|
FROM games WHERE timestamp >= $cutoff AND ${whereClause} ORDER BY timestamp DESC
|
||||||
|
LIMIT 1000
|
||||||
|
`;
|
||||||
|
const results = await (await dbInit).allAsync(querySql, {
|
||||||
|
$afterTime: checkInteger(Number(afterTime), 'afterTime', 0),
|
||||||
|
$cutoff: cutoff,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (afterTime === undefined || results.length > 0) {
|
||||||
|
let lastModified = afterTime || 0;
|
||||||
|
for (const result of results) {
|
||||||
|
if (result.modified > lastModified) {
|
||||||
|
lastModified = result.modified;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ games: results.map(pruneEmpty), modified: lastModified });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await Promise.race([gameUpdate, pollTimeout]) === 'timeout') {
|
||||||
|
res.status(204).json({ retry: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
internalErrorJson(err, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGameHandler(req, res, next) {
|
||||||
|
res.set('Cache-Control', 'no-store');
|
||||||
|
try {
|
||||||
|
const gameId = req.params.gameId;
|
||||||
|
const afterTime = req.params.afterTime;
|
||||||
|
|
||||||
|
if (!gameId.match(/^[0-9a-f]{16}$/)) {
|
||||||
|
res.status(400).json({ message: 'malformed game ID' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
||||||
|
res.status(400).json({ message: 'malformed time' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollTimeout = waitFor(POLLING_TIMEOUT).then(() => 'timeout').catch(()=>{});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Save the async promise _before_ the query so we don't miss any updates while suspended. */
|
||||||
|
const gameUpdate = waitForGameUpdate(gameId).then(() => 'update');
|
||||||
|
|
||||||
|
const querySql = `
|
||||||
|
SELECT board, modified FROM games WHERE gameId = $gameId
|
||||||
|
`;
|
||||||
|
const result = await (await dbInit).getAsync(querySql, { $gameId: gameId });
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
res.status(404).json({ message: 'unknown game ID' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterTime === undefined || result.modified > afterTime) {
|
||||||
|
const parsed = (result.board === '') ? null : JSON.parse(result.board);
|
||||||
|
res.json({ board: parsed, modified: result.modified });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await Promise.race([gameUpdate, pollTimeout]) === 'timeout') {
|
||||||
|
res.status(204).json({ retry: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
internalErrorJson(err, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMetaHandler(req, res, next) {
|
||||||
|
res.set('Cache-Control', 'no-store');
|
||||||
|
try {
|
||||||
|
const gameId = req.params.gameId;
|
||||||
|
const afterTime = req.params.afterTime;
|
||||||
|
|
||||||
|
if (!gameId.match(/^[0-9a-f]{16}$/)) {
|
||||||
|
res.status(400).json({ message: 'malformed game ID' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
||||||
|
res.status(400).json({ message: 'malformed time' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollTimeout = waitFor(POLLING_TIMEOUT).then(() => 'timeout').catch(()=>{});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Save the async promise _before_ the query so we don't miss any updates while suspended. */
|
||||||
|
const metaUpdate = waitForGameUpdate(gameId).then(() => 'update');
|
||||||
|
|
||||||
|
const querySql = `
|
||||||
|
SELECT lightName, darkName, moves, status, timestamp, modified FROM games
|
||||||
|
WHERE gameId = $gameId
|
||||||
|
`;
|
||||||
|
const result = await (await dbInit).getAsync(querySql, { $gameId: gameId });
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
res.status(404).json({ message: 'unknown game ID' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterTime === undefined || result.modified > afterTime) {
|
||||||
|
res.json(pruneEmpty(result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await Promise.race([metaUpdate, pollTimeout]) === 'timeout') {
|
||||||
|
res.status(204).json({ retry: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
internalErrorJson(err, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get('/pacosako/api/games/poll/:afterTime', getGameListHandler);
|
||||||
|
app.get('/pacosako/api/games', getGameListHandler);
|
||||||
|
|
||||||
|
app.get('/pacosako/api/game/:gameId/poll/:afterTime', getGameHandler);
|
||||||
|
app.get('/pacosako/api/game/:gameId', getGameHandler);
|
||||||
|
|
||||||
|
app.get('/pacosako/api/meta/:gameId/poll/:afterTime', getMetaHandler);
|
||||||
|
app.get('/pacosako/api/meta/:gameId', getMetaHandler);
|
||||||
|
|
||||||
|
/* TODO: Add APIs for posting updates. */
|
||||||
|
app.use('/pacosako/api/posttest', express.json());
|
||||||
|
app.post('/pacosako/api/posttest', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
res.json(req.body);
|
||||||
|
} catch (err) {
|
||||||
|
internalErrorJson(err, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use('/pacosako/api', express.static('public'));
|
||||||
|
|
||||||
|
app.use('/gun', Gun.serve(__dirname));
|
||||||
|
|
||||||
|
config.server.listen(config.port);
|
||||||
|
|
|
||||||
|
|
@ -1201,6 +1201,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
|
||||||
"integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
|
"integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
|
||||||
},
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
"array-map": {
|
"array-map": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
|
||||||
|
|
@ -1463,6 +1468,52 @@
|
||||||
"file-uri-to-path": "1.0.0"
|
"file-uri-to-path": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"raw-body": "2.4.0",
|
||||||
|
"type-is": "~1.6.17"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"bplist-creator": {
|
"bplist-creator": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz",
|
||||||
|
|
@ -1848,6 +1899,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||||
},
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
|
},
|
||||||
"convert-source-map": {
|
"convert-source-map": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
|
||||||
|
|
@ -1856,6 +1920,16 @@
|
||||||
"safe-buffer": "~5.1.1"
|
"safe-buffer": "~5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
"copy-descriptor": {
|
"copy-descriptor": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
|
||||||
|
|
@ -2247,6 +2321,50 @@
|
||||||
"base64-js": "^1.3.0"
|
"base64-js": "^1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||||
|
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.7",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.19.0",
|
||||||
|
"content-disposition": "0.5.3",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.4.0",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "~1.1.2",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.5",
|
||||||
|
"qs": "6.7.0",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"send": "0.17.1",
|
||||||
|
"serve-static": "1.14.1",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": "~1.5.0",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"qs": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
|
|
@ -2528,6 +2646,11 @@
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
"fragment-cache": {
|
"fragment-cache": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
||||||
|
|
@ -3402,6 +3525,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
||||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
|
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
|
||||||
},
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||||
|
},
|
||||||
"is-accessor-descriptor": {
|
"is-accessor-descriptor": {
|
||||||
"version": "0.1.6",
|
"version": "0.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
||||||
|
|
@ -4078,6 +4206,11 @@
|
||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
"mem": {
|
"mem": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
|
||||||
|
|
@ -4086,6 +4219,11 @@
|
||||||
"mimic-fn": "^1.0.0"
|
"mimic-fn": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
"merge-stream": {
|
"merge-stream": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
|
||||||
|
|
@ -4094,6 +4232,11 @@
|
||||||
"readable-stream": "^2.0.1"
|
"readable-stream": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
"metro": {
|
"metro": {
|
||||||
"version": "0.56.4",
|
"version": "0.56.4",
|
||||||
"resolved": "https://registry.npmjs.org/metro/-/metro-0.56.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro/-/metro-0.56.4.tgz",
|
||||||
|
|
@ -5024,6 +5167,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||||
},
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
|
},
|
||||||
"path-type": {
|
"path-type": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
|
||||||
|
|
@ -5199,6 +5347,15 @@
|
||||||
"react-is": "^16.8.1"
|
"react-is": "^16.8.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "~0.1.2",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pseudomap": {
|
"pseudomap": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
|
|
@ -5254,6 +5411,41 @@
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
},
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"rc": {
|
"rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
|
|
@ -6450,6 +6642,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
|
||||||
"integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="
|
"integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="
|
||||||
},
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
"typedarray": {
|
"typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
|
"express": "^4.17.1",
|
||||||
"gun": "^0.2020.301",
|
"gun": "^0.2020.301",
|
||||||
"gun-db": "^1.0.571",
|
"gun-db": "^1.0.571",
|
||||||
"react": "^16.9.0",
|
"react": "^16.9.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Polling Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="current"></div>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
|
||||||
|
<script>
|
||||||
|
let gameId = '5b687c23df28945e';
|
||||||
|
let currentRequest = null;
|
||||||
|
function startPoll(fromTime) {
|
||||||
|
const thisRequest = currentRequest = $.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
//url: `https://jessemcdonald.info/pacosako/api/meta/${gameId}/poll/${fromTime || 0}`,
|
||||||
|
url: `https://jessemcdonald.info/pacosako/api/games/poll/${fromTime || 0}`,
|
||||||
|
cache: false,
|
||||||
|
timeout: 0,
|
||||||
|
}).done((data, textStatus, jqXHR) => {
|
||||||
|
if (currentRequest === thisRequest) {
|
||||||
|
if (jqXHR.status == 204) {
|
||||||
|
startPoll(fromTime);
|
||||||
|
} else {
|
||||||
|
$('<p></p>').text(JSON.stringify(data)).appendTo('#current');
|
||||||
|
startPoll(data.modified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||||
|
if (currentRequest === thisRequest) {
|
||||||
|
$('#current').empty().text(JSON.stringify({ textStatus, errorThrown }));
|
||||||
|
setTimeout(() => {
|
||||||
|
if (currentRequest === thisRequest) {
|
||||||
|
startPoll(0);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function stopPoll() {
|
||||||
|
const request = currentRequest;
|
||||||
|
if (request !== null) {
|
||||||
|
currentRequest = null;
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function doPost(data) {
|
||||||
|
stopPoll();
|
||||||
|
$.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
url: `https://jessemcdonald.info/pacosako/api/posttest`,
|
||||||
|
method: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
timeout: 5000,
|
||||||
|
}).done((responseData, textStatus, jqXHR) => {
|
||||||
|
$('#current').empty().text(JSON.stringify(responseData));
|
||||||
|
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||||
|
$('#current').empty().text(JSON.stringify({ textStatus, errorThrown }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$(() => {
|
||||||
|
let counter = 0;
|
||||||
|
$('<button>Start Polling</button>').appendTo('body').on('click', () => startPoll());
|
||||||
|
$('<button>Stop Polling</button>').appendTo('body').on('click', () => stopPoll());
|
||||||
|
$('<button>Do Post</button>').appendTo('body').on('click', () => doPost({ counter: ++counter }));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<!-- vim:set noexpandtab sw=2 ts=2: -->
|
||||||
|
|
||||||
Loading…
Reference in New Issue