<?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"><channel><title><![CDATA[Theraloss.com - Sharing what I didn't know to know]]></title><description><![CDATA[Tips and tutorial from a Senior Software Engineer with more than 10 years of experience in PHP, looking to learn new stuff everyday.]]></description><link>https://theraloss.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1689194718179/fZixEmFpn.png</url><title>Theraloss.com - Sharing what I didn&apos;t know to know</title><link>https://theraloss.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 08:36:50 GMT</lastBuildDate><atom:link href="https://theraloss.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[RNEC Quest 1: The realm]]></title><description><![CDATA[Let's start with an important introduction: I always used Ionic with Vue for my mobile apps, but you, as everyone should do, must choose the technology based on your needs and, eventually, admit that what you used is not the best deal.
This app is a ...]]></description><link>https://theraloss.com/react-native-expo-chronicles</link><guid isPermaLink="true">https://theraloss.com/react-native-expo-chronicles</guid><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Expo]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Wed, 12 Jul 2023 20:53:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689195428268/8999894c-1d1a-457b-976f-540575091e34.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's start with an important introduction: I always used <strong>Ionic with Vue</strong> for my mobile apps, but you, as everyone should do, <mark>must choose the technology based on your needs</mark> and, eventually, admit that what you used is not the best deal.</p>
<p>This app is a PoC for a personal area mobile app to understand if React Native is, or is not, the right path to follow.</p>
<p>What I need is:</p>
<ul>
<li><p><strong>Authentication</strong> via OAuth2, in my company we use FusionAuth, similar to Auth0</p>
</li>
<li><p><strong>Secure storage</strong> to save the user auth and refresh token</p>
</li>
<li><p>Simple to interact with <strong>NFC</strong></p>
</li>
<li><p>Easy to build and deploy</p>
</li>
<li><p>Customizable and extendable UI to match our design and identity</p>
</li>
<li><p>As a plus, accessible to everyone with frontend or full stack knowledge.</p>
</li>
</ul>
<h3 id="heading-ionic-vue">👎Ionic + Vue</h3>
<p>Since Vue and Nuxt are the main frontend stack in my company, it would make sense to go with Ionic. I didn't need outstanding performance (and, to give a pitch Ionic, nowadays it's really fast), but I later found out that the only two required points I have, authorization and secure storage, are part of a premium package that costs... <strong>2500$ per month</strong>.</p>
<p>You could achieve a similar product with open-source packages, such as <a target="_blank" href="https://github.com/martinkasa/capacitor-secure-storage-plugin">Capacitor Secure Storage</a> and <a target="_blank" href="https://github.com/moberwasserlechner/capacitor-oauth2">Capacitor OAuth2</a>, but they seem to have a few issues on Android and you would create a Frankenstein. Also, a lot capacitor/ionic/cordova plugins are likely to be outdated.</p>
<p><strong>Pros</strong>:</p>
<ul>
<li><p>Available for Vue (but also React and Angular)</p>
</li>
<li><p>Beautiful UI out-of-the-box and easy to use</p>
</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li><p>No official (free) support for OAuth and secure storage</p>
</li>
<li><p>There are community-driven libraries, but likely to be outdated</p>
</li>
</ul>
<h3 id="heading-flutter">🤷Flutter</h3>
<p>You can't talk of mobile development nowadays without talking about Flutter. It's a great product, with a lot of different plugins to reach my needs, but I don't know anything about Dart (<em>Google, why yet another language?</em>) and, although I'm willing to learn it, it's an unnecessary overhead for an app (for me).</p>
<p><strong>Pros</strong>:</p>
<ul>
<li><p>Stable, powerful and supported by Google</p>
</li>
<li><p>Trusted by many big companies</p>
</li>
<li><p>Great community with a lot of solutions</p>
</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li><p>Dart: I don't know it, and for now I'm not interested in investing in learning it</p>
</li>
<li><p>How many people know Dart? The eventual hiring would be <em>hard</em></p>
</li>
</ul>
<h3 id="heading-react-native-expo">👍React Native + Expo</h3>
<p>Honestly, I never really liked React, but I think React Native is one of the most robust yet versatile solutions out there to build mobile apps; since it's React-based it's also really accessible to whoever has front-end knowledge, that's why we are here 😄</p>
<p>In the last few months on Twitter I came across <strong>Expo</strong> - not knowing what it was - and, although bare React Native would be enough, Expo seems to be the current standard and almost required for modern development.</p>
<p>I quickly went through their (beautiful) docs and found out about <strong>routing</strong> (we're going to talk more about this in a future post), but most importantly about authorization via <strong>OAuth2</strong>, <strong>secure storage</strong>, and how these topics integrate into the navigation flow. Basically, everything we need!</p>
<p>I acknowledged lately that in the past years, Expo was not <em>so much</em> liked, because of several issues and missing parts, but I joined the "Expo movement" right when they released the SDK 49 beta (and then the stable release) and, looking on <mark>how they precisely worked and how fast the answered to community</mark> issues and discussions on Discord and Github, I've fallen in love.</p>
<p><strong>Pros</strong>:</p>
<ul>
<li><p>Stable, powerful and supported by Facebook (well, the last commit on React is a year ago, but...)</p>
</li>
<li><p>Trusted by many big companies (Expo is even being <a target="_blank" href="https://twitter.com/Baconbrix/status/1677375995752620032">used by Tesla</a>), among with Facebook (and their products, Instagram etc.) of course</p>
</li>
<li><p>Great community with tons of packages, since they come from React</p>
</li>
<li><p>Easy to run locally, to build and update live through EAS (cloud-hosted services)</p>
</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li><p>Expo Router, although reached v2 stable, still lacks some features (such as customizable linking from deep linking, which is possible on bare React Navigation)</p>
</li>
<li><p>Expo documentation is really great, but a bit messy when talking about EAS different builds, how to build locally etc. (I found myself lost multiple times asking: how the hell can I build locally the app?)</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How Ionic and Directus helped me start volunteering]]></title><description><![CDATA[A few weeks ago I finally decided to start volunteering and I found a beautiful cat colony in a cemetery. Well, it's not a common cemetery, it's the "Cimitero Monumentale" in Milan, Italy, a beautiful mix of graves and architecture.

There was only o...]]></description><link>https://theraloss.com/how-ionic-directus-helped-volunteering</link><guid isPermaLink="true">https://theraloss.com/how-ionic-directus-helped-volunteering</guid><category><![CDATA[Ionic Framework]]></category><category><![CDATA[directus]]></category><category><![CDATA[Just chatting]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Thu, 04 Nov 2021 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689192017330/d348a725-461f-414b-80d7-8cf3695d700d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago I finally decided to start volunteering and I found a beautiful cat colony in a cemetery. Well, it's not a common cemetery, it's the "Cimitero Monumentale" in Milan, Italy, a beautiful mix of graves and architecture.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812750641/ef039e8b-b198-41aa-b418-ef749727cd9b.jpeg" alt class="image--center mx-auto" /></p>
<p>There was only one, big, issue: the cat colony is <strong>HUGE</strong>. Really. There are <strong>more than 20 cats</strong> scattered over <strong>250,000 m2</strong> (2,690,977 ft2, thank you Google). They are grouped in about 10 different spots (they've chosen their spot, of course) and going to feed every cat in there makes you <strong>walk about 2.5km</strong> (1.55 mi, thank you Google again). Furthermore, I have literally 0 sense of direction and that was the biggest pain at the beginning, that's why I decided to solve my problem, and hopefully for future volunteers too, with the help of the technology (and Open Source).</p>
<h2 id="heading-ionic-app">Ionic App</h2>
<p>As I said earlier there's this big area and I <strong>wanted to see the spots where the cats are</strong>. In the beginning I decided to go with a flat image of the cemetery and map with x,y the points in it. I don't know why, don't ask. In the end, luckily, I came to my senses and opted for a real, interactive map, currently using <strong>Mapbox</strong>.</p>
<p>For the app, I had no doubts: <strong>Ionic with Vue</strong>. I saw that there's an upcoming v6, currently in beta, and decided to seize the opportunity and start with that. I also installed <strong>Tailwind</strong> with JIT, because... Well, it's Tailwind, the quickest way to style your code. Luckily Ionic v6 upgraded Webpack with PostCSS 8 and the installation of Tailwind was incredibly fast.</p>
<p>After installing node modules, and digging a bit into the Vue integration (that's super easy btw, huge work by the Ionic team) I finally managed to restrict the map to the given coordinates, create custom markers and add them into the map. When you click a marker, a <a target="_blank" href="https://beta.ionicframework.com/docs/api/modal#sheet-modals-4">sheet modal</a> will pop up with the cats at that point.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812829402/7648c6b8-86f6-415c-b72b-80777cf2beac.gif" alt class="image--center mx-auto" /></p>
<p>As you can see, when you click one or more cat the <strong>marker changes color</strong>: it helps me to keep track of where I went and what I already did. The pins by default are red, they become green when <em>all the cats</em> are checked or, eventually, will be yellow if a few cats, but not everyone, of a point are marked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812851865/0a188da9-f3b8-4a35-95c2-f8135467cadc.gif" alt class="image--center mx-auto" /></p>
<p>And, finally, what's the best solution to orientate yourself? A <strong>live GPS</strong>! Thanks to Ionic Native (well, I should say Capacitor, actually) this was super easy as well. It's a great resource to know where I am and where's the closest point to visit.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812877648/8055ca57-9d44-47df-bbc3-8a95c1d5c449.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-directus-backend-and-cms">Directus Backend and CMS</h2>
<p>For what I needed the final result of the app is great, but obviously, it has to fetch the data (cats, point coordinates etc.) somewhere. I was really undecided, but I wanted a headless solution to reduce the server costs and work to manage it (I should say that <em>Ploi</em> does that for me) and to also deploy the frontend to Vercel or Netlify. And, of course, it must have a <em>beautiful and intuitive UI and UX</em>.</p>
<p>I had four choices in my head: <em>Statamic</em>, a great headless CMS built in Laravel, for Laravel with Static Site Generation, but it was too expensive; <em>GraphCMS</em>, beautiful, but expose only GraphQL APIs (and I don't really like it) plus one or two more things that I didn't like of their CMS; <em>Strapi</em>, a Node headless CMS, pretty young but very popular; and, finally, <strong>Directus</strong>, a no-code headless CMS in Node. Because I already knew Strapi, I had a few company projects with it and I know how it works, I decided to try Directus and that has been the best choice.</p>
<p>I have two collections: <strong>Cats</strong>, where I store all the colony pets with their photo, name and some other information and <strong>Points</strong> to build the markers with latitude, longitude and a Many-to-Many field to connect the cats.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812920122/478cf1fd-6a5c-400e-9165-1ba5e6b0a16b.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812928206/76206367-0a07-4496-a8f1-301ce8b15cd8.png" alt class="image--center mx-auto" /></p>
<p>Do you know how many lines of code I've written to set up these fields, many-to-many relations, validations etc.? <strong>Zero</strong>. None. Nada. I just spent a few minutes to setup everything up, connecting to my Directus instance's APIs with the <a target="_blank" href="https://docs.directus.io/reference/sdk/">official SDK</a> and digging around with image thumbnails auto-generation.</p>
<p>Isn't that beautiful? That's the magic of great and robust no-code software and the amazing team behind it.</p>
<h3 id="heading-next-steps">Next steps</h3>
<p>Since the app and the backend are basically done I can say I reached my goal. In the future, I'm going to create a website for the colony where people can see the cats and donate money to help buy food and probably I'll use Gatsby (ouch, React) with the <a target="_blank" href="https://www.gatsbyjs.com/plugins/gatsby-source-directus/">Directus Gatsby plugin</a> to fetch data and build the SSG easily.</p>
<p>Hey, here's a gift for you because you reached the end of the post: a photo of three cats from the colony. <em>Meow</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812987402/78e02aa0-c037-4c78-912b-ced1f44936f0.jpeg" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Add elapsed and estimated time to Laravel (and Symfony) console commands]]></title><description><![CDATA[Laravel Artisan Console is a powerful functionality to write your console commands, also thanks to Symfony Console Component which represents the base engine for them.
One feature I always wanted, but found out it exists only recently, is the estimat...]]></description><link>https://theraloss.com/laravel-command-estimated-time</link><guid isPermaLink="true">https://theraloss.com/laravel-command-estimated-time</guid><category><![CDATA[PHP]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[Symfony]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Sun, 25 Jul 2021 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689192664306/a856f717-2302-45b4-9605-ccb7ebfec411.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Laravel <a target="_blank" href="https://laravel.com/docs/8.x/artisan">Artisan Console</a> is a powerful functionality to write your console commands, also thanks to <a target="_blank" href="https://symfony.com/doc/current/components/console.html">Symfony Console Component</a> which represents the base engine for them.</p>
<p>One feature I always wanted, but found out it exists only recently, is the estimated time (ETA): basically how much time remains until the command finishes.</p>
<p>And as I said it's already a thing in the Console Component and the best part is that it could not be easier to implement!</p>
<p>You basically have two solutions:</p>
<ol>
<li><p>Run your command with <code>-vv</code> flag at the end, for example <code>php artisan mycommand -vv</code> and, automatically, your Progress Bar inside the command will show the elapsed and estimated time. Easy, right? The only "cons" I can think of is that every other component that uses the verbosity flag will be very verbose.</p>
</li>
<li><p>To set only the Progress Bar as verbose, you can invoke the <code>setFormat()</code> method passing <code>very_verbose</code> as value. Take the following example, where we create a bar and set its verbosity:</p>
</li>
</ol>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Commands</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Console</span>\<span class="hljs-title">Command</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProgressBar</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Command</span>
</span>{
    <span class="hljs-comment">/**
     * The signature of the command.
     */</span>
    <span class="hljs-keyword">protected</span> $signature = <span class="hljs-string">'progressbar'</span>;

    <span class="hljs-comment">/**
     * Execute the console command.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span>(<span class="hljs-params"></span>): <span class="hljs-title">int</span>
    </span>{
        $data = range(<span class="hljs-number">1</span>, <span class="hljs-number">10</span>);

        $bar = <span class="hljs-keyword">$this</span>-&gt;output-&gt;createProgressBar(count($data));
        $bar-&gt;setFormat(<span class="hljs-string">'very_verbose'</span>);
        $bar-&gt;start();

        <span class="hljs-keyword">foreach</span> ($data <span class="hljs-keyword">as</span> $item) {
            <span class="hljs-comment">// Do something here</span>

            $bar-&gt;advance();
        }

        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    }
}
</code></pre>
<p>Simple, right? Now if you run <code>php artisan progressbar</code> you should see an output similar to this post thumbnail, but compressed in only one line.</p>
<p>You can discover more about Progress Bars in the Symfony docs: <a target="_blank" href="https://symfony.com/doc/current/components/console/helpers/progressbar.html#built-in-formats">https://symfony.com/doc/current/components/console/helpers/progressbar.html#built-in-formats</a></p>
]]></content:encoded></item><item><title><![CDATA[Time travel after each Laravel Factory]]></title><description><![CDATA[If you ever tested an endpoint returning a collection of data and, for example, by default, you sort them by created_at, you may have been struggling to check the correct order of the result. If you either created multiple data with User::factory()->...]]></description><link>https://theraloss.com/laravel-time-travel-factory</link><guid isPermaLink="true">https://theraloss.com/laravel-time-travel-factory</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Thu, 15 Jul 2021 09:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689192744607/6ba39daa-7b61-4f4c-ad28-320539567e42.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you ever tested an endpoint returning a collection of data and, for example, by default, you sort them by <code>created_at</code>, you may have been struggling to check the correct order of the result. If you either created multiple data with <code>User::factory()-&gt;count(3)-&gt;create()</code> or just a few <code>User::factory()-&gt;create()</code> one after another, their timestamps will be pretty much the same, providing wrong results because the sorting will be wrong.</p>
<p>You have two solutions to solve this problem: the first one (<em>ok, but not reusable</em>) is to travel in time after each factory using <a target="_blank" href="https://laravel.com/docs/8.x/mocking#interacting-with-time">Laravel's time-testing helpers</a>:</p>
<pre><code class="lang-php">$user = User::factory()-&gt;create();

<span class="hljs-keyword">$this</span>-&gt;travel(<span class="hljs-number">1</span>)-&gt;minutes();

$user2 = User::factory()-&gt;create();

<span class="hljs-keyword">$this</span>-&gt;travel(<span class="hljs-number">1</span>)-&gt;minutes();

$user3 = User::factory()-&gt;create();

dump($user1-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:00:00</span>
dump($user2-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:01:00</span>
dump($user3-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:02:00</span>

<span class="hljs-keyword">$this</span>-&gt;getJson(<span class="hljs-string">'/users'</span>)
    -&gt;assertOk()
    -&gt;assertJson(...);
</code></pre>
<p>Although this just works fine, it can be annoying when you need this logic in multiple tests. The smartest solution (<strong>recommended</strong>) is to use the <a target="_blank" href="https://laravel.com/docs/8.x/database-testing#factory-callbacks">Factory's callbacks</a> with a bit of <a target="_blank" href="https://carbon.nesbot.com/docs/#api-testing">Carbon testing magic</a>:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Database</span>\<span class="hljs-title">Factories</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Database</span>\<span class="hljs-title">Eloquent</span>\<span class="hljs-title">Factories</span>\<span class="hljs-title">Factory</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Str</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Carbon</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Factory</span>
</span>{
    <span class="hljs-comment">/**
     * The name of the factory's corresponding model.
     *
     * <span class="hljs-doctag">@var</span> string
     */</span>
    <span class="hljs-keyword">protected</span> $model = User::class;

    <span class="hljs-comment">/**
     * Configure the model factory.
     *
     * <span class="hljs-doctag">@return</span> $this
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">configure</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>-&gt;afterCreating(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">User $user</span>) </span>{
            Carbon::setTestNow(Carbon::now()-&gt;addMinute());
        });
    }
}
</code></pre>
<p>What we are doing here is telling Carbon to set the current time to "<em>+1 minute</em>" after each Factory creation. Then, in our test, we can get rid of those time travels:</p>
<pre><code class="lang-php">$user = User::factory()-&gt;create();
$user2 = User::factory()-&gt;create();
$user3 = User::factory()-&gt;create();

dump($user1-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:00:00</span>
dump($user2-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:01:00</span>
dump($user3-&gt;created_at); <span class="hljs-comment">// 2021-01-01 00:02:00</span>

<span class="hljs-comment">// Or...</span>

$users = User::factory()-&gt;count(<span class="hljs-number">3</span>)-&gt;create();

<span class="hljs-keyword">foreach</span> ($users <span class="hljs-keyword">as</span> $user) {
    dump($user-&gt;created_at);
}

<span class="hljs-comment">// 2021-01-01 00:00:00</span>
<span class="hljs-comment">// 2021-01-01 00:01:00</span>
<span class="hljs-comment">// 2021-01-01 00:02:00</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Laravel FIFO queue with Redis]]></title><description><![CDATA[Recently I needed to create a FIFO (First In, First Out) queue in Laravel to run specific actions inside a long-running worker from outside the worker itself. I had two possibilities:

Database, creating a table, but I had also to create a Model, Ser...]]></description><link>https://theraloss.com/laravel-fifo-queue-with-redis</link><guid isPermaLink="true">https://theraloss.com/laravel-fifo-queue-with-redis</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Redis]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Mon, 05 Jul 2021 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689192993665/c7fe84bb-4603-48ae-8e15-433fadff3d30.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I needed to create a FIFO (First In, First Out) queue in Laravel to run specific actions inside a long-running worker from outside the worker itself. I had two possibilities:</p>
<ol>
<li><p>Database, creating a table, but I had also to create a Model, Service, Repository and many other things;</p>
</li>
<li><p>Redis, already installed.</p>
</li>
</ol>
<p>Of course, I went with <strong>Redis</strong>, taking advantage of two main commands: <a target="_blank" href="https://redis.io/commands/rpush">RPUSH</a> and <a target="_blank" href="https://redis.io/commands/lpop">LPOP</a>.</p>
<h2 id="heading-theory">Theory</h2>
<p>The first one, <strong>RPUSH</strong>, will append an item to a given list (and create if it does not exist) and will be responsible for pushing new items to the list; <strong>LPOP</strong>, instead, will pop out the first item in the list, deleting it from our "queue". A quick visual explanation:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687813548895/458cf7fe-edb9-4f60-8a37-e75314833f02.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-code">Code</h2>
<p>Thanks to <a target="_blank" href="https://laravel.com/docs/8.x/redis">Laravel Redis facade</a> we can use the Redis commands as static methods and, because <strong>LPOP</strong> will return <code>false</code> if the list is empty, we can easily loop until there are no more items to process:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Redis</span>;

<span class="hljs-comment">// Define our queue name</span>
$queueName = <span class="hljs-string">'fifo'</span>;

<span class="hljs-comment">// Put some items in our queue</span>
<span class="hljs-comment">// You can run this code outside your long-running worker</span>
Redis::rpush($queueName, <span class="hljs-string">'item1'</span>);
Redis::rpush($queueName, <span class="hljs-string">'item2'</span>);


<span class="hljs-comment">// ---------------------- somewhere else ----------------------</span>

<span class="hljs-comment">// Process until there are no more elements</span>
<span class="hljs-comment">// Note that will close when the queue is empty</span>
<span class="hljs-keyword">while</span> ($item = Redis::lpop($queueName)) {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">'Found: '</span> . $item . PHP_EOL;
}

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// "Found: item1"</span>
<span class="hljs-comment">// "Found: item2"</span>
</code></pre>
<p>If you want to run it in a <strong>long-running worker</strong>, you'll need to slightly change the <code>while</code> loop into something like this (or using a library like the great <a target="_blank" href="https://reactphp.org/event-loop/">ReactPHP</a>) to not stop the script even if the queue is empty:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Redis</span>;

<span class="hljs-comment">// Define our queue name</span>
$queueName = <span class="hljs-string">'fifo'</span>;

<span class="hljs-comment">// Process until there are no more elements</span>
<span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
    <span class="hljs-keyword">if</span> ($item = Redis::lpop($queueName)) {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'Found: '</span> . $item . PHP_EOL;
    }
}

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// "Found: item1"</span>
<span class="hljs-comment">// "Found: item2"</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Resovable trait for Laravel container]]></title><description><![CDATA[If you really work on Laravel, you're gonna deal a lot of times with its Container and, probably, you often will need to resolve something from it, with a syntax like this:
$service = resolve(MyService::class);

// or

$service = App::make(MyService:...]]></description><link>https://theraloss.com/laravel-container-resolvable-trait</link><guid isPermaLink="true">https://theraloss.com/laravel-container-resolvable-trait</guid><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Wed, 30 Jun 2021 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689193071116/41f8b8af-cc28-4fc9-ae87-11bb2a96a697.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you <strong>really</strong> work on Laravel, you're gonna deal a lot of times with its <a target="_blank" href="https://laravel.com/docs/8.x/container">Container</a> and, probably, you often will need to resolve something from it, with a syntax like this:</p>
<pre><code class="lang-php">$service = resolve(MyService::class);

<span class="hljs-comment">// or</span>

$service = App::make(MyService::class, [<span class="hljs-string">'id'</span> =&gt; <span class="hljs-number">3</span>]);
</code></pre>
<p>In my case I'm doing this <strong>a lot of times</strong>, particularly when writing tests and, because we don't have generics in PHP, the IDE won't know what <code>resolve</code> or <code>App::make</code> return, therefore you must rely on tools like IntelliSense or Intelephense with a PHPDoc:</p>
<pre><code class="lang-php"><span class="hljs-comment">/** <span class="hljs-doctag">@var</span> MyService */</span>
$service = App::make(MyService::class, [<span class="hljs-string">'id'</span> =&gt; <span class="hljs-number">3</span>]);
</code></pre>
<p>This is pretty straightforward, right? But I find it annoying in the long run, therefore I created this little yet powerful trait to resolve something from the container without any additional PHPDoc and just using the standard PHP typed syntax:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Traits</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">App</span>;

<span class="hljs-keyword">trait</span> Resolvable
{
    <span class="hljs-comment">/**
     * Resolve itself from the container.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolve</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> $parameters = []</span>): <span class="hljs-title">static</span>
    </span>{
        <span class="hljs-keyword">return</span> App::make(<span class="hljs-built_in">static</span>::class, $parameters);
    }
}
</code></pre>
<p>Then, in your class, you can just "include" it and the magic is ready 🪄 Thanks to the <code>static</code> keyword as return type, the IDE will automatically know what's being returned and we don't need anymore those PHPDoc.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Traits</span>\<span class="hljs-title">Resolvable</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyService</span>
</span>{
    <span class="hljs-keyword">use</span> <span class="hljs-title">Resolvable</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-comment">// Something</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sayHi</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello there'</span>;
    }
}

<span class="hljs-comment">// Somewhere else</span>
$greet = MyService::resolve()-&gt;sayHi();
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Run a one-off command on Twitch IRC with TMI.php]]></title><description><![CDATA[If you've ever interacted with Twitch Chat IRC, you probably have used tmi.js or a similar library.
Recently, because I mainly use PHP, I discovered the great TMI.php library by Ghostzero and I'm already using it to manage events from my chat, when I...]]></description><link>https://theraloss.com/run-a-one-off-command-s-with-twitch-php-tmi-by-ghostzero</link><guid isPermaLink="true">https://theraloss.com/run-a-one-off-command-s-with-twitch-php-tmi-by-ghostzero</guid><category><![CDATA[PHP]]></category><category><![CDATA[Twitch]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Thu, 01 Apr 2021 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>If you've ever interacted with Twitch Chat IRC, you probably have used <code>tmi.js</code> or a similar library.</p>
<p>Recently, because I mainly use PHP, I discovered the great <a target="_blank" href="https://github.com/ghostzero/tmi">TMI.php</a> library by Ghostzero and I'm already using it to manage events from my chat, when I encountered a need: how can I send a one-off message (or any IRC-related action)?</p>
<p>The main issue is that you cannot run anything after <code>connect()</code> because it starts a blocking ReactPHP loop and the code under it will be executed only when the loop closes.</p>
<p>For example, take this code:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">GhostZero</span>\<span class="hljs-title">Tmi</span>\<span class="hljs-title">Client</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">GhostZero</span>\<span class="hljs-title">Tmi</span>\<span class="hljs-title">ClientOptions</span>;

$client = <span class="hljs-keyword">new</span> Client(<span class="hljs-keyword">new</span> ClientOptions([
    <span class="hljs-string">'identity'</span> =&gt; [
        <span class="hljs-string">'username'</span> =&gt; <span class="hljs-string">'ghostzero'</span>,
        <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'oauth:...'</span>,
    ],
    <span class="hljs-string">'channels'</span> =&gt; [<span class="hljs-string">'ghostzero'</span>],
]));


$client-&gt;connect();

<span class="hljs-keyword">echo</span> <span class="hljs-string">"I will be executed only when connect() ends"</span>;
</code></pre>
<p>If we cannot run our code <em>after</em> connecting, we can run it inside an event, right?</p>
<h3 id="heading-the-solution">The solution</h3>
<p>I found out that the <code>WelcomeEvent</code> is fired only once when your user connects to the chat, so it's the perfect place where to place our logic. We can write our one-off methods and then close the connection:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">GhostZero</span>\<span class="hljs-title">Tmi</span>\<span class="hljs-title">Events</span>\<span class="hljs-title">Irc</span>\<span class="hljs-title">WelcomeEvent</span>;

$client
    -&gt;on(WelcomeEvent::class, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) <span class="hljs-title">use</span> (<span class="hljs-params">$client</span>) </span>{
        $client-&gt;say(<span class="hljs-string">'ghostzero'</span>, <span class="hljs-string">'Hello from my code'</span>);

        $client-&gt;close();
    })
    -&gt;connect();
</code></pre>
<p>If you would run this code straight away the script would never close and the <code>WelcomeEvent</code> would trigger more than once. This happens because the package is smart enough to reconnect when the connection to the IRC channel is closed and, guess what, we're creating a beautiful loop! We send a message on <code>Welcome</code> -&gt; we close the connection -&gt; the package reconnects -&gt; a <code>Welcome</code> event is triggered -&gt; we send a message on <code>Welcome</code> -&gt; ... and so on.</p>
<p>We can easily solve this by disabling the auto reconnection in our client settings:</p>
<pre><code class="lang-php">$client = <span class="hljs-keyword">new</span> Client(<span class="hljs-keyword">new</span> ClientOptions([
    <span class="hljs-string">'connection'</span> =&gt; [
        <span class="hljs-string">'reconnect'</span> =&gt; <span class="hljs-literal">false</span>,
    ],
    <span class="hljs-string">'identity'</span> =&gt; [
        <span class="hljs-string">'username'</span> =&gt; <span class="hljs-string">'ghostzero'</span>,
        <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'oauth:...'</span>,
    ],
    <span class="hljs-string">'channels'</span> =&gt; [<span class="hljs-string">'ghostzero'</span>],
]));
</code></pre>
<p>If we now run our code it will... do nothing. It will But everything is correct now, doesn't it? It even printed <em>"I will be executed only when connect() ends"</em> !. Well, <em>theoretically</em> it does, but technically it does not for one simple reason: we close the loop before our code is fullfilled. To get it better, IRC methods like <code>say()</code> are not sync, that means that their execution does not mean that the desired action is already done. In our case, <code>$client-&gt;close()</code> disconnects the client and stops the loop <strong>before</strong> <code>say()</code> actually sends something.</p>
<p>The way I found that can solve this is to close the loop after a few seconds, just to be sure that our IRC message is actually sent. We can use the <code>addTimer</code> method of ReactPHP to achieve it:</p>
<pre><code class="lang-php">$client
    -&gt;on(WelcomeEvent::class, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) <span class="hljs-title">use</span> (<span class="hljs-params">$client</span>) </span>{
        $client-&gt;say(<span class="hljs-string">'ghostzero'</span>, <span class="hljs-string">'Hello from my code'</span>);

        $client-&gt;getLoop()-&gt;addTimer(<span class="hljs-number">3</span>, <span class="hljs-function"><span class="hljs-keyword">fn</span> (<span class="hljs-params"></span>)  =&gt; $<span class="hljs-title">client</span>-&gt;<span class="hljs-title">close</span>(<span class="hljs-params"></span>))</span>;
    })
    -&gt;connect();
</code></pre>
<p>Putting everything back together we will get the following snippet. The loop runs, sends a message in our chat and then ends, showing the message <code>I will be executed only when connect() ends</code>.</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">GhostZero</span>\<span class="hljs-title">Tmi</span>\<span class="hljs-title">Client</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">GhostZero</span>\<span class="hljs-title">Tmi</span>\<span class="hljs-title">ClientOptions</span>;

$client = <span class="hljs-keyword">new</span> Client(<span class="hljs-keyword">new</span> ClientOptions([
    <span class="hljs-string">'connection'</span> =&gt; [
        <span class="hljs-string">'reconnect'</span> =&gt; <span class="hljs-literal">false</span>,
    ],
    <span class="hljs-string">'identity'</span> =&gt; [
        <span class="hljs-string">'username'</span> =&gt; <span class="hljs-string">'ghostzero'</span>,
        <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'oauth:...'</span>,
    ],
    <span class="hljs-string">'channels'</span> =&gt; [<span class="hljs-string">'ghostzero'</span>],
]));

$client
    -&gt;on(WelcomeEvent::class, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) <span class="hljs-title">use</span> (<span class="hljs-params">$client</span>) </span>{
        $client-&gt;say(<span class="hljs-string">'ghostzero'</span>, <span class="hljs-string">'Hello from my code'</span>);

        $client-&gt;getLoop()-&gt;addTimer(<span class="hljs-number">3</span>, <span class="hljs-function"><span class="hljs-keyword">fn</span> (<span class="hljs-params"></span>)  =&gt; $<span class="hljs-title">client</span>-&gt;<span class="hljs-title">close</span>(<span class="hljs-params"></span>))</span>;
    })
    -&gt;connect();

<span class="hljs-keyword">echo</span> <span class="hljs-string">"I will be executed only when connect() ends"</span>;
</code></pre>
<p>Hopefully, in the future, the package will provide a shortcut to avoid this hacky way. Remember to leave a star to the <a target="_blank" href="https://github.com/ghostzero/tmi">TMI.php</a> project!</p>
]]></content:encoded></item><item><title><![CDATA[Setup a Valheim private server on a VPS in less than 5 minutes]]></title><description><![CDATA[Valheim may tell nothing to you and you're right, why should it sound familiar when it got in the 1° position of Steam popular games in less than 2 weeks...
Sarcasm aside, one of the greatest features of the game is that it allows you to create your ...]]></description><link>https://theraloss.com/setup-valheim-server</link><guid isPermaLink="true">https://theraloss.com/setup-valheim-server</guid><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Tue, 16 Feb 2021 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687814020754/1e2cae56-a258-44f5-b320-a21684835c42.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://store.steampowered.com/app/892970/Valheim/">Valheim</a> may tell nothing to you and you're right, why should it sound familiar when it got in the 1° position of Steam popular games in less than 2 weeks...</p>
<p>Sarcasm aside, one of the greatest features of the game is that it allows you to create your own private server and that's why you're here. Let's just jump into it.</p>
<h2 id="heading-setup">Setup</h2>
<p>For our server, I'm using a $5 VPS (CX21) on <a target="_blank" href="https://hetzner.cloud/?ref=1AnKVX49UhkT">Hetzner</a>. You can get <a target="_blank" href="https://hetzner.cloud/?ref=1AnKVX49UhkT">one too with a free credit of $20, if you want</a>.</p>
<p>We're gonna use the beautiful CLI <a target="_blank" href="https://linuxgsm.com/lgsm/vhserver/">LinuxGSM</a> to quickly set up our server, so let's just follow their instructions.</p>
<ol>
<li><p>Install needed dependencies running with root privileges the command <code>sudo dpkg --add-architecture i386; sudo apt update; sudo apt install curl wget file tar bzip2 gzip unzip bsdmainutils python util-linux ca-certificates binutils bc jq tmux netcat lib32gcc1 lib32stdc++6 steamcmd</code>.</p>
</li>
<li><p>Create a new user for our game server: <code>adduser vhserver</code></p>
</li>
<li><p>Log in as the created user with <code>su - vhserver</code></p>
</li>
<li><p>Download the game manager CLI tool: <code>wget -O linuxgsm.sh https://linuxgsm.sh &amp;&amp; chmod +x linuxgsm.sh &amp;&amp; bash linuxgsm.sh vhserver</code></p>
</li>
<li><p>Install the game server following the on-screen instructions: <code>./vhserver install</code></p>
</li>
</ol>
<blockquote>
<p>If you get an error about <code>Error processing libc6:i386 and libtinfo5:i386</code> just run <code>sudo apt-get install --reinstall libc6-i386</code> into your terminal.</p>
</blockquote>
<p>So far so good. Now let's jump to server settings.</p>
<h2 id="heading-settings-name-password-and-more">Settings (name, password and more)</h2>
<p>We may want to change some settings about our server, like <strong>choosing a password</strong> or <strong>renaming it</strong>. This is simpler than it sounds like.</p>
<p>Every setting file can be found inside our <code>/home/vhserver/lgsm/config-lgsm/vhserver</code> folder. In our case, we want to <strong>change the password</strong> and the <strong>server name</strong>.</p>
<p>Let's edit the file <code>vhserver.cfg</code> by running <code>nano ~/lgsm/config-lgsm/vhserver/vhserver.cfg</code> and just paste the following content inside it:</p>
<pre><code class="lang-apache"><span class="hljs-attribute"><span class="hljs-nomarkup">servername</span></span>=<span class="hljs-string">"Our beautiful server"</span>
<span class="hljs-comment"># Minimum password length is 5.</span>
<span class="hljs-attribute">serverpassword</span>=<span class="hljs-string">"mypassword"</span>
</code></pre>
<p>That's it. Simple, right? There are a lot of settings you can customize; if you're curious about them, just give a shot to the <code>_default.cfg</code> file: <code>cat ~/lgsm/config-lgsm/vhserver/_default.cfg</code>.</p>
<p>You will see you can change the port, the number of backups kept in the disk and, of course, the world-saving files.</p>
<h2 id="heading-run-the-server">Run the server</h2>
<p>To start your server just hit <code>./vhserver start</code> from your home directory (<code>/home/vhserver</code>) and that's it!</p>
<p>You can see all the details about your game server by running <code>./vhserver details</code>: you'll get useful information such as available resources (CPU, Memory), a recap of server configurations and many other things.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687814101149/32cf0e9e-0e07-4801-af8c-2c3abf624de7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-connect-to-the-server">Connect to the server</h2>
<p>To connect directly to your server go to <em>Steam -&gt; View -&gt; Servers -&gt; Favorites -&gt; Add server</em> and type your <em>IP:PORT</em>, for example <code>1.2.3.4:2456</code>. If Steam says that the "<em>Server is not responding</em>" try to put the next port, for example <code>2457</code>.</p>
<p>Now just click on "<strong>Connect</strong>", go to your Valheim game window and select your character.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687814129715/0fac9fbf-6878-4bbc-88aa-1542b76e84b5.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-keep-alive-backups-and-updates">Keep alive, Backups and Updates</h2>
<p>Following again the <a target="_blank" href="https://linuxgsm.com/lgsm/vhserver/">LinuxGSM Valheim</a> guide we can set up several cronjobs to keep our server up-to-date and, most importantly, to <strong>keep it alive and to back up it</strong>, you know, just to be safe.</p>
<p>To setup them up, run <code>crontab -e</code> from your terminal and past our commands:</p>
<pre><code class="lang-plaintext">*/5 * * * * /home/vhserver/vhserver monitor &gt; /dev/null 2&gt;&amp;1
*/30 * * * * /home/vhserver/vhserver update &gt; /dev/null 2&gt;&amp;1
0 0 * * 0 /home/vhserver/vhserver update-lgsm &gt; /dev/null 2&gt;&amp;1
30 1 * * * /home/vhserver/vhserver backup &gt; /dev/null 2&gt;&amp;1
</code></pre>
<p>With the given commands, it will:</p>
<ul>
<li><p>Check <em>every 5 minutes</em> if the server is still alive and, if it is not, it will be restarted;</p>
</li>
<li><p>Check <em>every 30 minutes</em> if there are updates for our game server;</p>
</li>
<li><p>Check <em>every Sunday at 1am</em> if there are updates for LinuxGSM CLI;</p>
</li>
<li><p><strong>Do a backup every day at 1:30am</strong>.</p>
</li>
</ul>
<p>By default 4 backups will be kept in our disk: if another one is gonna be created, the oldest will be deleted to keep the number always at 4. You can customize this behavior by editing the <a target="_blank" href="https://docs.linuxgsm.com/commands/backup#backup-settings">backup settings</a> in your <code>vhserver.cfg</code> file like we did before to change our game name and password.</p>
<h2 id="heading-bonus-1-change-server-port">Bonus 1: change server port</h2>
<p>If you would like to change the game server port, edit the config file with <code>nano ~/lgsm/config-lgsm/vhserver/vhserver.cfg</code> and add a <code>port</code> settings with your desired value.</p>
<pre><code class="lang-apache"><span class="hljs-attribute">port</span>=<span class="hljs-string">"1337"</span>
</code></pre>
<p>Of course remember to change your server on Steam favorite's too, for example <code>1.2.3.4:1337</code>. If Steam says that the "Server is not responding", try to use the next port, for example <code>1.2.3.4:1338</code>.</p>
<h2 id="heading-bonus-2-restore-an-existing-game-world">Bonus 2: restore an existing game world</h2>
<p>If you already have a game server and you're migrating to this one or you just want to use your local world, only two files are required to achieve this: <code>.db</code>and <code>.fwl</code>. For example, on your computer you can find them in <code>%AppData%/LocalRow/IronGate/Valheim/worlds/</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687814264622/1422cf0b-e504-4386-a6ba-38bccf73570d.png" alt class="image--center mx-auto" /></p>
<p>Now put them inside the <code>~/.config/unity3d/IronGate/Valheim/worlds/</code> folder of your account, be sure the permissions are right running <code>chmod 664 ~/.config/unity3d/IronGate/Valheim/worlds/</code> and restart the game server with <code>./vhserver restart</code> .</p>
<p>Enjoy your game!</p>
]]></content:encoded></item><item><title><![CDATA[Preloading Laravel on PHP7.4 in the real world]]></title><description><![CDATA[One of the greatest features of PHP7.4 is for sure the preload - part of the opcache extension.
We recently moved a big part of my company's core APIs from a Node-Frankenstein to a well-tested Laravel 8 project, but of course, the performance - altho...]]></description><link>https://theraloss.com/preloading-laravel-in-php74</link><guid isPermaLink="true">https://theraloss.com/preloading-laravel-in-php74</guid><category><![CDATA[PHP]]></category><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Mon, 14 Dec 2020 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>One of the greatest features of PHP7.4 is for sure the preload - part of the <code>opcache</code> extension.</p>
<p>We recently moved a big part of my company's core APIs from a Node-Frankenstein to a well-tested Laravel 8 project, but of course, the performance - although <code>opcache</code> is enabled, is not <em>really</em> close to a Node app; but we already knew this.</p>
<p>So... what's next? "Let's try the preloading", I said! It should be easy, right? Weeell...</p>
<h3 id="heading-approach-1-vendor-centric">Approach 1: vendor-centric</h3>
<p>I had no idea where to start. The needed result was simple: loop over "desired" files and <code>require_once</code> - or <code>opcache_compile_file</code> - them.</p>
<p>I found time ago the great article "<a target="_blank" href="https://stitcher.io/blog/preloading-in-php-74">Preloading in PHP7.4</a>" on stitcher by Brent. It also has a full code! That's a dream, omg. Like a child with the Hogwarts Lego I quickly copied and pasted it. Of course, I set the needed configurations: <code>opcache.preload=/app/preload.php</code> and <code>opcache.preload_user=www-data</code> - this last one is needed if your account is <code>root</code>.</p>
<p>Then I restarted my container. And it crashed. Let's see the errors...</p>
<p><code>Uncaught Error: Class 'Illuminate\Queue\Queue' not found in /app/vendor/laravel/framework/src/Illuminate/Queue/DatabaseQueue.php:13</code></p>
<p>Well, that's fine, I'm just gonna <code>ignore</code> that file (there's a method in the example class to ignore specific files). Time to restart. New error: <code>Uncaught Error: Class 'Opis\Closure\SerializableClosure' not found in /app/vendor/laravel/framework/src/Illuminate/Queue/SerializableClosure.php:7</code>. Ok...? Let's ignore that too. Then another one. And another one.</p>
<p>In the end, I ignored the whole Laravel framework and 0 classes were preloaded. What am I doing wrong? Why does it work in the article? I'll never know this, but let's try to replace <code>require_once</code> in the class with <code>opcache_compile_file</code>.</p>
<p>Now restart and... It started! But there are a few errors like <code>Can't preload unlinked class Illuminate\Database\PDO\PostgresDriver: Unknown parent Doctrine\DBAL\Driver\AbstractPostgreSQLDriver in /app/vendor/laravel/framework/src/Illuminate/Database/PDO/PostgresDriver.php on line 8</code>, I guess I can filter out those files later and we're good.</p>
<p>Time to benchmark it! First, let's test the performance <strong>without preload</strong>. Note that the project is running in a Docker container with <code>opcache</code> enabled, XDebug and other 20+ containers alive. And if you're asking yourself <em>"What's ab?"</em>, the answer is: Apache Bench.</p>
<pre><code class="lang-basic">    ab -n <span class="hljs-number">100</span> -c <span class="hljs-number">5</span> -l http://mysite.localhost/page

    Requests per second:    <span class="hljs-number">21.78</span> [#/sec] (mean)
    Time per request:       <span class="hljs-number">229.516</span> [ms] (mean)
    Time per request:       <span class="hljs-number">45.903</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>That's our <em>status quo</em>. Now let's see <strong>with preload</strong>.</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">25.03</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">199.774</span> [ms] (mean)
Time per request:       <span class="hljs-number">39.955</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>That's an increase for sure! We gained <strong>±14.9% req/s</strong> and <strong>±12.9% time/req</strong>!</p>
<p>Can it be better? Giving a shot to the logs, it preloaded <strong>1022 classes</strong> and, in the list of preloaded files, there are a lot of dev-dependencies (like Telescope, Tinker, Testing) and some unusued classes too (Session, Console...). Let's strip them out and see if something changes. I'm just gonna put the namespaces in the <code>ignore()</code> method like this:</p>
<pre><code class="lang-php">(<span class="hljs-keyword">new</span> Preloader())
    -&gt;paths(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/laravel'</span>)
    -&gt;ignore(
        <span class="hljs-string">'Laravel\Telescope'</span>,
        <span class="hljs-string">'Laravel\Tinker'</span>,
        <span class="hljs-string">'Illuminate\Queue'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Queue'</span>,
        <span class="hljs-string">'Illuminate\View'</span>,
        <span class="hljs-string">'Illuminate\Contracts\View'</span>,
        <span class="hljs-string">'Illuminate\Foundation\Console'</span>,
        <span class="hljs-string">'Illuminate\Notification'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Notifications'</span>,
        <span class="hljs-string">'Illuminate\Bus'</span>,
        <span class="hljs-string">'Illuminate\Session'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Session'</span>,
        <span class="hljs-string">'Illuminate\Console'</span>,
        <span class="hljs-string">'Illuminate\Testing'</span>,
        <span class="hljs-string">'Illuminate\Http\Testing'</span>,
        <span class="hljs-string">'Illuminate\Support\Testing'</span>,
        <span class="hljs-string">'Illuminate\Cookie'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Cookie'</span>,
        <span class="hljs-string">'Illuminate\Broadcasting'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Broadcasting'</span>,
        <span class="hljs-string">'Illuminate\Mail'</span>,
        <span class="hljs-string">'Illuminate\Contracts\Mail'</span>,
    )
    -&gt;load();
</code></pre>
<p>Did something change? Restart again, now there are <strong>625 preloaded classes</strong>, run <code>ab</code> and the results:</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">25.22</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">198.274</span> [ms] (mean)
Time per request:       <span class="hljs-number">39.655</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>That's pretty much the same, nevermind, but I want to make a final test: add more vendors to <code>paths()</code> method:</p>
<pre><code class="lang-php">(<span class="hljs-keyword">new</span> Preloader())
      -&gt;paths(
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/psr'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/monolog'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/doctrine'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/guzzlehttp'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/ramsey/uuid'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/ramsey/collection'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/vlucas/phpdotenv'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/symfony'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/laravel'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/nesbot/carbon'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/sentry'</span>,
        <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/auth0'</span>,
      )
</code></pre>
<p>And running again the <code>ab</code> gives us:</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">26.54</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">188.400</span> [ms] (mean)
Time per request:       <span class="hljs-number">37.680</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>Ok, that's something, right? And that "something" is <strong>±21.8% req/s</strong> and <strong>±17.9% time/req</strong> from the no-preload checks.</p>
<p>Now let's group and compare all the data:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>Req/s</td><td>Time/req</td></tr>
</thead>
<tbody>
<tr>
<td>No preload</td><td>21.78</td><td>229ms</td></tr>
<tr>
<td>Naive preload</td><td>25.03</td><td>199ms</td></tr>
<tr>
<td>Refined preload</td><td>25.22</td><td>198ms</td></tr>
<tr>
<td>Refined preload + vendors</td><td>26.54</td><td>188ms</td></tr>
</tbody>
</table>
</div><h3 id="heading-approach-2-hot-files">Approach 2: "Hot" files</h3>
<p>The second approach I tried was to identify the hot files using <a target="_blank" href="https://github.com/DarkGhostHunter/Laraload">Laraload</a>, a wrapper for Laravel of <a target="_blank" href="https://github.com/DarkGhostHunter/Preloader">Preloader</a>; it creates a global middleware for your app and for every 500 requests (or your custom logic) it generates a <code>preload.php</code> with all the detected files. Pretty straightforward.</p>
<p>I "forced" the requests using ApacheBench with 500 requests and in the <code>preload.php</code> now there are <strong>1490 classes</strong>. Wow, the problem is that they include dev-dependencies too like Telescope or Tinker, so I started removing them - also because they would not be found in production and I went down to <strong>1351 classes</strong> of both vendors and files of my app.</p>
<p>Like in the <em>Approach 1</em> there were some errors, for example, I had to remove the local <code>app/Http/Kernel.php</code> from the preloaded files and finally, the PHP container started again.</p>
<p>Remember our <strong>no-preload status</strong>:</p>
<pre><code class="lang-basic">ab -n <span class="hljs-number">100</span> -c <span class="hljs-number">5</span> -l http://mysite.localhost/page

Requests per second:    <span class="hljs-number">21.78</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">229.516</span> [ms] (mean)
Time per request:       <span class="hljs-number">45.903</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>As usual, I ran my Apache Bench command to check the performance aaaand my app crashed because of <code>amphp</code> - I don't even know which vendor uses it but nevermind -, so I removed all its references from the big files array, restarted again and ran the bench command.</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">25.16</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">198.694</span> [ms] (mean)
Time per request:       <span class="hljs-number">39.739</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>Doing the math we gained <strong>±15.5% req/s</strong> and <strong>±13.4% time/request</strong>, but I wanted to make another test: what happens if I keep only Laravel vendor in the array? This is the result:</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">25.03</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">199.787</span> [ms] (mean)
Time per request:       <span class="hljs-number">39.957</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p>Well, it's pretty much the same.</p>
<h3 id="heading-final-thoughts">Final thoughts</h3>
<p>Ok kids, time to bring something to the table. All the data to me!</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>Req/s</td><td>Time/req</td></tr>
</thead>
<tbody>
<tr>
<td>No preload</td><td>21.78</td><td>229ms</td></tr>
<tr>
<td>Naive preload (#1)</td><td>25.03</td><td>199ms</td></tr>
<tr>
<td>Refined preload (#2)</td><td>25.22</td><td>198ms</td></tr>
<tr>
<td>Refined preload + vendors (#3)</td><td>26.54</td><td>188ms</td></tr>
<tr>
<td>Hot files preload (#4)</td><td>25.16</td><td>198ms</td></tr>
<tr>
<td>Hot files Laravel preload (#5)</td><td>25.03</td><td>199ms</td></tr>
</tbody>
</table>
</div><p>As we can see all the preload methods produce a similar performance boost, but one thing is sure: <mark>you </mark> <strong><mark>should definitely preload</mark></strong>.</p>
<p>How to preload is up to you, I will probably stick to the <code>Preloader</code> class from Brent (Sticher.io) because it's easier to read and maintain.</p>
<h3 id="heading-staging-benchmarks">Staging benchmarks</h3>
<p>The real test: how does this impact on a staging environment? I released this for our company in our staging environment using the "<em>Refined preload + vendors</em>" method. Our APIs are running on K8S and I took some before-after benchmarks. Below you can see the results; doing our math, we gained <strong>±10.5% req/s</strong> and <strong>±9.5% time/request</strong>.</p>
<p><strong>Before preload</strong>:</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">12.22</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">409.068</span> [ms] (mean)
Time per request:       <span class="hljs-number">81.814</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p><strong>After preload</strong>:</p>
<pre><code class="lang-basic">Requests per second:    <span class="hljs-number">13.51</span> [#/sec] (mean)
Time per request:       <span class="hljs-number">370.110</span> [ms] (mean)
Time per request:       <span class="hljs-number">74.022</span> [ms] (mean, across all concurrent requests)
</code></pre>
<p><strong>Summary</strong>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>Req/s</td><td>Time/req</td></tr>
</thead>
<tbody>
<tr>
<td>No preload</td><td>12.22</td><td>409ms</td></tr>
<tr>
<td>Preload</td><td>13.51</td><td>370ms</td></tr>
</tbody>
</table>
</div><p><em>Will be updated soon with</em> <strong><em>Production</em></strong> <em>benchmarks!</em></p>
]]></content:encoded></item><item><title><![CDATA[Successfully implement Auth0 in Laravel 8 APIs with a SPA]]></title><description><![CDATA[When you have to choose how to start a new project you have many choices if you think about languages, frameworks and authentication.
In our case (we = company where I work) we're going with a classic SPA: Vue + Laravel API. Additionally, we're using...]]></description><link>https://theraloss.com/laravel-auth0-jwt</link><guid isPermaLink="true">https://theraloss.com/laravel-auth0-jwt</guid><category><![CDATA[Laravel]]></category><category><![CDATA[authentication]]></category><category><![CDATA[Auth0]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Fri, 25 Sep 2020 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687815007571/a315e9b0-678c-4d0b-895b-4a4d7ab78e0e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you have to choose how to start a new project you have many choices if you think about languages, frameworks and authentication.</p>
<p>In our case (we = company where I work) we're going with a classic SPA: Vue + Laravel API. Additionally, we're using Auth0 to authenticate our SPA, because it's a company backoffice and with Auth0 we're creating a kind-of-SSO between all our backoffices.<br />Sounds cool, right? I mean, there are plenty of tutorials about this for Vue. Indeed. But I found a lack of tutorials for Laravel.</p>
<p>Wait wait, does Auth0 have an official quickstart for Laravel? They have two!? One for classic webapp application and one for "API only"? This is awesome! And there's more? Their guide is for Laravel 6? Well, I'm using Laravel 8, I'm sure it wor--. Yes, that's why we're here.</p>
<h3 id="heading-installation">Installation</h3>
<p>One of the best things Auth0 did is a provider for Laravel to integrate their service: https://github.com/auth0/laravel-auth0. Before you go, I have to sadly inform you that when I'm writing this and the provider is at v6.1.0 it still doesn't support Guzzle7, so you have to downgrade it to 6.5.5 by running <code>composer require guzzlehttp/guzzle:^6.5.5</code>. Very sad times. There's already an issue for this, we hope they upgrade it soon.</p>
<p>Let's focus folks and proceed with the tutorial: install the provider running <code>composer require auth0/login:"~6.0"</code> and the magic word of Laravel and Composer will automatically discover and register it.</p>
<h3 id="heading-usage">Usage</h3>
<p>Now, following their README, we can set up a Guard and use it as a middleware to protect our routes. Open your config/auth.php file and add a new Guard:</p>
<pre><code class="lang-php"><span class="hljs-string">'guards'</span> =&gt; [
    ...
    <span class="hljs-string">'auth0'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'auth0'</span>,
        <span class="hljs-string">'provider'</span> =&gt; <span class="hljs-string">'auth0'</span>,
    ],
],
</code></pre>
<p>Now, in the same file, add the provider too:</p>
<pre><code class="lang-php"><span class="hljs-string">'providers'</span> =&gt; [
    ...
    <span class="hljs-string">'auth0'</span> =&gt; [
        <span class="hljs-string">'driver'</span> =&gt; <span class="hljs-string">'auth0'</span>,
    ],
],
</code></pre>
<p>Given that, we can protect our routes using the guard as a middleware:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/private'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> response()-&gt;json([<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Private endpoint'</span>]);
})-&gt;middleware(<span class="hljs-string">'auth:auth0'</span>);
</code></pre>
<p>From now on, you can only access <code>/private</code> endpoint providing a valid JWT token in the <code>Authorization</code> header like this: <code>Authorization: Bearer &lt;jwt-here&gt;</code>.</p>
<h3 id="heading-testing">Testing</h3>
<p>Since Laravel 8 the way Models Factories are made is a bit different, in fact, you have to add <code>HasFactory</code> trait to your model to create a new "fake" model. I think that currently Auth0 still does not support this for the <code>Auth0User</code> model and, because of that, we can't (still) rely on <code>Auth0User::factory()</code> syntax.</p>
<p>To workaround this we're just gonna initialize a new <code>Auth0\Login\Auth0User</code> instance and using that for the <code>actingAs</code> helper.</p>
<p>When creating an Auth0User instance you should provide 2 arguments:</p>
<ul>
<li><p><code>array $userInfo</code> , where you can find the <code>sub</code> key (provided by Auth0) or the <code>user_id</code> key;</p>
</li>
<li><p><code>string|null $token</code> - I think this is the JWT, but I don't really know.</p>
</li>
</ul>
<p>Anyway for our test where we don't need to store the user nor to retrieve it from the <code>sub</code> / <code>user_id</code> property, we can just leave them both "empty".</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Tests</span>\<span class="hljs-title">Feature</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Tests</span>\<span class="hljs-title">TestCase</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Auth0</span>\<span class="hljs-title">Login</span>\<span class="hljs-title">Auth0User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Response</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PrivateEndpointTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">TestCase</span>
</span>{
  <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testAuthorizedWithFakeUser</span>(<span class="hljs-params"></span>)
  </span>{
    $user = <span class="hljs-keyword">new</span> Auth0User([], <span class="hljs-literal">null</span>);

    <span class="hljs-keyword">$this</span>-&gt;actingAs($user, <span class="hljs-string">'auth0'</span>)
      -&gt;getJson(<span class="hljs-string">'/api/private'</span>)
      -&gt;assertStatus(Response::HTTP_OK)
      -&gt;assertExactJson([
        <span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'Private endpoint'</span>,
      ]);
  }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[HTTP Status Code crawling in Bash]]></title><description><![CDATA[On the last day I had to rewrite the whole sitemap of a site to boost its performance and I needed to check if the generated URLs were responding 2xx and not another HTTP Status Code, so I wrote a little script in Bash.
Why Bash and not another langu...]]></description><link>https://theraloss.com/http-status-code-crawling-in-bash</link><guid isPermaLink="true">https://theraloss.com/http-status-code-crawling-in-bash</guid><category><![CDATA[Bash]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Tue, 15 May 2018 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>On the last day I had to rewrite the whole sitemap of a site to boost its performance and I needed to check if the generated URLs were responding 2xx and not another HTTP Status Code, so I wrote a little script in Bash.</p>
<p>Why Bash and not another language? Since I had to use <code>curl -I</code> I thought it was simpler in Bash and it is more portable; let's see the result.</p>
<p>The first thing is to loop line by line a file, which will contain our URLs and I found on StackOverflow this <code>while</code> loop to achieve it. It will take the filename from the args when you run the script (<code>done &lt; "$1"</code>).</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-variable">${i}</span>] Doing <span class="hljs-variable">$line</span>"</span>
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
</code></pre>
<p>At this time it just prints each line, so we need to capture the output of <code>curl</code> to get the HTTP Code.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-variable">${i}</span>] Doing <span class="hljs-variable">$line</span>"</span>
  statusCode=$(curl -s -o /dev/null -I -w <span class="hljs-string">"%{http_code}"</span> <span class="hljs-variable">$line</span>)
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
</code></pre>
<p>Let's explain a bit of the <code>curl</code> arguments:</p>
<ul>
<li><p><code>-s</code> hides the progress of the "call";</p>
</li>
<li><p><code>-o /dev/null</code> hides the body of the results;</p>
</li>
<li><p><code>-I</code> fetches the headers only (<code>HEAD</code> method);</p>
</li>
<li><p><code>-w "%{http_code}"</code> writes the HTTP Status Code to <code>stdout</code> after exit.</p>
</li>
</ul>
<p>Now the most important check: is the status code a valid one? Right after the declaration of <code>statusCode</code>, let's add this:</p>
<pre><code class="lang-bash"><span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$statusCode</span>"</span> != 2* ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Code: <span class="hljs-variable">$statusCode</span>"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>We are telling the script: if the content of <code>statusCode</code> doesn't start with <em>"2"</em> (200, 201, 204 and so on), print the error.</p>
<p>The script is finished and its final results will be this:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-variable">${i}</span>] Doing <span class="hljs-variable">$line</span>"</span>
  statusCode=$(curl -s -o /dev/null -I -w <span class="hljs-string">"%{http_code}"</span> <span class="hljs-variable">$line</span>)

  <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$statusCode</span>"</span> != 2* ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Code: <span class="hljs-variable">$statusCode</span>"</span>
  <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
</code></pre>
<p>Let's try it. Create a file called <code>urls.txt</code> with this content and save it in the same directory of our script (<code>status_code.sh</code>):</p>
<pre><code class="lang-bash">http://google.com
https://twitter.com/icantfindafakeuser
https://www.facebook.com
</code></pre>
<p>Now run it with <code>bash status_code.sh urls.txt</code> and the output will look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687893429803/133ccd7e-77a1-4c59-aee3-aa68b3072767.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-improvements">Improvements</h2>
<p>The script works pretty well, but if you have a <em>lot of URLs</em> it could be hard to retrieve all the errors, so we can store them in an array and print them at the end of the script.</p>
<p>We will add an empty array at the beginning of the file and then push the "wrong" URL to it when it doesn't return a 2xx status code.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Empty array</span>
errors=()

<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Doing <span class="hljs-variable">$line</span>"</span>
  statusCode=$(curl -s -o /dev/null -I -w <span class="hljs-string">"%{http_code}"</span> <span class="hljs-variable">$line</span>)
  <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$statusCode</span>"</span> != 2* ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Code: <span class="hljs-variable">$statusCode</span>"</span>
    errors+=(<span class="hljs-string">"[<span class="hljs-variable">${statusCode}</span>] <span class="hljs-variable">${line}</span>"</span>)
  <span class="hljs-keyword">fi</span>

  sleep 2
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>
</code></pre>
<p>Now, after the <code>while</code> loop, we are going to print the number of errors and their content.</p>
<p><strong>Note</strong>: I have added a <code>sleep 2</code> to create a delay of 2 seconds between each <code>curl</code>.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Empty array</span>
errors=()

<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Doing <span class="hljs-variable">$line</span>"</span>
  statusCode=$(curl -s -o /dev/null -I -w <span class="hljs-string">"%{http_code}"</span> <span class="hljs-variable">$line</span>)
  <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$statusCode</span>"</span> != 2* ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Code: <span class="hljs-variable">$statusCode</span>"</span>
    errors+=(<span class="hljs-string">"[<span class="hljs-variable">${statusCode}</span>] <span class="hljs-variable">${line}</span>"</span>)
  <span class="hljs-keyword">fi</span>

  sleep 2
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"---------------"</span>
errorsCount=<span class="hljs-variable">${#errors[@]}</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Found <span class="hljs-variable">$errorsCount</span> errors."</span>

<span class="hljs-keyword">if</span> (( <span class="hljs-variable">$errorsCount</span> &gt; 0 )); <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">'%s\n'</span> <span class="hljs-string">"<span class="hljs-variable">${errors[@]}</span>"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>The <code>printf</code> in the last condition will print the items in our array one-per-line, otherwise, they would be all in the same line, hard to read.</p>
<p>If we run again our script, always with <code>bash status_code.sh urls.txt</code>, the new output will be the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687893496587/0dd629ec-9c43-47b8-96bb-9d79f66b9276.png" alt class="image--center mx-auto" /></p>
<p>Of course, it there won't be errors, the array won't be printed.</p>
<h2 id="heading-the-final-touch-line-offset">The final touch: line offset</h2>
<p>The last improvement we can make is to add the possibility to choose the offset of the script, so we can specify from which line we want to start. This is useful if we have a lot of URLs and our script dies or there is some unexpected error.</p>
<p>We will create a <code>i</code> variable incremented in each iteration of the loop. If, when we run the script, there is a second <em>(actually third, the index starts from 0)</em> argument (<code>"$2"</code>), we will check if our <code>i</code> is less than it and, in case, we will skip the current URL.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Empty array</span>
errors=()
i=0

<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">''</span> <span class="hljs-built_in">read</span> -r line || [[ -n <span class="hljs-string">"<span class="hljs-variable">$line</span>"</span> ]]; <span class="hljs-keyword">do</span>
  <span class="hljs-comment"># Increment the variable  </span>
  ((i++))

  <span class="hljs-comment"># Skip if offset if specified and current index less than it</span>
  <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$2</span>"</span> &amp;&amp; <span class="hljs-variable">$i</span> -lt <span class="hljs-string">"<span class="hljs-variable">$2</span>"</span> ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">continue</span>
  <span class="hljs-keyword">fi</span>

  <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-variable">${i}</span>] Doing <span class="hljs-variable">$line</span>"</span>
  statusCode=$(curl -s -o /dev/null -I -w <span class="hljs-string">"%{http_code}"</span> <span class="hljs-variable">$line</span>)
  <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$statusCode</span>"</span> != 2* ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Code: <span class="hljs-variable">$statusCode</span>"</span>
    errors+=(<span class="hljs-string">"[<span class="hljs-variable">${statusCode}</span>] <span class="hljs-variable">${line}</span>"</span>)
  <span class="hljs-keyword">fi</span>

  sleep 2
<span class="hljs-keyword">done</span> &lt; <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"---------------"</span>
errorsCount=<span class="hljs-variable">${#errors[@]}</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Found <span class="hljs-variable">$errorsCount</span> errors."</span>

<span class="hljs-keyword">if</span> (( <span class="hljs-variable">$errorsCount</span> &gt; 0 )); <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">printf</span> <span class="hljs-string">'%s\n'</span> <span class="hljs-string">"<span class="hljs-variable">${errors[@]}</span>"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<p>We added also a "tracker" in the line printing to know the current line index. Let's try it out: for example, we want to start from the second line: <code>bash status_code.sh urls.txt 2</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687893529454/203be118-fb15-4c53-93bc-b89d2255593f.png" alt class="image--center mx-auto" /></p>
<p>This is it, hoping it helped.</p>
]]></content:encoded></item><item><title><![CDATA[DropzoneJS: upload with delay between files]]></title><description><![CDATA[Dropzone is a great product: include their JS and you have a working drag-n-drop enabled multi-upload form.
Some days ago, I had the need to upload multiple files, but one per (at least) second, because on the server side, I put a timestamp in second...]]></description><link>https://theraloss.com/dropzonejs-upload-items-with-delay-between-them</link><guid isPermaLink="true">https://theraloss.com/dropzonejs-upload-items-with-delay-between-them</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Dropzone.js]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Mon, 30 Apr 2018 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Dropzone is a great product: include their JS and you have a working drag-n-drop enabled multi-upload form.</p>
<p>Some days ago, I had the need to upload multiple files, but one per (at least) second, because on the server side, I put a timestamp in seconds near the image and it would be the same for every item uploaded. Since I couldn't switch to milliseconds' timestamps and Dropzone doesn't have a built-in function to create a delay between uploads, I found this solution, thanks also to some Stackoverflow answers.</p>
<p>Note that this code won't work if you use <code>autoProcessQueue</code>, because we don't know when users finished choosing its files, so we init the uploads from a button.</p>
<h3 id="heading-basic-html">Basic HTML</h3>
<p>In our HTML we need to include the CSS (<code>https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.css</code>) and the JS (<code>https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.js</code>).</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Our magic Dropzone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Now, let's create a form where Dropzone will be available.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Our magic Dropzone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/file-upload"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropzone"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"my-awesome-dropzone"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"submit-files"</span>&gt;</span>Upload<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Since Dropzone has autodiscovery enabled by default, it will be immediately working; we are going to disable this to create our DZ instance to use its method. We also added a button that will trigger the upload of the files.</p>
<h2 id="heading-javascript">JavaScript</h2>
<p>We need to adjust the settings of our Dropzone form and initialize it programmatically, disabling the <code>autoDiscover</code> functionality and disabling the auto queue, otherwise selected files are automatically uploaded.</p>
<pre><code class="lang-js">Dropzone.autoDiscover = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">const</span> myDropzone = <span class="hljs-keyword">new</span> Dropzone(<span class="hljs-string">'#my-awesome-dropzone'</span>, {
  <span class="hljs-attr">autoProcessQueue</span>: <span class="hljs-literal">false</span>,
});
</code></pre>
<p>Now it's time to connect the button: when clicked, it will fetch all the files inside our form and, with the <code>processFile</code> method of DZ, we'll tell to Dropzone which file to process.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> $button = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'submit-files'</span>);

$button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Retrieve selected files</span>
  <span class="hljs-keyword">const</span> acceptedFiles = myDropzone.getAcceptedFiles();
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> i = <span class="hljs-number">0</span>; i &lt; acceptedFiles.length; i++) {
    myDropzone.processFile(acceptedFiles[i]);
  }
});
</code></pre>
<p>If you try to click the button, the files will be uploaded, but it works like the default Dropzone, so are going to add a <em>timeout</em> inside the <code>for</code> loop, where the milliseconds of timeout will be the current index * our desired delay, for example, 2000 (2 seconds).</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> $button = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'submit-files'</span>);

