From 7f851dd87f662b3afdb1790628ce8018fb5b8925 Mon Sep 17 00:00:00 2001 From: hoseacodes Date: Thu, 11 Apr 2024 12:18:02 -0500 Subject: [PATCH] fix(*): update blog for publishing --- .env example | 11 + controllers/article.js | 457 ++++++++++++++----------- src/Pages/Articles/CreateArticle.jsx | 133 +++---- src/Pages/Projects/Project/Project.jsx | 4 +- utils/logger.js | 92 ++--- 5 files changed, 371 insertions(+), 326 deletions(-) create mode 100644 .env example diff --git a/.env example b/.env example new file mode 100644 index 00000000..6fd9de5b --- /dev/null +++ b/.env example @@ -0,0 +1,11 @@ +SKIP_PREFLIGHT_CHECK= +MONGODB_URL= +FOREMAPI= +MEDIUMAPI= +MEDIUMUSER= +ACCESS_TOKEN_SECRET= +REFRESH_TOKEN_SECRET= +CLOUD_API_KEY= +CLOUD_API_SECRET= +CLOUND_NAME= +REACT_APP_VITE_Open_AI_Key= \ No newline at end of file diff --git a/controllers/article.js b/controllers/article.js index 70e5f2ea..971fa080 100755 --- a/controllers/article.js +++ b/controllers/article.js @@ -1,187 +1,233 @@ -import Articles from '../models/article.js'; -import Comments from '../models/comment.js'; -import Logger from '../utils/logger.js'; -import {cache} from '../utils/cache.js'; -import axios from 'axios' +import Articles from "../models/article.js"; +import Comments from "../models/comment.js"; +import Logger from "../utils/logger.js"; +import { cache } from "../utils/cache.js"; +import axios from "axios"; -const logger = new Logger('articles') +const logger = new Logger("articles"); async function getArticle(req, res) { - try { - const articles = await Articles.find() - - logger.info("Returning the list of articles"); - - res.cookie('articles-cache', articles.length + "articles", { - maxAge: 1000 * 60 * 60, // would expire after an hour - httpOnly: true, // The cookie only accessible by the web server - }) - - cache.set( articles.length + "articles", { - status: 'success', - articles: articles, - result: articles.length, - location: 'cache', - }); - - res.json({ - status: 'success', - articles: articles, - result: articles.length, - location: 'main', - - }) - } catch (err) { - - logger.error(err); + try { + const articles = await Articles.find(); + + logger.info("Returning the list of articles"); + + res.cookie("articles-cache", articles.length + "articles", { + maxAge: 1000 * 60 * 60, // would expire after an hour + httpOnly: true, // The cookie only accessible by the web server + }); + + cache.set(articles.length + "articles", { + status: "success", + articles: articles, + result: articles.length, + location: "cache", + }); + + res.json({ + status: "success", + articles: articles, + result: articles.length, + location: "main", + }); + } catch (err) { + logger.error(err); - return res.status(500).json({ msg: err.message }) - } + return res.status(500).json({ msg: err.message }); + } } async function getArticleByID(req, res) { - try { - const article = await Articles.findOne({ _id: req.params.id }) - - logger.info("Returning the list of articles"); + try { + const article = await Articles.findOne({ _id: req.params.id }); - if(!article) return res.status(400).send({ msg: 'Article does not exisit'}) + logger.info("Returning the list of articles"); - res.json({ - status: 'success', - article: article, - }) - } catch (err) { + if (!article) + return res.status(400).send({ msg: "Article does not exisit" }); - logger.error(err); + res.json({ + status: "success", + article: article, + }); + } catch (err) { + logger.error(err); - return res.status(500).json({ msg: err.message }) - } + return res.status(500).json({ msg: err.message }); + } } async function createArticle(req, res) { - try { + try { + const { + article_id, + title, + subtitle, + markdown, + description, + images, + categories, + dev, + medium, + postedBy, + series, + } = req.body; + + switch (req.body) { + case !article_id: + logger.error("No article id provided."); + return res.status(400).json({ msg: "No article id provided." }); + case !title: + logger.error("No title provided."); + return res.status(400).json({ msg: "No title provided." }); + case !subtitle: + logger.error("No subtitle provided."); + return res.status(400).json({ msg: "No subtitle provided." }); + case !markdown: + logger.error("No markdown provided."); + return res.status(400).json({ msg: "No markdown provided." }); + case !description: + logger.error("No description provided."); + return res.status(400).json({ msg: "No description provided." }); + case !categories: + logger.error("No categories provided."); + return res.status(400).json({ msg: "No categories provided." }); + case !postedBy: + logger.error("No postedBy provided."); + return res.status(400).json({ msg: "No postedBy provided." }); + default: + break; + } - const { article_id, title, subtitle, markdown, description, images, categories, dev, medium, postedBy, series } = req.body; + if (!images) { + logger.error("No image provided."); + return res.status(400).json({ msg: "No image upload" }); + } - if (!images) { - logger.error("No image provided."); - return res.status(400).json({ msg: "No image upload" }); - } + const article = await Articles.find({ article_id }); + if (article.length > 0) { + logger.error("Article already exist."); + return res.status(400).json({ msg: "This article already exists." }); + } - const article = await Articles.find({ article_id }); - if (article.length > 0) { - logger.error("Article already exist."); - return res.status(400).json({ msg: "This article already exists." }) + const newArticle = new Articles({ + article_id, + title, + subtitle, + markdown, + description, + images, + postedBy, + tags: ["api", "hoseacodes"], + categories, + slug: title.toLowerCase().replace(/ /g, "-"), + }); + + try { + if (dev) { + if (!title || !markdown || !series) { + logger.error("No title, markdown or series provided."); + return res + .status(400) + .json({ msg: "No title, markdown or series provided." }); } - - const newArticle = new Articles({ - article_id, - title, - subtitle, - markdown, - description, - images, - postedBy, - tags: ["api", "hoseacodes"], - categories, - slug: title.toLowerCase().replace(/ /g, "-"), - }); - - try { - if (dev) { - await axios.post('https://dev.to/api/articles', - { - "article": { - "title": title, - "published": false, - "body_markdown": markdown, - "tags": ["api", "hoseacodes"], - "series": series - } - }, { - headers: { "api-key": process.env.FOREMAPI }, - } - ) - logger.info('Published to Dev To') - } - - if (medium) { - const userId = process.env.MEDIUMAPI - await axios.post( - `https://api.medium.com/v1/users/${process.env.MEDIUMUSER}/posts`, - { - title: title, - contentFormat: "markdown", - content: markdown, - canonicalUrl: images, - tags: ["api", "hoseacodes"], - publishStatus: "public", - notifyFollowers: true, - }, - { - headers: { Authorization: `Bearer ${process.env.MEDIUMAPI}` }, - } - ); - logger.info('Published to Medium') + if (!process.env.FOREMAPI) { + logger.error("No dev api provided."); + return res.status(400).json({ msg: "No dev api provided." }); + } + await axios.post( + "https://dev.to/api/articles", + { + article: { + title: title, + published: false, + body_markdown: markdown, + tags: ["api", "hoseacodes"], + series: series, + }, + }, + { + headers: { "api-key": process.env.FOREMAPI }, } + ); + logger.info("Published to Dev To"); + } - } catch (error) { - logger.error(`Error: ${error}`); + if (medium) { + if (!series) { + logger.error("No series provided."); + return res.status(400).json({ msg: "No series upload" }); + } + if (!process.env.MEDIUMUSER || !process.env.MEDIUMAPI) { + logger.error("No medium user or api provided."); return res - .status(error.response.status) - .json({ - code: error.response.statusText, - msg: error.response.data, - }); + .status(400) + .json({ msg: "No medium user or api provided." }); } - + await axios.post( + `https://api.medium.com/v1/users/${process.env.MEDIUMUSER}/posts`, + { + title: title, + contentFormat: "markdown", + content: markdown, + canonicalUrl: images.secure_url, + tags: ["api", "hoseacodes"], + publishStatus: "public", + notifyFollowers: true, + }, + { + headers: { Authorization: `Bearer ${process.env.MEDIUMAPI}` }, + } + ); + logger.info("Published to Medium"); + } + } catch (error) { + logger.error(error); + return res.status(error.response.status).json({ + code: error.response.statusText, + msg: error.response.data, + }); + } - res.clearCookie('artilces-cache'); - await newArticle.save() + res.clearCookie("artilces-cache"); + await newArticle.save(); - logger.info(`New article ${title} has been created`); + logger.info(`New article ${title} has been created`); - res.json({ msg: "Created a new article" }); - } catch (err) { - console.log(err, "error"); - logger.error(err) - return res.status(500).json({ msg: err.message }) - } + res.json({ msg: "Created a new article" }); + } catch (err) { + logger.error(err); + return res.status(500).json({ msg: err.message }); + } } async function deleteArticle(req, res) { - try { - - logger.info(`Deleted article ${req.params.id} has been deleted`); - - await Articles.findByIdAndDelete(req.params.id) - res.clearCookie('articles-cache'); - res.json({ msg: "Deleted a article" }) - } catch (err) { + try { + logger.info(`Deleted article ${req.params.id} has been deleted`); - logger.error(err) + await Articles.findByIdAndDelete(req.params.id); + res.clearCookie("articles-cache"); + res.json({ msg: "Deleted a article" }); + } catch (err) { + logger.error(err); - return res.status(500).json({ msg: err.message }) - } + return res.status(500).json({ msg: err.message }); + } } - + async function updateLikes(req, res) { try { - - const post_id = req.params.id + const post_id = req.params.id; let { likes } = req.body; likes += 1; - await Articles.findOneAndUpdate({ _id: post_id }, - {likes}); - - res.json({ + await Articles.findOneAndUpdate({ _id: post_id }, { likes }); + + res.json({ msg: `${post_id} received a new like`, - totalLikes: likes - }) + totalLikes: likes, + }); } catch (err) { - console.log(err) logger.error(err); return res.status(500).json({ msg: err.message }); @@ -189,56 +235,71 @@ async function updateLikes(req, res) { } async function updateArticleComment(req, res) { - try { - const { article_id, post_id, comment, markdown, user_id, date_created } = req.body; - - const originalBody = req.body - - await Comments.findOneAndUpdate({ _id: req.params.id }, { - article_id, post_id, comment, user_id, markdown, date_created - }) - - const preparedLog = `Changing the following: ${originalBody} to ${req.body} for the comment ${title}`; - res.clearCookie('comments-cache'); - logger.info(preparedLog); + try { + const { article_id, post_id, comment, markdown, user_id, date_created } = + req.body; + + const originalBody = req.body; + + await Comments.findOneAndUpdate( + { _id: req.params.id }, + { + article_id, + post_id, + comment, + user_id, + markdown, + date_created, + } + ); - res.json({ msg: 'Updated a comment' }) - } catch (err) { + const preparedLog = `Changing the following: ${originalBody} to ${req.body} for the comment ${title}`; + res.clearCookie("comments-cache"); + logger.info(preparedLog); - logger.error(err); + res.json({ msg: "Updated a comment" }); + } catch (err) { + logger.error(err); - return res.status(500).json({ msg: err.message }); - } + return res.status(500).json({ msg: err.message }); + } } async function updateArticle(req, res) { try { - const { title, subtitle, description, content, images, category } = req.body; - res.clearCookie('articles-cache'); - - if (!images) { + const { title, subtitle, description, content, images, category } = + req.body; + res.clearCookie("articles-cache"); + + if (!images) { + logger.error("No image provided."); + res.clearCookie("user-cache"); + return res.status(400).json({ msg: "No image upload" }); + } - logger.error("No image provided."); - res.clearCookie('user-cache'); - return res.status(400).json({ msg: "No image upload" }) + const originalBody = req.body; + + await Articles.findOneAndUpdate( + { _id: req.params.id }, + { + title: title.toLowerCase(), + subtitle, + description, + content, + images, + category, } + ); - const originalBody = req.body - - await Articles.findOneAndUpdate({ _id: req.params.id }, { - title: title.toLowerCase(), subtitle, description, content, images, category - }) + const preparedLog = `Changing the following: ${originalBody} to ${req.body} for the article ${title}`; - const preparedLog = `Changing the following: ${originalBody} to ${req.body} for the article ${title}`; + logger.info(preparedLog); - logger.info(preparedLog); - - res.json({ msg: 'Updated a article' }) + res.json({ msg: "Updated a article" }); } catch (err) { + logger.error(err); - logger.error(err); - - return res.status(500).json({ msg: err.message }); + return res.status(500).json({ msg: err.message }); } } @@ -246,27 +307,31 @@ async function conditionalArticle(req, res) { try { const { archive, draft } = req.body; if (archive) { - await Articles.findOneAndUpdate({ _id: req.params.id }, { - archived: archive - }) - logger.info('Updated archive'); - res.json({ msg: `Moved ${req.params.id} to archive`}) + await Articles.findOneAndUpdate( + { _id: req.params.id }, + { + archived: archive, + } + ); + logger.info("Updated archive"); + res.json({ msg: `Moved ${req.params.id} to archive` }); } else if (draft) { - await Articles.findOneAndUpdate({ _id: req.params.id }, { - draft: draft - }) - logger.info('Updated draft'); - res.json({ msg: `Moved ${req.params.id} to archive`}) + await Articles.findOneAndUpdate( + { _id: req.params.id }, + { + draft: draft, + } + ); + logger.info("Updated draft"); + res.json({ msg: `Moved ${req.params.id} to archive` }); } } catch (err) { - logger.error(err); - return res.status(500).json({msg: err.message}); + return res.status(500).json({ msg: err.message }); } } - export { getArticle, getArticleByID, @@ -276,4 +341,4 @@ export { updateArticle, updateArticleComment, updateLikes, - }; +}; diff --git a/src/Pages/Articles/CreateArticle.jsx b/src/Pages/Articles/CreateArticle.jsx index a4a954c3..f8c338ad 100755 --- a/src/Pages/Articles/CreateArticle.jsx +++ b/src/Pages/Articles/CreateArticle.jsx @@ -11,22 +11,27 @@ import { articleTempltes } from "./ArticleTemplate"; import Preview from "../../Components/Article/Preview"; import AITemplate from "../../Components/OpenAI/AITemplate"; import { Button } from "../../Components/Button/Button"; +import { sleep } from "../../Utils/helperFunctions"; function CreatArticle() { const [markdown, setMarkdown] = useState(articleTempltes[3].markdown); const initialState = { - article_id: "", + article_id: uuidv4(), title: "", subtitle: "", description: "Description", markdown: markdown, - category: "", - id: "" + categories: "", + id: "", + series: "Hoseacodes", + dev: false, + medium: false, + archived: false, + draft: false, }; const state = useContext(GlobalState); const [article, setArticle] = useState(initialState); const [images, setImages] = useState(false); - // const [input, setInput] = useState('') const [isMobileView, setIsMobileView] = useState(false); const [loading, setLoading] = useState(false); const history = useHistory(); @@ -39,41 +44,17 @@ function CreatArticle() { const [show, setShow] = useState(false); const [showAITemplate, setShowAITemplate] = useState(false); const loggedIn = localStorage.getItem("isLoggedIn"); - const [selectedCategory, setselectedCategory] = useState( - localStorage.getItem("category") === null - ? "Programming" - : localStorage.getItem("category") - ); - const [allBlogCategory, setAllBlogCategory] = useState([ - { name: "Programming" }, - { name: "Software Engineering" }, - { name: "Python" }, - { name: "Java" }, - { name: "JavaScript" }, - { name: "Tech" } - ]); - - useEffect(() => { - localStorage.setItem("category", selectedCategory); - }, [selectedCategory]); + const [selectedCategory, setselectedCategory] = useState("Programming"); + const [allBlogCategory, setAllBlogCategory] = useState([]); useEffect(async () => { const config = { headers: { "Content-Type": "application/json" } }; const res = await axios.get("/api/category", config); if (res) { - setAllBlogCategory(res.data.category); + setAllBlogCategory(res.data.categories); } }, []); - function sleep(num) { - let now = new Date(); - const stop = now.getTime() + num; - while (true) { - now = new Date(); - if (now.getTime() > stop) return; - } - } - function handleClick(e) { e.preventDefault(); window.location.href = "/blog"; @@ -90,7 +71,7 @@ function CreatArticle() { if (param.id) { setOnEdit(true); if (articles !== undefined) { - articles.forEach(article => { + articles.forEach((article) => { if (article._id === param.id) { setArticle(article); // setImages(article.images) @@ -105,10 +86,10 @@ function CreatArticle() { }, [param.id, articles]); const styleUpload = { - display: images ? "block" : "none" + display: images ? "block" : "none", }; - - const handleUpload = async e => { + + const handleUpload = async (e) => { e.preventDefault(); try { const file = e.target.files[0]; @@ -123,7 +104,7 @@ function CreatArticle() { setLoading(true); const res = await axios.post("/api/upload", formData, { - headers: { "content-type": "multipart/form-data" } + headers: { "content-type": "multipart/form-data" }, }); setLoading(false); setImages(res.data.result); @@ -144,29 +125,32 @@ function CreatArticle() { } }; - const handleChangeInput = e => { + const handleChangeInput = (e) => { const { name, value } = e.target; - setArticle({ ...article, [name]: value }); + if (name === "tags") { + setArticle({ ...article, [name]: [value] }); + } else { + setArticle({ ...article, [name]: value }); + } }; - const handleSubmit = async e => { + const handleSubmit = async (e) => { e.preventDefault(); try { if (!images) return alert("No Image Upload"); if (onEdit) { await axios.put(`/api/articles/${article._id}`, { ...articles, - images + images, }); } else { setArticle({ ...article, ["article_id"]: uuidv4(), - category: selectedCategory, - id: user.id + categories: [selectedCategory], + id: user.id, }); - console.log(article); - await axios.post("/api/articles", { ...article, images, ...user}); + await axios.post("/api/articles", { ...article, images, ...user }); setImages(false); setArticle(initialState); } @@ -178,35 +162,20 @@ function CreatArticle() { } }; - const updateMarkdown = e => { + const updateMarkdown = (e) => { const { name } = e.target; setMarkdown(articleTempltes[e.target.options.selectedIndex].markdown); setArticle({ ...article, - [name]: articleTempltes[e.target.options.selectedIndex].markdown + [name]: articleTempltes[e.target.options.selectedIndex].markdown, }); }; - const handlePublish = e => { + const handlePublish = (e) => { const { name, checked } = e.target; setArticle({ ...article, [name]: checked }); }; -// useEffect(() => { -// const onresize = () => { -// // if (window.screen.width > 600) { -// // setIsMobileView(false); -// // setDesktopView(true); -// // } -// }; -// window.addEventListener("resize", onresize); -// onresize(); - -// return () => { -// window.removeEventListener("resize", onresize); -// }; -// }, []); - return ( <> {loggedIn ? ( @@ -229,7 +198,6 @@ function CreatArticle() { > Title* - {/*
*/} - {/*
*/}
@@ -277,9 +244,9 @@ function CreatArticle() {
@@ -301,10 +268,12 @@ function CreatArticle() { className="form-control mb" style={{ height: "auto" }} > - - - - + + + + @@ -318,7 +287,7 @@ function CreatArticle() {
diff --git a/src/Pages/Projects/Project/Project.jsx b/src/Pages/Projects/Project/Project.jsx index ee90fdfc..b86e7249 100755 --- a/src/Pages/Projects/Project/Project.jsx +++ b/src/Pages/Projects/Project/Project.jsx @@ -3,7 +3,7 @@ import { useParams } from "react-router-dom"; import { StyledHr } from "../../../Layout/Hr/styledHr"; import { projectData } from "../ProjectsData"; import "./Project.css"; -import Carousel from "../../../Components/Carousel/Carousel"; +// import Carousel from "../../../Components/Carousel/Carousel"; import AnimatedImage from "../../../Components/Animation/Image/AnimatedImage"; import AnimatedTextCharacter from "../../../Components/Animation/Text/AnimatedTextCharacter"; import AnimatedTextSlide from "../../../Components/Animation/Text/AnimatedTextSlide"; @@ -598,7 +598,7 @@ const ProjectItem = () => { data-aos-duration="1500" data-aos-easing="ease-in" > - + {/* */}
) : ( { -return new Date(Date.now()).toUTCString(); + return new Date(Date.now()).toUTCString(); }; //Here we create our Custom Logger class class CustomLogger { + //We want to attach the service route to each instance, so when we call it from our services it gets attached to the message + constructor(service) { + this.log_data = null; + this.service = service; - //We want to attach the service route to each instance, so when we call it from our services it gets attached to the message - constructor(service) { - this.log_data = null; - this.service = service; - - const logger = winston.createLogger({ - transports: [ - //Here we declare the winston transport, and assign it to our file: allLogs.log - new winston.transports.File({ - filename: `./logs/allLogs.log`, + const logger = winston.createLogger({ + transports: [ + //Here we declare the winston transport, and assign it to our file: allLogs.log + new winston.transports.File({ + filename: `./logs/allLogs.log`, }), - ], - - format: winston.format.printf((info) => { + ], - //Here is our custom message - let message = `${timeStamp()} | ${info.level} |  ${info.message} | From: ${service} controller `; + format: winston.format.printf((info) => { + //Here is our custom message + let message = `${timeStamp()} | ${info.level} |  ${ + info.message + } | From: ${service} controller `; - return message; - }), -}); + return message; + }), + }); this.logger = logger; - } + } - setLogData(log_data) { + setLogData(log_data) { this.log_data = log_data; - } + } - async info(message) { - this.logger.log("info", message); - } + async info(message) { + this.logger.log("info", message); + } - async info(message, obj) { - this.logger.log("info", message, { - obj, - }); - } + async info(message, obj) { + this.logger.log("info", message, { + obj, + }); + } - async debug(message) { - this.logger.log("debug", message); - } + async debug(message) { + this.logger.log("debug", message); + } - async debug(message, obj) { - this.logger.log("debug", message, { - obj, - }); - } + async debug(message, obj) { + this.logger.log("debug", message, { + obj, + }); + } - async error(message) { - this.logger.log("error", message); - } - async error(message, obj) { - this.logger.log("error", message, { - obj, - }); - } + async error(message) { + this.logger.log("error", message); + } + async error(message, obj) { + this.logger.log("error", message, { + obj, + }); + } } export default CustomLogger;