Commit e5224df233e19b693d822ea6d60d3121d23f747a
1 parent
d44e72af69
Exists in
master
gateway name pada title signin dan hilangkan remember me
Showing 2 changed files with 1 additions and 6 deletions Inline Diff
index.js
1 | var express = require('express'); | 1 | var express = require('express'); |
2 | var app = express(); | 2 | var app = express(); |
3 | var nunjucks = require('nunjucks'); | 3 | var nunjucks = require('nunjucks'); |
4 | var passport = require('passport'); | 4 | var passport = require('passport'); |
5 | var LocalStrategy = require('passport-local').Strategy; | 5 | var LocalStrategy = require('passport-local').Strategy; |
6 | var crypto = require('crypto'); | 6 | var crypto = require('crypto'); |
7 | //var sha256sum = crypto.createHash('sha256'); | 7 | //var sha256sum = crypto.createHash('sha256'); |
8 | var fsextra = require('fs-extra'); | 8 | var fsextra = require('fs-extra'); |
9 | var strftime = require('strftime'); | 9 | var strftime = require('strftime'); |
10 | var fs = require('fs'); | 10 | var fs = require('fs'); |
11 | var ini = require('ini'); | 11 | var ini = require('ini'); |
12 | var os = require('os'); | 12 | var os = require('os'); |
13 | 13 | ||
14 | var config; | 14 | var config; |
15 | var aaa; | 15 | var aaa; |
16 | var matrix; | 16 | var matrix; |
17 | var view_path = __dirname + '/views'; | 17 | var view_path = __dirname + '/views'; |
18 | 18 | ||
19 | function loggedIn(req, res, next) { | 19 | function loggedIn(req, res, next) { |
20 | if (req.user) { | 20 | if (req.user) { |
21 | next(); | 21 | next(); |
22 | } else { | 22 | } else { |
23 | res.redirect('/login'); | 23 | res.redirect('/login'); |
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 | ||
27 | function matchedPasswordAndHash(password, hash) { | 27 | function matchedPasswordAndHash(password, hash) { |
28 | var sha256sum = crypto.createHash('sha256'); | 28 | var sha256sum = crypto.createHash('sha256'); |
29 | sha256sum.update(password); | 29 | sha256sum.update(password); |
30 | return (hash == sha256sum.digest('hex')) | 30 | return (hash == sha256sum.digest('hex')) |
31 | } | 31 | } |
32 | 32 | ||
33 | function renderConfigIndex(req, res) { | 33 | function renderConfigIndex(req, res) { |
34 | 34 | ||
35 | masked = ['config.expresso.password']; | 35 | masked = ['config.expresso.password']; |
36 | 36 | ||
37 | res.render('config.index.html', { | 37 | res.render('config.index.html', { |
38 | gateway_name: config.globals.gateway_name, | 38 | gateway_name: config.globals.gateway_name, |
39 | title: 'CONFIG', configs: config, | 39 | title: 'CONFIG', configs: config, |
40 | config_dirty: matrix.config_dirty, | 40 | config_dirty: matrix.config_dirty, |
41 | isMask: function(scope, keyword) { | 41 | isMask: function(scope, keyword) { |
42 | return masked.indexOf('config.' + scope + '.' + keyword) >= 0; | 42 | return masked.indexOf('config.' + scope + '.' + keyword) >= 0; |
43 | }, | 43 | }, |
44 | }); | 44 | }); |
45 | } | 45 | } |
46 | 46 | ||
47 | function renderConfigEdit(req, res) { | 47 | function renderConfigEdit(req, res) { |
48 | var template = "config.edit.html"; | 48 | var template = "config.edit.html"; |
49 | 49 | ||
50 | if (req.scope == 'expresso' && req.keyword == 'password') { | 50 | if (req.scope == 'expresso' && req.keyword == 'password') { |
51 | template = "config.edit.expresso.password.html"; | 51 | template = "config.edit.expresso.password.html"; |
52 | } | 52 | } |
53 | 53 | ||
54 | res.render(template, { | 54 | res.render(template, { |
55 | gateway_name: config.globals.gateway_name, | 55 | gateway_name: config.globals.gateway_name, |
56 | title: 'Edit Konfigurasi', | 56 | title: 'Edit Konfigurasi', |
57 | scope: req.scope, | 57 | scope: req.scope, |
58 | keyword: req.keyword, | 58 | keyword: req.keyword, |
59 | old_value: config[req.scope][req.keyword] | 59 | old_value: config[req.scope][req.keyword] |
60 | }); | 60 | }); |
61 | } | 61 | } |
62 | 62 | ||
63 | function renderConfigAdd(req, res){ | 63 | function renderConfigAdd(req, res){ |
64 | 64 | ||
65 | var template = 'config.add.html'; | 65 | var template = 'config.add.html'; |
66 | if (req.scope == 'products') { | 66 | if (req.scope == 'products') { |
67 | template = 'config.add.products.html'; | 67 | template = 'config.add.products.html'; |
68 | } | 68 | } |
69 | 69 | ||
70 | res.render(template, { | 70 | res.render(template, { |
71 | gateway_name: config.globals.gateway_name, | 71 | gateway_name: config.globals.gateway_name, |
72 | title: 'Tambah Item Konfigurasi', | 72 | title: 'Tambah Item Konfigurasi', |
73 | scope: req.scope | 73 | scope: req.scope |
74 | }); | 74 | }); |
75 | } | 75 | } |
76 | 76 | ||
77 | function submitConfigEdit(req, res) { | 77 | function submitConfigEdit(req, res) { |
78 | 78 | ||
79 | if (req.scope == 'expresso' && req.keyword == 'password') { | 79 | if (req.scope == 'expresso' && req.keyword == 'password') { |
80 | if (req.body.newValue != req.body.newValue2) { | 80 | if (req.body.newValue != req.body.newValue2) { |
81 | res.redirect('/config/edit/' + req.scope + '/' + req.keyword); | 81 | res.redirect('/config/edit/' + req.scope + '/' + req.keyword); |
82 | return; | 82 | return; |
83 | } | 83 | } |
84 | 84 | ||
85 | var sha256sum = crypto.createHash('sha256'); | 85 | var sha256sum = crypto.createHash('sha256'); |
86 | sha256sum.update(req.body.newValue); | 86 | sha256sum.update(req.body.newValue); |
87 | req.body.newValue = sha256sum.digest('hex'); | 87 | req.body.newValue = sha256sum.digest('hex'); |
88 | 88 | ||
89 | } else if (req.scope == 'globals' && req.keyword == 'products' && req.body.newValue.trim()) { | 89 | } else if (req.scope == 'globals' && req.keyword == 'products' && req.body.newValue.trim()) { |
90 | 90 | ||
91 | try { | 91 | try { |
92 | var unsortedString = req.body.newValue.toUpperCase().replace(/ /g, '').trim(); | 92 | var unsortedString = req.body.newValue.toUpperCase().replace(/ /g, '').trim(); |
93 | var unsortedProducts = unsortedString.split(','); | 93 | var unsortedProducts = unsortedString.split(','); |
94 | var sortedProducts = aaa.sortProductsArray(unsortedProducts); | 94 | var sortedProducts = aaa.sortProductsArray(unsortedProducts); |
95 | req.body.newValue = sortedProducts.join(','); | 95 | req.body.newValue = sortedProducts.join(','); |
96 | } | 96 | } |
97 | catch(e) { | 97 | catch(e) { |
98 | console.log('Error sorting products'); | 98 | console.log('Error sorting products'); |
99 | } | 99 | } |
100 | } | 100 | } |
101 | 101 | ||
102 | if (config[req.body.scope][req.body.keyword] != req.body.newValue.trim()) { | 102 | if (config[req.body.scope][req.body.keyword] != req.body.newValue.trim()) { |
103 | config[req.body.scope][req.body.keyword] = req.body.newValue.trim(); | 103 | config[req.body.scope][req.body.keyword] = req.body.newValue.trim(); |
104 | matrix.config_dirty = 1; | 104 | matrix.config_dirty = 1; |
105 | } | 105 | } |
106 | 106 | ||
107 | res.redirect('/config'); | 107 | res.redirect('/config'); |
108 | } | 108 | } |
109 | 109 | ||
110 | function submitConfigAdd(req, res) { | 110 | function submitConfigAdd(req, res) { |
111 | 111 | ||
112 | if (!req.body.newKeyword.trim()) { | 112 | if (!req.body.newKeyword.trim()) { |
113 | res.redirect('/config'); | 113 | res.redirect('/config'); |
114 | return; | 114 | return; |
115 | } | 115 | } |
116 | 116 | ||
117 | if (config[req.body.scope] === undefined) { | 117 | if (config[req.body.scope] === undefined) { |
118 | config[req.body.scope] = {}; | 118 | config[req.body.scope] = {}; |
119 | } | 119 | } |
120 | 120 | ||
121 | config[req.body.scope][req.body.newKeyword.trim()] = req.body.newValue.trim(); | 121 | config[req.body.scope][req.body.newKeyword.trim()] = req.body.newValue.trim(); |
122 | matrix.config_dirty = 1; | 122 | matrix.config_dirty = 1; |
123 | 123 | ||
124 | res.redirect('/config#config.' + req.body.scope + '.' + req.body.newKeyword); | 124 | res.redirect('/config#config.' + req.body.scope + '.' + req.body.newKeyword); |
125 | } | 125 | } |
126 | 126 | ||
127 | function submitConfigDelete(req, res) { | 127 | function submitConfigDelete(req, res) { |
128 | 128 | ||
129 | matrix.config_dirty = 1; | 129 | matrix.config_dirty = 1; |
130 | delete config[req.scope][req.keyword]; | 130 | delete config[req.scope][req.keyword]; |
131 | 131 | ||
132 | res.redirect('/config'); | 132 | res.redirect('/config'); |
133 | } | 133 | } |
134 | 134 | ||
135 | function submitConfigSave(req, res) { | 135 | function submitConfigSave(req, res) { |
136 | fsextra.copy('config.ini', 'config.ini.backup_' + strftime('%F_%H%M%S', new Date()), function(err) { | 136 | fsextra.copy('config.ini', 'config.ini.backup_' + strftime('%F_%H%M%S', new Date()), function(err) { |
137 | fs.writeFileSync('./config.ini', ini.stringify(config)); | 137 | fs.writeFileSync('./config.ini', ini.stringify(config)); |
138 | matrix.config_dirty = 0; | 138 | matrix.config_dirty = 0; |
139 | res.redirect('/config'); | 139 | res.redirect('/config'); |
140 | }); | 140 | }); |
141 | } | 141 | } |
142 | 142 | ||
143 | function renderLoginPage(req, res) { | 143 | function renderLoginPage(req, res) { |
144 | res.render('signin.html', {title: 'Signin'}); | 144 | res.render('signin.html', {title: config.globals.gateway_name}); |
145 | } | 145 | } |
146 | 146 | ||
147 | function renderConfigAskDelete(req, res) { | 147 | function renderConfigAskDelete(req, res) { |
148 | res.render('config.ask.delete.html', { | 148 | res.render('config.ask.delete.html', { |
149 | gateway_name: config.globals.gateway_name, | 149 | gateway_name: config.globals.gateway_name, |
150 | title: 'Konfirmasi Penghapusan', | 150 | title: 'Konfirmasi Penghapusan', |
151 | scope: req.scope, | 151 | scope: req.scope, |
152 | keyword: req.keyword, | 152 | keyword: req.keyword, |
153 | value: config[req.scope][req.keyword], | 153 | value: config[req.scope][req.keyword], |
154 | }); | 154 | }); |
155 | } | 155 | } |
156 | 156 | ||
157 | function renderDashboardIndex(req, res) { | 157 | function renderDashboardIndex(req, res) { |
158 | var template = "dashboard.index.html"; | 158 | var template = "dashboard.index.html"; |
159 | 159 | ||
160 | var os_info = { | 160 | var os_info = { |
161 | uptime: os.uptime(), | 161 | uptime: os.uptime(), |
162 | loadavg: os.loadavg(), | 162 | loadavg: os.loadavg(), |
163 | hostname: os.hostname(), | 163 | hostname: os.hostname(), |
164 | type: os.type(), | 164 | type: os.type(), |
165 | platform: os.platform(), | 165 | platform: os.platform(), |
166 | arch: os.arch(), | 166 | arch: os.arch(), |
167 | release: os.release(), | 167 | release: os.release(), |
168 | totalmem: os.totalmem(), | 168 | totalmem: os.totalmem(), |
169 | } | 169 | } |
170 | 170 | ||
171 | res.render(template, { | 171 | res.render(template, { |
172 | gateway_name: config.globals.gateway_name, | 172 | gateway_name: config.globals.gateway_name, |
173 | title: 'Dashboard', | 173 | title: 'Dashboard', |
174 | matrix: JSON.stringify(matrix, null, 2), | 174 | matrix: JSON.stringify(matrix, null, 2), |
175 | config: JSON.stringify(config, null, 2), | 175 | config: JSON.stringify(config, null, 2), |
176 | nodejs_versions: JSON.stringify(process.versions, null, 2), | 176 | nodejs_versions: JSON.stringify(process.versions, null, 2), |
177 | pendings: JSON.stringify(aaa.getPendingList(), null, 2), | 177 | pendings: JSON.stringify(aaa.getPendingList(), null, 2), |
178 | memory_usage: JSON.stringify(process.memoryUsage(), null, 2), | 178 | memory_usage: JSON.stringify(process.memoryUsage(), null, 2), |
179 | uptime: process.uptime(), | 179 | uptime: process.uptime(), |
180 | os_info: JSON.stringify(os_info, null, 2), | 180 | os_info: JSON.stringify(os_info, null, 2), |
181 | net_ifaces: JSON.stringify(os.networkInterfaces(), null, 2), | 181 | net_ifaces: JSON.stringify(os.networkInterfaces(), null, 2), |
182 | }); | 182 | }); |
183 | } | 183 | } |
184 | 184 | ||
185 | function isNoTemplateCache() { | 185 | function isNoTemplateCache() { |
186 | var retval = false; | 186 | var retval = false; |
187 | try { | 187 | try { |
188 | retval = config.expresso.no_template_cache.toUpperCase() == 'YES'; | 188 | retval = config.expresso.no_template_cache.toUpperCase() == 'YES'; |
189 | } | 189 | } |
190 | catch(err) { | 190 | catch(err) { |
191 | return retval; | 191 | return retval; |
192 | } | 192 | } |
193 | 193 | ||
194 | return retval; | 194 | return retval; |
195 | } | 195 | } |
196 | 196 | ||
197 | function createServer() { | 197 | function createServer() { |
198 | if (!config.expresso || !config.expresso.listen_port) { | 198 | if (!config.expresso || !config.expresso.listen_port) { |
199 | console.log('Not starting expresso admin UI'); | 199 | console.log('Not starting expresso admin UI'); |
200 | return; | 200 | return; |
201 | } | 201 | } |
202 | 202 | ||
203 | nunjucks.configure(view_path, { | 203 | nunjucks.configure(view_path, { |
204 | autoescape: true, | 204 | autoescape: true, |
205 | noCache: isNoTemplateCache(), | 205 | noCache: isNoTemplateCache(), |
206 | express: app | 206 | express: app |
207 | }); | 207 | }); |
208 | 208 | ||
209 | app.use(express.static(__dirname + '/public')); | 209 | app.use(express.static(__dirname + '/public')); |
210 | app.use(require('cookie-parser')()); | 210 | app.use(require('cookie-parser')()); |
211 | app.use(require('body-parser').urlencoded({ extended: true })); | 211 | app.use(require('body-parser').urlencoded({ extended: true })); |
212 | app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false })); | 212 | app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false })); |
213 | app.use(passport.initialize()); | 213 | app.use(passport.initialize()); |
214 | app.use(passport.session()); | 214 | app.use(passport.session()); |
215 | 215 | ||
216 | passport.use(new LocalStrategy( | 216 | passport.use(new LocalStrategy( |
217 | function(username, password, done) { | 217 | function(username, password, done) { |
218 | 218 | ||
219 | if (username == 'admin' && matchedPasswordAndHash(password, config.expresso.password)) { | 219 | if (username == 'admin' && matchedPasswordAndHash(password, config.expresso.password)) { |
220 | var user = { | 220 | var user = { |
221 | username: username, | 221 | username: username, |
222 | last_login: Date.now() / 1000 | 0 | 222 | last_login: Date.now() / 1000 | 0 |
223 | } | 223 | } |
224 | 224 | ||
225 | return done(null, user); | 225 | return done(null, user); |
226 | } | 226 | } |
227 | 227 | ||
228 | return done(null, false, { message: 'Incorrect password.' }); | 228 | return done(null, false, { message: 'Incorrect password.' }); |
229 | } | 229 | } |
230 | )); | 230 | )); |
231 | 231 | ||
232 | passport.serializeUser(function(user, done) { | 232 | passport.serializeUser(function(user, done) { |
233 | done(null, user.username); | 233 | done(null, user.username); |
234 | }); | 234 | }); |
235 | 235 | ||
236 | passport.deserializeUser(function(username, done) { | 236 | passport.deserializeUser(function(username, done) { |
237 | var user = { | 237 | var user = { |
238 | username: username | 238 | username: username |
239 | }; | 239 | }; |
240 | done(null, user); | 240 | done(null, user); |
241 | }); | 241 | }); |
242 | 242 | ||
243 | app.get('/', loggedIn, function(req, res) { res.redirect('/dashboard'); }); | 243 | app.get('/', loggedIn, function(req, res) { res.redirect('/dashboard'); }); |
244 | app.get('/config', loggedIn, renderConfigIndex); | 244 | app.get('/config', loggedIn, renderConfigIndex); |
245 | app.get('/login', renderLoginPage); | 245 | app.get('/login', renderLoginPage); |
246 | app.post('/login', passport.authenticate('local', { successRedirect: '/dashboard', failureRedirect: '/login' })); | 246 | app.post('/login', passport.authenticate('local', { successRedirect: '/dashboard', failureRedirect: '/login' })); |
247 | app.get('/logout', function(req, res){ | 247 | app.get('/logout', function(req, res){ |
248 | req.logout(); | 248 | req.logout(); |
249 | res.redirect('/login'); | 249 | res.redirect('/login'); |
250 | }); | 250 | }); |
251 | 251 | ||
252 | app.param('scope', function(req, res, next, value) { | 252 | app.param('scope', function(req, res, next, value) { |
253 | req.scope = value; | 253 | req.scope = value; |
254 | next(); | 254 | next(); |
255 | }); | 255 | }); |
256 | 256 | ||
257 | app.param('keyword', function(req, res, next, value) { | 257 | app.param('keyword', function(req, res, next, value) { |
258 | req.keyword = value; | 258 | req.keyword = value; |
259 | next(); | 259 | next(); |
260 | }); | 260 | }); |
261 | 261 | ||
262 | app.get('/config/edit/:scope/:keyword', loggedIn, renderConfigEdit); | 262 | app.get('/config/edit/:scope/:keyword', loggedIn, renderConfigEdit); |
263 | app.post('/config/edit/:scope/:keyword', loggedIn, submitConfigEdit); | 263 | app.post('/config/edit/:scope/:keyword', loggedIn, submitConfigEdit); |
264 | 264 | ||
265 | app.get('/config/ask-delete/:scope/:keyword', loggedIn, renderConfigAskDelete); | 265 | app.get('/config/ask-delete/:scope/:keyword', loggedIn, renderConfigAskDelete); |
266 | app.get('/config/delete/:scope/:keyword', loggedIn, submitConfigDelete); | 266 | app.get('/config/delete/:scope/:keyword', loggedIn, submitConfigDelete); |
267 | 267 | ||
268 | app.get('/config/add/:scope', loggedIn, renderConfigAdd); | 268 | app.get('/config/add/:scope', loggedIn, renderConfigAdd); |
269 | app.post('/config/add/:scope', loggedIn, submitConfigAdd); | 269 | app.post('/config/add/:scope', loggedIn, submitConfigAdd); |
270 | 270 | ||
271 | app.get('/config/save', loggedIn, submitConfigSave); | 271 | app.get('/config/save', loggedIn, submitConfigSave); |
272 | 272 | ||
273 | app.get('/dashboard', loggedIn, renderDashboardIndex); | 273 | app.get('/dashboard', loggedIn, renderDashboardIndex); |
274 | 274 | ||
275 | var server = app.listen(config.expresso.listen_port, function () { | 275 | var server = app.listen(config.expresso.listen_port, function () { |
276 | var host = server.address().address; | 276 | var host = server.address().address; |
277 | var port = server.address().port; | 277 | var port = server.address().port; |
278 | 278 | ||
279 | console.log('Expresso admin UI listening at http://%s:%s', host, port); | 279 | console.log('Expresso admin UI listening at http://%s:%s', host, port); |
280 | }); | 280 | }); |
281 | } | 281 | } |
282 | 282 | ||
283 | function sanitizeMatrix(matrix) { | 283 | function sanitizeMatrix(matrix) { |
284 | if (matrix === undefined) { | 284 | if (matrix === undefined) { |
285 | matrix = {}; | 285 | matrix = {}; |
286 | } | 286 | } |
287 | if (matrix.config_dirty === undefined) { | 287 | if (matrix.config_dirty === undefined) { |
288 | matrix.config_dirty = 0; | 288 | matrix.config_dirty = 0; |
289 | } | 289 | } |
290 | } | 290 | } |
291 | 291 | ||
292 | function start(options) { | 292 | function start(options) { |
293 | if (options['config']) { | 293 | if (options['config']) { |
294 | config = options['config']; | 294 | config = options['config']; |
295 | } | 295 | } |
296 | 296 | ||
297 | if (options['matrix']) { | 297 | if (options['matrix']) { |
298 | matrix = options['matrix']; | 298 | matrix = options['matrix']; |
299 | } | 299 | } |
300 | sanitizeMatrix(matrix); | 300 | sanitizeMatrix(matrix); |
301 | 301 | ||
302 | if (options['aaa']) { | 302 | if (options['aaa']) { |
303 | aaa = options['aaa']; | 303 | aaa = options['aaa']; |
304 | } | 304 | } |
305 | 305 | ||
306 | if (options['expresso_views']) { | 306 | if (options['expresso_views']) { |
307 | view_path = options['expresso_views']; | 307 | view_path = options['expresso_views']; |
308 | } | 308 | } |
309 | 309 | ||
310 | createServer(); | 310 | createServer(); |
311 | } | 311 | } |
312 | 312 | ||
313 | exports.start = start; | 313 | exports.start = start; |
314 | 314 |
views/signin.html
1 | <!DOCTYPE html> | 1 | <!DOCTYPE html> |
2 | <html lang="en"> | 2 | <html lang="en"> |
3 | <head> | 3 | <head> |
4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
5 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | 5 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
6 | <meta name="viewport" content="width=device-width, initial-scale=1"> | 6 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
7 | <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> | 7 | <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> |
8 | <meta name="description" content=""> | 8 | <meta name="description" content=""> |
9 | <meta name="author" content=""> | 9 | <meta name="author" content=""> |
10 | <link rel="icon" href="../../favicon.ico"> | 10 | <link rel="icon" href="../../favicon.ico"> |
11 | 11 | ||
12 | <title>{{ title }}</title> | 12 | <title>{{ title }}</title> |
13 | 13 | ||
14 | <!-- Bootstrap core CSS --> | 14 | <!-- Bootstrap core CSS --> |
15 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> | 15 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> |
16 | 16 | ||
17 | <!-- Custom styles for this template --> | 17 | <!-- Custom styles for this template --> |
18 | <link href="/css/signin.css" rel="stylesheet"> | 18 | <link href="/css/signin.css" rel="stylesheet"> |
19 | 19 | ||
20 | <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> | 20 | <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> |
21 | <!--[if lt IE 9]> | 21 | <!--[if lt IE 9]> |
22 | <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | 22 | <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> |
23 | <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | 23 | <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> |
24 | <![endif]--> | 24 | <![endif]--> |
25 | </head> | 25 | </head> |
26 | 26 | ||
27 | <body> | 27 | <body> |
28 | 28 | ||
29 | <div class="container"> | 29 | <div class="container"> |
30 | 30 | ||
31 | <form class="form-signin" method="POST"> | 31 | <form class="form-signin" method="POST"> |
32 | <h2 class="form-signin-heading">Please sign in</h2> | 32 | <h2 class="form-signin-heading">Please sign in</h2> |
33 | <label for="inputUsername" class="sr-only">Username</label> | 33 | <label for="inputUsername" class="sr-only">Username</label> |
34 | <input name="username" type="text" id="inputUsername" class="form-control" placeholder="Username" required autofocus> | 34 | <input name="username" type="text" id="inputUsername" class="form-control" placeholder="Username" required autofocus> |
35 | <label for="inputPassword" class="sr-only">Password</label> | 35 | <label for="inputPassword" class="sr-only">Password</label> |
36 | <input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required> | 36 | <input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required> |
37 | <div class="checkbox"> | ||
38 | <label> | ||
39 | <input type="checkbox" value="remember-me"> Remember me | ||
40 | </label> | ||
41 | </div> | ||
42 | <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> | 37 | <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> |
43 | </form> | 38 | </form> |
44 | 39 | ||
45 | </div> <!-- /container --> | 40 | </div> <!-- /container --> |
46 | 41 | ||
47 | </body> | 42 | </body> |
48 | </html> | 43 | </html> |
49 | 44 |