зеркало из
1
0
Форкнуть 0

refactor(everything): everything was refactored

This commit is contained in:
Vlad Filippov 2016-01-22 20:35:32 -05:00
Родитель b3defa6655
Коммит f73a0101da
16 изменённых файлов: 267 добавлений и 145 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -1,3 +1,2 @@
node_modules node_modules
public/out.json
*.log *.log

8
README.md Normal file
Просмотреть файл

@ -0,0 +1,8 @@
# fxa-telemetry-dashboard
## Usage
```
npm install
npm start
```

23
bin/server.js Normal file
Просмотреть файл

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var path = require('path');
var config = require('../lib/config');
var dashboards = require('../lib/dashboards')();
var app = require('../lib/express')();
var routesAuth = require('../lib/routes/auth');
app.use('/auth', routesAuth);
app.use('/static_secure/out.json', function(req, res, next){
if(req.session && req.session.email){
return res.sendFile(path.resolve(__dirname + '/../static_secure/out.json'));
} else {
res.sendStatus(403);
}
});
var port = config.get('port');
app.listen(port);
console.log('Started on port:', port);

4
config/local.json Normal file
Просмотреть файл

@ -0,0 +1,4 @@
{
"google_oauth_client_id": "501118371406-njrjt9r48obid0nng4mdr83afvspgl6n.apps.googleusercontent.com",
"session_secret": "secret"
}

3
config/production.json Normal file
Просмотреть файл

@ -0,0 +1,3 @@
{
"google_oauth_client_id": "501118371406-3nuvgor9inia5mqmmm24ff1pmjss2j84.apps.googleusercontent.com",
}

106
index.js
Просмотреть файл

@ -1,106 +0,0 @@
var Xray = require('x-ray');
var JWTool = require('fxa-jwtool');
var async = require('async');
var _ = require('lodash');
var fs = require('fs');
var dashboards = require('./dashboards');
var x = Xray();
var findOutput = function (book, cb) {
var error;
try {
x(book.url, '.output_png img@src')(function(err, contents) {
error = err;
cb(null, contents);
})
} catch (e) {
error = e;
}
if (error) {
console.log(error, 'for', book.url);
cb(null);
}
}
async.map(dashboards, findOutput, function(err, results){
var finalResults = results.filter(Boolean);
var output = {}
var output = _.extend({}, finalResults);
if (! err && results) {
fs.writeFile('static_secure/out.json', JSON.stringify(output))
}
});
var express = require('express')
var serveStatic = require('serve-static')
var bodyParser = require('body-parser')
var session = require('express-session')
var app = express()
app.use(session({ secret: 'todosecret', cookie: { maxAge: 60000 }}))
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());
app.use(serveStatic('static/', {'index': ['index.html']}))
app.use('/static_secure/out.json', function(req, res, next){
if(req.session && req.session.email){
return res.sendFile(__dirname + '/static_secure/out.json');
} else {
res.sendStatus(403);
}
});
var jwtool = new JWTool(['https://www.googleapis.com/oauth2/v3/certs'])
app.post('/api/auth', function (req, res) {
if (! req.body.idtoken) {
return res.send(401)
}
// Verify the idtoken's (JWT) signature with the key set from the configured JKU.
// (Google's jwt include a `kid` but no `jku`)
jwtool.verify(req.body.idtoken, { jku: 'https://www.googleapis.com/oauth2/v3/certs' })
.then(
function (data) {
// ensure the token meets all of our criteria
if (
data.aud === '501118371406-3nuvgor9inia5mqmmm24ff1pmjss2j84.apps.googleusercontent.com'
&& data.exp > (Date.now() / 1000)
&& data.hd === 'mozilla.com'
) {
// set a cookie for authenticating against our other endpoints
req.session.email = data.email
res.send(data)
}
else {
// this user is not authorized
res.sendStatus(401)
}
},
function (err) {
// the token was not valid
res.send(500, err)
}
)
})
app.get('/config', function (req, res) {
res.type('application/javascript')
res.send('var client_id = "501118371406-3nuvgor9inia5mqmmm24ff1pmjss2j84.apps.googleusercontent.com"')
});
app.post('/api/logout', function(req, res) {
if (req.session) {
req.session.email = null;
}
res.sendStatus(200);
});
app.listen(10157)
console.log('Started on port 10157!')

61
lib/config.js Normal file
Просмотреть файл