$button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Retrieve selected files</span>
  <span class="hljs-keyword">const</span> acceptedFiles = myDropzone.getAcceptedFiles();
  <span class="hljs-keyword">for</span> (consti = <span class="hljs-number">0</span>; i &lt; acceptedFiles.length; i++) {
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      myDropzone.processFile(acceptedFiles[i]);
    }, i * <span class="hljs-number">2000</span>);
  }
})
</code></pre>
<p>What will happen now? The first file will be processed instantly, because its index is 0 and 0x2000 = 0; the second file will be processed after 2s, the third after 4s <strong>from the first file</strong>, which means it will be uploaded after 2s from the second file.</p>
<p><strong>Example</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687812595623/deb32b5e-aa0e-43ef-8e90-a45e982c2a15.gif" alt="Delay upload example" class="image--center mx-auto" /></p>
<p>To check if it correctly works, we can log in the console the current time in the timeout. This would be the output (the upload endpoint doesn't exists in my case):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687893761841/44151e3c-13c7-457b-978a-31c5e6c0395f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-final-code-and-working-demo">Final code and working demo</h2>
<p>The final code, putting it all together, will be this.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Our magic Dropzone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/file-upload"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropzone"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"my-awesome-dropzone"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"submit-files"</span>&gt;</span>Upload<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.4.0/min/dropzone.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-comment">// Init dropzone instance</span>
    Dropzone.autoDiscover = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">const</span> myDropzone = <span class="hljs-keyword">new</span> Dropzone(<span class="hljs-string">'#my-awesome-dropzone'</span>, {
      <span class="hljs-attr">autoProcessQueue</span>: <span class="hljs-literal">false</span>,
    });

    <span class="hljs-comment">// Submit</span>
    <span class="hljs-keyword">const</span> $button = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'submit-files'</span>);
    $button.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-comment">// Retrieve selected files</span>
      <span class="hljs-keyword">const</span> acceptedFiles = myDropzone.getAcceptedFiles();
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> i = <span class="hljs-number">0</span>; i &lt; acceptedFiles.length; i++) {
        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
          myDropzone.processFile(acceptedFiles[i]);
        }, i * <span class="hljs-number">2000</span>);
      }
    })
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You can see a working demo within <a target="_blank" href="https://codepen.io/DaniloPolani/pen/oqKZQQ">this CodePen</a> or here below.</p>
<iframe height="360" src="//codepen.io/DaniloPolani/embed/oqKZQQ/?height=360&amp;theme-id=0&amp;default-tab=js,result&amp;embed-version=2" style="width:100%">See the Pen <a href="https://codepen.io/DaniloPolani/pen/oqKZQQ/">Dropzone upload files with delay</a> by Danilo (<a href="https://codepen.io/DaniloPolani">@DaniloPolani</a>) on <a href="https://codepen.io">CodePen</a>.
</iframe>]]></content:encoded></item><item><title><![CDATA[Fetch Instagram profiles photos without API and without __a=1 parameter]]></title><description><![CDATA[Until now, scraping posts from an Instagram account was very easy: the only thing to do was add a __a=1 as a query string and you have the JSON ready to be read. Now it won't work anymore: this little RegExp will save you time.
In the most recent day...]]></description><link>https://theraloss.com/fetch-instagram-posts-from-profile-without-a-parameter</link><guid isPermaLink="true">https://theraloss.com/fetch-instagram-posts-from-profile-without-a-parameter</guid><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Sun, 15 Apr 2018 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Until now, scraping posts from an Instagram account was very easy: the only thing to do was add a <code>__a=1</code> as a query string and you have the JSON ready to be read. Now it won't work anymore: this little RegExp will save you time.</p>
<p>In the most recent days, the Instagram team decided (<em>probably after Cambridge Analytica?</em>) to restrict their APIs, so the parameter is not more available, returning a <code>403 Forbidden</code> error.</p>
<p>So, what to do now? Should I create an app on Instagram? Access tokens? OAuth?</p>
<p>Nothing like that. The JSON of Instagram is always in the public profile of users, so we just need to get it with a simple RegExp.</p>
<hr />
<p><strong>Note</strong>: this works, of course, only for public profiles.</p>
<p>In the source of a profile, we can find the <code>window._sharedData</code> variable which is a big object with a lot of links; those links are posts. The HTML is this:</p>
<p><code>&lt;script type="text/javascript"&gt;window._sharedData = { ... };&lt;/script&gt;</code></p>
<p>With a simple RegExp, we will be able to fetch its content and convert it into a readable object from our code.</p>
<h3 id="heading-code">Code</h3>
<p>In this case, we'll use Axios as the HTTP library and Node.js as language (JavaScript) with async/await feature.</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">instagramPhotos</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> userInfoSource = <span class="hljs-keyword">await</span> Axios.get(<span class="hljs-string">'https://www.instagram.com/theraloss/'</span>);
}
</code></pre>
<p>Now that we have the source, we need to write the RegExp. Since <code>window._sharedData</code> is an object, we can then read it with <code>JSON.parse</code> function.</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">instagramPhotos</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> userInfoSource = <span class="hljs-keyword">await</span> Axios.get(<span class="hljs-string">'https://www.instagram.com/theraloss/'</span>);

    <span class="hljs-comment">// userInfoSource.data contains the HTML from Axios</span>
    <span class="hljs-keyword">const</span> jsonObject = userInfoSource.data.match(<span class="hljs-regexp">/&lt;script type="text\/javascript"&gt;window\._sharedData = (.*)&lt;\/script&gt;/</span>)[<span class="hljs-number">1</span>].slice(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>)
}
</code></pre>
<p>Here we do three things:</p>
<ol>
<li><p>We use <code>Foo</code> as RegExp to get all the JSON;</p>
</li>
<li><p>We retrieve the 2° item (<code>[1]</code>) of the returned matches from <code>.match()</code> JS function, since the 1° is the whole contains also our "delimitator";</p>
</li>
<li><p>We delete the last character of our object because, on the source page, it ends with <code>;</code> and, of course, it won't be a valid JSON.</p>
</li>
</ol>
<p>Now that we have our JSON, we can easily read it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">instagramPhotos</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> userInfoSource = <span class="hljs-keyword">await</span> Axios.get(<span class="hljs-string">'https://www.instagram.com/theraloss/'</span>);

    <span class="hljs-comment">// userInfoSource.data contains the HTML from Axios</span>
    <span class="hljs-keyword">const</span> jsonObject = userInfoSource.data.match(<span class="hljs-regexp">/&lt;script type="text\/javascript"&gt;window\._sharedData = (.*)&lt;\/script&gt;/</span>)[<span class="hljs-number">1</span>].slice(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>);

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(jsonObject);
}
</code></pre>
<p>That's it! Now you can access the most recents posts with the path <code>userInfo.entry_data.ProfilePage[0].graphql.user.edge_owner_to_timeline_media.edges</code>. If, for example, we want to retrieve the most recent 10 images - excluding the videos - and store them in an array, we could write a code like this.</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">instagramPhotos</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// It will contain our photos' links</span>
    <span class="hljs-keyword">const</span> res = [];

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> userInfoSource = <span class="hljs-keyword">await</span> Axios.get(<span class="hljs-string">'https://www.instagram.com/theraloss/'</span>);

        <span class="hljs-comment">// userInfoSource.data contains the HTML from Axios</span>
        <span class="hljs-keyword">const</span> jsonObject = userInfoSource.data.match(<span class="hljs-regexp">/&lt;script type="text\/javascript"&gt;window\._sharedData = (.*)&lt;\/script&gt;/</span>)[<span class="hljs-number">1</span>].slice(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>);

        <span class="hljs-keyword">const</span> userInfo = <span class="hljs-built_in">JSON</span>.parse(jsonObject);
        <span class="hljs-comment">// Retrieve only the first 10 results</span>
        <span class="hljs-keyword">const</span> mediaArray = userInfo.entry_data.ProfilePage[<span class="hljs-number">0</span>].graphql.user.edge_owner_to_timeline_media.edges.splice(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> media <span class="hljs-keyword">of</span> mediaArray) {
            <span class="hljs-keyword">const</span> node = media.node;

            <span class="hljs-comment">// Process only if is an image</span>
            <span class="hljs-keyword">if</span> ((node.__typename &amp;&amp; node.__typename !== <span class="hljs-string">'GraphImage'</span>)) {
                <span class="hljs-keyword">continue</span>;
            }

            <span class="hljs-comment">// Push the thumbnail src in the array</span>
            res.push(node.thumbnail_src);
        }
    } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Unable to retrieve photos. Reason: '</span> + e.toString());
    }

    <span class="hljs-keyword">return</span> res;
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Easy social login in Go]]></title><description><![CDATA[🤕
The package used in this article is no longer maintained; you should switch to something more robust, like Goth.


In the era of the conversion rate, where a user sign-up is fundamental and must be quick, the social login became a required tile of...]]></description><link>https://theraloss.com/easy-social-login-oauth-in-go-lang</link><guid isPermaLink="true">https://theraloss.com/easy-social-login-oauth-in-go-lang</guid><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Sun, 10 Dec 2017 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">🤕</div>
<div data-node-type="callout-text">The package used in this article is no longer maintained; you should switch to something more robust, like <a target="_blank" href="https://github.com/markbates/goth">Goth</a>.</div>
</div>

