<?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: Open Sauce Projects</title>
    <description>The latest articles on DEV Community by Open Sauce Projects (@pakos).</description>
    <link>https://dev.to/pakos</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%2F776764%2Fe9934c61-f96c-456e-871d-f43476ad1731.jpeg</url>
      <title>DEV Community: Open Sauce Projects</title>
      <link>https://dev.to/pakos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pakos"/>
    <language>en</language>
    <item>
      <title>Tech bits 1 - Cypress screenshot errors in headless mode</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sun, 08 Jun 2025 18:14:48 +0000</pubDate>
      <link>https://dev.to/pakos/tech-bits-1-cypress-screenshot-errors-in-headless-mode-f1g</link>
      <guid>https://dev.to/pakos/tech-bits-1-cypress-screenshot-errors-in-headless-mode-f1g</guid>
      <description>&lt;p&gt;In this series, I share key lessons I’ve learned—one concept per post—for future me and anyone it might help.&lt;/p&gt;

&lt;p&gt;In Cypress headless mode, when a test encounters an error, you can see the result of the test as a screenshot. However, the screenshot doesn’t show the cause of the error—it only shows where the error occurred (e.g., the failing selector or the function like &lt;code&gt;then&lt;/code&gt; where it happened).&lt;/p&gt;

&lt;p&gt;You can work around this limitation by creating a separate screenshot that displays the error message directly on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fail&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runnable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Append the error message to the body&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;document&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;errorDiv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Error: &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="nx"&gt;message&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;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9999&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="c1"&gt;// Wait a moment so it renders&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// Take the screenshot&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;errors/error_screenshot_with_message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// Rethrow the error to fail the test&lt;/span&gt;
   &lt;span class="k"&gt;throw&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code block listens for the &lt;code&gt;fail&lt;/code&gt; event. When triggered, it creates a red banner at the top of the page displaying the error message. The code should be placed inside the &lt;code&gt;cypress/support/e2e.ts&lt;/code&gt; file (or &lt;code&gt;e2e.js&lt;/code&gt; if you're using JavaScript).&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>cypress</category>
      <category>javascript</category>
      <category>testing</category>
      <category>webtesting</category>
    </item>
    <item>
      <title>Transitioning from Win to Mac 2- Managing ARM and x86 nodeJS and Python versions</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sat, 02 Nov 2024 15:57:00 +0000</pubDate>
      <link>https://dev.to/pakos/transitioning-from-win-to-mac-2-managing-arm-and-x86-nodejs-and-python-versions-32n4</link>
      <guid>https://dev.to/pakos/transitioning-from-win-to-mac-2-managing-arm-and-x86-nodejs-and-python-versions-32n4</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Lot of python and NodeJS packages are compatible with ARM but not all, from a performance perspective you better of using an ARM native software. &lt;/li&gt;
&lt;li&gt;So you might run into scenarios where you need a certain version of Python or NodeJS in both it's ARM and x86 version, i will attempt to give a solution for these problems in this article without costing ARM or a leg.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Launching a "x86 terminal"
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It is possible to launch a terminal session where all commands will go through rosetta 2 here are the steps to achieve that:

&lt;ol&gt;
&lt;li&gt;Edit the terminal user profile with nano or any other terminal file editor: nano ~/.zshrc&lt;/li&gt;
&lt;li&gt;Add the following lines to the file:
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;arm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"env /usr/bin/arch -arm64 /bin/zsh --login"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;x86&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"env /usr/bin/arch -x86_64 /bin/zsh --login"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In this way if you type the "intel" command into the terminal you will get an x86 session, if you write the "arm" command you will get and a native not emulated ARM session.&lt;/li&gt;
&lt;li&gt;This will be needed when installing the x86 version of NodeJS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Managing Python versions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Creating and managing python environments is made easier using miniconda, which can be installed like this: brew install miniconda&lt;/li&gt;
&lt;li&gt;conda is like a python virtual environment you can install packages into it using the conda install command or use pip&lt;/li&gt;
&lt;li&gt;Here are steps to create a python environment with miniconda:

&lt;ol&gt;
&lt;li&gt;Create the environment with following command: &lt;code&gt;conda create -n my_x86_env -y&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can active the environment with the command like this: &lt;code&gt;conda activate my_x86_env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You need to set which architecture you want to use for x86: &lt;code&gt;conda config --env --set subdir osx-64&lt;/code&gt; 
for arm: &lt;code&gt;replace osx-64 with osx-arm64&lt;/code&gt; but if you don't specify the architecture it will use ARM as a default&lt;/li&gt;
&lt;li&gt;Then you can install python &lt;code&gt;conda install python=3.9&lt;/code&gt;
you can search for available python version with &lt;code&gt;conda search python&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now your env is ready for installing packages &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Managing NodeJS versions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For this we gonna use nvm which is version manager for node, this can be installed with brew: brew install nvm&lt;/li&gt;
&lt;li&gt;Here is list of useful nvm &lt;a href="https://gist.github.com/chranderson/b0a02781c232f170db634b40c97ff455" rel="noopener noreferrer"&gt;commands&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Steps to get you going:

&lt;ol&gt;
&lt;li&gt;The architecture will depend on what terminal you in, so if you want x86 use the "x86" command we discussed earlier&lt;/li&gt;
&lt;li&gt;Install node: &lt;code&gt;nvm install VERSION&lt;/code&gt;
you can't install the exact same version in both architectures but you can install slightly older or newer one&lt;/li&gt;
&lt;li&gt;To check the current version of node use: &lt;code&gt;node -v&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For switching between the versions: &lt;code&gt;nvm use VERSION&lt;/code&gt; command.
Fun fact when switching versions you don't need to specify the version number exactly for example if i want to switch to 20.16.0, I just have to type &lt;code&gt;nvm use 20&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/71691598/how-to-run-python-as-x86-with-rosetta2-on-arm-macos-machine" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/71691598/how-to-run-python-as-x86-with-rosetta2-on-arm-macos-machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/39604271/conda-environments-not-showing-up-in-jupyter-notebook" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/39604271/conda-environments-not-showing-up-in-jupyter-notebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/75942145/how-to-quickly-change-between-arm64-or-x86-architecture-in-m1-m2-mac-terminals" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/75942145/how-to-quickly-change-between-arm64-or-x86-architecture-in-m1-m2-mac-terminals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Transitioning from Win to Mac 1-Downloading software</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sat, 02 Nov 2024 15:56:01 +0000</pubDate>
      <link>https://dev.to/pakos/transitioning-from-win-to-mac-1-downloading-software-810</link>
      <guid>https://dev.to/pakos/transitioning-from-win-to-mac-1-downloading-software-810</guid>
      <description>&lt;ul&gt;
&lt;li&gt;In this series of articles I want to document my experience of switching from windows to a mac machine in a work environment and highlight the differences between them to hopefully help other people who are also making this transition.&lt;/li&gt;
&lt;li&gt;These articles are applicable for the mac's that have an ARM processor, so from the M1 mac mini and upwards. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rosetta 2
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Here i'm not talking about the rosseta stone, so don't worry you wont have to carry a stone with you to work, no this is a translation layer. &lt;/li&gt;
&lt;li&gt;These new mac's processors are based on the ARM architecture, the old macs were based on the x86 (sometimes called x64 for 64bit hardware) architecture because of this software written for the x86 won't run on ARM without a translation layer.&lt;/li&gt;
&lt;li&gt;Also this kind of emulation can have a 20-30% performance (older benchmark link below) impact and it is not guaranteed that all software will run.&lt;/li&gt;
&lt;li&gt;If you try to open a x86 app the OS will prompt you to install Rosetta but you can manually install it with the following command: 
&lt;code&gt;softwareupdate --install-rosetta&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting the software
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For getting software the safest and easiest way is to get it from the Appstore but for lots of apps you need the brew package manager.&lt;/li&gt;
&lt;li&gt;You can install it with the folowing command: &lt;code&gt;/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In brew there are two types of software you can download, a cask or a formula, formula is usually a refers to a non gui app.&lt;/li&gt;
&lt;li&gt;Most apps can be found on the official brew site but some can be installed from a github repo using the "Tap" feature which adds that github repo as a new "repository" for packages.&lt;/li&gt;
&lt;li&gt;When you found your package you just run brew install package_name and that is it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My software is installed but I can't seem to launch it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes a software can't be found in the Launchpad and can't be started from the terminal either.&lt;/li&gt;
&lt;li&gt;Don't worry, get a well brewed beer and we gonna fix that, here are the steps to fix a terminal app not working:

&lt;ol&gt;
&lt;li&gt;Run brew info package_name to find out where the program is saved&lt;/li&gt;
&lt;li&gt;Try to find the executable in the folder and try to run in from there&lt;/li&gt;
&lt;li&gt;If it works, add the folder to PATH variable on startup so the program can be launched from anywhere by editing your terminal profile using a terminal file editor like nano:

&lt;ul&gt;
&lt;li&gt;After installing nano from brew like: brew install nano, edit the terminal profile: &lt;code&gt;nano ~/.zshrc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Here you can add the folder where your program resides to the PATH by adding this line to the end of the file: &lt;code&gt;export PATH=$PATH:executable_location&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt;Now if a gui app not in the Launchpad you just drag the executable's icon to the Launchpad's icon on your dock, that will add it.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anandtech.com/show/16252/mac-mini-apple-m1-tested/6" rel="noopener noreferrer"&gt;https://www.anandtech.com/show/16252/mac-mini-apple-m1-tested/6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://osxdaily.com/2020/12/04/how-install-rosetta-2-apple-silicon-mac/" rel="noopener noreferrer"&gt;https://osxdaily.com/2020/12/04/how-install-rosetta-2-apple-silicon-mac/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flotato.com/help/create-and-manage/launchpad.html" rel="noopener noreferrer"&gt;https://flotato.com/help/create-and-manage/launchpad.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;https://brew.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Access Nvidia gpu inside docker container in Arch linux</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sat, 24 Aug 2024 00:19:03 +0000</pubDate>
      <link>https://dev.to/pakos/access-nvidia-gpu-inside-docker-container-in-arch-linux-4k6b</link>
      <guid>https://dev.to/pakos/access-nvidia-gpu-inside-docker-container-in-arch-linux-4k6b</guid>
      <description>&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you need gpu acceleration in a container or just have too much free time on your hands, you can run all kinds of things. For example Large Language Models or GUI apps.&lt;/li&gt;
&lt;li&gt;You can even create your own gpu accelerated docker image by using &lt;a href="https://hub.docker.com/r/nvidia/cuda" rel="noopener noreferrer"&gt;Nvidia ubuntu images&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We have to install the NVIDIA Container Toolkit&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The package can be installed from the Extras repository&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; nvidia-container-toolkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Configure the container runtime by using the nvidia-ctk command. The &lt;code&gt;nvidia-ctk&lt;/code&gt; command modifies the &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; file on the host. The file is updated so that Docker can use the NVIDIA Container Runtime.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nvidia-ctk runtime configure &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the change to take effect we have to restart the docker daemon.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can add the gpu using docker compose:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;....&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;devices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nvidia&lt;/span&gt;
            &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gpu&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If you use the &lt;code&gt;docker run&lt;/code&gt; command, you can do:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nvidia &lt;span class="nt"&gt;--gpus&lt;/span&gt; all ....
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#id1" rel="noopener noreferrer"&gt;NVIDIA Container Toolkit documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://archlinux.org/packages/extra/x86_64/nvidia-container-toolkit/" rel="noopener noreferrer"&gt;Toolkit Arch package page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Exposing your services using subdomains with a dynamic ip</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sat, 13 Jan 2024 23:32:30 +0000</pubDate>
      <link>https://dev.to/pakos/exposing-your-services-using-subdomains-with-a-dynamic-ip-2jh7</link>
      <guid>https://dev.to/pakos/exposing-your-services-using-subdomains-with-a-dynamic-ip-2jh7</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This guide assumes that you using Docker and Docker compose to manage your self hosted services, Caddy can be installed without Docker but ddns-updater have to be replaced with another service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How will this work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I'm glad you asked, we will use two containerized tools. The first is &lt;a href="https://hub.docker.com/r/qmcgaw/ddns-updater" rel="noopener noreferrer"&gt;ddns-updater&lt;/a&gt; which will update your &lt;a href="https://developers.cloudflare.com/dns/manage-dns-records/reference/dns-record-types/" rel="noopener noreferrer"&gt;A records&lt;/a&gt; (subdomains) when your ip has changed, so your domains will always point to your server. &lt;/li&gt;
&lt;li&gt;The second is &lt;a href="https://hub.docker.com/_/caddy" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt; a reverse proxy which will forward a request for a subdomain to a port on your machine and it will give you automatic HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why do i need a reverse proxy?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You may ask why do you need a reverse proxy if you know the port of your service and have a domain from which you can access it?&lt;/li&gt;
&lt;li&gt;Due to the automatic HTTPS your traffic will be encrypted, without SSL (HTTPS) everyone on your WIFI network and your ISP can read your traffic.&lt;/li&gt;
&lt;li&gt;You only need to open two ports on your router 443 (HTTPS), 80 (HTTP).&lt;/li&gt;
&lt;li&gt;You can do a lot of additional things between the client sending a requests for a site and and sending a response using a middleware for example, you can do additional authentication for an unprotected site with a &lt;a href="https://github.com/greenpau/caddy-security/blob/main/README.md" rel="noopener noreferrer"&gt;caddy plugin&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up the domain and subdomains
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First you have to get a domain, you can get a free domain on &lt;a href="https://www.freenom.com/en/index.html?lang=en" rel="noopener noreferrer"&gt;Freenom&lt;/a&gt; but I don't recommend it. Many people reported on Reddit that they domain's were removed.&lt;/li&gt;
&lt;li&gt;You can get a free sub domain with &lt;a href="https://www.duckdns.org/" rel="noopener noreferrer"&gt;DuckDNS&lt;/a&gt;, in this case you can setup your sub domains in a form of "sub sub domain", for example if you clam the &lt;em&gt;mydomain.duckdns.org&lt;/em&gt; subdomain, your service would be located at &lt;em&gt;service.mydomain.duckdns.org&lt;/em&gt;, but i haven't tried this option but people on Reddit reported success with it. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manage domain with Dynamic DNS service
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This service can manage your domain, your domain provider will provide you with an API key or token which ddns-updater can use to update the domain.&lt;/li&gt;
&lt;li&gt;Please check if your provider is on the supported list for ddns-updater.&lt;/li&gt;
&lt;li&gt;It also provides a web UI from you can see the status of you domains.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fqdm12%2Fddns-updater%2Fmaster%2Freadme%2Fwebui.png" alt="Web UI"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Setting up ddns-updater
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Docker compose setup:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Dynamic-dns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="na"&gt;   image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;qmcgaw/ddns-updater&lt;/span&gt;  
&lt;span class="na"&gt;   container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dynamic-dns&lt;/span&gt;  
&lt;span class="na"&gt;   restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;  
&lt;span class="na"&gt;   privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
&lt;span class="na"&gt;   volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="s"&gt;     - /your/path/:/updater/data&lt;/span&gt;  
&lt;span class="na"&gt;   ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="s"&gt;     - 8000:8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The configuration stored in the config.json in the specified data folder.&lt;/li&gt;
&lt;li&gt;Every subdomain has an entry in the settings list with the domain provider, the domain, the sub domain and the api key.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"namecheap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"domain.cc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"provider_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SAetting up Caddy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Docker compose setup:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;caddy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="na"&gt;   container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy&lt;/span&gt;  
&lt;span class="na"&gt;   image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy:latest&lt;/span&gt;  
&lt;span class="na"&gt;   restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;  
&lt;span class="na"&gt;   ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="s"&gt;     - 443:443/tcp&lt;/span&gt;  
&lt;span class="s"&gt;     - 80:80/tcp&lt;/span&gt;  
&lt;span class="na"&gt;   volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
&lt;span class="s"&gt;     - /your/path/caddy_config:/config:rw&lt;/span&gt;  
&lt;span class="s"&gt;     - /your/path/site:/srv:rw&lt;/span&gt;  
&lt;span class="s"&gt;     - /your/path/caddy_data:/data:rw&lt;/span&gt;  
&lt;span class="s"&gt;     - /your/path/Caddyfile:/etc/caddy/Caddyfile:rw&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Configuration stored in the Caddyfile, for every subdomain you need to define a reverse_proxy ip:port, you can write comments with hashtags:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;sub.example.cc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;       encode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gzip&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;forward&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;header&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;               log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;               output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/data/monitor.log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;                   roll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Rotate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;enabled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;default&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_size_mb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MB&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_gzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Whether&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;compress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;rolled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_local_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;                   roll_keep_days&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;days&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;   reverse_proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ip:port&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example with Namecheap domain provider
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;if you want to create a self hosted netflix with the &lt;a href="https://jellyfin.org" rel="noopener noreferrer"&gt;Jellyfin&lt;/a&gt; thats on the &lt;em&gt;8096&lt;/em&gt; port with a domain from Namecheap&lt;/li&gt;
&lt;li&gt;Create an A record for the subdomain for the service, for example  &lt;em&gt;jellyfin.mydomain.com&lt;/em&gt; on your domain providers dashboard, in Namecheap navigate to Account-&amp;gt;Dashboard-&amp;gt;Domain List-&amp;gt;Manage-&amp;gt;Advanced DNS. The "Type" enter 'A + Dynamic DNS Record' for "Host" enter the sub domain-&amp;gt; 'jellyfin', and Value is the your ip, set the &lt;a href="https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/" rel="noopener noreferrer"&gt;TTL&lt;/a&gt; (update interval for the record) to Automatic&lt;/li&gt;
&lt;li&gt;Add an entry to the settings list in the config.json for the ddns updater:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"namecheap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"mydomain.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"jellyfin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"provider_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add an entry in the Caddyfile:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;jellyfin.mydomain.com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;       encode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gzip&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;forward&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;header&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;               log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;               output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/data/jellyfin.log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;                   roll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Rotate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;enabled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;default&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_size_mb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MB&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_gzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Whether&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;compress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;rolled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_local_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       roll_keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;                   roll_keep_days&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;days&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;                       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;               &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;span class="err"&gt;   reverse_proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;192.168&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.101&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8096&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enjoy!&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>networking</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Store images/files for API, using MySQL/MariaDB, Express and Sequelize</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Wed, 01 Nov 2023 13:04:32 +0000</pubDate>
      <link>https://dev.to/pakos/store-imagesfiles-for-api-using-mysqlmariadb-express-and-sequelize-6hj</link>
      <guid>https://dev.to/pakos/store-imagesfiles-for-api-using-mysqlmariadb-express-and-sequelize-6hj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Co-authored with &lt;a class="mentioned-user" href="https://dev.to/dezsicsaba"&gt;@dezsicsaba&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;This guide is focused on storing and serving images, but you can store any kind of data in MySQL/MariaDB in this manner&lt;/li&gt;
&lt;li&gt;We also need to install the multer extension to Express to easily deal with file uploads

&lt;ul&gt;
&lt;li&gt;multer is an &lt;strong&gt;npm&lt;/strong&gt; plugin. Here is the complete &lt;a href="https://www.npmjs.com/package/multer"&gt;multer documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DB side of the story
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First we have to define what datatype we're going to use in our db. In our project we used the BLOB datatype, because it can store raw binary data.&lt;/li&gt;
&lt;li&gt;It has four sub-types:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TINYBLOB&lt;/strong&gt;: Maximum length of 255 bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BLOB&lt;/strong&gt;: Maximum length of 65535 bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MEDIUMBLOB&lt;/strong&gt;: Maximum length of 16777215 bytes, or 16MB in storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LONGBLOB&lt;/strong&gt;: Maximum length of 4294967295 bytes or 4GB in storage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;You thought this is that easy, weell you are mistaken. We have to specify how much data we want to transfer between database and the API, since the default values are too small.

&lt;ul&gt;
&lt;li&gt;We can do this by setting the following two variables.&lt;/li&gt;
&lt;li&gt;(Variables can be set by editing the mysqld config file):
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  [mysqld]
  ......
  net_buffer_length = 1000000
  max_allowed_packet=256M
  ......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Another way is to use the MySQL command line using these commands
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  set global net_buffer_length=1000000; 
  set global max_allowed_packet=256M;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Don't forget to change these variables based on data size&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So what code do we need to write?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sequelize supports all four types of BLOBs. This can be adjusted when you are defining your model.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;DataTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOB&lt;/span&gt;                &lt;span class="c1"&gt;// BLOB (bytea for PostgreSQL)&lt;/span&gt;
  &lt;span class="nx"&gt;DataTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tiny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;// TINYBLOB (bytea for PostgreSQL)&lt;/span&gt;
  &lt;span class="nx"&gt;DataTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// MEDIUMBLOB (bytea for PostgreSQL)&lt;/span&gt;
  &lt;span class="nx"&gt;DataTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;// LONGBLOB (bytea for PostgreSQL)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;First we need to define the field and datatype in our model
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;pictures&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


  &lt;span class="nx"&gt;pictures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Picture&lt;/span&gt;&lt;span class="p"&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="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;allowNull&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;ItemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//suppose we have an item where the image belongs to&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;allowNull&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//data connection object: new Sequelize(....&lt;/span&gt;
      &lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pictures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pictures&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pictures&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Receive and store image/file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In this example, we send the file with a form using the multipart/form-data MIME type and an item id to describe which item this image belongs to, server handles the request with the multer extension&lt;/li&gt;
&lt;li&gt;From the received image we need the buffer property. It has the type of &lt;a href="https://www.w3schools.com/nodejs/ref_buffer.asp"&gt;Buffer&lt;/a&gt;, it is a stream of binary data
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/img/:itemId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&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;img&lt;/span&gt;&lt;span class="p"&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; [ERROR] no img!!!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;406&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//function that lets us connect the sequelize to the dataBase&lt;/span&gt;

  &lt;span class="c1"&gt;//You are highly advised to have the build and save part of the code inside a picture-controller, outside the router&lt;/span&gt;
  &lt;span class="c1"&gt;//We have it here for the sake of clarity&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;picture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Picture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ItemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;saved&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;picture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; [ERROR] could not save 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;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="c1"&gt;//presuming you are handling your errors at the highest level of the api(api.listen...),&lt;/span&gt;
                      &lt;span class="c1"&gt;// in the index.js with a middleware&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;modelInstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Send image back to the client
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To send images to the client we first get the buffer from the image after that we can convert it to the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Base64"&gt;Base64&lt;/a&gt; format so we can included in a JSON response.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SIDENOTE&lt;/strong&gt;: If you are working with lets say VueJs, the base64 conversion is not at all needed. We needed to convert the buffer to base64 in our project for many different reasons&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The drawback is that the base64 conversion takes a lot of processing power.&lt;/li&gt;
&lt;li&gt;Suppose we want to get the images that belong to an item, based on the item id in the URL:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Pictures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/Pictures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//obviously the path for the Picture model may differ&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/img/:itemId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&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;itemId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//function that lets us connect the sequelize to the dataBase&lt;/span&gt;

  &lt;span class="c1"&gt;//You are highly advised to have the findAll part of the code inside a picture-controller, outside the router&lt;/span&gt;
  &lt;span class="c1"&gt;//We have it here for the sake of clarity&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&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;Pictures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ItemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;406&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="c1"&gt;//The conversion part is obviously only needed if you need to return base64 as part of your response&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;convertToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//!!IMPORTANT!!&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;//if you wannt to return the base64 encoded image array&lt;/span&gt;
    &lt;span class="na"&gt;pictures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="c1"&gt;//if you want to return the images received from the db without converting them to base64&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;convertToBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;outputArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;//this will contain the base64 strings&lt;/span&gt;
    &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;arraybuffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Picture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arraybuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;outputArray&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The definition of our Item model:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&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;Pictures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./pictures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//the Picture model&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;ProductName&lt;/span&gt;&lt;span class="p"&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="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;allowNull&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&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;//if you are using MariaDb you can define the connection by:&lt;/span&gt;
&lt;span class="c1"&gt;//for example while using XAMPP's MySql that technically uses MariaDb&lt;/span&gt;
&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foreignKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ItemId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//if you are directly using MySql:&lt;/span&gt;
&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;foreignKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ItemId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="na"&gt;foreignKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ItemId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tableplus.com/blog/2019/10/mysql-blob.html"&gt;https://tableplus.com/blog/2019/10/mysql-blob.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sequelize.org/docs/v6/other-topics/other-data-types/#blobs"&gt;https://sequelize.org/docs/v6/other-topics/other-data-types/#blobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/expressjs/multer"&gt;https://github.com/expressjs/multer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/93128/mysql-error-1153-got-a-packet-bigger-than-max-allowed-packet-bytes"&gt;https://stackoverflow.com/questions/93128/mysql-error-1153-got-a-packet-bigger-than-max-allowed-packet-bytes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>mysql</category>
      <category>tutorial</category>
      <category>sequelize</category>
    </item>
    <item>
      <title>Easily check if web service available outside the local network</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Thu, 15 Sep 2022 15:35:54 +0000</pubDate>
      <link>https://dev.to/pakos/easily-check-if-web-service-available-outside-the-local-network-1d0h</link>
      <guid>https://dev.to/pakos/easily-check-if-web-service-available-outside-the-local-network-1d0h</guid>
      <description>&lt;h2&gt;
  
  
  When is this relevant?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you have a web service on your local network you want check if its accessible publicly on the internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What do i need?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's a bit of a hack, but you can use google translate as a proxy. From &lt;a href="https://translate.google.com/?sl=auto&amp;amp;tl=en&amp;amp;op=websites"&gt;here&lt;/a&gt; in google translate you can get a url from which you can access your site's translated version. The site might not render properly but the important thing is that this url will always point to your website. So if this site is available so is yours and vice versa. &lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can do a GET request and test for site availability in two ways. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do a keyword search if you know a word that should always appear on the site&lt;/li&gt;
&lt;li&gt;Another way is to check the request's status code, if it's not 200 the site is most likely down. &lt;/li&gt;
&lt;li&gt;You can monitor this with a scheduled script that would notify you if the site is down, here's a simple python example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;  
&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"'translated' url"&lt;/span&gt;  
&lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;  
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"send notification"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="c1"&gt;# or  
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"send notification"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For less headaches instead of a script i would suggest downloading &lt;a href="https://github.com/louislam/uptime-kuma"&gt;Uptime kuma&lt;/a&gt; which is specifically made for checking website status, it supports status code and keyword checking and a lot more. It has a lot of options for notifications, not limited to but including Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP) and it has a nice web ui.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://camo.githubusercontent.com/9674a2b1b7d094b060fd79e6df7dca10b86a484ce6015b2668cff768dfc786ee/68747470733a2f2f757074696d652e6b756d612e7065742f696d672f6461726b2e6a7067" class="article-body-image-wrapper"&gt;&lt;img src="https://camo.githubusercontent.com/9674a2b1b7d094b060fd79e6df7dca10b86a484ce6015b2668cff768dfc786ee/68747470733a2f2f757074696d652e6b756d612e7065742f696d672f6461726b2e6a7067" alt="Uptime Kuma dashboard" width="1024" height="641"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Soo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I hope it was helpful in some way, if you have an alternative idea be sure to comment it below.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>selfhosting</category>
      <category>portforwarding</category>
      <category>devops</category>
    </item>
    <item>
      <title>Build a media center from almost anything</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sun, 21 Aug 2022 12:53:00 +0000</pubDate>
      <link>https://dev.to/pakos/build-media-center-from-almost-anything-2f35</link>
      <guid>https://dev.to/pakos/build-media-center-from-almost-anything-2f35</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Waaait what is a media center, and why should i care?