@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var convict = require('convict');
var fs = require('fs');
var path = require('path');
var conf = module.exports = convict({
google_oauth_client_id: {
default: '501118371406-3nuvgor9inia5mqmmm24ff1pmjss2j84.apps.googleusercontent.com',
doc: 'Google OAuth Client ID',
format: String,
},
session_secret: {
default: '',
doc: 'Session Secret',
format: String,
},
env: {
default: 'production',
doc: 'What environment are we running in? Note: all hosted environments are \'production\'.',
env: 'NODE_ENV',
format: [
'production',
'development'
]
},
port: {
default: 10157,
doc: 'Server Port',
env: 'PORT',
format: 'port',
},
});
var DEV_CONFIG_PATH = path.join(__dirname, '..', 'config', 'local.json');
var files;
// handle configuration files. you can specify a CSV list of configuration
// files to process, which will be overlayed in order, in the CONFIG_FILES
// environment variable
if (process.env.CONFIG_FILES && process.env.CONFIG_FILES.trim() !== '') {
files = process.env.CONFIG_FILES.split(',');
} else if (fs.existsSync(DEV_CONFIG_PATH)) {
files = [ DEV_CONFIG_PATH ];
}
if (files) {
conf.loadFile(files);
}
if (! process.env.NODE_ENV) {
process.env.NODE_ENV = conf.get('env');
}
var options = {
strict: true
};
conf.validate(options);

44
lib/dashboards.js Normal file
Просмотреть файл

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var _ = require('lodash');
var async = require('async');
var fs = require('fs');
var Xray = require('x-ray');
var dashboards = require('../dashboards');
var findOutput = function (book, cb) {
var x = Xray();
var error;
try {
x(book.url, '.output_png img@src')(function(err, contents) {
error = err;
cb(null, contents);
})
} catch (e) {
error = e;
}
if (error) {
console.log(error, 'for', book.url);
cb(null);
}
}
module.exports = function generateDashboardOutput() {
console.log('Fetching dashboard data...');
async.map(dashboards, findOutput, function(err, results){
var finalResults = results.filter(Boolean);
var output = {};
var output = _.extend({}, finalResults);
if (! err && results) {
console.log('Writing dashboard data...');
fs.writeFile('static_secure/out.json', JSON.stringify(output))
} else {
console.log(err);
}
});
}

37
lib/express.js Normal file
Просмотреть файл

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var express = require('express');
var serveStatic = require('serve-static');
var bodyParser = require('body-parser');
var session = require('express-session');
var config = require('./config');
module.exports = function configureExpress() {
var app = express();
if (! config.get('session_secret')) {
throw new Error('Set a session secret in configuration');
}
app.use(session({
secret: config.get('session_secret'),
cookie: {
maxAge: 60000,
},
saveUninitialized: true,
resave: false,
}));
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(serveStatic('static/', {'index': ['index.html']}))
return app;
};

53
lib/routes/auth.js Normal file
Просмотреть файл

@ -0,0 +1,53 @@
var express = require('express');
var JWTool = require('fxa-jwtool');
var GOOGLE_API_JKU = 'https://www.googleapis.com/oauth2/v3/certs';
var config = require('../config');
var router = express.Router();
var jwtool = new JWTool([GOOGLE_API_JKU])
router.post('/login', function (req, res) {
if (!req.body.idtoken) {
return res.send(401)
}
// Verify the idtoken's (JWT) signature with the key set from the configured JKU.
// (Google's jwt include a `kid` but no `jku`)
jwtool.verify(req.body.idtoken, {jku: GOOGLE_API_JKU})
.then(
function (data) {
// ensure the token meets all of our criteria
if (
data.aud === config.get('google_oauth_client_id')
&& data.exp > (Date.now() / 1000)
&& data.hd === 'mozilla.com'
) {
// set a cookie for authenticating against our other endpoints
req.session.email = data.email
res.send(data)
}
else {
// this user is not authorized
res.sendStatus(401)
}
},
function (err) {
// the token was not valid
res.send(500, err)
}
)
});
router.get('/config', function (req, res) {
res.type('application/javascript');
res.send('var client_id = "'+ config.get('google_oauth_client_id') + '";');
});
router.post('/logout', function (req, res) {
if (req.session) {
req.session.email = null;
}
res.sendStatus(200);
});
module.exports = router;

Просмотреть файл

