Made much smarter, store to DB, use epiphany etc.

This commit is contained in:
Linus Miller 2015-08-07 17:13:32 +02:00
parent 538c1158de
commit 0736e4c45c
30 changed files with 866 additions and 49 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "gulp"]
path = gulp
url = git@github.com:thecodebureau/gulp.git

25
app.js
View File

@ -1,25 +0,0 @@
var express = require('express');
var UAParser = require('ua-parser-js');
var app = express();
var parser = new UAParser();
var dust = require('dustjs-linkedin');
var fs = require('fs');
fs.readFile('./index.dust', function(error, data){
dust.loadSource(dust.compile(data.toString(), 'index'));
});
var modern = '<html><head><script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script></head>';
app.use(function(request, response){
parser.setUA(request.get('user-agent'));
dust.render('index', {ua: JSON.stringify(parser.getResult(), null, 2)}, function(error, out){
response.write(out);
response.end();
});
});
app.listen(3993, function(){
console.log("App listening on port 3993.");
});

1
gulp Submodule

@ -0,0 +1 @@
Subproject commit 05881fd4a1af2f0ab2983d52605e28db643c3fc3

112
gulpconfig.js Normal file
View File

@ -0,0 +1,112 @@
module.exports = {
"autoprefixer": {
"browsers": [
"safari >= 5",
"ie >= 8",
"ios >= 6",
"opera >= 12.1",
"firefox >= 17",
"chrome >= 30",
"android >= 4"
],
"cascade": true
},
"bower": {
"src": "/home/sup3rman/dev/browser/bower_components",
"dest": "/home/sup3rman/dev/browser/public/js/bower"
},
"browserSync": {
"browser": "chromium",
"ghostMode": false,
"proxy": "localhost:3810",
"files": [
"/home/sup3rman/dev/browser/public/css/**/*.css",
"/home/sup3rman/dev/browser/public/js/**/*.js"
]
},
"browserify": {
"debug": true,
"dest": "/home/sup3rman/dev/browser/public/js",
"entries": [
"app.js",
"admin.js"
],
"paths": [
"/home/sup3rman/dev/browser/node_modules",
"/home/sup3rman/dev/browser/modules"
],
"outputs": [
"app.js",
"admin.js"
],
"src": "/home/sup3rman/dev/browser/src/js"
},
"fonts": {
"src": "/home/sup3rman/dev/browser/src/fonts/**/*.{eot,ttf,woff}",
"dest": "/home/sup3rman/dev/browser/public/fonts"
},
"nodemon": {
"ext": "js,dust",
"ignore": [
"bower_components",
"gulp",
"node_modules",
"public",
"samples",
"src"
],
"reloadDelay": 300,
"script": "./server/server.js"
},
"raster": {
"src": "/home/sup3rman/dev/browser/src/raster/**/*.{png,gif,jpg}",
"dest": "/home/sup3rman/dev/browser/public/img"
},
"sass": {
"src": "/home/sup3rman/dev/browser/src/sass/**/*.{sass,scss}",
"dest": "/home/sup3rman/dev/browser/public/css",
"options": {
"outputStyle": "nested",
"includePaths": [
"./node_modules/spysass/sass"
],
"imagePath": "../img"
}
},
"static": {
"src": "/home/sup3rman/dev/browser/src/static/**/*",
"dest": "/home/sup3rman/dev/browser/public"
},
"tasks": {
"default": [
"wipe",
[
"bower",
"browserify",
"fonts",
"nodemon",
"raster",
"sass",
"static",
"svg"
],
[
"watch",
"browser-sync"
]
]
},
"svg": {
"src": "/home/sup3rman/dev/browser/src/svg/**/*.svg",
"dest": "/home/sup3rman/dev/browser/public/img"
},
"watch": {
"browserify": "/home/sup3rman/dev/browser/src/js/**/*.js",
"sass": "/home/sup3rman/dev/browser/src/sass/**/*.{sass,scss}"
},
"wipe": {
"src": [
"/home/sup3rman/dev/browser/public"
]
}
}

1
gulpfile.js Symbolic link
View File

