parent
c9d13320d5
commit
e60f58a975
|
@ -2,3 +2,4 @@
|
|||
/node_modules
|
||||
/data
|
||||
*.code-workspace
|
||||
.vscode/settings.json
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
const { PrismaClient } = require("@prisma/client");
|
||||
const prisma = new PrismaClient();
|
||||
const core = require("./core");
|
||||
|
||||
// Check if user registration is allowed via the settings
|
||||
function registration() {
|
||||
return true;
|
||||
}
|
||||
const settings = require("../settings");
|
||||
|
||||
async function userRegistration(username, password) {
|
||||
if (!username) return { success: false, message: "No username provided" };
|
||||
|
@ -29,4 +25,4 @@ async function userLogin(username, password) {
|
|||
return { success: true, data: existing_user.data };
|
||||
}
|
||||
|
||||
module.exports = { registration, userRegistration, userLogin };
|
||||
module.exports = { userRegistration, userLogin };
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
const validate = require("./form_validation");
|
||||
const core = require("./core");
|
||||
const settings = require("../settings");
|
||||
|
||||
async function registerUser(username, password) {
|
||||
const registration_allowed = validate.registration(); // Check if user registration is allowed
|
||||
const registration_allowed = await settings.userRegistrationAllowed(); // Check if user registration is allowed
|
||||
const form_valid = await validate.userRegistration(username, password); // Check form for errors
|
||||
|
||||
const is_setup_complete = await settings.setupComplete();
|
||||
let role = is_setup_complete ? "ADMIN" : null;
|
||||
|
||||
// Register the user in the database
|
||||
if (registration_allowed && form_valid.success) return await core.registerUser(username, password);
|
||||
if (registration_allowed && form_valid.success) return await core.registerUser(username, password, { role: role });
|
||||
|
||||
// Something went wrong!
|
||||
return { success: false, message: form_valid.message };
|
||||
|
@ -19,4 +23,15 @@ async function loginUser(username, password) {
|
|||
return { success: true, data: { username: user.data.username, id: user.data.id, password: user.data.password } };
|
||||
}
|
||||
|
||||
module.exports = { registerUser, loginUser };
|
||||
async function getUser({ id, username } = {}) {
|
||||
let user;
|
||||
if (id) user = await core.getUser({ id: id });
|
||||
else if (username) user = await core.getUser({ username: username });
|
||||
|
||||
// Make sure we only get important identifier and nothing sensitive!
|
||||
if (user.success) return { success: true, data: { username: user.data.username, id: user.data.id, role: user.data.role } };
|
||||
|
||||
return { success: false, message: "No user found" };
|
||||
}
|
||||
|
||||
module.exports = { registerUser, loginUser, getUser };
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
const internal = require("./core/internal_api");
|
||||
const bcrypt = require("bcrypt");
|
||||
const persistent_setting = require("node-persist");
|
||||
persistent_setting.init({ dir: "data/" });
|
||||
const settings = require("./settings");
|
||||
|
||||
async function index(request, response) {
|
||||
// Check if the master admin has been created
|
||||
const is_setup_complete = (await persistent_setting.getItem("SETUP_COMPLETE")) || false;
|
||||
const is_setup_complete = (await settings.setupComplete()) || false;
|
||||
if (!is_setup_complete) return response.redirect("/register");
|
||||
|
||||
response.render("index.ejs", { website_name: process.env.WEBSITE_NAME });
|
||||
|
@ -17,6 +16,13 @@ function register(request, response) {
|
|||
function login(request, response) {
|
||||
response.render("login.ejs", { website_name: process.env.WEBSITE_NAME });
|
||||
}
|
||||
async function admin(request, response) {
|
||||
const reg_allowed = await settings.userRegistrationAllowed();
|
||||
response.render("admin.ejs", {
|
||||
website_name: process.env.WEBSITE_NAME,
|
||||
settings: { registration_enabled: reg_allowed },
|
||||
});
|
||||
}
|
||||
|
||||
async function registerPost(request, response) {
|
||||
const hashedPassword = await bcrypt.hash(request.body.password, 10); // Hash the password for security :^)
|
||||
|
@ -31,4 +37,15 @@ async function loginPost(request, response) {
|
|||
request.session.user = { username: login.data.username, id: login.data.id };
|
||||
response.json({ success: true });
|
||||
}
|
||||
module.exports = { index, register, login, registerPost, loginPost };
|
||||
|
||||
async function settingPost(request, response) {
|
||||
const user = await internal.getUser({ id: request.session.user.id });
|
||||
|
||||
if (!user.success) return response.json({ success: false, message: user.message });
|
||||
if (user.data.role !== "ADMIN") return response.json({ success: false, message: "User is not permitted" });
|
||||
|
||||
if (request.body.setting_name === "ACCOUNT_REGISTRATION") settings.setUserRegistrationAllowed(request.body.value);
|
||||
|
||||
response.json({ success: true });
|
||||
}
|
||||
module.exports = { index, register, login, admin, registerPost, loginPost, settingPost };
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
const persistent_setting = require("node-persist");
|
||||
persistent_setting.init({ dir: "data/" });
|
||||
|
||||
const setupComplete = async () => (await persistent_setting.getItem("SETUP_COMPLETE")) || false;
|
||||
const userRegistrationAllowed = async () => (await persistent_setting.getItem("REGISTRATION_ALLOWED")) == "true";
|
||||
|
||||
const setUserRegistrationAllowed = (new_value) => persistent_setting.setItem("REGISTRATION_ALLOWED", String(new_value));
|
||||
|
||||
module.exports = { setupComplete, userRegistrationAllowed, setUserRegistrationAllowed };
|
|
@ -0,0 +1,133 @@
|
|||
body {
|
||||
margin: 0;
|
||||
background-color: #111;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #222;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.header .left {
|
||||
margin: auto auto auto 0;
|
||||
font-size: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
.header .left a {
|
||||
width: inherit;
|
||||
}
|
||||
.header .right {
|
||||
margin: auto 0 auto auto;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.header .right a:hover,
|
||||
.header .right a:focus {
|
||||
background-color: #333;
|
||||
}
|
||||
.header a {
|
||||
height: 100%;
|
||||
width: 130px;
|
||||
margin: auto 0;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
transition: background-color ease-in-out 0.1s;
|
||||
}
|
||||
.header a div {
|
||||
margin: auto;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #1c478a;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background-color: #122d57;
|
||||
}
|
||||
|
||||
button.good {
|
||||
background-color: #015b01;
|
||||
}
|
||||
|
||||
button.bad {
|
||||
background-color: #8e0000;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 1000px;
|
||||
min-height: 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.page .horizontal-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.page .horizontal-button-container button {
|
||||
width: 100px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1010px) {
|
||||
.page {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
background-color: #222;
|
||||
min-height: 10px;
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container .settings-group .settings-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container .settings-group .settings-row .label {
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: auto 0 auto auto;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group .spinner {
|
||||
margin: auto 10px auto auto;
|
||||
animation: spin 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
.container .settings-group .settings-row .button-group button {
|
||||
height: 30px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
.container .settings-group .settings-row:nth-child(even) {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
@use "theme";
|
||||
|
||||
.container {
|
||||
background-color: theme.$header-color;
|
||||
min-height: 10px;
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.settings-group {
|
||||
.settings-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
.label {
|
||||
margin: auto auto auto 0;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: auto 0 auto auto;
|
||||
.spinner {
|
||||
margin: auto 10px auto auto;
|
||||
animation: spin 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
button {
|
||||
height: 30px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.settings-row:nth-child(even) {
|
||||
background-color: #00000033;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
|
@ -58,6 +58,14 @@ button:focus {
|
|||
background-color: #122d57;
|
||||
}
|
||||
|
||||
button.good {
|
||||
background-color: #015b01;
|
||||
}
|
||||
|
||||
button.bad {
|
||||
background-color: #8e0000;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 1000px;
|
||||
min-height: 10px;
|
||||
|
@ -72,6 +80,10 @@ button:focus {
|
|||
min-height: 30px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1010px) {
|
||||
.page {
|
||||
width: 100%;
|
||||
|
|
|
@ -65,6 +65,13 @@ button:focus {
|
|||
background-color: #122d57;
|
||||
}
|
||||
|
||||
button.good {
|
||||
background-color: #015b01;
|
||||
}
|
||||
button.bad {
|
||||
background-color: #8e0000;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 1000px;
|
||||
min-height: 10px;
|
||||
|
@ -81,6 +88,9 @@ button:focus {
|
|||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
@media screen and (max-width: 1010px) {
|
||||
.page {
|
||||
width: 100%;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
async function toggleState(setting_name, new_value, element_id) {
|
||||
// Show spinner
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.remove("hidden");
|
||||
|
||||
// Send request
|
||||
const form = new FormData();
|
||||
form.append("setting_name", setting_name);
|
||||
form.append("value", new_value);
|
||||
|
||||
const response = await request("/setting", "POST", form);
|
||||
|
||||
// Check response for errors
|
||||
if (response.body.success) {
|
||||
qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden");
|
||||
|
||||
// Update visual to reflect current setting
|
||||
// Class
|
||||
const add_class = new_value ? "good" : "bad";
|
||||
const remove_class = new_value ? "bad" : "good";
|
||||
qs(`#${element_id}`).classList.remove(remove_class);
|
||||
qs(`#${element_id}`).classList.add(add_class);
|
||||
|
||||
// Text
|
||||
const new_text = new_value ? "Enabled" : "Disabled";
|
||||
qs(`#${element_id}`).children[0].innerText = new_text;
|
||||
|
||||
// Function
|
||||
const add_function = new_value ? "toggleState('ACCOUNT_REGISTRATION', false, this.id)" : "toggleState('ACCOUNT_REGISTRATION', true, this.id)";
|
||||
qs(`#${element_id}`).removeAttribute("onclick");
|
||||
qs(`#${element_id}`).setAttribute("onclick", add_function);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" type="text/css" href="/css/admin.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/css/theme.css" />
|
||||
<script src="/js/generic.js"></script>
|
||||
<title><%= website_name %> | Administration</title>
|
||||
</head>
|
||||
<body>
|
||||
<%- include("partials/header.ejs", {selected: 'home'}) %>
|
||||
|
||||
<div class="page">
|
||||
<div class="container">
|
||||
<div class="settings-group">
|
||||
<h1>Accounts</h1>
|
||||
<div class="settings-row">
|
||||
<div class="label">Account registration</div>
|
||||
<div class="button-group">
|
||||
<div class="spinner hidden">
|
||||
<div>↺</div>
|
||||
</div>
|
||||
<% if (!settings.registration_enabled ) { %>
|
||||
<button id="registration_toggle" onclick="toggleState('ACCOUNT_REGISTRATION', true, this.id)" class="bad"><div>Disabled</div></button>
|
||||
<% } else { %>
|
||||
<button id="registration_toggle" onclick="toggleState('ACCOUNT_REGISTRATION', false, this.id)" class="good"><div>Enabled</div></button>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include("partials/footer.ejs") %>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script defer src="/js/admin.js"></script>
|
2
yab.js
2
yab.js
|
@ -42,6 +42,7 @@ app.get("/register", checkNotAuthenticated, page_scripts.register);
|
|||
app.post("/register", checkNotAuthenticated, page_scripts.registerPost);
|
||||
|
||||
// Account Required Endpoints
|
||||
app.post("/setting", checkAuthenticated, page_scripts.settingPost);
|
||||
// app.get("/blog/new", checkAuthenticated, page_scripts.blogNew);
|
||||
// app.post("/blog", checkAuthenticated, page_scripts.postBlog);
|
||||
// app.delete("/logout", page_scripts.logout);
|
||||
|
@ -51,6 +52,7 @@ app.post("/register", checkNotAuthenticated, page_scripts.registerPost);
|
|||
|
||||
// Endpoints
|
||||
app.get("/", page_scripts.index);
|
||||
app.get("/admin", checkAuthenticated, page_scripts.admin);
|
||||
// app.get("/blog", page_scripts.blogList);
|
||||
// app.get("/blog/:id", page_scripts.blogSingle);
|
||||
// app.get("/projects", page_scripts.projectList);
|
||||
|
|
Loading…
Reference in New Issue