summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore59
-rw-r--r--api.js181
-rw-r--r--index.html30
-rw-r--r--package.json18
4 files changed, 288 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..00cbbdf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,59 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
diff --git a/api.js b/api.js
new file mode 100644
index 0000000..5bd341e
--- /dev/null
+++ b/api.js
@@ -0,0 +1,181 @@
+#!/usr/bin/env node
+/* jshint esnext: true */
+
+var request = require("request-promise");
+
+var pg = require("pg");
+var Pool = pg.Pool;
+var db_config_raw = {
+ user: "vainraw",
+ password: "vainraw",
+ host: "localhost",
+ database: "vainsocial-raw",
+ port: 5433,
+ max: 10
+};
+var db_config_web = {
+ user: "vainweb",
+ password: "vainweb",
+ host: "localhost",
+ database: "vainsocial-web",
+ port: 5432,
+ max: 10
+};
+var pool_raw = new Pool(db_config_raw);
+var pool_web = new Pool(db_config_web);
+
+var app = require("express")();
+var http = require("http").Server(app);
+var io = require("socket.io")(http);
+
+var APITOKEN = process.env.VAINSOCIAL_APITOKEN;
+if (APITOKEN == undefined) throw "Need a valid API token!";
+
+http.listen(8080);
+
+/* API helper */
+/* searches for player name across all shards */
+async function findPlayer(name) {
+ var regions = ["na", "eu", "sg"],
+ finds = [];
+
+ for (let region of regions) {
+ var options = {
+ uri: "https://api.dc01.gamelockerapp.com/shards/" + region + "/players",
+ headers: {
+ "X-Title-Id": "semc-vainglory",
+ "Authorization": APITOKEN
+ },
+ qs: {
+ "filter[playerNames]": name
+ },
+ json: true,
+ gzip: true
+ };
+ try {
+ res = await request(options);
+ finds.push({
+ "region": res.data[0].attributes.shardId,
+ "id": res.data[0].id,
+ "last_update": res.data[0].attributes.createdAt
+ });
+ } catch (err) {
+ // TODO
+ }
+ }
+
+ if (finds.length == 0)
+ return undefined;
+
+ // due to an API bug, many players are also present in NA
+ // TODO: get history for all regions in case of region transfer
+ finds.sort((a, b) => { return a.last_update < b.last_update; });
+ return finds[0];
+}
+
+/* routes */
+app.get("/api/player/:name", async (req, res) => {
+ var name = req.params.name;
+
+ var raw = await pool_raw.connect(),
+ web = await pool_web.connect();
+
+ /* search for the player in our db */
+ var player = await web.query(`
+ SELECT api_id, shard_id, last_match_created_date
+ FROM player WHERE name=$1
+ `, [name]);
+ var player_id, player_region, grab_start;
+
+ /* found */
+ if (player.rows.length > 0) {
+ console.log("player '" + name + "' was found in db");
+ player_id = player.rows[0].api_id;
+ player_region = player.rows[0].shard_id;
+ grab_start = player.rows[0].last_match_created_date;
+ // TODO db does not save time zone offset @stormcaller remove this
+ grab_start.setMinutes(grab_start.getMinutes() - (new Date().getTimezoneOffset()));
+ }
+ /* not found */
+ if (player.rows.length == 0) {
+ console.log("player '" + name + "' not found in db");
+ /* search in all regions */
+ player = await findPlayer(name);
+ if (player == undefined) {
+ console.log("player '" + name + "' not found in API");
+ /* give up */
+ res.sendStatus(404);
+ return;
+ }
+
+ player_id = player.id;
+ player_region = player.region;
+ grab_start = new Date("2017-01-01T00:00:00Z");
+ }
+
+ /* request update job */
+ // createdAt-start <= x <= createdAt-end
+ grab_start.setSeconds(grab_start.getSeconds() + 1);
+ payload = {
+ "region": player_region,
+ "params": {
+ "filter[playerIds]": player_id,
+ "filter[playerNames]": name, // TODO remove in 2.0 - backwards compat
+ "filter[createdAt-start]": grab_start.toISOString(),
+ "filter[gameMode]": "casual,ranked"
+ }
+ };
+ var jobq = await raw.query(`
+ INSERT INTO jobs(type, payload, priority)
+ VALUES('grab', $1, 0)
+ RETURNING id
+ `, [payload]);
+ console.log("player '" + name + "' updating after " + grab_start.toISOString());
+
+ /* wake apigrabber up */
+ await raw.query(`NOTIFY grab_open`, []);
+ jobid = jobq.rows[0].id;
+
+ /* clean up */
+ raw.release();
+ web.release();
+
+ res.json({
+ "job_id": jobid,
+ "player_id": player_id,
+ "player_region": player_region
+ });
+});
+
+/* internal monitoring */
+app.get("/", async (req, res) => {
+ res.sendFile(__dirname + "/index.html");
+});
+
+/* notifications from database */
+function listen() {
+ var client = new pg.Client(db_config_raw);
+ client.connect();
+ client.on('notification', (msg) => {
+ io.emit("job update", msg.channel);
+ });
+ client.query("LISTEN grab_open");
+ client.query("LISTEN process_open");
+ client.query("LISTEN compile_open");
+ client.query("LISTEN analyze_open");
+ client.query("LISTEN grab_running");
+ client.query("LISTEN process_running");
+ client.query("LISTEN compile_running");
+ client.query("LISTEN analyze_running");
+ client.query("LISTEN grab_finished");
+ client.query("LISTEN process_finished");
+ client.query("LISTEN compile_finished");
+ client.query("LISTEN analyze_finished");
+ client.query("LISTEN grab_failed");
+ client.query("LISTEN process_failed");
+ client.query("LISTEN compile_failed");
+ client.query("LISTEN analyze_failed");
+ // keep open forever
+}
+
+listen();
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..b31ad37
--- /dev/null
+++ b/index.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Vainsocial backend status</title>
+ </head>
+ <body>
+ <form id="update-form">
+ <input type="text">
+ <input type="submit" value="Update">
+ </form>
+
+ <ul id="updates"></ul>
+ <script src="/socket.io/socket.io.js"></script>
+ <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
+ <script>
+ $("#update-form").submit(function(e) {
+ $.get("/api/player/" + $("input:first").val()).done(function() {
+ $("#updates").append($("<li>").text("(found)"));
+ }).fail(function() {
+ $("#updates").append($("<li>").text("(not found)"));
+ });
+ e.preventDefault();
+ });
+ var socket = io();
+ socket.on("job update", function(msg) {
+ $("#updates").append($("<li>").text(msg));
+ });
+ </script>
+ </body>
+</html>
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..07b7c83
--- /dev/null
+++ b/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "vainsocial-updater",
+ "version": "2.0.0",
+ "description": "Vainsocial frontend to backend communication service",
+ "main": "api.js",
+ "dependencies": {
+ "express": "^4.15.2",
+ "pg": "^6.1.4",
+ "request-promise": "^4.2.0",
+ "socketio": "^1.0.0"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": ""
+}