@ -0,0 +1 @@
gulp/gulpfile.js

View File

@ -1,12 +0,0 @@
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
</head>
<body>
<div id="mod"></div>
<pre>
{ua}
</pre>
<script>document.getElementById("mod").textContent = document.querySelector("html").classList;</script>
</body>
</html>

11
nodemon.json Normal file
View File

@ -0,0 +1,11 @@
{
"ext": "js,dust",
"ignore": [
"bower_components",
"gulp",
"node_modules",
"public",
"samples",
"src"
]
}

View File

@ -2,18 +2,14 @@
"name": "browser",
"version": "0.1.0",
"description": "",
"main": "app.js",
"dependencies": {
"dustjs-linkedin": "^2.7.2",
"express": "^4.13.3",
"ua-parser-js": "^0.7.9"
},
"devDependencies": {},
"scripts": {},
"repository": {
"type": "git",
"url": "gituser@git.thecodebureau.com:tcb/browser"
"main": "./server/server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "The Code Bureau <info@thecodebureau.com> (https://thecodebureau.com)",
"license": "MIT"
"dependencies": {
"epiphany": "^0.5.3",
"jquery-jsonify": "^0.1.2",
"ua-parser-js": "^0.7.9"
}
}

111
public/js/admin.js Normal file

File diff suppressed because one or more lines are too long

101
public/js/app.js Normal file
View File

@ -0,0 +1,101 @@
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({2:[function(require,module,exports){
$(function() {
var device = {};
var nav = _.clone(window.navigator);
nav.plugins = _.map(navigator.plugins, function(obj, key) {
return obj.name + ' (' + obj.filename + ')';
});
//device.navigator = _.omit(nav, [
// 'appCodeName',
// 'appMinorVersion',
// 'appName',
// 'appVersion',
// 'battery',
// 'cpuClass',
// 'geolocation',
// 'mimeTypes',
// 'oscpu',
// 'platform',
// 'product',
// 'vendor',
// 'vendorSub'
//]);
device.navigator = _.pick(nav, [
'cookieEnabled',
'doNotTrack',
'hardwareConcurrency',
'language',
'languages',
'maxTouchPoints',
'onLine',
'plugins',
'productSub',
'browserLanguage',
'systemLanguage',
'userLanguage',
'userAgent'
]);
device.screen = _.pick(window.screen, [
"availHeight",
"availLeft",
"availTop",
"availWidth",
"colorDepth",
"height",
"pixelDepth",
"width"
]);
device.screen.body = _.pick(document.body, 'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight', 'scrollWidth', 'scrollHeight');
device.screen.pixelRatio = window.devicePixelRatio;
var orientation = window.screen.orientation || window.screen.mozOrientation || window.screen.msOrientation;
if(!_.isString(orientation))
orientation = _.pick(orientation, [ 'type', 'angle' ]);
device.screen.orientation = orientation;
device.modernizr = _.compact($(document.documentElement).attr('class').split(' '));
//nav.javaEnabled = nav.javaEnabled();
var $div = $('#content');
_.each(device, function(value, key) {
$div.append($('<h2>' + key + '</h2>'));
var $pre = $('<pre id="' + key + '">');
$pre.text(JSON.stringify(value, null, 2));
$div.append($pre);
});
// TODO maybe have server send Headers from AJAX request
var id = window.location.search ? window.location.search.split('=')[1] : 'null';
var path = window.location.protocol + '//' + window.location.host + '/api/fingerprints/' + id;
console.log(path);
$.ajax({
method: 'POST',
dataType: 'json',
data: device,
url: path,
success: function(res) {
var key = 'headers';
$div.append($('<h2>' + key + '</h2>'));
var $pre = $('<pre id="' + key + '">');
$pre.text(JSON.stringify(res.headers, null, 2));
$div.append($pre);
console.log('success');
},
error: function() {
console.log('error');
}
});
});
},{}]},{},[2])
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9mYWN0b3ItYnVuZGxlL25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvanMvYXBwLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIkKGZ1bmN0aW9uKCkge1xuXHR2YXIgZGV2aWNlID0ge307XG5cblx0dmFyIG5hdiA9IF8uY2xvbmUod2luZG93Lm5hdmlnYXRvcik7XG5cblx0bmF2LnBsdWdpbnMgPSBfLm1hcChuYXZpZ2F0b3IucGx1Z2lucywgZnVuY3Rpb24ob2JqLCBrZXkpIHtcblx0XHRyZXR1cm4gb2JqLm5hbWUgKyAnICgnICsgb2JqLmZpbGVuYW1lICsgJyknO1xuXHR9KTtcblxuXHQvL2RldmljZS5uYXZpZ2F0b3IgPSBfLm9taXQobmF2LCBbXG5cdC8vXHQnYXBwQ29kZU5hbWUnLFxuXHQvL1x0J2FwcE1pbm9yVmVyc2lvbicsXG5cdC8vXHQnYXBwTmFtZScsXG5cdC8vXHQnYXBwVmVyc2lvbicsXG5cdC8vXHQnYmF0dGVyeScsXG5cdC8vXHQnY3B1Q2xhc3MnLFxuXHQvL1x0J2dlb2xvY2F0aW9uJyxcblx0Ly9cdCdtaW1lVHlwZXMnLFxuXHQvL1x0J29zY3B1Jyxcblx0Ly9cdCdwbGF0Zm9ybScsXG5cdC8vXHQncHJvZHVjdCcsXG5cdC8vXHQndmVuZG9yJyxcblx0Ly9cdCd2ZW5kb3JTdWInXG5cdC8vXSk7XG5cdGRldmljZS5uYXZpZ2F0b3IgPSBfLnBpY2sobmF2LCBbXG5cdFx0J2Nvb2tpZUVuYWJsZWQnLFxuXHRcdCdkb05vdFRyYWNrJyxcblx0XHQnaGFyZHdhcmVDb25jdXJyZW5jeScsXG5cdFx0J2xhbmd1YWdlJyxcblx0XHQnbGFuZ3VhZ2VzJyxcblx0XHQnbWF4VG91Y2hQb2ludHMnLFxuXHRcdCdvbkxpbmUnLFxuXHRcdCdwbHVnaW5zJyxcblx0XHQncHJvZHVjdFN1YicsXG5cdFx0J2Jyb3dzZXJMYW5ndWFnZScsXG5cdFx0J3N5c3RlbUxhbmd1YWdlJyxcblx0XHQndXNlckxhbmd1YWdlJyxcblx0XHQndXNlckFnZW50J1xuXHRdKTtcblxuXHRkZXZpY2Uuc2NyZWVuID0gXy5waWNrKHdpbmRvdy5zY3JlZW4sIFtcblx0XHRcImF2YWlsSGVpZ2h0XCIsXG5cdFx0XCJhdmFpbExlZnRcIixcblx0XHRcImF2YWlsVG9wXCIsXG5cdFx0XCJhdmFpbFdpZHRoXCIsXG5cdFx0XCJjb2xvckRlcHRoXCIsXG5cdFx0XCJoZWlnaHRcIixcblx0XHRcInBpeGVsRGVwdGhcIixcblx0XHRcIndpZHRoXCJcblx0XSk7XG5cblx0ZGV2aWNlLnNjcmVlbi5ib2R5ID0gXy5waWNrKGRvY3VtZW50LmJvZHksICdjbGllbnRXaWR0aCcsICdjbGllbnRIZWlnaHQnLCAnb2Zmc2V0V2lkdGgnLCAnb2Zmc2V0SGVpZ2h0JywgJ3Njcm9sbFdpZHRoJywgJ3Njcm9sbEhlaWdodCcpO1xuXHRkZXZpY2Uuc2NyZWVuLnBpeGVsUmF0aW8gPSB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbztcblxuXHR2YXIgb3JpZW50YXRpb24gPSB3aW5kb3cuc2NyZWVuLm9yaWVudGF0aW9uIHx8IHdpbmRvdy5zY3JlZW4ubW96T3JpZW50YXRpb24gfHwgd2luZG93LnNjcmVlbi5tc09yaWVudGF0aW9uO1xuXG5cdGlmKCFfLmlzU3RyaW5nKG9yaWVudGF0aW9uKSlcblx0XHRvcmllbnRhdGlvbiA9IF8ucGljayhvcmllbnRhdGlvbiwgWyAndHlwZScsICdhbmdsZScgXSk7XG5cblx0ZGV2aWNlLnNjcmVlbi5vcmllbnRhdGlvbiA9IG9yaWVudGF0aW9uO1xuXG5cdGRldmljZS5tb2Rlcm5penIgPSBfLmNvbXBhY3QoJChkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQpLmF0dHIoJ2NsYXNzJykuc3BsaXQoJyAnKSk7XG5cblx0Ly9uYXYuamF2YUVuYWJsZWQgPSBuYXYuamF2YUVuYWJsZWQoKTtcblx0dmFyICRkaXYgPSAkKCcjY29udGVudCcpO1xuXHRfLmVhY2goZGV2aWNlLCBmdW5jdGlvbih2YWx1ZSwga2V5KSB7XG5cdFx0JGRpdi5hcHBlbmQoJCgnPGgyPicgKyBrZXkgKyAnPC9oMj4nKSk7XG5cdFx0dmFyICRwcmUgPSAkKCc8cHJlIGlkPVwiJyArIGtleSArICdcIj4nKTtcblx0XHQkcHJlLnRleHQoSlNPTi5zdHJpbmdpZnkodmFsdWUsIG51bGwsIDIpKTtcblx0XHQkZGl2LmFwcGVuZCgkcHJlKTtcblx0fSk7XG5cblxuXHQvLyBUT0RPIG1heWJlIGhhdmUgc2VydmVyIHNlbmQgSGVhZGVycyBmcm9tIEFKQVggcmVxdWVzdFxuXHR2YXIgaWQgPSB3aW5kb3cubG9jYXRpb24uc2VhcmNoID8gd2luZG93LmxvY2F0aW9uLnNlYXJjaC5zcGxpdCgnPScpWzFdIDogJ251bGwnO1xuXG5cdHZhciBwYXRoID0gd2luZG93LmxvY2F0aW9uLnByb3RvY29sICsgJy8vJyArIHdpbmRvdy5sb2NhdGlvbi5ob3N0ICsgJy9hcGkvZmluZ2VycHJpbnRzLycgKyBpZDtcblxuXHRjb25zb2xlLmxvZyhwYXRoKTtcblx0JC5hamF4KHtcblx0XHRtZXRob2Q6ICdQT1NUJyxcblx0XHRkYXRhVHlwZTogJ2pzb24nLFxuXHRcdGRhdGE6IGRldmljZSxcblx0XHR1cmw6IHBhdGgsXG5cdFx0c3VjY2VzczogZnVuY3Rpb24ocmVzKSB7XG5cdFx0XHR2YXIga2V5ID0gJ2hlYWRlcnMnO1xuXHRcdFx0JGRpdi5hcHBlbmQoJCgnPGgyPicgKyBrZXkgKyAnPC9oMj4nKSk7XG5cdFx0XHR2YXIgJHByZSA9ICQoJzxwcmUgaWQ9XCInICsga2V5ICsgJ1wiPicpO1xuXHRcdFx0JHByZS50ZXh0KEpTT04uc3RyaW5naWZ5KHJlcy5oZWFkZXJzLCBudWxsLCAyKSk7XG5cdFx0XHQkZGl2LmFwcGVuZCgkcHJlKTtcblx0XHRcdGNvbnNvbGUubG9nKCdzdWNjZXNzJyk7XG5cdFx0fSxcblx0XHRlcnJvcjogZnVuY3Rpb24oKSB7XG5cdFx0XHRjb25zb2xlLmxvZygnZXJyb3InKTtcblx0XHR9XG5cdH0pO1xufSk7XG4iXX0=

1
public/js/common.js Normal file
View File

@ -0,0 +1 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({},{},[]);

5
server/config/details.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
title: 'Epiphany',
name: 'epiphany'
};

