From 8e20b0b6827722c625917a1ea0122808f4c705d1 Mon Sep 17 00:00:00 2001 From: GongYuhua Date: Thu, 27 Sep 2018 19:48:29 +0800 Subject: [PATCH] add HQ server --- HQ-Server-Nodejs/.gitignore | 7 + HQ-Server-Nodejs/.vscode/launch.json | 20 + HQ-Server-Nodejs/deploy_cn.sh | 14 + HQ-Server-Nodejs/deploy_en.sh | 14 + HQ-Server-Nodejs/ecosystem.config.js | 18 + HQ-Server-Nodejs/modules/ApiRestful.js | 325 ++++ HQ-Server-Nodejs/modules/FakeEncrypt.js | 13 + HQ-Server-Nodejs/modules/HQ.js | 433 ++++++ HQ-Server-Nodejs/modules/QuizFactory.js | 75 + HQ-Server-Nodejs/modules/config.js | 6 + HQ-Server-Nodejs/modules/logger.js | 52 + HQ-Server-Nodejs/modules/token.js | 29 + HQ-Server-Nodejs/package-lock.json | 1431 ++++++++++++++++++ HQ-Server-Nodejs/package.json | 30 + HQ-Server-Nodejs/readme.md | 11 + HQ-Server-Nodejs/resources/quiz/quiz-1.json | 141 ++ HQ-Server-Nodejs/resources/quiz/quiz-2.json | 141 ++ HQ-Server-Nodejs/resources/quiz/quiz-ch.json | 56 + HQ-Server-Nodejs/resources/quiz/quiz-en.json | 141 ++ HQ-Server-Nodejs/robot/ecosystem.config.js | 21 + HQ-Server-Nodejs/robot/test.js | 124 ++ HQ-Server-Nodejs/samples/answer.json | 1 + HQ-Server-Nodejs/samples/quiz.json | 56 + HQ-Server-Nodejs/samples/quiz_answer.sh | 1 + HQ-Server-Nodejs/samples/quiz_update.sh | 1 + HQ-Server-Nodejs/samples/quiz_update_us.sh | 1 + HQ-Server-Nodejs/server.js | 58 + 27 files changed, 3220 insertions(+) create mode 100644 HQ-Server-Nodejs/.gitignore create mode 100644 HQ-Server-Nodejs/.vscode/launch.json create mode 100755 HQ-Server-Nodejs/deploy_cn.sh create mode 100755 HQ-Server-Nodejs/deploy_en.sh create mode 100644 HQ-Server-Nodejs/ecosystem.config.js create mode 100644 HQ-Server-Nodejs/modules/ApiRestful.js create mode 100644 HQ-Server-Nodejs/modules/FakeEncrypt.js create mode 100644 HQ-Server-Nodejs/modules/HQ.js create mode 100644 HQ-Server-Nodejs/modules/QuizFactory.js create mode 100644 HQ-Server-Nodejs/modules/config.js create mode 100644 HQ-Server-Nodejs/modules/logger.js create mode 100644 HQ-Server-Nodejs/modules/token.js create mode 100644 HQ-Server-Nodejs/package-lock.json create mode 100644 HQ-Server-Nodejs/package.json create mode 100644 HQ-Server-Nodejs/readme.md create mode 100644 HQ-Server-Nodejs/resources/quiz/quiz-1.json create mode 100644 HQ-Server-Nodejs/resources/quiz/quiz-2.json create mode 100644 HQ-Server-Nodejs/resources/quiz/quiz-ch.json create mode 100644 HQ-Server-Nodejs/resources/quiz/quiz-en.json create mode 100644 HQ-Server-Nodejs/robot/ecosystem.config.js create mode 100644 HQ-Server-Nodejs/robot/test.js create mode 100644 HQ-Server-Nodejs/samples/answer.json create mode 100644 HQ-Server-Nodejs/samples/quiz.json create mode 100644 HQ-Server-Nodejs/samples/quiz_answer.sh create mode 100755 HQ-Server-Nodejs/samples/quiz_update.sh create mode 100755 HQ-Server-Nodejs/samples/quiz_update_us.sh create mode 100644 HQ-Server-Nodejs/server.js diff --git a/HQ-Server-Nodejs/.gitignore b/HQ-Server-Nodejs/.gitignore new file mode 100644 index 0000000..80a8473 --- /dev/null +++ b/HQ-Server-Nodejs/.gitignore @@ -0,0 +1,7 @@ +node_modules +.DS_Store +log +config_cn.js +config_en.js +Encrypt.js +test/ diff --git a/HQ-Server-Nodejs/.vscode/launch.json b/HQ-Server-Nodejs/.vscode/launch.json new file mode 100644 index 0000000..92dbe32 --- /dev/null +++ b/HQ-Server-Nodejs/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/server.js" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Test", + "program": "${workspaceFolder}/test/test.js" + } + ] +} \ No newline at end of file diff --git a/HQ-Server-Nodejs/deploy_cn.sh b/HQ-Server-Nodejs/deploy_cn.sh new file mode 100755 index 0000000..01e1009 --- /dev/null +++ b/HQ-Server-Nodejs/deploy_cn.sh @@ -0,0 +1,14 @@ +#!/bin/sh +#copy client static files + +deployDir=/home/devops/web_demo/project/HQ-PHASE-3 +ServerName=${@:$OPTIND:1} + +# Rev="$(git rev-parse HEAD)" + +# echo $Rev +# cd dist +cp modules/config_cn.js modules/config.js +rsync -v -z -r --progress -h --exclude=node_modules/* --exclude=.* --exclude=deploy_test.sh . $ServerName:$deployDir + +echo service deployed on:$ServerName:$deployDir diff --git a/HQ-Server-Nodejs/deploy_en.sh b/HQ-Server-Nodejs/deploy_en.sh new file mode 100755 index 0000000..570dff0 --- /dev/null +++ b/HQ-Server-Nodejs/deploy_en.sh @@ -0,0 +1,14 @@ +#!/bin/sh +#copy client static files + +deployDir=/home/test-pc/HQ-PHASE-3 +ServerName=${@:$OPTIND:1} + +# Rev="$(git rev-parse HEAD)" + +# echo $Rev +# cd dist +cp modules/config_en.js modules/config.js +rsync -v -z -r --progress -h --exclude=node_modules/* --exclude=.* --exclude=deploy_test.sh . $ServerName:$deployDir + +echo service deployed on:$ServerName:$deployDir diff --git a/HQ-Server-Nodejs/ecosystem.config.js b/HQ-Server-Nodejs/ecosystem.config.js new file mode 100644 index 0000000..9e17c11 --- /dev/null +++ b/HQ-Server-Nodejs/ecosystem.config.js @@ -0,0 +1,18 @@ +module.exports = { + /** + * Application configuration section + * http://pm2.keymetrics.io/docs/usage/application-declaration/ + */ + apps : [ + + // First application + { + name : 'HQ-3', + script : 'server.js', + env: { + COMMON_VARIABLE: 'true', + NODE_ENV: 'production' + } + } + ] +}; diff --git a/HQ-Server-Nodejs/modules/ApiRestful.js b/HQ-Server-Nodejs/modules/ApiRestful.js new file mode 100644 index 0000000..051335a --- /dev/null +++ b/HQ-Server-Nodejs/modules/ApiRestful.js @@ -0,0 +1,325 @@ +const logger = require("./logger").get("hq"); +const QuizFactory = require("./QuizFactory"); +const HQ = require("./HQ"); +let cipher = null; +try { + cipher = require("./Encrypt"); +} catch (e) { + cipher = require("./FakeEncrypt"); +} + +const ApiRestful = (maker, app) => { + app.use(function (req, res, next) { + logger.info(`incoming restful request: ${req.method}, ${req.url}, ${req.method === "GET" ? JSON.stringify(req.query) : JSON.stringify(req.body)}`); + next(); + }); + + /*------------------------------------------------ + | function : Communicate with Windows + \*----------------------------------------------*/ + app.get("/v1/inviteStatus", (req, res) => { + let query = req.query; + let gid = query.gid || null; + + if (!gid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + res.json({data: game.inviting}); + } + } + }); + + app.post("/v1/reset", (req, res) => { + let query = req.body; + let gid = query.gid || null; + let hqmsgid = query.hqmsgid || "" + + if (!gid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + game.reset(); + res.json({}); + } + } + }); + + app.post("/v1/publish", (req, res) => { + let query = req.body; + let gid = query.gid || null; + + if (!gid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + return; + } + game.publish().then(result => { + res.json({data: result}); + }).catch(e => { + res.json({err: e}); + }); + } + }); + + app.post("/v1/stopAnswer", (req, res) => { + let query = req.body; + let gid = query.gid || null; + + if (!gid) { + res.json({ err: "missing_room_id" }); + return; + } + + let game = maker.get(gid); + + if (!game) { + res.json({err: "room_not_found"}); + return; + } + if (game.open) { + game.closeQuiz().then(data => { + res.json(data); + }).catch(e => { + res.json({err: e}); + }); + } else { + res.json({err: "game_closed_already"}); + } + }); + + app.post("/v1/requestChannel", (req, res) => { + let query = req.body; + let gid = query.gid || ""; + let encrypt = query.encrypt || null; + let lang = query.QuestionLanguage || "0"; + let quiz = "quiz-1"; + + + if (!gid) { + res.json({ err: "missing_room_id" }); + return; + } + + let game = maker.get(gid); + + if (!game) { + logger.info(`room not exist, create new...`); + quiz = lang === "0" ? "quiz-2" : "quiz-1"; + if (!cipher.supported.includes(encrypt)) { + encrypt = null; + logger.info(`ignore unsupported encrpyt method ${encrypt}`); + } + logger.info(`using quiz set ${quiz}`); + QuizFactory.load(quiz).then(result => { + maker.add(new HQ.Game(gid, "Test Game1", result, encrypt)).catch(_ => { }); + logger.info(`game ${gid} added`); + res.json({ type: "channel", data: gid }); + }); + } else { + logger.info(`room exits, reuse ${game.gid}`); + game.encrypt = encrypt; + game.reset(); + res.json({ type: "channel", data: gid }); + } + }); + + app.post("/v1/inviteRequest", (req, res) => { + let query = req.body; + let gid = query.gid || ""; + let uid = query.uid || ""; + + if (!gid || !uid) { + res.json({ err: "info_missing" }) + return; + } + let game = maker.get(gid); + + if (!game) { + logger.info(`room ${gid} not exist, cannot invite`); + res.json({ err: "room_not_found" }); + return; + } + + game.inviteRequest(uid).then(() => { + logger.info(`invite successfully sent to ${uid}`); + res.json({}) + }); + }); + + + /*------------------------------------------------ + | function : Client Apis, talk to iOS & Android + \*----------------------------------------------*/ + app.post("/v1/join", (req, res) => { + let body = req.body; + let gid = body.gid || null; + let uid = body.uid || null; + + if (!gid || !uid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + if(game.sequence !== 0 || game.open === true){ + res.json({ err: "game_started" }); + return; + } + let player = game.joinGame(uid); + res.json({}); + } + } + }); + app.get("/v1/canplay", (req, res) => { + let query = req.query; + let gid = query.gid || null; + let uid = query.uid || null; + + if (!gid || !uid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + res.json(game.canplay(uid)); + } + } + }); + + app.post("/v1/relive", (req, res) => { + let query = req.body; + let gid = query.gid || null; + let uid = query.uid || null; + + if (!gid || !uid) { + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + game.relive(uid); + res.json({}); + } + } + }); + + app.post("/v1/inviteResponse", (req, res) => { + let query = req.body; + let account = query.account || null; + let accept = query.accept || "false"; + let mediaUid = query.mediaUid || ""; + let gid = query.gid || ""; + + if (!account || !mediaUid || !gid) { + res.json({ err: "info_missing" }); + return; + } + + accept = accept === "true"; + + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + logger.info(`api call invite response ${accept}`); + game.inviteResponse(account, accept, mediaUid); + res.json({}); + } + + }); + + app.post("/v1/answer", (req, res) => { + let query = req.body; + let uid = query.uid || null; + let gid = query.gid || null; + let sid = query.sid; + let answer = query.result; + + logger.info(`will answer: ${uid} ${gid} ${sid} ${answer}`); + if (!gid || sid === undefined || answer === undefined) { + logger.info("info_missing"); + res.json({ err: "info_missing" }); + } else { + let game = maker.get(gid); + if (!game) { + logger.info("room_not_found"); + res.json({ err: "room_not_found" }); + } else { + if (parseInt(sid) !== parseInt(game.sequence)) { + logger.info(`mismatch quiz id given: ${sid}, current: ${game.sequence}`); + res.json({ err: "incorrect_quiz_id" }); + return; + } + if (!game.open) { + logger.info(`game_closed`); + res.json({ err: "game_closed" }); + return; + } + let canplay = game.canplay(uid); + if (!canplay.result) { + logger.info(`cannot_play`); + res.json({ err: canplay.err }); + return; + } + if (game.answerCommited(uid)) { + logger.info(`answer_given`); + res.json({ err: "answer_given" }); + return; + } + + logger.info(`passed check..`); + game.commitanswer(uid, parseInt(answer)); + res.json({}); + } + } + }); + + + + /*------------------------------------------------ + | function : Extra + \*----------------------------------------------*/ + app.post("/v1/quiz", (req, res) => { + let query = req.body; + let gid = query.gid || null; + let quiz = query.quiz || null; + let timeout = query.timeout || 20; + + if (!gid || !quiz) { + res.json({ err: "info_missing" }); + return; + } + + let parsed = QuizFactory.parse(quiz); + if (parsed.err) { + res.json({ err: parsed.err }); + } else { + //good quiz, set to channel + let game = maker.get(gid); + if (!game) { + res.json({ err: "room_not_found" }); + } else { + game.quizSet = parsed.data; + logger.info(`updating quiz: ${JSON.stringify(game.quizSet)}`); + game.timeout = timeout; + game.reset(); + res.json({}); + } + } + }); +} + +module.exports = ApiRestful \ No newline at end of file diff --git a/HQ-Server-Nodejs/modules/FakeEncrypt.js b/HQ-Server-Nodejs/modules/FakeEncrypt.js new file mode 100644 index 0000000..69cd397 --- /dev/null +++ b/HQ-Server-Nodejs/modules/FakeEncrypt.js @@ -0,0 +1,13 @@ +function encrypt(algorithm, text, key) { + return text; +} + +function decrypt(algorithm, text, key) { + return text; +} + +module.exports = { + encrypt: encrypt, + decrypt: decrypt, + supported: [] +} \ No newline at end of file diff --git a/HQ-Server-Nodejs/modules/HQ.js b/HQ-Server-Nodejs/modules/HQ.js new file mode 100644 index 0000000..685c628 --- /dev/null +++ b/HQ-Server-Nodejs/modules/HQ.js @@ -0,0 +1,433 @@ +const logger = require("./logger").get("hq"); +const config = require("./config"); +const cc_id = config.cc_id; +const socks_host = config.socks_proxy_host; +const socks_port = config.socks_proxy_port; +const QuizFactory = require("./QuizFactory"); +const request = require("request"); +const Agent = require('socks5-http-client/lib/Agent'); +const sig_appid = config.agora_appid; +let cipher = null; +try { + cipher = require("./Encrypt"); +} catch (e) { + cipher = require("./FakeEncrypt"); +} + + +function parseResult(err, resultText) { + if (err) { + logger.error(`request failed: ${err}`); + } else { + let result = JSON.parse(resultText); + logger.info(`request: ${resultText}`); + if (result.code === 200) { + return result; + } + } + return null; +} + +function proxy(options) { + if (socks_host) { + logger.info(`using proxy`); + options.agentClass = Agent; + options.socksHost = socks_host; + options.socksPort = socks_port; + } + return options; +} + +class GameMaker { + constructor() { + this.__games = []; + this.sig = null; + } + + add(game) { + return new Promise((resolve, reject) => { + let g = this.get(game.gid); + if (g) { + g.quizSet = game.quizSet; + g.reset(); + resolve(); + return; + } + + this.__games.push(game); + }); + }; + + get(gid) { + let game = this.__games.filter(item => { + return `${item.gid}` === `${gid}`; + }); + return game.length > 0 ? game[0] : null; + }; + + init() { + return Promise.resolve(); + }; + + handle(action, options) { + return new Promise((resolve, reject) => { + if (action === "reset") { + this.handleReset(options); + } + }); + } + + + handleReset(options) { + let gid = options.gid || ""; + if (!gid) { + reject("gid_needed"); + return; + } + + let game = this.get(gid); + if (!game) { + reject("room_not_found"); + return; + } + + game.reset(); + resolve(); + } +} + +class Game { + constructor(gid, name, quizSet, encrypt) { + let game = this; + this.gid = gid || `${parseInt(Math.random() * 1000000)}`; + this.name = name; + this.quizSet = quizSet || []; + this.sequence = 0; + this.open = false; + this.live = false; + this.players = {}; + this.sig_session = null; + this.timeout = 20; + this.encrypt = encrypt || null; + this.inviting = null; + + if (quizSet.length === 0) { + logger.warn(`game ${gid} has an empty quiz set`); + } + } + + reset() { + this.sequence = 0; + this.open = false; + this.live = false; + this.players = {}; + }; + + setLive(live) { + this.live = live; + } + + hasNext() { + let game = this; + return game.sequence < game.quizSet.length; + }; + + nextQuiz() { + let game = this; + return game.quizSet[game.sequence]; + }; + + publish() { + let game = this; + return new Promise((resolve, reject) => { + if (game.open) { + reject("quiz_going_on"); + return; + } + if (!game.hasNext()) { + reject("no_more_quiz"); + return; + } + game.publishNextQuiz().then(result => { + resolve(result); + }); + }); + } + + canplay(uid) { + let game = this; + if (game.sequence === 0) { + return { result: true }; + } + let player = game.players[uid]; + if (!player) { + logger.info(`not a player ${uid}`); + return { result: false, err: `not a player ${uid}` }; + } else { + if (!player.alive) { + logger.info(`${uid} is already game over`); + return { result: false, err: `${uid} is already game over` }; + } else { + return { result: true }; + } + } + }; + + closeQuiz() { + let game = this; + logger.info(`quiz closed for ${game.gid}`); + game.open = false; + return game.summaryResult(game.sequence++); + }; + + publishNextQuiz() { + let game = this; + game.open = true; + return new Promise((resolve, reject) => { + let quiz = Object.assign({}, game.nextQuiz()); + let encrypted_quiz = null; + delete quiz.answer; + quiz.total = game.quizSet.length; + quiz.timeout = game.timeout; + if (cipher.supported.includes(game.encrypt)) { + encrypted_quiz = cipher.encrypt("v1", JSON.stringify(quiz), game.gid); + } + let raw_quiz = { type: "quiz", data: quiz }; + encrypted_quiz = encrypted_quiz ? { type: "quiz", data: encrypted_quiz, encrypt: game.encrypt } : { type: "quiz", data: quiz, encrypt: "null" }; + raw_quiz = JSON.stringify(raw_quiz); + encrypted_quiz = JSON.stringify(encrypted_quiz); + var options = { + uri: `http://hq-im.agoraio.cn:8000/signaling/v1/${sig_appid}/sendChannelMessage`, + method: 'POST', + json: { "m": encrypted_quiz, "channel": game.gid } + }; + proxy(options); + logger.info(`sending quiz ${quiz} to ${game.gid}`) + logger.info(`sending quiz ${encrypted_quiz} to ${game.gid}`) + request(options, function (error, response, body) { + if (!error && response.statusCode == 200) { + resolve(raw_quiz); + } else { + reject(error); + } + }); + }); + }; + + joinGame(uid, force){ + let game = this; + let player = game.players[uid]; + if(player && !force){ + logger.error(`user ${uid} already exists`); + return player; + } + player = {alive: true, answers: {}}; + game.players[uid] = player; + return player; + }; + + relive(uid) { + let game = this; + logger.info(`player ${uid} try to revive himself...`) + let canplay = game.canplay(uid); + logger.info(`can player ${uid} play? ${canplay.result}`); + if (canplay.result) { + logger.info(`player ${uid} revive not needed`) + } else { + logger.info(`god bless ${uid}....now your life has returned`); + this.joinGame(uid, true); + } + }; + + answerCommited(uid) { + let game = this; + let player = game.players[uid]; + if(!player){ + return false; + } + return player.answers[game.sequence] !== undefined; + }; + + commitanswer(uid, result) { + let game = this; + let question = game.quizSet[game.sequence]; + let resultSize = question.options.length; + let answer = parseInt(result); + let player = game.players[uid]; + + if(!player){ + logger.info(`not a player ${uid}`); + return; + } + + if (answer >= resultSize || answer < 0) { + logger.error("invalid answer"); + return; + } + + logger.info(`anwser collected from ${uid}, ${answer}`); + player.answers[game.sequence] = answer; + }; + + + summaryResult(sequence) { + let game = this; + return new Promise((resolve, reject) => { + let results = {}; + let quiz = game.quizSet[sequence]; + let options = quiz.options; + let answer = game.quizSet[sequence].answer; + let rightUids = []; + let wrongUids = []; + let resultSpread = {}; + let players = game.players; + + for (let i = 0; i < options.length; i++) { + resultSpread[i] = 0; + } + //-1 for missing answers + resultSpread[-1] = 0; + + + Object.keys(players).forEach(uid => { + let player = players[uid]; + let commited = player.answers[sequence]; + if (commited === answer) { + rightUids.push(uid); + } else { + wrongUids.push(uid); + player.alive = false; + } + (resultSpread[commited] !== undefined) ? resultSpread[commited]++ : resultSpread[-1]++; + }); + + if (sequence === game.quizSet.length - 1) { + logger.info("=========================FINAL ROUND=========================="); + } else { + logger.info(`=========================QUIZ ${sequence + 1}==========================`) + } + logger.info(`right: ${rightUids.length} in total, {${JSON.stringify(rightUids)}}`); + logger.info(`wrong: ${wrongUids.length} in total, {${JSON.stringify(wrongUids)}}`); + logger.info(`total: ${rightUids.length + wrongUids.length} in total, {${JSON.stringify(rightUids)}`); + logger.info(`spread: ${JSON.stringify(resultSpread)}`); + let data = JSON.stringify({ + type: "result", + data: { + correct: rightUids.length, + total: rightUids.length + wrongUids.length, + sid: sequence, + result: answer, + spread: resultSpread + } + }); + if (sequence === game.quizSet.length - 1) { + data = JSON.stringify({ + type: "result", + data: { + correct: rightUids.length, + total: Object.keys(results).length, + sid: sequence, + result: answer, + spread: resultSpread, + winners: rightUids + } + }) + } + let request_options = { + uri: `http://hq-im.agoraio.cn:8000/signaling/v1/${sig_appid}/sendChannelMessage`, + method: 'POST', + json: { "m": data, "channel": game.gid } + }; + proxy(request_options); + request(request_options, function (error, response, body) { + if (!error && response.statusCode == 200) { + resolve(JSON.parse(data)); + } else { + reject(error); + } + }); + }); + }; + + inviteRequest(invitee) { + let game = this; + logger.info(`try to inivite ${invitee}`); + return new Promise((resolve, reject) => { + game.inviteEnd().then(() => { + let invite_msg = { + type: "inviteRequest", + data: { + uid: invitee + } + } + let request_options = { + uri: `http://hq-im.agoraio.cn:8000/signaling/v1/${sig_appid}/sendMessageTo`, + method: 'POST', + json: { "m": JSON.stringify(invite_msg), "uid": invitee } + }; + proxy(request_options); + request(request_options, function (error, response, body) { + if (!error && response.statusCode == 200) { + game.inviting = { + responded: false, + uid: invitee + }; + resolve(); + } else { + reject(error); + } + }); + }); + }); + }; + + inviteEnd() { + let game = this; + logger.info(`try to end inivite ${JSON.stringify(game.inviting)}`); + if (!game.inviting) { + logger.info(`no inviting exists`); + return Promise.resolve(); + } else { + return new Promise((resolve, reject) => { + let invite_msg = { + type: "inviteEnd", + data: { + uid: game.inviting.uid + } + } + let request_options = { + uri: `http://hq-im.agoraio.cn:8000/signaling/v1/${sig_appid}/sendMessageTo`, + method: 'POST', + json: { "m": JSON.stringify(invite_msg), "uid": game.inviting.uid } + }; + proxy(request_options); + request(request_options, function (error, response, body) { + if (!error && response.statusCode == 200) { + logger.info(`invite end for ${game.inviting.uid} successfully sent`); + resolve(); + } else { + reject(error); + } + }); + }); + } + }; + + inviteResponse(invitee, accept, mediaUid) { + let game = this; + if (!this.inviting){ + logger.info(`no invite exist`); + } else if (invitee !== this.inviting.uid) { + logger.info(`sending invite response for ${invitee} while ${this.inviting.uid} is invited instead`); + } else { + logger.info(`invite response received from ${invitee}, send back to ${this.gid}`); + // server.sig.messageInstantSend(this.gid, JSON.stringify({ type: "inviteResponse", data: { accept: accept, mediaUid: mediaUid, uid: invitee } })); + game.inviting = { responded: true, accept: accept, mediaUid: mediaUid, uid: invitee }; + } + } +} + +module.exports = { + GameMaker: GameMaker, + Game: Game +}; \ No newline at end of file diff --git a/HQ-Server-Nodejs/modules/QuizFactory.js b/HQ-Server-Nodejs/modules/QuizFactory.js new file mode 100644 index 0000000..7927e40 --- /dev/null +++ b/HQ-Server-Nodejs/modules/QuizFactory.js @@ -0,0 +1,75 @@ +const fs = require("fs"); +const path = require("path"); +let QuizFactory = {}; + +QuizFactory.load = function (name) { + return new Promise((resolve, reject) => { + fs.readFile(path.join(__dirname, `../resources/quiz/${name}.json`), (err, data) => { + if (err) { + reject(err); + } else { + let json = JSON.parse(data.toString("utf8")); + let parsed = QuizFactory.parse(json); + if(parsed.err){ + resolve([]); + } else { + resolve(parsed.data); + } + } + }); + }); +}; + + +QuizFactory.loadAll = function (names) { + let tasks = []; + names.forEach(name => { + tasks.push(QuizFactory.load(name)); + }); + return Promise.all(tasks); +}; + + +QuizFactory.parse = function(data) { + if(!Array.isArray(data)){ + return {err: "invalid_format"}; + } + + // "question": "世界上最大的宫殿是", + // "type": "multi", + // "options": [ + // "故宫", + // "白宫", + // "爱丽舍宫", + // "克里姆林宫" + // ], + // "answer": 0 + + let results = []; + let error = ""; + for(let i = 0; i < data.length; i++){ + let item = data[i]; + let options = item.options; + let answer = parseInt(item.answer); + if(answer < 0 || answer >= options.length || isNaN(answer)){ + error = "invalid_data"; + break; + } + options = Array.isArray(options) ? options : []; + results.push({ + id: i, + question: `${item.question}` || "", + type: `${item.type}` || "multi", + options: options, + answer: answer + }); + } + + if(error){ + return {err: error}; + } else { + return {err: null, data: results} + } +}; + +module.exports = QuizFactory; \ No newline at end of file diff --git a/HQ-Server-Nodejs/modules/config.js b/HQ-Server-Nodejs/modules/config.js new file mode 100644 index 0000000..bc15d9e --- /dev/null +++ b/HQ-Server-Nodejs/modules/config.js @@ -0,0 +1,6 @@ +module.exports = { + agora_appid: "319294c67d174c878cc7922551e6e773", + // agora_appid: "363f97d9690e4c0a9780499ab14a16c0", + socks_proxy_host: "23.236.124.12", + socks_proxy_port: 26666 +}; diff --git a/HQ-Server-Nodejs/modules/logger.js b/HQ-Server-Nodejs/modules/logger.js new file mode 100644 index 0000000..55943ff --- /dev/null +++ b/HQ-Server-Nodejs/modules/logger.js @@ -0,0 +1,52 @@ +var winston = require("winston"); +var fs = require("fs"); +var path = require("path"); +var logDir = path.join(__dirname, "../log"); // directory path you want to set +const { printf, combine, timestamp, label } = winston.format; +if (!fs.existsSync(logDir)) { + // Create the directory if it does not exist + fs.mkdirSync(logDir); +} + + +const myFormat = printf(info => { + return `[${info.timestamp}] [${info.label}] ${info.level}: ${info.message}`; +}); +winston.loggers.add("hq", { + format: combine( + timestamp(), + label({ label: "hq" }), + myFormat + ) +}); + +var hqLogger = winston.loggers.get("hq"); +hqLogger.add(new winston.transports.File({ + filename: path.join(logDir, "/hq_log.txt") +})); + +if (process.env.NODE_ENV !== "production") { + hqLogger.add(new winston.transports.Console({ + colorize: true + })); +} + +module.exports = { + get: function (name) { + // return winston.loggers.get(name); + return { + info: function(m){ + let time = (new Date()).toISOString(); + console.log(`${time} - ${m}`); + }, + error: function(m){ + let time = (new Date()).toISOString(); + console.log(`${time} - ${m}`); + }, + warn: function(m){ + let time = (new Date()).toISOString(); + console.log(`${time} - ${m}`); + } + }; + } +}; \ No newline at end of file diff --git a/HQ-Server-Nodejs/modules/token.js b/HQ-Server-Nodejs/modules/token.js new file mode 100644 index 0000000..d8e0bb0 --- /dev/null +++ b/HQ-Server-Nodejs/modules/token.js @@ -0,0 +1,29 @@ +const md5 = require("md5"); + +var SignalingToken = {} + +SignalingToken.get = function(appid, appcertificate, account, validDays){ + var expiredTime = parseInt(new Date().getTime() / 1000)+ 3600* 24 * validDays; + var token_items = []; + + //append SDK VERSION + token_items.push("1"); + + //append appid + token_items.push(appid); + + //expired time + token_items.push(expiredTime); + + //md5 account + appid + appcertificate + expiredtime + token_items.push(md5(account + appid + appcertificate + expiredTime)); + + return token_items.join(":"); +} + +//convenience function to get token valid within 1 day +SignalingToken.get1DayToken = function(appid, appcertificate, account){ + return SignalingToken.get(appid, appcertificate, account, 1); +} + +module.exports = SignalingToken; \ No newline at end of file diff --git a/HQ-Server-Nodejs/package-lock.json b/HQ-Server-Nodejs/package-lock.json new file mode 100644 index 0000000..b90c2fb --- /dev/null +++ b/HQ-Server-Nodejs/package-lock.json @@ -0,0 +1,1431 @@ +{ + "name": "hq", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.2.0" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "integrity": "sha1-Aq1Eo4Cr8nraxebwzdewQ9dMU+M=", + "requires": { + "exit": "0.1.2", + "glob": "3.2.11" + } + }, + "cliff": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=", + "requires": { + "colors": "1.0.3", + "eyes": "0.1.8", + "winston": "0.8.3" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "requires": { + "async": "0.2.10", + "colors": "0.6.2", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "pkginfo": "0.3.1", + "stack-trace": "0.0.10" + }, + "dependencies": { + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + } + } + } + } + }, + "cluster": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", + "integrity": "sha1-5JfiZ8yVa9CwUTrbSqOTNX0Ahe8=", + "requires": { + "log": "1.4.0", + "mkdirp": "0.5.1" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "color": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/color/-/color-0.8.0.tgz", + "integrity": "sha1-iQwHw/1OZJU3Y4kRz2keVFi2/KU=", + "requires": { + "color-convert": "0.5.3", + "color-string": "0.3.0" + } + }, + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "requires": { + "color-name": "1.1.3" + } + }, + "colornames": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-0.0.2.tgz", + "integrity": "sha1-2BH9bIT1kClJmorEQ2ICk1uSvjE=" + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, + "colorspace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.0.1.tgz", + "integrity": "sha1-yZx5btMRKLmHalLh7l7gOkpxl0k=", + "requires": { + "color": "0.8.0", + "text-hex": "0.0.0" + } + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "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==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/cookiejar/download/cookiejar-2.1.1.tgz", + "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diagnostics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.0.tgz", + "integrity": "sha1-4QkJALSVI+hSe+IPCBJ1IF8q42o=", + "requires": { + "colorspace": "1.0.1", + "enabled": "1.0.2", + "kuler": "0.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.3" + } + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "env-variable": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.3.tgz", + "integrity": "sha1-uGwWQb5WECZ9UG8YBx6nbXBwl8s=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fecha": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.2.tgz", + "integrity": "sha1-Ng8DXdbt2VS8lYH5XypKfyo1BcE=" + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "http://registry.npm.taobao.org/formidable/download/formidable-1.2.1.tgz", + "integrity": "sha1-cPt8oCkO5v+WEJBBX0s989IIJlk=", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.1.0" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" + }, + "http": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz", + "integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ip-address": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-4.2.0.tgz", + "integrity": "sha1-cWLCyUmYyilqOM2CvCMWjVHvwnI=", + "requires": { + "cli": "0.6.6", + "cliff": "0.1.10", + "jsbn": "0.0.0", + "lodash.find": "3.2.1", + "lodash.merge": "3.3.2", + "sprintf": "0.1.5" + }, + "dependencies": { + "jsbn": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.0.0.tgz", + "integrity": "sha1-xScBvc7b33CE4c/HAaf4ZGSteCg=" + } + } + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kuler": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-0.0.0.tgz", + "integrity": "sha1-tmu0a5NOVQ9Z2BiEjgq7pPf1VTw=", + "requires": { + "colornames": "0.0.2" + } + }, + "lodash._arraycopy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", + "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=" + }, + "lodash._arrayeach": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", + "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=" + }, + "lodash._basecallback": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz", + "integrity": "sha1-t7K7Q9whYEJKIczybFfkQ3cqjic=", + "requires": { + "lodash._baseisequal": "3.0.7", + "lodash._bindcallback": "3.0.1", + "lodash.isarray": "3.0.4", + "lodash.pairs": "3.0.1" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._baseeach": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz", + "integrity": "sha1-z4cGVyyhROjZ11InyZDamC+TKvM=", + "requires": { + "lodash.keys": "3.1.2" + } + }, + "lodash._basefind": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basefind/-/lodash._basefind-3.0.0.tgz", + "integrity": "sha1-srugXMZF+XLeLPkl+iv2Og9gyK4=" + }, + "lodash._basefindindex": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/lodash._basefindindex/-/lodash._basefindindex-3.6.0.tgz", + "integrity": "sha1-8IM2ChsCJBjtgbyJm+sxLiHnSk8=" + }, + "lodash._basefor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", + "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=" + }, + "lodash._baseisequal": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz", + "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=", + "requires": { + "lodash.isarray": "3.0.4", + "lodash.istypedarray": "3.0.6", + "lodash.keys": "3.1.2" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=" + }, + "lodash._createassigner": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz", + "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", + "requires": { + "lodash._bindcallback": "3.0.1", + "lodash._isiterateecall": "3.0.9", + "lodash.restparam": "3.6.1" + } + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash.find": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-3.2.1.tgz", + "integrity": "sha1-BG4xnzrOkSrGySRsf2g8XsB7Nq0=", + "requires": { + "lodash._basecallback": "3.3.1", + "lodash._baseeach": "3.0.4", + "lodash._basefind": "3.0.0", + "lodash._basefindindex": "3.6.0", + "lodash.isarray": "3.0.4", + "lodash.keys": "3.1.2" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.isplainobject": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz", + "integrity": "sha1-moI4rhayAEMpYM1zRlEtASP79MU=", + "requires": { + "lodash._basefor": "3.0.3", + "lodash.isarguments": "3.1.0", + "lodash.keysin": "3.0.8" + } + }, + "lodash.istypedarray": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", + "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.keysin": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-3.0.8.tgz", + "integrity": "sha1-IsRJPrvtsUJ5YqVLRFssinZ/tH8=", + "requires": { + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.merge": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-3.3.2.tgz", + "integrity": "sha1-DZDZPtY3sYeEN7s+IWASYNev6ZQ=", + "requires": { + "lodash._arraycopy": "3.0.0", + "lodash._arrayeach": "3.0.0", + "lodash._createassigner": "3.1.1", + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4", + "lodash.isplainobject": "3.2.0", + "lodash.istypedarray": "3.0.6", + "lodash.keys": "3.1.2", + "lodash.keysin": "3.0.8", + "lodash.toplainobject": "3.0.0" + } + }, + "lodash.pairs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz", + "integrity": "sha1-u+CNV4bu6qCaFckevw3LfSvjJqk=", + "requires": { + "lodash.keys": "3.1.2" + } + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" + }, + "lodash.toplainobject": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz", + "integrity": "sha1-KHkK2ULSk9eKpmOgfs9/UsoEGY0=", + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keysin": "3.0.8" + } + }, + "log": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/log/-/log-1.4.0.tgz", + "integrity": "sha1-S6HYkP3iSbAx3KA7w36q8yVlbxw=" + }, + "logform": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-1.2.2.tgz", + "integrity": "sha512-a0TCbuqQWYhVdLie9f0tEP33bMxniAuw2StG1c5KhiTANm+RBRNpbSiGrNGpaiTZeoCiVWVsL+V5F0fpy7Q2Og==", + "requires": { + "colors": "1.1.2", + "fecha": "2.3.2" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "1.1.6" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, + "os": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", + "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "0.11.10", + "util": "0.10.3" + } + }, + "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=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.0.tgz", + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.5.tgz", + "integrity": "sha1-tPhQA6k4y7bsvOKhJPsQEr0ag40=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "send": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + } + } + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.2.0" + } + }, + "socks5-client": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socks5-client/-/socks5-client-1.1.2.tgz", + "integrity": "sha1-CHju7wZUECz0I5sTteaMIUMRxrg=", + "requires": { + "ip-address": "4.2.0" + } + }, + "socks5-http-client": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/socks5-http-client/-/socks5-http-client-1.0.2.tgz", + "integrity": "sha1-1Mq75H5bxY3kOFARC9F7VcDB/So=", + "requires": { + "socks5-client": "1.1.2" + } + }, + "sprintf": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz", + "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=" + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "superagent": { + "version": "3.8.2", + "resolved": "http://registry.npm.taobao.org/superagent/download/superagent-3.8.2.tgz", + "integrity": "sha1-5KEbnQR/fT7+s7vlNtnsACHRZAM=", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.1", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.6.0", + "qs": "6.5.1", + "readable-stream": "2.3.5" + } + }, + "supertest": { + "version": "3.0.0", + "resolved": "http://registry.npm.taobao.org/supertest/download/supertest-3.0.0.tgz", + "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", + "dev": true, + "requires": { + "methods": "1.1.2", + "superagent": "3.8.2" + } + }, + "text-hex": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-0.0.0.tgz", + "integrity": "sha1-V4+8haapJjbkLdF7QdAhjM6esrM=" + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "triple-beam": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.1.0.tgz", + "integrity": "sha1-KsOHyMS9BL0mxh34kaYHn4WS/hA=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "winston": { + "version": "3.0.0-rc1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.0.0-rc1.tgz", + "integrity": "sha512-aNtKirnK2UEe5v56SK0TIEr5ylyYdXyjAaIJXZTk21UlNx7FQclTkVU2T1ZzMtdDM+Rk2b7vrI/e/4n8U84XaQ==", + "requires": { + "async": "1.5.2", + "diagnostics": "1.1.0", + "isstream": "0.1.2", + "logform": "1.2.2", + "one-time": "0.0.4", + "stack-trace": "0.0.10", + "triple-beam": "1.1.0", + "winston-transport": "3.0.1" + } + }, + "winston-transport": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-3.0.1.tgz", + "integrity": "sha1-gAixXu9WYMT7P6CU1YzL0IUoxY0=" + }, + "ws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-4.0.0.tgz", + "integrity": "sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ==", + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + } + } +} diff --git a/HQ-Server-Nodejs/package.json b/HQ-Server-Nodejs/package.json new file mode 100644 index 0000000..d328827 --- /dev/null +++ b/HQ-Server-Nodejs/package.json @@ -0,0 +1,30 @@ +{ + "name": "hq", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "cluster": "^0.7.7", + "crypto": "^1.0.1", + "crypto-js": "^3.1.9-1", + "express": "^4.16.2", + "fs": "0.0.1-security", + "http": "0.0.0", + "md5": "^2.2.1", + "os": "^0.1.1", + "path": "^0.12.7", + "request": "^2.83.0", + "socks5-http-client": "^1.0.2", + "winston": "^3.0.0-rc1", + "ws": "^4.0.0", + "xmlhttprequest": "^1.8.0" + }, + "devDependencies": { + "supertest": "^3.0.0" + } +} diff --git a/HQ-Server-Nodejs/readme.md b/HQ-Server-Nodejs/readme.md new file mode 100644 index 0000000..36f2157 --- /dev/null +++ b/HQ-Server-Nodejs/readme.md @@ -0,0 +1,11 @@ + +#### deployment + +1. clone the source code, modify modules/config.js +2. provide your agora signaling appid and signaling broadcast server +3. back to root folder and run following + +```javascript +npm install +node server.js +``` diff --git a/HQ-Server-Nodejs/resources/quiz/quiz-1.json b/HQ-Server-Nodejs/resources/quiz/quiz-1.json new file mode 100644 index 0000000..32d35bf --- /dev/null +++ b/HQ-Server-Nodejs/resources/quiz/quiz-1.json @@ -0,0 +1,141 @@ +[{ + "question": "In the year 1900 in the U.S. what were the most popular first names given to boy and girl babies?", + "type": "multi", + "options": [ + "William and Elizabeth", + "Joseph and Catherine", + "John and Mary", + "George and Anne" + ], + "answer": 2 +},{ + "question": "When did the Liberty Bell get its name?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 2 +},{ + "question": "In the Roy Rogers -Dale Evans Museum, you will find Roy and Dales stuffed horses. Roy's horse was named Trigger, which was Dales horse?", + "type": "multi", + "options": [ + "Buttermilk", + "Daisy", + "Scout", + "Tulip" + ], + "answer": 0 +},{ + "question": "The Daniel Boon museum at the home where he died can best be described how?", + "type": "multi", + "options": [ + "a log cabin in Kentucky", + "a two-story clapboard house in Tennessee", + "a four-story Georgian-style home in Missouri", + "a three story brick house in Arkansas" + ], + "answer": 2 +},{ + "question": "Which of the following items was owned by the fewest U.S. homes in 1990?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 1 +},{ + "question": "Who holds the record for the most victories in a row on the professional golf tour?", + "type": "multi", + "options": [ + "Jack Nicklaus", + "Arnold Palmer", + "Byron Nelson", + "Ben Hogan" + ], + "answer": 2 +},{ + "question": "Who is third behind Hank Aaron and Babe Ruth in major league career home runs?", + "type": "multi", + "options": [ + "Reggie Jackson", + "Harmon Killebrew", + "Willie Mays", + "Frank Robinson" + ], + "answer": 2 +},{ + "question": "In 1990, in what percentage of U.S. married couples did the wife earn more money than the husband?", + "type": "multi", + "options": [ + "8", + "18", + "38", + "58" + ], + "answer": 1 +},{ + "question": "During the 1980s for six consecutive years what breed of dog was the most popular in the U.S.?", + "type": "multi", + "options": [ + "cocker spaniel", + "German shepherd", + "Labrador retriever", + "poodle." + ], + "answer": 0 +},{ + "question": "In 1985, five percent of U.S. households had telephone answering machines. By 1990 what percentage of homes had answering machines?", + "type": "multi", + "options": [ + "10 percent", + "15 percent", + "31 percent", + "51 percent" + ], + "answer": 2 +},{ + "question": "The first black American pictured on a U.S. postage stamp was who?", + "type": "multi", + "options": [ + "Frederick Douglass", + "Booker T. Washington", + "Louis Armstrong", + "Joe Louis" + ], + "answer": 3 +},{ + "question": "What did the \"D\" in \"D-Day\" stand for?", + "type": "multi", + "options": [ + "doom", + "day", + "Dwight (Eisenhower)", + "Dunkirk" + ], + "answer": 1 +},{ + "question": "The Brownie Box Camera introduced by Eastman Kodak in 1900 had a retail price of what?", + "type": "multi", + "options": [ + "$1", + "$5", + "$10", + "$20" + ], + "answer": 0 +},{ + "question": "Which of these characters turned 40 years old in 1990?", + "type": "multi", + "options": [ + "Charlie Brown", + "Bugs Bunny", + "Mickey Mouse", + "Fred Flintstone" + ], + "answer": 0 +}] \ No newline at end of file diff --git a/HQ-Server-Nodejs/resources/quiz/quiz-2.json b/HQ-Server-Nodejs/resources/quiz/quiz-2.json new file mode 100644 index 0000000..32d35bf --- /dev/null +++ b/HQ-Server-Nodejs/resources/quiz/quiz-2.json @@ -0,0 +1,141 @@ +[{ + "question": "In the year 1900 in the U.S. what were the most popular first names given to boy and girl babies?", + "type": "multi", + "options": [ + "William and Elizabeth", + "Joseph and Catherine", + "John and Mary", + "George and Anne" + ], + "answer": 2 +},{ + "question": "When did the Liberty Bell get its name?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 2 +},{ + "question": "In the Roy Rogers -Dale Evans Museum, you will find Roy and Dales stuffed horses. Roy's horse was named Trigger, which was Dales horse?", + "type": "multi", + "options": [ + "Buttermilk", + "Daisy", + "Scout", + "Tulip" + ], + "answer": 0 +},{ + "question": "The Daniel Boon museum at the home where he died can best be described how?", + "type": "multi", + "options": [ + "a log cabin in Kentucky", + "a two-story clapboard house in Tennessee", + "a four-story Georgian-style home in Missouri", + "a three story brick house in Arkansas" + ], + "answer": 2 +},{ + "question": "Which of the following items was owned by the fewest U.S. homes in 1990?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 1 +},{ + "question": "Who holds the record for the most victories in a row on the professional golf tour?", + "type": "multi", + "options": [ + "Jack Nicklaus", + "Arnold Palmer", + "Byron Nelson", + "Ben Hogan" + ], + "answer": 2 +},{ + "question": "Who is third behind Hank Aaron and Babe Ruth in major league career home runs?", + "type": "multi", + "options": [ + "Reggie Jackson", + "Harmon Killebrew", + "Willie Mays", + "Frank Robinson" + ], + "answer": 2 +},{ + "question": "In 1990, in what percentage of U.S. married couples did the wife earn more money than the husband?", + "type": "multi", + "options": [ + "8", + "18", + "38", + "58" + ], + "answer": 1 +},{ + "question": "During the 1980s for six consecutive years what breed of dog was the most popular in the U.S.?", + "type": "multi", + "options": [ + "cocker spaniel", + "German shepherd", + "Labrador retriever", + "poodle." + ], + "answer": 0 +},{ + "question": "In 1985, five percent of U.S. households had telephone answering machines. By 1990 what percentage of homes had answering machines?", + "type": "multi", + "options": [ + "10 percent", + "15 percent", + "31 percent", + "51 percent" + ], + "answer": 2 +},{ + "question": "The first black American pictured on a U.S. postage stamp was who?", + "type": "multi", + "options": [ + "Frederick Douglass", + "Booker T. Washington", + "Louis Armstrong", + "Joe Louis" + ], + "answer": 3 +},{ + "question": "What did the \"D\" in \"D-Day\" stand for?", + "type": "multi", + "options": [ + "doom", + "day", + "Dwight (Eisenhower)", + "Dunkirk" + ], + "answer": 1 +},{ + "question": "The Brownie Box Camera introduced by Eastman Kodak in 1900 had a retail price of what?", + "type": "multi", + "options": [ + "$1", + "$5", + "$10", + "$20" + ], + "answer": 0 +},{ + "question": "Which of these characters turned 40 years old in 1990?", + "type": "multi", + "options": [ + "Charlie Brown", + "Bugs Bunny", + "Mickey Mouse", + "Fred Flintstone" + ], + "answer": 0 +}] \ No newline at end of file diff --git a/HQ-Server-Nodejs/resources/quiz/quiz-ch.json b/HQ-Server-Nodejs/resources/quiz/quiz-ch.json new file mode 100644 index 0000000..08d1032 --- /dev/null +++ b/HQ-Server-Nodejs/resources/quiz/quiz-ch.json @@ -0,0 +1,56 @@ +[{ + "id": 0, + "question": "下面哪一种烫衣服的方法是不正确的?", + "type": "multi", + "options": [ + "熨之前在衣服上喷一些水", + "尽可能长时间的熨", + "掌握合适的温度", + "在衣服和熨斗之间垫一块湿布" + ], + "answer": 1 +},{ + "id": 1, + "question": "世界上最大的宫殿是", + "type": "multi", + "options": [ + "故宫", + "白宫", + "爱丽舍宫", + "克里姆林宫" + ], + "answer": 0 +},{ + "id": 2, + "question": "古代的“侍从”、“书童”和“仆人”起到了现代导游服务中的( )角色。", + "type": "multi", + "options": [ + "全程陪同", + "领队", + "景点讲解员", + "地方陪同" + ], + "answer": 0 +},{ + "id": 3, + "question": "导游人员在讲解西湖旖旎的自然风光时,穿插介绍许仙和白娘子“千年等一回”的传说故事。这种导游讲解技巧是( )。", + "type": "multi", + "options": [ + "突出重点法", + "触景生情法", + "虚实结合法", + "制造悬念法" + ], + "answer": 2 +},{ + "id": 4, + "question": "下列旅游事故中,属重大故障的是", + "type": "multi", + "options": [ + "游客走失", + "集体食物中毒", + "丢失钱物", + "一般车祸" + ], + "answer": 1 +}] \ No newline at end of file diff --git a/HQ-Server-Nodejs/resources/quiz/quiz-en.json b/HQ-Server-Nodejs/resources/quiz/quiz-en.json new file mode 100644 index 0000000..32d35bf --- /dev/null +++ b/HQ-Server-Nodejs/resources/quiz/quiz-en.json @@ -0,0 +1,141 @@ +[{ + "question": "In the year 1900 in the U.S. what were the most popular first names given to boy and girl babies?", + "type": "multi", + "options": [ + "William and Elizabeth", + "Joseph and Catherine", + "John and Mary", + "George and Anne" + ], + "answer": 2 +},{ + "question": "When did the Liberty Bell get its name?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 2 +},{ + "question": "In the Roy Rogers -Dale Evans Museum, you will find Roy and Dales stuffed horses. Roy's horse was named Trigger, which was Dales horse?", + "type": "multi", + "options": [ + "Buttermilk", + "Daisy", + "Scout", + "Tulip" + ], + "answer": 0 +},{ + "question": "The Daniel Boon museum at the home where he died can best be described how?", + "type": "multi", + "options": [ + "a log cabin in Kentucky", + "a two-story clapboard house in Tennessee", + "a four-story Georgian-style home in Missouri", + "a three story brick house in Arkansas" + ], + "answer": 2 +},{ + "question": "Which of the following items was owned by the fewest U.S. homes in 1990?", + "type": "multi", + "options": [ + "when it was made, in 1701", + "when it rang on July 4, 1776", + "in the 19th century, when it became a symbol of the abolition of slavery", + "none of the above" + ], + "answer": 1 +},{ + "question": "Who holds the record for the most victories in a row on the professional golf tour?", + "type": "multi", + "options": [ + "Jack Nicklaus", + "Arnold Palmer", + "Byron Nelson", + "Ben Hogan" + ], + "answer": 2 +},{ + "question": "Who is third behind Hank Aaron and Babe Ruth in major league career home runs?", + "type": "multi", + "options": [ + "Reggie Jackson", + "Harmon Killebrew", + "Willie Mays", + "Frank Robinson" + ], + "answer": 2 +},{ + "question": "In 1990, in what percentage of U.S. married couples did the wife earn more money than the husband?", + "type": "multi", + "options": [ + "8", + "18", + "38", + "58" + ], + "answer": 1 +},{ + "question": "During the 1980s for six consecutive years what breed of dog was the most popular in the U.S.?", + "type": "multi", + "options": [ + "cocker spaniel", + "German shepherd", + "Labrador retriever", + "poodle." + ], + "answer": 0 +},{ + "question": "In 1985, five percent of U.S. households had telephone answering machines. By 1990 what percentage of homes had answering machines?", + "type": "multi", + "options": [ + "10 percent", + "15 percent", + "31 percent", + "51 percent" + ], + "answer": 2 +},{ + "question": "The first black American pictured on a U.S. postage stamp was who?", + "type": "multi", + "options": [ + "Frederick Douglass", + "Booker T. Washington", + "Louis Armstrong", + "Joe Louis" + ], + "answer": 3 +},{ + "question": "What did the \"D\" in \"D-Day\" stand for?", + "type": "multi", + "options": [ + "doom", + "day", + "Dwight (Eisenhower)", + "Dunkirk" + ], + "answer": 1 +},{ + "question": "The Brownie Box Camera introduced by Eastman Kodak in 1900 had a retail price of what?", + "type": "multi", + "options": [ + "$1", + "$5", + "$10", + "$20" + ], + "answer": 0 +},{ + "question": "Which of these characters turned 40 years old in 1990?", + "type": "multi", + "options": [ + "Charlie Brown", + "Bugs Bunny", + "Mickey Mouse", + "Fred Flintstone" + ], + "answer": 0 +}] \ No newline at end of file diff --git a/HQ-Server-Nodejs/robot/ecosystem.config.js b/HQ-Server-Nodejs/robot/ecosystem.config.js new file mode 100644 index 0000000..44f2249 --- /dev/null +++ b/HQ-Server-Nodejs/robot/ecosystem.config.js @@ -0,0 +1,21 @@ +module.exports = { + /** + * Application configuration section + * http://pm2.keymetrics.io/docs/usage/application-declaration/ + */ + apps : [ + + // First application + { + name : 'HQ-Robot', + script : 'test.js', + args : '319294c67d174c878cc7922551e6e773 12345678 agora_hq_cc_server_en_2', + env: { + COMMON_VARIABLE: 'true' + }, + env_production : { + NODE_ENV: 'production' + } + } + ] +}; diff --git a/HQ-Server-Nodejs/robot/test.js b/HQ-Server-Nodejs/robot/test.js new file mode 100644 index 0000000..f2f7b5b --- /dev/null +++ b/HQ-Server-Nodejs/robot/test.js @@ -0,0 +1,124 @@ +const Signal = require("../modules/sig_agora") +const request = require("request"); + +if (process.argv.length !== 5) { + console.error("node server.js "); + process.exit(1); +} + +const appid = process.argv[2]; +const channel = process.argv[3]; +const server_account = process.argv[4]; + +function Agolet() { + let service = this; + this.uid = "1886000234233856"; + this.token = "653d8112ab1151112a9dda5cb49031ed0dc4208b26a3bb840ddc8ef7c1bc6368"; + + this.login = (user, pass) => { + return new Promise((resolve, reject) => { + let options = { + url: "http://agolet.agoralab.co/v1/login", + method: "POST", + json: { account: user, password: md5(pass) } + }; + request(options, function (error, response, loginret) { + if (response.statusCode === 200) { + service.uid = loginret.uid; + service.token = loginret.token; + resolve(); + } else { + reject(error); + } + }) + }); + } + + + this.message = (channel, msg) => { + if (!service.uid || !service.token) { + return Promise.reject("uid or token not exist"); + } + return new Promise((resolve, reject) => { + let options = { + url: "http://agolet.agoralab.co/v1/agobot/message", + method: "POST", + json: { channel: channel, body: msg, uid: service.uid, token: service.token } + }; + request(options, function (error, response) { + if (response.statusCode === 200) { + resolve(); + } else { + reject(error); + } + }) + }); + } +} + + +function Test(){ + this.run = () => { + return new Promise((resolve, reject) => { + let signal = new Signal(appid); + let inst_msg = false; + let chnl_msg = false; + let timer = setTimeout(() => { + reject({ + inst: inst_msg, + chnl: chnl_msg + }); + }, 1000 * 30); + signal.setup_debugging('env', 'lbs100'); + let session = signal.login(channel, "_no_need_token"); + session.onLoginSuccess = function () { + console.log("login successful"); + session.onMessageInstantReceive = (account, uid, msg) => { + console.log(`msg received from ${account}: ${msg}`); + inst_msg = true; + let json = JSON.parse(msg); + if(json.type === "channel"){ + let ch = session.channelJoin(channel); + ch.onChannelJoined = function () { + console.log(`channel joined`); + + ch.onMessageChannelReceive = (account, uid, msg) => { + console.log(`channel msg received ${msg}`) + chnl_msg = true; + clearTimeout(timer); + resolve(); + }; + console.log(`start publish`); + session.messageInstantSend(server_account, JSON.stringify({ + type: "publish" + })) + }; + } + }; + console.log(`message sent`); + session.messageInstantSend(server_account, JSON.stringify({ + type: "RequestChannelName", + QuestionLanguage: "1" + })) + } + }); + } +}; + +let agolet = new Agolet(); +function Runner(){ + new Test().run().then(() => { + console.log("success"); + }).catch(ret => { + let inst_msg = ret.inst; + let chnl_msg = ret.chnl; + console.log(`failed inst_msg: ${inst_msg} chnl_msg: ${chnl_msg}`); + agolet.message("HQ", `failed inst_msg: ${inst_msg} chnl_msg: ${chnl_msg}`); + }); +} + + +Runner(); +setInterval(() => { + Runner(); +}, 1000 * 60 * 30); diff --git a/HQ-Server-Nodejs/samples/answer.json b/HQ-Server-Nodejs/samples/answer.json new file mode 100644 index 0000000..34e83fd --- /dev/null +++ b/HQ-Server-Nodejs/samples/answer.json @@ -0,0 +1 @@ +{"uid": "my_uid", "gid": "12345", "sid": 0, "answer": 0} \ No newline at end of file diff --git a/HQ-Server-Nodejs/samples/quiz.json b/HQ-Server-Nodejs/samples/quiz.json new file mode 100644 index 0000000..b68a93f --- /dev/null +++ b/HQ-Server-Nodejs/samples/quiz.json @@ -0,0 +1,56 @@ +{"quiz": [{ + "id": 0, + "question": "我改了题目?", + "type": "multi", + "options": [ + "熨之前在衣服上喷一些水", + "尽可能长时间的熨", + "掌握合适的温度", + "在衣服和熨斗之间垫一块湿布" + ], + "answer": 1 +},{ + "id": 1, + "question": "世界上最大的宫殿是", + "type": "multi", + "options": [ + "故宫", + "白宫", + "爱丽舍宫", + "克里姆林宫" + ], + "answer": 0 +},{ + "id": 2, + "question": "古代的“侍从”、“书童”和“仆人”起到了现代导游服务中的( )角色。", + "type": "multi", + "options": [ + "全程陪同", + "领队", + "景点讲解员", + "地方陪同" + ], + "answer": 0 +},{ + "id": 3, + "question": "导游人员在讲解西湖旖旎的自然风光时,穿插介绍许仙和白娘子“千年等一回”的传说故事。这种导游讲解技巧是( )。", + "type": "multi", + "options": [ + "突出重点法", + "触景生情法", + "虚实结合法", + "制造悬念法" + ], + "answer": 2 +},{ + "id": 4, + "question": "下列旅游事故中,属重大故障的是", + "type": "multi", + "options": [ + "游客走失", + "集体食物中毒", + "丢失钱物", + "一般车祸" + ], + "answer": 1 +}], "gid": "1215937204"} diff --git a/HQ-Server-Nodejs/samples/quiz_answer.sh b/HQ-Server-Nodejs/samples/quiz_answer.sh new file mode 100644 index 0000000..063b073 --- /dev/null +++ b/HQ-Server-Nodejs/samples/quiz_answer.sh @@ -0,0 +1 @@ +curl -X POST -H "Content-Type: application/json" -d @answer.json http://123.155.153.87:9000/v1/answer \ No newline at end of file diff --git a/HQ-Server-Nodejs/samples/quiz_update.sh b/HQ-Server-Nodejs/samples/quiz_update.sh new file mode 100755 index 0000000..c399238 --- /dev/null +++ b/HQ-Server-Nodejs/samples/quiz_update.sh @@ -0,0 +1 @@ +curl -X POST -H "Content-Type: application/json" -d @quiz.json http://123.155.153.87:10000/v1/quiz diff --git a/HQ-Server-Nodejs/samples/quiz_update_us.sh b/HQ-Server-Nodejs/samples/quiz_update_us.sh new file mode 100755 index 0000000..4562c09 --- /dev/null +++ b/HQ-Server-Nodejs/samples/quiz_update_us.sh @@ -0,0 +1 @@ +curl -X POST -H "Content-Type: application/json" -d @quiz.json http://76.231.168.66:10000/v1/quiz diff --git a/HQ-Server-Nodejs/server.js b/HQ-Server-Nodejs/server.js new file mode 100644 index 0000000..ce9411d --- /dev/null +++ b/HQ-Server-Nodejs/server.js @@ -0,0 +1,58 @@ + +const express = require("express"); +const app = express(); +const bodyParser = require("body-parser"); +const http_server = require("http").Server(app); +const logger = require("./modules/logger").get("hq"); +const HQ = require("./modules/HQ"); +const cluster = require("cluster"); +const QuizFactory = require("./modules/QuizFactory") + + +logger.info(`start in ${process.env.NODE_ENV} mode`); + + +function initProcess(application) { + // parse application/json + application.use(bodyParser.json()); + application.use(bodyParser.urlencoded({ extended: false })); + + // Add headers + application.use(function (req, res, next) { + // Website you wish to allow to connect + res.setHeader("Access-Control-Allow-Origin", "*"); + + // Request methods you wish to allow + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); + + // Request headers you wish to allow + res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type"); + + // Set to true if you need the website to include cookies in the requests sent + // to the API (e.g. in case you use sessions) + // res.setHeader("Access-Control-Allow-Credentials", true); + + // Pass to next layer of midd leware + next(); + }); +} + +let server = null; + +if (cluster.isMaster) { + //master node + const api = require("./modules/ApiRestful"); + initProcess(app); + let maker = new HQ.GameMaker(); + let init = maker.init(); + init.then(() => { + api(maker, app); + http_server.listen(process.env.PORT || 10000); + }).catch((e) => { + logger.error(e); + }); +} else { +} + + +module.exports = http_server; \ No newline at end of file