Compare commits
No commits in common. "e39fce5f407a3fe73eb84061c1e37ef5a02721ca" and "0dd6a7fa00dd0835481008ca8f2d010730295e1f" have entirely different histories.
e39fce5f40
...
0dd6a7fa00
|
@ -4,6 +4,4 @@
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
/frontend/public/img/.dev
|
/frontend/public/img/.dev
|
||||||
/prisma/migrations
|
/prisma/migrations
|
||||||
/frontend/views/themes
|
|
||||||
!/frontend/views/themes/default
|
|
|
@ -1 +0,0 @@
|
||||||
*.ejs
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,74 +3,68 @@ const bcrypt = require("bcrypt");
|
||||||
const validate = require("../form_validation");
|
const validate = require("../form_validation");
|
||||||
|
|
||||||
async function postRegister(req, res) {
|
async function postRegister(req, res) {
|
||||||
const { username, password } = req.body; // Get the username and password from the request body
|
const { username, password } = req.body; // Get the username and password from the request body
|
||||||
|
|
||||||
const form_validation = await validate.newUser({ username: username, password: password }); // Check form for errors
|
const form_validation = await validate.newUser({ username: username, password: password }); // Check form for errors
|
||||||
|
|
||||||
// User registration disabled?
|
// User registration disabled?
|
||||||
// We also check if the server was setup. If it was not set up, the server will proceed anyways.
|
// We also check if the server was setup. If it was not set up, the server will proceed anyways.
|
||||||
if (!core.settings["ACCOUNT_REGISTRATION"] && core.settings["SETUP_COMPLETE"]) return res.json({ success: false, message: "Account registrations are disabled" });
|
if (!core.settings["ACCOUNT_REGISTRATION"] && core.settings["SETUP_COMPLETE"]) return res.json({ success: false, message: "Account registrations are disabled" });
|
||||||
|
|
||||||
// User data valid?
|
// User data valid?
|
||||||
if (!form_validation.success) return res.json({ success: false, message: form_validation.message });
|
if (!form_validation.success) return res.json({ success: false, message: form_validation.message });
|
||||||
|
|
||||||
// If setup incomplete, set the user role to Admin. This is the initial user so it will be the master user.
|
// If setup incomplete, set the user role to Admin. This is the initial user so it will be the master user.
|
||||||
const role = core.settings["SETUP_COMPLETE"] ? undefined : "ADMIN";
|
const role = core.settings["SETUP_COMPLETE"] ? undefined : "ADMIN";
|
||||||
|
|
||||||
const hashed_password = await bcrypt.hash(password, 10); // Hash the password for security :^)
|
const hashed_password = await bcrypt.hash(password, 10); // Hash the password for security :^)
|
||||||
res.json(await core.newUser({ username: username, password: hashed_password, role: role }));
|
res.json(await core.newUser({ username: username, password: hashed_password, role: role }));
|
||||||
}
|
}
|
||||||
async function postLogin(req, res) {
|
async function postLogin(req, res) {
|
||||||
const { username, password } = req.body; // Get the username and password from the request body
|
const { username, password } = req.body; // Get the username and password from the request body
|
||||||
|
|
||||||
// Get the user by username
|
// Get the user by username
|
||||||
const existing_user = await core.getUser({ username: username, include_password: true });
|
const existing_user = await core.getUser({ username: username, include_password: true });
|
||||||
if (!existing_user.success) return res.json({ success: false, message: existing_user.message });
|
if (!existing_user.success) return res.json({ success: false, message: existing_user.message });
|
||||||
|
|
||||||
// Check the password
|
// Check the password
|
||||||
const password_match = await bcrypt.compare(password, existing_user.data.password);
|
const password_match = await bcrypt.compare(password, existing_user.data.password);
|
||||||
if (!password_match) return res.json({ success: false, message: "Incorrect password" });
|
if (!password_match) return res.json({ success: false, message: "Incorrect password" });
|
||||||
|
|
||||||
// Send the cookies to the user & return successful
|
// Send the cookies to the user & return successful
|
||||||
req.session.user = { username: username, id: existing_user.data.id };
|
req.session.user = { username: username, id: existing_user.data.id };
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
}
|
}
|
||||||
async function postSetting(request, response) {
|
async function postSetting(request, response) {
|
||||||
const user = await core.getUser({ user_id: request.session.user.id });
|
const user = await core.getUser({ user_id: request.session.user.id });
|
||||||
|
|
||||||
if (!user.success) return response.json({ success: false, message: user.message });
|
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 (user.data.role !== "ADMIN") return response.json({ success: false, message: "User is not permitted" });
|
||||||
|
|
||||||
response.json(await core.postSetting(request.body.setting_name, request.body.value));
|
response.json(await core.postSetting(request.body.setting_name, request.body.value));
|
||||||
}
|
}
|
||||||
async function postImage(request, response) {
|
async function postImage(request, response) {
|
||||||
// TODO: Permissions for uploading images
|
// TODO: Permissions for uploading images
|
||||||
// TODO: Verification for image uploading
|
// TODO: Verification for image uploading
|
||||||
return response.json(await core.uploadMedia({ parent_id: request.body.post_id, parent_type: request.body.parent_type, file_buffer: request.body.buffer, content_type: request.body.content_type }));
|
return response.json(await core.uploadMedia({ parent_id: request.body.post_id, parent_type: request.body.parent_type, file_buffer: request.body.buffer, content_type: request.body.content_type }));
|
||||||
}
|
}
|
||||||
async function deleteImage(req, res) {
|
async function deleteImage(req, res) {
|
||||||
// TODO: Permissions for deleting image
|
// TODO: Permissions for deleting image
|
||||||
return res.json(await core.deleteImage(req.body, req.session.user.id));
|
return res.json(await core.deleteImage(req.body, req.session.user.id));
|
||||||
}
|
}
|
||||||
async function deleteBlog(req, res) {
|
async function deleteBlog(req, res) {
|
||||||
// TODO: Permissions for deleting blog
|
// TODO: Permissions for deleting blog
|
||||||
return res.json(await core.deletePost({ post_id: req.body.id, requester_id: req.session.user.id }));
|
return res.json(await core.deletePost({ post_id: req.body.id, requester_id: req.session.user.id }));
|
||||||
}
|
}
|
||||||
async function patchBlog(req, res) {
|
async function patchBlog(req, res) {
|
||||||
return res.json(await core.editPost({ requester_id: req.session.user.id, post_id: req.body.id, post_content: req.body }));
|
return res.json(await core.editPost({ requester_id: req.session.user.id, post_id: req.body.id, post_content: req.body }));
|
||||||
}
|
}
|
||||||
async function patchBiography(request, response) {
|
async function patchBiography(request, response) {
|
||||||
// TODO: Validate
|
// TODO: Validate
|
||||||
return response.json(await core.updateBiography({ requester_id: request.session.user.id, author_id: request.body.id, biography_content: request.body }));
|
return response.json(await core.updateBiography({ requester_id: request.session.user.id, author_id: request.body.id, biography_content: request.body }));
|
||||||
}
|
}
|
||||||
async function patchUser(request, response) {
|
async function patchUser(request, response) {
|
||||||
return response.json(await core.editUser({ requester_id: request.session.user.id, user_id: request.body.id, user_content: request.body }));
|
return response.json(await core.editUser({ requester_id: request.session.user.id, user_id: request.body.id, user_content: request.body }));
|
||||||
}
|
|
||||||
async function postTheme(request, response) {
|
|
||||||
return response.json(await core.installTheme(request.body.url, { requester_id: request.session.user.id }));
|
|
||||||
}
|
|
||||||
async function deleteTheme(request, response) {
|
|
||||||
return response.json(await core.deleteTheme(request.body.name, { requester_id: request.session.user.id }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { postRegister, patchBiography, postLogin, postSetting, postImage, deleteImage, deleteBlog, patchBlog, patchUser, postTheme, deleteTheme };
|
module.exports = { postRegister, patchBiography, postLogin, postSetting, postImage, deleteImage, deleteBlog, patchBlog, patchUser };
|
||||||
|
|
|
@ -1,139 +1,127 @@
|
||||||
const external = require("./core/external_api");
|
const external = require("./core/external_api");
|
||||||
const core = require("./core/core");
|
const core = require("./core/core");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
function _getThemePage(page_name) {
|
function _getThemePage(page_name) {
|
||||||
let manifest = require(`../frontend/views/themes/${core.settings.theme}/manifest.json`);
|
let manifest = require(`../frontend/views/themes/${core.settings.theme}/manifest.json`);
|
||||||
return `themes/${core.settings.theme}/${manifest.pages[page_name]}`;
|
return `themes/${core.settings.theme}/${manifest.pages[page_name]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDefaults(req) {
|
async function getDefaults(req) {
|
||||||
// TODO: Fix reference to website_name
|
// TODO: Fix reference to website_name
|
||||||
let user;
|
let user;
|
||||||
if (req.session.user) user = await core.getUser({ user_id: req.session.user.id });
|
if (req.session.user) user = await core.getUser({ user_id: req.session.user.id });
|
||||||
if (user?.success) user = user.data;
|
if (user?.success) user = user.data;
|
||||||
return { logged_in_user: user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings };
|
return { logged_in_user: user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings };
|
||||||
}
|
}
|
||||||
async function index(request, response) {
|
async function index(request, response) {
|
||||||
// Check if the master admin has been created
|
// Check if the master admin has been created
|
||||||
// const is_setup_complete = core.settings["SETUP_COMPLETE"];
|
// const is_setup_complete = core.settings["SETUP_COMPLETE"];
|
||||||
// if (!is_setup_complete) return response.redirect("/register");
|
// if (!is_setup_complete) return response.redirect("/register");
|
||||||
|
|
||||||
const blog_list = await core.getPost({ requester_id: request.session.user?.id }, {}, { page: request.query.page || 0 });
|
const blog_list = await core.getPost({ requester_id: request.session.user?.id }, {}, { page: request.query.page || 0 });
|
||||||
const tags = await core.getTags();
|
const tags = await core.getTags();
|
||||||
|
|
||||||
blog_list.data.forEach((post) => {
|
blog_list.data.forEach((post) => {
|
||||||
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");
|
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");
|
||||||
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
||||||
post.publish_date = formatted_date;
|
post.publish_date = formatted_date;
|
||||||
});
|
});
|
||||||
|
|
||||||
response.render(_getThemePage("index"), {
|
response.render(_getThemePage("index"), {
|
||||||
...(await getDefaults(request)),
|
...(await getDefaults(request)),
|
||||||
blog_list: blog_list.data,
|
blog_list: blog_list.data,
|
||||||
pagination: blog_list.pagination,
|
pagination: blog_list.pagination,
|
||||||
current_page: request.query.page || 0,
|
current_page: request.query.page || 0,
|
||||||
loaded_page: request.path,
|
loaded_page: request.path,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function register(request, response) {
|
async function register(request, response) {
|
||||||
response.render(_getThemePage("register"), await getDefaults(request));
|
response.render(_getThemePage("register"), await getDefaults(request));
|
||||||
}
|
}
|
||||||
async function login(request, response) {
|
async function login(request, response) {
|
||||||
response.render(_getThemePage("login"), await getDefaults(request));
|
response.render(_getThemePage("login"), await getDefaults(request));
|
||||||
}
|
}
|
||||||
async function author(req, res) {
|
async function author(req, res) {
|
||||||
const user = await core.getUser({ user_id: req.params.author_id });
|
const user = await core.getUser({ user_id: req.params.author_id });
|
||||||
// FIXME: Bandage fix for author get error
|
// FIXME: Bandage fix for author get error
|
||||||
if (!user.success) return res.redirect("/");
|
if (!user.success) return res.redirect("/");
|
||||||
const profile = await core.getBiography({ author_id: user.data.id });
|
const profile = await core.getBiography({ author_id: user.data.id });
|
||||||
// TODO: Check for success
|
// TODO: Check for success
|
||||||
const posts = await core.getPost({ requester_id: user.data.id });
|
const posts = await core.getPost({ requester_id: user.data.id });
|
||||||
|
|
||||||
res.render(_getThemePage("author"), { ...(await getDefaults(req)), post: { ...profile.data, post_count: posts.data.length } });
|
res.render(_getThemePage("author"), { ...(await getDefaults(req)), post: { ...profile.data, post_count: posts.data.length } });
|
||||||
}
|
}
|
||||||
async function authorEdit(request, response) {
|
async function authorEdit(request, response) {
|
||||||
let author = await core.getBiography({ author_id: request.params.author_id });
|
let author = await core.getBiography({ author_id: request.params.author_id });
|
||||||
if (!author.success) return response.redirect("/");
|
if (!author.success) return response.redirect("/");
|
||||||
response.render(_getThemePage("authorEdit"), { ...(await getDefaults(request)), profile: author.data });
|
response.render(_getThemePage("authorEdit"), { ...(await getDefaults(request)), profile: author.data });
|
||||||
}
|
}
|
||||||
async function blogList(req, res) {
|
async function blogList(req, res) {
|
||||||
const blog_list = await core.getPost({ requester_id: req.session.user?.id }, { search: req.query.search, search_title: true, search_tags: true, search_content: true });
|
const blog_list = await core.getPost({ requester_id: req.session.user?.id }, { search: req.query.search, search_title: true, search_tags: true, search_content: true });
|
||||||
|
|
||||||
blog_list.data.forEach((post) => {
|
blog_list.data.forEach((post) => {
|
||||||
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");
|
let published_date_parts = new Date(post.publish_date).toLocaleDateString().split("/");
|
||||||
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
||||||
post.publish_date = formatted_date;
|
post.publish_date = formatted_date;
|
||||||
});
|
});
|
||||||
|
|
||||||
res.render(_getThemePage("postSearch"), {
|
res.render(_getThemePage("postSearch"), {
|
||||||
...(await getDefaults(req)),
|
...(await getDefaults(req)),
|
||||||
blog_list: blog_list.data,
|
blog_list: blog_list.data,
|
||||||
pagination: blog_list.pagination,
|
pagination: blog_list.pagination,
|
||||||
current_page: req.query.page || 0,
|
current_page: req.query.page || 0,
|
||||||
loaded_page: req.path,
|
loaded_page: req.path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function blogSingle(req, res) {
|
async function blogSingle(req, res) {
|
||||||
const blog = await core.getPost({ requester_id: req.session.user?.id, post_id: req.params.blog_id });
|
const blog = await core.getPost({ requester_id: req.session.user?.id, post_id: req.params.blog_id });
|
||||||
if (blog.success === false) return res.redirect("/");
|
if (blog.success === false) return res.redirect("/");
|
||||||
res.render(_getThemePage("post"), { ...(await getDefaults(req)), blog_post: blog.data });
|
res.render(_getThemePage("post"), { ...(await getDefaults(req)), blog_post: blog.data });
|
||||||
}
|
}
|
||||||
async function blogNew(request, response) {
|
async function blogNew(request, response) {
|
||||||
const new_post = await core.newPost({ requester_id: request.session.user.id });
|
const new_post = await core.newPost({ requester_id: request.session.user.id });
|
||||||
return response.redirect(`/post/${new_post}/edit`);
|
return response.redirect(`/post/${new_post}/edit`);
|
||||||
}
|
}
|
||||||
async function blogEdit(req, res) {
|
async function blogEdit(req, res) {
|
||||||
let existing_blog = await core.getPost({ post_id: req.params.blog_id });
|
let existing_blog = await core.getPost({ post_id: req.params.blog_id });
|
||||||
if (!existing_blog.success) return res.redirect("/");
|
if (!existing_blog.success) return res.redirect("/");
|
||||||
existing_blog = existing_blog.data;
|
existing_blog = existing_blog.data;
|
||||||
|
|
||||||
let published_time_parts = new Date(existing_blog.publish_date).toLocaleTimeString([], { timeStyle: "short" }).slice(0, 4).split(":");
|
let published_time_parts = new Date(existing_blog.publish_date).toLocaleTimeString([], { timeStyle: "short" }).slice(0, 4).split(":");
|
||||||
const formatted_time = `${published_time_parts[0].padStart(2, "0")}:${published_time_parts[1].padStart(2, "0")}`;
|
const formatted_time = `${published_time_parts[0].padStart(2, "0")}:${published_time_parts[1].padStart(2, "0")}`;
|
||||||
existing_blog.publish_time = formatted_time;
|
existing_blog.publish_time = formatted_time;
|
||||||
|
|
||||||
let published_date_parts = new Date(existing_blog.publish_date).toLocaleDateString().split("/");
|
let published_date_parts = new Date(existing_blog.publish_date).toLocaleDateString().split("/");
|
||||||
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
const formatted_date = `${published_date_parts[2]}-${published_date_parts[0].padStart(2, "0")}-${published_date_parts[1].padStart(2, "0")}`;
|
||||||
existing_blog.publish_date = formatted_date;
|
existing_blog.publish_date = formatted_date;
|
||||||
|
|
||||||
res.render(_getThemePage("postNew"), { ...(await getDefaults(req)), existing_blog: existing_blog });
|
res.render(_getThemePage("postNew"), { ...(await getDefaults(req)), existing_blog: existing_blog });
|
||||||
}
|
}
|
||||||
async function admin(request, response) {
|
async function admin(request, response) {
|
||||||
let theme_data = {
|
response.render(_getThemePage("admin-settings"), { ...(await getDefaults(request)) });
|
||||||
installed: [],
|
|
||||||
current: core.settings.theme,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get theme list
|
|
||||||
fs.readdir(path.resolve(__dirname, "../frontend/views/themes"), (err, files) => {
|
|
||||||
files.forEach((theme) => theme_data.installed.push(theme));
|
|
||||||
});
|
|
||||||
|
|
||||||
response.render(_getThemePage("admin-settings"), { ...(await getDefaults(request)), theme_data: theme_data });
|
|
||||||
}
|
}
|
||||||
async function atom(req, res) {
|
async function atom(req, res) {
|
||||||
res.type("application/xml");
|
res.type("application/xml");
|
||||||
res.send(await external.getFeed({ type: "atom" }));
|
res.send(await external.getFeed({ type: "atom" }));
|
||||||
}
|
}
|
||||||
async function jsonFeed(req, res) {
|
async function jsonFeed(req, res) {
|
||||||
res.type("application/json");
|
res.type("application/json");
|
||||||
res.send(await external.getFeed({ type: "json" }));
|
res.send(await external.getFeed({ type: "json" }));
|
||||||
}
|
}
|
||||||
// Internal API ------------------------------
|
// Internal API ------------------------------
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
index,
|
index,
|
||||||
register,
|
register,
|
||||||
login,
|
login,
|
||||||
author,
|
author,
|
||||||
blogList,
|
blogList,
|
||||||
blogNew,
|
blogNew,
|
||||||
blogEdit,
|
blogEdit,
|
||||||
blogSingle,
|
blogSingle,
|
||||||
admin,
|
admin,
|
||||||
atom,
|
atom,
|
||||||
jsonFeed,
|
jsonFeed,
|
||||||
authorEdit,
|
authorEdit,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import globals from "globals";
|
|
||||||
import pluginJs from "@eslint/js";
|
|
||||||
|
|
||||||
export default [{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } }, { languageOptions: { globals: globals.browser } }, { rules: { "no-unused-vars": "error", "no-undef": "error", indent: ["error", "tab", { tabWidth: 4 }] } }, pluginJs.configs.recommended];
|
|
|
@ -66,45 +66,6 @@
|
||||||
.page .page-center .setting-list .setting:nth-child(even) {
|
.page .page-center .setting-list .setting:nth-child(even) {
|
||||||
background-color: rgb(250, 250, 250);
|
background-color: rgb(250, 250, 250);
|
||||||
}
|
}
|
||||||
.page .page-center .theme-list .add-theme-area {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .add-theme-area input {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .add-theme-area button {
|
|
||||||
width: 5rem;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry {
|
|
||||||
width: 100%;
|
|
||||||
height: 32px;
|
|
||||||
background-color: rgb(240, 240, 240);
|
|
||||||
padding: 0.1rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry .title {
|
|
||||||
margin: auto auto auto 0;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry .value {
|
|
||||||
margin: 0 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry .value button {
|
|
||||||
margin: auto;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry:nth-child(even) {
|
|
||||||
background-color: rgb(250, 250, 250);
|
|
||||||
}
|
|
||||||
.page .page-center .theme-list .entry.active {
|
|
||||||
background-color: #85c8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -1,173 +1,127 @@
|
||||||
.page .page-center {
|
.page .page-center {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
width: 700px;
|
width: 700px;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
margin-top: 4rem;
|
margin-top: 4rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: #0000001c 0 0px 5px;
|
box-shadow: #0000001c 0 0px 5px;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-list {
|
.setting-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.setting {
|
.setting {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
background-color: rgb(240, 240, 240);
|
background-color: rgb(240, 240, 240);
|
||||||
padding: 0.1rem;
|
padding: 0.1rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin: auto auto auto 0;
|
margin: auto auto auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
margin: 0 0 0 auto;
|
margin: 0 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.largeset {
|
.largeset {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
.value {
|
.value {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 3rem;
|
min-height: 3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting.fit-column {
|
.setting.fit-column {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting:nth-child(even) {
|
|
||||||
background-color: rgb(250, 250, 250);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.theme-list {
|
|
||||||
.add-theme-area {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
input {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
width: 5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry {
|
|
||||||
width: 100%;
|
|
||||||
height: 32px;
|
|
||||||
background-color: rgb(240, 240, 240);
|
|
||||||
padding: 0.1rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: auto auto auto 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
margin: 0 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: auto;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.entry:nth-child(even) {
|
|
||||||
background-color: rgb(250, 250, 250);
|
|
||||||
}
|
|
||||||
.entry.active {
|
|
||||||
background-color: #85c8ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.setting:nth-child(even) {
|
||||||
|
background-color: rgb(250, 250, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
.switch input {
|
.switch input {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
.slider {
|
.slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: #b8b8b8;
|
background-color: #b8b8b8;
|
||||||
-webkit-transition: 0.4s;
|
-webkit-transition: 0.4s;
|
||||||
transition: 0.4s;
|
transition: 0.4s;
|
||||||
}
|
}
|
||||||
.slider:before {
|
.slider:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: "";
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
-webkit-transition: 0.4s;
|
-webkit-transition: 0.4s;
|
||||||
transition: 0.4s;
|
transition: 0.4s;
|
||||||
}
|
}
|
||||||
input:checked + .slider {
|
input:checked + .slider {
|
||||||
background-color: #2196f3;
|
background-color: #2196f3;
|
||||||
}
|
}
|
||||||
input:focus + .slider {
|
input:focus + .slider {
|
||||||
box-shadow: 0 0 1px #2196f3;
|
box-shadow: 0 0 1px #2196f3;
|
||||||
}
|
}
|
||||||
input:checked + .slider:before {
|
input:checked + .slider:before {
|
||||||
-webkit-transform: translateX(20px);
|
-webkit-transform: translateX(20px);
|
||||||
-ms-transform: translateX(20px);
|
-ms-transform: translateX(20px);
|
||||||
transform: translateX(20px);
|
transform: translateX(20px);
|
||||||
}
|
}
|
||||||
.slider.round {
|
.slider.round {
|
||||||
border-radius: 34px;
|
border-radius: 34px;
|
||||||
}
|
}
|
||||||
.slider.round:before {
|
.slider.round:before {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,35 +68,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-center">
|
|
||||||
<div class="header">Themes</div>
|
|
||||||
|
|
||||||
<div class="theme-list">
|
|
||||||
<div class="add-theme-area">
|
|
||||||
<input id="theme-url" placeholder="Enter a git repository URL" type="text">
|
|
||||||
<button onclick="addTheme()">Add</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% for(theme of theme_data.installed) { %>
|
|
||||||
<!-- -->
|
|
||||||
<div class="entry <% if(theme == theme_data.current) {%> active <% } %>">
|
|
||||||
<div class="title"><%= theme %></div>
|
|
||||||
<div class="value">
|
|
||||||
<button onclick="setTheme('<%= theme %>')" type="button">Use</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- -->
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%- include("partials/footer.ejs") %>
|
||||||
<%- include("partials/footer.ejs") %>
|
</body>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
<script defer src="/js/admin.js"></script>
|
<script defer src="/js/admin.js"></script>
|
||||||
|
|
|
@ -1,49 +1,24 @@
|
||||||
async function toggleState(setting_name, element) {
|
async function toggleState(setting_name, element) {
|
||||||
console.log(element.checked);
|
console.log(element.checked);
|
||||||
const form = {
|
const form = {
|
||||||
setting_name: setting_name,
|
setting_name: setting_name,
|
||||||
value: element.checked,
|
value: element.checked,
|
||||||
};
|
};
|
||||||
const response = await request("/setting", "POST", form);
|
const response = await request("/setting", "POST", form);
|
||||||
|
|
||||||
// TODO: On failure, notify the user
|
// TODO: On failure, notify the user
|
||||||
if (response.body.success) {
|
if (response.body.success) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeValue(setting_name, element) {
|
async function changeValue(setting_name, element) {
|
||||||
const form = {
|
const form = {
|
||||||
setting_name: setting_name,
|
setting_name: setting_name,
|
||||||
value: element.value,
|
value: element.value,
|
||||||
};
|
};
|
||||||
const response = await request("/setting", "POST", form);
|
const response = await request("/setting", "POST", form);
|
||||||
|
|
||||||
// TODO: On failure, notify the user
|
// TODO: On failure, notify the user
|
||||||
if (response.body.success) {
|
if (response.body.success) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function addTheme() {
|
|
||||||
const url = qs("#theme-url").value;
|
|
||||||
|
|
||||||
if (!url || url.length == 0) return false;
|
|
||||||
|
|
||||||
const response = await request("/api/theme", "POST", { url: url });
|
|
||||||
|
|
||||||
if (response.body.success) {
|
|
||||||
alert("Added theme.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setTheme(name) {
|
|
||||||
const form = {
|
|
||||||
setting_name: "theme",
|
|
||||||
value: name,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await request("/setting", "POST", form);
|
|
||||||
|
|
||||||
if (response.body.success) {
|
|
||||||
alert("Changed theme.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,9 +19,6 @@
|
||||||
"author": "Armored Dragon",
|
"author": "Armored Dragon",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.6.0",
|
|
||||||
"eslint": "^9.6.0",
|
|
||||||
"globals": "^15.8.0",
|
|
||||||
"nodemon": "^3.0.1"
|
"nodemon": "^3.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
28
yab.js
28
yab.js
|
@ -15,18 +15,16 @@ app.set("views", path.join(__dirname, "frontend/views"));
|
||||||
app.use(express.json({ limit: "500mb" }));
|
app.use(express.json({ limit: "500mb" }));
|
||||||
app.use(express.urlencoded({ extended: false }));
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
// TODO: Does this persist previous themes? May cause security issues!
|
||||||
let theme = require("./backend/core/core").settings.theme;
|
const refreshTheme = (theme_name) => app.use(express.static(path.join(__dirname, `frontend/views/themes/${theme_name}`)));
|
||||||
let middleware = express.static(path.join(__dirname, `frontend/views/themes/${theme}`));
|
refreshTheme("default");
|
||||||
middleware(req, res, next);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: require("crypto").randomBytes(128).toString("base64"),
|
secret: require("crypto").randomBytes(128).toString("base64"),
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
@ -36,11 +34,9 @@ app.post("/setting", checkAuthenticated, internal.postSetting);
|
||||||
app.post("/api/web/image", checkAuthenticated, internal.postImage);
|
app.post("/api/web/image", checkAuthenticated, internal.postImage);
|
||||||
app.delete("/api/web/post/image", checkAuthenticated, internal.deleteImage);
|
app.delete("/api/web/post/image", checkAuthenticated, internal.deleteImage);
|
||||||
app.delete("/api/web/post", checkAuthenticated, internal.deleteBlog);
|
app.delete("/api/web/post", checkAuthenticated, internal.deleteBlog);
|
||||||
app.delete("/api/theme", checkAuthenticated, internal.deleteTheme);
|
|
||||||
app.patch("/api/web/post", checkAuthenticated, internal.patchBlog);
|
app.patch("/api/web/post", checkAuthenticated, internal.patchBlog);
|
||||||
app.patch("/api/web/biography", checkAuthenticated, internal.patchBiography);
|
app.patch("/api/web/biography", checkAuthenticated, internal.patchBiography);
|
||||||
app.patch("/api/web/user", checkAuthenticated, internal.patchUser);
|
app.patch("/api/web/user", checkAuthenticated, internal.patchUser);
|
||||||
app.post("/api/theme", checkAuthenticated, internal.postTheme);
|
|
||||||
|
|
||||||
// app.delete("/logout", page_scripts.logout);
|
// app.delete("/logout", page_scripts.logout);
|
||||||
|
|
||||||
|
@ -59,13 +55,13 @@ app.get("/atom", page_scripts.atom);
|
||||||
app.get("/json", page_scripts.jsonFeed);
|
app.get("/json", page_scripts.jsonFeed);
|
||||||
|
|
||||||
function checkAuthenticated(req, res, next) {
|
function checkAuthenticated(req, res, next) {
|
||||||
if (req.session.user) return next();
|
if (req.session.user) return next();
|
||||||
res.redirect("/login");
|
res.redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkNotAuthenticated(req, res, next) {
|
function checkNotAuthenticated(req, res, next) {
|
||||||
if (req.session.user) return res.redirect("/");
|
if (req.session.user) return res.redirect("/");
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.listen(5004);
|
app.listen(5004);
|
||||||
|
|
Loading…
Reference in New Issue