47
server/config/dir.js Normal file
View File

@ -0,0 +1,47 @@
// MODULES
// modules : NATIVE
var path = require('path');
var PWD = process.env.PWD;
// Server folders. All folder related to the Node server handling most things.
module.exports = {
// Any code that needs to a "middle landing spot" while built, place it anywhere here
build: {
root: path.join(PWD, 'build')
},
// public directories. Where all static/built/live content goes.
public: {
css: path.join(PWD, 'public', 'css'),
fonts: path.join(PWD, 'public', 'fonts'),
img: path.join(PWD, 'public', 'img'),
root: path.join(PWD, 'public'),
scripts: path.join(PWD, 'public', 'js')
},
root: PWD,
server: {
middleware: path.join(PWD, 'server', 'middleware'),
models: path.join(PWD, 'server', 'models'),
routes: path.join(PWD, 'server', 'routes'),
root: path.join(PWD, 'server'),
schemas: path.join(PWD, 'server', 'schema.org'),
templates: path.join(PWD, 'server', 'templates'),
uploads: path.join(PWD, 'server', 'uploads')
},
// Source directories. This is where you put all content that needs to be built before use.
src: {
fonts: path.join(PWD, 'src', 'fonts'),
raster: path.join(PWD, 'src', 'raster'),
root: path.join(PWD, 'src'),
sass: path.join(PWD, 'src', 'sass'),
static: path.join(PWD, 'src', 'static'),
scripts: path.join(PWD, 'src', 'js'),
svg: path.join(PWD, 'src', 'svg'),
}
};

