diff --git a/backend/core/core.js b/backend/core/core.js index dce8d11..46997f2 100644 --- a/backend/core/core.js +++ b/backend/core/core.js @@ -14,9 +14,10 @@ const s3 = new S3Client({ }); const settings = require("../settings"); +// FIXME: This does not properly validate that a user was created async function registerUser(username, password, options) { const new_user = await prisma.user.create({ data: { username: username, password: password, ...options } }); - await settings.setSetupComplete(); + settings.act("SETUP_COMPLETE", true); if (new_user) return { success: true, message: `Successfully created ${new_user.username}` }; } diff --git a/backend/core/form_validation.js b/backend/core/form_validation.js index 6599105..eb6b506 100644 --- a/backend/core/form_validation.js +++ b/backend/core/form_validation.js @@ -1,6 +1,3 @@ -const { PrismaClient } = require("@prisma/client"); -const prisma = new PrismaClient(); -const core = require("./core"); const settings = require("../settings"); async function userRegistration(username, password) { @@ -9,20 +6,16 @@ async function userRegistration(username, password) { // TODO: Admin customizable minimum password length if (password.length < 4) return { success: false, message: "Password not long enough" }; - const existing_user = await core.getUser({ username: username }); - - if (existing_user.success) return { success: false, message: "Username already exists" }; + // Check if username only uses URL safe characters + if (!is_url_safe(username)) return { success: false, message: "Username is not URL safe" }; + // All good! Validation complete return { success: true }; } -async function userLogin(username, password) { - const existing_user = await core.getUser({ username: username }); - - if (!existing_user.success) return { success: false, message: "User does not exist" }; - if (existing_user.role === "LOCKED") return { success: false, message: "Account is locked: Contact your administrator" }; - - return { success: true, data: existing_user.data }; +function is_url_safe(str) { + const pattern = /^[A-Za-z0-9\-_.~]+$/; + return pattern.test(str); } -module.exports = { userRegistration, userLogin }; +module.exports = { userRegistration }; diff --git a/backend/core/internal_api.js b/backend/core/internal_api.js index ea3b2d7..60d4ff0 100644 --- a/backend/core/internal_api.js +++ b/backend/core/internal_api.js @@ -3,35 +3,37 @@ const core = require("./core"); const settings = require("../settings"); async function registerUser(username, password) { - const registration_allowed = await settings.userRegistrationAllowed(); // Check if user registration is allowed + // Get current and relevant settings + const s = Promise.all([settings.act("ACCOUNT_REGISTRATION"), settings.act("SETUP_COMPLETE")]); const form_valid = await validate.userRegistration(username, password); // Check form for errors - const is_setup_complete = await settings.setupComplete(); - let role = is_setup_complete ? undefined : "ADMIN"; + // Set variables for easy reading + const registration_allowed = s[0]; + const setup_complete = s[1]; + + if (!registration_allowed && setup_complete) return { success: false, message: "Registration is disabled" }; // Registration disabled + if (!form_valid.success) return form_valid; // Registration details did not validate + + // Does a user using that username exist already? + const existing_user = await core.getUser({ username: username }); + if (existing_user.success) return { success: false, message: "Username is taken" }; // Register the user in the database - if (registration_allowed && form_valid.success) return await core.registerUser(username, password, { role: role }); + const role = setup_complete ? undefined : "ADMIN"; + const registration_status = await core.registerUser(username, password, { role: role }); - // Something went wrong! - return { success: false, message: form_valid.message }; + if (registration_status.success) return registration_status; + else return registration_status; } async function loginUser(username, password) { - const user = await validate.userLogin(username); - if (!user.success) return user; + // Get the user by username + const existing_user = await core.getUser({ username: username }); - return { success: true, data: { username: user.data.username, id: user.data.id, password: user.data.password } }; + // Check for errors or problems + if (!existing_user.success) return { success: false, message: "User does not exist" }; + if (existing_user.role === "LOCKED") return { success: false, message: "Account is locked: Contact your administrator" }; + return { success: true, data: { username: existing_user.data.username, id: existing_user.data.id, password: existing_user.data.password } }; } -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 }; +module.exports = { registerUser, loginUser }; diff --git a/backend/page_scripts.js b/backend/page_scripts.js index 685cfbc..6a92fb3 100644 --- a/backend/page_scripts.js +++ b/backend/page_scripts.js @@ -4,7 +4,7 @@ const settings = require("./settings"); async function index(request, response) { // Check if the master admin has been created - const is_setup_complete = (await settings.setupComplete()) || false; + const is_setup_complete = (await settings.act("SETUP_COMPLETE")) || false; if (!is_setup_complete) return response.redirect("/register"); response.render("index.ejs", { user: request.session.user || null, website_name: process.env.WEBSITE_NAME }); @@ -36,8 +36,10 @@ async function registerPost(request, response) { async function loginPost(request, response) { const login = await internal.loginUser(request.body.username, request.body.password); + if (!login.success) return response.json(login); + const password_match = await bcrypt.compare(request.body.password, login.data.password); - if (!password_match) return { success: false, message: "Incorrect password" }; + if (!password_match) return response.json({ success: false, message: "Incorrect password" }); request.session.user = { username: login.data.username, id: login.data.id }; response.json({ success: true }); diff --git a/backend/settings.js b/backend/settings.js index 04d63bf..ef03781 100644 --- a/backend/settings.js +++ b/backend/settings.js @@ -1,14 +1,40 @@ const persistent_setting = require("node-persist"); -persistent_setting.init({ dir: "data/" }); +persistent_setting.init({ dir: "data/site/" }); -const setupComplete = async () => (await persistent_setting.getItem("SETUP_COMPLETE")) || false; -const userRegistrationAllowed = async () => { - const setting = await persistent_setting.getItem("REGISTRATION_ALLOWED"); - if (typeof setting == "undefined") return true; - console.log(setting); - return setting == "true"; +let settings = { + SETUP_COMPLETE: false, + ACCOUNT_REGISTRATION: false, + BLOG_UPLOADING: false, + + USER_MINIMUM_PASSWORD_LENGTH: 6, }; -const setSetupComplete = async () => await persistent_setting.setItem("SETUP_COMPLETE", "true"); -const setUserRegistrationAllowed = (new_value) => persistent_setting.setItem("REGISTRATION_ALLOWED", String(new_value)); -module.exports = { setupComplete, userRegistrationAllowed, setSetupComplete, setUserRegistrationAllowed }; +async function act(key, value) { + // Change value if we have a value field + if (value) { + // Just incase the value is a string instead of a boolean + value = String(value).toLowerCase() === "true"; + + await persistent_setting.setItem(key, value); + settings[key] = value; + } + + // Return the current setting + return settings[key]; +} + +function getSettings() { + return settings; +} + +// Initialize our settings +setTimeout(async () => { + for (let i = 0; Object.keys(settings).length > i; i++) { + const setting_title = Object.keys(settings)[i]; + const setting_value = await persistent_setting.getItem(setting_title); + + settings[setting_title] = setting_value == true || setting_value == "true"; + } +}, 3000); + +module.exports = { act, getSettings }; diff --git a/frontend/public/css/new-blog.css b/frontend/public/css/new-blog.css index bb7b673..a17a1c8 100644 --- a/frontend/public/css/new-blog.css +++ b/frontend/public/css/new-blog.css @@ -42,6 +42,11 @@ width: 100%; margin-bottom: 10px; } +.e-image-area .placeholder { + margin: auto; + grid-row: 1/-1; + grid-column: 1/-1; +} .e-image-area .image { height: 100px; aspect-ratio: 16/9; diff --git a/frontend/public/css/new-blog.scss b/frontend/public/css/new-blog.scss index b616e7e..4905ec9 100644 --- a/frontend/public/css/new-blog.scss +++ b/frontend/public/css/new-blog.scss @@ -49,6 +49,12 @@ $background-body: #222; width: 100%; margin-bottom: 10px; + .placeholder { + margin: auto; + grid-row: 1 / -1; + grid-column: 1 / -1; + } + .image { height: 100px; aspect-ratio: 16/9; diff --git a/frontend/views/blogNew.ejs b/frontend/views/blogNew.ejs index aadc988..ca4e0c4 100644 --- a/frontend/views/blogNew.ejs +++ b/frontend/views/blogNew.ejs @@ -23,9 +23,7 @@
-
- -
+
Drop images here