<commit_before>require("newrelic");
var express = require("express");
var app = express();
var server = require("http").createServer(app);
var config = require("./config.js");
var store = require("./store.js")(config.redis);
var io = require("socket.io")(server, config.io);

// error handling
var errorHandler;
if (config.airbrake && config.airbrake.key) {
  var airbrake = require("airbrake").createClient(config.airbrake.key, config.env);
  for (var setting in config.airbrake) {
    airbrake[setting] = config.airbrake[setting];
  }
  // this registers uncaughtException for node errors and then returns error handler
  errorHandler = airbrake.expressHandler();
}

// utilities
var url = require("url");
var httpClient = require("request");
var logger = require("./logger.js");
var genId = require("./genId.js");

// setup express to parse various content-type
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// configure socket.io redis
var redis = require("redis");
var redisAdapter = require("socket.io-redis");
config.redis.return_buffers = true;
var pub = redis.createClient(config.redis.port, config.redis.host, config.redis);
var sub = redis.createClient(config.redis.port, config.redis.host, config.redis);
io.adapter(redisAdapter({pubClient: pub, subClient: sub}));

// start app
var port = process.env.PORT || config.port || 1337;
logger.info({"category": "app start", "message": "Listening on " + port});
server.listen(port);

// send message to client
app.post("/send", function (req, res) {
  var reqId = genId();
  logger.info({
    "category": "send message request",
    "requestId": reqId,
    "rooms": req.body.rooms,
    "event": req.body.event,
    "args": req.body.args
  });

  if (!req.query.site || !req.query.apiKey) {
    logger.error({"category":"send message error","requestId":reqId,"message":"Missing query param 'site' or 'apiKey'."});
    return res.status(400).send("Missing query param 'site' or 'apiKey'.");
  }
  var site = config.sites[req.query.site];
  if (!site || site.apiKey !== req.query.apiKey) {
    logger.error({"category":"send message error","requestId":reqId,"message":"ApiKey invalid"});
    return res.sendStatus(401).send("ApiKey invalid.");
  }

  var nsp = io.of("/" + req.query.site);
  if (!site.userRoomEnabled) {
    nsp.to(room).emit.apply(nsp, args);
    return;
  }

  // turn shared rooms into array of private rooms for connected users
  var rooms = [];
  [].concat(req.body.rooms).forEach(function(room) {
    if (nsp.isUserRoom(room)) {
      rooms.push(room);
    } else {
      rooms = rooms.concat(nsp.getUserRooms(room));
    }
  });
  // only unique
  rooms = rooms.filter(function(value, index, self) { return self.indexOf(value) === index; });

  rooms.forEach(function(room) {
    if (!nsp.users[room]) {
      return;
    }
    var args = [req.body.event].concat(req.body.args);
    nsp.users[room].devices.forEach(function(device) {
      // user not connected or TTL expired and requires a full sync so no need to store message
      if (config.device_ttl !== 0 && device.connected !== true &&
        (Date.now() - device.disconnectTime) > config.device_ttl * 1000) {
        nsp.removeDevice(room, device);
        if (nsp.users[room].devices.length === 0) {
          delete nsp.users[room];
        }
        logger.info({"category":"user not connected","requestId":reqId,"message":"User " + room + " with deviceId " + device.id + " not connected"});
        return;
      }

      var dataId = store.add(nsp.name, device.storeListName, req.body.event, args);
      logger.info({"category":"message stored","requestId":reqId,"dataId": dataId, "room": room, "args": args});

      var socket = nsp.getSocket(device.socketId);
      if (socket) {
        var callback = function() {
          logger.info({"category":"message removed","requestId":reqId,"message": "Remove message: " + dataId});
          store.remove(nsp.name, device.storeListName, req.body.event, dataId, !req.query.resync ? null : function() {
            socket.emit("_resync");
            logger.error({"category":"resync event","requestId":reqId,"message":"Resync emitted from message: " + dataId});
            store.clear(nsp.name, device.storeListName, req.body.event);
          });
        };

        socket.emit.apply(socket, args.concat(callback));
      }
    });
  });
  return res.sendStatus(200);
});