<p>In the era of the conversion rate, where a user sign-up is fundamental and must be quick, the social login became a required tile of the UX. Not having this option will probably reduce the registrations, but nowadays enabling this feature is really simple, since every programming language has a lot of libraries that, with few lines of code, create a complete oAuth flow.</p>
<p>In this article we'll explore the case of Go with <a target="_blank" href="https://github.com/danilopolani/gocialite">Gocialite</a>, a Socialite (Laravel) inspired package very simple to use.</p>
<p>The idea of Gocialite is to provide both an easy package but also a hackable one. I was trying Goth, but with Revel, it was very hard to make it working, so why don't write a library more simple? For example, if you want to contribute, you just have to clone a file, change same variable et voilà.</p>
<p>But now, let's coding.</p>
<p>For this example we'll use <a target="_blank" href="https://github.com/gin-gonic/gin">Gin</a> as router, but you can use as wall whatever you want, like Gorilla/Mux or others.</p>
<h2 id="heading-starting-code-and-homepage">Starting code and homepage</h2>
<p>The first thing is of course to create a new Go project, in this case we'll name it "codelikepro/social". Inside it, there will be only a file named <code>main.go</code>. We'll use terminal, but of course you can create folders and the file manually.</p>
<pre><code class="lang-bash">$ mkdir -p <span class="hljs-variable">$GOPATH</span>/src/github.com/codelikepro/social
$ touch <span class="hljs-variable">$GOPATH</span>/src/github.com/codelikepro/social/main.go
</code></pre>
<p>Now that our file exists, we need to start writing the package name and the necessary imports.</p>
<p>First of all, install gocialite and gin with <code>go get</code>:</p>
<pre><code class="lang-bash">$ go get github.com/danilopolani/gocialite
$ go get github.com/gin-gonic/gin
</code></pre>
<p>These are the only two external packages we'll use, so we are ready to code. Note I commented <code>fmt</code> and <code>net/http</code> packages because we'll need them later.</p>
<p>Open <code>main.go</code> with our favorite editor and write the following:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-comment">// "fmt"</span>
    <span class="hljs-comment">// "net/http"</span>

    <span class="hljs-string">"github.com/danilopolani/gocialite"</span>
    <span class="hljs-string">"github.com/gin-gonic/gin"</span>
)

