fst – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | #!/usr/bin/env node |
2 | |||
3 | const MetricsData = require('./MetricsData.js') |
||
4 | const SyncData = require('./SyncData.js') |
||
5 | const symmetric = require('./symmetric.js') |
||
6 | const fs = require('fs') |
||
7 | const path = require('path') |
||
8 | const YAML = require('yamljs') |
||
9 | const { createLogger, format, transports } = require('winston') |
||
10 | const net = require('net') |
||
11 | const express = require('express') |
||
12 | const whois = require('whois') |
||
13 | const marked = require('marked') |
||
14 | const passport = require('passport') |
||
15 | const Strategy = require('passport-local').Strategy |
||
16 | |||
17 | // Set options for marked.js |
||
18 | marked.setOptions({ |
||
19 | renderer: new marked.Renderer(), |
||
20 | highlight: function (code) { |
||
21 | return require('highlight.js').highlightAuto(code).value |
||
22 | }, |
||
23 | pedantic: false, |
||
24 | gfm: true, |
||
25 | tables: true, |
||
26 | breaks: true, |
||
27 | sanitize: false, |
||
28 | smartLists: true, |
||
29 | smartypants: false, |
||
30 | xhtml: false |
||
31 | }) |
||
32 | |||
33 | // Load configuration file. |
||
34 | const config = YAML.load( |
||
35 | path.join( |
||
36 | path.dirname( |
||
37 | require.main.filename |
||
38 | ), |
||
39 | 'config.yml' |
||
40 | ) |
||
41 | ) |
||
42 | |||
43 | // Set up logger. |
||
44 | const logger = createLogger({ |
||
45 | format: format.combine( |
||
46 | format.splat(), |
||
47 | format.simple() |
||
48 | ), |
||
49 | transports: [ |
||
50 | new transports.Console(), |
||
51 | new transports.File({ filename: config.log }) |
||
52 | ] |
||
53 | }) |
||
54 | |||
55 | // Initialize the metrics object. |
||
56 | var metrics |
||
57 | try { |
||
58 | const store = fs.readFileSync(config.metrics.store) |
||
59 | metrics = MetricsData.fromJSON(store) |
||
60 | logger.log('info', 'metrics store has been loaded %s', config.metrics.store) |
||
61 | } catch (error) { |
||
62 | logger.info('store file not found, starting fresh') |
||
63 | metrics = new MetricsData() |
||
64 | } |
||
65 | |||
66 | // Create socket server to listen for payloads. |
||
67 | var server = net.createServer({ host: config.metrics.host, port: config.metrics.port }, function (stream) { |
||
68 | stream.on('data', processPayload) |
||
69 | }).on('listening', function () { |
||
70 | logger.info('metrics server running, waiting for payloads') |
||
71 | }).on('error', function (error) { |
||
72 | logger.log('error', 'metrics server could not listen on configured host %s and port %s', |
||
73 | config.metrics.host, |
||
74 | config.metrics.port |
||
75 | ) |
||
76 | }) |
||
77 | |||
78 | // Listen on the configured socket. |
||
79 | server.listen(config.metrics.port, config.metrics.host) |
||
80 | |||
81 | // Initialize passport authentication. |
||
82 | passport.use(new Strategy(passportStrategy)) |
||
83 | passport.serializeUser(passportSerializeUser) |
||
84 | passport.deserializeUser(passportDeserializeUser) |
||
85 | |||
86 | // Set up web server. |
||
87 | const app = express() |
||
88 | |||
89 | // Configure view engine to render EJS templates. |
||
90 | app.set('views', config.metrics.wwwroot) |
||
91 | app.set('view engine', 'ejs') |
||
92 | |||
93 | // Static files |
||
94 | app.use(express.static(config.metrics.wwwroot)) |
||
95 | |||
96 | // Use application-level middleware for common functionality, including |
||
97 | // logging, parsing, and session handling. |
||
98 | app.use(require('cookie-parser')()) |
||
99 | app.use(require('body-parser').urlencoded({ extended: true })) |
||
100 | app.use(require('express-session')({ secret: 'shazam', resave: false, saveUninitialized: false })) |
||
101 | |||
102 | // Initialize Passport and restore authentication state, if any, from the |
||
103 | // session. |
||
104 | app.use(passport.initialize()) |
||
105 | app.use(passport.session()) |
||
106 | |||
107 | app.listen(config.metrics.wwwport, () => { |
||
108 | logger.log('info', 'web interface listening on port %s', config.metrics.wwwport) |
||
109 | }) |
||
110 | |||
111 | // Login, logout and user processing. |
||
112 | app.get('/', |
||
113 | (req, res) => { |
||
114 | res.render('login', { user: req.user }) |
||
115 | }) |
||
116 | |||
117 | app.get('/login', |
||
118 | (req, res) => { |
||
119 | res.render('login') |
||
120 | }) |
||
121 | |||
122 | app.post('/login', |
||
123 | passport.authenticate('local', { failureRedirect: '/unauthorized' }), |
||
124 | (req, res) => { |
||
125 | res.render('index') |
||
126 | }) |
||
127 | |||
128 | app.get('/logout', |
||
129 | (req, res) => { |
||
130 | req.logout() |
||
131 | res.redirect('/') |
||
132 | }) |
||
133 | |||
134 | app.get('/unauthorized', |
||
135 | (req, res) => { |
||
136 | res.render('unauthorized') |
||
137 | }) |
||
138 | |||
139 | // All views. |
||
140 | // All EJS: fs.readdirSync(config.metrics.wwwroot).filter((view) => /\.ejs$/.test(view)).map((view) => `/${view.replace(/\.ejs$/gm, '')}`) |
||
141 | app.get('/index', |
||
142 | require('connect-ensure-login').ensureLoggedIn(), |
||
143 | (req, res) => { |
||
144 | const view = req.originalUrl.replace(/^\//gm, '') |
||
145 | res.render(view, { user: req.user, view: view }) |
||
146 | }) |
||
147 | |||
148 | // Send metrics. |
||
149 | app.get('/metrics', |
||
150 | require('connect-ensure-login').ensureLoggedIn(), |
||
151 | (req, res) => |
||
152 | res.send(JSON.stringify(metrics))) |
||
153 | |||
154 | // Process whois requests. |
||
155 | app.get('/whois/:host', |
||
156 | require('connect-ensure-login').ensureLoggedIn(), |
||
157 | (req, res) => { |
||
158 | whois.lookup(req.params.host, (err, data) => { |
||
159 | if (err) { |
||
160 | res.send('Failed to retireve WHOIS information. Please try again later...') |
||
161 | return |
||
162 | } |
||
163 | res.send(marked(data)) |
||
164 | }) |
||
165 | }) |
||
166 | |||
167 | // Retrieve a list of peers. |
||
168 | app.get('/peers', |
||
169 | require('connect-ensure-login').ensureLoggedIn(), |
||
170 | (req, res) => { |
||
171 | res.send(JSON.stringify(metrics.peers)) |
||
172 | }) |
||
173 | |||
174 | function passportSerializeUser(user, cb) { |
||
175 | cb(null, user.id) |
||
176 | } |
||
177 | |||
178 | function passportDeserializeUser(id, cb) { |
||
179 | var user = config.metrics.users.filter((user) => { |
||
180 | return user.id === id |
||
181 | }).shift() |
||
182 | |||
183 | if (typeof user === 'undefined' || user === null) |
||
184 | return cb(err) |
||
185 | |||
186 | return cb(null, user) |
||
187 | } |
||
188 | |||
189 | function passportStrategy(username, password, cb) { |
||
190 | var user = config.metrics.users.filter((user) => { |
||
191 | return user.username === username && user.password === password |
||
192 | }).shift() |
||
193 | |||
194 | if (typeof user === 'undefined' || user === null) |
||
195 | return cb(null, false) |
||
196 | |||
197 | return cb(null, user) |
||
198 | } |
||
199 | |||
200 | function processPayload(message) { |
||
201 | // Decrypt the payload. |
||
202 | var decrypted = "" |
||
203 | try { |
||
204 | decrypted = symmetric.decrypt(message, config.secret) |
||
205 | } |
||
206 | catch (error) { |
||
207 | logger.log('warn', 'decrypting payload %s failed with error %s', message, error) |
||
208 | return |
||
209 | } |
||
210 | |||
211 | // Deserialize the payload. |
||
212 | var syncMessage |
||
213 | try { |
||
214 | syncMessage = SyncData.fromJSON(decrypted) |
||
215 | } catch (error) { |
||
216 | // Not a sync message so ignore it. |
||
217 | return |
||
218 | } |
||
219 | |||
220 | metrics.update(syncMessage) |
||
221 | |||
222 | var json = JSON.stringify(metrics) |
||
223 | |||
224 | logger.log('info', 'metrics updated %s', json) |
||
225 | |||
226 | fs.writeFile(config.metrics.store, json, function (error) { |
||
227 | if (error) { |
||
228 | logger.log('error', 'could not write metrics file %s', config.metrics.store) |
||
229 | } |
||
230 | }) |
||
231 | |||
232 | } |