12
server/config/domain.js Normal file
View File

@ -0,0 +1,12 @@
var _ = require('lodash');
module.exports = function(config) {
if(!config.port || !_.isNumber(config.port)) throw new Error('Port undefined or not a number');
return {
development: 'localhost:' + config.port,
testing: 'changeThisFool.testing.thecodebureau.com',
staging: 'changeThisFool.thecodebureau.com',
production: 'www.changeThisFool.se'
}[ENV];
};

View File

@ -0,0 +1,23 @@
module.exports = {
defaults: {
mystify: {
properties: [ 'message', 'name', 'status', 'statusText' ]
},
log: {
// if database = true there has to be a mongoose model name ErrorModel
ignore: [],
}
},
development: {
log: {
database: false,
console: true,
}
},
production: {
log: {
database: true,
console: false,
}
},
};

13
server/config/globals.js Normal file
View File

@ -0,0 +1,13 @@
var path = require('path');
module.exports = function(bind) {
bind = bind || global;
bind.ENV = process.env.NODE_ENV || 'development';
bind.PWD = process.env.PWD;
bind.DEBUG_ERROR_HANDLER = false;
bind.DEBUG_RESPONDER = false;
bind.LOGIN_USER = false;
//bind.LOGIN_USER = 'username';
//bind.LOGIN_USER = 'linus.miller@thecodebureau.com';
};

