Start RESTful server
This commit is contained in:
+11
-29
@@ -5,7 +5,8 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "flow-remove-types src/ -d out/",
|
||||
"buildWithMaps": "flow-remove-types src/ -d out/ --pretty --sourcemaps",
|
||||
"buildWithMaps":
|
||||
"flow-remove-types src/ -d out/ --pretty --sourcemaps",
|
||||
"start": "npm run build && node out/doroundmath.js",
|
||||
"start-db": "npm run build && node out/dbaccess.js",
|
||||
"nodemon": "nodemon --watch src --delay 1 --exec npm start",
|
||||
@@ -19,11 +20,7 @@
|
||||
"author": "Federico Kereki",
|
||||
"license": "ISC",
|
||||
"babel": {
|
||||
"presets": [
|
||||
"env",
|
||||
"node",
|
||||
"flow"
|
||||
]
|
||||
"presets": ["env", "node", "flow"]
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parserOptions": {
|
||||
@@ -35,37 +32,22 @@
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:flowtype/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"babel",
|
||||
"flowtype"
|
||||
],
|
||||
"extends": ["eslint:recommended", "plugin:flowtype/recommended"],
|
||||
"plugins": ["babel", "flowtype"],
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error"
|
||||
"prefer-const": "error",
|
||||
"flowtype/no-types-missing-file-annotation": 0
|
||||
}
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"**/out/*.js"
|
||||
],
|
||||
"eslintIgnore": ["**/out/*.js"],
|
||||
"flow-coverage-report": {
|
||||
"concurrentFiles": 1,
|
||||
"excludeGlob": [
|
||||
"node_modules/**"
|
||||
],
|
||||
"includeGlob": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"excludeGlob": ["node_modules/**"],
|
||||
"includeGlob": ["src/**/*.js"],
|
||||
"threshold": 90,
|
||||
"type": [
|
||||
"text",
|
||||
"html",
|
||||
"json"
|
||||
]
|
||||
"type": ["text", "html", "json"]
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 4,
|
||||
|
||||
@@ -4,33 +4,51 @@
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
const jwt = require("jsonwebtoken");
|
||||
const bodyParser = require("body-parser");
|
||||
|
||||
const validateUser = require("./validate_user.js");
|
||||
|
||||
const SECRET_JWT_KEY = "modernJSbook";
|
||||
|
||||
const bodyParser = require("body-parser");
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.get("/public", (req, res) => {
|
||||
res.send("the /public endpoint needs no token!");
|
||||
});
|
||||
|
||||
app.post("/gettoken", (req, res) => {
|
||||
validateUser(req.body.user, req.body.password, (idErr, userid) => {
|
||||
if (idErr !== null) {
|
||||
res.status(401).send(idErr);
|
||||
} else {
|
||||
jwt.sign(
|
||||
{ userid },
|
||||
SECRET_JWT_KEY,
|
||||
{ algorithm: "HS256", expiresIn: "1h" },
|
||||
(err, token) => res.status(200).send(token)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
// First check for the Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader.startsWith("Bearer ")) {
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return res.status(401).send("No token specified");
|
||||
}
|
||||
|
||||
// Now validate the token itself
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
jwt.verify(token, SECRET_JWT_KEY, function(err, decoded) {
|
||||
jwt.verify(token, SECRET_JWT_KEY, (err, decoded) => {
|
||||
if (err) {
|
||||
// Token bad formed, or expired, or other problem
|
||||
return res.status(403).send("Token expired or not valid");
|
||||
} else {
|
||||
req.decoded = decoded;
|
||||
// Everything OK, keep processing the request
|
||||
// Token OK; get the user id from it
|
||||
req.userid = decoded.userid;
|
||||
// Keep processing the request
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
const mariaSQL = require("mariasql");
|
||||
const { promisify } = require("util");
|
||||
|
||||
const DB_HOST = "127.0.0.1";
|
||||
const DB_USER = "fkereki";
|
||||
const DB_PASS = "modernJS!!";
|
||||
const DB_SCHEMA = "world";
|
||||
|
||||
const getDbConnection = (host, user, password, db) => {
|
||||
const dbConn = new mariaSQL({ host, user, password, db });
|
||||
dbConn.query = promisify(dbConn.query);
|
||||
return dbConn;
|
||||
};
|
||||
|
||||
const dbConn = getDbConnection(DB_HOST, DB_USER, DB_PASS, DB_SCHEMA);
|
||||
|
||||
module.exports = dbConn;
|
||||
@@ -0,0 +1,105 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
const getRegion = async (
|
||||
res: any,
|
||||
dbConn: any,
|
||||
country: ?string,
|
||||
id: ?string
|
||||
) => {
|
||||
console.log("COUNTRY", country, "ID", id, typeof id);
|
||||
|
||||
let sqlQuery = "";
|
||||
if (country == null) {
|
||||
sqlQuery = `
|
||||
SELECT rr.*, cc.countryName
|
||||
FROM regions rr
|
||||
JOIN countries cc
|
||||
ON cc.countryCode=rr.countryCode
|
||||
ORDER BY cc.countryCode, rr.regionCode
|
||||
`;
|
||||
} else if (id == null) {
|
||||
sqlQuery = `
|
||||
SELECT rr.*, cc.countryName
|
||||
FROM regions rr
|
||||
JOIN countries cc
|
||||
ON cc.countryCode=rr.countryCode
|
||||
WHERE rr.countryCode="${country}"
|
||||
ORDER BY rr.regionCode
|
||||
`;
|
||||
} else {
|
||||
sqlQuery = `
|
||||
SELECT rr.*, cc.countryName
|
||||
FROM regions rr
|
||||
JOIN countries cc
|
||||
ON cc.countryCode=rr.countryCode
|
||||
WHERE rr.countryCode="${country}"
|
||||
AND rr.regionCode="${id}"
|
||||
`;
|
||||
}
|
||||
|
||||
try {
|
||||
const regions = await dbConn.query(sqlQuery);
|
||||
res
|
||||
.status(200)
|
||||
.set("Content-Type", "application/json")
|
||||
.send(JSON.stringify(regions));
|
||||
} catch (e) {
|
||||
res.status(500).send("Server error");
|
||||
}
|
||||
};
|
||||
|
||||
const deleteRegion = async (
|
||||
res: any,
|
||||
dbConn: any,
|
||||
country: string,
|
||||
region: string
|
||||
) => {
|
||||
const sqlCities = `
|
||||
SELECT 1 FROM cities
|
||||
WHERE countryCode="${country}
|
||||
AND regionCode="${region}
|
||||
LIMIT 1"
|
||||
`;
|
||||
|
||||
try {
|
||||
const cities = await dbConn.query(sqlCities);
|
||||
if (cities.length > 0) {
|
||||
res.status(403).send("Cannot delete a region with cities");
|
||||
} else {
|
||||
const deleteRegion = `
|
||||
DELETE FROM regions
|
||||
WHERE countryCode="${country}
|
||||
AND regionCode="${region}
|
||||
`;
|
||||
|
||||
const result = await dbConn.query(deleteRegion);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
res.status(204).send("Region deleted");
|
||||
} else {
|
||||
res.status(404).send("Region not found");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
res.status(500).send("Server error");
|
||||
}
|
||||
};
|
||||
|
||||
const putRegion = async (
|
||||
res: any,
|
||||
dbConn: any,
|
||||
country: string,
|
||||
id: string,
|
||||
region: any
|
||||
) => {
|
||||
res.status(200).send("NOTHING DOING NOW...");
|
||||
};
|
||||
|
||||
const postRegion = async (res: any, dbConn: any, region: any) => {
|
||||
res.status(200).send("NOTHING DOING NOW...");
|
||||
};
|
||||
|
||||
module.exports = { getRegion, putRegion, deleteRegion, postRegion };
|
||||
@@ -0,0 +1,95 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
const jwt = require("jsonwebtoken");
|
||||
const bodyParser = require("body-parser");
|
||||
|
||||
const validateUser = require("./validate_user.js");
|
||||
|
||||
const dbConn = require("./restful_db.js");
|
||||
|
||||
const {
|
||||
getRegion,
|
||||
deleteRegion,
|
||||
putRegion,
|
||||
postRegion
|
||||
} = require("./restful_regions.js");
|
||||
|
||||
const SECRET_JWT_KEY = "modernJSbook";
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.post("/gettoken", (req, res) => {
|
||||
validateUser(req.body.user, req.body.password, (idErr, userid) => {
|
||||
if (idErr !== null) {
|
||||
res.status(401).send(idErr);
|
||||
} else {
|
||||
jwt.sign(
|
||||
{ userid },
|
||||
SECRET_JWT_KEY,
|
||||
{ algorithm: "HS256", expiresIn: "1h" },
|
||||
(err, token) => res.status(200).send(token)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
/*
|
||||
app.use((req, res, next) => {
|
||||
// First check for the Authorization header
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return res.status(401).send("No token specified");
|
||||
}
|
||||
|
||||
// Now validate the token itself
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
jwt.verify(token, SECRET_JWT_KEY, (err, decoded) => {
|
||||
if (err) {
|
||||
// Token bad formed, or expired, or other problem
|
||||
return res.status(403).send("Token expired or not valid");
|
||||
} else {
|
||||
// Token OK; get the user id from it
|
||||
req.userid = decoded.userid;
|
||||
// Keep processing the request
|
||||
next();
|
||||
}
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
// START ROUTING FOR REGIONS
|
||||
|
||||
app.get("/regions/", (req, res) => getRegion(res, dbConn));
|
||||
|
||||
app.get("/regions/:country/", (req, res) =>
|
||||
getRegion(res, dbConn, req.params.country)
|
||||
);
|
||||
|
||||
app.get("/regions/:country/:region/", (req, res) =>
|
||||
getRegion(res, dbConn, req.params.country, req.params.region)
|
||||
);
|
||||
|
||||
app.delete("/regions/:country/:region", (req, res) =>
|
||||
deleteRegion(res, dbConn, req.params.country, req.params.region)
|
||||
);
|
||||
|
||||
app.put("/regions/:country/:region", (req, res) =>
|
||||
putRegion(res, dbConn, req.params.country, req.params.region)
|
||||
);
|
||||
|
||||
app.post("/regions", (req, res) => postRegion(res, dbConn));
|
||||
|
||||
// END OF ROUTING FOR REGIONS
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
app.use((err, req, res, next) => {
|
||||
console.error("Error....", err.message);
|
||||
res.status(500).send("INTERNAL SERVER ERROR");
|
||||
});
|
||||
|
||||
app.listen(8080, () =>
|
||||
console.log("Mini JWT server ready, at http://localhost:8080/!")
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
/* @flow */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
In real life, validateUser could check a database,
|
||||
look into an Active Directory, call another service,
|
||||
etc. -- but for this demo, let's keep it quite
|
||||
simple and only accept a single hardcoded user.
|
||||
*/
|
||||
|
||||
const validateUser = (
|
||||
userName: string,
|
||||
password: string,
|
||||
callback: (?string, ?string) => void
|
||||
) => {
|
||||
if (userName === "fkereki" && password === "modernjsbook") {
|
||||
callback(null, "fkereki"); // OK, send userName back
|
||||
} else {
|
||||
callback("Not valid user", null);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = validateUser;
|
||||
Reference in New Issue
Block a user