<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[All your code are belong to us]]></title><description><![CDATA[Thoughts, stories and ideas on code and technology in general.<br>Blog title inspired by <a href="https://en.wikipedia.org/wiki/All_your_base_are_belong_to_us" target="_blank">this meme</a>]]></description><link>https://allurcode.com/</link><image><url>https://allurcode.com/favicon.png</url><title>All your code are belong to us</title><link>https://allurcode.com/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Tue, 14 Apr 2026 04:33:49 GMT</lastBuildDate><atom:link href="https://allurcode.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How to Install the Latest PHP Version on Windows Subsystem for Linux (WSL)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I needed to locally run a PHP project that required PHP 8. By default only PHP 7.x is available in WSL repository. To upgrade your PHP installation to the latest PHP 8.x version within Windows Subsystem for Linux (WSL) read on.</p>
<h2 id="step-1-launch-your-wsl-environment">Step 1: Launch Your WSL Environment</h2>
<p>Start</p>]]></description><link>https://allurcode.com/how-to-install-the-latest-php-version-on-windows-subsystem-for-linux-wsl/</link><guid isPermaLink="false">65d25128b4210f20e145ced0</guid><category><![CDATA[PHP]]></category><category><![CDATA[WSL]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 18 Feb 2024 19:37:25 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I needed to locally run a PHP project that required PHP 8. By default only PHP 7.x is available in WSL repository. To upgrade your PHP installation to the latest PHP 8.x version within Windows Subsystem for Linux (WSL) read on.</p>
<h2 id="step-1-launch-your-wsl-environment">Step 1: Launch Your WSL Environment</h2>
<p>Start by opening your Linux distribution through the Start menu or by typing its name into PowerShell or Command Prompt.</p>
<h2 id="step-2-prepare-your-system">Step 2: Prepare Your System</h2>
<p>Before proceeding with the PHP upgrade, ensure your system&apos;s package list is up to date. In your WSL terminal, run:</p>
<pre><code class="language-shell">sudo apt update &amp;&amp; sudo apt upgrade -y
</code></pre>
<p>This command updates the package list and upgrades any existing packages to their latest versions.</p>
<h2 id="step-3-install-php-8x">Step 3: Install PHP 8.x</h2>
<p>The default repositories in your Linux distribution may not immediately offer the latest PHP version. To install PHP 8.x, you might first need to add a third-party repository, such as the widely-used Ond&#x159;ej Sur&#xFD; PPA for Ubuntu and Debian distributions. Here&#x2019;s how you do it:</p>
<p><strong>Add the PHP Repository</strong>: Install the <code>software-properties-common</code> package to manage additional repositories and then add the Ond&#x159;ej Sur&#xFD; PHP repository.</p>
<pre><code class="language-shell">sudo apt -y install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
</code></pre>
<p><strong>Install PHP 8.x</strong>: With the repository added, you can now install PHP 8.x. Replace <code>8.x</code> with the specific minor version you wish to install, I used <code>8.3</code>.</p>
<pre><code class="language-shell">sudo apt install php8.3
</code></pre>
<p>This command installs PHP 8.3 along with common extensions. If you need additional PHP extensions, install them using the <code>sudo apt install php8.3-extension_name</code> format.</p>
<h2 id="step-4-verify-your-php-installation">Step 4: Verify Your PHP Installation</h2>
<p>After installation, verify that PHP 8.3 is correctly installed by checking its version:</p>
<pre><code class="language-shell">php -v

PHP 8.3.3-1+ubuntu20.04.1+deb.sury.org+1 (cli) (built: Feb 15 2024 18:38:21) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.3-1+ubuntu20.04.1+deb.sury.org+1, Copyright (c), by Zend Technologies
</code></pre>
<p>This command displays the installed PHP version, confirming that you&apos;re now running PHP 8.3 on your WSL environment.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Network-attached storage (NAS) on local network with Raspberry PI]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Do you have a few old external USB drives lying around collecting dust? Well, you could put them to good use with a simple and robust NAS (Network-attached storage) using a Rasberry Pi.<br>
Moreover, if you have plenty of old travel photos on that external drive, you could host my</p>]]></description><link>https://allurcode.com/network-attached-storage-on-local-network-with-raspberry-pi/</link><guid isPermaLink="false">620007e960a53816cf31be37</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[bash]]></category><category><![CDATA[nas]]></category><category><![CDATA[usb]]></category><category><![CDATA[drive]]></category><category><![CDATA[Raspberry Pi OS]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 18 Sep 2022 16:03:20 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Do you have a few old external USB drives lying around collecting dust? Well, you could put them to good use with a simple and robust NAS (Network-attached storage) using a Rasberry Pi.<br>
Moreover, if you have plenty of old travel photos on that external drive, you could host my open source NodeJS app (<a href="https://github.com/SixBytesUnder/infoboard">GitHub repo here</a>) to display them on an old tablet, just like an interactive smart photo frame.</p>
<p>I&apos;ve tested this on Rasberry Pi 3 and 4, but really any should work. If your external USB drive has it&apos;s own power source, you can connect it to RPi directly, but if it doesn&apos;t have it&apos;s own power source, you might need an externally powered USB hub to which you&apos;ll connect your USB drives. Raspberry might not be able to suply enough current to power more than 2-3 external USB drives.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>I assume you already have Raspberry Pi OS installed, but if not, install it from official source <a href="https://www.raspberrypi.com/software/">https://www.raspberrypi.com/software/</a> (make sure you enable SSH during intallation).<br>
If you already have a different Linux distribution installed, this should also work, but you might need to slightly modify some of below commands.</p>
<ol>
<li>
<p>ssh to your RPi and update your system</p>
<pre><code class="language-bash">sudo apt update &amp;&amp; sudo apt full-upgrade
</code></pre>
</li>
<li>
<p>Connect any USB drives you want to make accessible over the network.<br>
Raspberry Pi OS should automatically detect USB drives when you plug them in. To see what drives it found run:</p>
<pre><code class="language-bash">sudo blkid

/dev/mmcblk0p1: LABEL_FATBOOT=&quot;boot&quot; LABEL=&quot;boot&quot; UUID=&quot;17B6-FC00&quot; BLOCK_SIZE=&quot;512&quot; TYPE=&quot;vfat&quot; PARTUUID=&quot;834cc527-01&quot;
/dev/mmcblk0p2: LABEL=&quot;rootfs&quot; UUID=&quot;b101bb80-3338-4b94-a775-b3844f8f2aa8&quot; BLOCK_SIZE=&quot;4096&quot; TYPE=&quot;ext4&quot; PARTUUID=&quot;834cc527-02&quot;
/dev/sdc1: LABEL=&quot;SAMSUNG&quot; BLOCK_SIZE=&quot;512&quot; UUID=&quot;8E84B2B084B29A63&quot; TYPE=&quot;ntfs&quot; PARTUUID=&quot;01022cb5-01&quot;
/dev/sdd1: LABEL=&quot;SAMSUNG&quot; BLOCK_SIZE=&quot;512&quot; UUID=&quot;1E904FEC904FC8CB&quot; TYPE=&quot;ntfs&quot; PARTUUID=&quot;d9d225e4-01&quot;
/dev/sdb1: LABEL=&quot;Elements&quot; BLOCK_SIZE=&quot;512&quot; UUID=&quot;DC74B02074AFFB80&quot; TYPE=&quot;ntfs&quot; PARTLABEL=&quot;Elements&quot; PARTUUID=&quot;ae55a0c1-17a6-41a5-bf6f-c4d86a960b7e&quot;
/dev/sda1: LABEL=&quot;SAMSUNG&quot; BLOCK_SIZE=&quot;512&quot; UUID=&quot;0C6EBE646EBE466C&quot; TYPE=&quot;ntfs&quot; PARTUUID=&quot;411e3984-01&quot;
</code></pre>
<p>or</p>
<pre><code class="language-bash">ls -l /dev/disk/by-uuid