@ -1,22 +1,30 @@
{ {
"name": "fxa-telemetry-dashboard", "name": "fxa-telemetry-dashboard",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "FxA Metrics Dashboard",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "node index.js", "start": "nodemon -e js bin/server.js",
"test": "echo \"Error: no test specified\" && exit 1" "start-production": "CONFIG_FILES=config/production.json node bin/server.js"
}, },
"author": "", "repository": {
"type": "git",
"url": "https://github.com/mozilla/fxa-telemetry-dashboard"
},
"author": "Mozilla (https://mozilla.org/)",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"async": "1.5.0", "async": "1.5.0",
"body-parser": "^1.14.2", "body-parser": "1.14.2",
"convict": "1.0.2",
"express": "4.13.3", "express": "4.13.3",
"express-session": "^1.13.0", "express-session": "1.13.0",
"fxa-jwtool": "^0.7.2", "fxa-jwtool": "0.7.2",
"lodash": "3.10.1", "lodash": "3.10.1",
"serve-static": "1.10.0", "serve-static": "1.10.0",
"x-ray": "2.0.2" "x-ray": "2.0.2"
},
"devDependencies": {
"nodemon": "1.8.1"
} }
} }

Просмотреть файл

@ -6,8 +6,6 @@
body { body {
border-left: 22px solid orange; border-left: 22px solid orange;
padding-bottom: 1000px;
overflow: scroll;
} }
h1 { h1 {
@ -25,7 +23,7 @@ button {
margin: 10px; margin: 10px;
} }
img { #placeholder img {
max-width: 400px; max-width: 400px;
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
@ -33,24 +31,6 @@ img {
background: white; background: white;
} }
img { .lg-outer * {
-webkit-transition: all 1s ease; text-align: left;
-moz-transition: all 1s ease; }
-o-transition: all 1s ease;
-ms-transition: all 1s ease;
transition: all 1s ease;
}
img:hover {
-webkit-transform:scale(2.25);
-moz-transform:scale(2.25);
-ms-transform:scale(2.25);
-o-transform:scale(2.25);
transform:scale(2.25);
-webkit-transform-origin : left top;
-moz-transform-origin : left top;
-o-transform-origin : left top;
-ms-transform-origin : left top;
transform-origin : left top;
border: 2px solid orange;
}

Просмотреть файл

@ -7,6 +7,8 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.2.14/css/lightgallery.min.css">
<link rel="stylesheet" href="index.css"> <link rel="stylesheet" href="index.css">
</head> </head>
<body> <body>
@ -17,8 +19,9 @@
<div id="placeholder"><img id="spinner" style="display: none" src="spinner.gif"/></div> <div id="placeholder"><img id="spinner" style="display: none" src="spinner.gif"/></div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/1.2.14/js/lightgallery-all.min.js"></script>
<script src="https://apis.google.com/js/platform.js"></script> <script src="https://apis.google.com/js/platform.js"></script>
<script src="/config"></script> <script src="/auth/config"></script>
<script src="index.js"></script> <script src="index.js"></script>
</body> </body>

Просмотреть файл

@ -1,8 +1,10 @@
;window.loggedInEmail = null ;window.loggedInEmail = null
var PLACEHOLDER = $('#placeholder');
function logout() { function logout() {
$.post('/api/logout'); $.post('/auth/logout');
$('#placeholder').html(''); PLACEHOLDER.html('');
} }
function updateUI(data) { function updateUI(data) {
@ -18,7 +20,10 @@ function updateUI(data) {
items.push('<a target="_blank" href="' + val + '"><img src="' + val + '" id="dash"' + key + '"/></a>'); items.push('<a target="_blank" href="' + val + '"><img src="' + val + '" id="dash"' + key + '"/></a>');
}); });
$('#placeholder').html(items) PLACEHOLDER.html(items)
PLACEHOLDER.lightGallery({
thumbnail:true
});
}); });
} else { } else {
@ -35,11 +40,10 @@ function signInChanged(signedIn) {
function userChanged(user) { function userChanged(user) {
var id_token = user.getAuthResponse().id_token var id_token = user.getAuthResponse().id_token
console.log('user changed: ' + id_token)
if (id_token) { if (id_token) {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '/api/auth', // this creates a cookie used to authenicate other api requests url: '/auth/login', // this creates a cookie used to authenicate other api requests
data: 'idtoken=' + id_token, data: 'idtoken=' + id_token,
contentType: 'application/x-www-form-urlencoded', contentType: 'application/x-www-form-urlencoded',
dataType: 'json', dataType: 'json',

1
static_secure/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
*

Различия файлов скрыты, потому что одна или несколько строк слишком длинны