frontend/src/routes/community/create-post/+page.server.ts
2024-09-19 14:36:02 +02:00

365 lines
11 KiB
TypeScript

import {
createPostTextSchema,
createPostImageSchema,
createPostLinkSchema,
} from "$lib/schemas";
import { validateData } from "$lib/utils";
import { error, fail, redirect } from "@sveltejs/kit";
import { serialize } from "object-to-formdata";
import { marked } from "marked";
import got from "got";
import cheerio from "cheerio";
import * as blobUtil from "blob-util"; // Changed import
import sharp from "sharp";
function removeDuplicateClasses(str) {
return str.replace(
/class="([^"]*)"/,
(match, classAttr) =>
`class="${[...new Set(classAttr.split(" "))].join(" ")}"`
);
}
function addClassesToHtml(htmlString) {
// Helper function to add a class to a specific tag
function addClassToTag(tag, className) {
// Add class if the tag doesn't already have a class attribute
const regex = new RegExp(`<${tag}(?![^>]*\\bclass=)([^>]*)>`, "g");
htmlString = htmlString.replace(regex, `<${tag} class="${className}"$1>`);
// Append the new class to tags that already have a class attribute, ensuring no duplicates
const regexWithClass = new RegExp(
`(<${tag}[^>]*\\bclass=["'][^"']*)(?!.*\\b${className}\\b)([^"']*)["']`,
"g"
);
htmlString = htmlString.replace(regexWithClass, `$1 ${className}$2"`);
}
// Add classes to headings
addClassToTag("h1", "text-lg");
addClassToTag("h2", "text-lg");
addClassToTag("h3", "text-lg");
addClassToTag("h4", "text-lg");
addClassToTag("h5", "text-lg");
addClassToTag("h6", "text-lg");
// Add classes to anchor tags
addClassToTag("a", "text-blue-400 hover:text-white underline");
// Add classes to ordered lists
addClassToTag("ol", "list-decimal ml-10 text-sm");
// Add classes to unordered lists
addClassToTag("ul", "list-disc ml-10 text-sm -mt-5");
// Add classes to blockquotes and their paragraphs
function addClassToBlockquote() {
// Add class to blockquote
htmlString = htmlString.replace(
/<blockquote/g,
'<blockquote class="pl-4 pr-4 rounded-lg bg-[#323232]"'
);
// Add class to p inside blockquote
htmlString = htmlString.replace(
/<blockquote([^>]*)>\s*<p/g,
`<blockquote$1>\n<p class="text-sm font-medium leading-relaxed text-white"`
);
}
addClassToBlockquote();
// Remove duplicate classes after all modifications
htmlString = removeDuplicateClasses(htmlString);
return htmlString;
}
export const load = async ({ locals }) => {
if (!locals.pb.authStore.isValid) {
redirect(303, "/login");
}
};
export const actions = {
createPostText: async ({ request, locals }) => {
let newPost = "";
const body = await request.formData();
body.delete("thumbnail");
body.append("user", locals?.user?.id);
let { formData, errors } = await validateData(body, createPostTextSchema);
formData.description = addClassesToHtml(marked(formData?.description));
formData.tagTopic = JSON.parse(formData.tagTopic)[0];
formData.upvote = 1;
if (errors) {
return fail(400, {
data: formData,
errors: errors.fieldErrors,
});
}
//Each post gives the user +1 Karma points
await locals.pb.collection("users").update(locals.user.id, {
"karma+": 1,
});
try {
newPost = await locals.pb.collection("posts").create(serialize(formData));
// add the tagTopic manually because serialize does not work on arrays
await locals?.pb.collection("posts").update(newPost.id, {
tagTopic: formData.tagTopic,
});
//Save it collection alreadyVoted to keep track if user voted or not
//FormData for alreadyVoted
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append("post", newPost?.id);
formDataAlreadyVoted.append("user", newPost?.user);
formDataAlreadyVoted.append("type", "upvote");
//console.log(formDataAlreadyVoted)
await locals.pb.collection("alreadyVoted").create(formDataAlreadyVoted);
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
if (newPost?.id?.length !== 0) {
redirect(303, "/community/post/" + newPost?.id);
} else {
redirect(303, "/community");
}
},
createPostImage: async ({ request, locals }) => {
let newPost = "";
const body = await request.formData();
const thumb = body.get("thumbnail");
if (thumb?.size === 0) {
body.delete("thumbnail");
}
body.append("user", locals?.user?.id);
let { formData, errors } = await validateData(body, createPostImageSchema);
formData.tagTopic = JSON.parse(formData.tagTopic)[0];
formData.upvote = 1;
if (errors) {
return fail(400, {
data: formData,
errors: errors.fieldErrors,
});
}
//Each post gives the user +1 Karma points
await locals.pb.collection("users").update(locals?.user?.id, {
"karma+": 1,
});
if (formData.thumbnail.type.includes("image")) {
try {
const image = formData.thumbnail;
const imageBuffer = await image.arrayBuffer();
const imageBufferArray = new Uint8Array(imageBuffer);
let optimizedImageBuffer;
if (image.type === "image/gif") {
// Process GIF files differently
optimizedImageBuffer = imageBufferArray;
} else {
// Process other image formats (e.g., JPEG, PNG) using sharp library
optimizedImageBuffer = await sharp(imageBufferArray)
.resize({
width: 800,
height: 1000,
fit: sharp.fit.inside,
withoutEnlargement: true,
})
.jpeg({ quality: 80 })
.toBuffer();
}
formData.thumbnail = new File([optimizedImageBuffer], image.name, {
type: image.type,
lastModified: image.lastModified,
});
} catch (err) {
console.log("Error:", err);
error(err.status, err.message);
}
}
try {
newPost = await locals.pb.collection("posts").create(serialize(formData));
// add the tagTopic manually because serialize does not work on arrays
await locals?.pb.collection("posts").update(newPost.id, {
tagTopic: formData?.tagTopic,
});
//Save it collection alreadyVoted to keep track if user voted or not
//FormData for alreadyVoted
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append("post", newPost.id);
formDataAlreadyVoted.append("user", newPost.user);
formDataAlreadyVoted.append("type", "upvote");
//console.log(formDataAlreadyVoted)
await locals.pb.collection("alreadyVoted").create(formDataAlreadyVoted);
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
if (newPost?.id?.length !== 0) {
redirect(303, "/community/post/" + newPost?.id);
} else {
redirect(303, "/community");
}
},
createPostLink: async ({ request, locals }) => {
let newPost = "";
const body = await request.formData();
const url = body.get("link");
let image;
let description;
let imageBlob;
try {
const response = await got(url, {
headers: {
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
},
responseType: "buffer",
});
const $ = cheerio.load(response.body);
//const title = $('head title').text();
description = $('head meta[property="og:description"]').attr("content");
image = $('head meta[property="og:image"]').attr("content");
if (!image) {
let largestSize = 0;
let largestImage = "";
$("img").each(async function () {
if (
$(this).attr("src") &&
$(this)
.attr("src")
.match(/\.(webp|jpg|jpeg|png|gif)$/)
) {
try {
const imageBuffer = await got(image, {
headers: {
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
},
responseType: "buffer",
}).then((response) => response.body);
imageBlob = await blobUtil.createBlob([imageBuffer], {
// Changed usage
type: "image/jpeg",
});
} catch (error) {
// Handle the error when getting the image
console.error("Error getting image:", error);
}
}
});
image = largestImage;
}
// Download the image and append it to the form data
const imageBuffer = await got(image, {
headers: {
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
},
responseType: "buffer",
}).then((response) => response.body);
imageBlob = await blobUtil.createBlob([imageBuffer], {
// Changed usage
type: "image/jpeg",
});
} catch (e) {
console.log(e);
}
//body.delete('thumbnail')
//body.append('thumbnail', imageBlob);
// Append the title to the form data
//body.append('name', title);
body.append("user", locals.user.id);
body.append("description", description);
/*
const thumb = body.get('thumbnail');
if (thumb.size === 0) {
body.delete('thumbnail');
}
*/
let { formData, errors } = await validateData(body, createPostLinkSchema);
formData.tagTopic = JSON.parse(formData.tagTopic)[0];
formData.upvote = 1;
if (imageBlob?.length !== 0) {
formData.thumbnail = imageBlob;
}
if (errors) {
return fail(400, {
data: formData,
errors: errors.fieldErrors,
});
}
//Each post gives the user +1 Karma points
await locals.pb.collection("users").update(locals?.user?.id, {
"karma+": 1,
});
try {
newPost = await locals.pb.collection("posts").create(serialize(formData));
// add the tagTopic manually because serialize does not work on arrays
await locals.pb.collection("posts").update(newPost.id, {
tagTopic: formData.tagTopic,
});
//Save it collection alreadyVoted to keep track if user voted or not
//FormData for alreadyVoted
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append("post", newPost.id);
formDataAlreadyVoted.append("user", newPost.user);
formDataAlreadyVoted.append("type", "upvote");
//console.log(formDataAlreadyVoted)
await locals.pb.collection("alreadyVoted").create(formDataAlreadyVoted);
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
if (newPost?.id?.length !== 0) {
redirect(303, "/community/post/" + newPost?.id);
} else {
redirect(303, "/community");
}
},
};