lrwxrwxrwx 1 root root 10 Aug 11 20:28 0C6EBE646EBE466C -&gt; ../../sda1
lrwxrwxrwx 1 root root 15 Aug  9 22:07 17B6-FC00 -&gt; ../../mmcblk0p1
lrwxrwxrwx 1 root root 10 Aug  9 22:08 1E904FEC904FC8CB -&gt; ../../sdd1
lrwxrwxrwx 1 root root 10 Aug  9 22:08 8E84B2B084B29A63 -&gt; ../../sdc1
lrwxrwxrwx 1 root root 15 Aug  9 22:07 b101bb80-3338-4b94-a775-b3844f8f2aa8 -&gt; ../../mmcblk0p2
lrwxrwxrwx 1 root root 10 Aug 11 20:28 DC74B02074AFFB80 -&gt; ../../sdb1
</code></pre>
<p>for a bit more visual representation, try:</p>
<pre><code class="language-bash">lsblk

NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0   1.8T  0 disk
&#x2514;&#x2500;sda1        8:1    0   1.8T  0 part /media/pi/SAMSUNG2
sdb           8:16   0   3.6T  0 disk
&#x2514;&#x2500;sdb1        8:17   0   3.6T  0 part /media/pi/Elements
sdc           8:32   0 931.5G  0 disk
&#x2514;&#x2500;sdc1        8:33   0 931.5G  0 part /media/pi/SAMSUNG1
sdd           8:48   0 931.5G  0 disk
&#x2514;&#x2500;sdd1        8:49   0 931.5G  0 part /media/pi/SAMSUNG
mmcblk0     179:0    0  29.7G  0 disk
&#x251C;&#x2500;mmcblk0p1 179:1    0   256M  0 part /boot
&#x2514;&#x2500;mmcblk0p2 179:2    0  29.5G  0 part /
</code></pre>
<p>We&apos;re interested in <code>/dev/sda1</code>, <code>/dev/sdb1</code>, <code>/dev/sdc1</code> and <code>/dev/sdd1</code><br>
Make a note of UUID for each drive you want to use.</p>
</li>
<li>
<p>Create folders for each drive, where you&apos;ll see their contents</p>
<pre><code class="language-bash">sudo mkdir /mnt/nas1
sudo chown -R pi:pi /mnt/nas1
</code></pre>
<p>Repeat above for each drive, you&apos;re configuring.</p>
</li>
<li>
<p>Install missing dependiencies<br>
Make sure your system is able to read NTFS partisions by installing <a href="https://packages.debian.org/search?keywords=ntfs-3g">ntfs-3g package</a> It should already be there if you&apos;re using original Raspberry Pi OS. If not install it.</p>
<pre><code class="language-bash">sudo apt install ntfs-3g
</code></pre>
<p>You might need to also install Extended FAT package to read FAT partitions.</p>
<pre><code class="language-bash">sudo apt install exfat-fuse exfat-utils
</code></pre>
</li>
<li>
<p>Create a user that will access mounted drives<br>
I&apos;m creating a new user called &quot;nas&quot; for the purpose of accessing NAS resources over network.</p>
<pre><code class="language-bash">sudo useradd nas
sudo passwd nas

New password:
Retype new password:
passwd: password updated successfully
</code></pre>
<p>Find out new user&apos;s id and group id.</p>
<pre><code class="language-bash">id -u nas
id -g nas
</code></pre>
<p>In my case both returned &quot;1001&quot;.</p>
</li>
<li>
<p>Automatically mount drives<br>
Now that we have all the dependencies installed, we need to make sure our drives mount automatically after system restart.</p>
<pre><code class="language-bash">sudo vim.tiny /etc/fstab
</code></pre>
<p>which if unmodified, should look similar to this:</p>
<pre><code class="language-bash">proc            /proc           proc    defaults          0       0
PARTUUID=834cc527-01  /boot           vfat    defaults,flush    0       2
PARTUUID=834cc527-02  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that
</code></pre>
<p>Don&apos;t change any of that, just leave it as it is and add your entries below. Remember to change the values of <code>UUID</code>, <code>uid</code> and <code>gid</code> with the values we noted before.</p>
<pre><code class="language-bash"># Drive 1 - 2T
UUID=0C6EBE646EBE466C /mnt/nas1 auto nofail,uid=1001,gid=1001,noatime 0 0

# Drive 2 - 1T
UUID=1E904FEC904FC8CB /mnt/nas2 auto nofail,uid=1001,gid=1001,noatime 0 0

# Drive 3 - 1T
UUID=8E84B2B084B29A63 /mnt/nas3 auto nofail,uid=1001,gid=1001,noatime 0 0

# Drive 4 - 4T
UUID=DC74B02074AFFB80 /mnt/nas4 auto nofail,uid=1001,gid=1001,noatime 0 0
</code></pre>
<p>Your system might have automatically mounted all the drives or if you&apos;re debugging or changing something and you had your drive already mounted, you&apos;ll have to unmount it and mount it again for the changes to take effect.</p>
<pre><code class="language-bash">sudo umount /dev/sda1
sudo umount /dev/sdb1
sudo umount /dev/sdc1
sudo umount /dev/sdd1
</code></pre>
<p>Mount defined drives</p>
<pre><code class="language-bash">sudo mount -av

/proc                    : already mounted
/boot                    : already mounted
/                        : ignored
/mnt/nas1                : successfully mounted
/mnt/nas2                : successfully mounted
/mnt/nas3                : successfully mounted
/mnt/nas4                : successfully mounted
</code></pre>
<p>Now we have drives mounted that should persist over system restarts.</p>
</li>
</ol>
<h2 id="share-mounted-drives-on-local-network">Share mounted drives on local network</h2>
<ol>
<li>
<p>Install Samba<br>
Samba is a suite of applications that implements the Server Message Block (SMB) protocol. Many operating systems, including Microsoft Windows, use the SMB protocol for client-server networking. Samba enables Linux / Unix machines to communicate with Windows machines in a network, which in our case simply means sharing that mounted drive(s) over network.</p>
<pre><code class="language-bash">sudo apt install samba samba-common-bin cifs-utils
</code></pre>
</li>
<li>
<p>Create samba user<br>
Remember we created a new system user <code>nas</code>, now we need to make it a Samba user. The password we created before for that user was for RPi OS, now you can specify a different password to use when accessing your resources over network. You can keep passwords the same for ease of use.</p>
<pre><code class="language-bash">sudo smbpasswd -a nas
New SMB password:
Retype new SMB password:
Added user nas.
</code></pre>
<p>The <code>-a</code> switch adds user <code>nas</code> to the Samba password list.<br>
If you need to modify existing Samba user you can do it with:</p>
<pre><code class="language-bash">sudo smbpasswd nas
</code></pre>
<p>To list existing samba users run:</p>
<pre><code class="language-bash">sudo pdbedit -L -v
</code></pre>
</li>
<li>
<p>Configure network shareable resource<br>
Open Samba config file</p>
<pre><code class="language-bash">sudo vim.tiny /etc/samba/smb.conf
</code></pre>
<p>Go to the bottom of the file and add</p>
<pre><code class="language-bash">[nas1]
comment = My USB drive 2T
path = /mnt/nas1
# you can list multiple users here separated by space
valid users = nas
force group = users
create mask = 0660
directory mask = 0771
read only = no
# if you need unathenticated access (guest users), uncomment below line
# guest ok = yes
</code></pre>
<p>Add a section like this for each of your drives. Changing the group name <code>[nas1]</code> and <code>path</code> accordingly.<br>
Test if the config is correct</p>
<pre><code class="language-bash">testparm

