Browse Source

Merge remote-tracking branch 'origin/master'

pull/60/head
jrtechs 6 years ago
parent
commit
b3475e6e1d
39 changed files with 54963 additions and 97 deletions
  1. +10
    -5
      admin/admin.js
  2. +71
    -0
      admin/analytics.js
  3. +0
    -24
      admin/analytics/analytics.js
  4. +3
    -3
      admin/login.js
  5. +175
    -0
      admin/users.js
  6. +1
    -1
      includes/css/bootstrap.css
  7. +18
    -2
      includes/html/adminHeader.html
  8. BIN
      includes/js/vis/dist/img/network/acceptDeleteIcon.png
  9. BIN
      includes/js/vis/dist/img/network/addNodeIcon.png
  10. BIN
      includes/js/vis/dist/img/network/backIcon.png
  11. BIN
      includes/js/vis/dist/img/network/connectIcon.png
  12. BIN
      includes/js/vis/dist/img/network/cross.png
  13. BIN
      includes/js/vis/dist/img/network/cross2.png
  14. BIN
      includes/js/vis/dist/img/network/deleteIcon.png
  15. BIN
      includes/js/vis/dist/img/network/downArrow.png
  16. BIN
      includes/js/vis/dist/img/network/editIcon.png
  17. BIN
      includes/js/vis/dist/img/network/leftArrow.png
  18. BIN
      includes/js/vis/dist/img/network/minus.png
  19. BIN
      includes/js/vis/dist/img/network/plus.png
  20. BIN
      includes/js/vis/dist/img/network/rightArrow.png
  21. BIN
      includes/js/vis/dist/img/network/upArrow.png
  22. BIN
      includes/js/vis/dist/img/network/zoomExtends.png
  23. +34
    -0
      includes/js/vis/dist/vis-graph3d.min.js
  24. +1
    -0
      includes/js/vis/dist/vis-network.min.css
  25. +42
    -0
      includes/js/vis/dist/vis-network.min.js
  26. +1
    -0
      includes/js/vis/dist/vis-timeline-graph2d.min.css
  27. +40
    -0
      includes/js/vis/dist/vis-timeline-graph2d.min.js
  28. +1448
    -0
      includes/js/vis/dist/vis.css
  29. +52833
    -0
      includes/js/vis/dist/vis.js
  30. +1
    -0
      includes/js/vis/dist/vis.js.map
  31. +1
    -0
      includes/js/vis/dist/vis.map
  32. +1
    -0
      includes/js/vis/dist/vis.min.css
  33. +46
    -0
      includes/js/vis/dist/vis.min.js
  34. +4
    -7
      sites/admin.js
  35. +1
    -1
      templates/admin/adminHome.html
  36. +95
    -0
      templates/admin/adminUsers.html
  37. +20
    -0
      templates/admin/analytics.html
  38. +117
    -30
      utils/sql.js
  39. +0
    -24
      utils/utils.js

+ 10
- 5
admin/admin.js View File

