remove GUN from the server since there are no more clients
This commit is contained in:
parent
674b460cbe
commit
931eb6a134
|
|
@ -1,9 +1,5 @@
|
|||
node_modules
|
||||
journal.txt
|
||||
bak/
|
||||
radata/
|
||||
journal/
|
||||
stats.radata
|
||||
pacosako.db
|
||||
.*.swp
|
||||
.*.swo
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
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;
|
||||
}
|
||||
};
|
||||
})();
|
||||
};
|
||||
543
index.js
543
index.js
|
|
@ -1,35 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var Gun = require('gun');
|
||||
var SEA = require('gun/sea');
|
||||
var NTS = require('gun/nts');
|
||||
var rtc = require('gun/lib/webrtc');
|
||||
var sqlite3 = require('./sqlite3-promises');
|
||||
var express = require('express');
|
||||
|
||||
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.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||
config.server = require('https').createServer(config, app);
|
||||
} else {
|
||||
config.server = require('http').createServer(app);
|
||||
}
|
||||
|
||||
var gun = Gun({
|
||||
web: config.server,
|
||||
peers: ['https://jessemcdonald.info/gun'],
|
||||
});
|
||||
console.log('Relay peer started on port ' + config.port + ' with /gun');
|
||||
|
||||
var appendJournal = (function() {
|
||||
let lastJournalText = null;
|
||||
return function appendJournal(text) {
|
||||
|
|
@ -154,229 +130,125 @@ function checkInteger(value, label, dflt) {
|
|||
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 insertSql = `
|
||||
INSERT INTO games (gameId, lightName, darkName, moves, status, timestamp, board, added, modified)
|
||||
VALUES ($gameId, $lightName, $darkName, $moves, $status, $timestamp, '', $time, $time)
|
||||
ON CONFLICT (gameId) DO UPDATE
|
||||
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, {
|
||||
$gameId: gameId,
|
||||
$lightName: checkString(meta.lightName, 'meta.lightName', ''),
|
||||
$darkName: checkString(meta.darkName, 'meta.darkName', ''),
|
||||
$moves: checkInteger(meta.moves, 'meta.moves', 0),
|
||||
$status: checkString(meta.status, 'meta.status', ''),
|
||||
$timestamp: checkInteger(meta.timestamp, 'meta.timestamp', 0),
|
||||
$time: time,
|
||||
});
|
||||
|
||||
signalGameUpdate(gameId);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
async function updateGame(gameId, moves, time) {
|
||||
if (arguments.length < 3) {
|
||||
time = +new Date();
|
||||
}
|
||||
|
||||
const db = await dbInit;
|
||||
|
||||
const insertSql = `
|
||||
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, {
|
||||
$gameId: gameId,
|
||||
$board: JSON.stringify(moves),
|
||||
$time: time,
|
||||
});
|
||||
|
||||
signalGameUpdate(gameId);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
const PacoSakoUUID = 'b425b812-6bdb-11ea-9414-6f946662bac3';
|
||||
|
||||
let cancellers = {};
|
||||
|
||||
gun.get(PacoSakoUUID + '/meta').on(function(meta) {
|
||||
for (const gameId in meta) { /* use of 'in' here is deliberate */
|
||||
/* 'gameId' may include extra GUN fields like '_' */
|
||||
if (gameId.match(/^[0-9a-f]{16}$/)) {
|
||||
if (!Gun.obj.is(meta[gameId])) {
|
||||
appendJournal(JSON.stringify({ meta: { [gameId]: null } }));
|
||||
if (gameId in cancellers) {
|
||||
cancellers[gameId]();
|
||||
delete cancellers[gameId];
|
||||
}
|
||||
} else if (!(gameId in cancellers)) {
|
||||
let cancelMeta = gun.get(meta[gameId]).onWithCancel(function(data) {
|
||||
updateMeta(gameId, data).catch(logDbError('updateMeta'));
|
||||
let text;
|
||||
try {
|
||||
let clean = null;
|
||||
if (data !== null) {
|
||||
clean = {};
|
||||
for (const k of Object.keys(data).sort()) {
|
||||
if (k !== '_') {
|
||||
clean[k] = data[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
text = JSON.stringify({ meta: { [gameId]: clean } });
|
||||
} catch(err) {}
|
||||
if (text) {
|
||||
appendJournal(text);
|
||||
}
|
||||
});
|
||||
|
||||
let cancelGame = gun.get(PacoSakoUUID + '/game/' + gameId).onWithCancel(function(data) {
|
||||
if (data && typeof data.board === 'string') {
|
||||
let text;
|
||||
try {
|
||||
const parsed = JSON.parse(data.board);
|
||||
updateGame(gameId, parsed).catch(logDbError('updateGame'));
|
||||
text = JSON.stringify({ game: { [gameId]: { board: parsed } } });
|
||||
} catch(err) {}
|
||||
if (text) {
|
||||
appendJournal(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cancellers[gameId] = function() { cancelMeta(); cancelGame(); };
|
||||
function catchExceptionsJson(wrapped) {
|
||||
const context = this;
|
||||
return function(req, res, next) {
|
||||
function internalErrorJson(err) {
|
||||
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' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { 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' });
|
||||
try {
|
||||
const result = wrapped.call(context, req, res, next);
|
||||
if (result instanceof Promise) {
|
||||
return result.catch(internalErrorJson);
|
||||
}
|
||||
} catch (err) {
|
||||
internalErrorJson(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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' });
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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' });
|
||||
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 lightName, darkName, moves, status, timestamp, board, modified FROM games
|
||||
WHERE gameId = $gameId
|
||||
`;
|
||||
const result = await (await dbInit).getAsync(querySql, { $gameId: gameId });
|
||||
|
||||
if (result && (afterTime === undefined || result.modified > afterTime)) {
|
||||
if (req.params.type === 'meta') {
|
||||
delete result.board;
|
||||
} else {
|
||||
result.board = (result.board === '') ? null : JSON.parse(result.board);
|
||||
}
|
||||
res.json(pruneEmpty(result));
|
||||
return;
|
||||
}
|
||||
|
||||
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
||||
res.status(400).json({ message: 'malformed time' });
|
||||
if (afterTime === undefined) {
|
||||
res.status(404).json({ message: 'unknown game ID' });
|
||||
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 lightName, darkName, moves, status, timestamp, board, modified FROM games
|
||||
WHERE gameId = $gameId
|
||||
`;
|
||||
const result = await (await dbInit).getAsync(querySql, { $gameId: gameId });
|
||||
|
||||
if (result && (afterTime === undefined || result.modified > afterTime)) {
|
||||
if (req.params.type === 'meta') {
|
||||
delete result.board;
|
||||
} else {
|
||||
result.board = (result.board === '') ? null : JSON.parse(result.board);
|
||||
}
|
||||
res.json(pruneEmpty(result));
|
||||
return;
|
||||
}
|
||||
|
||||
if (afterTime === undefined) {
|
||||
res.status(404).json({ message: 'unknown game ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (await Promise.race([gameUpdate, pollTimeout]) === 'timeout') {
|
||||
res.status(204).json({ retry: true });
|
||||
return;
|
||||
}
|
||||
if (await Promise.race([gameUpdate, pollTimeout]) === 'timeout') {
|
||||
res.status(204).json({ retry: true });
|
||||
return;
|
||||
}
|
||||
} catch(err) {
|
||||
internalErrorJson(err, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -413,137 +285,118 @@ function validateUpdate(body) {
|
|||
|
||||
async function postGameHandler(req, res, next) {
|
||||
res.set('Cache-Control', 'no-store');
|
||||
try {
|
||||
const time = +new Date();
|
||||
const gameId = req.params.gameId;
|
||||
const body = validateUpdate(req.body);
|
||||
|
||||
if (!gameId.match(/^[0-9a-f]{16}$/)) {
|
||||
res.status(400).json({ message: 'malformed game ID' });
|
||||
return;
|
||||
}
|
||||
const time = +new Date();
|
||||
const gameId = req.params.gameId;
|
||||
const body = validateUpdate(req.body);
|
||||
|
||||
if (!body) {
|
||||
res.status(400).json({ message: 'invalid request' });
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
$gameId: gameId,
|
||||
$lightName: '',
|
||||
$darkName: '',
|
||||
$moves: 0,
|
||||
$status: '',
|
||||
$timestamp: 0,
|
||||
$board: '',
|
||||
$modified: body.modified,
|
||||
$time: time,
|
||||
};
|
||||
|
||||
if (params.$modified >= params.$time) {
|
||||
res.status(400).json({ message: 'invalid modification time' });
|
||||
return;
|
||||
}
|
||||
|
||||
let setClause = '';
|
||||
let whereClause = '';
|
||||
let hasBoard = false;
|
||||
let hasMeta = false;
|
||||
|
||||
for (const key in body) {
|
||||
if (key !== 'modified') {
|
||||
if (key in updateTemplate === false) {
|
||||
throw { message: 'internal error' };
|
||||
}
|
||||
if (key === 'board') {
|
||||
params['$' + key] = JSON.stringify(body[key]);
|
||||
hasBoard = true;
|
||||
} else {
|
||||
params['$' + key] = body[key];
|
||||
hasMeta = true;
|
||||
}
|
||||
if (setClause !== '') {
|
||||
setClause += ', ';
|
||||
whereClause += ' OR ';
|
||||
}
|
||||
setClause += key + ' = $' + key;
|
||||
whereClause += key + ' <> $' + key;
|
||||
}
|
||||
}
|
||||
|
||||
if (setClause === '') {
|
||||
res.status(400).json({ message: 'empty update' });
|
||||
return;
|
||||
}
|
||||
|
||||
const db = await dbInit;
|
||||
|
||||
const insertSql = `
|
||||
INSERT INTO games (gameId, lightName, darkName, moves, status, timestamp, board, added, modified)
|
||||
VALUES ($gameId, $lightName, $darkName, $moves, $status, $timestamp, $board, $time, $time)
|
||||
ON CONFLICT (gameId) DO UPDATE SET ${setClause}, modified = $time
|
||||
WHERE (${whereClause}) AND modified = $modified
|
||||
`;
|
||||
const selectSql = `SELECT * FROM games WHERE gameId = $gameId`
|
||||
let beginP, insertP, selectP, commitP;
|
||||
db.serialize(() => {
|
||||
/* Important: We need to start all these queries without waiting in between. */
|
||||
/* Otherwise db.serialize() will not have the desired effect. */
|
||||
beginP = db.execAsync(`BEGIN TRANSACTION`);
|
||||
insertP = db.runAsync(insertSql, params);
|
||||
selectP = db.getAsync(selectSql, { $gameId: gameId });
|
||||
commitP = db.execAsync(`COMMIT TRANSACTION`);
|
||||
});
|
||||
/* Now wait for all the queries to finish. */
|
||||
await Promise.all([beginP, insertP, commitP]);
|
||||
const result = await selectP;
|
||||
|
||||
if (!result || result.modified !== params.$time) {
|
||||
res.status(409).json({ message: 'update failed', modified: (result || {}).modified });
|
||||
return;
|
||||
}
|
||||
|
||||
signalGameUpdate(gameId);
|
||||
|
||||
res.json({ success: true, modified: result.modified });
|
||||
|
||||
(async () => {
|
||||
if (hasBoard) {
|
||||
gun.get(PacoSakoUUID + '/game/' + gameId).get('board').put(result.board);
|
||||
}
|
||||
|
||||
if (hasMeta) {
|
||||
const meta = {
|
||||
lightName: result.lightName,
|
||||
darkName: result.darkName,
|
||||
moves: result.moves,
|
||||
timestamp: result.timestamp,
|
||||
status: (result.status === '') ? null : result.status,
|
||||
};
|
||||
|
||||
const metaRef = gun.get(PacoSakoUUID + '/meta/' + gameId).put(meta);
|
||||
|
||||
if (meta.lightName !== '' || meta.darkName !== '' || meta.moves !== 0) {
|
||||
gun.get(PacoSakoUUID + '/meta').get(gameId).put(metaRef);
|
||||
} else {
|
||||
gun.get(PacoSakoUUID + '/meta').get(gameId).put(null);
|
||||
}
|
||||
}
|
||||
})().catch(console.error);
|
||||
} catch (err) {
|
||||
internalErrorJson(err, res);
|
||||
if (!gameId.match(/^[0-9a-f]{16}$/)) {
|
||||
res.status(400).json({ message: 'malformed game ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!body) {
|
||||
res.status(400).json({ message: 'invalid request' });
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
$gameId: gameId,
|
||||
$lightName: '',
|
||||
$darkName: '',
|
||||
$moves: 0,
|
||||
$status: '',
|
||||
$timestamp: 0,
|
||||
$board: '',
|
||||
$modified: body.modified,
|
||||
$time: time,
|
||||
};
|
||||
|
||||
if (params.$modified >= params.$time) {
|
||||
res.status(400).json({ message: 'invalid modification time' });
|
||||
return;
|
||||
}
|
||||
|
||||
let setClause = '';
|
||||
let whereClause = '';
|
||||
let hasBoard = false;
|
||||
let hasMeta = false;
|
||||
|
||||
for (const key in body) {
|
||||
if (key !== 'modified') {
|
||||
if (key in updateTemplate === false) {
|
||||
throw { message: 'internal error' };
|
||||
}
|
||||
if (key === 'board') {
|
||||
params['$' + key] = JSON.stringify(body[key]);
|
||||
hasBoard = true;
|
||||
} else {
|
||||
params['$' + key] = body[key];
|
||||
hasMeta = true;
|
||||
}
|
||||
if (setClause !== '') {
|
||||
setClause += ', ';
|
||||
whereClause += ' OR ';
|
||||
}
|
||||
setClause += key + ' = $' + key;
|
||||
whereClause += key + ' <> $' + key;
|
||||
}
|
||||
}
|
||||
|
||||
if (setClause === '') {
|
||||
res.status(400).json({ message: 'empty update' });
|
||||
return;
|
||||
}
|
||||
|
||||
const db = await dbInit;
|
||||
|
||||
const insertSql = `
|
||||
INSERT INTO games (gameId, lightName, darkName, moves, status, timestamp, board, added, modified)
|
||||
VALUES ($gameId, $lightName, $darkName, $moves, $status, $timestamp, $board, $time, $time)
|
||||
ON CONFLICT (gameId) DO UPDATE SET ${setClause}, modified = $time
|
||||
WHERE (${whereClause}) AND modified = $modified
|
||||
`;
|
||||
const selectSql = `SELECT * FROM games WHERE gameId = $gameId`
|
||||
let transactionP;
|
||||
db.serialize(() => {
|
||||
/* Important: We need to start all these queries without waiting in between. */
|
||||
/* Otherwise db.serialize() will not have the desired effect. */
|
||||
const beginP = db.execAsync(`BEGIN TRANSACTION`);
|
||||
const insertP = db.runAsync(insertSql, params);
|
||||
const selectP = db.getAsync(selectSql, { $gameId: gameId });
|
||||
const commitP = db.execAsync(`COMMIT TRANSACTION`);
|
||||
transactionP = Promise.all([beginP, insertP, commitP]).then(() => selectP);
|
||||
});
|
||||
/* Now wait for all the queries to finish. */
|
||||
const result = await transactionP;
|
||||
|
||||
if (!result || result.modified !== params.$time) {
|
||||
res.status(409).json({ message: 'update failed', modified: (result || {}).modified });
|
||||
return;
|
||||
}
|
||||
|
||||
signalGameUpdate(gameId);
|
||||
|
||||
res.json({ success: true, modified: result.modified });
|
||||
}
|
||||
|
||||
app.get('/pacosako/api/games/poll/:afterTime', getGameListHandler);
|
||||
app.get('/pacosako/api/games', getGameListHandler);
|
||||
const app = express();
|
||||
|
||||
app.get('/pacosako/api/:type(game|meta)/:gameId/poll/:afterTime', getGameHandler);
|
||||
app.get('/pacosako/api/:type(game|meta)/:gameId', getGameHandler);
|
||||
app.post('/pacosako/api/:type(game|meta)/:gameId', express.json(), postGameHandler);
|
||||
app.get('/pacosako/api/games/poll/:afterTime', catchExceptionsJson(getGameListHandler));
|
||||
app.get('/pacosako/api/games', catchExceptionsJson(getGameListHandler));
|
||||
|
||||
app.use('/pacosako/api', express.static('public'));
|
||||
app.get('/pacosako/api/:type(game|meta)/:gameId/poll/:afterTime', catchExceptionsJson(getGameHandler));
|
||||
app.get('/pacosako/api/:type(game|meta)/:gameId', catchExceptionsJson(getGameHandler));
|
||||
app.post('/pacosako/api/:type(game|meta)/:gameId', express.json(), catchExceptionsJson(postGameHandler));
|
||||
|
||||
app.use('/gun', Gun.serve(__dirname));
|
||||
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.cert = fs.readFileSync(process.env.HTTPS_CERT);
|
||||
config.server = require('https').createServer(config, app);
|
||||
} else {
|
||||
config.server = require('http').createServer(app);
|
||||
}
|
||||
|
||||
config.server.listen(config.port);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,12 +4,7 @@
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.4",
|
||||
"express": "^4.17.1",
|
||||
"gun": "^0.2020.301",
|
||||
"gun-db": "^1.0.571",
|
||||
"react": "^16.9.0",
|
||||
"react-native": "^0.61.5",
|
||||
"sqlite3": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Polling Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
|
||||
<script>
|
||||
let gameId = '5b687c23df28945e';
|
||||
let currentRequest = null;
|
||||
function logResult(result) {
|
||||
$('<p></p>').text(JSON.stringify(result)).prependTo('#current');
|
||||
}
|
||||
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 {
|
||||
logResult(data);
|
||||
startPoll(data.modified);
|
||||
}
|
||||
}
|
||||
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||
if (currentRequest === thisRequest) {
|
||||
logResult({ textStatus, errorThrown });
|
||||
setTimeout(() => {
|
||||
if (currentRequest === thisRequest) {
|
||||
startPoll(0);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
}
|
||||
function stopPoll() {
|
||||
const request = currentRequest;
|
||||
if (request !== null) {
|
||||
currentRequest = null;
|
||||
request.abort();
|
||||
}
|
||||
}
|
||||
function doUpdate(gameId, data) {
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
url: `https://jessemcdonald.info/pacosako/api/game/${gameId}`,
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
data: JSON.stringify(data),
|
||||
timeout: 5000,
|
||||
}).done((responseData, textStatus, jqXHR) => {
|
||||
logResult({ [jqXHR.status]: responseData });
|
||||
if ('modified' in responseData) {
|
||||
data.modified = responseData.modified;
|
||||
$('#json').val(JSON.stringify(data));
|
||||
}
|
||||
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||
logResult({ status: jqXHR.status, textStatus, errorThrown });
|
||||
});
|
||||
}
|
||||
$(() => {
|
||||
let counter = 0;
|
||||
let row1 = $('<div></div>').appendTo('body');
|
||||
$('<button id="start">Start Polling</button>').appendTo('body').on('click', () => {
|
||||
$('#start').prop('disabled', true);
|
||||
$('#stop').prop('disabled', false);
|
||||
startPoll();
|
||||
});
|
||||
$('<button id="stop">Stop Polling</button>').prop('disabled', true).appendTo('body').on('click', () => {
|
||||
$('#start').prop('disabled', false);
|
||||
$('#stop').prop('disabled', true);
|
||||
stopPoll()
|
||||
});
|
||||
let row2 = $('<div></div>').appendTo('body');
|
||||
$('<input id="gameId">').appendTo(row2);
|
||||
$('<input id="json">').appendTo(row2);
|
||||
$('<button id="update">Update</button>').appendTo(row2).on('click', () => {
|
||||
try {
|
||||
doUpdate($('#gameId').val(), JSON.parse($('#json').val()));
|
||||
} catch(err) {
|
||||
logResult({ exception: err });
|
||||
}
|
||||
});
|
||||
$('<div id="current"></div>').appendTo('body');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!-- vim:set noexpandtab sw=2 ts=2: -->
|
||||
|
||||
Loading…
Reference in New Issue