Load smb config files from /etc/samba/smb.conf
Loaded services file OK.
Weak crypto is allowed
Server role: ROLE_STANDALONE
</code></pre>
<p>If everything is ok, restart Samba to apply new settings</p>
<pre><code class="language-bash">sudo systemctl restart smbd
</code></pre>
<p>In case of any issues or simply if you want to see active connections, run:</p>
<pre><code class="language-bash">sudo smbstatus
</code></pre>
</li>
</ol>
<h2 id="map-drive-on-windows">Map drive on Windows</h2>
<ol>
<li>Open file manager</li>
<li>Go to &quot;This PC&quot;</li>
<li>At the top in &quot;Computer&quot; tab, click on &quot;Map network drive&quot;, <strong>not</strong> the &quot;Add network location&quot;. There&apos;s a key difference.</li>
<li>Choose a Drive letter you want to assign to this resource.</li>
<li>In &quot;Folder&quot; field type in <code>\\RASPBERRYPI\nas1</code> or whatever location your Raspberry is at</li>
<li>Leave &quot;Reconnect at sign-in&quot; ticked.</li>
<li>Click Finish</li>
</ol>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://allurcode.com/content/images/2022/09/windows-map-drive.png" class="kg-image" alt loading="lazy" width="840" height="713"></figure><!--kg-card-begin: markdown--><p>On the credentials popup, Windows will most likely prefil your current user details. If that&apos;s the case click &quot;More choices&quot; and then &quot;Use a different account&quot;. This will allow you to specify your Samba username and password. For us the user was <code>nas</code>.</p>
<h2 id="map-drive-on-linux">Map drive on Linux</h2>
<p>If you want to access local drives on Linux (or Mac) it should be very similar to what we&apos;ve done before.</p>
<pre><code class="language-bash">sudo apt install cifs-utils
sudo mkdir /mnt/nas1
sudo mkdir /mnt/nas2
sudo mkdir /mnt/nas3
sudo mkdir /mnt/nas4
sudo vim /etc/fstab
</code></pre>
<p>Add below lines to the bottom of <code>fstab</code> file</p>
<pre><code class="language-bash">//192.168.0.100/nas1  /mnt/nas1 cifs    uid=0,username=nas,password=******,iocharset=utf8,file_mode=0777,dir_mode=0777,sec=ntlmssp  0  0
//192.168.0.100/nas2  /mnt/nas2 cifs    uid=0,username=nas,password=******,iocharset=utf8,file_mode=0777,dir_mode=0777,sec=ntlmssp  0  0
//192.168.0.100/nas3  /mnt/nas3 cifs    uid=0,username=nas,password=******,iocharset=utf8,file_mode=0777,dir_mode=0777,sec=ntlmssp  0  0
//192.168.0.100/nas4  /mnt/nas4 cifs    uid=0,username=nas,password=******,iocharset=utf8,file_mode=0777,dir_mode=0777,sec=ntlmssp  0  0
</code></pre>
<p>Remember to change the IP address to whatever your local RPi is using. The username and password should probably be stored in a separate file, but just to keep things simple, I&apos;ll leave it inline.<br>
Mount your drives</p>
<pre><code class="language-bash">sudo mount -av
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Install latest Python version on Raspberry Pi]]></title><description><![CDATA[Install latest version of Python from source on Raspberry Pi in few simple steps.
This should also work on most Linux distributions.]]></description><link>https://allurcode.com/install-latest-version-of-python-on-raspberry-pi/</link><guid isPermaLink="false">61fe5f2060a53816cf31bcd8</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 06 Feb 2022 17:33:50 GMT</pubDate><media:content url="https://allurcode.com/content/images/2022/02/rpi-python-4.png" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-image-card"><img src="https://allurcode.com/content/images/2022/02/rpi-python.png" class="kg-image" alt="Install latest Python version on Raspberry Pi" loading="lazy" width="840" height="205"></figure><!--kg-card-begin: markdown--><img src="https://allurcode.com/content/images/2022/02/rpi-python-4.png" alt="Install latest Python version on Raspberry Pi"><p>Recently I wanted to install a certain package on my Raspberry Pi that required Python 3.6 or higher. To my surprise the latest available Python version in the official Raspbian repo was 3.5.3 To install a newer one, I had to do a few more things than a simple <code>apt install python3</code>. If you&apos;re in the same boat as me, read on.</p>
<h2 id="find-already-installed-python-versions">Find already installed Python versions</h2>
<p>Multiple versions of Python can be installed at the same time. Usually you&apos;ll have both 2.x and 3.x You can easily check this by running below commands</p>
<pre><code class="language-bash">pi@raspberrypi:~ $ python -V
Python 2.7.13
pi@raspberrypi:~ $ python3 -V
Python 3.5.3
</code></pre>
<p>I want to keep Python 2.7.13 as is and only update Python 3.5.3 to the latest 3.x version.</p>
<h2 id="find-and-install-latest-available-python-version">Find and install latest available Python version</h2>
<p>Go to official Python <a href="https://www.python.org/downloads/">download page</a> and look under &quot;Looking for a specific release?&quot; section.</p>
<p><img src="https://allurcode.com/content/images/2022/02/python-releases.png" alt="Install latest Python version on Raspberry Pi" loading="lazy"></p>
<p>Click on the version you want, I chose 3.10.2. On the next page scroll down to the bottom and in &quot;Files&quot; section right click on &quot;Gzipped source tarball&quot; and choose &quot;Copy link address&quot; from the browser context menu.<br>
Go back to the command line on your Raspberry Pi</p>
<ul>
<li>Download your chosen version</li>
</ul>
<pre><code class="language-bash">wget https://www.python.org/ftp/python/3.10.2/Python-3.10.2.tgz
</code></pre>
<ul>
<li>Extract source files</li>
</ul>
<pre><code class="language-bash">tar -zxvf Python-3.10.2.tgz
</code></pre>
<ul>
<li>Go into just created directory</li>
</ul>
<pre><code class="language-bash">cd Python-3.10.2
</code></pre>
<ul>
<li>Run the configuration command</li>
</ul>
<pre><code class="language-bash">./configure --enable-optimizations
</code></pre>
<ul>
<li>Install any missing dependencies</li>
</ul>
<pre><code class="language-bash">sudo apt update
sudo apt install -y build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev libffi-dev
</code></pre>
<ul>
<li>Compile Python</li>
</ul>
<pre><code class="language-bash">sudo make altinstall
</code></pre>
<p>This can take a few minutes, so just be patient. If you&apos;ve followed all of the above steps, you should have no problems.</p>
<h2 id="make-the-new-version-default">Make the new version default</h2>
<p>Now you should have three versions of Python installed</p>
<ul>
<li>python : The default Python 2.7.13 version.</li>
<li>python3 : The default Python 3.5.3 version.</li>
<li>python3.10 : The one we installed from source.</li>
</ul>
<p>Usually Python 2 is linked to <code>python</code> command and Python 3 is linked to <code>python3</code> command. I&apos;d recommend leaving it like that and only change the <code>python3</code> link to the version we just installed.</p>
<ul>
<li>Go to /usr/bin</li>
</ul>
<pre><code class="language-bash">cd /usr/bin
</code></pre>
<ul>
<li>Remove the current link. This <strong>does not</strong> delete your older Python 3 version, it only unlinks it from that command.</li>
</ul>
<pre><code class="language-bash">sudo rm python3
</code></pre>
<ul>
<li>Link the version you want to use from now on</li>
</ul>
<pre><code class="language-bash">sudo ln -s /usr/local/bin/python3.10 python3
</code></pre>
<p>Check if everything is as expected. It should now display the version you just installed (3.10.2 for me).</p>
<pre><code class="language-bash">pi@raspberrypi:~ $ python -V
Python 2.7.13
pi@raspberrypi:~ $ python3 -V
Python 3.10.2
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Configure local website on WSL2 with PHP and nginx]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I needed to make some changes to an old PHP (Wordpress) site and wanted to quickly run it locally. I don&apos;t actively use PHP anymore therefore I didn&apos;t want to install XAMPP or anything big like this. The obvious idea was to use WSL to run</p>]]></description><link>https://allurcode.com/configure-local-website-on-wsl2-with-php-and-nginx/</link><guid isPermaLink="false">5f9db7c5dd1e18270fe58cf6</guid><category><![CDATA[WSL]]></category><category><![CDATA[nginx]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Apache]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 01 Nov 2020 15:56:32 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I needed to make some changes to an old PHP (Wordpress) site and wanted to quickly run it locally. I don&apos;t actively use PHP anymore therefore I didn&apos;t want to install XAMPP or anything big like this. The obvious idea was to use WSL to run the site. First I tried with Apache, but for some reason, despite everything being set up correctly, the site was just not loading in the Windows browser. When I accessed it within WSL it returned the correct HTML. I spent a good part of the day trying to figure it out with no success. Then I thought, screw Apache, let me try nginx and surprise, surprise it all worked just fine without a single issue.<br>
Below are all the steps I made to set up a simple dev site on WSL2 with PHP and nginx.</p>
<h2 id="configuration">Configuration</h2>
<ol>
<li>
<p>Install PHP with some common dependencies. I chose ones I needed for Wordpress. Feel free to remove or add modules as you see fit. Just make sure you install <code>php7.4-fpm</code>.</p>
<pre><code class="language-bash">sudo apt install php7.4 php7.4-cli php7.4-mysql php7.4-curl php7.4-mcrypt php7.4-json php7.4-mbstring php7.4-fpm
</code></pre>
</li>
<li>
<p>Install nginx and run the service.</p>
<pre><code class="language-bash">sudo apt install nginx
sudo service nginx start
</code></pre>
</li>
<li>
<p>Check the path for PHP socket and make a note of it.</p>
<pre><code class="language-bash">sudo grep &quot;listen =&quot; /etc/php/7.4/fpm/pool.d/www.conf
&gt; listen = /run/php/php7.4-fpm.sock
</code></pre>
</li>
<li>
<p>Make sure PHP is running.</p>
<pre><code class="language-bash">sudo service php7.4-fpm status
 * php-fpm7.4 is running