<span class="hljs-comment">// Define our gocialite instance</span>
<span class="hljs-keyword">var</span> gocial = gocialite.NewDispatcher()

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {

}
</code></pre>
<p>At this point in our <code>main</code> function we'll initialize our Gin router and render a homepage with a simple button <em>Sign in with Github</em>.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    router := gin.Default()

    router.GET(<span class="hljs-string">"/"</span>, indexHandler)
    <span class="hljs-comment">// router.GET("/auth/github", redirectHandler)</span>
    <span class="hljs-comment">// router.GET("/auth/github/callback", callbackHandler)</span>

    router.Run(<span class="hljs-string">"127.0.0.1:9090"</span>)
}

<span class="hljs-comment">// Show the homepage</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">indexHandler</span><span class="hljs-params">(c *gin.Context)</span></span> {
    c.Writer.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"&lt;html&gt;&lt;head&gt;&lt;title&gt;Gocialite example&lt;/title&gt;&lt;/head&gt;&lt;body&gt;"</span> +
    <span class="hljs-string">"&lt;a href='/auth/github'&gt;&lt;button&gt;Login with GitHub&lt;/button&gt;&lt;/a&gt;"</span> +
    <span class="hljs-string">"&lt;/body&gt;&lt;/html&gt;"</span>))
}

