<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: IndyMan</title>
    <description>The latest articles on DEV Community by IndyMan (@indyman).</description>
    <link>https://dev.to/indyman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1693860%2F7cbec494-add3-4fb4-8c39-0feef90b92fc.jpg</url>
      <title>DEV Community: IndyMan</title>
      <link>https://dev.to/indyman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/indyman"/>
    <language>en</language>
    <item>
      <title>How to easily fix OpenSSH RegreSSHion vulnerability</title>
      <dc:creator>IndyMan</dc:creator>
      <pubDate>Wed, 03 Jul 2024 18:48:21 +0000</pubDate>
      <link>https://dev.to/indyman/how-to-easily-fix-openssh-regresshion-vulnerability-216b</link>
      <guid>https://dev.to/indyman/how-to-easily-fix-openssh-regresshion-vulnerability-216b</guid>
      <description>&lt;p&gt;The &lt;code&gt;regreSSHion&lt;/code&gt; vulnerability &lt;code&gt;CVE-2024-6387&lt;/code&gt; is a critical remote unauthenticated code execution (RCE) vulnerability affecting OpenSSH server (sshd) on glibc-based Linux systems. If exploited, this vulnerability can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete system takeover&lt;/li&gt;
&lt;li&gt;Installation of malware&lt;/li&gt;
&lt;li&gt;Data manipulation and exfiltration&lt;/li&gt;
&lt;li&gt;Creation of backdoors for persistent access&lt;/li&gt;
&lt;li&gt;Network propagation to other systems within the organization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exploiting this vulnerability allows attackers to bypass critical security mechanisms and cause significant damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution for Ubuntu
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check your Ubuntu version
&lt;/h3&gt;

&lt;p&gt;To check your Ubuntu version, run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsb_release &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will display information about your Ubuntu distribution, including the release name.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to fix the vulnerability
&lt;/h3&gt;

&lt;p&gt;To fix the &lt;code&gt;regreSSHion&lt;/code&gt; vulnerability on your Ubuntu server, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update the package list and install available updates:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ensure you are running the latest version of OpenSSH for your release:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--only-upgrade&lt;/span&gt; openssh-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 If you like this article, you can &lt;a href="https://x.com/_indyman"&gt;follow me on Twitter&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if the fix is installed
&lt;/h3&gt;

&lt;p&gt;Ensure the version at least matches the patched version for your Ubuntu release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dpkg &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;openssh-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Jammy: &lt;code&gt;1:8.9p1-3ubuntu0.10&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mantic: &lt;code&gt;1:9.3p1-1ubuntu3.6&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Noble: &lt;code&gt;1:9.6p1-3ubuntu13.3&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Protect your server with automatic updates
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Unattended Upgrades&lt;/strong&gt; is a package on Ubuntu that allows automatic installation of security updates and critical packages without user intervention. This can help ensure that your system is always up-to-date with the latest security patches, including the fix for vulnerabilities like &lt;code&gt;regreSSHion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you had unattended upgrades configured on your Ubuntu system, it would have automatically applied the security update for OpenSSH as soon as it was available, thereby mitigating the vulnerability without requiring manual intervention.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Set Up Unattended Upgrades
&lt;/h3&gt;

&lt;p&gt;Follow these steps to set up unattended upgrades on your Ubuntu system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install Unattended Upgrades:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unattended-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable Unattended Upgrades:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg-reconfigure &lt;span class="nt"&gt;--priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;medium unattended-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking If Unattended Upgrades Is Working
&lt;/h3&gt;

&lt;p&gt;To verify that unattended upgrades are functioning correctly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the Status of the Service:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status unattended-upgrades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Review the Log Files:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Review the logs to see if updates have been applied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/unattended-upgrades/unattended-upgrades.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting up unattended upgrades, you can ensure that critical security updates, like those for the &lt;code&gt;regreSSHion&lt;/code&gt; vulnerability, are applied automatically, enhancing the security of your Ubuntu server without manual intervention.&lt;/p&gt;

&lt;p&gt;🎯 Find my next blog articles earlier on &lt;a href="https://easyselfhost.dev/blog"&gt;https://easyselfhost.dev/blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ubuntu</category>
      <category>devops</category>
      <category>news</category>
    </item>
    <item>
      <title>Umami, simple self-hosted analytics</title>
      <dc:creator>IndyMan</dc:creator>
      <pubDate>Tue, 02 Jul 2024 07:11:33 +0000</pubDate>
      <link>https://dev.to/indyman/umami-simple-self-hosted-analytics-4pk8</link>
      <guid>https://dev.to/indyman/umami-simple-self-hosted-analytics-4pk8</guid>
      <description>&lt;p&gt;I was looking for a simple analytics solution because I wasn't satisfied with Google Analytics' footprint on page performance and overall heaviness. After trying various options, I have been using Umami for a few weeks, and I'm very happy with it! Let's see why.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Umami?
&lt;/h2&gt;