# if it&apos;s &quot;not running&quot;, start it with below command
sudo service php7.4-fpm start
</code></pre>
</li>
<li>
<p>Add nginx config for your local website by creating a config file. The <code>example.local</code> is just the domain I used, you can change it to whatever your domain will be. Just make sure that anywhere I used <code>example.local</code> you change it to your domain.</p>
<pre><code class="language-bash">sudo vim /etc/nginx/sites-available/example.local.conf
</code></pre>
<p>Your config file should look similar to below.</p>
<pre><code class="language-bash">server {
    listen 80;

    access_log /var/log/nginx/example.local-access.log;
    error_log /var/log/nginx/example.local-error.log;

    root /mnt/c/Users/kosa/workspace/example-blog;
    index index.php index.html index.htm;
    server_name example.local www.example.local;

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files      $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass   unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}
</code></pre>
<p>Please note the <code>root</code> directive points to a path on a Windows drive. Essentially it&apos;s <code>c:\Users\kosa\workspace\example-blog</code>. Also &quot;kosa&quot; in there is just my username, you should change it to yours.<br>
Check the value of <code>fastcgi_pass</code> directive and make sure it is the same as the path you got in point 3 above.</p>
</li>
<li>
<p>Symlink your newly created config file to enable your new site on nginx.</p>
<pre><code class="language-bash">sudo ln -s /etc/nginx/sites-available/example.local.conf /etc/nginx/sites-enabled/

# test new nginx config
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# if all is good, restart nginx
sudo service nginx restart
</code></pre>
</li>
<li>
<p>Last step of the whole process is to add an entry to your Windows <code>hosts</code> file.  We&apos;re talking about the Windows hosts file located in <code>c:\Windows\System32\drivers\etc\</code>. You don&apos;t need to touch the internal WSL hosts file in <code>/etc/hosts</code>.<br>
Add the below and we&apos;re done.</p>
<pre><code>127.0.0.1        example.local
</code></pre>
</li>
</ol>
<p>Now, open your favourite browser on Windows and go to <a href="http://example.local/">http://example.local/</a> You should see your website load up.</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<p>If for some reason the above process did not work for you, see if you get the correct response header from your site within WSL. While in WSL console, run:</p>
<pre><code class="language-bash">curl -I http://example.local
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 01 Nov 2020 15:20:45 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Link: &lt;http://example.local/wp-json/&gt;; rel=&quot;https://api.w.org/&quot;

# if you get &quot;200 OK&quot;, then check the actual HTML returned by the server
curl http://example.local
</code></pre>
<p>You should see the full HTML code of your site. If you don&apos;t, then it means you made a mistake somewhere in the configuration or maybe simply nginx is not running. You can also use the Linux text based browser <code>lynx</code>, to see your website in text mode.</p>
<p>Another option is to fully restart WSL. Close your WSL console, open PowerShell and run below command:</p>
<pre><code>wsl --shutdown
</code></pre>
<p>Then open WSL console again and make sure all services are running before testing in the browser.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Upgrade Raspbian from Jessie to Stretch]]></title><description><![CDATA[It's recommended to download a fresh image of Raspbian Stretch from official Raspbian website, but if for whatever reason you don't want to do that, you can still upgrade from Jessie to Stretch.]]></description><link>https://allurcode.com/upgrade-raspbian-from-jessie-to-stretch/</link><guid isPermaLink="false">5eee3275d0de6d0bbc10bc97</guid><category><![CDATA[Raspbian]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sat, 20 Jun 2020 19:40:26 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>It&apos;s recommended to download a fresh image of Raspbian Stretch from official <a href="https://www.raspberrypi.org/downloads/">Raspbian website</a>, but if for whatever reason you don&apos;t want to do that, you can still upgrade from Jessie to Stretch.</p>
<h2 id="1diskspace">1. Disk space</h2>
<p>Check available disk space and make sure you have at least 1.5GB of free space.</p>
<pre><code class="language-bash">df -h
</code></pre>
<h2 id="2updatecurrentsystem">2. Update current system</h2>
<p>Before upgrading, make sure all your packages are up to date.</p>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
</code></pre>
<h2 id="3updatepackagelists">3. Update package lists</h2>
<p>Update your package lists to use <code>Stretch</code> repositories. In both below files, change every occurrence of the word &#x2018;jessie&#x2019; to &#x2018;stretch&#x2019;.</p>
<pre><code class="language-bash">vim.tiny /etc/apt/sources.list
vim.tiny /etc/apt/sources.list.d/raspi.list
</code></pre>
<h2 id="4updatepackages">4. Update packages</h2>
<p>Be aware this may take a while, depending on your internet connection and available resources. I&apos;d say, around two hours.</p>
<pre><code class="language-bash">sudo apt-get update
#sudo apt-get upgrade
sudo apt-get dist-upgrade
</code></pre>
<p>During installation, you&apos;ll be asked to confirm changes to configuration files. I suggest pressing <code>D</code> to see differences and then following with <code>Y</code> or <code>N</code> to accept or reject changes.<br>
Especially when it comes to configuration file <code>/etc/dhcpcd.conf</code> be sure you don&apos;t loose your static IP address configuration if you had one set up.</p>
<h2 id="5cleanupafteryourself">5. Clean up after yourself</h2>
<p>Below will remove no longer needed packages and save you up some disk space. In my case it was 1GB.</p>
<pre><code class="language-bash">sudo apt-get autoremove
sudo apt-get autoclean
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Install any version of NodeJS and npm on Raspberry Pi]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Or simply put, easily switch between versions of NodeJS on any Linux based system. There are two most popular Node version managers. One, my personal favourite is <a href="https://github.com/tj/n">n package</a>, the other one is <a href="https://github.com/nvm-sh/nvm">nvm</a>.</p>
<h2 id="n">n</h2>
<p>For me the easiest way is to use <code>n</code> package. Just install whatever Node version</p>]]></description><link>https://allurcode.com/install-any-version-of-nodejs-and-npm-on-raspberry-pi/</link><guid isPermaLink="false">5b7b0aa2b52b43084c9ea14c</guid><category><![CDATA[nodejs]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[npm]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sat, 20 Jun 2020 17:51:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Or simply put, easily switch between versions of NodeJS on any Linux based system. There are two most popular Node version managers. One, my personal favourite is <a href="https://github.com/tj/n">n package</a>, the other one is <a href="https://github.com/nvm-sh/nvm">nvm</a>.</p>
<h2 id="n">n</h2>
<p>For me the easiest way is to use <code>n</code> package. Just install whatever Node version your official distribution ships with.</p>
<pre><code class="language-bash">sudo yum install nodejs
</code></pre>
<p>or, depending on what Linux you&apos;re using.</p>
<pre><code class="language-bash">sudo apt-get install nodejs
</code></pre>
<p>Then install <code>n</code> as a global package.</p>
<pre><code class="language-bash">sudo npm install -g n

