diff options
-rw-r--r-- | package-lock.json | 38 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/index.js | 90 | ||||
-rw-r--r-- | src/models.js | 85 | ||||
-rw-r--r-- | src/server.js | 26 | ||||
-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.js | 27 | ||||
-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.html | 45 |
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> + |