diff --git a/API/DB.js b/API/DB.js
deleted file mode 100644
index a07aa8d..0000000
--- a/API/DB.js
+++ /dev/null
@@ -1,133 +0,0 @@
-const { Pool, Client } = require('pg')
-const pool = new Pool({
- user: 'postgres',
- password: '8ed89611-dff5-444f-867c-c66098a349cd',
- host: '172.17.0.1',
- database: 'ws_trail',
- port: 5432,
- max: 4
-})
-
-module.exports.SaveEvent = async function (data, sourceIP, country) {
-
- var userID = data.uid;
- var context = data.ctx;
- var eventType = data.type;
- var eventTitle = data.title;
- var eventValue = data.val;
- var eventData = data.data;
- var wlid = data.wlid;
-
- if ( country != null && country != ""){
- if ( eventData == null ){
- eventData = {};
- }
- eventData.country = country;
- }
-
- var SQL = `INSERT INTO Logs (time, userID, context, eventType, eventTitle, eventValue, eventData, sourceIP, wlid)
- VALUES (NOW(), '${Clean(userID)}', '${Clean(context)}', '${Clean(eventType)}', '${Clean(eventTitle)}',
- '${Clean(eventValue)}', '${Clean(JSON.stringify(eventData))}', '${sourceIP}', '${Clean(wlid)}' ) RETURNING time;`
-
- //console.log(SQL);
-
- var rez = null;
- try{
- rez = await pool.query(SQL);
- }
- catch(ex){
- console.log("Crash in SQL Query for SaveEvent: " + SQL);
- var obj = { count: 0, command: "INVALID_QUERY", time: 0 };
- return obj
- }
- //console.log(rez);
- var obj = { count: rez.rowCount, command: rez.command, time: rez.rows[0].time };
- return obj
-}
-
-module.exports.Query = async function (SQL, sourceIP) {
- var rez = await pool.query(SQL);
- //console.log(rez);
- var obj = { count: rez.rowCount, rows: rez.rows };
- return obj
-}
-
-module.exports.StartOrUpdateSession = async function (data, sourceIP, userAgent) {
-
- var userID = data.uid;
-
- var context = data.ctx;
- if ( context == null ){
- context = "";
- }
-
- var wlid = data.wlid;
-
- if ( wlid == null || wlid == "" ){
- wlid = -1;
- }
- else{
- try{
- wlid = parseInt(wlid);
- }catch(ex){}
- }
-
-
- var SQLSearch = `SELECT * FROM sessions WHERE lastupdate > ( now()::timestamp - INTERVAL '1 min' )::timestamp ORDER BY id desc LIMIT 1`;
- var rezSearch = await pool.query(SQLSearch);
-
- if ( rezSearch.rowCount == 0 ){
- var SQL = `INSERT INTO sessions (start_time, is_active, wlid, userid, context, total_time, active_time, idle_time, user_agent, sourceip, lastupdate)
- VALUES (NOW(), '1', '${Clean(wlid)}', '${Clean(userID)}', '${Clean(context)}', 0, 0, 0, '${Clean(userAgent)}', '${sourceIP}', NOW() ) RETURNING id;`
- var rez = await pool.query(SQL);
- //console.log(rez);
- var obj = { count: rez.rowCount, command: rez.command, sessionID: rez.rows[0].id };
- return obj
- }
- else{
- var timeDiffInMinutes = `extract(epoch from (NOW()::timestamp - start_time::timestamp))::integer`;
- var SQL = `UPDATE sessions SET is_active = '1', total_time = ${timeDiffInMinutes}, lastupdate = NOW() WHERE id = '${rezSearch.rows[0].id}';`
- var rez = await pool.query(SQL);
- //console.log(rez);
- var obj = { count: rez.rowCount, command: rez.command, sessionID: rezSearch.rows[0].id };
- return obj
- }
-
-}
-
-module.exports.UpdateSession = async function (sessionID, active_time, is_active) {
-
- var timeDiffInMinutes = `extract(epoch from (NOW() - start_time::timestamp))::integer`;
-
- var SQL = `UPDATE sessions SET is_active = '${Clean(is_active)}', total_time = ${timeDiffInMinutes}, active_time = active_time + ${active_time}, idle_time = (${timeDiffInMinutes}) - (active_time + ${active_time}), lastupdate = NOW() WHERE id = '${sessionID}';`
- //console.log(SQL);
-
- var rez = await pool.query(SQL);
-
- var obj = { count: rez.rowCount, command: rez.command, sessionID: sessionID };
- return obj
-
-}
-
-module.exports.CloseSession = async function (sessionID) {
-
- /*
- var timeDiffInMinutes = `(DATE_PART('day', end_time::timestamp - start_time::timestamp) * 24 +
- DATE_PART('hour', end_time::timestamp - start_time::timestamp)) * 60 +
- DATE_PART('minute', end_time::timestamp - start_time::timestamp)`;
- */
-
- var timeDiffInMinutes = `extract(epoch from (NOW()::timestamp - start_time::timestamp))::integer`;
-
- var SQL = `UPDATE sessions SET end_time = NOW(), is_active = '0', total_time = ${timeDiffInMinutes}, idle_time = (${timeDiffInMinutes}) - (active_time), lastupdate = NOW() WHERE id = '${sessionID}';`
- var rez = await pool.query(SQL);
- //console.log(rez);
- var obj = { count: rez.rowCount, command: rez.command, sessionID: sessionID };
- return obj
-
-}
-
-function Clean(txt){
- if ( txt == null ){ return ""; }
- else{ return (txt + "").replace(/\'/g, "''"); }
-}
\ No newline at end of file
diff --git a/API/REST/GenerateJWT.js b/API/REST/GenerateJWT.js
deleted file mode 100644
index 18a6104..0000000
--- a/API/REST/GenerateJWT.js
+++ /dev/null
@@ -1,56 +0,0 @@
-var jwt = require('jsonwebtoken');
-var JWTKey = require("../../appconfig.json").JWTKey;
-var hardcodedAPIKey = require("../../appconfig.json").ApiKey;
-
-exports.handler = async (event, context, callback) => {
-
- var beginPipeline = process.hrtime();
-
- //debug
- //var body = {"uid":"120", "token": "123615z1ef88136zg5e1"};
-
- var ApiKey = event.headers["x-api-key"];
-
- //PROD
- var body = null;
- try{
- body = JSON.parse(event.body);
- }
- catch(ex){
- callback(null, {
- status: 400,
- content: "Invalid content provided, posted body must be in JSON"
- });
- return;
- }
-
-
- var uid = body.uid;
- //if ( ApiKey != hardcodedAPIKey ){
- if ( ApiKey != hardcodedAPIKey ){
- //console.log(event);
- callback(null, {
- status: 400,
- content: "Invalid token provided"
- });
- return;
- }
-
- var token = jwt.sign(body, JWTKey);
-
-
- const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
- var durationMS = (nanoSeconds/1000000);
-
- var processTime = durationMS.toFixed(2) + "ms";
-
- callback(null, {
- status: 200,
- content: {"jwt": token},
- headers:{
- "processTime": processTime,
- "Content-Type": "application/json"
- }
- });
-
-};
\ No newline at end of file
diff --git a/API/REST/Query.js b/API/REST/Query.js
deleted file mode 100644
index 3f19774..0000000
--- a/API/REST/Query.js
+++ /dev/null
@@ -1,127 +0,0 @@
-var DB = require("../DB.js");
-var hardcodedAPIKey = require("../../appconfig.json").ApiKey;
-
-exports.handler = async (event, context, callback) => {
-
- var beginPipeline = process.hrtime();
-
- //debug
- //var body = {"uid":"120", "ctx":"", "start":"", "end":"", "offset": 0, "token": "123615z1ef88136zg5e1"};
- var ApiKey = event.headers["x-api-key"];
-
- //PROD
- var body = null;
- try{
- body = JSON.parse(event.body);
- }
- catch(ex){
- callback(null, {
- status: 400,
- content: "Invalid content provided, posted body must be in JSON"
- });
- return;
- }
-
- var uid = body.uid;
- var ctx = body.ctx;
- var start = body.start;
- var end = body.end;
-
- var offset = body.offset;
- var pageSize = 200;
- if ( offset == null || offset == ""){
- offset = 0;
- }
-
- var pageSize = body.pageSize;
- if ( pageSize == null || pageSize == ""){
- pageSize = 25;
- }
-
- var eventType = body.eventType;
- if ( eventType == null || eventType == ""){
- eventType = "";
- }
-
- var showSession = body.session;
- if ( showSession == null || showSession == ""){
- showSession = "";
- }
-
- var wlid = body.wlid;
- if ( wlid == null || wlid == ""){
- wlid = "";
- }
-
- if ( ApiKey != hardcodedAPIKey ){
- callback(null, {
- status: 400,
- content: "Invalid token provided"
- });
- return;
- }
-
- //TODO: ensure user is allowed to query this uid & ctx!
-
- var SQL = "SELECT * FROM logs WHERE 1=1 ";
- if ( uid != null && uid != "" ){
- SQL += " AND userID = '" + Clean(uid) + "' ";
- }
- if ( ctx != null && ctx != "" ){
- SQL += " AND context = '" + Clean(ctx) + "' ";
- }
- if ( start != null && start != "" ){
- SQL += " AND time >= '" + Clean(start) + "' ";
- }
- if ( end != null && end != "" ){
- SQL += " AND time <= '" + Clean(end) + "' ";
- }
-
- if ( eventType != "") {
- SQL += " AND eventtype = '" + Clean(eventType) + "' ";
- }
-
- if ( eventType != "Session" && showSession == false ) {
- SQL += " AND eventtype != 'Session' ";
- }
-
- if ( wlid != "") {
- SQL += " AND wlid = '" + Clean(wlid) + "' ";
- }
-
- //ORDER
- SQL += " ORDER BY time desc "
-
- //add a LIMIT & OFFSET
- SQL += " LIMIT " + pageSize + " OFFSET " + Clean(offset);
-
-
-
- var obj = await DB.Query(SQL, event.ip);
-
- const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
- var durationMS = (nanoSeconds/1000000);
-
- obj.processTime = durationMS.toFixed(2) + "ms";
-
- callback(null, {
- status: 200,
- content: obj,
- headers:{
- "processTime": obj.processTime,
- "Content-Type": "application/json; charset=utf-8"
- }
- });
-
-};
-
-
-
-function Clean(txt){
- if ( txt == null ){
- return "";
- }
- else{
- return (txt + "").replace(/\'/g, "''");
- }
-}
\ No newline at end of file
diff --git a/API/REST/Save.js b/API/REST/Save.js
deleted file mode 100644
index 9de000b..0000000
--- a/API/REST/Save.js
+++ /dev/null
@@ -1,84 +0,0 @@
-var DB = require("../DB.js");
-
-var jwt = require('jsonwebtoken');
-var JWTKey = require("../../appconfig.json").JWTKey;
-var hardcodedAPIKey = require("../../appconfig.json").ApiKey;
-
-exports.handler = async (event, context, callback) => {
-
- var beginPipeline = process.hrtime();
-
-
- var Authorization = event.headers["authorization"];
- var jwtSTR = Authorization;
- if ( jwtSTR != null ){
- jwtSTR = Authorization.replace("Bearer ", "");
- }
-
- var ApiKey = event.headers["x-api-key"];
- var apiAuth = false;
- if ( ApiKey == hardcodedAPIKey ){
- apiAuth = true;
- }
-
- var decodedJWT = null;
- if (jwtSTR != null && jwtSTR != ""){
- decodedJWT = jwt.verify(jwtSTR, JWTKey);
- }
-
- var jwtAuth = false;
- if ( decodedJWT == null || decodedJWT.uid == null ){
- //
- }
- else{
- jwtAuth = true;
- }
-
-
- if ( !apiAuth && !jwtAuth ){
- callback(null, {
- status: 400,
- content: "Invalid token / jwt provided"
- });
- return;
- }
-
-
- //debug
- //var body = {"uid":"120", "ctx":"", "type":"Start", "title":"", "val": 1, "data": {}};
-
- //PROD
-
- var body = null;
- try{
- body = JSON.parse(event.body);
- }
- catch(ex){
- callback(null, {
- status: 400,
- content: '{ "error": "Invalid content provided, posted body must be in JSON" }'
- });
- return;
- }
-
- if ( event.headers["cf-ipcountry"] != null && event.headers["cf-ipcountry"] != ""){
- if ( event.ws == null ){
- event.ws = {};
- }
- event.ws.country = event.headers["cf-ipcountry"];
- }
-
- var obj = await DB.SaveEvent(body, event.ip, event.ws.country);
- const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
- var durationMS = (nanoSeconds/1000000);
-
- callback(null, {
- status: 200,
- content: obj,
- headers:{
- "execTime": durationMS.toFixed(2) + "ms",
- "Content-Type": "application/json"
- }
- });
-
-};
\ No newline at end of file
diff --git a/API/REST/Screenshot.js b/API/REST/Screenshot.js
new file mode 100644
index 0000000..f64a9fa
--- /dev/null
+++ b/API/REST/Screenshot.js
@@ -0,0 +1,58 @@
+const puppeteer = require('puppeteer');
+var hardcodedAPIKey = require("../../appconfig.json").ApiKey;
+const tools = require('../shared.js');
+
+const os = require('os')
+const cpuCount = os.cpus().length
+var maxConcurrency = (cpuCount)/2;
+if (maxConcurrency < 1){
+ maxConcurrency = 1;
+}
+
+exports.handler = async (event, context, callback) => {
+
+ var sharedmem = context.sharedmem;
+ var beginPipeline = process.hrtime();
+
+ /*
+ if ( !apiAuth && !jwtAuth ){
+ callback(null, {
+ status: 400,
+ content: "Invalid token / jwt provided"
+ });
+ return;
+ }
+ */
+ while ( sharedmem.getInteger("nbPuppeteerProcess") >= maxConcurrency ){
+ await sleep(20);
+ }
+
+ sharedmem.incInteger("nbPuppeteerProcess", 1);
+
+ var url = event.queryStringParameters.url;
+ if ( url == null || url == "" ){
+ url = "https://www.google.com";
+ }
+ else{
+ url = decodeURIComponent(url);
+ }
+
+ var screenshotResult = await tools.screnshotForUrl(url, true);
+ //var screenshotResult = await screnshotForUrlTab(url);
+
+ sharedmem.incInteger("nbPuppeteerProcess", -1);
+
+ const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
+ var durationMS = (nanoSeconds/1000000);
+
+ callback(null, {
+ status: 200,
+ content: screenshotResult.data,
+ headers:{
+ "execTime": durationMS.toFixed(2) + "ms",
+ "nbPuppeteerProcess": sharedmem.getInteger("nbPuppeteerProcess"),
+ "Content-Type": screenshotResult.mimeType
+ }
+ });
+
+};
\ No newline at end of file
diff --git a/API/REST/Sessions.js b/API/REST/Sessions.js
deleted file mode 100644
index ed3bbc4..0000000
--- a/API/REST/Sessions.js
+++ /dev/null
@@ -1,104 +0,0 @@
-var DB = require("../DB.js");
-var hardcodedAPIKey = require("../../appconfig.json").ApiKey;
-
-exports.handler = async (event, context, callback) => {
-
- var beginPipeline = process.hrtime();
-
- //debug
- //var body = {"uid":"120", "ctx":"", "start":"", "end":"", "offset": 0, "token": "123615z1ef88136zg5e1"};
- var ApiKey = event.headers["x-api-key"];
-
- //PROD
- var body = null;
- try{
- body = JSON.parse(event.body);
- }
- catch(ex){
- callback(null, {
- status: 400,
- content: "Invalid content provided, posted body must be in JSON"
- });
- return;
- }
-
- var uid = body.uid;
- var ctx = body.ctx;
-
- var wlid = "";
- if ( wlid != null && wlid != "" ){
- wlid = body.wlid;
- }
-
- var start = body.start;
- var end = body.end;
-
- var offset = body.offset;
- if ( offset == null || offset == ""){
- offset = 0;
- }
-
-
- if ( ApiKey != hardcodedAPIKey ){
- callback(null, {
- status: 400,
- content: "Invalid token provided"
- });
- return;
- }
-
- //TODO: ensure user is allowed to query this uid & ctx!
-
- var SQL = "SELECT * FROM sessions WHERE 1=1 ";
- if ( uid != null && uid != "" ){
- SQL += " AND userid = '" + Clean(uid) + "' ";
- }
- if ( ctx != null && ctx != "" ){
- SQL += " AND context = '" + Clean(ctx) + "' ";
- }
- if ( wlid != null && wlid != "" ){
- SQL += " AND wlid = '" + Clean(ctx) + "' ";
- }
- if ( start != null && start != "" ){
- SQL += " AND lastupdate >= '" + Clean(start) + "' ";
- }
- if ( end != null && end != "" ){
- SQL += " AND lastupdate <= '" + Clean(end) + "' ";
- }
-
- //ORDER
- SQL += " ORDER BY lastupdate desc "
-
- //add a LIMIT & OFFSET
- SQL += " LIMIT 200 OFFSET " + Clean(offset);
-
-
-
- var obj = await DB.Query(SQL, event.ip);
-
- const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
- var durationMS = (nanoSeconds/1000000);
-
- obj.processTime = durationMS.toFixed(2) + "ms";
-
- callback(null, {
- status: 200,
- content: obj,
- headers:{
- "processTime": obj.processTime,
- "Content-Type": "application/json"
- }
- });
-
-};
-
-
-
-function Clean(txt){
- if ( txt == null ){
- return "";
- }
- else{
- return (txt + "").replace(/\'/g, "''");
- }
-}
\ No newline at end of file
diff --git a/API/WS/Chat.js b/API/WS/Chat.js
deleted file mode 100644
index a1205bf..0000000
--- a/API/WS/Chat.js
+++ /dev/null
@@ -1,25 +0,0 @@
-
-exports.open = (event, context, callback) => {
- //Say hello or send a message to the client, increment number of connected users, ...
- event.ws.subscribe('mainChannel');
- callback(null, "Open from CloudGate! Thread: " + require('worker_threads').threadId);
-};
-
-exports.message = (event, context, callback) => {
- //Do something with the message received from the client (echo, broadcast it, subscribe to a channel, execute some code ...)
-
- if ( event.body != null && event.body != ""){
- event.app.publish('mainChannel', event.body);
- }
-
- //return the body received (echo)
- callback(null, null);
-};
-
-exports.close = (event, context, callback) => {
- // Do something like decrement number of users, close session, ...
-
- //here your response will be discarded because the websocket
- //is already closed at clientside when we receive this event
- callback(null, null);
-};
\ No newline at end of file
diff --git a/API/WS/Monitor.js b/API/WS/Monitor.js
deleted file mode 100644
index a6d9266..0000000
--- a/API/WS/Monitor.js
+++ /dev/null
@@ -1,299 +0,0 @@
-const qs = require('querystring');
-var DB = require("../DB.js");
-
-var jwt = require('jsonwebtoken');
-var JWTKey = require("../../appconfig.json").JWTKey;
-
-var sessionsList = {};
-var maxDurationBeforeQuitSession = 30000;
-
-exports.upgrade = async (event, context, callback) => {
- //console.log("Upgraded!");
-};
-
-exports.open = async (event, context, callback) => {
-
- try{
-
- if ( event.headers["cf-connecting-ip"] != null && event.headers["cf-connecting-ip"] != ""){
- event.ip = event.headers["cf-connecting-ip"];
- }
-
- if ( event.headers["cf-ipcountry"] != null && event.headers["cf-ipcountry"] != ""){
- event.ws.country = event.headers["cf-ipcountry"];
- }
-
- event.ws.startTime = (+new Date());
- event.ws.startActiveTime = (+new Date());
- event.ws.isActive = true;
-
- var params = {};
- try{
- params = qs.parse(event.query);
- }
- catch(exParams){
-
- }
-
- var jwtSTR = params.jwt;
- var wlid = "-1";
- if ( params.wlid != null && params.wlid != ""){
- wlid = params.wlid;
- }
- event.ws.wlid = wlid;
-
- //console.log(jwtSTR);
- event.ws.jwt = jwtSTR;
-
- if ( event.ws.jwt != null && event.ws.jwt != "" ){
- try{
-
- var decodedJWT = jwt.verify(jwtSTR, JWTKey);
- if ( decodedJWT == null || decodedJWT.uid == null ){
- callback(null, "Invalid JWT provided");
- return;
- }
-
- event.ws.decodedJWT = decodedJWT;
- event.ws.uid = decodedJWT.uid;
-
- //Check if we have an existing active session for this user
- //IN-MEMORY -> should be moved to shared memory in C++
- sharedmem = context.sharedmem;
- var cachedSession = sharedmem.getString(decodedJWT.uid + "", "SessionsList");
-
- //keep count of number of websockets opened for this user
- sharedmem.incInteger(decodedJWT.uid + "", 1, "SessionsCount");
- sharedmem.setString(event.ws.uid + "", "0", "SessionsFinished");
-
- //if ( sessionsList[decodedJWT.uid] == null ){
- if ( cachedSession == null || cachedSession == "" ){
- //console.log(cachedSession);
- //console.log(event);
-
- //sessionsList[decodedJWT.uid] = (+new Date());
- var newSessionID = (+new Date()) + "";
- sharedmem.setString(decodedJWT.uid + "", newSessionID + "", "SessionsList");
- sharedmem.setString(decodedJWT.uid + "_lastBeat", (+new Date()), "Heartbeats");
- event.ws.sessionID = newSessionID;
-
- //INSERT Start of session in logs table
- var newSessionEvent = {"uid": decodedJWT.uid, "ctx": "", "wlid": wlid, "type": "Session", "title": "Start", "val": "", "data": {"sessionID": newSessionID, "userAgent": event.headers["user-agent"]}};
-
- if ( decodedJWT.firstname != null){
- newSessionEvent.data.firstname = decodedJWT.firstname;
- }
- if ( decodedJWT.lastname != null){
- newSessionEvent.data.lastname = decodedJWT.lastname;
- }
- if ( decodedJWT.email != null){
- newSessionEvent.data.email = decodedJWT.email;
- }
-
- //console.log("newSessionEvent");
- //console.log(newSessionEvent);
-
- await DB.SaveEvent(newSessionEvent, event.ip, event.ws.country);
-
- console.log("start of sessionID: " + newSessionID)
- }
- else{
- event.ws.sessionID = cachedSession;
- console.log("resuming of sessionID: " + cachedSession)
- }
-
- callback(null, "Monitoring started");
- }
- catch(ex){
- console.log("crash in monitor: ");
- console.log(ex);
- callback(null, ex.message);
- return;
- }
- }
-
- }
- catch(ex){
- console.log(ex);
- console.log(ex.stack);
- console.log("Crash in OPEN");
- }
-
-
- callback(null, JSON.stringify({"status": "OK", "msg": "Monitoring Started"}));
-};
-
-
-var timeoutsList = {};
-
-exports.message = async (event, context, callback) => {
-
- //console.log("MSG 0");
- try{
-
- //Do something with the message received from the client (echo, broadcast it, subscribe to a channel, execute some code ...)
- var msg = event.body;
- var obj = JSON.parse(msg);
- var decodedJWT = event.ws.decodedJWT;
-
- if ( (obj.ctx || obj.channel) != "public"){
- if ( event.ws.jwt != null && event.ws.jwt != "" ){
- try{
- decodedJWT = jwt.verify(event.ws.jwt, JWTKey);
- if ( decodedJWT == null || decodedJWT.uid == null ){
- callback(null, JSON.stringify({"error": "Invalid JWT provided"}));
- return;
- }
- }
- catch(ex){
- callback(null, JSON.stringify({"error": "Invalid JWT provided"}));
- return;
- }
- }
- else{
- callback(null, JSON.stringify({"error": "You must provide a valid JWT to publish or subscribe to CTX: " + (obj.ctx || obj.channel) }));
- return;
- }
- }
-
- //add firstname, lastname, email to data object if available in jwt
- if ( obj.data == null ){
- obj.data = {};
- }
- if ( decodedJWT.firstname != null){
- obj.data.firstname = decodedJWT.firstname;
- }
- if ( decodedJWT.lastname != null){
- obj.data.lastname = decodedJWT.lastname;
- }
- if ( decodedJWT.email != null){
- obj.data.email = decodedJWT.email;
- }
- msg = JSON.stringify(obj);
-
- var resp = null;
- if ( obj.cmd == "event" ){
-
- //publish on the context (appid) channel
- //TODO: ensure the user have permission to publish to that ctx (appid)
- if ( obj.ctx != null && obj.ctx != ""){
- event.app.publish(obj.ctx, msg);
- }
-
- //Store in DB
- var result = await DB.SaveEvent(obj, event.ip, event.ws.country);
-
- //resp = result; //disabled because we don't need to confirm to the client
- }
- else if ( obj.cmd == "heartbeat" ){
-
- event.ws.isActive = true;
- sharedmem.setString(event.ws.uid + "_lastBeat", (+new Date()), "Heartbeats");
- sharedmem.setString(event.ws.uid + "_isActive", "1", "Heartbeats");
-
- //cancel previous timeout if exist
- if ( timeoutsList[event.ws.sessionID] != null ){
- clearTimeout(timeoutsList[event.ws.sessionID]);
- //console.log("timeout cleared for session: " + event.ws.sessionID)
- delete timeoutsList[event.ws.sessionID];
- }
-
- timeoutsList[event.ws.sessionID] = setTimeout( async function(){
- event.ws.isActive = false;
- var lastHeartbeat = -1;
- if ( event != null && event.ws != null ){
- lastHeartbeat = sharedmem.getString(event.ws.uid + "_lastBeat", "Heartbeats");
- if ( lastHeartbeat != "") {
- lastHeartbeat = parseInt(lastHeartbeat);
- }
- }
- }, maxDurationBeforeQuitSession);
-
- }
- else if ( obj.cmd == "subscribe" ){
- //subscribe the caller to his ctx channel
- //TODO: ensure the user is allowed to register this context (have permission for that appID)
- if ( obj.channel != null && obj.channel != ""){
- event.ws.subscribe(obj.channel);
- resp = {cmd: "subscriptionStatus", subscribed: true, channel: obj.channel};
- }
- else{
- resp = {cmd: "subscriptionStatus", subscribed: false, msg: "Channel not found or you don't have permission..."};
- }
- }
-
- }
- catch(ex){
- console.log(ex);
- console.log("Crash in Message");
- }
-
-
-
- //return the response to the websocket client (caller)
- if ( resp != null ){
- callback(null, JSON.stringify(resp));
- }
-
-};
-
-exports.close = async (event, context, callback) => {
-
- //check in 15 sec if the session was not reopened by another tab
- console.log("Closing websocket for sessionID: " + event.ws.sessionID);
- sharedmem.incInteger(event.ws.uid + "", -1, "SessionsCount");
- console.log("Remaining websockets for that uid: " + sharedmem.getInteger(event.ws.uid + "", "SessionsCount") );
-
- var remainingSockets = parseInt(sharedmem.getInteger(event.ws.uid + "", "SessionsCount"));
-
- //No more sockets open for that uid, let's wait 15 seconds to check if the user is going to reconnect
- //if not we will end the session
- if ( remainingSockets == 0 ){
- setTimeout( async function(){
- var lastHeartbeat = parseInt(sharedmem.getString(event.ws.uid + "_lastBeat", "Heartbeats"));
- var remainingSockets = parseInt(sharedmem.getInteger(event.ws.uid + "", "SessionsCount"));
-
- //check if session have already been ended elsewhere
- if ( sharedmem.getString(event.ws.uid + "", "SessionsFinished") == "1" ) {
- return; //session already ended by another thread
- }
-
- if ( lastHeartbeat + maxDurationBeforeQuitSession < (+new Date()) && remainingSockets == 0 ) {
- sharedmem.setString(event.ws.uid + "_isActive", "0", "Heartbeats");
-
- //lets close the session, no other thread have resumed that session in the last 15 seconds
- var activeTimeDuration = (+new Date()) - event.ws.startActiveTime;
- activeTimeDuration = activeTimeDuration / 1000;
-
- //INSERT end of session log
- var endSessionEvent = {"uid": event.ws.decodedJWT.uid, "ctx": "", "wlid": event.ws.wlid, "type": "Session", "title": "Stop", "val": "", "data": {"sessionID": event.ws.sessionID, "durationInSeconds": activeTimeDuration}};
-
- //add firstname, lastname, email to data object if available in jwt
- if ( event.ws.decodedJWT.firstname != null){
- endSessionEvent.data.firstname = event.ws.decodedJWT.firstname;
- }
- if ( event.ws.decodedJWT.lastname != null){
- endSessionEvent.data.lastname = event.ws.decodedJWT.lastname;
- }
- if ( event.ws.decodedJWT.email != null){
- endSessionEvent.data.email = event.ws.decodedJWT.email;
- }
- await DB.SaveEvent(endSessionEvent, event.ip, event.ws.country);
-
- console.log("end of sessionID: " + event.ws.sessionID + ", Duration: " + activeTimeDuration);
-
- delete sessionsList[event.ws.decodedJWT.uid];
- event.ws.startActiveTime = null;
- sharedmem.setString(event.ws.uid + "", "", "SessionsList");
- sharedmem.setString(event.ws.uid + "", "1", "SessionsFinished");
- }
- else{
- console.log("sessionID: " + event.ws.sessionID + " is continued in another thread ... so we are not closing the session");
- }
- }, maxDurationBeforeQuitSession)
- }
-
- //here your response will be discarded because the websocket
- //is already closed at clientside when we receive this event
- callback(null, null);
-};
diff --git a/API/WS/Screenshot.js b/API/WS/Screenshot.js
new file mode 100644
index 0000000..0d63553
--- /dev/null
+++ b/API/WS/Screenshot.js
@@ -0,0 +1,103 @@
+const qs = require('querystring');
+var jwt = require('jsonwebtoken');
+var JWTKey = require("../../appconfig.json").JWTKey;
+const tools = require('../shared.js');
+
+const os = require('os')
+const cpuCount = os.cpus().length
+var maxConcurrency = (cpuCount)/2;
+if (maxConcurrency < 1){
+ maxConcurrency = 1;
+}
+
+exports.upgrade = async (event, context, callback) => {
+ //console.log("Upgraded!");
+};
+
+exports.open = async (event, context, callback) => {
+ callback(null, JSON.stringify({"status": "OK", "msg": "Connected to websocket screenshot service"}));
+};
+
+
+var timeoutsList = {};
+
+exports.message = async (event, context, callback) => {
+
+ try{
+
+ //Do something with the message received from the client (echo, broadcast it, subscribe to a channel, execute some code ...)
+ var msg = event.body;
+ var obj = JSON.parse(msg);
+ msg = JSON.stringify(obj);
+
+ var resp = null;
+
+ if ( obj.cmd == "screenshot" ){
+
+ var sharedmem = context.sharedmem;
+ var beginPipeline = process.hrtime();
+
+ while ( sharedmem.getInteger("nbPuppeteerProcess") >= maxConcurrency ){
+ await sleep(20);
+ }
+
+ sharedmem.incInteger("nbPuppeteerProcess", 1);
+
+ var url = obj.url;
+ if ( url == null || url == "" ){
+ url = "https://www.google.com";
+ }
+ else{
+ url = decodeURIComponent(url);
+ }
+
+ var screenshotResult = await tools.screnshotForUrl(url, true);
+ //var screenshotResult = await screnshotForUrlTab(url);
+
+ sharedmem.incInteger("nbPuppeteerProcess", -1);
+
+ const nanoSeconds = process.hrtime(beginPipeline).reduce((sec, nano) => sec * 1e9 + nano);
+ var durationMS = (nanoSeconds/1000000);
+
+ /*
+ callback(null, {
+ status: 200,
+ content: screenshotResult.data,
+ headers:{
+ "execTime": durationMS.toFixed(2) + "ms",
+ "nbPuppeteerProcess": sharedmem.getInteger("nbPuppeteerProcess"),
+ "Content-Type": screenshotResult.mimeType
+ }
+ });
+ */
+
+ resp = {
+ "cmd": "responseScreenshot",
+ "data": screenshotResult.data.toString("base64"),
+ "execTime": durationMS.toFixed(2) + "ms",
+ "originalTS": obj.originalTS,
+ "Content-Type": screenshotResult.mimeType
+ };
+ }
+
+ }
+ catch(ex){
+ console.log(ex);
+ console.log("Crash in Message handling");
+ }
+
+
+
+ //return the response to the websocket client (caller)
+ if ( resp != null ){
+ callback(null, JSON.stringify(resp));
+ }
+
+};
+
+exports.close = async (event, context, callback) => {
+
+ //here your response will be discarded because the websocket
+ //is already closed at clientside when we receive this event
+ callback(null, null);
+};
diff --git a/API/shared.js b/API/shared.js
new file mode 100644
index 0000000..b0f61e1
--- /dev/null
+++ b/API/shared.js
@@ -0,0 +1,167 @@
+const puppeteer = require('puppeteer');
+
+var browser = null;
+module.exports.screnshotForUrlTab = async function (url, isfullPage) {
+ return new Promise(async function (resolve, reject) {
+
+ var timestamp = (+new Date());
+ var buffer = null;
+ try{
+
+ if ( browser == null){
+ browser = await puppeteer.launch({args: ['--no-sandbox']});
+ }
+
+ //const browser = await puppeteer.launch({args: ['--no-sandbox']});
+ const page = await browser.newPage();
+ await page.goto(url);
+
+ await page.setViewport({
+ width: 1280,
+ height: 900,
+ isMobile: false,
+ deviceScaleFactor: 1,
+ });
+
+ //scroll the whole page for lazy loading
+ //await autoScroll(page);
+
+ //wait for the page to be fully loaded - max 1000ms wait
+ /*
+ try{
+ await page.waitForNavigation({waitUntil: 'networkidle2', timeout: 1000});
+ } catch(ex){
+
+ }
+ */
+
+ buffer = await page.screenshot({
+ //type:'png',
+ type:'jpeg',
+ quality: 88,
+ encoding: 'binary',
+ fullPage: isfullPage
+ });
+ //await browser.close();
+ await page.close();
+
+ let mimeType = "image/jpeg";
+ var resp = {
+ mimeType: mimeType,
+ data: buffer
+ };
+
+ resolve(resp);
+
+ }
+ catch(ex){
+ console.log("Error while taking screenshot: " + ex.message);
+ console.log(ex);
+
+ try{
+ //await browser.close();
+ }catch(ex){
+
+ }
+
+ var resp = {
+ status: "error",
+ details: ex,
+ mimeType: "application/json"
+ };
+ reject(resp);
+ }
+ });
+}
+
+module.exports.screnshotForUrl = async function (url, isfullPage) {
+ return new Promise(async function (resolve, reject) {
+
+ var timestamp = (+new Date());
+ var buffer = null;
+ var browser = null;
+ try {
+
+ browser = await puppeteer.launch({args: ['--no-sandbox']});
+ const page = await browser.newPage();
+ await page.goto(url);
+
+ await page.setViewport({
+ width: 1280,
+ height: 900,
+ isMobile: false,
+ deviceScaleFactor: 1,
+ });
+
+ //scroll the whole page for lazy loading
+ await autoScroll(page);
+
+ //wait for the page to be fully loaded - max 1000ms wait
+ try{
+ //await page.waitForNavigation({waitUntil: 'networkidle2', timeout: 1000});
+ } catch(ex){
+
+ }
+
+
+ buffer = await page.screenshot({
+ type:'jpeg',
+ quality: 84,
+ encoding: 'binary',
+ fullPage: isfullPage
+ });
+ await browser.close();
+
+ let mimeType = "image/jpeg";
+ var resp = {
+ mimeType: mimeType,
+ data: buffer
+ };
+
+ resolve(resp);
+
+ }
+ catch(ex){
+ console.log("Error while taking screenshot: " + ex.message);
+ console.log(ex);
+
+ try{
+ await browser.close();
+ }catch(ex){
+
+ }
+
+ var resp = {
+ status: "error",
+ details: ex,
+ mimeType: "application/json"
+ };
+ reject(resp);
+ }
+ });
+}
+
+
+async function autoScroll (page) {
+ await page.evaluate(async () => {
+ await new Promise((resolve, reject) => {
+ let totalHeight = 0
+ let distance = 500
+ let timer = setInterval(() => {
+ let scrollHeight = document.body.scrollHeight
+ window.scrollBy(0, distance)
+ totalHeight += distance
+ if(totalHeight >= scrollHeight){
+ clearInterval(timer)
+ resolve()
+ }
+ }, 100)
+ })
+ })
+}
+
+module.exports.sleep = function (ms) {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+}
\ No newline at end of file
diff --git a/DB/createDB.sh b/DB/createDB.sh
deleted file mode 100755
index 8894154..0000000
--- a/DB/createDB.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-# CREATE THE DB
-docker exec timescaledb psql -U postgres -c "CREATE DATABASE ws_trail;";
-
-# COPY SQL script to the container
-docker cp ./createTables.sql timescaledb:/tmp/createTables.sql;
-
-# CREATE Tables with the SQL script
-docker exec timescaledb psql -U postgres -d ws_trail -a -f /tmp/createTables.sql;
-
-# CREATE THE HYPERTABLE (TimescaleDB) ON THE TIME COLUMN
-docker exec timescaledb psql -U postgres -d ws_trail -c "SELECT create_hypertable('logs', 'time');";
diff --git a/DB/createTables.sql b/DB/createTables.sql
deleted file mode 100644
index 3da88d4..0000000
--- a/DB/createTables.sql
+++ /dev/null
@@ -1,64 +0,0 @@
---
--- PostgreSQL database dump
---
-
--- Dumped from database version 12.3
--- Dumped by pg_dump version 12.3
-
-SET statement_timeout = 0;
-SET lock_timeout = 0;
-SET idle_in_transaction_session_timeout = 0;
-SET client_encoding = 'UTF8';
-SET standard_conforming_strings = on;
-SELECT pg_catalog.set_config('search_path', '', false);
-SET check_function_bodies = false;
-SET xmloption = content;
-SET client_min_messages = warning;
-SET row_security = off;
-
-SET default_tablespace = '';
-
-SET default_table_access_method = heap;
-
---
--- Name: logs; Type: TABLE; Schema: public; Owner: postgres
---
-
-CREATE TABLE public.logs (
- "time" timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
- userid character varying(255) NOT NULL,
- context character varying(255),
- eventtype character varying(255),
- eventtitle character varying(255),
- eventvalue character varying(255),
- eventdata character varying(500),
- sourceip character varying(255),
- wlid integer
-);
-ALTER TABLE public.logs OWNER TO postgres;
-CREATE INDEX logs_time_idx ON public.logs USING btree ("time" DESC);
-
-
-CREATE TABLE public.sessions (
- id SERIAL PRIMARY KEY,
- start_time timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
- end_time timestamp,
- is_active boolean,
- wlid integer,
- userid character varying(255) NOT NULL,
- context character varying(255),
- total_time integer,
- active_time integer,
- idle_time integer,
- user_agent character varying(500),
- sourceip character varying(255),
- lastupdate timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
-
-);
-ALTER TABLE public.sessions OWNER TO postgres;
-CREATE INDEX sessions_time_idx ON public.sessions USING btree (start_time DESC);
-
-
---
--- PostgreSQL database dump complete
---
\ No newline at end of file
diff --git a/DB/startTimescaleDB.sh b/DB/startTimescaleDB.sh
deleted file mode 100755
index f000ab2..0000000
--- a/DB/startTimescaleDB.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-docker run -d --name timescaledb -p 172.17.0.1:5432:5432 -e POSTGRES_PASSWORD=8ed89611-dff5-444f-867c-c66098a349cd timescale/timescaledb:latest-pg12
-
diff --git a/Dockerfile b/Dockerfile
index d1a1c44..f34a2f1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,8 +7,13 @@ WORKDIR /usr/src/app
# Install app dependencies
COPY * ./
+
+RUN apt-get update && apt-get install -y git
+
RUN npm install
+RUN apt-get update && apt-get install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libgbm-dev libxcb-dri3-0
+
# Bundle app source
COPY . .
diff --git a/appconfig.json b/appconfig.json
index 0b3af7a..0bed268 100644
--- a/appconfig.json
+++ b/appconfig.json
@@ -9,38 +9,19 @@
"TypeAPI": "LOCAL",
"TypeFS": "LOCAL",
"apiEndpoints": {
- "/api/save" : {
- "src": "./API/REST/",
- "handler": "Save.handler"
- },
- "/api/query" : {
+ "/api/screenshot" : {
"src" : "./API/REST/",
- "handler": "Query.handler"
- },
- "/api/sessions" : {
- "src" : "./API/REST/",
- "handler": "Sessions.handler"
- },
- "/api/GenerateJWT" : {
- "src" : "./API/REST/",
- "handler": "GenerateJWT.handler"
+ "handler": "Screenshot.handler"
}
},
"websocketEndpoints": {
- "/ws/monitor" : {
- "src" : "./API/WS/",
- "upgrade": "Monitor.upgrade",
- "open": "Monitor.open",
- "message": "Monitor.message",
- "close": "Monitor.close"
- },
- "/ws/chat" : {
+ "/ws/screenshot" : {
"src" : "./API/WS/",
- "open": "Chat.open",
- "message": "Chat.message",
- "close": "Chat.close"
+ "upgrade": "Screenshot.upgrade",
+ "open": "Screenshot.open",
+ "message": "Screenshot.message",
+ "close": "Screenshot.close"
}
},
- "ApiKey": "1d0ea49c-e026-43f5-93ce-6428bfa86e0d",
- "JWTKey": "2142979b-d237-4792-b197-d2720c973e2d"
+ "ApiKey": "1d0ea49c-e026-43f5-93ce-6428bfa86e0d"
}
\ No newline at end of file
diff --git a/deploy.sh b/deploy.sh
index 2186fad..f9b1e7e 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -1 +1 @@
-ssh root@116.203.203.32 "cd ws-trail && git pull && pm2 reload ws-trail";
\ No newline at end of file
+ssh root@xxx.xxx.xxx.xxx "cd ws-screenshot && git pull && pm2 reload ws-screenshot";
\ No newline at end of file
diff --git a/docker-build.sh b/docker-build.sh
index a7382c8..a80920f 100755
--- a/docker-build.sh
+++ b/docker-build.sh
@@ -1 +1 @@
-docker build -t ws-trail .
+docker build -t ws-screenshot .
diff --git a/docker-run.sh b/docker-run.sh
index 96cf77e..85ca9be 100755
--- a/docker-run.sh
+++ b/docker-run.sh
@@ -1 +1 @@
-docker run -p 3000:3000 -it ws-trail
+docker run -p 3000:3000 -it ws-screenshot
diff --git a/docker-service.sh b/docker-service.sh
index db33138..72310dd 100755
--- a/docker-service.sh
+++ b/docker-service.sh
@@ -1 +1 @@
-docker run -p 3000:3000 -it ws-trail -d --restart always
\ No newline at end of file
+docker run -p 3000:3000 -it ws-screenshot -d --restart always
\ No newline at end of file
diff --git a/installPuppeteerNativeDeps.sh b/installPuppeteerNativeDeps.sh
new file mode 100755
index 0000000..e964601
--- /dev/null
+++ b/installPuppeteerNativeDeps.sh
@@ -0,0 +1 @@
+sudo apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget libgbm-dev libxcb-dri3-0
\ No newline at end of file
diff --git a/omnidb/run.sh b/omnidb/run.sh
deleted file mode 100755
index ba724dc..0000000
--- a/omnidb/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-docker run -it --rm -v conf:/etc/omnidb -p 0.0.0.0:9000:8080 -p 0.0.0.0:25482:25482 taivokasper/omnidb
diff --git a/package-lock.json b/package-lock.json
index 658b6f8..294ea28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"@elestio/cloudgate": {
- "version": "1.0.52",
- "resolved": "https://registry.npmjs.org/@elestio/cloudgate/-/cloudgate-1.0.52.tgz",
- "integrity": "sha512-BEfGoEn/w3A7PKiQ7ISY7XP63wGKUnXsn95UDRTH3GOmGh06882pbi3tDcyiR2RRfYAaXAAxpHcUHVz/k9bR5A==",
+ "version": "1.0.60",
+ "resolved": "https://registry.npmjs.org/@elestio/cloudgate/-/cloudgate-1.0.60.tgz",
+ "integrity": "sha512-mte1f4hm4SPqRXIGaQR2dbK10ZTPhQQ3wQgCLhmJtudsuC5eoL740t33kHStDeB1UoW5SWf88y3sbNnsYdighA==",
"requires": {
"@greenlock/manager": "^3.1.0",
"@root/csr": "^0.8.1",
@@ -25,7 +25,9 @@
"hashlru": "^2.3.0",
"ioredis": "^4.16.0",
"mime": "^2.4.4",
+ "multiparty": "^4.2.2",
"mysql": "^2.18.1",
+ "node-forge": "^0.9.1",
"optimist": "^0.6.1",
"request": "^2.88.2",
"serverless-http": "^2.3.2",
@@ -58,9 +60,9 @@
},
"dependencies": {
"@root/keypairs": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.10.0.tgz",
- "integrity": "sha512-t8VocY46Mtb0NTsxzyLLf5tsgfw0BXLYVADAyiRdEdqHcvPFGJdjkXNtHVQuSV/FMaC65iTOHVP4E6X8iT3Ikg==",
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.10.1.tgz",
+ "integrity": "sha512-LuBheiQwMLCN5iWmhjyW9Mgc7uX16+gABSDfFafaBQnSPVylnOksHa22bqZmDkMCsinOOjUecgy2UgREOT3cOg==",
"requires": {
"@root/encoding": "^1.0.1",
"@root/pem": "^1.0.4",
@@ -127,11 +129,26 @@
}
},
"@types/aws-lambda": {
- "version": "8.10.60",
- "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.60.tgz",
- "integrity": "sha512-zce8DXLRcBJ3ZAJBsNGkobIdXb34whTtZaGCaauRaDI9ZfleZk/x9wNeIcyvDy4m5w5SJSCZ3yFo3xreOgWowA==",
+ "version": "8.10.62",
+ "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.62.tgz",
+ "integrity": "sha512-43XhJY5jvyH03U0cRsSqeHWhvmGZNoLzTPSAZ3JAqgG9p20uw+Sx9Auk5OAefJajd9ZcfjDJ/9lJ2BiBxfKJRw==",
"optional": true
},
+ "@types/node": {
+ "version": "14.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.1.tgz",
+ "integrity": "sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ==",
+ "optional": true
+ },
+ "@types/yauzl": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
+ "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
+ "optional": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"acme": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/acme/-/acme-3.0.3.tgz",
@@ -148,10 +165,15 @@
"@root/mkdirp": "^1.0.0"
}
},
+ "agent-base": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
+ "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
+ },
"ajv": {
- "version": "6.12.3",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
- "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+ "version": "6.12.4",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
+ "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -187,9 +209,9 @@
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sdk": {
- "version": "2.730.0",
- "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.730.0.tgz",
- "integrity": "sha512-hYHplsbgUDP0XuaVJaHx2L/nC0E1i+4dGlkAQpkJz3qQ4NDfLnrbUFsA2g3AyWAsrnjrBNCkexPvwnV82Qng1g==",
+ "version": "2.751.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.751.0.tgz",
+ "integrity": "sha512-0lo7YKTfjEwoP+2vK7F7WGNigvwFxqiM96PzBaseOpOelfhFHPKEJpk2Poa12JI89c8dGoc1PhTQ1TSTJK3ZqQ==",
"requires": {
"buffer": "4.9.2",
"events": "1.1.1",
@@ -215,9 +237,9 @@
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
- "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
+ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
},
"axios": {
"version": "0.19.2",
@@ -255,6 +277,37 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
},
+ "bl": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
+ "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ },
+ "dependencies": {
+ "buffer": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+ "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -282,6 +335,11 @@
"isarray": "^1.0.0"
}
},
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+ },
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
@@ -317,6 +375,11 @@
"readdirp": "~3.4.0"
}
},
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+ },
"clear-module": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.1.tgz",
@@ -380,6 +443,16 @@
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "devtools-protocol": {
+ "version": "0.0.799653",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.799653.tgz",
+ "integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg=="
+ },
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -415,6 +488,32 @@
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
+ "extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "requires": {
+ "@types/yauzl": "^2.9.1",
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -442,6 +541,14 @@
"undici": "^1.2.2"
}
},
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -450,6 +557,15 @@
"to-regex-range": "^5.0.1"
}
},
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
@@ -473,6 +589,11 @@
"mime-types": "^2.1.12"
}
},
+ "fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ },
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -484,6 +605,14 @@
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"optional": true
},
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -528,11 +657,11 @@
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
- "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"requires": {
- "ajv": "^6.5.5",
+ "ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
@@ -541,6 +670,18 @@
"resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz",
"integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A=="
},
+ "http-errors": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
+ "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -551,6 +692,30 @@
"sshpk": "^1.7.0"
}
},
+ "https-proxy-agent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
+ "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
+ "requires": {
+ "agent-base": "5",
+ "debug": "4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
@@ -731,6 +896,14 @@
"safe-buffer": "^5.0.1"
}
},
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
@@ -807,11 +980,33 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
+ "mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "multiparty": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
+ "integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
+ "requires": {
+ "http-errors": "~1.8.0",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "2.1.5"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ }
+ }
+ },
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
@@ -823,6 +1018,11 @@
"sqlstring": "2.3.1"
}
},
+ "node-forge": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz",
+ "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw=="
+ },
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -850,6 +1050,27 @@
"wordwrap": "~0.0.2"
}
},
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ },
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
@@ -863,6 +1084,11 @@
"callsites": "^3.1.0"
}
},
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ },
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -873,6 +1099,11 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ },
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -945,6 +1176,14 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
},
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "requires": {
+ "find-up": "^4.0.0"
+ }
+ },
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
@@ -973,6 +1212,16 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
+ },
+ "proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -992,6 +1241,40 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
+ "puppeteer": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.3.0.tgz",
+ "integrity": "sha512-GjqMk5GRro3TO0sw3QMsF1H7n+/jaK2OW45qMvqjYUyJ7y4oA//9auy969HHhTG3HZXaMxY/NWXF/NXlAFIvtw==",
+ "requires": {
+ "debug": "^4.1.0",
+ "devtools-protocol": "0.0.799653",
+ "extract-zip": "^2.0.0",
+ "https-proxy-agent": "^4.0.0",
+ "mime": "^2.0.3",
+ "pkg-dir": "^4.2.0",
+ "progress": "^2.0.1",
+ "proxy-from-env": "^1.0.0",
+ "rimraf": "^3.0.2",
+ "tar-fs": "^2.0.0",
+ "unbzip2-stream": "^1.3.3",
+ "ws": "^7.2.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -1002,6 +1285,11 @@
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
},
+ "random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
+ },
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@@ -1097,6 +1385,14 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
},
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -1130,6 +1426,11 @@
"@types/aws-lambda": "^8.10.19"
}
},
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
"shelljs": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
@@ -1174,6 +1475,11 @@
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz",
"integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg=="
},
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -1183,9 +1489,44 @@
}
},
"systeminformation": {
- "version": "4.26.10",
- "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.26.10.tgz",
- "integrity": "sha512-bO4FIzrjESAfh4KHwkUJym3jvKtJ4oJ2PG0BBQGBmKa0pF2oanpkB7CF4ZsSX7vfp3+GKaLzioVwpV/3Tyk+lQ=="
+ "version": "4.27.3",
+ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.27.3.tgz",
+ "integrity": "sha512-0Nc8AYEK818h7FI+bbe/kj7xXsMD5zOHvO9alUqQH/G4MHXu5tHQfWqC/bzWOk4JtoQPhnyLgxMYncDA2eeSBw=="
+ },
+ "tar-fs": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz",
+ "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==",
+ "requires": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.0.0"
+ }
+ },
+ "tar-stream": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
+ "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
+ "requires": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
},
"through": {
"version": "2.3.8",
@@ -1205,6 +1546,11 @@
"is-number": "^7.0.0"
}
},
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -1238,15 +1584,43 @@
"version": "github:uNetworking/uWebSockets.js#bec5db11465aa6ddd3f5d2b098fb3180a7639ccb",
"from": "github:uNetworking/uWebSockets.js#binaries"
},
+ "uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "requires": {
+ "random-bytes": "~1.0.0"
+ }
+ },
+ "unbzip2-stream": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+ "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+ "requires": {
+ "buffer": "^5.2.1",
+ "through": "^2.3.8"
+ },
+ "dependencies": {
+ "buffer": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+ "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ }
+ }
+ },
"undici": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-1.3.0.tgz",
- "integrity": "sha512-JE5HzCCsZQzfTtgAVY9LUipmRv3/L+yzcTN6S3DSJWxk1E3RIgyVCIkYv9qV7B3OWu515wci37hhOLllVvGCyA=="
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-1.3.1.tgz",
+ "integrity": "sha512-NX7FzPHOrsIEHv3PDlVX4wzN1gB7WlCUzkcL2Y3rrEE4vKrlJ2Oyvn+QdACR5qpeCI7j3XtfDirGuZlkL2JDDw=="
},
"uri-js": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
- "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
"requires": {
"punycode": "^2.1.0"
},
@@ -1320,6 +1694,15 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
}
}
}
diff --git a/package.json b/package.json
index e813bf7..e6d6eb0 100644
--- a/package.json
+++ b/package.json
@@ -17,9 +17,10 @@
},
"homepage": "https://github.com/elestio/ws-trail#readme",
"dependencies": {
- "@elestio/cloudgate": "^1.0.52",
+ "@elestio/cloudgate": "^1.0.60",
"jsonwebtoken": "^8.5.1",
"pg": "^8.3.0",
+ "puppeteer": "^5.3.0",
"uWebSockets.js": "github:uNetworking/uWebSockets.js#binaries"
}
}
diff --git a/public/client.js b/public/client.js
index fb755f2..a6fca71 100644
--- a/public/client.js
+++ b/public/client.js
@@ -12,14 +12,10 @@ function handler() {
}
function connect() {
- var jwt = "";
- if ( _wsTrail_jwt != "" ){
- jwt = _wsTrail_jwt;
- }
-
+
var protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:';
var rootURL = protocolPrefix + '//' + location.host;
- var ws = new WebSocket(rootURL + '/ws/monitor?jwt=' + jwt);
+ var ws = new WebSocket(rootURL + '/ws/screenshot');
var nbConnectRetry = 5;
globalWS = ws;
ws.onopen = function() {
@@ -35,8 +31,17 @@ function connect() {
var msg = e.data;
//console.log('Message:', msg);
- $(_wsTrail_logSelector).append( sanitizeHTML(msg) + "
");
- $('body').scrollTop($('body').height());
+ var obj = JSON.parse(msg);
+ if ( obj.cmd == "responseScreenshot" ){
+ $("#resultImg").attr("src", "data:image/jpeg;base64, " + obj.data);
+ $("#stats").html("Processing time: " + obj.execTime + " - Roundtrip: " + ( (+new Date()) - obj.originalTS) + "ms" );
+ $("#resultImg").show();
+ }
+ else{
+ $(_wsTrail_logSelector).append( sanitizeHTML(msg) + "
");
+ $('body').scrollTop($('body').height());
+ }
+
};
ws.onclose = function(e) {
@@ -54,11 +59,11 @@ function connect() {
}
var sendQueue = [];
-function SendEvent(eventType, eventTitle, eventValue, eventData){
-
- var event = { cmd: "event", uid: _wsTrail_uid, ctx: _wsTrail_ctx, type: eventType,
- title: eventTitle, val: eventValue, data: eventData, wlid: _wsTrail_wlid };
- Send(event);
+function AskForScreenshot(url){
+ $("#resultImg").hide();
+ $("#stats").html("Please wait ...");
+ var event = { cmd: "screenshot", url: url, originalTS: (+new Date()) };
+ Send(event);
}
diff --git a/public/index.html b/public/index.html
index 691fde5..7e19339 100644
--- a/public/index.html
+++ b/public/index.html
@@ -15,10 +15,15 @@