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
|
node_modules
|
||||||
journal.txt
|
|
||||||
bak/
|
bak/
|
||||||
radata/
|
|
||||||
journal/
|
|
||||||
stats.radata
|
|
||||||
pacosako.db
|
pacosako.db
|
||||||
.*.swp
|
.*.swp
|
||||||
.*.swo
|
.*.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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
225
index.js
225
index.js
|
|
@ -1,35 +1,11 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
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 sqlite3 = require('./sqlite3-promises');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
|
|
||||||
require('./gun-with-cancel')(Gun);
|
|
||||||
|
|
||||||
const POLLING_TIMEOUT = 60000/*ms*/;
|
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() {
|
var appendJournal = (function() {
|
||||||
let lastJournalText = null;
|
let lastJournalText = null;
|
||||||
return function appendJournal(text) {
|
return function appendJournal(text) {
|
||||||
|
|
@ -154,119 +130,10 @@ function checkInteger(value, label, dflt) {
|
||||||
throw { message: `${label || 'value'} should be an integer` };
|
throw { message: `${label || 'value'} should be an integer` };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateMeta(gameId, meta, time) {
|
function catchExceptionsJson(wrapped) {
|
||||||
if (arguments.length < 3) {
|
const context = this;
|
||||||
time = +new Date();
|
return function(req, res, next) {
|
||||||
}
|
function internalErrorJson(err) {
|
||||||
|
|
||||||
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(); };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, { change: true });
|
|
||||||
|
|
||||||
function internalErrorJson(err, res) {
|
|
||||||
res.status(500);
|
res.status(500);
|
||||||
if (err && 'message' in err) {
|
if (err && 'message' in err) {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
|
|
@ -277,9 +144,20 @@ function internalErrorJson(err, res) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
async function getGameListHandler(req, res, next) {
|
||||||
res.set('Cache-Control', 'no-store');
|
res.set('Cache-Control', 'no-store');
|
||||||
try {
|
|
||||||
const afterTime = req.params.afterTime;
|
const afterTime = req.params.afterTime;
|
||||||
|
|
||||||
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
if (afterTime !== undefined && !afterTime.match(/^\d+$/)) {
|
||||||
|
|
@ -322,14 +200,11 @@ async function getGameListHandler(req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
internalErrorJson(err, res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGameHandler(req, res, next) {
|
async function getGameHandler(req, res, next) {
|
||||||
res.set('Cache-Control', 'no-store');
|
res.set('Cache-Control', 'no-store');
|
||||||
try {
|
|
||||||
const gameId = req.params.gameId;
|
const gameId = req.params.gameId;
|
||||||
const afterTime = req.params.afterTime;
|
const afterTime = req.params.afterTime;
|
||||||
|
|
||||||
|
|
@ -375,9 +250,6 @@ async function getGameHandler(req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(err) {
|
|
||||||
internalErrorJson(err, res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateTemplate = {
|
const updateTemplate = {
|
||||||
|
|
@ -413,7 +285,7 @@ function validateUpdate(body) {
|
||||||
|
|
||||||
async function postGameHandler(req, res, next) {
|
async function postGameHandler(req, res, next) {
|
||||||
res.set('Cache-Control', 'no-store');
|
res.set('Cache-Control', 'no-store');
|
||||||
try {
|
|
||||||
const time = +new Date();
|
const time = +new Date();
|
||||||
const gameId = req.params.gameId;
|
const gameId = req.params.gameId;
|
||||||
const body = validateUpdate(req.body);
|
const body = validateUpdate(req.body);
|
||||||
|
|
@ -485,18 +357,18 @@ async function postGameHandler(req, res, next) {
|
||||||
WHERE (${whereClause}) AND modified = $modified
|
WHERE (${whereClause}) AND modified = $modified
|
||||||
`;
|
`;
|
||||||
const selectSql = `SELECT * FROM games WHERE gameId = $gameId`
|
const selectSql = `SELECT * FROM games WHERE gameId = $gameId`
|
||||||
let beginP, insertP, selectP, commitP;
|
let transactionP;
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
/* Important: We need to start all these queries without waiting in between. */
|
/* Important: We need to start all these queries without waiting in between. */
|
||||||
/* Otherwise db.serialize() will not have the desired effect. */
|
/* Otherwise db.serialize() will not have the desired effect. */
|
||||||
beginP = db.execAsync(`BEGIN TRANSACTION`);
|
const beginP = db.execAsync(`BEGIN TRANSACTION`);
|
||||||
insertP = db.runAsync(insertSql, params);
|
const insertP = db.runAsync(insertSql, params);
|
||||||
selectP = db.getAsync(selectSql, { $gameId: gameId });
|
const selectP = db.getAsync(selectSql, { $gameId: gameId });
|
||||||
commitP = db.execAsync(`COMMIT TRANSACTION`);
|
const commitP = db.execAsync(`COMMIT TRANSACTION`);
|
||||||
|
transactionP = Promise.all([beginP, insertP, commitP]).then(() => selectP);
|
||||||
});
|
});
|
||||||
/* Now wait for all the queries to finish. */
|
/* Now wait for all the queries to finish. */
|
||||||
await Promise.all([beginP, insertP, commitP]);
|
const result = await transactionP;
|
||||||
const result = await selectP;
|
|
||||||
|
|
||||||
if (!result || result.modified !== params.$time) {
|
if (!result || result.modified !== params.$time) {
|
||||||
res.status(409).json({ message: 'update failed', modified: (result || {}).modified });
|
res.status(409).json({ message: 'update failed', modified: (result || {}).modified });
|
||||||
|
|
@ -506,44 +378,25 @@ async function postGameHandler(req, res, next) {
|
||||||
signalGameUpdate(gameId);
|
signalGameUpdate(gameId);
|
||||||
|
|
||||||
res.json({ success: true, modified: result.modified });
|
res.json({ success: true, modified: result.modified });
|
||||||
|
|
||||||
(async () => {
|
|
||||||
if (hasBoard) {
|
|
||||||
gun.get(PacoSakoUUID + '/game/' + gameId).get('board').put(result.board);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMeta) {
|
const app = express();
|
||||||
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);
|
app.get('/pacosako/api/games/poll/:afterTime', catchExceptionsJson(getGameListHandler));
|
||||||
|
app.get('/pacosako/api/games', catchExceptionsJson(getGameListHandler));
|
||||||
|
|
||||||
if (meta.lightName !== '' || meta.darkName !== '' || meta.moves !== 0) {
|
app.get('/pacosako/api/:type(game|meta)/:gameId/poll/:afterTime', catchExceptionsJson(getGameHandler));
|
||||||
gun.get(PacoSakoUUID + '/meta').get(gameId).put(metaRef);
|
app.get('/pacosako/api/:type(game|meta)/:gameId', catchExceptionsJson(getGameHandler));
|
||||||
|
app.post('/pacosako/api/:type(game|meta)/:gameId', express.json(), catchExceptionsJson(postGameHandler));
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
gun.get(PacoSakoUUID + '/meta').get(gameId).put(null);
|
config.server = require('http').createServer(app);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})().catch(console.error);
|
|
||||||
} catch (err) {
|
|
||||||
internalErrorJson(err, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get('/pacosako/api/games/poll/:afterTime', getGameListHandler);
|
|
||||||
app.get('/pacosako/api/games', getGameListHandler);
|
|
||||||
|
|
||||||
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.use('/pacosako/api', express.static('public'));
|
|
||||||
|
|
||||||
app.use('/gun', Gun.serve(__dirname));
|
|
||||||
|
|
||||||
config.server.listen(config.port);
|
config.server.listen(config.port);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,12 +4,7 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.4",
|
|
||||||
"express": "^4.17.1",
|
"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"
|
"sqlite3": "^4.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"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