/usr/local/bin/n -&gt; /usr/local/lib/node_modules/n/bin/n
+ n@6.5.1
added 1 package from 4 contributors in 0.201s
</code></pre>
<p>Done, it was that easy. Now install the version of node you want to use:</p>
<pre><code class="language-bash">sudo n lts &lt;--- latest LTS official release
sudo n latest &lt;--- latest official release
sudo n stable &lt;--- stable official release
sudo n 10.16.0 &lt;--- specific version
</code></pre>
<p>Execute <code>n</code> on its own to view your downloaded versions, and install the selected one.</p>
<pre><code class="language-bash">n

    node/8.11.2
    node/10.14.2
    node/10.15.0
    node/10.16.0
  &#x3BF; node/12.18.1

Use up/down arrow keys to select a version, return key to install, d to delete, q to quit
</code></pre>
<h2 id="nvm">nvm</h2>
<p>Run the official <a href="https://github.com/nvm-sh/nvm/blob/v0.35.3/install.sh">install script</a> by executing one of below commands:</p>
<pre><code class="language-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
</code></pre>
<pre><code class="language-bash">wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
</code></pre>
<p>Rerun Profile script to start NVM</p>
<pre><code class="language-bash">source ~/.bashrc
</code></pre>
<p>Now <code>nvm</code> should be running and you can confirm it by running below</p>
<pre><code class="language-bash">nvm

Node Version Manager (v0.35.3)
</code></pre>
<p>If everything went fine, make <code>nvm</code> use your currently installed Node version</p>
<pre><code class="language-bash">nvm use system

Now using system version of node: v8.11.2 (npm v6.9.0)
</code></pre>
<p>List available versions using</p>
<pre><code class="language-bash">nvm ls-remote
</code></pre>
<p>Install the latest release of node</p>
<pre><code class="language-bash">nvm install node &lt;--- latest version
nvm install 10.16.0 &lt;--- specific version
</code></pre>
<p>If after exiting console and logging in again <code>nvm</code> is not a recognised command, you&apos;ll need to add <code>source ~/.bashrc</code> to your <code>.profile</code> or <code>.bash_profile</code> file to make sure it&apos;s automatically run on every login. A good explanation of what&apos;s going on, can be found <a href="https://apple.stackexchange.com/questions/12993/why-doesnt-bashrc-run-automatically#comment13715_13019">here</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[VueJS pass data and methods to child component]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I couldn&apos;t find a clear explanation of how to pass both data and methods from parent component to a child component in VueJS.<br>
Remember, there&apos;s always more that one way of doing almost anything. Below is just one of those ways. More detailed, albeint no so</p>]]></description><link>https://allurcode.com/vuejs-pass-data-and-methods-to-child-component/</link><guid isPermaLink="false">5e3efc2fc6c4824630385b1d</guid><category><![CDATA[vuejs]]></category><category><![CDATA[vue]]></category><category><![CDATA[component]]></category><category><![CDATA[props]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sat, 08 Feb 2020 20:07:15 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I couldn&apos;t find a clear explanation of how to pass both data and methods from parent component to a child component in VueJS.<br>
Remember, there&apos;s always more that one way of doing almost anything. Below is just one of those ways. More detailed, albeint no so clear, explanation and all the other ways of passing props can be found in <a href="https://vuejs.org/v2/guide/components-props.html">official documentation</a>.</p>
<h2 id="parentcomponent">Parent component</h2>
<p>Let&apos;s assume you have a child component called <code>Childcomponent</code>. Passing properties down to the child component is as simple as adding <code>:prop=&quot;value&quot;</code> multiple times.<br>
You can pass a single value, array <code>:ids=&quot;[324, 12, 434]&quot;</code> or an object <code>:name=&quot;{ first: &apos;AAA&apos;, last: &apos;BBB&apos; }&quot;</code>. What was a very nice surprise for me, you can pass methods in exactly the same way. Then you can run methods from within your child component that are defined in your parent component. Super handy.</p>
<pre><code class="language-html">&lt;div id=&quot;components-demo&quot;&gt;
  &lt;Childcomponent
    :first-name=&quot;firstName&quot;
    :status=&quot;whatever.path.status&quot;
    :some-method=&quot;someMethod&quot;
    :submit=&quot;submitForm&quot; /&gt;
&lt;/div&gt;


&lt;script&gt;
import Childcomponent from &apos;~/components/Childcomponent.vue&apos;

