Chapter 11: initial samples
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
var createError = require("http-errors");
|
||||||
|
var express = require("express");
|
||||||
|
var path = require("path");
|
||||||
|
var cookieParser = require("cookie-parser");
|
||||||
|
var logger = require("morgan");
|
||||||
|
|
||||||
|
var indexRouter = require("./routes/index");
|
||||||
|
var usersRouter = require("./routes/users");
|
||||||
|
var inventoryRouter = require("./routes/inventory");
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set("views", path.join(__dirname, "views"));
|
||||||
|
app.set("view engine", "ejs");
|
||||||
|
|
||||||
|
app.use(logger("dev"));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
|
app.use("/", indexRouter);
|
||||||
|
app.use("/users", usersRouter);
|
||||||
|
app.use("/inventory", inventoryRouter);
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.render("error");
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
|
console.log("Unhandled Rejection at:", promise, "reason:", reason);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
Executable
+90
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('bookstore-web-app:server');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
var createError = require('http-errors');
|
||||||
|
var express = require('express');
|
||||||
|
var path = require('path');
|
||||||
|
var cookieParser = require('cookie-parser');
|
||||||
|
var logger = require('morgan');
|
||||||
|
|
||||||
|
var indexRouter = require('./routes/index');
|
||||||
|
var usersRouter = require('./routes/users');
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
|
app.use(logger('dev'));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
app.use('/', indexRouter);
|
||||||
|
app.use('/users', usersRouter);
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function(req, res, next) {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function(err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||||
|
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.render('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('bookstore-web-appcd:server');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "bookstore-web-appcd",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"ejs": "~2.6.1",
|
||||||
|
"express": "~4.16.1",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"morgan": "~1.9.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.render('index', { title: 'Express' });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET users listing. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<h1><%= message %></h1>
|
||||||
|
<h2><%= error.status %></h2>
|
||||||
|
<pre><%= error.stack %></pre>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= title %></title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><%= title %></h1>
|
||||||
|
<p>Welcome to <%= title %></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+409
@@ -0,0 +1,409 @@
|
|||||||
|
{
|
||||||
|
"name": "bookstore-web-app",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.24",
|
||||||
|
"negotiator": "0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
|
"basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
|
||||||
|
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"iconv-lite": "0.4.23",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"qs": "6.5.2",
|
||||||
|
"raw-body": "2.3.3",
|
||||||
|
"type-is": "~1.6.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-parser": {
|
||||||
|
"version": "1.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||||
|
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||||
|
"requires": {
|
||||||
|
"cookie": "0.4.0",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"ejs": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q=="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.16.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
|
||||||
|
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.5",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.18.3",
|
||||||
|
"content-disposition": "0.5.2",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.3.1",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "1.1.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.4",
|
||||||
|
"qs": "6.5.2",
|
||||||
|
"range-parser": "~1.2.0",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"send": "0.16.2",
|
||||||
|
"serve-static": "1.13.2",
|
||||||
|
"setprototypeof": "1.1.0",
|
||||||
|
"statuses": "~1.4.0",
|
||||||
|
"type-is": "~1.6.16",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"statuses": "~1.4.0",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||||
|
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.0",
|
||||||
|
"statuses": ">= 1.4.0 < 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.44.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"morgan": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "~2.0.0",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"on-headers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "~0.1.2",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
|
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||||
|
},
|
||||||
|
"range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"http-errors": "1.6.3",
|
||||||
|
"iconv-lite": "0.4.23",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.16.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
|
||||||
|
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"destroy": "~1.0.4",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "~1.6.2",
|
||||||
|
"mime": "1.4.1",
|
||||||
|
"ms": "2.0.0",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"range-parser": "~1.2.0",
|
||||||
|
"statuses": "~1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve-static": {
|
||||||
|
"version": "1.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
|
||||||
|
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"send": "0.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "bookstore-web-app",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"ejs": "~2.6.1",
|
||||||
|
"express": "~4.16.1",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"morgan": "~1.9.1",
|
||||||
|
"node-fetch": "^2.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.render('index', { title: 'Express' });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
const { Router } = require("express");
|
||||||
|
const fetch = require("node-fetch");
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/", function (req, res) {
|
||||||
|
fetch("http://localhost:3000/books")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) =>
|
||||||
|
res.render("inventory", {
|
||||||
|
books: json,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
res.render("error", {
|
||||||
|
error: error,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/add", function (req, res) {
|
||||||
|
console.log(req.body);
|
||||||
|
|
||||||
|
fetch("http://localhost:3000/books", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(req.body),
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
})
|
||||||
|
.then(res.redirect("/inventory"))
|
||||||
|
.catch((err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET users listing. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<h1><%= message %></h1>
|
||||||
|
<h2><%= error.status %></h2>
|
||||||
|
<pre><%= error.stack %></pre>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= title %></title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><%= title %></h1>
|
||||||
|
<p>Welcome to <%= title %></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Book Inventory</title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Book Inventory</h1>
|
||||||
|
<ul>
|
||||||
|
<% for(let book of books) { %>
|
||||||
|
<li><%= book.title %> - <%= book.author %></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Add Book:</h2>
|
||||||
|
<form action="/inventory/add" method="POST">
|
||||||
|
<label for="title">Title</label>
|
||||||
|
<input type="text" name="title" />
|
||||||
|
<label for="author">Author</label>
|
||||||
|
<input type="text" name="author" />
|
||||||
|
<button type="submit" value="Submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"git.ignoreLimitWarning": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
var createError = require('http-errors');
|
||||||
|
var express = require('express');
|
||||||
|
var path = require('path');
|
||||||
|
var cookieParser = require('cookie-parser');
|
||||||
|
var logger = require('morgan');
|
||||||
|
|
||||||
|
var indexRouter = require('./routes/index');
|
||||||
|
var usersRouter = require('./routes/users');
|
||||||
|
var inventoryRouter = require('./routes/inventory');
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// view engine setup
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
|
app.use(logger('dev'));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
app.use('/', indexRouter);
|
||||||
|
app.use('/users', usersRouter);
|
||||||
|
app.use('/inventory', inventoryRouter);
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use(function(req, res, next) {
|
||||||
|
next(createError(404));
|
||||||
|
});
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
app.use(function(err, req, res, next) {
|
||||||
|
// set locals, only providing error in development
|
||||||
|
res.locals.message = err.message;
|
||||||
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||||
|
|
||||||
|
// render the error page
|
||||||
|
res.status(err.status || 500);
|
||||||
|
res.render('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (reason, promise) => {
|
||||||
|
console.log("Unhandled Rejection at:", promise, "reason:", reason);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Executable
+90
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var app = require('../app');
|
||||||
|
var debug = require('debug')('bookstore-web-app:server');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
+409
@@ -0,0 +1,409 @@
|
|||||||
|
{
|
||||||
|
"name": "bookstore-web-app",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.24",
|
||||||
|
"negotiator": "0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
|
"basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
|
||||||
|
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"iconv-lite": "0.4.23",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"qs": "6.5.2",
|
||||||
|
"raw-body": "2.3.3",
|
||||||
|
"type-is": "~1.6.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-parser": {
|
||||||
|
"version": "1.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||||
|
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||||
|
"requires": {
|
||||||
|
"cookie": "0.4.0",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"ejs": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q=="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.16.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
|
||||||
|
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.5",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.18.3",
|
||||||
|
"content-disposition": "0.5.2",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.3.1",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "1.1.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.4",
|
||||||
|
"qs": "6.5.2",
|
||||||
|
"range-parser": "~1.2.0",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"send": "0.16.2",
|
||||||
|
"serve-static": "1.13.2",
|
||||||
|
"setprototypeof": "1.1.0",
|
||||||
|
"statuses": "~1.4.0",
|
||||||
|
"type-is": "~1.6.16",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"statuses": "~1.4.0",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||||
|
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.0",
|
||||||
|
"statuses": ">= 1.4.0 < 2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.44.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"morgan": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
|
||||||
|
"requires": {
|
||||||
|
"basic-auth": "~2.0.0",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"on-headers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "~0.1.2",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
|
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||||
|
},
|
||||||
|
"range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.0.0",
|
||||||
|
"http-errors": "1.6.3",
|
||||||
|
"iconv-lite": "0.4.23",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.16.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
|
||||||
|
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"destroy": "~1.0.4",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "~1.6.2",
|
||||||
|
"mime": "1.4.1",
|
||||||
|
"ms": "2.0.0",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"range-parser": "~1.2.0",
|
||||||
|
"statuses": "~1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve-static": {
|
||||||
|
"version": "1.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
|
||||||
|
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.2",
|
||||||
|
"send": "0.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "bookstore-web-app",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./bin/www"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"debug": "~2.6.9",
|
||||||
|
"ejs": "~2.6.1",
|
||||||
|
"express": "~4.16.1",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"morgan": "~1.9.1",
|
||||||
|
"node-fetch": "^2.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.render('index', { title: 'Express' });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
const { Router } = require("express");
|
||||||
|
const fetch = require("node-fetch");
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/", function (req, res) {
|
||||||
|
fetch("http://localhost:3000/books")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) =>
|
||||||
|
res.render("inventory", {
|
||||||
|
books: json,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
res.render("error", {
|
||||||
|
error: error,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.post("/add", function (req, res) {
|
||||||
|
console.log(req.body);
|
||||||
|
|
||||||
|
fetch("http://localhost:3000/books", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(req.body),
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
})
|
||||||
|
.then(res.redirect("/inventory"))
|
||||||
|
.catch((err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = express.Router();
|
||||||
|
|
||||||
|
/* GET users listing. */
|
||||||
|
router.get('/', function(req, res, next) {
|
||||||
|
res.send('respond with a resource');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<h1><%= message %></h1>
|
||||||
|
<h2><%= error.status %></h2>
|
||||||
|
<pre><%= error.stack %></pre>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= title %></title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><%= title %></h1>
|
||||||
|
<p>Welcome to <%= title %></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Book Inventory</title>
|
||||||
|
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Book Inventory</h1>
|
||||||
|
<ul>
|
||||||
|
<% for(let book of books) { %>
|
||||||
|
<li><%= book.title %> - <%= book.author %></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<h2>Add Book:</h2>
|
||||||
|
<form action="/inventory/add" method="POST">
|
||||||
|
<label for="title">Title</label>
|
||||||
|
<input type="text" name="title" />
|
||||||
|
<label for="author">Author</label>
|
||||||
|
<input type="text" name="author" />
|
||||||
|
<button type="submit" value="Submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
jspm_packages
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# 0x
|
||||||
|
profile-*
|
||||||
|
|
||||||
|
# mac files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# vim swap files
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# webstorm
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# vscode
|
||||||
|
.vscode
|
||||||
|
*code-workspace
|
||||||
|
|
||||||
|
# clinic
|
||||||
|
profile*
|
||||||
|
*clinic*
|
||||||
|
*flamegraph*
|
||||||
|
|
||||||
|
# generated code
|
||||||
|
*/**/*.js
|
||||||
|
dist
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
FROM node
|
||||||
|
|
||||||
|
WORKDIR "/app"
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get dist-upgrade -y \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& echo 'Finished installing dependencies'
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
ENV PORT 3000
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const AutoLoad = require('fastify-autoload')
|
||||||
|
|
||||||
|
module.exports = async function (fastify, opts) {
|
||||||
|
// Place here your custom code!
|
||||||
|
|
||||||
|
// Do not touch the following lines
|
||||||
|
|
||||||
|
// This loads all plugins defined in plugins
|
||||||
|
// those should be support plugins that are reused
|
||||||
|
// through your application
|
||||||
|
fastify.register(AutoLoad, {
|
||||||
|
dir: path.join(__dirname, 'plugins'),
|
||||||
|
options: Object.assign({}, opts)
|
||||||
|
})
|
||||||
|
|
||||||
|
// This loads all plugins defined in routes
|
||||||
|
// define your routes in one of these
|
||||||
|
fastify.register(AutoLoad, {
|
||||||
|
dir: path.join(__dirname, 'routes'),
|
||||||
|
options: Object.assign({}, opts)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: fastify-app-svc
|
||||||
|
labels:
|
||||||
|
run: fastify
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: fastify
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
type: NodePort
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: fastify-app
|
||||||
|
labels:
|
||||||
|
app: fastify
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: fastify
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: fastify
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: fastify-app
|
||||||
|
image: fastify-microservice:latest
|
||||||
|
imagePullPolicy: Never
|
||||||
|
ports:
|
||||||
|
- containerPort: 3000
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "fastify-microservice",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "tap test/**/*.test.js",
|
||||||
|
"start": "fastify start -l info app.js",
|
||||||
|
"dev": "fastify start -w -l info -P app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fastify": "^3.0.0",
|
||||||
|
"fastify-plugin": "^2.0.0",
|
||||||
|
"fastify-autoload": "^3.0.2",
|
||||||
|
"fastify-cli": "^2.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tap": "^14.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Plugins Folder
|
||||||
|
|
||||||
|
Plugins define behavior that is common to all the routes in your
|
||||||
|
application. Authentication, caching, templates, and all the other cross
|
||||||
|
cutting concerns should be handled by plugins placed in this folder.
|
||||||
|
|
||||||
|
Files in this folder are typically defined through the
|
||||||
|
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module,
|
||||||
|
making them non-encapsulated. They can define decorators and set hooks
|
||||||
|
that will then be used in the rest of your application.
|
||||||
|
|
||||||
|
Check out:
|
||||||
|
|
||||||
|
* [The hitchhiker's guide to plugins](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md)
|
||||||
|
* [Fastify decorators](https://www.fastify.io/docs/latest/Decorators/).
|
||||||
|
* [Fastify lifecycle](https://www.fastify.io/docs/latest/Lifecycle/).
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Services Folder
|
||||||
|
|
||||||
|
Services define routes within your application. Fastify provides an
|
||||||
|
easy path to a microservice architecture, in the future you might want
|
||||||
|
to independently deploy some of those.
|
||||||
|
|
||||||
|
In this folder you should define all the services that define the routes
|
||||||
|
of your web application.
|
||||||
|
Each service is a [Fastify
|
||||||
|
plugin](https://www.fastify.io/docs/latest/Plugins/), it is
|
||||||
|
encapsulated (it can have its own independent plugins) and it is
|
||||||
|
typically stored in a file; be careful to group your routes logically,
|
||||||
|
e.g. all `/users` routes in a `users.js` file. We have added
|
||||||
|
a `root.js` file for you with a '/' root added.
|
||||||
|
|
||||||
|
If a single file become too large, create a folder and add a `index.js` file there:
|
||||||
|
this file must be a Fastify plugin, and it will be loaded automatically
|
||||||
|
by the application. You can now add as many files as you want inside that folder.
|
||||||
|
In this way you can create complex services within a single monolith,
|
||||||
|
and eventually extract them.
|
||||||
|
|
||||||
|
If you need to share functionality between services, place that
|
||||||
|
functionality into the `plugins` folder, and share it via
|
||||||
|
[decorators](https://www.fastify.io/docs/latest/Decorators/).
|
||||||
|
|
||||||
|
If you have a bit confused about using `async/await` write services, you would better take a look at [Promise resolution](https://github.com/fastify/fastify/blob/master/docs/Routes.md#promise-resolution) for more details.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
/dist
|
||||||
|
# Cache used by TypeScript's incremental build
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: '@loopback/eslint-config',
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Transpiled JavaScript files from Typescript
|
||||||
|
/dist
|
||||||
|
|
||||||
|
# Cache used by TypeScript's incremental build
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"recursive": true,
|
||||||
|
"require": "source-map-support/register"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
package-lock=true
|
||||||
|
scripts-prepend-node-path=true
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
dist
|
||||||
|
*.json
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"editor.rulers": [80],
|
||||||
|
"editor.tabCompletion": "on",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.trimAutoWhitespace": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.hg": true,
|
||||||
|
"**/.svn": true,
|
||||||
|
"**/CVS": true,
|
||||||
|
"dist": true,
|
||||||
|
},
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
|
||||||
|
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||||
|
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
|
||||||
|
"typescript.preferences.quoteStyle": "single",
|
||||||
|
"eslint.run": "onSave",
|
||||||
|
"eslint.nodePath": "./node_modules",
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"typescript"
|
||||||
|
]
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Watch and Compile Project",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["--silent", "run", "build:watch"],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": "$tsc-watch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Build, Test and Lint",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm",
|
||||||
|
"args": ["--silent", "run", "test:dev"],
|
||||||
|
"group": {
|
||||||
|
"kind": "test",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": ["$tsc", "$eslint-compact", "$eslint-stylish"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"@loopback/cli": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"packageManager": "npm"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Developer's Guide
|
||||||
|
|
||||||
|
We use Visual Studio Code for developing LoopBack and recommend the same to our
|
||||||
|
users.
|
||||||
|
|
||||||
|
## VSCode setup
|
||||||
|
|
||||||
|
Install the following extensions:
|
||||||
|
|
||||||
|
- [eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
|
||||||
|
- [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||||
|
|
||||||
|
## Development workflow
|
||||||
|
|
||||||
|
### Visual Studio Code
|
||||||
|
|
||||||
|
1. Start the build task (Cmd+Shift+B) to run TypeScript compiler in the
|
||||||
|
background, watching and recompiling files as you change them. Compilation
|
||||||
|
errors will be shown in the VSCode's "PROBLEMS" window.
|
||||||
|
|
||||||
|
2. Execute "Run Rest Task" from the Command Palette (Cmd+Shift+P) to re-run the
|
||||||
|
test suite and lint the code for both programming and style errors. Linting
|
||||||
|
errors will be shown in VSCode's "PROBLEMS" window. Failed tests are printed
|
||||||
|
to terminal output only.
|
||||||
|
|
||||||
|
### Other editors/IDEs
|
||||||
|
|
||||||
|
1. Open a new terminal window/tab and start the continuous build process via
|
||||||
|
`npm run build:watch`. It will run TypeScript compiler in watch mode,
|
||||||
|
recompiling files as you change them. Any compilation errors will be printed
|
||||||
|
to the terminal.
|
||||||
|
|
||||||
|
2. In your main terminal window/tab, run `npm run test:dev` to re-run the test
|
||||||
|
suite and lint the code for both programming and style errors. You should run
|
||||||
|
this command manually whenever you have new changes to test. Test failures
|
||||||
|
and linter errors will be printed to the terminal.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Check out https://hub.docker.com/_/node to select a new base image
|
||||||
|
FROM node:10-slim
|
||||||
|
|
||||||
|
# Set to a non-root built-in user `node`
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Create app directory (with user `node`)
|
||||||
|
RUN mkdir -p /home/node/app
|
||||||
|
|
||||||
|
WORKDIR /home/node/app
|
||||||
|
|
||||||
|
# Install app dependencies
|
||||||
|
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||||
|
# where available (npm@5+)
|
||||||
|
COPY --chown=node package*.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Bundle app source code
|
||||||
|
COPY --chown=node . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Bind to all network interfaces so that it can be mapped to the host OS
|
||||||
|
ENV HOST=0.0.0.0 PORT=3000
|
||||||
|
|
||||||
|
EXPOSE ${PORT}
|
||||||
|
CMD [ "node", "." ]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# loopback-bookstore
|
||||||
|
|
||||||
|
[-@2x.png)](http://loopback.io/)
|
||||||
+5183
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"name": "loopback-bookstore",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "loopback-bookstore",
|
||||||
|
"keywords": [
|
||||||
|
"loopback-application",
|
||||||
|
"loopback"
|
||||||
|
],
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "lb-tsc",
|
||||||
|
"build:watch": "lb-tsc --watch",
|
||||||
|
"lint": "npm run prettier:check && npm run eslint",
|
||||||
|
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
|
||||||
|
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
|
||||||
|
"prettier:check": "npm run prettier:cli -- -l",
|
||||||
|
"prettier:fix": "npm run prettier:cli -- --write",
|
||||||
|
"eslint": "lb-eslint --report-unused-disable-directives .",
|
||||||
|
"eslint:fix": "npm run eslint -- --fix",
|
||||||
|
"pretest": "npm run clean && npm run build",
|
||||||
|
"test": "lb-mocha --allow-console-logs \"dist/__tests__\"",
|
||||||
|
"posttest": "npm run lint",
|
||||||
|
"test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest",
|
||||||
|
"docker:build": "docker build -t loopback-bookstore .",
|
||||||
|
"docker:run": "docker run -p 3000:3000 -d loopback-bookstore",
|
||||||
|
"migrate": "node ./dist/migrate",
|
||||||
|
"openapi-spec": "node ./dist/openapi-spec",
|
||||||
|
"prestart": "npm run build",
|
||||||
|
"start": "node -r source-map-support/register .",
|
||||||
|
"clean": "lb-clean dist *.tsbuildinfo .eslintcache"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "",
|
||||||
|
"files": [
|
||||||
|
"README.md",
|
||||||
|
"dist",
|
||||||
|
"src",
|
||||||
|
"!*/__tests__"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@loopback/boot": "^2.3.5",
|
||||||
|
"@loopback/core": "^2.9.1",
|
||||||
|
"@loopback/openapi-v3": "^3.4.5",
|
||||||
|
"@loopback/repository": "^2.9.0",
|
||||||
|
"@loopback/rest": "^5.2.0",
|
||||||
|
"@loopback/rest-explorer": "^2.2.6",
|
||||||
|
"@loopback/service-proxy": "^2.3.4",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@loopback/build": "^6.1.0",
|
||||||
|
"source-map-support": "^0.5.19",
|
||||||
|
"@loopback/testlab": "^3.2.0",
|
||||||
|
"@types/node": "^10.17.26",
|
||||||
|
"@loopback/eslint-config": "^8.0.3",
|
||||||
|
"eslint": "^7.3.1",
|
||||||
|
"typescript": "~3.9.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>loopback-bookstore</title>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="https://loopback.io/favicon.ico">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h3 {
|
||||||
|
margin-left: 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:visited {
|
||||||
|
color: #3f5dff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 a {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:focus, a:active {
|
||||||
|
color: #001956;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%)
|
||||||
|
}
|
||||||
|
|
||||||
|
.info h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info p {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: rgb(29, 30, 32);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:visited {
|
||||||
|
color: #4990e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:focus, a:active {
|
||||||
|
color: #2b78ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="info">
|
||||||
|
<h1>loopback-bookstore</h1>
|
||||||
|
<p>Version 1.0.0</p>
|
||||||
|
|
||||||
|
<h3>OpenAPI spec: <a href="/openapi.json">/openapi.json</a></h3>
|
||||||
|
<h3>API Explorer: <a href="/explorer">/explorer</a></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="power">
|
||||||
|
<a href="https://loopback.io" target="_blank">
|
||||||
|
<img src="https://loopback.io/images/branding/powered-by-loopback/blue/powered-by-loopback-sm.png" />
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
Please place your tests in this folder.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {Client} from '@loopback/testlab';
|
||||||
|
import {LoopbackBookstoreApplication} from '../..';
|
||||||
|
import {setupApplication} from './test-helper';
|
||||||
|
|
||||||
|
describe('HomePage', () => {
|
||||||
|
let app: LoopbackBookstoreApplication;
|
||||||
|
let client: Client;
|
||||||
|
|
||||||
|
before('setupApplication', async () => {
|
||||||
|
({app, client} = await setupApplication());
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await app.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exposes a default home page', async () => {
|
||||||
|
await client
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', /text\/html/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exposes self-hosted explorer', async () => {
|
||||||
|
await client
|
||||||
|
.get('/explorer/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', /text\/html/)
|
||||||
|
.expect(/<title>LoopBack API Explorer/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import {Client, expect} from '@loopback/testlab';
|
||||||
|
import {LoopbackBookstoreApplication} from '../..';
|
||||||
|
import {setupApplication} from './test-helper';
|
||||||
|
|
||||||
|
describe('PingController', () => {
|
||||||
|
let app: LoopbackBookstoreApplication;
|
||||||
|
let client: Client;
|
||||||
|
|
||||||
|
before('setupApplication', async () => {
|
||||||
|
({app, client} = await setupApplication());
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await app.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invokes GET /ping', async () => {
|
||||||
|
const res = await client.get('/ping?msg=world').expect(200);
|
||||||
|
expect(res.body).to.containEql({greeting: 'Hello from LoopBack'});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {LoopbackBookstoreApplication} from '../..';
|
||||||
|
import {
|
||||||
|
createRestAppClient,
|
||||||
|
givenHttpServerConfig,
|
||||||
|
Client,
|
||||||
|
} from '@loopback/testlab';
|
||||||
|
|
||||||
|
export async function setupApplication(): Promise<AppWithClient> {
|
||||||
|
const restConfig = givenHttpServerConfig({
|
||||||
|
// Customize the server configuration here.
|
||||||
|
// Empty values (undefined, '') will be ignored by the helper.
|
||||||
|
//
|
||||||
|
// host: process.env.HOST,
|
||||||
|
// port: +process.env.PORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new LoopbackBookstoreApplication({
|
||||||
|
rest: restConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
await app.boot();
|
||||||
|
await app.start();
|
||||||
|
|
||||||
|
const client = createRestAppClient(app);
|
||||||
|
|
||||||
|
return {app, client};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppWithClient {
|
||||||
|
app: LoopbackBookstoreApplication;
|
||||||
|
client: Client;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {BootMixin} from '@loopback/boot';
|
||||||
|
import {ApplicationConfig} from '@loopback/core';
|
||||||
|
import {
|
||||||
|
RestExplorerBindings,
|
||||||
|
RestExplorerComponent,
|
||||||
|
} from '@loopback/rest-explorer';
|
||||||
|
import {RepositoryMixin} from '@loopback/repository';
|
||||||
|
import {RestApplication} from '@loopback/rest';
|
||||||
|
import {ServiceMixin} from '@loopback/service-proxy';
|
||||||
|
import path from 'path';
|
||||||
|
import {MySequence} from './sequence';
|
||||||
|
|
||||||
|
export {ApplicationConfig};
|
||||||
|
|
||||||
|
export class LoopbackBookstoreApplication extends BootMixin(
|
||||||
|
ServiceMixin(RepositoryMixin(RestApplication)),
|
||||||
|
) {
|
||||||
|
constructor(options: ApplicationConfig = {}) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
// Set up the custom sequence
|
||||||
|
this.sequence(MySequence);
|
||||||
|
|
||||||
|
// Set up default home page
|
||||||
|
this.static('/', path.join(__dirname, '../public'));
|
||||||
|
|
||||||
|
// Customize @loopback/rest-explorer configuration here
|
||||||
|
this.configure(RestExplorerBindings.COMPONENT).to({
|
||||||
|
path: '/explorer',
|
||||||
|
});
|
||||||
|
this.component(RestExplorerComponent);
|
||||||
|
|
||||||
|
this.projectRoot = __dirname;
|
||||||
|
// Customize @loopback/boot Booter Conventions here
|
||||||
|
this.bootOptions = {
|
||||||
|
controllers: {
|
||||||
|
// Customize ControllerBooter Conventions here
|
||||||
|
dirs: ['controllers'],
|
||||||
|
extensions: ['.controller.js'],
|
||||||
|
nested: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Controllers
|
||||||
|
|
||||||
|
This directory contains source files for the controllers exported by this app.
|
||||||
|
|
||||||
|
To add a new empty controller, type in `lb4 controller [<name>]` from the
|
||||||
|
command-line of your application's root directory.
|
||||||
|
|
||||||
|
For more information, please visit
|
||||||
|
[Controller generator](http://loopback.io/doc/en/lb4/Controller-generator.html).
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import {
|
||||||
|
Count,
|
||||||
|
CountSchema,
|
||||||
|
Filter,
|
||||||
|
FilterExcludingWhere,
|
||||||
|
repository,
|
||||||
|
Where,
|
||||||
|
} from '@loopback/repository';
|
||||||
|
import {
|
||||||
|
post,
|
||||||
|
param,
|
||||||
|
get,
|
||||||
|
getModelSchemaRef,
|
||||||
|
patch,
|
||||||
|
put,
|
||||||
|
del,
|
||||||
|
requestBody,
|
||||||
|
} from '@loopback/rest';
|
||||||
|
import {Book} from '../models';
|
||||||
|
import {BookRepository} from '../repositories';
|
||||||
|
|
||||||
|
export class BooksController {
|
||||||
|
constructor(
|
||||||
|
@repository(BookRepository)
|
||||||
|
public bookRepository : BookRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@post('/books', {
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Book model instance',
|
||||||
|
content: {'application/json': {schema: getModelSchemaRef(Book)}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async create(
|
||||||
|
@requestBody({
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: getModelSchemaRef(Book, {
|
||||||
|
title: 'NewBook',
|
||||||
|
exclude: ['id'],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
book: Omit<Book, 'id'>,
|
||||||
|
): Promise<Book> {
|
||||||
|
return this.bookRepository.create(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
@get('/books/count', {
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Book model count',
|
||||||
|
content: {'application/json': {schema: CountSchema}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async count(
|
||||||
|
@param.where(Book) where?: Where<Book>,
|
||||||
|
): Promise<Count> {
|
||||||
|
return this.bookRepository.count(where);
|
||||||
|
}
|
||||||
|
|
||||||
|
@get('/books', {
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Array of Book model instances',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'array',
|
||||||
|
items: getModelSchemaRef(Book, {includeRelations: true}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async find(
|
||||||
|
@param.filter(Book) filter?: Filter<Book>,
|
||||||
|
): Promise<Book[]> {
|
||||||
|
return this.bookRepository.find(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@patch('/books', {
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Book PATCH success count',
|
||||||
|
content: {'application/json': {schema: CountSchema}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async updateAll(
|
||||||
|
@requestBody({
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: getModelSchemaRef(Book, {partial: true}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
book: Book,
|
||||||
|
@param.where(Book) where?: Where<Book>,
|
||||||
|
): Promise<Count> {
|
||||||
|
return this.bookRepository.updateAll(book, where);
|
||||||
|
}
|
||||||
|
|
||||||
|
@get('/books/{id}', {
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Book model instance',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: getModelSchemaRef(Book, {includeRelations: true}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async findById(
|
||||||
|
@param.path.number('id') id: number,
|
||||||
|
@param.filter(Book, {exclude: 'where'}) filter?: FilterExcludingWhere<Book>
|
||||||
|
): Promise<Book> {
|
||||||
|
return this.bookRepository.findById(id, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@patch('/books/{id}', {
|
||||||
|
responses: {
|
||||||
|
'204': {
|
||||||
|
description: 'Book PATCH success',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async updateById(
|
||||||
|
@param.path.number('id') id: number,
|
||||||
|
@requestBody({
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: getModelSchemaRef(Book, {partial: true}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
book: Book,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.bookRepository.updateById(id, book);
|
||||||
|
}
|
||||||
|
|
||||||
|
@put('/books/{id}', {
|
||||||
|
responses: {
|
||||||
|
'204': {
|
||||||
|
description: 'Book PUT success',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async replaceById(
|
||||||
|
@param.path.number('id') id: number,
|
||||||
|
@requestBody() book: Book,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.bookRepository.replaceById(id, book);
|
||||||
|
}
|
||||||
|
|
||||||
|
@del('/books/{id}', {
|
||||||
|
responses: {
|
||||||
|
'204': {
|
||||||
|
description: 'Book DELETE success',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async deleteById(@param.path.number('id') id: number): Promise<void> {
|
||||||
|
await this.bookRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './ping.controller';
|
||||||
|
export * from './books.controller';
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import {Request, RestBindings, get, ResponseObject} from '@loopback/rest';
|
||||||
|
import {inject} from '@loopback/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAPI response for ping()
|
||||||
|
*/
|
||||||
|
const PING_RESPONSE: ResponseObject = {
|
||||||
|
description: 'Ping Response',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
title: 'PingResponse',
|
||||||
|
properties: {
|
||||||
|
greeting: {type: 'string'},
|
||||||
|
date: {type: 'string'},
|
||||||
|
url: {type: 'string'},
|
||||||
|
headers: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
'Content-Type': {type: 'string'},
|
||||||
|
},
|
||||||
|
additionalProperties: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple controller to bounce back http requests
|
||||||
|
*/
|
||||||
|
export class PingController {
|
||||||
|
constructor(@inject(RestBindings.Http.REQUEST) private req: Request) {}
|
||||||
|
|
||||||
|
// Map to `GET /ping`
|
||||||
|
@get('/ping', {
|
||||||
|
responses: {
|
||||||
|
'200': PING_RESPONSE,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ping(): object {
|
||||||
|
// Reply with a greeting, the current time, the url, and request headers
|
||||||
|
return {
|
||||||
|
greeting: 'Hello from LoopBack',
|
||||||
|
date: new Date(),
|
||||||
|
url: this.req.url,
|
||||||
|
headers: Object.assign({}, this.req.headers),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Datasources
|
||||||
|
|
||||||
|
This directory contains config for datasources used by this app.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './local.datasource';
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
|
||||||
|
import {juggler} from '@loopback/repository';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
name: 'local',
|
||||||
|
connector: 'memory',
|
||||||
|
localStorage: '',
|
||||||
|
file: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Observe application's life cycle to disconnect the datasource when
|
||||||
|
// application is stopped. This allows the application to be shut down
|
||||||
|
// gracefully. The `stop()` method is inherited from `juggler.DataSource`.
|
||||||
|
// Learn more at https://loopback.io/doc/en/lb4/Life-cycle.html
|
||||||
|
@lifeCycleObserver('datasource')
|
||||||
|
export class LocalDataSource extends juggler.DataSource
|
||||||
|
implements LifeCycleObserver {
|
||||||
|
static dataSourceName = 'local';
|
||||||
|
static readonly defaultConfig = config;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@inject('datasources.config.local', {optional: true})
|
||||||
|
dsConfig: object = config,
|
||||||
|
) {
|
||||||
|
super(dsConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import {ApplicationConfig, LoopbackBookstoreApplication} from './application';
|
||||||
|
|
||||||
|
export * from './application';
|
||||||
|
|
||||||
|
export async function main(options: ApplicationConfig = {}) {
|
||||||
|
const app = new LoopbackBookstoreApplication(options);
|
||||||
|
await app.boot();
|
||||||
|
await app.start();
|
||||||
|
|
||||||
|
const url = app.restServer.url;
|
||||||
|
console.log(`Server is running at ${url}`);
|
||||||
|
console.log(`Try ${url}/ping`);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
// Run the application
|
||||||
|
const config = {
|
||||||
|
rest: {
|
||||||
|
port: +(process.env.PORT ?? 3000),
|
||||||
|
host: process.env.HOST,
|
||||||
|
// The `gracePeriodForClose` provides a graceful close for http/https
|
||||||
|
// servers with keep-alive clients. The default value is `Infinity`
|
||||||
|
// (don't force-close). If you want to immediately destroy all sockets
|
||||||
|
// upon stop, set its value to `0`.
|
||||||
|
// See https://www.npmjs.com/package/stoppable
|
||||||
|
gracePeriodForClose: 5000, // 5 seconds
|
||||||
|
openApiSpec: {
|
||||||
|
// useful when used with OpenAPI-to-GraphQL to locate your application
|
||||||
|
setServersFromRequest: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
main(config).catch(err => {
|
||||||
|
console.error('Cannot start the application.', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import {LoopbackBookstoreApplication} from './application';
|
||||||
|
|
||||||
|
export async function migrate(args: string[]) {
|
||||||
|
const existingSchema = args.includes('--rebuild') ? 'drop' : 'alter';
|
||||||
|
console.log('Migrating schemas (%s existing schema)', existingSchema);
|
||||||
|
|
||||||
|
const app = new LoopbackBookstoreApplication();
|
||||||
|
await app.boot();
|
||||||
|
await app.migrateSchema({existingSchema});
|
||||||
|
|
||||||
|
// Connectors usually keep a pool of opened connections,
|
||||||
|
// this keeps the process running even after all work is done.
|
||||||
|
// We need to exit explicitly.
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate(process.argv).catch(err => {
|
||||||
|
console.error('Cannot migrate database schema', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Models
|
||||||
|
|
||||||
|
This directory contains code for models provided by this app.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {Entity, model, property} from '@loopback/repository';
|
||||||
|
|
||||||
|
@model()
|
||||||
|
export class Book extends Entity {
|
||||||
|
@property({
|
||||||
|
type: 'number',
|
||||||
|
id: true,
|
||||||
|
generated: true,
|
||||||
|
})
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@property({
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
author: string;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(data?: Partial<Book>) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BookRelations {
|
||||||
|
// describe navigational properties here
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BookWithRelations = Book & BookRelations;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './book.model';
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import {ApplicationConfig} from '@loopback/core';
|
||||||
|
import {LoopbackBookstoreApplication} from './application';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export the OpenAPI spec from the application
|
||||||
|
*/
|
||||||
|
async function exportOpenApiSpec(): Promise<void> {
|
||||||
|
const config: ApplicationConfig = {
|
||||||
|
rest: {
|
||||||
|
port: +(process.env.PORT ?? 3000),
|
||||||
|
host: process.env.HOST ?? 'localhost',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const outFile = process.argv[2] ?? '';
|
||||||
|
const app = new LoopbackBookstoreApplication(config);
|
||||||
|
await app.boot();
|
||||||
|
await app.exportOpenApiSpec(outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportOpenApiSpec().catch(err => {
|
||||||
|
console.error('Fail to export OpenAPI spec from the application.', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Repositories
|
||||||
|
|
||||||
|
This directory contains code for repositories provided by this app.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import {DefaultCrudRepository} from '@loopback/repository';
|
||||||
|
import {Book, BookRelations} from '../models';
|
||||||
|
import {LocalDataSource} from '../datasources';
|
||||||
|
import {inject} from '@loopback/core';
|
||||||
|
|
||||||
|
export class BookRepository extends DefaultCrudRepository<
|
||||||
|
Book,
|
||||||
|
typeof Book.prototype.id,
|
||||||
|
BookRelations
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
@inject('datasources.local') dataSource: LocalDataSource,
|
||||||
|
) {
|
||||||
|
super(Book, dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './book.repository';
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import {inject} from '@loopback/core';
|
||||||
|
import {
|
||||||
|
FindRoute,
|
||||||
|
InvokeMethod,
|
||||||
|
InvokeMiddleware,
|
||||||
|
ParseParams,
|
||||||
|
Reject,
|
||||||
|
RequestContext,
|
||||||
|
RestBindings,
|
||||||
|
Send,
|
||||||
|
SequenceHandler,
|
||||||
|
} from '@loopback/rest';
|
||||||
|
|
||||||
|
const SequenceActions = RestBindings.SequenceActions;
|
||||||
|
|
||||||
|
export class MySequence implements SequenceHandler {
|
||||||
|
/**
|
||||||
|
* Optional invoker for registered middleware in a chain.
|
||||||
|
* To be injected via SequenceActions.INVOKE_MIDDLEWARE.
|
||||||
|
*/
|
||||||
|
@inject(SequenceActions.INVOKE_MIDDLEWARE, {optional: true})
|
||||||
|
protected invokeMiddleware: InvokeMiddleware = () => false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
|
||||||
|
@inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
|
||||||
|
@inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
|
||||||
|
@inject(SequenceActions.SEND) public send: Send,
|
||||||
|
@inject(SequenceActions.REJECT) public reject: Reject,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async handle(context: RequestContext) {
|
||||||
|
try {
|
||||||
|
const {request, response} = context;
|
||||||
|
const finished = await this.invokeMiddleware(context);
|
||||||
|
if (finished) return;
|
||||||
|
const route = this.findRoute(request);
|
||||||
|
const args = await this.parseParams(request, route);
|
||||||
|
const result = await this.invoke(route, args);
|
||||||
|
this.send(response, result);
|
||||||
|
} catch (err) {
|
||||||
|
this.reject(context, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "@loopback/build/config/tsconfig.common.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user