&lt;p&gt;Umami is an open-source, lightweight, self-hosted alternative to Google Analytics that respects your users' privacy. It is focused on giving you the essential tools you need to track your website traffic, where users are coming from, and what they are doing on your site.&lt;/p&gt;

&lt;p&gt;If you're looking for a very complete suite of features, Umami might not be for you, but I wanted a simple, privacy-focused analytics tool, which is why I chose Umami.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Love Umami ❤️
&lt;/h2&gt;

&lt;p&gt;I initially went with Google Analytics, but its SDK was heavy and sent a lot of data to Google. Then, I tried Plausible, which is self-hostable and great, but it was too CPU-heavy for my VPS (using around 30% CPU!). Finally, I found Umami, which is just what I needed! Lightweight for both my VPS and website, easy to use, privacy-focused, and open-source.&lt;/p&gt;

&lt;p&gt;👉 Want to see more ? &lt;a href="https://x.com/_indyman"&gt;Follow me on Twitter&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install
&lt;/h2&gt;

&lt;p&gt;Umami's installation process is straightforward, and their documentation is excellent. Essentially, it just requires setting up a database and running a Docker container.&lt;/p&gt;

&lt;p&gt;However, if you're looking for an even simpler method, you can use Coolify for a "one-click" install. With Coolify, you simply click on "Install Umami" under resources, and you're good to go!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kyir33d6qix6yguzt27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kyir33d6qix6yguzt27.png" alt="Image description" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Umami one-click install with Coolify&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick look
&lt;/h2&gt;

&lt;p&gt;Let's take a quick look at Umami's dashboard and key features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgg6x3j7084np46phiex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgg6x3j7084np46phiex.png" alt="Image description" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the interface is pretty minimal yet contains most of the essential information you need. You can see the number of page views, unique visitors, and top referrers. You can also see the most visited pages and the devices your visitors are using.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0a1xbrya997hjkb2a4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0a1xbrya997hjkb2a4s.png" alt="Image description" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tracking custom events&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition, you can add custom events to track specific actions on your website, such as button clicks or form submissions. This is a great feature to track user interactions and conversions.&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Easy to Use
&lt;/h4&gt;

&lt;p&gt;Let's be honest, some analytics tools are way too powerful and complex for most user needs. Umami, on the other hand, is incredibly user-friendly. The setup was straightforward, and within minutes, I was able to start collecting and analyzing data. It’s got all the essentials without the bloat.&lt;/p&gt;

&lt;h4&gt;
  
  
  Comprehensive Insights
&lt;/h4&gt;

&lt;p&gt;Despite its simplicity, Umami Analytics provides a wealth of information. From tracking page views and referrers to monitoring user behavior and campaigns, it covers all the bases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential Drawbacks
&lt;/h2&gt;

&lt;p&gt;Of course, no tool is perfect, and Umami Analytics has its limitations. Here are a few things to keep in mind:&lt;/p&gt;

&lt;h4&gt;
  
  
  Limited Integrations
&lt;/h4&gt;

&lt;p&gt;If you rely heavily on third-party integrations, you might find Umami a bit lacking. It doesn’t have the extensive plugin ecosystem that some other analytics tools boast. However, for many users, its core features are more than sufficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  Basic Customization
&lt;/h4&gt;

&lt;p&gt;While Umami is easy to use, it doesn’t offer the deep customization options that power users might crave. If you need highly specific data segmentation or complex custom reports, you might need to look elsewhere or complement Umami with other tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up: is Umami for you?
&lt;/h2&gt;

&lt;p&gt;Umami is a fantastic tool for those who value privacy and simplicity without compromising on insights. Whether you’re managing a personal blog, an online store, or a corporate website, it’s worth giving Umami a shot.&lt;/p&gt;

&lt;p&gt;Find my next blog articles earlier on &lt;a href="https://easyselfhost.dev/blog"&gt;https://easyselfhost.dev/blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>analytics</category>
      <category>news</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Easy image management for MDX blogs</title>
      <dc:creator>IndyMan</dc:creator>
      <pubDate>Thu, 27 Jun 2024 13:28:15 +0000</pubDate>
      <link>https://dev.to/indyman/frictionless-image-management-for-mdx-blogs-4015</link>
      <guid>https://dev.to/indyman/frictionless-image-management-for-mdx-blogs-4015</guid>
      <description>&lt;p&gt;Writing a blog should be a seamless and enjoyable process. However, I found myself constantly frustrated with the high-friction workflow involved in adding images to my MD(X) blog files. The process typically involved several tedious steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the image to the clipboard.&lt;/li&gt;
&lt;li&gt;Use the VSCode "paste image" extension to paste the image into the repository root&lt;/li&gt;
&lt;li&gt;Move the file to the &lt;code&gt;public/static&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Rename the file.&lt;/li&gt;
&lt;li&gt;Go to the MDX file, use the Image component, and point it to &lt;code&gt;/static/imagename&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process was not only time-consuming but also detracted from the pleasure of writing articles. So I went looking for CMS solutions, which I didn't know much about but felt promising.&lt;/p&gt;

