diff --git a/backend/core/core.js b/backend/core/core.js index 8b7fd3f..915bb5c 100644 --- a/backend/core/core.js +++ b/backend/core/core.js @@ -19,6 +19,12 @@ let settings = { HIDE_LOGIN: false, BLOG_UPLOADING: false, + CD_RSS: false, + CD_AP: false, + + WEBSITE_NAME: "", + PLAUSIBLE_URL: "", + USER_MINIMUM_PASSWORD_LENGTH: 7, BLOG_MINIMUM_TITLE_LENGTH: 7, @@ -372,9 +378,18 @@ async function _getSettings() { let found_value = await prisma.setting.findUnique({ where: { id: key } }); if (!found_value) return; - return (settings[key] = JSON.parse(found_value.value)); + let value; + // Parse JSON if possible + try { + value = JSON.parse(found_value.value); + } catch { + value = found_value.value; + } + + return (settings[key] = value); }); } + async function getSetting(key, { parse = true }) { if (!settings[key]) return null; diff --git a/backend/core/internal_api.js b/backend/core/internal_api.js index 1e64863..08729aa 100644 --- a/backend/core/internal_api.js +++ b/backend/core/internal_api.js @@ -43,9 +43,7 @@ async function postSetting(request, response) { 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" }); - await core.postSetting(request.body.setting_name, request.body.value); - - response.json({ success: true }); + response.json(await core.postSetting(request.body.setting_name, request.body.value)); } async function deleteImage(req, res) { // TODO: Permissions for deleting image diff --git a/backend/page_scripts.js b/backend/page_scripts.js index f578187..1ef56f0 100644 --- a/backend/page_scripts.js +++ b/backend/page_scripts.js @@ -2,7 +2,8 @@ const external = require("./core/external_api"); const core = require("./core/core"); function getDefaults(req) { - return { logged_in_user: req.session.user, website_name: process.env.WEBSITE_NAME, settings: core.settings }; + // TODO: Fix reference to website_name + return { logged_in_user: req.session.user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings }; } async function index(request, response) { diff --git a/frontend/public/css/admin.css b/frontend/public/css/admin.css index ea562f1..83d1ee1 100644 --- a/frontend/public/css/admin.css +++ b/frontend/public/css/admin.css @@ -2,6 +2,7 @@ body { margin: 0; background-color: #111; color: white; + font-family: Verdana, Geneva, Tahoma, sans-serif; } .header { @@ -58,11 +59,18 @@ button:focus { background-color: #122d57; } -button.good { +button.good, +a.good { background-color: #015b01; } -button.bad { +button.yellow, +a.yellow { + background-color: #4a4a00; +} + +button.bad, +a.bad { background-color: #8e0000; } @@ -75,9 +83,78 @@ button.bad { display: flex; flex-direction: row; } -.page .horizontal-button-container button { - width: 100px; +.page .horizontal-button-container button, +.page .horizontal-button-container a { + width: 120px; min-height: 30px; + text-decoration: none; + display: flex; + margin-right: 5px; +} +.page .horizontal-button-container button span, +.page .horizontal-button-container a span { + margin: auto; +} +.page .horizontal-button-container button:last-of-type, +.page .horizontal-button-container a:last-of-type { + margin-right: 0; +} +.page .blog-admin { + margin-bottom: 10px; +} +.page .pagination { + display: flex; + flex-direction: row; + width: 100%; + margin: 0 auto; +} +.page .pagination a { + height: 40px; + width: 150px; + margin-right: 5px; + display: flex; + text-decoration: none; + background-color: #222; +} +.page .pagination a span { + margin: auto; + color: white; + font-size: 20px; +} +.page .pagination a:last-of-type { + margin-right: 0; + margin-left: 5px; +} +.page .pagination a.disabled { + filter: brightness(50%); +} +.page .pagination .page-list { + flex-grow: 1; + display: flex; + flex-direction: row; + margin-bottom: 50px; +} +.page .pagination .page-list a { + width: 40px; + height: 40px; + display: flex; + text-decoration: none; + background-color: #222; + border-radius: 10px; + margin: 0 10px 0 0; +} +.page .pagination .page-list a span { + margin: auto; + color: white; +} +.page .pagination .page-list a:first-of-type { + margin: auto 10px auto auto; +} +.page .pagination .page-list a:last-of-type { + margin: auto auto auto 0; +} +.page .pagination .page-list a.active { + background-color: #993d00; } .hidden { @@ -86,7 +163,7 @@ button.bad { @media screen and (max-width: 1010px) { .page { - width: 100%; + width: 95%; } } .container { @@ -95,32 +172,73 @@ button.bad { padding: 10px 20px; box-sizing: border-box; } -.container .settings-group .settings-row { +.container .category-navigation { + width: 100%; + height: 30px; + display: flex; + margin-bottom: 15px; +} +.container .category-navigation button { + width: 100%; + margin-right: 5px; + border-radius: 5px; + text-decoration: none; + display: flex; +} +.container .category-navigation button span { + margin: auto; + color: white; +} +.container .category-navigation button:not(.active) { + background-color: #4c515e; +} +.container .category-navigation button:hover, +.container .category-navigation button:focus { + filter: brightness(50%); +} +.container .setting-row { display: flex; flex-direction: row; - padding: 4px; + min-height: 30px; + padding: 5px; box-sizing: border-box; } -.container .settings-group .settings-row .label { +.container .setting-row .setting-title { + font-size: 18px; margin: auto auto auto 0; } -.container .settings-group .settings-row .button-group { +.container .setting-row .setting-toggleable { + min-width: 150px; display: flex; - flex-direction: row; margin: auto 0 auto auto; } -.container .settings-group .settings-row .button-group .spinner { - margin: auto 10px auto auto; +.container .setting-row .setting-toggleable .spinner { + margin: auto 0 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 .setting-row .setting-toggleable input { + padding: 0; + margin: auto 0 auto auto; + width: 100px; + box-sizing: border-box; + text-align: center; + border-radius: 10px; } -.container .settings-group .settings-row:nth-child(even) { - background-color: rgba(0, 0, 0, 0.2); +.container .setting-row .setting-toggleable input[type=text] { + width: 350px; +} +.container .setting-row .setting-toggleable button { + width: 125px; + margin-left: auto; + height: 30px; +} +.container .setting-row:nth-child(even) { + background-color: #1c1c1c; +} +.container .setting-row:nth-child(odd) { + background-color: #191919; } @keyframes spin { diff --git a/frontend/public/css/admin.scss b/frontend/public/css/admin.scss index 58efe6a..a2369b9 100644 --- a/frontend/public/css/admin.scss +++ b/frontend/public/css/admin.scss @@ -6,35 +6,89 @@ padding: 10px 20px; box-sizing: border-box; - .settings-group { - .settings-row { + .category-navigation { + width: 100%; + height: 30px; + display: flex; + margin-bottom: 15px; + + button { + width: 100%; + margin-right: 5px; + border-radius: 5px; + text-decoration: none; display: flex; - flex-direction: row; - padding: 4px; - box-sizing: border-box; - .label { - margin: auto auto auto 0; + + span { + margin: auto; + color: white; + } + } + + button:not(.active) { + background-color: #4c515e; + } + + button:hover, + button:focus { + filter: brightness(50%); + } + + button:active { + // display: none; + } + } + + .setting-row { + display: flex; + flex-direction: row; + min-height: 30px; + padding: 5px; + box-sizing: border-box; + + .setting-title { + font-size: 18px; + margin: auto auto auto 0; + } + + .setting-toggleable { + min-width: 150px; + display: flex; + margin: auto 0 auto auto; + + .spinner { + margin: auto 0 auto auto; + animation: spin 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; } - .button-group { - display: flex; - flex-direction: row; + input { + padding: 0; 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; - } + // width: 100%; + width: 100px; + box-sizing: border-box; + text-align: center; + border-radius: 10px; + } + + input[type="text"] { + width: 350px; + } + + button { + width: 125px; + margin-left: auto; + height: 30px; } } - .settings-row:nth-child(even) { - background-color: #00000033; - } + } + .setting-row:nth-child(even) { + background-color: #1c1c1c; + } + .setting-row:nth-child(odd) { + background-color: #191919; } } @keyframes spin { diff --git a/frontend/public/js/admin.js b/frontend/public/js/admin.js index 4987bd6..3bfa6fd 100644 --- a/frontend/public/js/admin.js +++ b/frontend/public/js/admin.js @@ -9,10 +9,11 @@ async function toggleState(setting_name, new_value, element_id) { const response = await request("/setting", "POST", form); + qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden"); + + // TODO: On failure, notify the user // 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"; @@ -30,3 +31,42 @@ async function toggleState(setting_name, new_value, element_id) { qs(`#${element_id}`).setAttribute("onclick", add_function); } } + +async function updateValue(key_pressed, setting_name, new_value, element_id) { + if (key_pressed !== 13) return; + // Show spinner + qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.remove("hidden"); + + const form = { + setting_name: setting_name, + value: new_value, + }; + + const response = await request("/setting", "POST", form); + + qs(`#${element_id}`).parentNode.querySelector(".spinner").classList.add("hidden"); + + // TODO: On failure, notify the user + // Check response for errors + if (response.body.success) { + } +} + +function toggleActiveCategory(category_id) { + // Pages ---------------- + // Hide all pages + qsa(".category-page").forEach((page) => { + page.classList.add("hidden"); + }); + // Show requested page + qs(`#${category_id}`).classList.remove("hidden"); + + // Navigation bar ------- + // Unactive all buttons + qsa(".category-navigation button").forEach((btn) => { + btn.classList.remove("active"); + }); + // Active current page + qs(`#${category_id}-nav-btn`).classList.add("active"); + qs(`#${category_id}-nav-btn`).blur(); // Unfocus the button +} diff --git a/frontend/public/js/generic.js b/frontend/public/js/generic.js index 5072956..6a035a5 100644 --- a/frontend/public/js/generic.js +++ b/frontend/public/js/generic.js @@ -2,6 +2,7 @@ const qs = (selector) => document.querySelector(selector); const qsa = (selector) => document.querySelectorAll(selector); +// TODO: Try/Catch for failed requests async function request(url, method, body) { const response = await fetch(url, { method: method, diff --git a/frontend/views/admin.ejs b/frontend/views/admin.ejs index c6f3451..ff02d16 100644 --- a/frontend/views/admin.ejs +++ b/frontend/views/admin.ejs @@ -13,35 +13,38 @@
-
-

Accounts

-
-
Account registration
-
- - <% if (!settings.ACCOUNT_REGISTRATION ) { %> - - <% } else { %> - - <%}%> -
-
+
+ + + + +
+
+ + <%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'ACCOUNT_REGISTRATION', name_pretty: 'Account registration', enabled: + settings.ACCOUNT_REGISTRATION}}) %> + + <%- include("partials/admin-setting-toggle.ejs", {setting: {name: 'HIDE_LOGIN', name_pretty: 'Hide Login', enabled: settings.HIDE_LOGIN}}) %> + + <%- include("partials/admin-setting-number.ejs", {setting: {name:'USER_MINIMUM_PASSWORD_LENGTH', name_pretty: 'Minimum Password Length', value: + settings.USER_MINIMUM_PASSWORD_LENGTH}}) %> +
-
-
Hide Login
-
- - <% if (!settings.HIDE_LOGIN ) { %> - - <% } else { %> - - <%}%> -
-
+ + + +
diff --git a/frontend/views/partials/admin-setting-number.ejs b/frontend/views/partials/admin-setting-number.ejs new file mode 100644 index 0000000..7002e4f --- /dev/null +++ b/frontend/views/partials/admin-setting-number.ejs @@ -0,0 +1,15 @@ +
+
<%=setting.name_pretty%>
+
+ + +
+
diff --git a/frontend/views/partials/admin-setting-text.ejs b/frontend/views/partials/admin-setting-text.ejs new file mode 100644 index 0000000..989e806 --- /dev/null +++ b/frontend/views/partials/admin-setting-text.ejs @@ -0,0 +1,15 @@ +
+
<%=setting.name_pretty%>
+
+ + +
+
diff --git a/frontend/views/partials/admin-setting-toggle.ejs b/frontend/views/partials/admin-setting-toggle.ejs new file mode 100644 index 0000000..d9def8f --- /dev/null +++ b/frontend/views/partials/admin-setting-toggle.ejs @@ -0,0 +1,13 @@ +
+
<%=setting.name_pretty%>
+
+ + <% if (!setting.enabled) { %> + + <% } else { %> + + <%}%> +
+