<span class="hljs-comment">// Redirect user to social login</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">redirectHandler</span><span class="hljs-params">()</span></span> {

}

<span class="hljs-comment">// Collect user info</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">callbackHandler</span><span class="hljs-params">()</span></span> {

}
</code></pre>
<p>With Gin we declared three routes:</p>
<ul>
<li><p>A homepage, responding to route <em>/</em></p>
</li>
<li><p>A redirect bridge at <em>/auth/github</em>. This page will only redirect the user to correct login of the provided driver.</p>
</li>
<li><p>A callback page at <em>/auth/github/callback</em> where we'll collect the user information.</p>
</li>
</ul>
<p>If we test our script running, inside our project folder, <code>go run main.go</code>, we'll just see a button – not working, <em>obviously</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894248609/5ce76924-d879-4e55-bd69-3880a81e948e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-redirect-the-user">Redirect the user</h2>
<p>The next step is to create our <em>bridge</em>, where we retrieve the redirect URL and we send the user to it.</p>
<p>Uncomment the route <code>router.GET("/auth/github", redirectHandler)</code> and let's write the <code>redirectHandler</code> function.</p>
<p>If you don't have a GitHub app, go to <a target="_blank" href="https://github.com/settings/applications/new">New OAuth Application</a> and create a new one. Make sure to use <code>http://localhost:9090/auth/github/callback</code> as <strong>Authorization callback URL</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894115947/ed8c5cce-2f93-4a86-99be-2dc57c9f3a28.png" alt class="image--center mx-auto" /></p>
<p>In the next step you'll Client ID and Client Secret, to populate our <code>appSettings</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894261370/c8ea921a-ef5b-45c5-b784-74607fc49d01.png" alt class="image--center mx-auto" /></p>
<p>With our client information obtained, we are ready to write the code to redirect the user.</p>
<pre><code class="lang-go"><span class="hljs-comment">// Redirect user to social login</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">redirectHandler</span><span class="hljs-params">(c *gin.Context)</span></span> {
    <span class="hljs-comment">// Define our settings</span>
    appSettings := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">string</span>{
        <span class="hljs-string">"clientID"</span>:     <span class="hljs-string">"xxxxxxxxxxxxxx"</span>,
        <span class="hljs-string">"clientSecret"</span>: <span class="hljs-string">"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"</span>,
        <span class="hljs-string">"redirectURL"</span>:  <span class="hljs-string">"http://localhost:9090/auth/github/callback"</span>,
    }

    <span class="hljs-comment">// Retrieve the URL</span>
    authURL, err := gocial.New().
        Driver(<span class="hljs-string">"github"</span>).
        Scopes([]<span class="hljs-keyword">string</span>{<span class="hljs-string">"public_repo"</span>}). <span class="hljs-comment">// Specify custom scopes</span>
        Redirect(
            appSettings[<span class="hljs-string">"clientID"</span>],
            appSettings[<span class="hljs-string">"clientSecret"</span>],
            appSettings[<span class="hljs-string">"redirectURL"</span>],
        )

    <span class="hljs-comment">// Check for errors</span>
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.Writer.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Error: "</span> + err.Error()))
        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-comment">// Redirect to authURL with status 302</span>
    c.Redirect(http.StatusFound, authURL)
}
</code></pre>
<p>We used <code>Driver()</code> method to specify our provider, in this case, <em>Github</em>, but you can use <a target="_blank" href="https://github.com/danilopolani/gocialite/blob/master/README.md#available-drivers">a lot of other social</a>. Then, with <code>Scopes()</code>, we defined our custom scopes. In our case, we want access to its public repositories, but if you don't need custom privileges you can omit the function. <strong>Note</strong> that we didn't pass "user" scopes because Gocialite includes them by itself.</p>
<p>Finally, in the <code>Redirect()</code> function we pass our information and we retrieve our authURL.</p>
<p>Before redirecting the user, we check for errors and then, with <code>c.Redirect()</code>, a Gin function, we send the user to GitHub login approval with status 302 (Temporary redirect) taken from the <a target="_blank" href="https://golang.org/pkg/net/http/#pkg-constants">net/http</a> package (click it to see all the available status). <strong>Remember</strong> to uncomment the <code>"net/http"</code> line in our imports!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894173549/b6db2a97-a16c-4d69-a76c-4b7e624639e5.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-get-user-info">Get user info</h2>
<p>The last part of our flow is the callback to handle user information and we are going to write it in the <code>callbackHandler</code> function.</p>
<p>Here we have to pass to the <code>Handle()</code> method two values retrievable from the query string: <strong>state</strong> and <strong>code</strong>. <strong>State</strong> is a secret string that Gocialite passed in the beginning to the provider and now it checks if it's equal, in order to block <a target="_blank" href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF attacks</a>. <strong>Code</strong> is a string needed to start the exchange with the provider for user authentication.</p>
<p>It will return three variables: a <em>user</em>, a <em>token</em> and an <em>error</em>. If there's an error, the first two will be nil, otherwise the error will be nil.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">callbackHandler</span><span class="hljs-params">(c *gin.Context)</span></span> {
    <span class="hljs-comment">// Retrieve query params for state and code</span>
    state := c.Query(<span class="hljs-string">"state"</span>)
    code := c.Query(<span class="hljs-string">"code"</span>)

    <span class="hljs-comment">// Handle callback and check for errors</span>
    user, token, err := gocial.Handle(state, code)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.Writer.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Error: "</span> + err.Error()))
        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-comment">// Print token information</span>
    fmt.Printf(<span class="hljs-string">"%#v"</span>, token)
    <span class="hljs-comment">// Print in terminal user information</span>
    fmt.Printf(<span class="hljs-string">"%#v"</span>, user)

    <span class="hljs-comment">// If no errors, show provider name</span>
    c.Writer.Write([]<span class="hljs-keyword">byte</span>(<span class="hljs-string">"Hi, "</span> + user.FullName))
}
</code></pre>
<p>If everything went well, it will print in your browser "<em>Hi, NAME</em>", in my case "<em>Hi, Grork</em>".</p>
<p>We also use <code>fmt.Printf</code> with the <code>%#v</code> format to print in the terminal the full <code>user</code> and <code>token</code> content, but in production you should remove them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894293945/6894db54-d986-43b4-b951-99c4fe961df1.png" alt class="image--center mx-auto" /></p>
<p>The <code>user</code> variable will contain the main information of the current logged-in user:</p>
<ul>
<li><p>ID</p>
</li>
<li><p>Username</p>
</li>
<li><p>FirstName</p>
</li>
<li><p>LastName</p>
</li>
<li><p>FullName</p>
</li>
<li><p>Email</p>
</li>
<li><p>Avatar</p>
</li>
<li><p>Raw</p>
</li>
</ul>
<p>They will be all strings except for raw, which is a map of interfaces (<code>map[string]interface{}</code>) containing the original JSON returned by the provider. Note that all fields (except <em>Raw</em>) could be empty.</p>
<p>The token will be an <a target="_blank" href="https://godoc.org/golang.org/x/oauth2#Token">oauth2.Token</a> struct containing the following data:</p>
<ul>
<li><p>AccessToken <em>string</em></p>
</li>
<li><p>TokenType <em>string</em></p>
</li>
<li><p>RefreshToken <em>string</em></p>
</li>
<li><p>Expiry <em>time.Time</em></p>
</li>
</ul>
<p>If you want to set up a multi provider social login, <a target="_blank" href="https://github.com/danilopolani/gocialite/wiki/Multi-provider-example">here there is a full example</a>.</p>
<hr />
<p>If you like Gocialite, feel free to leave a star!</p>
<iframe src="https://ghbtns.com/github-btn.html?user=danilopolani&amp;repo=gocialite&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px"></iframe>]]></content:encoded></item><item><title><![CDATA[Create a rotating proxy crawler in Python 3]]></title><description><![CDATA[One day, a friend of mine was crawling a website (let's be honest: everybody crawls someone other) but, of course, he was banned a lot of times and he had to start Tor or find some other system. After some research, finding a complete rotating proxy ...]]></description><link>https://theraloss.com/create-a-crawler-with-rotating-ip-proxy-in-python</link><guid isPermaLink="true">https://theraloss.com/create-a-crawler-with-rotating-ip-proxy-in-python</guid><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Tue, 31 Oct 2017 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>One day, a friend of mine was crawling a website (let's be honest: everybody crawls someone other) but, of course, he was banned a lot of times and he had to start Tor or find some other system. After some research, finding a complete rotating proxy system in Python was impossible, so I created my own and I want to share it with you.</p>
<p>The crawler will follow this flow:</p>
<p><strong>1.</strong> At the very beginning, it retrieves a list of valid HTTPS proxies from <em>sslproxies.org</em></p>
<ol>
<li><p>At the very beginning, it retrieves a list of valid HTTPS proxies from <em>sslproxies.org</em></p>
<ol>
<li><p>With <em>BeautifulSoup</em>, it scans the table and obtains the values</p>
</li>
<li><p><em>1.2.</em> With the Python module <code>Fake-useragent</code>, simulates a user agent to simulate a real request</p>
</li>
</ol>
</li>
<li><p>It does its stuff and calls (in this case, we use a range from 1 to 100 to test it)</p>
</li>
<li><p>Every 10 requests (you can of course increment this value), he changes proxy</p>
</li>
<li><p>If the proxy is broken, it deletes it from our list and catches another one.</p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Our code will be compatible with Python 3.x, so make sure you have it installed. We are going to use also two external modules:</p>
<ul>
<li><p><a target="_blank" href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-beautiful-soup">BeautifulSoup</a>, to scrape the source. Install it with <code>pip3 install beautifulsoup4</code></p>
</li>
<li><p><a target="_blank" href="https://pypi.python.org/pypi/fake-useragent">Fake-useragent</a> to retrieve a random User Agent, to simulate a "real" request. Install it with <code>pip3 install fake-useragent</code>. <strong>Note</strong>: This is optional. You can replace it with a "hard coded" and fixed string, it doesn't matter.</p>
</li>
</ul>
<h2 id="heading-coding-a-hot-damn-script">Coding a hot damn script</h2>
<p>The first part of every language is the "inclusion" section, so we are going to include our needed libraries.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> urllib.request <span class="hljs-keyword">import</span> Request, urlopen
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup
<span class="hljs-keyword">from</span> fake_useragent <span class="hljs-keyword">import</span> UserAgent
<span class="hljs-keyword">import</span> random
</code></pre>
<p>Now we generate a random user agent and initialize an empty list where we'll put our proxies.</p>
<pre><code class="lang-python">ua = UserAgent() <span class="hljs-comment"># From here we generate a random user agent</span>
proxies = [] <span class="hljs-comment"># Will contain proxies [ip, port]</span>
</code></pre>
<p>Since we like a structure where the most important code is at the top and the other small functions at the bottom (and in Python this is "not possible"), our code will be in a method called <code>main</code> which will be recalled at the end, like this:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Main function</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    <span class="hljs-comment"># Our code here</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
  main()
</code></pre>
<h3 id="heading-retrieve-proxies">Retrieve proxies</h3>
<p>Because we want a rotating proxy system, we need to retrieve them from the beginning and we'll achieve that by scraping the site <em>sslproxies.org</em> with <code>BeautifulSoup</code> (<em>BS</em> from now), the most famous scraping library.</p>
<p>Inside our <code>main</code> function, we initialize a new <code>Request</code>, attach a User Agent to it and retrieve its contents.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Retrieve latest proxies</span>
proxies_req = Request(<span class="hljs-string">'https://www.sslproxies.org/'</span>)
proxies_req.add_header(<span class="hljs-string">'User-Agent'</span>, ua.random)
proxies_doc = urlopen(proxies_req).read().decode(<span class="hljs-string">'utf8'</span>)
</code></pre>
<p>Now creating a new BS instance, we scan all the rows contained in the main table.</p>
<pre><code class="lang-python">soup = BeautifulSoup(proxies_doc, <span class="hljs-string">'html.parser'</span>)
proxies_table = soup.find(id=<span class="hljs-string">'proxylisttable'</span>)

<span class="hljs-comment"># Save proxies in the array</span>
<span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> proxies_table.tbody.find_all(<span class="hljs-string">'tr'</span>):
  proxies.append({
    <span class="hljs-string">'ip'</span>:   row.find_all(<span class="hljs-string">'td'</span>)[<span class="hljs-number">0</span>].string,
    <span class="hljs-string">'port'</span>: row.find_all(<span class="hljs-string">'td'</span>)[<span class="hljs-number">1</span>].string
  })
</code></pre>
<p>With <code>proxies.append</code>, we save a new <em>dict</em> with the keys <code>ip</code> and <code>port</code>. At this point, all we have to do is to get a random proxy from our list. Since we need this many times, we'll put the code inside a function called <code>random_proxy</code>, right before the line <code>if __name__ == '__main__'</code>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Retrieve a random index proxy (we need the index to delete it if not working)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">random_proxy</span>():</span>
  <span class="hljs-keyword">return</span> random.randint(<span class="hljs-number">0</span>, len(proxies) - <span class="hljs-number">1</span>)
</code></pre>
<p>Note that we return the <strong>key</strong> and not the value of the proxy: why this? Because if a proxy fails, we want to remove it from the list, so we won't catch it again.</p>
<h3 id="heading-request-request-request">Request, request, request</h3>
<p>For testing purposes, we'll make 100 requests to <em>icanhazip.com</em> which will return our current IP (proxied, of course). Using the <code>random_proxy</code> method written above, we retrieve a proxy and then attach it to our request and every 10 requests we change IP.</p>
<pre><code class="lang-python">proxy_index = random_proxy()
proxy = proxies[proxy_index]

<span class="hljs-keyword">for</span> n <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, <span class="hljs-number">100</span>):
    req = Request(<span class="hljs-string">'http://icanhazip.com'</span>)
    req.set_proxy(proxy[<span class="hljs-string">'ip'</span>] + <span class="hljs-string">':'</span> + proxy[<span class="hljs-string">'port'</span>], <span class="hljs-string">'http'</span>)

    <span class="hljs-comment"># Every 10 requests, generate a new proxy</span>
    <span class="hljs-keyword">if</span> n % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>:
      proxy_index = random_proxy()
      proxy = proxies[proxy_index]
</code></pre>
<p>Now with a try/catch we intercept broken proxies and delete them from our list and notice the user, or, if the request has been successful, we print what IP <em>icanhazip</em> sees.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Make the call</span>
    <span class="hljs-keyword">try</span>:
      my_ip = urlopen(req).read().decode(<span class="hljs-string">'utf8'</span>)
      print(<span class="hljs-string">'#'</span> + str(n) + <span class="hljs-string">': '</span> + my_ip)
    <span class="hljs-keyword">except</span>: <span class="hljs-comment"># If error, delete this proxy and find another one</span>
      <span class="hljs-keyword">del</span> proxies[proxy_index]
      print(<span class="hljs-string">'Proxy '</span> + proxy[<span class="hljs-string">'ip'</span>] + <span class="hljs-string">':'</span> + proxy[<span class="hljs-string">'port'</span>] + <span class="hljs-string">' deleted.'</span>)
      proxy_index = random_proxy()
      proxy = proxies[proxy_index]
</code></pre>
<p>Save the file (in my case <code>scrape_me.py</code>, just a tribute to "Rape me" of Nirvana) and execute it. You'll see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687894832764/cde1c43c-6054-4d65-9eb6-abb9015e3c4b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-code">Code</h3>
<p>This is the full code or, if you want, you can see it in <a target="_blank" href="https://github.com/danilopolani/rotating-proxy-python">this Github repository</a>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> urllib.request <span class="hljs-keyword">import</span> Request, urlopen
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup
<span class="hljs-keyword">from</span> fake_useragent <span class="hljs-keyword">import</span> UserAgent
<span class="hljs-keyword">import</span> random

ua = UserAgent() <span class="hljs-comment"># From here we generate a random user agent</span>
proxies = [] <span class="hljs-comment"># Will contain proxies [ip, port]</span>

<span class="hljs-comment"># Main function</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
  <span class="hljs-comment"># Retrieve latest proxies</span>
  proxies_req = Request(<span class="hljs-string">'https://www.sslproxies.org/'</span>)
  proxies_req.add_header(<span class="hljs-string">'User-Agent'</span>, ua.random)
  proxies_doc = urlopen(proxies_req).read().decode(<span class="hljs-string">'utf8'</span>)

  soup = BeautifulSoup(proxies_doc, <span class="hljs-string">'html.parser'</span>)
  proxies_table = soup.find(id=<span class="hljs-string">'proxylisttable'</span>)

  <span class="hljs-comment"># Save proxies in the array</span>
  <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> proxies_table.tbody.find_all(<span class="hljs-string">'tr'</span>):
    proxies.append({
      <span class="hljs-string">'ip'</span>:   row.find_all(<span class="hljs-string">'td'</span>)[<span class="hljs-number">0</span>].string,
      <span class="hljs-string">'port'</span>: row.find_all(<span class="hljs-string">'td'</span>)[<span class="hljs-number">1</span>].string
    })

  <span class="hljs-comment"># Choose a random proxy</span>
  proxy_index = random_proxy()
  proxy = proxies[proxy_index]

  <span class="hljs-keyword">for</span> n <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, <span class="hljs-number">100</span>):
    req = Request(<span class="hljs-string">'http://icanhazip.com'</span>)
    req.set_proxy(proxy[<span class="hljs-string">'ip'</span>] + <span class="hljs-string">':'</span> + proxy[<span class="hljs-string">'port'</span>], <span class="hljs-string">'http'</span>)

    <span class="hljs-comment"># Every 10 requests, generate a new proxy</span>
    <span class="hljs-keyword">if</span> n % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>:
      proxy_index = random_proxy()
      proxy = proxies[proxy_index]

    <span class="hljs-comment"># Make the call</span>
    <span class="hljs-keyword">try</span>:
      my_ip = urlopen(req).read().decode(<span class="hljs-string">'utf8'</span>)
      print(<span class="hljs-string">'#'</span> + str(n) + <span class="hljs-string">': '</span> + my_ip)
    <span class="hljs-keyword">except</span>: <span class="hljs-comment"># If error, delete this proxy and find another one</span>
      <span class="hljs-keyword">del</span> proxies[proxy_index]
      print(<span class="hljs-string">'Proxy '</span> + proxy[<span class="hljs-string">'ip'</span>] + <span class="hljs-string">':'</span> + proxy[<span class="hljs-string">'port'</span>] + <span class="hljs-string">' deleted.'</span>)
      proxy_index = random_proxy()
      proxy = proxies[proxy_index]

