database cleanup (#3)
Reviewed-on: #3 Co-authored-by: Armored-Dragon <forgejo3829105@armoreddragon.com> Co-committed-by: Armored-Dragon <forgejo3829105@armoreddragon.com>pull/2/head
parent
624b46e345
commit
bc92cd5681
|
@ -1,6 +1,5 @@
|
|||
const { PrismaClient } = require("@prisma/client");
|
||||
const prisma = new PrismaClient();
|
||||
const crypto = require("crypto");
|
||||
const sharp = require("sharp");
|
||||
const { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsCommand, DeleteObjectsCommand } = require("@aws-sdk/client-s3");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
|
@ -12,61 +11,77 @@ const s3 = new S3Client({
|
|||
region: process.env.S3_REGION,
|
||||
endpoint: process.env.S3_ENDPOINT,
|
||||
});
|
||||
const settings = require("../settings");
|
||||
const md = require("markdown-it")();
|
||||
|
||||
let settings = {
|
||||
SETUP_COMPLETE: false,
|
||||
ACCOUNT_REGISTRATION: false,
|
||||
HIDE_LOGIN: false,
|
||||
BLOG_UPLOADING: false,
|
||||
|
||||
USER_MINIMUM_PASSWORD_LENGTH: 7,
|
||||
|
||||
BLOG_MINIMUM_TITLE_LENGTH: 7,
|
||||
BLOG_MINIMUM_DESCRIPTION_LENGTH: 7,
|
||||
BLOG_MINIMUM_CONTENT_LENGTH: 7,
|
||||
};
|
||||
let groups = [];
|
||||
_getSettings();
|
||||
_getGroups();
|
||||
|
||||
async function registerUser(username, password, options) {
|
||||
const new_user = await prisma.user.create({ data: { username: username, password: password, ...options } });
|
||||
let user_database_entry;
|
||||
let user_profile_database_entry;
|
||||
|
||||
if (new_user.id) {
|
||||
// If the user was created as an admin, make sure that the server knows the setup process is complete.
|
||||
if (options.role === "ADMIN") settings.act("SETUP_COMPLETE", true);
|
||||
// Create the entry in the database
|
||||
try {
|
||||
user_database_entry = await prisma.user.create({ data: { username: username, password: password, ...options } });
|
||||
} catch (e) {
|
||||
let message;
|
||||
|
||||
// Create a user profile page
|
||||
const profile_page = await prisma.profilePage.create({ data: { owner: new_user.id } });
|
||||
if (!profile_page.id) return { success: false, message: `Error creating profile page for user ${new_user.username}` };
|
||||
if (e.code === "P2002") message = "Username already exists";
|
||||
else message = "Unknown error";
|
||||
|
||||
// User has been successfully created
|
||||
return { success: true, message: `Successfully created ${new_user.username}` };
|
||||
return { success: false, message: message };
|
||||
}
|
||||
|
||||
return { success: false, message: "Unknown error" };
|
||||
// Create a user profile page
|
||||
try {
|
||||
user_profile_database_entry = await prisma.profilePage.create({ data: { owner: { connect: { id: user_database_entry.id } } } });
|
||||
} catch (e) {
|
||||
return { success: false, message: `Error creating profile page for user ${username}` };
|
||||
}
|
||||
|
||||
// Master user was created; server initialized
|
||||
postSetting("SETUP_COMPLETE", true);
|
||||
|
||||
// User has been successfully created
|
||||
return { success: true, message: `Successfully created ${username}` };
|
||||
}
|
||||
async function getUser({ id, username } = {}) {
|
||||
if (id || username) {
|
||||
let user;
|
||||
if (id) user = await prisma.user.findUnique({ where: { id: id } });
|
||||
else if (username) user = await prisma.user.findUnique({ where: { username: username } });
|
||||
|
||||
if (!user) return { success: false, message: "No matching user" };
|
||||
else return { success: true, data: user };
|
||||
}
|
||||
async function getUser({ id, username } = {}) {
|
||||
let user;
|
||||
if (id) user = await prisma.user.findUnique({ where: { id: id } });
|
||||
else if (username) user = await prisma.user.findUnique({ where: { username: username } });
|
||||
|
||||
if (!user) return { success: false, message: "No matching user" };
|
||||
else return { success: true, data: user };
|
||||
}
|
||||
async function postBlog(blog_post, owner_id) {
|
||||
// Check if user has permissions to upload a blog post
|
||||
const user = await getUser({ id: owner_id });
|
||||
if (!user.success) return { success: false, message: "User not found" };
|
||||
// Check if user has permissions to upload a blog post
|
||||
|
||||
if (user.data.role !== "ADMIN" && user.data.role !== "AUTHOR") return { success: false, message: "User is not permitted" };
|
||||
|
||||
const [year, month, day] = blog_post.date.split("-");
|
||||
const [hour, minute] = blog_post.time.split(":");
|
||||
let publish_date = new Date(year, month - 1, day, hour, minute);
|
||||
|
||||
let blog_post_formatted = {
|
||||
title: blog_post.title,
|
||||
description: blog_post.description,
|
||||
content: blog_post.content,
|
||||
visibility: blog_post.unlisted ? "UNLISTED" : "PUBLISHED",
|
||||
publish_date: publish_date,
|
||||
};
|
||||
|
||||
const database_blog = await prisma.blogPost.create({ data: { ...blog_post_formatted, owner: { connect: { id: owner_id } } } });
|
||||
// Save to database
|
||||
const database_blog = await prisma.blogPost.create({ data: { ...blog_post, owner: { connect: { id: owner_id } } } });
|
||||
|
||||
// Init image vars
|
||||
let uploaded_images = [];
|
||||
let uploaded_thumbnail = "DEFAULT";
|
||||
|
||||
// For Each image, upload to S3
|
||||
if (blog_post.images) {
|
||||
// For Each image, upload to S3
|
||||
for (let i = 0; blog_post.images.length > i; i++) {
|
||||
const image = blog_post.images[i];
|
||||
const image_data = Buffer.from(image.data_blob.split(",")[1], "base64");
|
||||
|
@ -82,12 +97,13 @@ async function postBlog(blog_post, owner_id) {
|
|||
uploaded_thumbnail = name;
|
||||
}
|
||||
|
||||
// Update the blog post to include references to our images
|
||||
await prisma.blogPost.update({ where: { id: database_blog.id }, data: { images: uploaded_images, thumbnail: uploaded_thumbnail } });
|
||||
return { success: true, blog_id: database_blog.id };
|
||||
}
|
||||
async function deleteBlog(blog_id, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getBlogList({ id: blog_id });
|
||||
const post = await getBlog({ id: blog_id });
|
||||
|
||||
let can_delete = post.owner.id === user.data.id || user.data.role === "ADMIN";
|
||||
|
||||
|
@ -101,7 +117,7 @@ async function deleteBlog(blog_id, requester_id) {
|
|||
}
|
||||
async function updateBlog(blog_post, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getBlogList({ id: blog_post.id, raw: true });
|
||||
const post = await getBlog({ id: blog_post.id, raw: true });
|
||||
|
||||
delete blog_post.id;
|
||||
|
||||
|
@ -152,42 +168,24 @@ async function updateBlog(blog_post, requester_id) {
|
|||
|
||||
return { success: true };
|
||||
}
|
||||
async function getBlogList({ id, visibility = "PUBLISHED", owner_id, raw = false } = {}, { limit = 10, page = 0 } = {}) {
|
||||
async function getBlog({ id, visibility = "PUBLISHED", owner_id, limit = 10, page = 0 } = {}) {
|
||||
if (id) {
|
||||
// Get the database entry for the blog post
|
||||
let post = await prisma.blogPost.findUnique({ where: { id: id }, include: { owner: true } });
|
||||
if (!post) return { success: false, message: "Post does not exist" };
|
||||
|
||||
if (!post) return null;
|
||||
|
||||
if (raw) {
|
||||
// Had to do this, only God knows why.
|
||||
post.raw_images = [];
|
||||
post.images.forEach((image) => post.raw_images.push(image));
|
||||
|
||||
post.raw_thumbnail = post.thumbnail;
|
||||
post.raw_content = post.content;
|
||||
}
|
||||
|
||||
// Get the image urls for the post
|
||||
for (i = 0; post.images.length > i; i++) {
|
||||
post.images[i] = await _getImage(post.id, "blog", post.images[i]);
|
||||
}
|
||||
|
||||
// get thumbnail URL
|
||||
post.thumbnail = await _getImage(post.id, "blog", post.thumbnail);
|
||||
|
||||
// Render the markdown contents of the post
|
||||
post.content = md.render(post.content);
|
||||
|
||||
// Replace custom formatting with what we want
|
||||
post.content = _format_blog_content(post.content, post.images);
|
||||
// Render the post
|
||||
const rendered_post = _renderPost(post, true);
|
||||
|
||||
// Return the post with valid image urls
|
||||
return post;
|
||||
return rendered_post;
|
||||
}
|
||||
|
||||
let rendered_post_list = [];
|
||||
|
||||
const where_object = {
|
||||
OR: [
|
||||
// Standard discovery: Public, and after the publish date
|
||||
{
|
||||
AND: [
|
||||
{
|
||||
|
@ -201,6 +199,7 @@ async function getBlogList({ id, visibility = "PUBLISHED", owner_id, raw = false
|
|||
],
|
||||
},
|
||||
|
||||
// User owns the post
|
||||
{
|
||||
ownerid: owner_id,
|
||||
},
|
||||
|
@ -214,30 +213,21 @@ async function getBlogList({ id, visibility = "PUBLISHED", owner_id, raw = false
|
|||
include: { owner: true },
|
||||
orderBy: [{ publish_date: "desc" }, { created_date: "desc" }],
|
||||
});
|
||||
// Get the thumbnails
|
||||
for (i = 0; blog_posts.length > i; i++) {
|
||||
blog_posts[i].thumbnail = await _getImage(blog_posts[i].id, "blog", blog_posts[i].thumbnail);
|
||||
|
||||
// Get the image urls for the post
|
||||
for (imgindx = 0; blog_posts[i].images.length > imgindx; imgindx++) {
|
||||
blog_posts[i].images[imgindx] = await _getImage(blog_posts[i].id, "blog", blog_posts[i].images[imgindx]);
|
||||
}
|
||||
|
||||
// Render the markdown contents of the post
|
||||
blog_posts[i].content = md.render(blog_posts[i].content);
|
||||
|
||||
// Replace custom formatting with what we want
|
||||
blog_posts[i].content = _format_blog_content(blog_posts[i].content, blog_posts[i].images);
|
||||
// Render each of the posts in the list
|
||||
for (blog_post of blog_posts) {
|
||||
rendered_post_list.push(await _renderPost(blog_post, true));
|
||||
}
|
||||
|
||||
// Calculate pagination
|
||||
let pagination = await prisma.blogPost.count({
|
||||
where: where_object,
|
||||
});
|
||||
return { data: blog_posts, pagination: _getNavigationList(page, Math.ceil(pagination / limit)) };
|
||||
return { data: rendered_post_list, pagination: _getNavigationList(page, Math.ceil(pagination / limit)) };
|
||||
}
|
||||
async function deleteImage(image, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getBlogList({ id: image.parent, raw: true });
|
||||
const post = await getBlog({ id: image.parent, raw: true });
|
||||
|
||||
// Check if post exists
|
||||
if (!post) return { success: false, message: "Post does not exist" };
|
||||
|
@ -319,6 +309,34 @@ async function _deleteS3Directory(id, type) {
|
|||
// If there are more objects to delete (truncated result), recursively call the function again
|
||||
// if (listed_objects.IsTruncated) await emptyS3Directory(bucket, dir);
|
||||
}
|
||||
async function _renderPost(blog_post, raw, { post_type = "blog" } = {}) {
|
||||
if (raw) {
|
||||
// Had to do this, only God knows why.
|
||||
blog_post.raw_images = [];
|
||||
if (blog_post.images) blog_post.images.forEach((image) => blog_post.raw_images.push(image));
|
||||
|
||||
blog_post.raw_thumbnail = blog_post.thumbnail;
|
||||
blog_post.raw_content = blog_post.content;
|
||||
}
|
||||
|
||||
if (blog_post.images) {
|
||||
// Get the image urls for the post
|
||||
for (i = 0; blog_post.images.length > i; i++) {
|
||||
blog_post.images[i] = await _getImage(blog_post.id, post_type, blog_post.images[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// get thumbnail URL
|
||||
blog_post.thumbnail = await _getImage(blog_post.id, post_type, blog_post.thumbnail);
|
||||
|
||||
// Render the markdown contents of the post
|
||||
blog_post.content = md.render(blog_post.content);
|
||||
|
||||
// Replace custom formatting with what we want
|
||||
blog_post.content = _format_blog_content(blog_post.content, blog_post.images);
|
||||
|
||||
return blog_post;
|
||||
}
|
||||
function _format_blog_content(content, images) {
|
||||
// Replace Images
|
||||
const image_regex = /{image:([^}]+)}/g;
|
||||
|
@ -348,5 +366,36 @@ function _getNavigationList(current_page, max_page) {
|
|||
const pageList = [current_page - 2, current_page - 1, current_page, current_page + 1, current_page + 2].filter((num) => num >= 0 && num < max_page);
|
||||
return pageList.slice(0, 5);
|
||||
}
|
||||
async function _getSettings() {
|
||||
// Go though each object key in our settings to get the value if it exists
|
||||
Object.keys(settings).forEach(async (key) => {
|
||||
let found_value = await prisma.setting.findUnique({ where: { id: key } });
|
||||
if (!found_value) return;
|
||||
|
||||
module.exports = { registerUser, getUser, postBlog, updateBlog, getBlogList, deleteBlog, deleteImage };
|
||||
return (settings[key] = JSON.parse(found_value.value));
|
||||
});
|
||||
}
|
||||
async function getSetting(key, { parse = true }) {
|
||||
if (!settings[key]) return null;
|
||||
|
||||
if (parse) {
|
||||
return JSON.parse(settings[key]);
|
||||
}
|
||||
return settings[key];
|
||||
}
|
||||
async function postSetting(key, value) {
|
||||
try {
|
||||
if (!Object.keys(settings).includes(key)) return { success: false, message: "Setting not valid" };
|
||||
|
||||
await prisma.setting.upsert({ where: { id: key }, update: { value: value }, create: { id: key, value: value } });
|
||||
settings[key] = JSON.parse(value);
|
||||
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
return { success: false, message: e.message };
|
||||
}
|
||||
}
|
||||
async function _getGroups() {
|
||||
const group_list = await prisma.group.findMany();
|
||||
}
|
||||
module.exports = { registerUser, getUser, postBlog, updateBlog, getBlogList: getBlog, deleteBlog, deleteImage, postSetting, getSetting, settings };
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
const settings = require("../settings");
|
||||
|
||||
async function userRegistration(username, password) {
|
||||
const active_settings = settings.getSettings();
|
||||
if (!username) return { success: false, message: "No username provided" };
|
||||
if (!password) return { success: false, message: "No password provided" };
|
||||
if (password.length < active_settings.USER_MINIMUM_PASSWORD_LENGTH) return { success: false, message: "Password not long enough" };
|
||||
|
||||
// Check if username only uses URL safe characters
|
||||
if (!_isUrlSafe(username)) return { success: false, message: "Username is not URL safe" };
|
||||
|
||||
// All good! Validation complete
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async function blogPost(blog_object) {
|
||||
// TODO: Validate blog posts before upload
|
||||
// Check title length
|
||||
// Check description length
|
||||
// Check content length
|
||||
// Check valid date
|
||||
}
|
||||
|
||||
function _isUrlSafe(str) {
|
||||
const pattern = /^[A-Za-z0-9\-_.~]+$/;
|
||||
return pattern.test(str);
|
||||
}
|
||||
|
||||
module.exports = { userRegistration };
|
|
@ -1,60 +1,79 @@
|
|||
const validate = require("./form_validation");
|
||||
const core = require("./core");
|
||||
const settings = require("../settings");
|
||||
const bcrypt = require("bcrypt");
|
||||
const validate = require("../form_validation");
|
||||
|
||||
async function registerUser(username, password) {
|
||||
// Get current and relevant settings
|
||||
const active_settings = settings.getSettings();
|
||||
const form_valid = await validate.userRegistration(username, password); // Check form for errors
|
||||
async function postRegister(req, res) {
|
||||
const { username, password } = req.body; // Get the username and password from the request body
|
||||
|
||||
// Set variables for easy reading
|
||||
const registration_allowed = active_settings.ACCOUNT_REGISTRATION;
|
||||
const setup_complete = active_settings.SETUP_COMPLETE;
|
||||
const form_validation = await validate.registerUser(username, password); // Check form for errors
|
||||
|
||||
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
|
||||
// User registration disabled?
|
||||
// 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" });
|
||||
|
||||
// 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" };
|
||||
// User data valid?
|
||||
if (!form_validation.success) return res.json({ success: false, message: form_validation.message });
|
||||
|
||||
// Register the user in the database
|
||||
const role = setup_complete ? undefined : "ADMIN";
|
||||
const registration_status = await core.registerUser(username, password, { role: role });
|
||||
// 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";
|
||||
|
||||
if (registration_status.success) return registration_status;
|
||||
else return registration_status;
|
||||
const hashed_password = await bcrypt.hash(password, 10); // Hash the password for security :^)
|
||||
res.json(await core.registerUser(username, hashed_password, { role: role }));
|
||||
}
|
||||
async function postLogin(req, res) {
|
||||
const { username, password } = req.body; // Get the username and password from the request body
|
||||
|
||||
async function loginUser(username, password) {
|
||||
// Get the user by username
|
||||
const existing_user = await core.getUser({ username: username });
|
||||
if (!existing_user.success) return res.json({ success: false, message: existing_user.message });
|
||||
|
||||
// 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 } };
|
||||
// Check the password
|
||||
const password_match = await bcrypt.compare(password, existing_user.data.password);
|
||||
if (!password_match) return res.json({ success: false, message: "Incorrect password" });
|
||||
|
||||
// Send the cookies to the user & return successful
|
||||
req.session.user = { username: username, id: existing_user.data.id };
|
||||
res.json({ success: true });
|
||||
}
|
||||
async function postSetting(request, response) {
|
||||
const user = await core.getUser({ id: request.session.user.id });
|
||||
|
||||
// TODO: Permissions for changing settings
|
||||
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 });
|
||||
}
|
||||
async function deleteImage(req, res) {
|
||||
// TODO: Permissions for deleting image
|
||||
return res.json(await core.deleteImage(req.body, req.session.user.id));
|
||||
}
|
||||
async function postBlog(req, res) {
|
||||
// Get user
|
||||
const user = await core.getUser({ id: req.session.user.id });
|
||||
if (!user.success) return user;
|
||||
|
||||
// TODO: Permissions for uploading posts
|
||||
// Can user upload?
|
||||
// const permissions = await permissions.postBlog(user);
|
||||
|
||||
// TODO: Validation for uploading posts
|
||||
// Validate blog info
|
||||
const valid = await validate.postBlog(req.body);
|
||||
|
||||
// Upload blog post
|
||||
return res.json(await core.postBlog(valid.data, req.session.user.id));
|
||||
}
|
||||
async function deleteBlog(req, res) {
|
||||
// TODO: Permissions for deleting blog
|
||||
return res.json(await core.deleteBlog(req.body.id, req.session.user.id));
|
||||
}
|
||||
async function patchBlog(req, res) {
|
||||
// TODO: Permissions for updating blog
|
||||
return res.json(await core.updateBlog(req.body, req.session.user.id));
|
||||
}
|
||||
|
||||
async function getBlogList({ id, visibility, owner_id, raw } = {}, { page = 0, limit = 10 } = {}) {
|
||||
const blog_list = await core.getBlogList({ id: id, visibility: visibility, owner_id: owner_id, raw: raw }, { page: page, limit: limit });
|
||||
return blog_list;
|
||||
}
|
||||
|
||||
async function getUser({ id } = {}) {
|
||||
return await core.getUser({ id: id });
|
||||
}
|
||||
|
||||
async function postBlog(blog_post, owner_id) {
|
||||
return await core.postBlog(blog_post, owner_id);
|
||||
}
|
||||
async function deleteBlog(blog_id, owner_id) {
|
||||
return await core.deleteBlog(blog_id, owner_id);
|
||||
}
|
||||
async function updateBlog(blog_post, requester_id) {
|
||||
return await core.updateBlog(blog_post, requester_id);
|
||||
}
|
||||
async function deleteImage(image_data, requester_id) {
|
||||
return await core.deleteImage(image_data, requester_id);
|
||||
}
|
||||
module.exports = { registerUser, loginUser, postBlog, getBlogList, deleteBlog, updateBlog, deleteImage, getUser };
|
||||
module.exports = { postRegister, postLogin, postSetting, deleteImage, postBlog, deleteBlog, patchBlog };
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
const core = require("./core/core");
|
||||
|
||||
async function registerUser(username, password) {
|
||||
if (!username) return { success: false, message: "No username provided" };
|
||||
if (!password) return { success: false, message: "No password provided" };
|
||||
if (password.length < core.settings["USER_MINIMUM_PASSWORD_LENGTH"]) return { success: false, message: "Password not long enough" };
|
||||
|
||||
// Check if username only uses URL safe characters
|
||||
if (!_isUrlSafe(username)) return { success: false, message: "Username is not URL safe" };
|
||||
|
||||
// All good! Validation complete
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async function postBlog(blog_object) {
|
||||
// TODO: Validate blog posts before upload
|
||||
// Check title length
|
||||
// Check description length
|
||||
// Check content length
|
||||
// Check valid date
|
||||
// Return formatted object
|
||||
|
||||
// Get the publish date in a standard format
|
||||
const [year, month, day] = blog_object.date.split("-");
|
||||
const [hour, minute] = blog_object.time.split(":");
|
||||
let publish_date = new Date(year, month - 1, day, hour, minute);
|
||||
|
||||
// Format our data to save
|
||||
let blog_post_formatted = {
|
||||
title: blog_object.title,
|
||||
description: blog_object.description,
|
||||
content: blog_object.content,
|
||||
visibility: blog_object.visibility,
|
||||
publish_date: publish_date,
|
||||
};
|
||||
|
||||
return { success: true, data: blog_post_formatted };
|
||||
}
|
||||
|
||||
function _isUrlSafe(str) {
|
||||
const pattern = /^[A-Za-z0-9\-_.~]+$/;
|
||||
return pattern.test(str);
|
||||
}
|
||||
|
||||
module.exports = { registerUser, postBlog };
|
|
@ -1,16 +1,14 @@
|
|||
const internal = require("./core/internal_api");
|
||||
const external = require("./core/external_api");
|
||||
const bcrypt = require("bcrypt");
|
||||
const settings = require("./settings");
|
||||
const core = require("./core/core");
|
||||
|
||||
function getDefaults(req) {
|
||||
const active_settings = settings.getSettings();
|
||||
return { logged_in_user: req.session.user, website_name: process.env.WEBSITE_NAME, settings: active_settings };
|
||||
return { logged_in_user: req.session.user, website_name: process.env.WEBSITE_NAME, settings: core.settings };
|
||||
}
|
||||
|
||||
async function index(request, response) {
|
||||
// Check if the master admin has been created
|
||||
const is_setup_complete = (await settings.act("SETUP_COMPLETE")) || false;
|
||||
|
||||
const is_setup_complete = core.settings["SETUP_COMPLETE"];
|
||||
if (!is_setup_complete) return response.redirect("/register");
|
||||
|
||||
response.redirect("/blog");
|
||||
|
@ -25,7 +23,7 @@ function author(request, response) {
|
|||
response.render("author.ejs", getDefaults(request));
|
||||
}
|
||||
async function blogList(req, res) {
|
||||
const blog_list = await internal.getBlogList({ owner_id: req.session.user?.id }, { page: req.query.page || 0 });
|
||||
const blog_list = await core.getBlogList({ owner_id: req.session.user?.id, page: req.query.page || 0 });
|
||||
res.render("blogList.ejs", {
|
||||
...getDefaults(req),
|
||||
blog_list: blog_list.data,
|
||||
|
@ -35,7 +33,7 @@ async function blogList(req, res) {
|
|||
});
|
||||
}
|
||||
async function blogSingle(req, res) {
|
||||
const blog = await internal.getBlogList({ id: req.params.blog_id });
|
||||
const blog = await core.getBlogList({ id: req.params.blog_id });
|
||||
if (blog === null) return res.redirect("/blog");
|
||||
res.render("blogSingle.ejs", { ...getDefaults(req), blog_post: blog });
|
||||
}
|
||||
|
@ -53,16 +51,16 @@ function blogNew(request, response) {
|
|||
response.render("blogNew.ejs", { ...getDefaults(request), existing_blog: existing_blog });
|
||||
}
|
||||
async function blogEdit(req, res) {
|
||||
const existing_blog = await internal.getBlogList({ id: req.params.blog_id, raw: true });
|
||||
|
||||
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")}`;
|
||||
existing_blog.publish_date = formatted_date;
|
||||
const existing_blog = await core.getBlogList({ id: req.params.blog_id, raw: true });
|
||||
|
||||
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")}`;
|
||||
existing_blog.publish_time = formatted_time;
|
||||
|
||||
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")}`;
|
||||
existing_blog.publish_date = formatted_date;
|
||||
|
||||
res.render("blogNew.ejs", { ...getDefaults(req), existing_blog: existing_blog });
|
||||
}
|
||||
async function admin(request, response) {
|
||||
|
@ -72,49 +70,9 @@ async function atom(req, res) {
|
|||
res.type("application/xml");
|
||||
res.send(await external.getFeed({ type: "atom" }));
|
||||
}
|
||||
// async function rss(req, res) {
|
||||
// res.type("application/rss+xml");
|
||||
// res.send(await external.getFeed({ type: "rss" }));
|
||||
// }
|
||||
|
||||
async function registerPost(request, response) {
|
||||
const hashedPassword = await bcrypt.hash(request.body.password, 10); // Hash the password for security :^)
|
||||
response.json(await internal.registerUser(request.body.username, hashedPassword));
|
||||
}
|
||||
async function loginPost(request, response) {
|
||||
const login = await internal.loginUser(request.body.username, request.body.password);
|
||||
// Internal API ------------------------------
|
||||
|
||||
if (!login.success) return response.json(login);
|
||||
|
||||
const password_match = await bcrypt.compare(request.body.password, login.data.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 });
|
||||
}
|
||||
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" });
|
||||
|
||||
settings.act(request.body.setting_name, request.body.value);
|
||||
|
||||
response.json({ success: true });
|
||||
}
|
||||
async function deleteImage(req, res) {
|
||||
res.json(await internal.deleteImage(req.body, req.session.user.id));
|
||||
}
|
||||
|
||||
async function postBlog(req, res) {
|
||||
return res.json(await internal.postBlog(req.body, req.session.user.id));
|
||||
}
|
||||
async function deleteBlog(req, res) {
|
||||
return res.json(await internal.deleteBlog(req.body.id, req.session.user.id));
|
||||
}
|
||||
async function updateBlog(req, res) {
|
||||
return res.json(await internal.updateBlog(req.body, req.session.user.id));
|
||||
}
|
||||
module.exports = {
|
||||
index,
|
||||
register,
|
||||
|
@ -126,12 +84,4 @@ module.exports = {
|
|||
blogSingle,
|
||||
admin,
|
||||
atom,
|
||||
// rss,
|
||||
registerPost,
|
||||
loginPost,
|
||||
settingPost,
|
||||
postBlog,
|
||||
deleteBlog,
|
||||
deleteImage,
|
||||
updateBlog,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// TODO: Permissions file
|
||||
function postBlog(user) {}
|
||||
|
||||
function checkPermissions(role, { minimum = true }) {}
|
||||
module.exports = { postBlog };
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
const persistent_setting = require("node-persist");
|
||||
persistent_setting.init({ dir: "data/site/" });
|
||||
|
||||
let settings = {
|
||||
SETUP_COMPLETE: false,
|
||||
ACCOUNT_REGISTRATION: false,
|
||||
HIDE_LOGIN: false,
|
||||
BLOG_UPLOADING: false,
|
||||
|
||||
USER_MINIMUM_PASSWORD_LENGTH: 6,
|
||||
|
||||
BLOG_MINIMUM_TITLE_LENGTH: 6,
|
||||
BLOG_MINIMUM_DESCRIPTION_LENGTH: 6,
|
||||
BLOG_MINIMUM_CONTENT_LENGTH: 6,
|
||||
};
|
||||
|
||||
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 };
|
|
@ -82,7 +82,7 @@ async function publishBlog(unlisted, edit) {
|
|||
title: qs("#title").value,
|
||||
description: qs("#description").value,
|
||||
content: qs("#content").value,
|
||||
unlisted: unlisted ? true : false,
|
||||
visibility: unlisted ? "UNLISTED" : "PUBLISHED",
|
||||
date: qs("#date").value,
|
||||
time: qs("#time").value,
|
||||
};
|
||||
|
@ -114,7 +114,7 @@ async function publishBlog(unlisted, edit) {
|
|||
const res = await request("/api/web/blog", method, form_data);
|
||||
|
||||
if (res.body.success) {
|
||||
window.location.href = `/blog/${res.body.blog_id}`;
|
||||
window.location.href = `/blog/${res.body.blog_id || blog_id}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"feed": "^4.2.2",
|
||||
"markdown-it": "^13.0.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-persist": "^3.1.3",
|
||||
"sharp": "^0.32.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -3075,14 +3074,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-persist": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/node-persist/-/node-persist-3.1.3.tgz",
|
||||
"integrity": "sha512-CaFv+kSZtsc+VeDRldK1yR47k1vPLBpzYB9re2z7LIwITxwBtljMq3s8VQnnr+x3E8pQfHbc5r2IyJsBLJhtXg==",
|
||||
"engines": {
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz",
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
"feed": "^4.2.2",
|
||||
"markdown-it": "^13.0.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-persist": "^3.1.3",
|
||||
"sharp": "^0.32.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ model User {
|
|||
id String @id @unique @default(uuid())
|
||||
username String @unique
|
||||
password String
|
||||
role Role @default(VISITOR)
|
||||
|
||||
role Role @default(USER)
|
||||
group String?
|
||||
|
||||
blog_posts BlogPost[]
|
||||
profile_page ProfilePage?
|
||||
|
@ -30,9 +32,13 @@ model BlogPost {
|
|||
thumbnail String?
|
||||
images String[]
|
||||
visibility PostStatus @default(UNLISTED)
|
||||
owner User? @relation(fields: [ownerid], references: [id])
|
||||
owner User? @relation(fields: [ownerid], references: [id], onDelete: Cascade)
|
||||
ownerid String?
|
||||
|
||||
// Tags
|
||||
organization_tag String?
|
||||
keyword_tags String[]
|
||||
|
||||
// Dates
|
||||
publish_date DateTime?
|
||||
created_date DateTime @default(now())
|
||||
|
@ -43,14 +49,23 @@ model ProfilePage {
|
|||
content String?
|
||||
images String[]
|
||||
visibility PostStatus @default(UNLISTED)
|
||||
owner User @relation(fields: [ownerid], references: [id])
|
||||
owner User @relation(fields: [ownerid], references: [id], onDelete: Cascade)
|
||||
ownerid String @unique
|
||||
}
|
||||
|
||||
model Setting {
|
||||
id String @unique
|
||||
value Json
|
||||
}
|
||||
|
||||
model Group {
|
||||
id String @unique
|
||||
permissions String[]
|
||||
}
|
||||
|
||||
enum Role {
|
||||
LOCKED
|
||||
VISITOR
|
||||
AUTHOR
|
||||
USER
|
||||
ADMIN
|
||||
}
|
||||
|
||||
|
|
25
yab.js
25
yab.js
|
@ -7,6 +7,7 @@ const path = require("path");
|
|||
|
||||
// Local modules
|
||||
const page_scripts = require("./backend/page_scripts");
|
||||
const internal = require("./backend/core/internal_api");
|
||||
|
||||
// Express settings
|
||||
app.set("view-engine", "ejs");
|
||||
|
@ -23,30 +24,28 @@ app.use(
|
|||
})
|
||||
);
|
||||
|
||||
// Account Creation Endpoints
|
||||
app.get("/login", page_scripts.login);
|
||||
app.post("/login", checkNotAuthenticated, page_scripts.loginPost);
|
||||
app.get("/register", checkNotAuthenticated, page_scripts.register);
|
||||
app.post("/register", checkNotAuthenticated, page_scripts.registerPost);
|
||||
// API
|
||||
app.post("/login", checkNotAuthenticated, internal.postLogin);
|
||||
app.post("/register", checkNotAuthenticated, internal.postRegister);
|
||||
app.post("/setting", checkAuthenticated, internal.postSetting);
|
||||
app.post("/api/web/blog", checkAuthenticated, internal.postBlog);
|
||||
app.delete("/api/web/blog/image", checkAuthenticated, internal.deleteImage);
|
||||
app.delete("/api/web/blog", checkAuthenticated, internal.deleteBlog);
|
||||
app.patch("/api/web/blog", checkAuthenticated, internal.patchBlog);
|
||||
|
||||
// Account Required Endpoints
|
||||
app.post("/setting", checkAuthenticated, page_scripts.settingPost);
|
||||
app.get("/blog/new", checkAuthenticated, page_scripts.blogNew);
|
||||
app.post("/api/web/blog", checkAuthenticated, page_scripts.postBlog);
|
||||
app.delete("/api/web/blog", checkAuthenticated, page_scripts.deleteBlog);
|
||||
app.patch("/api/web/blog", checkAuthenticated, page_scripts.updateBlog);
|
||||
app.delete("/api/web/blog/image", checkAuthenticated, page_scripts.deleteImage);
|
||||
// app.delete("/logout", page_scripts.logout);
|
||||
|
||||
// Endpoints
|
||||
app.get("/", page_scripts.index);
|
||||
app.get("/login", page_scripts.login);
|
||||
app.get("/register", checkNotAuthenticated, page_scripts.register);
|
||||
app.get("/author/:author_username", page_scripts.author);
|
||||
app.get("/admin", checkAuthenticated, page_scripts.admin);
|
||||
app.get("/blog", page_scripts.blogList);
|
||||
app.get("/blog/new", checkAuthenticated, page_scripts.blogNew);
|
||||
app.get("/blog/:blog_id", page_scripts.blogSingle);
|
||||
app.get("/blog/:blog_id/edit", checkAuthenticated, page_scripts.blogEdit);
|
||||
app.get("/atom", page_scripts.atom);
|
||||
// app.get("/rss", page_scripts.rss);
|
||||
|
||||
function checkAuthenticated(req, res, next) {
|
||||
if (req.session.user) return next();
|
||||
|
|
Loading…
Reference in New Issue