&lt;ul&gt;
&lt;li&gt;It's software that's basically a smart tv equivalent where you can view all your media/subscriptions in one place. &lt;/li&gt;
&lt;li&gt;In some ways it's better than a smart tv because it has generally more software than a cheap tv and you can repurpose your existing device. &lt;/li&gt;
&lt;li&gt;The one drawback is that it will require some setting up.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can use any 64bit intel/amd processor equipped laptop/pc from tha last 10 years if you are sticking to hd content.&lt;/li&gt;
&lt;li&gt;You can use an android box or phone with hdmi out or a cheap &lt;strong&gt;S&lt;/strong&gt;ingle &lt;strong&gt;B&lt;/strong&gt;oard &lt;strong&gt;C&lt;/strong&gt;omputer like the raspberry pi.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Software
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The star of the show is kodi, which is a dedicated media center app with an extensive addon ecosystem.&lt;/li&gt;
&lt;li&gt;It can be installed directly on windows, android or on linux, macos. &lt;/li&gt;
&lt;li&gt;On the other hand if you are using dedicated hardware for this purpose i would advise to install a &lt;strong&gt;J&lt;/strong&gt;ust &lt;strong&gt;E&lt;/strong&gt;nough &lt;strong&gt;O&lt;/strong&gt;perating &lt;strong&gt;S&lt;/strong&gt;ystem which is a stripped down linux system with kodi acting as the systems "desktop", they have dedicated controls for wifi and bluetooth inside the kodi interface.&lt;/li&gt;
&lt;li&gt;JEOS are:

&lt;ul&gt;
&lt;li&gt;for amd/intel and some &lt;strong&gt;S&lt;/strong&gt;ingle &lt;strong&gt;B&lt;/strong&gt;oard &lt;strong&gt;C&lt;/strong&gt;omputers you can install &lt;a href="https://libreelec.tv/downloads/"&gt;libreelec&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In case of the raspberry pi you can use libreelec or &lt;a href="https://osmc.tv/download/"&gt;OSMC&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Addons
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Sooo inside kodi, software is distributed as addons they can be installed from repositories and user repositories can be installed via zip files.&lt;/li&gt;
&lt;li&gt;You can basically watch every streaming service with right addon, most of it can be found in the &lt;a href="https://k.slyguy.xyz/"&gt;slyguy&lt;/a&gt; repository, but you can find every kind of content under the sun.&lt;/li&gt;
&lt;li&gt;Kodi comes included with a base repository with a lot of addons, also libreelec has its own repo with useful addons like a chrome browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Connectivity
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;You can remote control it with android apps like &lt;a href="https://play.google.com/store/apps/details?hl=en&amp;amp;id=org.leetzone.android.yatsewidgetfree"&gt;yatse&lt;/a&gt;, &lt;a href="https://play.google.com/store/apps/details?id=org.xbmc.kore&amp;amp;hl=en"&gt;kore&lt;/a&gt;, the &lt;a href="https://apps.apple.com/us/app/official-kodi-remote/id520480364"&gt;Official Kodi Remote&lt;/a&gt; ios app or with the built in web interface.&lt;/li&gt;
&lt;li&gt;Kodi includes a SMB file server, and you can cast to it using airplay, or you can share videos with kodi through the remote app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sooo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I hoped i managed to spark some interest in you. Stay curious!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kodi</category>
      <category>streaming</category>
      <category>diy</category>
      <category>mediacenter</category>
    </item>
    <item>
      <title>DIY low power NAS 2022: 1 - The hardware</title>
      <dc:creator>Open Sauce Projects</dc:creator>
      <pubDate>Sat, 13 Aug 2022 21:35:45 +0000</pubDate>
      <link>https://dev.to/pakos/diy-low-power-nas-2022-1-the-hardware-3pn3</link>
      <guid>https://dev.to/pakos/diy-low-power-nas-2022-1-the-hardware-3pn3</guid>
      <description>&lt;ul&gt;