<span class="hljs-comment"># Retrieve a random index proxy (we need the index to delete it if not working)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">random_proxy</span>():</span>
  <span class="hljs-keyword">return</span> random.randint(<span class="hljs-number">0</span>, len(proxies) - <span class="hljs-number">1</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
  main()
</code></pre>
<h3 id="heading-additional-resources">Additional resources</h3>
<p>Take a look at this <a target="_blank" href="http://www.zachburchill.ml/scraping_problems">awesome article</a> by Zach Burchill about how to scrape a lot of data in Python.</p>
]]></content:encoded></item><item><title><![CDATA[Schedule crontab jobs in an AdonisJs app]]></title><description><![CDATA[After some weeks we started to migrate our Laravel apps to Node.js and we have chosen to use, as framework, AdonisJs, because of its similarity with Laravel.
It works great, but something's missing: the possibility to schedule a job to run it in a sp...]]></description><link>https://theraloss.com/schedule-jobs-crontab-like-in-an-adonisjs-app</link><guid isPermaLink="true">https://theraloss.com/schedule-jobs-crontab-like-in-an-adonisjs-app</guid><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Tue, 24 Oct 2017 00:00:00 GMT</pubDate><content:encoded><![CDATA[<p>After some weeks we started to migrate our Laravel apps to Node.js and we have chosen to use, as framework, AdonisJs, because of its similarity with Laravel.</p>
<p>It works great, but something's missing: the possibility to schedule a job to run it in a specific time, like the crontab and the "schedule" of Laravel.</p>
<p>You have two ways to achieve it:</p>
<ol>
<li><p>Edit the crontab of your server (<code>crontab -e</code>) and add something like <code>15 * * * * cd /var/www/your_project &amp;&amp; adonis yourcommand &gt;/dev/null 2&gt;&amp;1</code> (just an example);</p>
</li>
<li><p>Use <a target="_blank" href="https://github.com/danilopolani/adonis-scheduler">Adonis Scheduler</a>, which is a third-party library (not official) but very useful.</p>
</li>
</ol>
<p>The repository I linked is a fork of a fork. The original repo doesn't have the support for Adonis 4 (the latest version), so the user <a target="_blank" href="https://github.com/ntvsx193">ntvsx193</a> updated it making it work with v4 and I created a "security" copy (forking its fork, yes).</p>
<h3 id="heading-installation">Installation</h3>
<p><strong>Note</strong>: If you are using Adonis v3.x, please follow the <a target="_blank" href="https://github.com/nrempel/adonis-scheduler">original installation tutorial on the repository</a>.</p>
<p>The first part of everything you do is the installation. Since its a node.js project, we use <strong>npm</strong> with this command: <code>npm i --save git+https://github.com/danilopolani/adonis-scheduler.git</code>. Note we used the syntax <code>git+</code> because the updated version is not available on npm packages, so we link directly to the repository.</p>
<p>Now open your <code>start/app.js</code> file and...</p>
<p><strong>1.</strong> Add <code>adonis-scheduler/providers/SchedulerProvider</code> to your providers array like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> providers = [
  ...
  <span class="hljs-string">'adonis-scheduler/providers/SchedulerProvider'</span>,
];
</code></pre>
<p><strong>2.</strong> Create the alias:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> aliases = {
  ...
  Scheduler: <span class="hljs-string">'Adonis/Addons/Scheduler'</span>,
};
</code></pre>
<p><strong>3.</strong> Register the command:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> aceProviders = [
  ...
  <span class="hljs-string">'adonis-scheduler/providers/CommandsProvider'</span>,
];
</code></pre>
<p>Now let's write a task.</p>
<h3 id="heading-tasks-schedule-the-world">Tasks: schedule the world</h3>
<p>The updates for our dear <em>ntvsx193</em> include a command to generate a task; you need just to run <code>adonis make:task ScheduleIt</code> from your terminal and here you go. <strong>Note</strong>: The <code>make:task</code> command will strip the word "Task" from the name, so if you write "MyTask" it will create a file named only "My.js".</p>
<p>Open your generated file at <code>app/Tasks/ScheduleIt.js</code> and you'll see this structure:</p>
<pre><code class="lang-js"><span class="hljs-meta">'use strict'</span>

<span class="hljs-keyword">const</span> Task = use(<span class="hljs-string">'Task'</span>);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ScheduleIt</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Task</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">get</span> <span class="hljs-title">schedule</span>() {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'0 */1 * * * *'</span>;
  }

  <span class="hljs-keyword">async</span> handle() {
    <span class="hljs-built_in">this</span>.info(<span class="hljs-string">'Task ScheduleIt handle'</span>);
  }
}