12
server/config/gulp.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
browserify: {
entries: [
'app.js',
'admin.js'
],
outputs: [
'app.js',
'admin.js'
]
}
};

8
server/config/mongo.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
defaults: {
uri: 'mongodb://localhost/fingerprint'
},
production: {
uri: 'mongodb://fpSupreme:weIzTouching--Peeps@localhost/fingerprint'
}
};

8
server/config/port.js Normal file
View File

@ -0,0 +1,8 @@
var basePort = 3810;
module.exports = {
development: basePort,
testing: basePort + 1,
staging: basePort + 2,
production: basePort + 3
}[ENV];

14
server/config/session.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
defaults: {
secret: 'changeThisFool',
resave: true,
saveUninitialized: true
},
production: {
redis: {
port: 6379,
host: 'localhost'
}
}
};

View File

@ -0,0 +1,66 @@
var UAParser = require('ua-parser-js');
var parser = new UAParser();
module.exports = function(config, mongoose) {
var Fingerprint = mongoose.model('Fingerprint');
return {
create: function(req, res, next) {
Fingerprint.create(req.body, function(err, fp) {
if(err) return next(err);
res.status(200).data.fingerprint = fp;
next();
});
},
findAll: function(req, res, next) {
},
push: function(req, res, next) {
var ua = req.get('user-agent');
parser.setUA(ua);
var uaParsed = _.omit(parser.getResult(), 'ua');
var headers = _.omit(req.headers, [ 'cookie', 'user-agent' ]);
var ip = req.ip;
var clientFingerprint = _.extend({
ip: ip,
ua: ua,
uaParsed: uaParsed,
headers: headers
}, req.body);
res.data.clientFingerprint = clientFingerprint;
if(req.params.id !== 'null') {
Fingerprint.findById( req.params.id, function(err, fp) {
if(err) return next(err);
if(fp) {
if(!_.find(fp.client, { ua: ua })) {
fp.client.push(clientFingerprint);
fp.save(function(err, fp) {
if(err) console.error(err);
res.status(201);
next();
});
} else {
next();
}
} else {
next();
}
});
} else {
res.status(200);
next();
}
}
};
};