&lt;p&gt;👉 Want to see more ? &lt;a href="https://x.com/_indyman" rel="noopener noreferrer"&gt;Follow me on Twitter&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring CMS Solutions
&lt;/h2&gt;

&lt;p&gt;I explored several CMS solutions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ghost&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strapi&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CraftCMS&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these platforms offered a lot of functionality, they introduced new challenges. For instance, you often need to pay for cloud hosting or manage self-hosting, which involves dealing with backups for both content (usually in a database) and media files (images and videos). Additionally, integrating the CMS content into a Next.js app requires making API calls, which adds complexity to the build process and necessitates environment variable configuration and deployment triggers.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Experience with Ghost
&lt;/h3&gt;

&lt;p&gt;Ghost provided an excellent WYSIWYG editor and robust metadata management, but it had significant drawbacks for my use case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Headless CMS Limitations&lt;/strong&gt;: Ghost is designed to expose a website, and while you can set it to "private mode," this causes the API to stop returning metadata (keywords, images) for articles. This seems like a bug and was a deal-breaker for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent API Responses&lt;/strong&gt;: Occasionally, the API would return an empty list of blogs, resulting in builds with an empty "Blog" section. Rebuilding the app would sometimes fix this, but after several failed builds, I lost confidence in this approach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ultimately, I missed the simplicity of having content and assets colocated with my app. A straightforward GitHub repository setup, without the need for complex environment variables, API calls or backups felt better suited to my needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the Image Management Process
&lt;/h2&gt;

&lt;p&gt;Fed up with the manual steps involved in image management, I decided to automate the process. My goal was to create a simple script that would:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run directly from my Next.js project using &lt;code&gt;npm run &amp;lt;myscript&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prompt for an image name and path, with default values.&lt;/li&gt;
&lt;li&gt;Paste the image from the clipboard to the desired destination.&lt;/li&gt;
&lt;li&gt;Generate the JSX code boilerplate pointing to the image.&lt;/li&gt;
&lt;li&gt;Copy the generated JSX to my clipboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Developing the Script
&lt;/h3&gt;

&lt;p&gt;I created a &lt;code&gt;scripts/pasteimg.ts&lt;/code&gt; file and began writing the script, leveraging GPT to expedite the process. One challenge was getting GPT to handle "taking the image from the clipboard," as it initially suggested using &lt;code&gt;clipboardy&lt;/code&gt;, which isn't compatible with images.&lt;/p&gt;

&lt;p&gt;Fortunately, I discovered the npm package &lt;a href="https://www.npmjs.com/package/save-clipboard-image" rel="noopener noreferrer"&gt;save-clipboard-image&lt;/a&gt;, which uses OSX native commands and perfectly fit my needs.&lt;/p&gt;

&lt;p&gt;After spending a few minutes troubleshooting errors related to ESM modules while trying to run the script with &lt;code&gt;ts-node&lt;/code&gt;, a recommendation to use &lt;a href="https://www.npmjs.com/package/tsx" rel="noopener noreferrer"&gt;tsx&lt;/a&gt; resolved the issue instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Final Result
&lt;/h3&gt;

&lt;p&gt;Here's a demonstration of the script in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/s84kmp09hWOstBL8NuQ1O6Hvr" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2Fs84kmp09hWOstBL8NuQ1O6Hvr.svg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the code behind it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Only works on MacOS, you'll need to adjust the script for Windows or Linux.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;saveClipboardImage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;save-clipboard-image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inquirer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;clipboardy&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clipboardy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NEXT_STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/static&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NEXT_STATIC_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NEXT_STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Ask for the image name&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;imageName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;imageName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter the name of the image (without extension):&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Image name cannot be empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Ask for the path with a default value&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;subdir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subdir&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter the target directory:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullTargetDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NEXT_STATIC_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subdir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Ensure the directory exists&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullTargetDirectory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullTargetDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Save the image from the clipboard&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveClipboardImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullTargetDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imageName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// No image in clipboard, explain and exit&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No image found in clipboard, please copy an image first.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Construct the full path&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullImagePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullTargetDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relativeImagePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NEXT_STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imageName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsxCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;Image
  src="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;relativeImagePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  alt="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
  width={500}
  height={200}
  className="text-center shadow rounded-md"
/&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Copy the generated JSX code to the clipboard using clipboardy&lt;/span&gt;
  &lt;span class="nx"&gt;clipboardy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsxCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Log success in terminal + summary (image path and JSX code)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Image saved successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Image path: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fullImagePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`JSX code copied to clipboard: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jsxCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I'm thrilled with this custom script. It significantly simplifies the process of adding images to my blog, allowing me to focus on writing. Next.js takes care of optimizing the images for various screen sizes, and by committing both the content and media to the same repository, I no longer worry about backups or synchronization issues.&lt;/p&gt;

&lt;p&gt;Find my next blogs earlier on &lt;a href="https://easyselfhost.dev/blog" rel="noopener noreferrer"&gt;https://easyselfhost.dev/blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