export default {
  components: {
    Childcomponent
  },
  data() {
    return {
      firstName: &apos;whatever&apos;,
      whatever: {
        path: {
          status: true
        }
      }
    }
  },
  methods: {
    someMethod() {
      // do something
    },
    submitForm() {
      // do something else
    }
  }
}
&lt;/script&gt;
</code></pre>
<h2 id="childcomponent">Child component</h2>
<p>In child component, you need to define passed properties. Notice, in parent component prop names should use kebab case <code>first-name</code>, but when defining them in child component, use camel case <code>firstName</code>. Also methods passed from parent need to be defined in the child as a type <code>Function</code>.<br>
Then, you can use your data and methods (from parent) in the child component just as you&apos;d do it normally.</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;div&gt;
    &lt;div class=&quot;col&quot;&gt;
      &lt;img :src=&quot;loadImage(status)&quot; alt=&quot;alt text&quot;&gt;
    &lt;/div&gt;
    &lt;button
      class=&quot;btn btn-sm btn-outline-light&quot;
      @click=&quot;submit&quot;&gt;
      Submit
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  name: &apos;Childcomponent&apos;,
  props: {
    firstName: {
      type: String,
      default() {
        return &apos;My name&apos;
      }
    },
    status: {
      type: Boolean,
      default() {
        return false
      }
    },
    loadImage: {
      type: Function,
      default() {
        return {}
      }
    },
    submit: {
      type: Function,
      default() {
        return {}
      }
    }
  }
}
&lt;/script&gt;
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Check laptop battery health on Windows 10]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>It&apos;s good practice to occasionally check the health of your laptop battery. There&apos;s a very easy way to do so on Windows 10. Of course there are lots of programs that will give you detailed information on your battery, but the built in command will also</p>]]></description><link>https://allurcode.com/check-laptop-battery-health-on-windows-10/</link><guid isPermaLink="false">5d6f81684a8e0f6b3f14f603</guid><category><![CDATA[Windows]]></category><category><![CDATA[battery]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Wed, 04 Sep 2019 10:53:55 GMT</pubDate><media:content url="https://allurcode.com/content/images/2019/09/Screenshot_24a.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://allurcode.com/content/images/2019/09/Screenshot_24a.png" alt="Check laptop battery health on Windows 10"><p>It&apos;s good practice to occasionally check the health of your laptop battery. There&apos;s a very easy way to do so on Windows 10. Of course there are lots of programs that will give you detailed information on your battery, but the built in command will also give you quite nice report.</p>
<ol>
<li>Open command line<br>
Press the Windows Key, then just type &quot;cmd&quot;. The top result should be &quot;Command Prompt&quot; app. Click on it.</li>
<li>Generate the report<br>
Run below command in the newly opened Command Prompt to generate a HTML file with detailed report<pre><code class="language-powershell">powercfg /batteryreport
</code></pre>
You should see output similar to this<pre><code class="language-powershell">powercfg /batteryreport
Battery life report saved to file path C:\Users\your.user\current\path\battery-report.html.
</code></pre>
If you want to save the report to a specific location, add the <code>output</code> parameter as below.<pre><code class="language-powershell">powercfg /batteryreport /output &quot;C:\custom\path\battery-report.html
</code></pre>
</li>
<li>View the report<br>
Just open your prefered browser and paste the output URL into the address bar, or find the file and double click on it to open it in the browser. You should see something similar to below screenshot.<br>
<img src="https://allurcode.com/content/images/2019/09/Screenshot_24.png" alt="Check laptop battery health on Windows 10" loading="lazy"><br>
What you&apos;re really looking for is &quot;Installed batteries&quot; section. With a new battery the &quot;DESIGN CAPACITY&quot; and &quot;FULL CHARGE CAPACITY&quot; should be the same. The older the battery, the bigger difference between those two numbers.</li>
</ol>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Custom linting rules in NuxtJS and eslint]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://nuxtjs.org/">NuxtJS</a> is a fantastic framework for creating <a href="https://vuejs.org/">VueJS</a> applications. It comes bundled with Vue Router, Vuex, Vue Server Renderer and many more. All configured for you out of the box.<br>
The problem I have with it, it comes with linting options set to force 2 spaces as indentation. I&apos;</p>]]></description><link>https://allurcode.com/custom-linting-rules-in-nuxtjs-and-eslint/</link><guid isPermaLink="false">5c62dae2a8a0ce0692c7bfc6</guid><category><![CDATA[nuxtjs]]></category><category><![CDATA[vuejs]]></category><category><![CDATA[eslint]]></category><category><![CDATA[chkconfig]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 07 Jul 2019 17:23:01 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://nuxtjs.org/">NuxtJS</a> is a fantastic framework for creating <a href="https://vuejs.org/">VueJS</a> applications. It comes bundled with Vue Router, Vuex, Vue Server Renderer and many more. All configured for you out of the box.<br>
The problem I have with it, it comes with linting options set to force 2 spaces as indentation. I&apos;m a &quot;tab&quot; guy, so making me use spaces for indentation drove me crazy. It took me a while to make eslint understand I want different rules in my project. Below is a version of <code>.eslintrc.js</code> file that worked for me.</p>
<p>Obviously, I assume you&apos;ve selected eslint while installing NuxtJS, so you have all the necessary modules installed. Below config has been tested with (at the time of writing this post) latest version of NuxtJS 2.8.1</p>
<pre><code class="language-javascript">module.exports = {
	root: true,
	env: {
		browser: true,
		node: true
	},
	parserOptions: {
		parser: &apos;babel-eslint&apos;
	},
	extends: [
		&apos;@nuxtjs&apos;,
		&apos;plugin:nuxt/recommended&apos;
	],
	// add your custom rules here
	rules: {
		&apos;nuxt/no-cjs-in-config&apos;: &apos;off&apos;,
		&apos;no-console&apos;: process.env.NODE_ENV === &apos;production&apos; ? &apos;error&apos; : &apos;off&apos;,
		&apos;no-debugger&apos;: process.env.NODE_ENV === &apos;production&apos; ? &apos;error&apos; : &apos;off&apos;,
		&apos;vue/html-indent&apos;: [&apos;error&apos;, &apos;tab&apos;],
		&apos;vue/html-closing-bracket-newline&apos;: &apos;off&apos;,
		&apos;indent&apos;: [2, &apos;tab&apos;],
		&apos;no-tabs&apos;: &apos;off&apos;
	}
}
</code></pre>
<p>Go to official <a href="https://eslint.vuejs.org/rules/">eslint VueJS plugin page</a> for documentation on other rules that might suite your taste.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Prevent image hotlinking / stealing from your site with nginx]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Have you noticed other sites hotlinking to your images or other assets? Are they eating up your bandtwitch? Or you just want to proactively prevent that? If so, there&apos;s a very easy way to do so with a simple configuration on nginx.<br>
Open up nginx config file for</p>]]></description><link>https://allurcode.com/prevent-image-hotlinking-stealing-from-your-site-with-nginx/</link><guid isPermaLink="false">5d2217ad1e346910a0144eb4</guid><category><![CDATA[nginx]]></category><category><![CDATA[image]]></category><category><![CDATA[hotlink]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 07 Jul 2019 16:59:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Have you noticed other sites hotlinking to your images or other assets? Are they eating up your bandtwitch? Or you just want to proactively prevent that? If so, there&apos;s a very easy way to do so with a simple configuration on nginx.<br>
Open up nginx config file for your site and add another <code>location</code> block like this:</p>
<pre><code class="language-nginx">server {
    # your normal site configuration is here

    location ~* \.(gif|png|jpe?g)$ {
        valid_referers none blocked server_names ~\.google\. ~\.bing\. ~\.yahoo\. ~\.facebook\. ~\.yoursite.com;
        if ($invalid_referer) {
            return 403;
        }
    }
}
</code></pre>
<p>With above, we&apos;re sending <code>403 Forbidden error</code> status code whenver an image is requested from a referer that is not in our approved list.<br>
<code>valid_referers</code> lists sites which we want to allow hotlinking our images. Remember to change <code>~\.yoursite.com</code> to your actual site domain.<br>
If you want to block other file types, just add an extra pipe &quot;|&quot; and file extension, like this <code>(gif|png|jpe?g|bmp|tiff|pdf)</code></p>
<p>Going one step further, you can serve one specific image every time a not approved sites tries to hotlink to your assets. To do that add below config:</p>
<pre><code class="language-nginx">server {
    # your normal site configuration is here

    location ~* \.(gif|png|jpe?g)$ {
        valid_referers none blocked server_names ~\.google\. ~\.bing\. ~\.yahoo\. ~\.facebook\. ~\.yoursite.com;
        if ($invalid_referer) {
            rewrite (.*) /path/to/image/hotlinking-denied.jpg redirect;
        }
    }
    # prevent redirect loop
    location = /path/to/image/hotlinking-denied.jpg { }
}
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Using nginx redirect www to non-www domain]]></title><description><![CDATA[To redirect all traffic from www.example.com to just plain example.com, add another `server {}` block to your nginx config file with just two lines:
server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}]]></description><link>https://allurcode.com/using-nginx-redirect-www-to-non-www-domain/</link><guid isPermaLink="false">5d22057c1e346910a0144dee</guid><category><![CDATA[nginx]]></category><category><![CDATA[redirect]]></category><category><![CDATA[domain]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sun, 07 Jul 2019 15:40:05 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Having your site configured on nginx with</p>
<pre><code class="language-nginx">server_name example.com www.example.com;
</code></pre>
<p>is a good thing as it&apos;ll allow your visitors to access your site on both <code>example.com</code> and <code>www.example.com</code>, but they&apos;ll stay on the version of the domain they used to access your site in the first place. In your analytics, you may start seeing traffic separated into <code>example.com</code> and <code>www.example.com</code> which definitely does not help you get a clear view of your visitors. It&apos;s good practice to decide on one version of the domain and stick to it. I prefer the non-www version, but it&apos;s entirely up to you.</p>
<h3 id="redirectwwwtononwww">Redirect www to non-www</h3>
<p>To redirect all traffic from <code>www.example.com</code> to just plain <code>example.com</code>, add another <code>server {}</code> block to your nginx config file with just two lines:</p>
<pre><code class="language-nginx"># new block
server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}
# your normal config
server {
    ...
    server_name example.com; # notice, this has just the non-www version
    ...
}
</code></pre>
<p>Restart nginx to apply above changes</p>
<pre><code class="language-bash">sudo systemctl restart nginx
</code></pre>
<h3 id="redirectnonwwwtowww">Redirect non-www to www</h3>
<p>To redirect your users from non-www to a www version of your domain add below:</p>
<pre><code class="language-nginx"># new block
server {
    server_name example.com;
    return 301 $scheme://www.example.com$request_uri;
}
# your normal config
server {
    ...
    server_name www.example.com;
    ...
}
</code></pre>
<p>Restart nginx to apply above changes</p>
<pre><code class="language-bash">sudo systemctl restart nginx
</code></pre>
<h3 id="conclusion">Conclusion</h3>
<p>That&apos;s it. Your users will now be able to access your site on both www and non-www versions and will be permanently redirected to one version you chose.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Configure Mailgun on self hosted Ghost blog]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://www.mailgun.com/">Mailgun</a> is a fantastic, <strong>free</strong> service which allows you to send, receive and track emails effortlessly. It&apos;s dead easy to integrate with your self hosted Ghost blog.</p>
<h3 id="mailgunconfiguration">Mailgun configuration</h3>
<ol>
<li>Log in to your Mailgun account.</li>
<li>Go to &quot;Domains&quot; section.</li>
<li>Click on &quot;Add New Domain&quot;</li></ol>]]></description><link>https://allurcode.com/configure-mailgun-on-self-hosted-ghost-blog/</link><guid isPermaLink="false">5c962968446ec0732dae7599</guid><category><![CDATA[mailgun]]></category><category><![CDATA[ghost]]></category><category><![CDATA[email]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sat, 23 Mar 2019 14:36:02 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://www.mailgun.com/">Mailgun</a> is a fantastic, <strong>free</strong> service which allows you to send, receive and track emails effortlessly. It&apos;s dead easy to integrate with your self hosted Ghost blog.</p>
<h3 id="mailgunconfiguration">Mailgun configuration</h3>
<ol>
<li>Log in to your Mailgun account.</li>
<li>Go to &quot;Domains&quot; section.</li>
<li>Click on &quot;Add New Domain&quot; and add a subdomain you want to send emails from.</li>
<li>Follow on page instructions to add all the DNS entries.</li>
<li>Once DNS changes propagate, verify your domain within Mailgun.</li>
</ol>
<h3 id="ghostconfiguration">Ghost configuration</h3>
<ol>
<li>SSH to your server and edit <code>config.production.json</code> file in your Ghost root directory.<br>
Your &quot;mail&quot; section should look like this:<pre><code class="language-javascript">  &quot;mail&quot;: {
    &quot;transport&quot;: &quot;SMTP&quot;,
    &quot;options&quot;: {
      &quot;service&quot;: &quot;Mailgun&quot;,
      &quot;host&quot;: &quot;smtp.eu.mailgun.org&quot;,
      &quot;auth&quot;: {
        &quot;user&quot;: &quot;postmaster@your_domain.com&quot;,
        &quot;pass&quot;: &quot;1234567890&quot;
      }
    }
  }