for (var siteName in config.sites) {
  var site = config.sites[siteName];
  var nsp = io.of("/" + siteName);
  nsp.users = {};
  site.userRoomEnabled = site.userRoomPrefix && site.userRoomPrefix !== "";

  // helper functions
  nsp.isUserRoom = function(room) {
    return room.indexOf(site.userRoomPrefix) === 0;
  };

  nsp.getUserRooms = function(sharedRoom) {
    return nsp.users.map(function(u) { u.rooms.filter(function(r) { return nsp.isUserRoom(r); })[0]; })
  };

  nsp.removeDevice = function(userRoom, device) {
    var devices = nsp.users[userRoom].devices;
    var idx = devices.indexOf(device);
    if (idx > -1) {
      devices.splice(idx, 1);
    }
  };

  nsp.getSocket = function(socketId) {
    return nsp.sockets.filter(function(s) { return s.id == socketId; })[0];
  };

  // socket.io authentication and room registration
  nsp.use(function(socket, next) {
    var query = url.parse(socket.request.url, true).query;
    var headers = {"Authorization": site.authScheme + " " + query.token};

    httpClient.get(site.authUrl, {headers:headers, json:true}, function(error, res, data) {
      if (error) {
        logger.error({"category":"authentication","message":"Client auth error: " + error});<commit_msg>Fix error about 'map' function undefined in getUserRooms Fix error about unable to modify headers when api key is invalid Fix apiKey not working if it's all numbers<commit_after>require("newrelic");
var express = require("express");
var app = express();
var server = require("http").createServer(app);
var config = require("./config.js");
var store = require("./store.js")(config.redis);
var io = require("socket.io")(server, config.io);

// error handling
var errorHandler;
if (config.airbrake && config.airbrake.key) {
  var airbrake = require("airbrake").createClient(config.airbrake.key, config.env);
  for (var setting in config.airbrake) {
    airbrake[setting] = config.airbrake[setting];
  }
  // this registers uncaughtException for node errors and then returns error handler
  errorHandler = airbrake.expressHandler();
}

// utilities
var url = require("url");
var httpClient = require("request");
var logger = require("./logger.js");
var genId = require("./genId.js");

// setup express to parse various content-type
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// configure socket.io redis
var redis = require("redis");
var redisAdapter = require("socket.io-redis");
config.redis.return_buffers = true;
var pub = redis.createClient(config.redis.port, config.redis.host, config.redis);
var sub = redis.createClient(config.redis.port, config.redis.host, config.redis);
io.adapter(redisAdapter({pubClient: pub, subClient: sub}));

// start app
var port = process.env.PORT || config.port || 1337;
logger.info({"category": "app start", "message": "Listening on " + port});
server.listen(port);

// send message to client
app.post("/send", function (req, res) {
  var reqId = genId();
  logger.info({
    "category": "send message request",
    "requestId": reqId,
    "rooms": req.body.rooms,
    "event": req.body.event,
    "args": req.body.args
  });

  if (!req.query.site || !req.query.apiKey) {
    logger.error({"category":"send message error","requestId":reqId,"message":"Missing query param 'site' or 'apiKey'."});
    return res.status(400).send("Missing query param 'site' or 'apiKey'.");
  }
  var site = config.sites[req.query.site];
  if (!site || site.apiKey.toString() !== req.query.apiKey) {
    logger.error({"category":"send message error","requestId":reqId,"message":"ApiKey invalid"});
    return res.status(401).send("ApiKey invalid.");
  }

  var nsp = io.of("/" + req.query.site);
  if (!site.userRoomEnabled) {
    nsp.to(room).emit.apply(nsp, args);
    return;
  }

  // turn shared rooms into array of private rooms for connected users
  var rooms = [];
  [].concat(req.body.rooms).forEach(function(room) {
    if (nsp.isUserRoom(room)) {
      rooms.push(room);
    } else {
      rooms = rooms.concat(nsp.getUserRooms(room));
    }
  });
  // only unique
  rooms = rooms.filter(function(value, index, self) { return self.indexOf(value) === index; });

  rooms.forEach(function(room) {
    if (!nsp.users[room]) {
      return;
    }
    var args = [req.body.event].concat(req.body.args);
    nsp.users[room].devices.forEach(function(device) {
      // user not connected or TTL expired and requires a full sync so no need to store message
      if (config.device_ttl !== 0 && device.connected !== true &&
        (Date.now() - device.disconnectTime) > config.device_ttl * 1000) {
        nsp.removeDevice(room, device);
        if (nsp.users[room].devices.length === 0) {
          delete nsp.users[room];
        }
        logger.info({"category":"user not connected","requestId":reqId,"message":"User " + room + " with deviceId " + device.id + " not connected"});
        return;
      }

      var dataId = store.add(nsp.name, device.storeListName, req.body.event, args);
      logger.info({"category":"message stored","requestId":reqId,"dataId": dataId, "room": room, "args": args});

      var socket = nsp.getSocket(device.socketId);
      if (socket) {
        var callback = function() {
          logger.info({"category":"message removed","requestId":reqId,"message": "Remove message: " + dataId});
          store.remove(nsp.name, device.storeListName, req.body.event, dataId, !req.query.resync ? null : function() {
            socket.emit("_resync");
            logger.error({"category":"resync event","requestId":reqId,"message":"Resync emitted from message: " + dataId});
            store.clear(nsp.name, device.storeListName, req.body.event);
          });
        };

        socket.emit.apply(socket, args.concat(callback));
      }
    });
  });
  return res.sendStatus(200);
});

for (var siteName in config.sites) {
  var site = config.sites[siteName];
  var nsp = io.of("/" + siteName);
  nsp.users = {};
  site.userRoomEnabled = site.userRoomPrefix && site.userRoomPrefix !== "";

  // helper functions
  nsp.isUserRoom = function(room) {
    return room.indexOf(site.userRoomPrefix) === 0;
  };

  nsp.getUserRooms = function(sharedRoom) {
    return Object.keys(nsp.users).filter(function(key) {
      return nsp.users[key].rooms.some(function(r) { return r.toLowerCase() == sharedRoom.toLowerCase(); });
    });
  };

  nsp.removeDevice = function(userRoom, device) {
    var devices = nsp.users[userRoom].devices;
    var idx = devices.indexOf(device);
    if (idx > -1) {
      devices.splice(idx, 1);
    }
  };

  nsp.getSocket = function(socketId) {
    return nsp.sockets.filter(function(s) { return s.id == socketId; })[0];
  };

  // socket.io authentication and room registration
  nsp.use(function(socket, next) {
    var query = url.parse(socket.request.url, true).query;
    var headers = {"Authorization": site.authScheme + " " + query.token};

    httpClient.get(site.authUrl, {headers:headers, json:true}, function(error, res, data) {
      if (error) {
        logger.error({"category":"authentication","message":"Client auth error: " + error});
{'fraction_non_alphanumeric': 0.1686247408431237, 'fraction_numerical': 0.004060124395300622, 'mean_word_length': 3.6851477134763253, 'pattern_counts': {'":': 60, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2022771', 'n_tokens_mistral': 3716, 'n_tokens_neox': 3644, 'n_words': 807}