&lt;li&gt;This is the beginning of a series of articles where I go over my choices and what other options I found for building a low power nas, as concisely as possible and hopefully help you along the way. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CPU and motherboard
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you want to build a 24/7 nas, a very important benchmark to look at, is idle power usage because chances are you're not going to be accessing your nas most of the day. You can look at CPU's TDP but almost always idle usage will be lower.
&lt;/li&gt;
&lt;li&gt;Unfortunately most desktop CPUs are not good options. If you're aiming for 10-20w idle, you can try undervolting them but usually you need a very specific combination of motherboard and CPU to achieve a good result. Here is a &lt;a href="https://docs.google.com/spreadsheets/d/1LHvT2fRp7I6Hf18LcSzsNnjp10VI-odvwZpQZKv_NCI/edit#gid=0"&gt;list&lt;/a&gt; of good combinations with benchmarks.&lt;/li&gt;
&lt;li&gt;That leaves mobile chips, you have 4 options here.

&lt;ul&gt;
&lt;li&gt;You can use an arm &lt;strong&gt;S&lt;/strong&gt;ingle &lt;strong&gt;B&lt;/strong&gt;oard &lt;strong&gt;C&lt;/strong&gt;omputer like the raspberry pi. They mostly idle under 5w, but they have limited sata ports without extension boards.&lt;/li&gt;
&lt;li&gt;You can use a laptop or a mini pc, but you're limited to 1 or 2 sata ports but you can convert NVME, mini pcie slots to sata or you can use usb enclosures, but you may need an external PSU to power those new sata ports.
&lt;/li&gt;
&lt;li&gt;Another option is to buy a mini itx motherboard with an integrated mobile chip. &lt;/li&gt;
&lt;li&gt;Right now good quad core options are the biostar/asus/asrock branded boards with j4125, j5005, j5040 chips they idle at 8-10w (I'm using the asrock j5040). &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GPU
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you choose an arm solution most likely the gpu will be pretty weak and hardware acceleration will be limited.&lt;/li&gt;
&lt;li&gt;If it's an intel/amd CPU from the last 5 years it will probably have decent support for hardware acceleration.&lt;/li&gt;
&lt;li&gt;You can try to add a low end gpu like the gt 1030 but it adds about 9w to the idle power consumption and if your media server is only used by few people you might not need it. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ram
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I would advise 8gb to be on the safe side but if you are using linux you can get away with 4gb too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Storage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you don't need much storage go with an ssd, they mostly idle below 3w.&lt;/li&gt;
&lt;li&gt;On the other hand if you have a lot of *&lt;em&gt;cough&lt;/em&gt;* legitly downloaded media *&lt;em&gt;cough&lt;/em&gt;* hdd's are much cheaper, also 5400rpm drives are slower but they consume little bit less power than 7200rpm drives.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  PSU
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you went with the mini Itx option you will need an efficient PSU.
&lt;/li&gt;
&lt;li&gt;Atx PSU's start at 300w and mostly the advertised efficiency does not apply to less than 50% load. Instead you can use a 60-160w pico PSU which uses an external ac to dc adapter like a laptop, it's slightly more efficient but they are harder to find. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sooo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hopefully i managed to help you, if you found an error or i missed something, please comment below. Happy Building! &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cybenetics.com/index.php?option=power-supplies"&gt;Cybenatics Labs: PSU benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/HomeServer/comments/a3ysbe/picopsu_vs_atx_psu_for_powering_small_server_with/"&gt;Pico vs atx psu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://browser.geekbench.com/"&gt;Geekbench: CPU benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=0uyT6bpRdlk"&gt;J5040 review&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=HRJvSux34Hk"&gt;j4105 review&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.tomshardware.com/reviews/nvidia-geforce-gt-1030-2gb,5110-9.html"&gt;gt 1030 review&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/homelab/"&gt;Homelab subreddit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>homelab</category>
      <category>nas</category>
      <category>diy</category>
    </item>
  </channel>
</rss>