</code></pre>
<strong>Notice</strong> <code>host</code> entry within <code>options</code>. Official Ghost documentation does not mention it. If your Mailgun domain is set up in the US region, you don&apos;t need that line. However, if you&apos;ve set up your domain in the EU region, you&apos;ll need to add it in. Otherwise you&apos;ll get an error that your username and password were not recognised.</li>
<li>Save changes and restart Ghost.<pre><code class="language-bash">ghost restart
</code></pre>
</li>
<li>Log in to your Ghost admin.</li>
<li>Go to &quot;Labs&quot;.</li>
<li>Click &quot;Send&quot; in &quot;Test email configuration&quot; section.<br>
The &quot;Send&quot; button should turn green and you should receive a test email confirming your configuration works.</li>
</ol>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Update Raspbian on Raspberry Pi]]></title><description><![CDATA[Update your Raspbian by running
sudo apt-get update
then upgrade packages with
sudo apt-get dist-upgrade]]></description><link>https://allurcode.com/update-raspbian-on-raspberry-pi/</link><guid isPermaLink="false">5c798977a8a0ce0692c7bfdf</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[Raspbian]]></category><category><![CDATA[update]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Fri, 01 Mar 2019 20:09:11 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>It&apos;s highly recommended to update your Raspbian as often as possible to make sure you always have the latest security patches and updates.</p>
<h3 id="manualupdate">Manual update</h3>
<p>Open Pi&apos;s console and update package list by running the following command:</p>
<pre><code class="language-bash">sudo apt update
</code></pre>
<p>Then upgrade your packages to their latest versions with below command:</p>
<pre><code class="language-bash">sudo apt full-upgrade
</code></pre>
<p>This should keep you up to date and away from trouble.</p>
<h3 id="automaticupdates">Automatic updates</h3>
<p>If you don&apos;t like to this this manually, or simply forget about it, I suggest you set up an automatic update and never have to think about it again.<br>
There&apos;s a number of ways you can do that. There are programs that take care of that for you and have lots of options and configurations. I&apos;ll focus here on the easiest and most basic option, which is a simple cron job.<br>
Switch to root and open crontab:</p>
<pre><code class="language-bash">sudo su
crontab -e
</code></pre>
<p>If this is the first time you run crontab as root, you&apos;ll be asked to choose your preffered editor.</p>
<pre><code class="language-bash">root@raspberrypi:/home/pi# crontab -e
no crontab for root - using an empty one

Select an editor.  To change later, run &apos;select-editor&apos;.
  1. /bin/ed
  2. /bin/nano
  3. /usr/bin/mcedit
  4. /usr/bin/vim.tiny  &lt;---- my favourite

Choose 1-4 [4]:
</code></pre>
<p>Once you choose your editor, enter below line at the bottom of your crontab:</p>
<pre><code class="language-bash">0 4 * * 1 apt update &amp;&amp; apt -y full-upgrade
</code></pre>
<p>Above will run update every Monday at 4am. If you prefer a different time but don&apos;t know how cron settings work, <a href="https://crontab.guru/">here&apos;s</a> a very helpful site.</p>
<!--kg-card-end: markdown--><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Create NodeJS command line script or app]]></title><description><![CDATA[Learn an extremely simple way of creating command line scripts or apps using NodeJS]]></description><link>https://allurcode.com/create-nodejs-command-line-script-or-app/</link><guid isPermaLink="false">5bc8884aa0c66e55d1ddf987</guid><category><![CDATA[nodejs]]></category><category><![CDATA[shell]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[script]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Sat, 15 Dec 2018 17:51:32 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I wanted to write a simple script that would convert multiple files from one encoding to another. I didn&apos;t want anything big or running in the browser. Command line script seemed like the right approach. Coming from a web developer background my first thought was a script written in PHP and converted to a command line app. Just saying that sounds awful. After a bit of googling, it turned out NodeJS is surprisingly easy and straightforward to write command line apps.</p>
<h3 id="initiatetheapp">Initiate the app</h3>
<p>Running <code>npm init</code> will ask you a few basic questions about your app and result in creating <code>package.json</code> file.</p>
<pre><code>$ npm init

name: cldemo
version: 0.0.1
description: Simple command line script
entry point: index.js
test command:
git repository:
keywords:
author: SixBytesUnder
license: (ISC)
</code></pre>
<p>Edit the newly created <code>package.json</code> and add &quot;bin&quot; section. The property key will be the command you&apos;ll type in to run your app. In our case <code>cldemo</code>. The value is a relative path to the entry point file. That&apos;s the file our code will reside in.</p>
<pre><code class="language-javascript">...
&quot;author&quot;: &quot;SixBytesUnder&quot;,
&quot;license&quot;: &quot;ISC&quot;,
&quot;bin&quot;: {
  &quot;cldemo&quot;: &quot;./index.js&quot;
}
</code></pre>
<p>Next, create <code>index.js</code> file or any other file you have provided as an &quot;entry point&quot; above. Paste example code show below.</p>
<pre><code class="language-javascript">#!/usr/bin/env node
&apos;use strict&apos;;