49
server/middleware/ua.js Normal file
View File

@ -0,0 +1,49 @@
var UAParser = require('ua-parser-js');
var parser = new UAParser();
module.exports = function(config, mongoose) {
var Fingerprint = mongoose.model('Fingerprint');
return function userAgent(req, res, next){
var ua = req.get('user-agent');
parser.setUA(ua);
var uaParsed = _.omit(parser.getResult(), 'ua');
var headers = _.omit(req.headers, [ 'cookie', 'user-agent' ]);
if(!_.isEmpty(req.query))
var ip = req.ip;
Fingerprint.findById( req.query.id, function(err, fp) {
if(err) return next(err);
if(fp) {
if(!_.find(fp.server, { ua: ua })) {
var obj = {
ip: ip,
ua: ua,
uaParsed: uaParsed,
headers: headers
};
fp.server.push(obj);
fp.save(function(err, fp) {
if(err) console.error(err);
});
}
}
});
res.locals = {
ua: ua,
uaParsed: uaParsed,
headers: headers
};
next();
};
};

View File

@ -0,0 +1,31 @@
module.exports = function(mongoose) {
// for information generated client side
var ClientSchema = new mongoose.Schema({
ua: String,
uaParsed: {},
ip: String,
headers: {},
datestamp: { type: Date, default: Date.now },
screen: {},
modernizr: [],
navigator: {}
});
var ServerSchema = new mongoose.Schema({
ua: String,
ip: String,
datestamp: { type: Date, default: Date.now },
uaParsed: {},
headers: {},
});
var Schema = new mongoose.Schema({
_id: { type: String, required: true },
name: { type: String, required: true },
project: String,
client: [ ClientSchema ],
server: [ ServerSchema ]
});
mongoose.model('Fingerprint', Schema);
};

14
server/routes/_index.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = function(mw, epiphany) {
return [
[ 'get', '/', [ mw.ua, function pg(req, res, next) {
res.master = 'pages/index';
next();
} ] ],
[ 'get', '/admin', [ function pg(req, res, next) {
res.master = 'pages/admin';
next();
} ] ],
[ 'post', '/api/fingerprints', mw.api.fingerprints.create ],
[ 'post', '/api/fingerprints/:id', mw.api.fingerprints.push ]
];
};

7
server/server.js Normal file
View File

@ -0,0 +1,7 @@
var Epiphany = require('epiphany');
var server = new Epiphany();
server.dust.filters.jsp = function(value) {
return JSON.stringify(value, null, 2);
};

View File

@ -0,0 +1,9 @@
<div class="error row">
<div class="col">
<h1>Oops!</h1>
<p>Ett problem har tyvärr uppstått.<p>
<h2>{error.status} : {error.statusText}</h2>
<p>{error.message}<p>
<pre>{error.stack}</pre>
</div>
</div>

View File

@ -0,0 +1,23 @@
<html>
<head>
<title>The Code Bureau : Browser Fingerprint</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
</head>
<body>
<form method="post" action="/api/fingerprints">
<div>
<input type="text" name="_id" placeholder="Id" /><button type="button" class="generate">Generate</button>
</div>
<div>
<input type="text" name="name" placeholder="Name" />
</div>
<div>
<input type="text" name="project" placeholder="Project" />
</div>
<button>Submit</button>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="/js/admin.js"></script>
</body>
</html>