@ -27,27 +27,31 @@ module.exports=
*/
main: function(request, clientAddress, templateContext, filename)
{
console.log("admin main called");
return new Promise(function(resolve, reject)
{
//if logged in
if(request.session && request.session.user)
{
console.log(filename);
templateContext.loggedIn = true;
utils.getPostData(request).then(function (postData)
{
console.log("temp 1");
var page = "./adminHome.js";
if(filename.includes('/downloads'))
{
page = "./adminDownloads.js";
console.log("downloads time")
}
else if(filename.includes("/posts"))
{
page = "./posts.js";
}
else if(filename.includes("/users"))
{
page = "./users.js";
}
else if(filename.includes("/analytics"))
{
page = "./analytics.js"
}
require(page).main(postData, templateContext).then(function(template)
{
@ -61,7 +65,8 @@ module.exports=
}
else
{
require("./login/login.js").main(request, clientAddress, templateContext).then(function()
require("./login.js").main(request, clientAddress, templateContext)
.then(function()
{
resolve();
}).catch(function(err)

+ 71
- 0
admin/analytics.js View File

@ -0,0 +1,71 @@
/** Whiskers template file */
const TEMPLATE_FILE = "admin/analytics.html";
const includes = require('../includes/includes.js');
//updates db
const sql = require('../utils/sql');
const generateData = function(templateContext)
{
return new Promise(function(resolve, reject)
{
var data = [];
sql.getTraffic().then(function(traffic)
{
var start = traffic[0].date;
var currentMonth = new Date(start.getUTCFullYear(), start.getMonth(), 1, 0,0,0);
templateContext.start = JSON.stringify(currentMonth);
var monthCount = 0;
for(var i = 0; i < traffic.length; i++)
{
var currentDate = traffic[i].date;
if(currentMonth.getMonth() != currentDate.getMonth())
{
var foo = new Object();
foo.x = currentMonth;
foo.y = monthCount;
data.push(foo);
monthCount = 0;
currentMonth = new Date(currentDate.getUTCFullYear(), currentDate.getMonth(), 1, 0,0,0);
}
monthCount = monthCount + 1;
}
templateContext.finish = JSON.stringify(currentMonth);
templateContext.dataset = JSON.stringify(data);
resolve();
});
});
};
module.exports=
{
/**
* Fetches context information for the admin blog page and handles post
* data sent regarding editing blog.
*
* @param postData posted by user
* @param templateContext json object used as the template context
* @returns {Promise} renders the template used for this page
*/
main: function(postData, templateContext)
{
return new Promise(function(resolve, reject)
{
Promise.all([includes.fetchTemplate(TEMPLATE_FILE), generateData(templateContext)]).then(function(template)
{
resolve(template[0]);
}).catch(function(error)
{
console.log("error in add admin blog.js");
reject(error);
});
});
}
};

+ 0
- 24
admin/analytics/analytics.js View File

@ -1,24 +0,0 @@
module.exports=
{
main: function(postData)
{
return new Promise(function(resolve, reject)
{
Promise.all([utils.include("./admin/addCategory.html"),
printCategories(),
processPost(postData)]).then(function(html)
{
resolve("<div class=\"col-md-6\">" +
html.join('') +
"</div></div>");
}).catch(function(error)
{
console.log("error in addCategory.js");
reject(error);
})
});
}
};

admin/login/login.js → admin/login.js View File

@ -1,8 +1,8 @@
//file io
const utils = require('../../utils/utils.js');
const utils = require('../utils/utils.js');
//update db
const sql = require('../../utils/sql');
const sql = require('../utils/sql');
const qs = require('querystring');
@ -23,7 +23,7 @@ const processLogin = function(request, clientAddress, templateContext)
{
if(DEBUG)
{
//what actually logs in the user
//logs in as first user in DB
request.session.user = 1;
console.log("user has logged in");
templateContext.goodLoginAttempt = true;

+ 175
- 0
admin/users.js View File

@ -0,0 +1,175 @@
/**
* File which deals with adding and removing downloads from
* the admin section of the website.
*
* @author Jeffery Russell 6-30-18
*/
/** Whiskers template file */
const TEMPLATE_FILE = "admin/adminUsers.html";
const includes = require('../includes/includes.js');
//updates db
const sql = require('../utils/sql');
//parses post data
const qs = require('querystring');
/**
* Processes post requests from the addDownload form
*
* @param postData
* @returns {*|Promise}
*/
const addUserPostData = function(postData)
{
return new Promise(function(resolve, reject)
{
const post = qs.parse(postData);
if(post.add_user)
{
sql.addUser(post.add_user_name, post.add_user_password)
.then(function()
{
resolve();
}).catch(function(error)
{
reject(error);
})
}
else
{
resolve();
}
});
};
/**
* Removes a download if requested by the
* post data from an admin.
*/
const removeUserPost = function(postData)
{
return new Promise(function(resolve, reject)
{
const post = qs.parse(postData);
console.log(post);
if(post.delete_user)
{
console.log("Removing user: " + post.delete_user);
sql.removeUser(post.delete_user).then(function()
{
resolve();
}).catch(function(err)
{
reject(err);
});
}
else
{
resolve();
}
});
};
/**
* Processes post data to determine if the user requested that
* a user be updated in the database.
*/
const editUserPost = function(postData, templateContext)
{
return new Promise(function(resolve, reject)
{
const post = qs.parse(postData);
if(post.edit_user)
{
sql.getUserByID(post.edit_user).then(function(user)
{
if(user.length == 1)
{
templateContext.edit_user = post.edit_user;
templateContext.user_name = user[0].user_name;
resolve();
}
else
{
resolve();
}
}).catch(function(err)
{
reject(err);
});
}
else if(post.edit_user_2)
{
sql.updateUser(post.edit_user_2, post.edit_user_name, post.edit_user_password)
.then(function()
{
resolve();
})
}
else
{
resolve();
}
});
};
/**
* Fetches the download items in the database so that
* the template engine can use it to display them in
* a table.
*
* @param templateContext-- context item used by whiskers
* @returns {Promise}
*/
const getUserInformation = function(templateContext)
{
return new Promise(function(resolve, reject)
{
sql.getAllUsers().then(function(users)
{
templateContext.users = users;
resolve();
}).catch(function(error)
{
reject(error);
});
});
};
module.exports=
{
/** Fetches context information for the template and handles
* post data for the downloads.
*
* @param postData posted by user
* @param templateContext json object used as the template context
* @returns {Promise} renders the template used for this page
*/
main: function(postData, templateContext)
{
return new Promise(function(resolve, reject)
{
Promise.all([includes.fetchTemplate(TEMPLATE_FILE),
addUserPostData(postData),
removeUserPost(postData),
editUserPost(postData, templateContext),
getUserInformation(templateContext)]).then(function(template)
{
resolve(template[0]);
}).catch(function(error)
{
console.log("error in add downloads.js");
reject(error);
});
});
}
};

+ 1
- 1
includes/css/bootstrap.css View File

@ -9706,5 +9706,5 @@ footer .footer-col
footer .footer-below
{
padding: 25px 0;
background-color: #2C3E50;
background-color: #1a252f;
}

+ 18
- 2
includes/html/adminHeader.html View File

@ -41,7 +41,17 @@
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
<script src="../../admin/login/login.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
<script src="/includes/js/vis/dist/vis.js"></script>
<link href="/includes/js/vis/dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" />
</head>
@ -58,7 +68,7 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="https://jrtechs.net">Live Blog<span class="sr-only">(current)</span></a>
<a class="nav-link" href="https://jrtechs.net">Live Blog</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin">Admin Home</a>
@ -69,6 +79,12 @@
<li class="nav-item">
<a class="nav-link" href="/admin/downloads">Downloads</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/analytics">Analytics</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="nav-item">

BIN
includes/js/vis/dist/img/network/acceptDeleteIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/addNodeIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/backIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/connectIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/cross.png View File

Before After
Width: 7  |  Height: 7  |  Size: 18 KiB

BIN
includes/js/vis/dist/img/network/cross2.png View File

Before After
Width: 5  |  Height: 5  |  Size: 17 KiB

BIN
includes/js/vis/dist/img/network/deleteIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/downArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
includes/js/vis/dist/img/network/editIcon.png View File

Before After
Width: 24  |  Height: 24  |  Size: 20 KiB

BIN
includes/js/vis/dist/img/network/leftArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
includes/js/vis/dist/img/network/minus.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.0 KiB

BIN
includes/js/vis/dist/img/network/plus.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.2 KiB

BIN
includes/js/vis/dist/img/network/rightArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
includes/js/vis/dist/img/network/upArrow.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

BIN
includes/js/vis/dist/img/network/zoomExtends.png View File

Before After
Width: 30  |  Height: 30  |  Size: 4.4 KiB

+ 34
- 0
includes/js/vis/dist/vis-graph3d.min.js
File diff suppressed because it is too large
View File


+ 1
- 0
includes/js/vis/dist/vis-network.min.css
File diff suppressed because it is too large
View File


+ 42
- 0
includes/js/vis/dist/vis-network.min.js
File diff suppressed because it is too large
View File


+ 1
- 0
includes/js/vis/dist/vis-timeline-graph2d.min.css
File diff suppressed because it is too large
View File


+ 40
- 0
includes/js/vis/dist/vis-timeline-graph2d.min.js
File diff suppressed because it is too large
View File


+ 1448
- 0
includes/js/vis/dist/vis.css
File diff suppressed because it is too large
View File


+ 52833
- 0
includes/js/vis/dist/vis.js
File diff suppressed because it is too large
View File


+ 1
- 0
includes/js/vis/dist/vis.js.map
File diff suppressed because it is too large
View File


+ 1
- 0
includes/js/vis/dist/vis.map
File diff suppressed because it is too large
View File


+ 1
- 0
includes/js/vis/dist/vis.min.css
File diff suppressed because it is too large
View File


+ 46
- 0
includes/js/vis/dist/vis.min.js
File diff suppressed because it is too large
View File


+ 4
- 7
sites/admin.js View File

@ -1,9 +1,6 @@
//sending static content
const includes = require('../includes/includes.js');
//used for file IO
const utils = require('../utils/utils.js');
//used to append static content to result
const contentLoader = require('../includes/staticContentServer.js');
@ -34,18 +31,18 @@ module.exports=
const clientAddress = (request.headers['x-forwarded-for'] || '').split(',')[0]
|| request.connection.remoteAddress;
result.writeHead(200, {'Content-Type': 'text/html'});
const file = "../admin/admin.js";
var templateContext = Object();
Promise.all([includes.printAdminHeader(templateContext),
Promise.all([includes.fetchTemplate("admin/adminMain.html"),
includes.printAdminHeader(templateContext),
require(file).main(request, clientAddress, templateContext, filename),
includes.printFooter(templateContext),
includes.fetchTemplate("admin/adminMain.html")]).then(function(content)
]).then(function(content)
{
result.write(whiskers.render(content.join(''), templateContext));
result.write(whiskers.render(content[0], templateContext));
result.end();
}).catch(function(err)

+ 1
- 1
templates/admin/adminHome.html View File

@ -94,7 +94,7 @@
<td>{cat.name}</td>
<td>{cat.url}</td>
<td>{cat.category_id}</td>
</tr>>
</tr>
{/for}
</tbody>
</table>

+ 95
- 0
templates/admin/adminUsers.html View File

@ -0,0 +1,95 @@
{if edit_user}
<div class="row">
<div class="col-12">
<div class='blogPost'>
<h1 class="text-center">Edit User</h1>
<form action="/admin/users/" method ="post" >
<div class="form-group">
<input class="form-control" type="text" name="edit_user_name" value='{user_name}' required>
<label class="w3-label w3-validate">User Name</label>
</div>
<div class="form-group">
<input class="form-control" type="password" name="edit_user_password" value='' required>
<label class="w3-label w3-validate">Password</label>
</div>
<div>
<input type="submit" name="submit" value="Edit" class="btn btn-lg btn-secondary"/>
</div>
<input type='hidden' name='edit_user_2' value='{edit_user}'/>
</form>
</div>
<br>
</div>
</div>
{/if}
<div class="row">
<!-- Current Users -->
<div class="col-md-6">
<div class='blogPost'>
<h1 class="text-center">Users</h1>
<div class="">
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<td>User ID</td>
<td>User Name</td>
<td>Edit</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{for user in users}
<tr>
<td>
{user.user_id}
</td>
<td>
{user.user_name}
</td>
<td>
<form action="/admin/users/" method ="post" >
<input type="submit" name="submit" value="Edit" class="btn btn-secondary"/>
<input type='hidden' name='edit_user' value='{user.user_id}' />
</form>
</td>
<td>
<form action="/admin/users/" method ="post" >
<input type="submit" name="submit" value="Delete" class="btn btn-secondary"/>
<input type='hidden' name='delete_user' value='{user.user_id}' />
</form>
</td>
</tr>
{/for}
</tbody>
</table>
</div>
</div>
</div>
<!-- Add User -->
<div class="col-md-6">
<div class="blogPost">
<h1 class="text-center">Add New User</h1>
<form action="/admin/users/" method ="post" class="p-2">
<div class="form-group">
<input class="form-control" type="text" name="add_user_name" required>
<label>User Name</label>
</div>
<div class="form-group">
<input class="form-control" type="password" name="add_user_password" required>
<label>Password</label>
</div>
<div class="text-center">
<input type="submit" name="add_user" value="Create User"
class="btn btn-lg btn-secondary"/>
</div>
</form>
</div>
<br>
</div>
</div>

+ 20
- 0
templates/admin/analytics.html View File

@ -0,0 +1,20 @@
<h4 class="text-center">Traffic Graph</h4>
<div class="row">
<div class="col-12">
<div id="visualization"></div>
</div>
<br><br>
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = {dataset};
var dataset = new vis.DataSet(items);
var options = {
start: {start},
end: {finish},
};
var graph2d = new vis.Graph2d(container, dataset, options);
</script>
</div>

+ 117
- 30
utils/sql.js View File

@ -56,6 +56,60 @@ const fetch = function(sqlStatement)
};
/**
* Function used to use insert statements into the database
*
* Don't worry, the input gets sanitized
*
* @param sqlStatement
* @return the id of the new record - if there is one
*/
const insert = function(sqlStatement)
{
return new Promise(function(resolve, reject)
{
con.query(sanitizer.sanitize(sqlStatement), function (err, result)
{
if (err)
{
console.log(err);
reject();
}
resolve(result.insertId);
});
})
};
/**
* Helper function to generate a hashed password
* from a given plain text password.
*
* This uses 64 bits of entropy as the random salt
* and uses sha256 hashing method to hash the password
* combined with the salt.
*
* @param password
* @returns {Object pass: hashedPassword, salt: salt used to hash}
*/
const createHashedPassword = function(password)
{
const randBuff = crypto.randomBytes(64);
const salt = crypto.createHash('sha256').update(randBuff).digest('hex');
const hashPass = crypto.createHash('sha256')
.update(password + salt)
.digest('hex');
var hashPassObject = new Object();
hashPassObject.pass = hashPass;
hashPassObject.salt = salt;
return hashPassObject;
};
/**
* Helper function which fetches the category url for all the
* posts returned in the posts table and appends them to the
@ -95,30 +149,6 @@ const fetchWithCategoryInformation = function(sqlPosts)
module.exports=
{
/**
* Function used to use insert statements into the database
*
* Don't worry, the input gets sanitized
*
* @param sqlStatement
* @return the id of the new record - if there is one
*/
insert : function(sqlStatement)
{
return new Promise(function(resolve, reject)
{
con.query(sanitizer.sanitize(sqlStatement), function (err, result)
{
if (err)
{
console.log(err);
reject();
}
resolve(result.insertId);
});
})
},
/**
* function which fetches the sql info on a post based on it's sql id
* @param id
@ -187,7 +217,7 @@ module.exports=
*/
getCategories : function()
{
var q = "select * from categories";
const q = "select * from categories";
return fetch(q);
},
@ -343,6 +373,58 @@ module.exports=
return fetch("select * from posts order by published desc");
},
getAllUsers: function()
{
return fetch("select * from users");
},
getUserByID: function(userID)
{
const cleanID = sanitizer.sanitize(userID);
const q = "select * from users where user_id='" + cleanID + "'";
return fetch(q);
},
removeUser: function(user_id)
{
const cleanID = sanitizer.sanitize(user_id);
return insert("delete from users where user_id='" + cleanID + "'");
},
addUser: function(username, password)
{
const cleanName = sanitizer.sanitize(username);
const cleanPassword = sanitizer.sanitize(password);
const hashedPassword = createHashedPassword(cleanPassword);
const q = "insert into users(user_name, password, salt) values('" + cleanName + "'," +
"'" + hashedPassword.pass + "','" + hashedPassword.salt + "')";
return insert(q);
},
updateUser: function(userID, username, password)
{
const cleanID = sanitizer.sanitize(userID);
const cleanName = sanitizer.sanitize(username);
const cleanPassword = sanitizer.sanitize(password);
const hashedPassword = createHashedPassword(cleanPassword);
const q = "update users " +
"set user_name='" + cleanName + "'" +
",password='" + hashedPassword.pass + "'" +
",salt='" + hashedPassword.salt + "'" +
" where user_id='" + cleanID + "'";
return insert(q);
},
/**
* Fetches the sql category information based on it's id
@ -397,7 +479,7 @@ module.exports=
(sqlRow[0].download_count + 1) + "' where download_id='" +
sqlRow[0].download_id + "'";
console.log(q);
module.exports.insert(q).then(function(r)
insert(q).then(function(r)
{
resolve(sqlRow);
}).catch(function(err)
@ -436,7 +518,7 @@ module.exports=
const q = "insert into downloads (name, file, download_count) " +
"values('" + name + "', '" + file + "', '0')";
return module.exports.insert(q);
return insert(q);
},
@ -448,7 +530,7 @@ module.exports=
{
const q = "delete from downloads where download_id='" + id + "'";
return module.exports.insert(q);
return insert(q);
},
@ -478,7 +560,7 @@ module.exports=
console.log(q);
return module.exports.insert(q);
return insert(q);
},
@ -551,6 +633,11 @@ module.exports=
const q = "insert into traffic_log (url, ip, date) values " +
"('" + page + "', '" + ip + "', now())";
module.exports.insert(q);
insert(q);
},
getTraffic: function()
{
return fetch("select * from traffic_log");
}
};

+ 0
- 24
utils/utils.js View File

@ -149,29 +149,5 @@ module.exports=
print404: function()
{
return this.include("includes/html/404.html");
},
/**
* Displays 404 error to user
*
* @param result
* @returns {*}
*/
printWrongHost: function()
{
return this.include("includes/html/incorrectHost.html");
},
/**
* Displays a ban message to the user
*
* @param result
* @returns {*}
*/
printBannedPage: function()
{
return this.include("includes/html/banHammer.html");
}
};

Loading…
Cancel
Save