From 57460c2328a376eb54fb8196ba192b54eb7ed58f Mon Sep 17 00:00:00 2001 From: Armored Dragon Date: Mon, 8 Jul 2024 13:21:36 -0500 Subject: [PATCH] Formatted with eslint. Signed-off-by: Armored Dragon --- backend/core/core.js | 882 +++++++++--------- backend/core/internal_api.js | 72 +- backend/page_scripts.js | 164 ++-- .../views/themes/default/css/settings.scss | 194 ++-- .../themes/default/ejs/admin-settings.ejs | 142 +-- frontend/views/themes/default/js/admin.js | 34 +- yab.js | 18 +- 7 files changed, 753 insertions(+), 753 deletions(-) diff --git a/backend/core/core.js b/backend/core/core.js index dd77715..7d48cfd 100644 --- a/backend/core/core.js +++ b/backend/core/core.js @@ -8,31 +8,31 @@ const crypto = require("crypto"); const validate = require("../form_validation"); const permissions = require("../permissions"); const md = require("markdown-it")() - .use(require("markdown-it-underline")) - .use(require("markdown-it-footnote")) - .use(require("markdown-it-sup")) - .use(require("markdown-it-anchor"), { - permalink: require("markdown-it-anchor").permalink.linkInsideHeader({ - placement: "before", - symbol: `⮺`, - }), - }); + .use(require("markdown-it-underline")) + .use(require("markdown-it-footnote")) + .use(require("markdown-it-sup")) + .use(require("markdown-it-anchor"), { + permalink: require("markdown-it-anchor").permalink.linkInsideHeader({ + placement: "before", + symbol: `⮺`, + }), + }); let settings = { - SETUP_COMPLETE: false, - ACCOUNT_REGISTRATION: false, - HIDE_LOGIN: false, - BLOG_UPLOADING: false, + SETUP_COMPLETE: false, + ACCOUNT_REGISTRATION: false, + HIDE_LOGIN: false, + BLOG_UPLOADING: false, - CD_RSS: true, - CD_JSON: true, + CD_RSS: true, + CD_JSON: true, - WEBSITE_NAME: "", - CUSTOM_HEADER: "", + WEBSITE_NAME: "", + CUSTOM_HEADER: "", - USER_MINIMUM_PASSWORD_LENGTH: 7, + USER_MINIMUM_PASSWORD_LENGTH: 7, - theme: "default", + theme: "default", }; let use_s3_storage = false; _initS3Storage(); @@ -40,427 +40,427 @@ _getSettings(); // Checks to see if S3 storage is set function _initS3Storage() { - if (process.env.S3_ACCESS_KEY && process.env.S3_SECRET_KEY && process.env.S3_REGION && process.env.S3_ENDPOINT) { - console.log("S3 Server configured. Proceeding using S3 bucket"); - use_s3_storage = true; - s3 = new S3Client({ - credentials: { - accessKeyId: process.env.S3_ACCESS_KEY, - secretAccessKey: process.env.S3_SECRET_KEY, - }, - region: process.env.S3_REGION, - endpoint: process.env.S3_ENDPOINT, - }); - return; - } else { - console.log("S3 Server NOT SET. Media uploads will not work."); - use_s3_storage = false; - return; - } + if (process.env.S3_ACCESS_KEY && process.env.S3_SECRET_KEY && process.env.S3_REGION && process.env.S3_ENDPOINT) { + console.log("S3 Server configured. Proceeding using S3 bucket"); + use_s3_storage = true; + s3 = new S3Client({ + credentials: { + accessKeyId: process.env.S3_ACCESS_KEY, + secretAccessKey: process.env.S3_SECRET_KEY, + }, + region: process.env.S3_REGION, + endpoint: process.env.S3_ENDPOINT, + }); + return; + } else { + console.log("S3 Server NOT SET. Media uploads will not work."); + use_s3_storage = false; + return; + } } // Users async function newUser({ username, password, role } = {}) { - // Sanity check on user registration. - const valid = validate.newUser({ username: username, password: password }); - if (!valid.success) return _r(false, valid.message); + // Sanity check on user registration. + const valid = validate.newUser({ username: username, password: password }); + if (!valid.success) return _r(false, valid.message); - // Create the account - try { - user_database_entry = await prisma.user.create({ data: { username: username, password: password, role: role } }); - } catch (e) { - let message = "Unknown error"; - return _r(false, message); - } + // Create the account + try { + user_database_entry = await prisma.user.create({ data: { username: username, password: password, role: role } }); + } catch (e) { + let message = "Unknown error"; + return _r(false, message); + } - // Create the profile page and link - try { - user_profile_database_entry = await prisma.profilePage.create({ data: { owner: { connect: { id: user_database_entry.id } } } }); - } catch (e) { - return _r(false, `Error creating profile page for user ${username}`); - } + // Create the profile page and link + try { + user_profile_database_entry = await prisma.profilePage.create({ data: { owner: { connect: { id: user_database_entry.id } } } }); + } catch (e) { + return _r(false, `Error creating profile page for user ${username}`); + } - // Master user was created; server initialized - editSetting({ name: "SETUP_COMPLETE", value: true }); + // Master user was created; server initialized + editSetting({ name: "SETUP_COMPLETE", value: true }); } async function getUser({ user_id, username, include_password = false }) { - if (!username && !user_id) return _r(false, "Either a user_id or username is needed."); + if (!username && !user_id) return _r(false, "Either a user_id or username is needed."); - let user; + let user; - if (user_id) user = await prisma.user.findUnique({ where: { id: user_id } }); - else if (username) user = await prisma.user.findUnique({ where: { username: username } }); + if (user_id) user = await prisma.user.findUnique({ where: { id: user_id } }); + else if (username) user = await prisma.user.findUnique({ where: { username: username } }); - if (!user) return _r(false, "No matching user"); + if (!user) return _r(false, "No matching user"); - // Delete the password from responses - if (!include_password) delete user.password; + // Delete the password from responses + if (!include_password) delete user.password; - return { success: true, data: user }; + return { success: true, data: user }; } // TODO: Rename patchUser async function editUser({ requester_id, user_id, user_content }) { - let user = await getUser({ user_id: user_id }); - if (!user.success) return _r(false, "User not found"); - user = user.data; + let user = await getUser({ user_id: user_id }); + if (!user.success) return _r(false, "User not found"); + user = user.data; - // TODO: - // If there was a role change, see if the acting user can make these changes + // TODO: + // If there was a role change, see if the acting user can make these changes - // TODO: - // If there was a password change, - // check to see if the user can make these changes - // Hash the password + // TODO: + // If there was a password change, + // check to see if the user can make these changes + // Hash the password - // FIXME: Not secure. ASAP! - let formatted = {}; - formatted[user_content.setting_name] = user_content.value; + // FIXME: Not secure. ASAP! + let formatted = {}; + formatted[user_content.setting_name] = user_content.value; - await prisma.user.update({ where: { id: user.id }, data: formatted }); - return _r(true); + await prisma.user.update({ where: { id: user.id }, data: formatted }); + return _r(true); } async function deleteUser({ user_id }) { - if (!user_id) return _r(false, "User_id not specified."); + if (!user_id) return _r(false, "User_id not specified."); - await prisma.user.delete({ where: { id: user_id } }); // TODO: Test - return _r(true, `User ${user_id} deleted`); + await prisma.user.delete({ where: { id: user_id } }); // TODO: Test + return _r(true, `User ${user_id} deleted`); } // Posts async function newPost({ requester_id }) { - // TODO: Validate request (Does user have perms?) - // TODO: Does server allow new posts? + // TODO: Validate request (Does user have perms?) + // TODO: Does server allow new posts? - // Find if user already has a draft - let existing_post = await prisma.post.findFirst({ where: { owner: { id: requester_id }, visibility: "DRAFT" } }); - if (existing_post) return existing_post.id; + // Find if user already has a draft + let existing_post = await prisma.post.findFirst({ where: { owner: { id: requester_id }, visibility: "DRAFT" } }); + if (existing_post) return existing_post.id; - const post = await prisma.post.create({ data: { owner: { connect: { id: requester_id } } } }); + const post = await prisma.post.create({ data: { owner: { connect: { id: requester_id } } } }); - return post.id; + return post.id; } async function getPost({ requester_id, post_id, visibility = "PUBLISHED" } = {}, { search, search_title, search_content, search_tags } = {}, { limit = 10, page = 0, pagination = true } = {}) { - let where_object = { - OR: [ - // Standard discovery: Public, and after the publish date - { - AND: [ - { - visibility: "PUBLISHED", - }, - { - publish_date: { - lte: new Date(), - }, - }, - ], - }, + let where_object = { + OR: [ + // Standard discovery: Public, and after the publish date + { + AND: [ + { + visibility: "PUBLISHED", + }, + { + publish_date: { + lte: new Date(), + }, + }, + ], + }, - // User owns the post - { - ownerid: requester_id, - }, - ], + // User owns the post + { + ownerid: requester_id, + }, + ], - AND: [ - { - OR: [], - }, - ], - }; + AND: [ + { + OR: [], + }, + ], + }; - // Admins can view everything at any point - let user; - if (requester_id) { - user = await getUser({ user_id: requester_id }); - if (user.success) user = user.data; - if (user.role === "ADMIN") where_object["OR"].push({ NOT: { id: "" } }); - } + // Admins can view everything at any point + let user; + if (requester_id) { + user = await getUser({ user_id: requester_id }); + if (user.success) user = user.data; + if (user.role === "ADMIN") where_object["OR"].push({ NOT: { id: "" } }); + } - // Get a single post - if (post_id) { - let post; + // Get a single post + if (post_id) { + let post; - // We can view unlisted posts, but we don't want them to show up otherwise in search. - // Inject a "unlisted" inclusion into where_object to allow direct viewing of unlisted posts - where_object["OR"].push({ visibility: "UNLISTED" }); + // We can view unlisted posts, but we don't want them to show up otherwise in search. + // Inject a "unlisted" inclusion into where_object to allow direct viewing of unlisted posts + where_object["OR"].push({ visibility: "UNLISTED" }); - // Allow getting drafts if the requesting user owns the draft - where_object["OR"].push({ AND: [{ visibility: "DRAFT" }, { ownerid: requester_id }] }); + // Allow getting drafts if the requesting user owns the draft + where_object["OR"].push({ AND: [{ visibility: "DRAFT" }, { ownerid: requester_id }] }); - post = await prisma.post.findUnique({ where: { ...where_object, id: post_id }, include: { owner: true, tags: true } }); - if (!post) return _r(false, "Post does not exist"); - post = _stripPrivatePost(post); + post = await prisma.post.findUnique({ where: { ...where_object, id: post_id }, include: { owner: true, tags: true } }); + if (!post) return _r(false, "Post does not exist"); + post = _stripPrivatePost(post); - // Tags - let post_tags = []; - post.raw_tags = []; - post.tags.forEach((tag) => { - post_tags.push(tag.name); - post.raw_tags.push(); - }); - post.tags = post_tags; + // Tags + let post_tags = []; + post.raw_tags = []; + post.tags.forEach((tag) => { + post_tags.push(tag.name); + post.raw_tags.push(); + }); + post.tags = post_tags; - // Render post - return { success: true, data: await _renderPost(post) }; - } + // Render post + return { success: true, data: await _renderPost(post) }; + } - // Otherwise build WHERE_OBJECT using data we do have - let post_list = []; + // Otherwise build WHERE_OBJECT using data we do have + let post_list = []; - // Build the "where_object" object - if (search) { - if (search_tags) where_object["AND"][0]["OR"].push({ tags: { some: { name: search?.toLowerCase() } } }); - if (search_title) where_object["AND"][0]["OR"].push({ title: { contains: search, mode: "insensitive" } }); - if (search_content) where_object["AND"][0]["OR"].push({ content: { contains: search, mode: "insensitive" } }); - } - // Execute search - let posts = await prisma.post.findMany({ - where: where_object, - take: limit, - skip: Math.max(page, 0) * limit, - include: { owner: true, tags: true }, - orderBy: [{ publish_date: "desc" }, { created_date: "desc" }], - }); + // Build the "where_object" object + if (search) { + if (search_tags) where_object["AND"][0]["OR"].push({ tags: { some: { name: search?.toLowerCase() } } }); + if (search_title) where_object["AND"][0]["OR"].push({ title: { contains: search, mode: "insensitive" } }); + if (search_content) where_object["AND"][0]["OR"].push({ content: { contains: search, mode: "insensitive" } }); + } + // Execute search + let posts = await prisma.post.findMany({ + where: where_object, + take: limit, + skip: Math.max(page, 0) * limit, + include: { owner: true, tags: true }, + orderBy: [{ publish_date: "desc" }, { created_date: "desc" }], + }); - for (post of posts) { - post = _stripPrivatePost(post); - post = await _renderPost(post); - post_list.push(post); + for (post of posts) { + post = _stripPrivatePost(post); + post = await _renderPost(post); + post_list.push(post); - let post_tags = []; - post.tags.forEach((tag) => post_tags.push(tag.name)); - post.tags = post_tags; - } + let post_tags = []; + post.tags.forEach((tag) => post_tags.push(tag.name)); + post.tags = post_tags; + } - // Calculate pagination - let post_count = await prisma.post.count({ - where: where_object, - }); + // Calculate pagination + let post_count = await prisma.post.count({ + where: where_object, + }); - return { data: post_list, pagination: _getNavigationList(page, Math.ceil(post_count / limit)), success: true }; + return { data: post_list, pagination: _getNavigationList(page, Math.ceil(post_count / limit)), success: true }; - function _getNavigationList(current_page, max_page) { - current_page = Number(current_page); - max_page = Number(max_page); + function _getNavigationList(current_page, max_page) { + current_page = Number(current_page); + max_page = Number(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); - } + 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); + } } // TODO: Rename patchPost async function editPost({ requester_id, post_id, post_content }) { - let user = await getUser({ user_id: requester_id }); - let post = await getPost({ post_id: post_id }); + let user = await getUser({ user_id: requester_id }); + let post = await getPost({ post_id: post_id }); - // Validate the post content - let validated_post = validate.patchPost(post_content, user, post); - if (!validated_post.success) return _r(false, validated_post.message); + // Validate the post content + let validated_post = validate.patchPost(post_content, user, post); + if (!validated_post.success) return _r(false, validated_post.message); - user = validated_post.data.user; - post = validated_post.data.post; - validated_post = validated_post.data.post_formatted; + user = validated_post.data.user; + post = validated_post.data.post; + validated_post = validated_post.data.post_formatted; - // Check if the user can preform the action - const can_act = permissions.patchPost(post, user); - if (!can_act.success) return _r(false, can_act.message); + // Check if the user can preform the action + const can_act = permissions.patchPost(post, user); + if (!can_act.success) return _r(false, can_act.message); - // Handle tags ---------- - let database_tag_list = []; - const existing_tags = post.tags?.map((tag) => ({ name: tag })) || []; + // Handle tags ---------- + let database_tag_list = []; + const existing_tags = post.tags?.map((tag) => ({ name: tag })) || []; - // Add new tags - for (let tag_index = 0; post_content.tags.length > tag_index; tag_index++) { - let tag = post_content.tags[tag_index]; + // Add new tags + for (let tag_index = 0; post_content.tags.length > tag_index; tag_index++) { + let tag = post_content.tags[tag_index]; - // Check to see if tag exists, create if necessary, - let database_tag = await prisma.tag.upsert({ where: { name: tag }, update: {}, create: { name: tag } }); + // Check to see if tag exists, create if necessary, + let database_tag = await prisma.tag.upsert({ where: { name: tag }, update: {}, create: { name: tag } }); - database_tag_list.push(database_tag); - } + database_tag_list.push(database_tag); + } - // Rebuild the post to save - let post_formatted = { - ...validated_post, - // Handle tag changes - tags: { disconnect: [...existing_tags], connect: [...database_tag_list] }, - // Handle media changes - media: [...post.raw_media, ...post_content.media], - }; + // Rebuild the post to save + let post_formatted = { + ...validated_post, + // Handle tag changes + tags: { disconnect: [...existing_tags], connect: [...database_tag_list] }, + // Handle media changes + media: [...post.raw_media, ...post_content.media], + }; - // Save the updated post to the database - await prisma.post.update({ where: { id: post.id }, data: post_formatted }); + // Save the updated post to the database + await prisma.post.update({ where: { id: post.id }, data: post_formatted }); - // Prune the post to save on storage - await pruneMedia({ parent_id: post_id, parent_type: "posts" }); + // Prune the post to save on storage + await pruneMedia({ parent_id: post_id, parent_type: "posts" }); - return _r(true); + return _r(true); } async function deletePost({ requester_id, post_id }) { - let user = await getUser({ user_id: requester_id }); - let post = await getPost({ post_id: post_id }); + let user = await getUser({ user_id: requester_id }); + let post = await getPost({ post_id: post_id }); - if (!user.success) return { success: false, message: user.message || "User does not exist" }; - user = user.data; + if (!user.success) return { success: false, message: user.message || "User does not exist" }; + user = user.data; - if (!post.success) return { success: false, message: post.message || "Post does not exist" }; - post = post.data; + if (!post.success) return { success: false, message: post.message || "Post does not exist" }; + post = post.data; - let can_delete = post.owner.id === user.id || user.role === "ADMIN"; + let can_delete = post.owner.id === user.id || user.role === "ADMIN"; - if (!can_delete) return { success: false, message: "Action not permitted" }; + if (!can_delete) return { success: false, message: "Action not permitted" }; - await prisma.post.delete({ where: { id: post.id } }); - _deleteS3Directory(post.id, "post"); + await prisma.post.delete({ where: { id: post.id } }); + _deleteS3Directory(post.id, "post"); - return { success: true }; + return { success: true }; } // User Profiles async function getBiography({ requester_id, author_id }) { - if (!author_id) return _r(false, "No Author specified."); - let post = await prisma.profilePage.findFirst({ where: { ownerid: author_id }, include: { owner: true } }); + if (!author_id) return _r(false, "No Author specified."); + let post = await prisma.profilePage.findFirst({ where: { ownerid: author_id }, include: { owner: true } }); - // Check if it is private - // TODO + // Check if it is private + // TODO - // HACK: - // When we render the post and reading from S3, we want the post id - // The problem is when a user views the biography page, the page shows the account id opposed to the "profile page" id. - // This causes a incorrect parent_id value and an incorrect key. - // Replace the "id" to the value it's expecting. - const original_post_id = post.id; - let rendering_formatted_post = {}; + // HACK: + // When we render the post and reading from S3, we want the post id + // The problem is when a user views the biography page, the page shows the account id opposed to the "profile page" id. + // This causes a incorrect parent_id value and an incorrect key. + // Replace the "id" to the value it's expecting. + const original_post_id = post.id; + let rendering_formatted_post = {}; - rendering_formatted_post = post; - rendering_formatted_post.id = author_id; + rendering_formatted_post = post; + rendering_formatted_post.id = author_id; - // Render - post = _stripPrivatePost(post); - post = await _renderPost(rendering_formatted_post); + // Render + post = _stripPrivatePost(post); + post = await _renderPost(rendering_formatted_post); - post.id = original_post_id; + post.id = original_post_id; - return { success: true, data: post }; + return { success: true, data: post }; } // TODO: Rename to patchBiography async function updateBiography({ requester_id, author_id, biography_content }) { - let user = await getUser({ user_id: requester_id }); - let biography = await getBiography({ author_id: author_id }); + let user = await getUser({ user_id: requester_id }); + let biography = await getBiography({ author_id: author_id }); - // Validate post ---------- - let formatted_biography = validate.patchBiography(biography_content, user, biography); - if (!formatted_biography.success) return _r(false, formatted_biography.message); + // Validate post ---------- + let formatted_biography = validate.patchBiography(biography_content, user, biography); + if (!formatted_biography.success) return _r(false, formatted_biography.message); - user = formatted_biography.data.user; - biography = formatted_biography.data.biography; - biography_content = formatted_biography.data.biography_content; + user = formatted_biography.data.user; + biography = formatted_biography.data.biography; + biography_content = formatted_biography.data.biography_content; - // Permission check ---------- - const can_act = permissions.patchBiography(biography_content, user, biography); - if (!can_act.success) return _r(false, "User not permitted"); + // Permission check ---------- + const can_act = permissions.patchBiography(biography_content, user, biography); + if (!can_act.success) return _r(false, "User not permitted"); - let formatted = { - content: biography_content.content, - media: [...biography.raw_media, ...biography_content.media], - }; + let formatted = { + content: biography_content.content, + media: [...biography.raw_media, ...biography_content.media], + }; - await prisma.profilePage.update({ where: { id: biography.id }, data: formatted }); + await prisma.profilePage.update({ where: { id: biography.id }, data: formatted }); - return _r(true); + return _r(true); } async function uploadMedia({ parent_id, parent_type, file_buffer, content_type }) { - if (!use_s3_storage) return null; - const content_name = crypto.randomUUID(); - let maximum_image_resolution = { width: 1920, height: 1080 }; + if (!use_s3_storage) return null; + const content_name = crypto.randomUUID(); + let maximum_image_resolution = { width: 1920, height: 1080 }; - // Images - const compressed_image = await sharp(Buffer.from(file_buffer.split(",")[1], "base64"), { animated: true }) - .resize({ ...maximum_image_resolution, withoutEnlargement: true, fit: "inside" }) - .webp({ quality: 90, animated: true }) - .toBuffer(); + // Images + const compressed_image = await sharp(Buffer.from(file_buffer.split(",")[1], "base64"), { animated: true }) + .resize({ ...maximum_image_resolution, withoutEnlargement: true, fit: "inside" }) + .webp({ quality: 90, animated: true }) + .toBuffer(); - let extension; - let s3_content_type; + let extension; + let s3_content_type; - if (content_type.includes("image/")) { - extension = ".webp"; - s3_content_type = "image/webp"; - } + if (content_type.includes("image/")) { + extension = ".webp"; + s3_content_type = "image/webp"; + } - const params = { - Bucket: process.env.S3_BUCKET_NAME, - Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${content_name}${extension}`, - Body: compressed_image, - ContentType: s3_content_type, - }; + const params = { + Bucket: process.env.S3_BUCKET_NAME, + Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${content_name}${extension}`, + Body: compressed_image, + ContentType: s3_content_type, + }; - const command = new PutObjectCommand(params); - await s3.send(command); + const command = new PutObjectCommand(params); + await s3.send(command); - return content_name + extension; + return content_name + extension; } async function getMedia({ parent_id, parent_type, file_name }) { - if (!use_s3_storage) return null; - const params = { Bucket: process.env.S3_BUCKET_NAME, Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${file_name}` }; - return await getSignedUrl(s3, new GetObjectCommand(params), { expiresIn: 3600 }); + if (!use_s3_storage) return null; + const params = { Bucket: process.env.S3_BUCKET_NAME, Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${file_name}` }; + return await getSignedUrl(s3, new GetObjectCommand(params), { expiresIn: 3600 }); } async function deleteMedia({ parent_id, parent_type, file_name }) { - const request_params = { - Bucket: process.env.S3_BUCKET_NAME, - Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${file_name}`, - }; + const request_params = { + Bucket: process.env.S3_BUCKET_NAME, + Key: `${process.env.ENVIRONMENT}/${parent_type}/${parent_id}/${file_name}`, + }; - const command = new DeleteObjectCommand(request_params); - await s3.send(command); + const command = new DeleteObjectCommand(request_params); + await s3.send(command); - return { success: true }; + return { success: true }; } // This cleans up all unused and unreferenced media files. // NOTE: Only made for posts, as that is all that there is right now async function pruneMedia({ parent_id, parent_type }) { - let post = await getPost({ post_id: parent_id }); + let post = await getPost({ post_id: parent_id }); - if (!post.success) return { success: false, message: post.message || "Post does not exist" }; - post = post.data; + if (!post.success) return { success: false, message: post.message || "Post does not exist" }; + post = post.data; - // const total_number_of_media = post.raw_media.length; + // const total_number_of_media = post.raw_media.length; - for (let media_index = 0; post.raw_media.length > media_index; media_index++) { - if (!post.raw_content.includes(post.raw_media[media_index])) { - // Delete the media off of the S3 server - let delete_request = await deleteMedia({ parent_id: parent_id, parent_type: parent_type, file_name: post.raw_media[media_index] }); - if (!delete_request.success) continue; + for (let media_index = 0; post.raw_media.length > media_index; media_index++) { + if (!post.raw_content.includes(post.raw_media[media_index])) { + // Delete the media off of the S3 server + let delete_request = await deleteMedia({ parent_id: parent_id, parent_type: parent_type, file_name: post.raw_media[media_index] }); + if (!delete_request.success) continue; - // Remove from the list in the database - await post.raw_media.splice(media_index, 1); - // Save in the database - await prisma.post.update({ where: { id: parent_id }, data: { media: post.raw_media } }); + // Remove from the list in the database + await post.raw_media.splice(media_index, 1); + // Save in the database + await prisma.post.update({ where: { id: parent_id }, data: { media: post.raw_media } }); - // Delete was successful, move the index back to account for new array length - media_index--; - } - } + // Delete was successful, move the index back to account for new array length + media_index--; + } + } } async function getTags({ order = "count" } = {}) { - if (order == "count") { - return await prisma.tag.findMany({ - include: { _count: { select: { posts: true } } }, - where: { - posts: { - some: {}, - }, - }, - take: 15, - orderBy: { - posts: { - _count: "desc", - }, - }, - }); - } + if (order == "count") { + return await prisma.tag.findMany({ + include: { _count: { select: { posts: true } } }, + where: { + posts: { + some: {}, + }, + }, + take: 15, + orderBy: { + posts: { + _count: "desc", + }, + }, + }); + } } // TODO: @@ -468,172 +468,172 @@ async function getTags({ order = "count" } = {}) { async function deleteTag({ tag_id }) {} async function _deleteS3Directory(id, type) { - // Erase database images from S3 server - const folder_params = { Bucket: process.env.S3_BUCKET_NAME, Prefix: `${process.env.ENVIRONMENT}/${type}/${id}` }; + // Erase database images from S3 server + const folder_params = { Bucket: process.env.S3_BUCKET_NAME, Prefix: `${process.env.ENVIRONMENT}/${type}/${id}` }; - // Retrieve a list of objects in the specified directory - const listed_objects = await s3.send(new ListObjectsCommand(folder_params)); + // Retrieve a list of objects in the specified directory + const listed_objects = await s3.send(new ListObjectsCommand(folder_params)); - // If the directory is already empty, return - if (listed_objects?.Contents?.length === 0 || !listed_objects.Contents) return; + // If the directory is already empty, return + if (listed_objects?.Contents?.length === 0 || !listed_objects.Contents) return; - // Create an object to specify the bucket and objects to be deleted - const delete_params = { - Bucket: process.env.S3_BUCKET_NAME, - Delete: { Objects: [] }, - }; + // Create an object to specify the bucket and objects to be deleted + const delete_params = { + Bucket: process.env.S3_BUCKET_NAME, + Delete: { Objects: [] }, + }; - // Iterate over each object and push its key to the deleteParams object - listed_objects.Contents.forEach(({ Key }) => { - delete_params.Delete.Objects.push({ Key }); - }); + // Iterate over each object and push its key to the deleteParams object + listed_objects.Contents.forEach(({ Key }) => { + delete_params.Delete.Objects.push({ Key }); + }); - // Delete the objects specified in deleteParams - await s3.send(new DeleteObjectsCommand(delete_params)); + // Delete the objects specified in deleteParams + await s3.send(new DeleteObjectsCommand(delete_params)); - // If there are more objects to delete (truncated result), recursively call the function again - // if (listed_objects.IsTruncated) await emptyS3Directory(bucket, dir); + // 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(post) { - post.raw_media = []; - post.raw_content = post.content; + post.raw_media = []; + post.raw_content = post.content; - // For some reason Node does not like to set a variable and leave it. - post.media.forEach((media) => post.raw_media.push(media)); + // For some reason Node does not like to set a variable and leave it. + post.media.forEach((media) => post.raw_media.push(media)); - if (post.media) { - for (i = 0; post.media.length > i; i++) { - post.media[i] = await getMedia({ parent_id: post.id, parent_type: "posts", file_name: post.media[i] }); - } - } + if (post.media) { + for (i = 0; post.media.length > i; i++) { + post.media[i] = await getMedia({ parent_id: post.id, parent_type: "posts", file_name: post.media[i] }); + } + } - if (post.content) { - // Render the markdown contents of the post - post.content = md.render(post.content); + if (post.content) { + // Render the markdown contents of the post + post.content = md.render(post.content); - // Replace custom formatting with what we want - post.content = _formatBlogContent(post.content, post.media); - } - return post; + // Replace custom formatting with what we want + post.content = _formatBlogContent(post.content, post.media); + } + return post; - function _formatBlogContent(content, media_list) { - // Replace Images - const image_regex = /{image:([^}]+)}/g; + function _formatBlogContent(content, media_list) { + // Replace Images + const image_regex = /{image:([^}]+)}/g; - // Replace Side-by-side - const side_by_side = /{sidebyside}(.*?){\/sidebyside}/gs; + // Replace Side-by-side + const side_by_side = /{sidebyside}(.*?){\/sidebyside}/gs; - // Replace video links - const video = /{video:([^}]+)}/g; + // Replace video links + const video = /{video:([^}]+)}/g; - content = content.replace(video, (match, inner_content) => { - return `
`; - }); + content = content.replace(video, (match, inner_content) => { + return `
`; + }); - // Replace Images - content = content.replace(image_regex, (match, image_name) => { - for (media of media_list) { - if (media.includes(image_name)) { - return `
`; - } - } + // Replace Images + content = content.replace(image_regex, (match, image_name) => { + for (media of media_list) { + if (media.includes(image_name)) { + return `
`; + } + } - // Unknown image (Image was probably deleted) - return ""; - }); + // Unknown image (Image was probably deleted) + return ""; + }); - content = content.replace(side_by_side, (match, inner_content) => { - return `
${inner_content}
`; - }); + content = content.replace(side_by_side, (match, inner_content) => { + return `
${inner_content}
`; + }); - // Finished formatting, return! - return content; + // Finished formatting, return! + return content; - function _getVideoEmbed(video_url) { - // YouTube - if (video_url.includes("youtu.be")) { - return `https://youtube.com/embed/${video_url.split("/")[3]}`; - } - if (video_url.includes("youtube")) { - let video_id = video_url.split("/")[3]; - video_id = video_id.split("watch?v=").pop(); - return `https://youtube.com/embed/${video_id}`; - } + function _getVideoEmbed(video_url) { + // YouTube + if (video_url.includes("youtu.be")) { + return `https://youtube.com/embed/${video_url.split("/")[3]}`; + } + if (video_url.includes("youtube")) { + let video_id = video_url.split("/")[3]; + video_id = video_id.split("watch?v=").pop(); + return `https://youtube.com/embed/${video_id}`; + } - // Odysee - if (video_url.includes("://odysee.com")) { - let video_link = `https://${video_url.split("/")[2]}/$/embed/${video_url.split("/")[3]}/${video_url.split("/")[4]}`; - return video_link; - } - } - } + // Odysee + if (video_url.includes("://odysee.com")) { + let video_link = `https://${video_url.split("/")[2]}/$/embed/${video_url.split("/")[3]}/${video_url.split("/")[4]}`; + return video_link; + } + } + } } 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; + // 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; - let value; - // Parse JSON if possible - try { - value = JSON.parse(found_value.value); - } catch { - value = 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); - }); + return (settings[key] = value); + }); } // TODO: Replace async function getSetting(key, { parse = true }) { - if (!settings[key]) return null; + if (!settings[key]) return null; - if (parse) { - return JSON.parse(settings[key]); - } - return settings[key]; + if (parse) { + return JSON.parse(settings[key]); + } + return settings[key]; } // TODO: Replace async function postSetting(key, value) { - try { - if (!Object.keys(settings).includes(key)) return { success: false, message: "Setting not valid" }; + 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 } }); - try { - settings[key] = JSON.parse(value); - } catch { - settings[key] = value; - } + await prisma.setting.upsert({ where: { id: key }, update: { value: value }, create: { id: key, value: value } }); + try { + settings[key] = JSON.parse(value); + } catch { + settings[key] = value; + } - return { success: true }; - } catch (e) { - return { success: false, message: e.message }; - } + return { success: true }; + } catch (e) { + return { success: false, message: e.message }; + } } // TODO: Replace async function editSetting({ name, value }) { - if (!Object.keys(settings).includes(name)) return _r(false, "Setting is not valid"); + if (!Object.keys(settings).includes(name)) return _r(false, "Setting is not valid"); - await prisma.setting.upsert({ where: { id: name }, update: { value: value }, create: { id: name, value: value } }); - try { - settings[key] = JSON.parse(value); - } catch { - settings[key] = value; - } + await prisma.setting.upsert({ where: { id: name }, update: { value: value }, create: { id: name, value: value } }); + try { + settings[key] = JSON.parse(value); + } catch { + settings[key] = value; + } - return _r(true); + return _r(true); } function _stripPrivatePost(post) { - if (!post) return; - if (post.owner) delete post.owner.password; - return post; + if (!post) return; + if (post.owner) delete post.owner.password; + return post; } const _r = (s, m) => { - return { success: s, message: m }; + return { success: s, message: m }; }; module.exports = { settings, newUser, getUser, editUser, getPost, newPost, editPost, deletePost, getBiography, updateBiography, uploadMedia, getTags, postSetting, getSetting }; diff --git a/backend/core/internal_api.js b/backend/core/internal_api.js index eb219e0..3adf840 100644 --- a/backend/core/internal_api.js +++ b/backend/core/internal_api.js @@ -3,68 +3,68 @@ const bcrypt = require("bcrypt"); const validate = require("../form_validation"); 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? - // 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" }); + // 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" }); - // User data valid? - if (!form_validation.success) return res.json({ success: false, message: form_validation.message }); + // User data valid? + 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. - const role = core.settings["SETUP_COMPLETE"] ? undefined : "ADMIN"; + // 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 hashed_password = await bcrypt.hash(password, 10); // Hash the password for security :^) - res.json(await core.newUser({ username: username, password: hashed_password, role: role })); + 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 })); } 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 - const existing_user = await core.getUser({ username: username, include_password: true }); - if (!existing_user.success) return res.json({ success: false, message: existing_user.message }); + // Get the user by username + const existing_user = await core.getUser({ username: username, include_password: true }); + if (!existing_user.success) return res.json({ success: false, message: existing_user.message }); - // 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" }); + // 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 }); + // 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({ 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.data.role !== "ADMIN") return response.json({ success: false, message: "User is not permitted" }); + 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" }); - 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) { - // TODO: Permissions for uploading images - // 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 })); + // TODO: Permissions for uploading images + // 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 })); } async function deleteImage(req, res) { - // TODO: Permissions for deleting image - return res.json(await core.deleteImage(req.body, req.session.user.id)); + // TODO: Permissions for deleting image + return res.json(await core.deleteImage(req.body, req.session.user.id)); } async function deleteBlog(req, res) { - // TODO: Permissions for deleting blog - return res.json(await core.deletePost({ post_id: req.body.id, requester_id: req.session.user.id })); + // TODO: Permissions for deleting blog + return res.json(await core.deletePost({ post_id: req.body.id, requester_id: req.session.user.id })); } 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) { - // TODO: Validate - return response.json(await core.updateBiography({ requester_id: request.session.user.id, author_id: request.body.id, biography_content: request.body })); + // TODO: Validate + 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) { - 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 })); } module.exports = { postRegister, patchBiography, postLogin, postSetting, postImage, deleteImage, deleteBlog, patchBlog, patchUser }; diff --git a/backend/page_scripts.js b/backend/page_scripts.js index 6e21d3f..58943ee 100644 --- a/backend/page_scripts.js +++ b/backend/page_scripts.js @@ -2,126 +2,126 @@ const external = require("./core/external_api"); const core = require("./core/core"); function _getThemePage(page_name) { - let manifest = require(`../frontend/views/themes/${core.settings.theme}/manifest.json`); - return `themes/${core.settings.theme}/${manifest.pages[page_name]}`; + let manifest = require(`../frontend/views/themes/${core.settings.theme}/manifest.json`); + return `themes/${core.settings.theme}/${manifest.pages[page_name]}`; } async function getDefaults(req) { - // TODO: Fix reference to website_name - let user; - if (req.session.user) user = await core.getUser({ user_id: req.session.user.id }); - if (user?.success) user = user.data; - return { logged_in_user: user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings }; + // TODO: Fix reference to website_name + let user; + if (req.session.user) user = await core.getUser({ user_id: req.session.user.id }); + if (user?.success) user = user.data; + return { logged_in_user: user, website_name: core.settings.WEBSITE_NAME || "Yet-Another-Blog", settings: core.settings }; } async function index(request, response) { - // Check if the master admin has been created - // const is_setup_complete = core.settings["SETUP_COMPLETE"]; - // if (!is_setup_complete) return response.redirect("/register"); + // Check if the master admin has been created + // const is_setup_complete = core.settings["SETUP_COMPLETE"]; + // 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 tags = await core.getTags(); + const blog_list = await core.getPost({ requester_id: request.session.user?.id }, {}, { page: request.query.page || 0 }); + const tags = await core.getTags(); - blog_list.data.forEach((post) => { - 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")}`; - post.publish_date = formatted_date; - }); + blog_list.data.forEach((post) => { + 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")}`; + post.publish_date = formatted_date; + }); - response.render(_getThemePage("index"), { - ...(await getDefaults(request)), - blog_list: blog_list.data, - pagination: blog_list.pagination, - current_page: request.query.page || 0, - loaded_page: request.path, - tags: tags, - }); + response.render(_getThemePage("index"), { + ...(await getDefaults(request)), + blog_list: blog_list.data, + pagination: blog_list.pagination, + current_page: request.query.page || 0, + loaded_page: request.path, + tags: tags, + }); } async function register(request, response) { - response.render(_getThemePage("register"), await getDefaults(request)); + response.render(_getThemePage("register"), await getDefaults(request)); } async function login(request, response) { - response.render(_getThemePage("login"), await getDefaults(request)); + response.render(_getThemePage("login"), await getDefaults(request)); } async function author(req, res) { - const user = await core.getUser({ user_id: req.params.author_id }); - // FIXME: Bandage fix for author get error - if (!user.success) return res.redirect("/"); - const profile = await core.getBiography({ author_id: user.data.id }); - // TODO: Check for success - const posts = await core.getPost({ requester_id: user.data.id }); + const user = await core.getUser({ user_id: req.params.author_id }); + // FIXME: Bandage fix for author get error + if (!user.success) return res.redirect("/"); + const profile = await core.getBiography({ author_id: user.data.id }); + // TODO: Check for success + 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) { - let author = await core.getBiography({ author_id: request.params.author_id }); - if (!author.success) return response.redirect("/"); - response.render(_getThemePage("authorEdit"), { ...(await getDefaults(request)), profile: author.data }); + let author = await core.getBiography({ author_id: request.params.author_id }); + if (!author.success) return response.redirect("/"); + response.render(_getThemePage("authorEdit"), { ...(await getDefaults(request)), profile: author.data }); } 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) => { - 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")}`; - post.publish_date = formatted_date; - }); + blog_list.data.forEach((post) => { + 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")}`; + post.publish_date = formatted_date; + }); - res.render(_getThemePage("postSearch"), { - ...(await getDefaults(req)), - blog_list: blog_list.data, - pagination: blog_list.pagination, - current_page: req.query.page || 0, - loaded_page: req.path, - }); + res.render(_getThemePage("postSearch"), { + ...(await getDefaults(req)), + blog_list: blog_list.data, + pagination: blog_list.pagination, + current_page: req.query.page || 0, + loaded_page: req.path, + }); } async function blogSingle(req, res) { - const blog = await core.getPost({ requester_id: req.session.user?.id, post_id: req.params.blog_id }); - if (blog.success === false) return res.redirect("/"); - res.render(_getThemePage("post"), { ...(await getDefaults(req)), blog_post: blog.data }); + const blog = await core.getPost({ requester_id: req.session.user?.id, post_id: req.params.blog_id }); + if (blog.success === false) return res.redirect("/"); + res.render(_getThemePage("post"), { ...(await getDefaults(req)), blog_post: blog.data }); } async function blogNew(request, response) { - const new_post = await core.newPost({ requester_id: request.session.user.id }); - return response.redirect(`/post/${new_post}/edit`); + const new_post = await core.newPost({ requester_id: request.session.user.id }); + return response.redirect(`/post/${new_post}/edit`); } async function blogEdit(req, res) { - let existing_blog = await core.getPost({ post_id: req.params.blog_id }); - if (!existing_blog.success) return res.redirect("/"); - existing_blog = existing_blog.data; + let existing_blog = await core.getPost({ post_id: req.params.blog_id }); + if (!existing_blog.success) return res.redirect("/"); + existing_blog = existing_blog.data; - 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_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; + 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(_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) { - response.render(_getThemePage("admin-settings"), { ...(await getDefaults(request)) }); + response.render(_getThemePage("admin-settings"), { ...(await getDefaults(request)) }); } async function atom(req, res) { - res.type("application/xml"); - res.send(await external.getFeed({ type: "atom" })); + res.type("application/xml"); + res.send(await external.getFeed({ type: "atom" })); } async function jsonFeed(req, res) { - res.type("application/json"); - res.send(await external.getFeed({ type: "json" })); + res.type("application/json"); + res.send(await external.getFeed({ type: "json" })); } // Internal API ------------------------------ module.exports = { - index, - register, - login, - author, - blogList, - blogNew, - blogEdit, - blogSingle, - admin, - atom, - jsonFeed, - authorEdit, + index, + register, + login, + author, + blogList, + blogNew, + blogEdit, + blogSingle, + admin, + atom, + jsonFeed, + authorEdit, }; diff --git a/frontend/views/themes/default/css/settings.scss b/frontend/views/themes/default/css/settings.scss index 6679347..3a0803e 100644 --- a/frontend/views/themes/default/css/settings.scss +++ b/frontend/views/themes/default/css/settings.scss @@ -1,127 +1,127 @@ .page .page-center { - background-color: white; - width: 700px; - min-height: 50px; - margin-top: 4rem; - padding: 1rem; - box-sizing: border-box; - box-shadow: #0000001c 0 0px 5px; + background-color: white; + width: 700px; + min-height: 50px; + margin-top: 4rem; + padding: 1rem; + box-sizing: border-box; + box-shadow: #0000001c 0 0px 5px; - .header { - text-align: center; - font-size: 1.5rem; - } + .header { + text-align: center; + font-size: 1.5rem; + } - .setting-list { - display: flex; - flex-direction: column; + .setting-list { + display: flex; + flex-direction: column; - .setting { - width: 100%; - height: 32px; - background-color: rgb(240, 240, 240); - padding: 0.1rem; - box-sizing: border-box; - display: flex; - flex-direction: row; + .setting { + 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; - } + .title { + margin: auto auto auto 0; + } - .value { - margin: 0 0 0 auto; - display: flex; + .value { + margin: 0 0 0 auto; + display: flex; - input[type="text"] { - margin: auto; - font-size: 1rem; - text-align: center; - } - input[type="number"] { - margin: auto; - width: 4rem; - text-align: right; - font-size: 1rem; - } - } - } + input[type="text"] { + margin: auto; + font-size: 1rem; + text-align: center; + } + input[type="number"] { + margin: auto; + width: 4rem; + text-align: right; + font-size: 1rem; + } + } + } - .largeset { - flex-direction: column; - height: fit-content; - .value { - width: 100%; - padding: 0.25rem; - box-sizing: border-box; - textarea { - width: 100%; - margin: auto; - font-size: initial; - resize: vertical; - min-height: 3rem; - } - } - } + .largeset { + flex-direction: column; + height: fit-content; + .value { + width: 100%; + padding: 0.25rem; + box-sizing: border-box; + textarea { + width: 100%; + margin: auto; + font-size: initial; + resize: vertical; + min-height: 3rem; + } + } + } - .setting.fit-column { - flex-direction: column; - width: 100%; - } + .setting.fit-column { + flex-direction: column; + width: 100%; + } - .setting:nth-child(even) { - background-color: rgb(250, 250, 250); - } - } + .setting:nth-child(even) { + background-color: rgb(250, 250, 250); + } + } } .switch { - position: relative; - display: inline-block; - width: 50px; - height: 28px; + position: relative; + display: inline-block; + width: 50px; + height: 28px; } .switch input { - opacity: 0; - width: 0; - height: 0; + opacity: 0; + width: 0; + height: 0; } .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #b8b8b8; - -webkit-transition: 0.4s; - transition: 0.4s; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #b8b8b8; + -webkit-transition: 0.4s; + transition: 0.4s; } .slider:before { - position: absolute; - content: ""; - height: 20px; - width: 20px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: 0.4s; - transition: 0.4s; + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; } input:checked + .slider { - background-color: #2196f3; + background-color: #2196f3; } input:focus + .slider { - box-shadow: 0 0 1px #2196f3; + box-shadow: 0 0 1px #2196f3; } input:checked + .slider:before { - -webkit-transform: translateX(20px); - -ms-transform: translateX(20px); - transform: translateX(20px); + -webkit-transform: translateX(20px); + -ms-transform: translateX(20px); + transform: translateX(20px); } .slider.round { - border-radius: 34px; + border-radius: 34px; } .slider.round:before { - border-radius: 50%; + border-radius: 50%; } diff --git a/frontend/views/themes/default/ejs/admin-settings.ejs b/frontend/views/themes/default/ejs/admin-settings.ejs index ec3f5b3..60317b4 100644 --- a/frontend/views/themes/default/ejs/admin-settings.ejs +++ b/frontend/views/themes/default/ejs/admin-settings.ejs @@ -1,77 +1,77 @@ - - <%- include("partials/document-head.ejs") %> - - - - - - <%- include("partials/header.ejs") %> -
-
-
<%= website_name %> Admin Settings
-
-
-
User registration
-
- -
-
-
-
Hide "Login" in navigation bar
-
- -
-
-
-
Serve ATOM feed
-
- -
-
-
-
Serve JSON feed
-
- -
-
-
-
Password minimum length
-
- -
-
-
-
Website Name
-
- -
-
-
-
Custom head
-
- -
-
-
-
-
+ + <%- include("partials/document-head.ejs") %> + + + + + + <%- include("partials/header.ejs") %> +
+
+
<%= website_name %> Admin Settings
+
+
+
User registration
+
+ +
+
+
+
Hide "Login" in navigation bar
+
+ +
+
+
+
Serve ATOM feed
+
+ +
+
+
+
Serve JSON feed
+
+ +
+
+
+
Password minimum length
+
+ +
+
+
+
Website Name
+
+ +
+
+
+
Custom head
+
+ +
+
+
+
+
- <%- include("partials/footer.ejs") %> - + <%- include("partials/footer.ejs") %> + diff --git a/frontend/views/themes/default/js/admin.js b/frontend/views/themes/default/js/admin.js index 8acbd17..577dc31 100644 --- a/frontend/views/themes/default/js/admin.js +++ b/frontend/views/themes/default/js/admin.js @@ -1,24 +1,24 @@ async function toggleState(setting_name, element) { - console.log(element.checked); - const form = { - setting_name: setting_name, - value: element.checked, - }; - const response = await request("/setting", "POST", form); + console.log(element.checked); + const form = { + setting_name: setting_name, + value: element.checked, + }; + const response = await request("/setting", "POST", form); - // TODO: On failure, notify the user - if (response.body.success) { - } + // TODO: On failure, notify the user + if (response.body.success) { + } } async function changeValue(setting_name, element) { - const form = { - setting_name: setting_name, - value: element.value, - }; - const response = await request("/setting", "POST", form); + const form = { + setting_name: setting_name, + value: element.value, + }; + const response = await request("/setting", "POST", form); - // TODO: On failure, notify the user - if (response.body.success) { - } + // TODO: On failure, notify the user + if (response.body.success) { + } } diff --git a/yab.js b/yab.js index 296e3dc..780cb67 100644 --- a/yab.js +++ b/yab.js @@ -20,11 +20,11 @@ const refreshTheme = (theme_name) => app.use(express.static(path.join(__dirname, refreshTheme("default"); app.use( - session({ - secret: require("crypto").randomBytes(128).toString("base64"), - resave: false, - saveUninitialized: false, - }) + session({ + secret: require("crypto").randomBytes(128).toString("base64"), + resave: false, + saveUninitialized: false, + }) ); // API @@ -55,13 +55,13 @@ app.get("/atom", page_scripts.atom); app.get("/json", page_scripts.jsonFeed); function checkAuthenticated(req, res, next) { - if (req.session.user) return next(); - res.redirect("/login"); + if (req.session.user) return next(); + res.redirect("/login"); } function checkNotAuthenticated(req, res, next) { - if (req.session.user) return res.redirect("/"); - next(); + if (req.session.user) return res.redirect("/"); + next(); } app.listen(5004);