<span class="hljs-built_in">module</span>.exports = ScheduleIt;
</code></pre>
<p>In the <strong>schedule</strong> section, you have to put the crontab value. I suggest you to use the useful <a target="_blank" href="https://crontab-generator.org/">Crontab generator</a> tool to know what to put in here.</p>
<p>For example, if I want to schedule my job at <em>3:15am</em> of every day, so I choose the minute and the hour in the list at the right of each box, leaving Days, Months and Weekday set to "Every ...".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687895048661/cab4444c-c77a-456c-a3b8-6da8faabac76.png" alt class="image--center mx-auto" /></p>
<p>Write whatever you want under "<em>Command to execute</em>" and press Enter. It will print the complete line to copy-paste, for example <code>15 3 * * * foo &gt;/dev/null 2&gt;&amp;1</code>, where 'foo' is my fake command. We need to retrieve only the part with the asterisks: <code>15 3 * * *</code>.</p>
<p>Putting it in the <em>schedule</em> function, it becomes this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">static</span> <span class="hljs-keyword">get</span> <span class="hljs-title">schedule</span>() {
  <span class="hljs-keyword">return</span> <span class="hljs-string">'15 3 * * *'</span>;
}
</code></pre>
<p>Since we need to test if it's working, we use it as "schedule time" every minute: <code>* * * * *</code>.</p>
<p>In the <strong>handle</strong> section, you'll put your real code. Since it's an <code>async</code> method, you can use as well the <code>await</code> functionality of ES6.</p>
<p>In this tutorial, we leave the <code>this.info</code> helper to print in our terminal, every minute, the phrase <em>Task ScheduleIt handle</em>.</p>
<h3 id="heading-run-the-scheduler">Run the scheduler</h3>
<p>The repository tells us only to run the command <code>adonis run:scheduler</code>, but of course, in our server we can't run it from the terminal, but we need something to keep it alive.</p>
<p>I have found three ways for this:</p>
<ol>
<li><p>Use <strong>pm2</strong>, the "equivalent" of supervisor for node.js processes.</p>
</li>
<li><p>Use <strong>supervisor</strong> to keep the process alive.</p>
</li>
<li><p>Include the schedule runner in your project.</p>
</li>
</ol>
<p>After thinking about them, I realized that the last one is the more simple to adopt: with <em>pm2</em> you need to create a new process and save it (to preserve it when you reboot your server); with <em>supervisor</em> you need to create a config file for the process, reread and update.</p>
<p>With the last option you just need to copy and paste a code in a file. Simple, isn't it?</p>
<p>Open the file <code>start/kernel.js</code> and right after the line <code>const Server = use ('Server')</code>, put this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/*
|--------------------------------------------------------------------------
| Run Scheduler
|--------------------------------------------------------------------------
|
| Run the scheduler on boot of the web sever.
|
*/</span>
<span class="hljs-keyword">const</span> Scheduler = use(<span class="hljs-string">'Adonis/Addons/Scheduler'</span>);
Scheduler.run();
</code></pre>
<p>Now we are ready to test it.</p>
<h3 id="heading-test">Test</h3>
<p>Run the app like always, with <code>adonis serve --dev</code> and wait one minute to see the first message.</p>
<p>After two minutes, your terminal will look like the screenshot below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687895109042/e7f853e0-7e4d-4b06-a686-c6d2bae8b260.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Set up cache with Redis in Node.js]]></title><description><![CDATA[😶
Although the package used in this article still works, I suggest you to switch to something like Keyv which is more scalable and multi-provider.


It's 2020 and having a cache it's today a required part of your stack. For example, you could cache ...]]></description><link>https://theraloss.com/set-up-cache-with-redis-in-node-js</link><guid isPermaLink="true">https://theraloss.com/set-up-cache-with-redis-in-node-js</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Redis]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Tue, 24 Oct 2017 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">😶</div>
<div data-node-type="callout-text">Although the package used in this article still works, I suggest you to switch to something like <a target="_blank" href="https://github.com/jaredwray/keyv">Keyv</a> which is more scalable and multi-provider.</div>
</div>