// your whole code goes here
console.log(&apos;Hello World!&apos;)
</code></pre>
<p>The most basic command line script is done.</p>
<h3 id="registeryourscript">Register your script</h3>
<p>Next command will register your app as a shell command. <strong>Note</strong>, on Windows you need to open command prompt as an admin, navigate to the home directory of your app and run below.</p>
<pre><code>$ npm install -g
</code></pre>
<p>That&apos;s it, it&apos;s a simple as that. Now you should be able to run your script from command line globally.</p>
<pre><code>$ cldemo
Hello World!
</code></pre>
<p>If you make any changes to your source files, remember to register your app again, so that latest version is copied to the global path.</p>
<p>To uninstall your script just type:</p>
<pre><code>$ npm uninstall -g cldemo
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Add reCAPTCHA to NodeJS and ExpressJS app with AJAX form]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Google&apos;s <a href="https://www.google.com/recaptcha/">reCAPTCHA</a> is great for getting rid of those pesky bots spamming your contact forms. Implementing reCAPTCHA on your site is very easy, albeit somewhat confusing if you try to follow <a href="https://developers.google.com/recaptcha/intro">official documentation</a>.<br>
Below example assumes you&apos;re using NodeJS with ExpressJS for back-end and Pug (formerly</p>]]></description><link>https://allurcode.com/add-recaptcha-to-nodejs-and-expressjs-app-with-ajax-form/</link><guid isPermaLink="false">5ba8b26ed282fe76158958ec</guid><category><![CDATA[reCAPTCHA]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[expressjs]]></category><category><![CDATA[Google]]></category><dc:creator><![CDATA[Wojtek]]></dc:creator><pubDate>Mon, 08 Oct 2018 11:01:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Google&apos;s <a href="https://www.google.com/recaptcha/">reCAPTCHA</a> is great for getting rid of those pesky bots spamming your contact forms. Implementing reCAPTCHA on your site is very easy, albeit somewhat confusing if you try to follow <a href="https://developers.google.com/recaptcha/intro">official documentation</a>.<br>
Below example assumes you&apos;re using NodeJS with ExpressJS for back-end and Pug (formerly Jade) templating engine on the front-end. General principle is the same regardless of languages used.<br>
I&apos;ll assume you&apos;ve already set up your reCAPTCHA API keys. If you haven&apos;t just go to <a href="https://www.google.com/recaptcha/">https://www.google.com/recaptcha/</a> and follow onscreen instructions.</p>
<h3 id="templatefiles">Template files</h3>
<p>Open up your template file where you want to use reCAPTCHA, in my case <code>index.pug</code>. Add reference to reCAPTCHA JavaScript file as mentioned in official documentation.</p>
<pre><code class="language-pug">script(src=&apos;https://www.google.com/recaptcha/api.js&apos;)
</code></pre>
<p>Then copy the submit button code from official docs and paste it in your template, or in case of Pug, make it similar to below.</p>
<pre><code class="language-pug">button.btn.btn-default.g-recaptcha(data-sitekey=&quot;your_site_key&quot;, data-callback=&quot;contactSubmit&quot;) Submit
</code></pre>
<p>Remember to change <code>your_site_key</code> to your actual Site Key. If you&apos;re copying and pasting code from reCAPTCHA website, it should have your key already filled in.</p>
<h3 id="javascripttosubmittheform">JavaScript to submit the form</h3>
<p>Notice <code>data-callback=&quot;contactSubmit&quot;</code> in the above code for the button. Copying code for the button directly from documentation may have a different callback function name. I called mine <code>contactSubmit</code>.<br>
All you need to do is create a function in your JS file to handle form submission, making sure you pass the token value to it as an agrument and then pass it down the line to the server. Below is a pretty standard AJAX form submission example. I kept it to minimum to illustrate just the reCAPTCHA variable.</p>
<pre><code class="language-javascript">function contactSubmit(token) {
	var name = $(&apos;#contactForm #name&apos;),
		email = $(&apos;#contactForm #email&apos;),
		message = $(&apos;#contactForm #message&apos;);
	
	// check if the form fields are not empty
	if (name.val() !== &apos;&apos; &amp;&amp; email.val() !== &apos;&apos; &amp;&amp; message.val() !== &apos;&apos;) {
		$.ajax({
			type: &apos;POST&apos;,
			url: &apos;/handlecontact&apos;,
			data: {
				name: name.val(),
				email: email.val(),
				message: message.val(),
				recaptcha: token
			},
			dataType: &apos;json&apos;,
			error: function (data) {
				$(&apos;#contactForm #formFeedback&apos;)
                    .removeClass()
                    .addClass(&apos;alert alert-danger&apos;)
                    .html(&apos;Something went wrong, please try again&apos;)
                    .fadeIn(&apos;slow&apos;)
                    .delay(10000)
                    .fadeOut(&apos;slow&apos;);
				return false;
			},
			success : function (data, res) {
				if (data.success === true) {
					$(&apos;#contactForm #formFeedback&apos;)
                        .removeClass()
                        .addClass(&apos;alert alert-success&apos;)
                        .html(data.msg)
                        .fadeIn(&apos;slow&apos;)
                        .delay(5000)
                        .fadeOut(&apos;slow&apos;);
					// clean up form fields
					name.val(&apos;&apos;);
					email.val(&apos;&apos;);
					message.val(&apos;&apos;);
					return true;
				} else {
					$(&apos;#contactForm #formFeedback&apos;)
                        .removeClass()
                        .addClass(&apos;alert alert-danger&apos;)
                        .html(data.msg)
                        .fadeIn(&apos;slow&apos;)
                        .delay(10000)
                        .fadeOut(&apos;slow&apos;);
					return false;
				}
			}
		});
	} else {
		$(&apos;#contactForm #formFeedback&apos;)
            .removeClass()
            .addClass(&apos;alert alert-warning&apos;)
            .html(&apos;Please fill in all fields&apos;)
            .fadeIn(&apos;slow&apos;)
            .delay(10000)
            .fadeOut(&apos;slow&apos;);
		return false;
	}
}
</code></pre>
<h3 id="expressjsserverside">ExpressJS - server side</h3>
<p>Open ExpressJS file in which you handle your form data on the server side. In my case it&apos;s <code>index.js</code>, and add <code>https</code> module at the top to handle captcha verification request.</p>
<pre><code class="language-javascript">var https = require(&apos;https&apos;);
</code></pre>
<p>Then in the method handling your form data verify if user has passed captcha verification.<br>
All you really need to do is send the reCAPTCHA token back to Google for verification. That&apos;s what we need https module for.</p>
<pre><code class="language-javascript">app.post(&apos;/handlecontact&apos;, function (req, res) {
	// verify recaptcha
	if (req.body.recaptcha === undefined || req.body.recaptcha === &apos;&apos; || req.body.recaptcha === null) {
		res.send({success: false, msg: &apos;Please select captcha first&apos;});
		return;
	}
	const secretKey = &apos;your_secret_key&apos;;
	const verificationURL = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&amp;response=${req.body.recaptcha}&amp;remoteip=${req.connection.remoteAddress}`;
	
	https.get(verificationURL, (resG) =&gt; {
		let rawData = &apos;&apos;;
		resG.on(&apos;data&apos;, (chunk) =&gt; { rawData += chunk })
		resG.on(&apos;end&apos;, function() {
			try {
				var parsedData = JSON.parse(rawData);
				if (parsedData.success === true) {
					// All good, send contact email or perform other actions that required successful validation
					res.send({success: true, msg: &apos;Your message has been sent. Thank you.&apos;});
					return;
				} else {
					res.send({success: false, msg: &apos;Failed captcha verification&apos;});
					return;
				}
			} catch (e) {
				res.send({success: false, msg: &apos;Failed captcha verification from Google&apos;});
				return;
			}
		});
	});
});
</code></pre>
<p>When testing reCAPTCHA make sure you refresh the page between &quot;submit&quot; button clicks. Otherwise Google may think you&apos;re a bot for clicking too many times and not submit the form at all.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>