View File

@ -0,0 +1,52 @@
<html>
<head>
<title>The Code Bureau : Browser Fingerprint</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
html, body {
margin: 0;
padding: 0;
}
body {
padding: 15px;
}
div {
width: 100%;
overflow: hidden;
}
pre {
white-space: -moz-pre-wrap;
white-space: -o-pre-wrap;
white-space: pre-wrap;
}
button {
display: block;
margin: 10px auto;
padding: 10px 30px;
}
</style>
</head>
<body>
<div id="content">
<h1>Server</h1>
<h2>User Agent</h2>
{ua}
<pre>{uaParsed|jsp}</pre>
<h2>Headers</h2>
<pre>{headers|jsp}</pre>
<h1>Client</h1>
<noscript>No JavaScript</noscript>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"/></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
<script src="/js/app.js"></script>
</body>
</html>

27
src/js/admin.js Normal file
View File

@ -0,0 +1,27 @@
require('jquery-jsonify');
var fairdice = require('fair-dice');
$(function() {
$('form button.generate').on('click', function() {
console.log(fairdice.string());
$('form input[name="_id"]').val(fairdice.string(6));
});
$('form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
$.ajax({
url: $form.attr('action'),
dataType: 'json',
method: 'POST',
data: $form.JSONify(),
success: function(res) {
console.log('success');
console.log(res);
var path = window.location.protocol + '//' + window.location.host + '/?id=' + res._id;
$form.before('<div><h1>Added</h1><a href="' + path + '">' + path + '</a></div>');
}
});
});
});

97
src/js/app.js Normal file
View File

@ -0,0 +1,97 @@
$(function() {
var device = {};
var nav = _.clone(window.navigator);
nav.plugins = _.map(navigator.plugins, function(obj, key) {
return obj.name + ' (' + obj.filename + ')';
});
//device.navigator = _.omit(nav, [
// 'appCodeName',
// 'appMinorVersion',
// 'appName',
// 'appVersion',
// 'battery',
// 'cpuClass',
// 'geolocation',
// 'mimeTypes',
// 'oscpu',
// 'platform',
// 'product',
// 'vendor',
// 'vendorSub'
//]);
device.navigator = _.pick(nav, [
'cookieEnabled',
'doNotTrack',
'hardwareConcurrency',
'language',
'languages',
'maxTouchPoints',
'onLine',
'plugins',
'productSub',
'browserLanguage',
'systemLanguage',
'userLanguage',
'userAgent'
]);
device.screen = _.pick(window.screen, [
"availHeight",
"availLeft",
"availTop",
"availWidth",
"colorDepth",
"height",
"pixelDepth",
"width"
]);
device.screen.body = _.pick(document.body, 'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight', 'scrollWidth', 'scrollHeight');
device.screen.pixelRatio = window.devicePixelRatio;
var orientation = window.screen.orientation || window.screen.mozOrientation || window.screen.msOrientation;
if(!_.isString(orientation))
orientation = _.pick(orientation, [ 'type', 'angle' ]);
device.screen.orientation = orientation;
device.modernizr = _.compact($(document.documentElement).attr('class').split(' '));
//nav.javaEnabled = nav.javaEnabled();
var $div = $('#content');
_.each(device, function(value, key) {
$div.append($('<h2>' + key + '</h2>'));
var $pre = $('<pre id="' + key + '">');
$pre.text(JSON.stringify(value, null, 2));
$div.append($pre);
});
// TODO maybe have server send Headers from AJAX request
var id = window.location.search ? window.location.search.split('=')[1] : 'null';
var path = window.location.protocol + '//' + window.location.host + '/api/fingerprints/' + id;
console.log(path);
$.ajax({
method: 'POST',
dataType: 'json',
data: device,
url: path,
success: function(res) {
var key = 'headers';
$div.append($('<h2>' + key + '</h2>'));
var $pre = $('<pre id="' + key + '">');
$pre.text(JSON.stringify(res.headers, null, 2));
$div.append($pre);
console.log('success');
},
error: function() {
console.log('error');
}
});
});