<p>It's 2020 and having a cache it's today a required part of your stack. For example, you could cache your database response for X time (or until a new entry comes in) to stop doing a query (or more!) for every request.</p>
<p>Recently I had to implement it in <strong>Node.js</strong> and I've chosen to go with <strong>Redis</strong>. I was looking for a decent but <strong>simple-to-use library</strong> and I didn't find so much, so I decided to write my own: <a target="_blank" href="https://github.com/danilopolani/redache">Redache</a>.</p>
<p>Let's see how to implement it!</p>
<h2 id="heading-installation">Installation</h2>
<p>To install the library, just run from your terminal <code>yarn add redache</code> or, if you use npm: <code>npm i redache --save</code>.</p>
<p>Then, of course, you have to include it in your project. If you use ES6 features, you can use <code>import</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Redache <span class="hljs-keyword">from</span> <span class="hljs-string">'redache'</span>;

<span class="hljs-comment">// ...</span>
</code></pre>
<p>Otherwise, you can go with the CommonJS way:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Redache = <span class="hljs-built_in">require</span>(<span class="hljs-string">'redache'</span>);

<span class="hljs-comment">// ...</span>
</code></pre>
<p>The last but not least step is to initialize it. This is a minimal configuration, but you can check out all the available parameters in the Redis official package: https://github.com/NodeRedis/node_redis#options-object-properties</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> Redache({
    <span class="hljs-attr">host</span>: <span class="hljs-string">'127.0.0.1'</span>,
    <span class="hljs-attr">port</span>: <span class="hljs-number">6379</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">'foobar'</span>,
});
</code></pre>
<h2 id="heading-save-values">Save values</h2>
<p>I wanted to keep the library <strong>as easy as possible</strong>. To store a value, you just have to call the <code>.set()</code> method providing a key, a value and a TTL (expiration time).<br />Usually with these libraries, you have to specify the TTL as a number representing the seconds, but guess what? I didn't want to make your life harder, but easier, so... You can choose between <strong>four methods</strong> to specify your expiration and the first one si my favorite.</p>
<p><strong>1. Human readable TTL</strong> Just pass a format like <code>number TIME_UNIT</code> to set the expiration. It uses <code>moment</code> behind the scenes to "decode" it, so <code>TIME_UNIT</code> can be something like <code>days</code>, <code>day</code>, <code>d</code>, <code>month</code> and so on.</p>
<pre><code class="lang-js"><span class="hljs-comment">// It expires in 5 hours</span>
<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'value'</span>, <span class="hljs-string">'5 hours'</span>);
</code></pre>
<p><strong>2.</strong> <code>Date</code> instance If you're more "old school" and you have a <code>Date</code> instance, don't worry, we accept it as well.</p>
<pre><code class="lang-js"><span class="hljs-comment">// It expires the 1st Jan 2050</span>
<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'value'</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2050</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>));
</code></pre>
<p><strong>3.</strong> <code>moment</code> instance We said it uses <code>moment</code> under the hood, so <em>of course</em> you can provide a <code>moment</code> object.</p>
<pre><code class="lang-js"><span class="hljs-comment">// It expires in 1 month</span>
<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'value'</span>, moment().add(<span class="hljs-number">1</span>, <span class="hljs-string">'month'</span>));
</code></pre>
<p><strong>4. Seconds</strong> And finally, if you're a real boss, you can pass a number representing the seconds. I don't know why you should, but ehy, up to you!</p>
<pre><code class="lang-js"><span class="hljs-comment">// It expires in 3600 seconds -&gt; 1 hour</span>
<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'value'</span>, <span class="hljs-number">3600</span>);
</code></pre>
<h3 id="heading-objects-and-arrays">Object(s) and Array(s)</h3>
<p>Most of the time you want to save a whole object or array, but the native Redis functions won't let you do that. But we do. Just pass your desired variable and the library will take care of JSON serializing and deserializing it.</p>
<h2 id="heading-retrieve-values">Retrieve values</h2>
<p>After you saved your value you may desire (or may not?) to retrieve it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>);
</code></pre>
<p>As we said, <strong>Redache</strong> accepts objects and arrays too, let's see how:</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, { <span class="hljs-attr">foo</span>: <span class="hljs-string">'bar'</span> }, <span class="hljs-string">'1 hour'</span>);

<span class="hljs-comment">// Retrieve it</span>
<span class="hljs-comment">// It will be: { foo: 'bar' } but stored as '{"foo":"bar"}'</span>
<span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>);
</code></pre>
<p>Easy, right? Now let's say that you want to store a value if a key is not found (or expired) on Redis, you would write something like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>);
<span class="hljs-keyword">if</span> (value === <span class="hljs-literal">null</span>) {
    value = <span class="hljs-string">'my fallback'</span>;
    <span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'my fallback'</span>, <span class="hljs-string">'1 day'</span>);
}
</code></pre>
<p><strong>NOOOOOOO!!</strong> The library can manage this for you, in just one line! In fact, the 2nd and 3rd arguments of <code>.get()</code> are, respectively, the fallback value and the fallback TTL.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>, <span class="hljs-string">'my fallback'</span>, <span class="hljs-string">'1 day'</span>);
</code></pre>
<p>Isn't it more simple? The <strong>fallback value accepts as well a function</strong> (async too):</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-string">'my fallback'</span>, <span class="hljs-string">'1 day'</span>);

<span class="hljs-comment">// Async example</span>
<span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Promise</span>.resolve(<span class="hljs-string">'my fallback'</span>), <span class="hljs-string">'1day '</span>);
</code></pre>
<h2 id="heading-delete-a-key">Delete a key</h2>
<p>Like alchemy, you can create and destroy (<em>yes, I recently saw Fullmetal Alchemist</em>). Just invoke <code>.forget()</code> method specifying your key.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Save a record</span>
<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'key'</span>, <span class="hljs-string">'value'</span>, <span class="hljs-string">'1 day'</span>);

<span class="hljs-comment">// It returns `value`</span>
<span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'key'</span>);

<span class="hljs-comment">// Now let's remove it</span>
<span class="hljs-keyword">await</span> cache.forget(<span class="hljs-string">'key'</span>);

<span class="hljs-comment">// It will return null now, because we removed it</span>
<span class="hljs-keyword">const</span> valueAgain = cache.get(<span class="hljs-string">'key'</span>);
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That's it! I hope this library can help you as much as it helped me and don't forget to leave a star!</p>
<p><a target="_blank" href="https://github.com/danilopolani/redache">Star</a></p>
]]></content:encoded></item><item><title><![CDATA[Customize the action button of a toast in Ionic 1 Framework]]></title><description><![CDATA[The problem
You are creating your hybrid app and when your user deletes an item from the cart you want to show a toast with the confirmation message and a button to undo the action, but in the documentation there's no way to customize the callback, s...]]></description><link>https://theraloss.com/customize-action-button-of-toast-in-ionic-framework</link><guid isPermaLink="true">https://theraloss.com/customize-action-button-of-toast-in-ionic-framework</guid><category><![CDATA[Ionic Framework]]></category><dc:creator><![CDATA[Danilo Polani]]></dc:creator><pubDate>Mon, 04 Sep 2017 00:00:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-the-problem">The problem</h3>
<p>You are creating your hybrid app and when your user deletes an item from the cart you want to show a toast with the confirmation message and a button to <strong>undo the action</strong>, but in the documentation there's no way to customize the callback, so how to do this?</p>
<h3 id="heading-my-boring-problem-and-the-way-to-solve-it">My boring problem and the way to solve it</h3>
<p>This was, more or less, my case. After a lot of searches, and a nice headache - maybe it was only the air conditioner - when I decided to renounce finally the answer!</p>
<p>Things to know about how to solve it:</p>
<ol>
<li><p>The <code>ToastController</code> class has a dismiss method;</p>
</li>
<li><p>The dismiss method callback has <em>two</em> arguments, and not one.</p>
</li>
</ol>
<p>Why this is so important? Because without the second parameter, which specifies the <em>motivation</em> of the dismiss: the callback is invoked when the toast disappears automatically after the specified time <strong>or</strong> when the user clicks the button, there was no way to understand when was the button and when the timeout was.</p>
<p>But stop talking and start coding.</p>
<h3 id="heading-code">Code!</h3>
<p>First of all, include <code>ToastController</code> in your <code>.ts</code> page:</p>
<p><code>import { ToastController } from 'ionic-angular';</code></p>
<p>And include it in your constructor:</p>
<p><code>constructor(public toastCtrl: ToastController) {</code></p>
<p>Now we can create the toast, like the documentation, customize also the close button message.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a toast</span>
<span class="hljs-keyword">let</span> toast = <span class="hljs-built_in">this</span>.toastCtrl.create({
    <span class="hljs-attr">message</span>: <span class="hljs-string">'Item removed from the cart'</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">3000</span>,
    <span class="hljs-attr">showCloseButton</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">closeButtonText</span>: <span class="hljs-string">'Undo'</span>,
});
</code></pre>
<p>I was talking about the dismiss callback, so let's write it.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Handle "undo" action</span>
toast.onDidDismiss(<span class="hljs-function">(<span class="hljs-params">data, role</span>) =&gt;</span> {
    <span class="hljs-comment">//</span>
});
</code></pre>
<p>As you can see, there's a second parameter called <code>role</code> and with it, we can execute our code <strong>only when the user hits the button</strong>.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Handle "undo" action</span>
toast.onDidDismiss(<span class="hljs-function">(<span class="hljs-params">data, role</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (role == <span class="hljs-string">'close'</span>) {
        yourRecoveringMethod();
    }
});
</code></pre>
<p>And, finally, let's show the toast with <code>toast.present();</code>. The final code will look like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a toast</span>
<span class="hljs-keyword">let</span> toast = <span class="hljs-built_in">this</span>.toastCtrl.create({
    <span class="hljs-attr">message</span>: <span class="hljs-string">'Item removed from the cart'</span>,
    <span class="hljs-attr">duration</span>: <span class="hljs-number">3000</span>,
    <span class="hljs-attr">showCloseButton</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">closeButtonText</span>: <span class="hljs-string">'Undo'</span>,
});

<span class="hljs-comment">// Handle "undo" action</span>
toast.onDidDismiss(<span class="hljs-function">(<span class="hljs-params">data, role</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (role == <span class="hljs-string">'close'</span>) {
        yourRecoveringMethod();
    }
});

toast.present();
</code></pre>
<p>This was so simple, why not include it in the documentation? This will be a mystery.</p>
]]></content:encoded></item></channel></rss>