aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Powers <markppowers0@gmail.com>2020-10-09 22:42:56 -0500
committerMark Powers <markppowers0@gmail.com>2020-10-09 22:42:56 -0500
commit0345f90a7baceae507f417abe30736cc95cdc0cf (patch)
tree8c9617f0a039f2c1cebe33d3ad826bb01a15b482
parent35b6d276bdeabdf7bf5eee59d85dfefee7531fce (diff)
Begin refactor to use server side templates
-rw-r--r--package-lock.json38
-rw-r--r--package.json1
-rw-r--r--src/index.js90
-rw-r--r--src/models.js85
-rw-r--r--src/server.js26
-rw-r--r--src/static/main.js (renamed from src/main.js)0
-rw-r--r--src/static/styles.css (renamed from src/styles.css)0
-rw-r--r--src/templates.js27
-rw-r--r--src/templates/index.html (renamed from src/index.html)4
-rw-r--r--src/templates/login.html (renamed from src/login.html)0
-rw-r--r--src/templates/summary.html45
11 files changed, 224 insertions, 92 deletions
diff --git a/package-lock.json b/package-lock.json
index ef7d304..a32ddd2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -223,6 +223,18 @@
"is-property": "^1.0.2"
}
},
+ "handlebars": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+ "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ }
+ },
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
@@ -397,6 +409,11 @@
"mime-db": "~1.38.0"
}
},
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
@@ -453,6 +470,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+ },
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -627,6 +649,11 @@
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
},
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
@@ -651,6 +678,12 @@
"mime-types": "~2.1.18"
}
},
+ "uglify-js": {
+ "version": "3.11.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.1.tgz",
+ "integrity": "sha512-OApPSuJcxcnewwjSGGfWOjx3oix5XpmrK9Z2j0fTRlHGoZ49IU6kExfZTM0++fCArOOCet+vIfWwFHbvWqwp6g==",
+ "optional": true
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -689,6 +722,11 @@
"@types/node": "*"
}
},
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ },
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
diff --git a/package.json b/package.json
index 1d1cbdc..e6f2bd8 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"body-parser": "^1.18.3",
"cookie-parser": "^1.4.4",
"express": "^4.16.4",
+ "handlebars": "^4.7.6",
"jsonwebtoken": "^8.5.1",
"mysql2": "^1.6.5",
"readline-sync": "^1.4.10",
diff --git a/src/index.js b/src/index.js
index 0cbba31..8e18272 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,12 +4,13 @@ const fs = require('fs');
const path = require('path');
const jwt = require('jsonwebtoken');
-
const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json')));
-
const dbCreds = config.database;
const secret = config.jwt_secret;
+const models = require('./models');
+const templates = require('./templates');
+
const jwtFunctions = {
sign: function (message) {
return jwt.sign({ value: message }, secret);
@@ -44,90 +45,11 @@ async function sync(alter, force, callback) {
await database.sync({ alter, force, logging: console.log });
}
-function setUpModels() {
- const models = {
- "transaction": database.define('transaction', {
- when: {
- type: Sequelize.DATE,
- allowNull: false,
- },
- amount: {
- type: Sequelize.DECIMAL,
- allowNull: false,
- },
- where: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- category: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- subcategory: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- username: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- }),
- "goals": database.define('goal', {
- username: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- name: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- total: {
- type: Sequelize.DECIMAL,
- allowNull: false,
- },
- amount: {
- type: Sequelize.DECIMAL,
- allowNull: false,
- }
- }),
- "expected": database.define('expected', {
- username: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- name: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- total: {
- type: Sequelize.DECIMAL,
- allowNull: false,
- },
- days: {
- type: Sequelize.INTEGER,
- allowNull: false,
- }
- }),
- "users": database.define('user', {
- username: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- password: {
- type: Sequelize.STRING,
- allowNull: false,
- },
- salt: {
- type: Sequelize.STRING,
- allowNull: false,
- },}),
- }
- return models;
-}
-const models = setUpModels();
sync();
-server.setUpRoutes(models, jwtFunctions, database);
+server.setUpRoutes(models.setUpModels(database),
+ jwtFunctions,
+ database, templates.setUpTemplates());
server.listen(config.port);
diff --git a/src/models.js b/src/models.js
new file mode 100644
index 0000000..23d2032
--- /dev/null
+++ b/src/models.js
@@ -0,0 +1,85 @@
+const Sequelize = require('sequelize');
+function setUpModels(database) {
+ const models = {
+ "transaction": database.define('transaction', {
+ when: {
+ type: Sequelize.DATE,
+ allowNull: false,
+ },
+ amount: {
+ type: Sequelize.DECIMAL,
+ allowNull: false,
+ },
+ where: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ category: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ subcategory: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ username: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ }),
+ "goals": database.define('goal', {
+ username: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ name: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ total: {
+ type: Sequelize.DECIMAL,
+ allowNull: false,
+ },
+ amount: {
+ type: Sequelize.DECIMAL,
+ allowNull: false,
+ }
+ }),
+ "expected": database.define('expected', {
+ username: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ name: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ total: {
+ type: Sequelize.DECIMAL,
+ allowNull: false,
+ },
+ days: {
+ type: Sequelize.INTEGER,
+ allowNull: false,
+ }
+ }),
+ "users": database.define('user', {
+ username: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ password: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ salt: {
+ type: Sequelize.STRING,
+ allowNull: false,
+ },
+ }),
+ }
+ return models;
+}
+module.exports = {
+ setUpModels
+} \ No newline at end of file
diff --git a/src/server.js b/src/server.js
index f72f21b..ffbc373 100644
--- a/src/server.js
+++ b/src/server.js
@@ -11,7 +11,7 @@ const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json')));
const server = express();
server.use(cookieParser())
server.use(bodyParser.json());
-//server.use(bodyParser.urlencoded({ extended: true }));
+server.use(bodyParser.urlencoded({ extended: true }));
function listen(port) {
server.listen(port, () => console.info(`Listening: http://localhost:${port} `));
@@ -23,7 +23,7 @@ function hashWithSalt(password, salt){
return hash.digest("base64");
};
-function setUpRoutes(models, jwtFunctions, database) {
+function setUpRoutes(models, jwtFunctions, database, templates) {
// Authentication routine
server.use(async function (req, res, next) {
if (!req.path.toLowerCase().startsWith("/login")) {
@@ -58,8 +58,19 @@ function setUpRoutes(models, jwtFunctions, database) {
server.get('/', (req, res) => res.sendFile(__dirname + "/index.html"))
server.get('/login', (req, res) => res.sendFile(__dirname + "/login.html"))
- server.get('/styles.css', (req, res) => res.sendFile(__dirname + "/styles.css"))
- server.get('/main.js', (req, res) => res.sendFile(__dirname + "/main.js"))
+
+ server.get('/summary', async (req, res) => {
+ var ledger = await database.query("SELECT * FROM transactions WHERE username = '" + res.locals.user.username + "' ORDER BY `when` DESC", { type: database.QueryTypes.SELECT })
+ ledger.forEach((element, i) => {
+ element.when = element.when.toString().substring(0, 10);
+ element.index = i+1
+ });
+ var name = res.locals.user.username
+ let body = templates["summary"]({ name, ledger })
+ res.status(200).send(body)
+ })
+
+ server.use('/static', express.static(path.join(__dirname, '/static')))
server.post('/login', async (req, res, next) => {
const user = await models.users.findOne({ where: { username: req.body.username} })
@@ -90,9 +101,11 @@ function setUpRoutes(models, jwtFunctions, database) {
let item = req.body;
console.log(item);
item.username = res.locals.user.username
+ if(!item.when){
+ item.when = new Date().toLocaleDateString();
+ }
await models.transaction.create(item);
- var result = await database.query("SELECT * FROM transactions WHERE username = '" + res.locals.user.username + "' ORDER BY `when` DESC", { type: database.QueryTypes.SELECT })
- res.status(200).send(result);
+ res.redirect("/summary")
} catch (e) {
console.log(e);
res.status(400).send(e.message);
@@ -126,6 +139,7 @@ function setUpRoutes(models, jwtFunctions, database) {
res.status(400).send(e.message);
}
})
+
server.get(`/goals`, async (req, res, next) => {
try {
var result = await database.query("SELECT * FROM goals WHERE username = '" + res.locals.user.username + "' ORDER BY `name` DESC", { type: database.QueryTypes.SELECT })
diff --git a/src/main.js b/src/static/main.js
index 0a381b0..0a381b0 100644
--- a/src/main.js
+++ b/src/static/main.js
diff --git a/src/styles.css b/src/static/styles.css
index 4eedd0b..4eedd0b 100644
--- a/src/styles.css
+++ b/src/static/styles.css
diff --git a/src/templates.js b/src/templates.js
new file mode 100644
index 0000000..2c02905
--- /dev/null
+++ b/src/templates.js
@@ -0,0 +1,27 @@
+const fs = require('fs');
+const path = require('path');
+const handlebars = require("handlebars");
+
+function setUpTemplates(){
+ let templates = {};
+
+ {
+ const templateContent = fs.readFileSync(path.join(__dirname, 'templates/login.html')).toString()
+ templates["login"] = handlebars.compile(templateContent);
+ }
+ {
+ const templateContent = fs.readFileSync(path.join(__dirname, 'templates/index.html')).toString()
+ // templates["index"] = handlebars.compile(templateContent);
+ }
+ {
+ const templateContent = fs.readFileSync(path.join(__dirname, 'templates/summary.html')).toString()
+ templates["summary"] = handlebars.compile(templateContent);
+ }
+
+ return templates
+}
+
+
+module.exports = {
+ setUpTemplates
+};
diff --git a/src/index.html b/src/templates/index.html
index cba242d..525f8fe 100644
--- a/src/index.html
+++ b/src/templates/index.html
@@ -8,8 +8,8 @@
<link rel="shortcut icon" href="/favicon.ico">
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- <script src="main.js"></script>
- <link rel="stylesheet" type="text/css" href="styles.css">
+ <script src="static/main.js"></script>
+ <link rel="stylesheet" type="text/css" href="static/styles.css">
</head>
<body>
diff --git a/src/login.html b/src/templates/login.html
index ec41762..ec41762 100644
--- a/src/login.html
+++ b/src/templates/login.html
diff --git a/src/templates/summary.html b/src/templates/summary.html
new file mode 100644
index 0000000..5bf828e
--- /dev/null
+++ b/src/templates/summary.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <title>{{name}}'s Budget</title>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" type="text/css" href="static/styles.css">
+</head>
+
+<body>
+ <h1>{{name}}'s Budget</h1>
+ <form method="post" action="/transaction">
+ <input id="datePicker" name="when" placeholder="date" type="date">
+ <input name="where" placeholder="where" type="text">
+ <input name="amount" placeholder="amount" type="number" step="0.01">
+ <input name="category" placeholder="category" type="text">
+ <input name="subcategory" placeholder="tags (csv)" type="text">
+ <input type="submit" value="Add">
+ </form>
+ <table>
+ <tr>
+ <th></th>
+ <th>When</th>
+ <th>Where</th>
+ <th>Amount</th>
+ <th>Category</th>
+ <th>Tags</th>
+ </tr>
+ {{#each ledger}}
+ <tr>
+ <td>{{this.index}}</td>
+ <td>{{this.when}}</td>
+ <td>{{this.where}}</td>
+ <td>{{this.amount}}</td>
+ <td>{{this.category}}</td>
+ <td>{{this.subcategory}}</td>
+ </tr>
+ {{/each}}
+ </table>
+ <script>
+ document.getElementById('datePicker').value = new Date().toLocaleDateString();
+ </script>
+</body>
+</html>
+