<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chirill Ceban</title>
    <description>The latest articles on DEV Community by Chirill Ceban (@bitkidd).</description>
    <link>https://dev.to/bitkidd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F321393%2Faa10cb4a-fd25-45ea-9636-350b89417e96.jpeg</url>
      <title>DEV Community: Chirill Ceban</title>
      <link>https://dev.to/bitkidd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bitkidd"/>
    <language>en</language>
    <item>
      <title>Deploy Ghost using Dokku</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Fri, 03 Jun 2022 09:53:10 +0000</pubDate>
      <link>https://dev.to/bitkidd/deploy-ghost-using-dokku-151j</link>
      <guid>https://dev.to/bitkidd/deploy-ghost-using-dokku-151j</guid>
      <description>&lt;p&gt;I started using &lt;a href="https://ghost.org"&gt;Ghost&lt;/a&gt; as a headless cms for my &lt;a href="https://bitkidd.dev"&gt;personal website&lt;/a&gt; and it works like a charm, but I had to spend some time to try and deploy it using &lt;a href="https://dokku.com"&gt;Dokku&lt;/a&gt;, that's what I am going to write about.&lt;/p&gt;

&lt;p&gt;Dokku, oh, it is my favorite self-hosted deployment software for personal and sometimes commercial needs. It has its flaws but overall it works in most situations and allows me to save some money, so I use it for my pet-projects and for my website too.&lt;/p&gt;

&lt;p&gt;Before we start, I assume you've already installed &lt;a href="https://dokku.com"&gt;Dokku&lt;/a&gt; on your server, configured all the things properly, installed &lt;a href="https://github.com/dokku/dokku-mysql"&gt;dokku-mysql&lt;/a&gt;, created a new database (ex: &lt;strong&gt;ghost-db&lt;/strong&gt;), created a new app for Ghost, let's call it simply &lt;strong&gt;ghost&lt;/strong&gt; and linked them.&lt;/p&gt;

&lt;p&gt;As of &lt;strong&gt;v0.24.0&lt;/strong&gt; Dokku allows to deploy Docker images using a straightforward command, before this it was a nightmare with tagging.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku git:from-image node-js-app dokku/node-js-getting-started:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, &lt;strong&gt;dokku git:from-image&lt;/strong&gt; is exactly what we need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1. Deploy Ghost image
&lt;/h3&gt;

&lt;p&gt;We are going to use Docker Ghost image that can be found &lt;a href="https://hub.docker.com/_/ghost/"&gt;here&lt;/a&gt;. You can use any of the tags, but it is fine to use &lt;strong&gt;:latest&lt;/strong&gt; for initial deployment.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku git:from-image ghost ghost:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will build your app using the image you provided and then deploy it using your Dokku settings. By the end of the process you will have a working Ghost instance without any persistance and wrong public ports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2. Dealing with ports
&lt;/h3&gt;

&lt;p&gt;Here we are going to make Ghost app public by adding some new ports and removing those that we don't need.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku proxy:ports-add ghost http:80:2368
dokku proxy:ports-remove ghost http:2368:2368
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By the end of this step your Ghost app should now be accessible via a domain you provided initially to Dokku or via port mapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3. Persist it
&lt;/h3&gt;

&lt;p&gt;Docker images do not keep any data between restarts, that's why we should provide some persistant volumes to it. Dokku allows us to do this easily via &lt;strong&gt;storage:mount&lt;/strong&gt; command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku storage:ensure-directory ghost
dokku storage:mount /var/lib/dokku/data/storage/ghost:/var/lib/ghost/content
dokku ps:rebuild ghost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this step we create a new folder &lt;strong&gt;ghost&lt;/strong&gt; in a special place where Dokku docs suggests us to keep persisted data, then we use our new folder &lt;strong&gt;/var/lib/dokku/data/storage/ghost&lt;/strong&gt; to keep Ghost data from docker image folder &lt;strong&gt;/var/lib/ghost/content&lt;/strong&gt;. This way everything that Ghost creates and keeps in this &lt;strong&gt;content&lt;/strong&gt; folder will be persisted between reloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4. Database
&lt;/h3&gt;

&lt;p&gt;Ghost can work even without a separate database, using a SQLite and keeping it on the disk, I don't really like it but if you are okay with this, &lt;em&gt;then skip this step&lt;/em&gt;. Before we even started, you already installed dokku-mysql and created a database for Ghost, we will need it here.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku config:set \
    database__client=mysql \
    database__connection__database=&amp;lt;YOUR_DATABASE_NAME&amp;gt; \
    database__connection__host=&amp;lt;YOUR_DATABASE_HOST&amp;gt; \
    database__connection__password=&amp;lt;YOUR_DATABASE_PASSWORD&amp;gt; \
    database__connection__port=&amp;lt;YOUR_DATABASE_PORT&amp;gt; \
    database__connection__user=mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this step we provide some essential database configuration params to the Ghost app, as you set them, Dokku will restart it and it should then work using mysql database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5. Domain
&lt;/h3&gt;

&lt;p&gt;One of the main things we should do, is to add a domain to our app, Dokku has some useful commands for this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku domains:add ghost my.ghost.website
dokku config:set ghost url=http://my.ghost.website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 6. SSL certificate
&lt;/h3&gt;

&lt;p&gt;You need SSL certificate for your website. With Dokku you can add it in several ways, using official &lt;a href="https://github.com/dokku/dokku-letsencrypt"&gt;Let's Encypt&lt;/a&gt; plugin, or using your own certificate, or using a &lt;a href="https://bitkidd.dev/posts/use-cloudflare-ssl-certificates-with-dokku"&gt;Cloudflare SSL&lt;/a&gt;. Any of these do, but don't neglect this step.&lt;/p&gt;

&lt;p&gt;Just don't forget to change your website url in configs.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku config:set ghost url=https://my.ghost.website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's it, by now you have a working website powered by Ghost and deployed using Dokku on your own server for little to no money 🪄🦄✨.&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>dokku</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Over-engineering madness or hype-driven development in JS</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Tue, 18 Jan 2022 10:23:33 +0000</pubDate>
      <link>https://dev.to/bitkidd/over-engineering-madness-or-hype-driven-development-in-js-54k5</link>
      <guid>https://dev.to/bitkidd/over-engineering-madness-or-hype-driven-development-in-js-54k5</guid>
      <description>&lt;p&gt;Let's start with a question, how would you create a website that should just display a logo and some info?&lt;/p&gt;

&lt;p&gt;If you have in mind something like Next.js, Nuxt.js, Svelte or any other framework, maybe with a little bit of CSS-in-JS or SASS, or maybe Redux, or even Angular, then you are definitely over-engineering.&lt;/p&gt;

&lt;p&gt;I'm not saying that either of these tools are bad or you should not use it, I'd say that they are great, but only when used properly in a proper situation.&lt;/p&gt;

&lt;p&gt;So what I am talking about?&lt;/p&gt;

&lt;h3&gt;
  
  
  Hype-driven development
&lt;/h3&gt;

&lt;p&gt;JavaScript is one of the most popular programming languages in the world, &lt;a href="https://www.tiobe.com/tiobe-index/"&gt;#7 currently&lt;/a&gt; and this is awesome, you don't even need to install something special to work with it, just launch you favorite browser and you are good to go.&lt;/p&gt;

&lt;p&gt;This tremendous accessibility allows millions of developers to start out their carier with it, we even have probably the biggest package registry in the world - NPM, with stunning 1.8 million packages, isn't it awesome? Sure, it is!&lt;/p&gt;

&lt;p&gt;You can now use javascript literally everywhere, you can build backends, mobile apps, program micro-controllers and even launch something into the space. There are thousands of amazing libraries and packages in the eco-system, even more, companies that built some of the most popular tools truly fight for your attention.&lt;/p&gt;

&lt;p&gt;And all of this brings us to a question, how to navigate and pick the right tool, how to choose wisely which framework is worth using and when to use it?&lt;/p&gt;

&lt;p&gt;If you are a junior developer I bet you use something like React or Vue, Redux or Mobx, and it is not bad, but do you understand why you are using it? Or you followed the hype?&lt;/p&gt;

&lt;p&gt;You may even find googling some &lt;strong&gt;'amazing'&lt;/strong&gt; packages like &lt;a href="https://www.npmjs.com/package/is-string"&gt;is-string&lt;/a&gt; with 20M downloads, or &lt;a href="https://www.npmjs.com/package/is-date-object"&gt;is-date-object&lt;/a&gt; with 21M downloads, or &lt;a href="https://www.npmjs.com/package/is-boolean-object"&gt;is-boolean-object&lt;/a&gt; with 17M downloads and my favorite &lt;a href="https://www.npmjs.com/package/is-odd"&gt;is-odd&lt;/a&gt; with 400K downloads, and use it because so many people cannot be wrong.&lt;/p&gt;

&lt;p&gt;Hype, is the problem, hype and github stars rule here, they create unnecessary FOMO feeling that is not a constructive one.&lt;/p&gt;

&lt;p&gt;Hype leads to over-engineering when you pick a tool just because it is popular and has 50K, 100K, 200K stars on GitHub, or it was promoted by your favorite dev-blogger or a twitter thread.&lt;/p&gt;

&lt;p&gt;Hope you got the idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsible development
&lt;/h3&gt;

&lt;p&gt;The hype problem is a real one, I know I sound like an old fart, like I'm against new approaches and new ideas, not at all. I think that every tool has its use, but it becomes harder and harder to pick the right one for the job.&lt;/p&gt;

&lt;p&gt;And the only real way to deal with it is responsible development, like responsible consumption, but development. A great example of this approach is Redux, that clearly states on the website: "You'll know when you need Flux/Redux. If you aren't sure if you need it, you don't need it.". &lt;/p&gt;

&lt;p&gt;As per companies that build tools, they should take the responsability and clearly explain on when you may need their library or tech, like FaaS or edge-computing, on why you may not need it and what are the benefits or disadvantages.&lt;/p&gt;

&lt;p&gt;Before using anything try to find a second opinion and try not to use a tool only because it is marketed well. When you use something that is popular right now, you may lead the company you work for to increased costs for development and support in the future. And the best way to check something new is to create a pet project.&lt;/p&gt;

&lt;p&gt;I usually tend to run a mind exercise and ask myself several questions before using any library:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do I really understand it?&lt;/strong&gt; Like really, if I feel it works like a magicbox, I will try to research and clear my gaps in knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What are the benefits, disadvantages and consequences?&lt;/strong&gt; This one stems from the first one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Will it make something better for me or for the company mid term, long term?&lt;/strong&gt; Like speed of development, ease of support, ease of hiring other developers, costs and etc. etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answers to these three question help me decide whenever I should use GraphQL for my personal blog or not or should I use a super hyped JAM-stack framework for a clients' project or just stick with something more reliable like &lt;a href="https://adonisjs.com"&gt;AdonisJS&lt;/a&gt;, Laravel or Ruby on Rails.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Deploy your Node.js app without a hassle</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Sun, 11 Jul 2021 13:10:23 +0000</pubDate>
      <link>https://dev.to/bitkidd/deploy-your-node-js-app-without-a-hassle-28ke</link>
      <guid>https://dev.to/bitkidd/deploy-your-node-js-app-without-a-hassle-28ke</guid>
      <description>&lt;p&gt;The process of app deployment takes some time, you have to configure the server, find all the information and be ready to handle the issues, but there is an easier way.&lt;/p&gt;

&lt;p&gt;This guide is relevant literally for any app, for Ruby and Rails, for Python and Django, for PHP and Laravel, for Go and micro-services, you can deploy easily anything, but I will use &lt;a href="https://adonisjs.com"&gt;Adonis.js&lt;/a&gt; as an example.&lt;/p&gt;

&lt;p&gt;I guess any developer knows what is Heroku, they've been de facto a standard of Platform-as-a-Service and showed the world how an app deployment should really work, easily and fast, requiring zero configuration for most cases.&lt;/p&gt;

&lt;p&gt;Heroku is not a cheap service, their prices are pretty high and aren't great for pet projects or small services with zero revenue.&lt;/p&gt;

&lt;p&gt;There are some cheaper alternatives like Render, Railway or Digital Ocean App platform, but there is a self-hosted solution that works almost like Heroku and is free of charge, you only have to bring your own server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dokku
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dokku.com"&gt;&lt;strong&gt;Dokku&lt;/strong&gt;&lt;/a&gt; is the smallest PaaS implementation that allows you to build, manage and deploy your apps on your own server, that may cost you as little as &lt;strong&gt;$3.50&lt;/strong&gt; per month( I use and recommend &lt;a href="https://hetzner.cloud/?ref=l6kYYPTcviZx"&gt;Hetzner Cloud&lt;/a&gt;, it is a referral link that will give you €20 in credit).&lt;/p&gt;

&lt;p&gt;It uses heroku buildpacks and is able to deploy your app using dockerfile as well, it configures nginx as a proxy server, you can install databases and connect them to your app, you can install Let's Encrypt for SSL certificates, you can deploy monorepos, you can mount local storage, there are multiple plugins that handle most use cases and require almost zero configuration.&lt;/p&gt;

&lt;p&gt;To install Dokku, you have to allocate a server from you favorite provider, with a minimum requirement of 1 core and 1GB of RAM, then login into your new shiny server and run two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# for latest tag check Dokku git repo&lt;/span&gt;
&lt;span class="c"&gt;# or https://dokku.com website&lt;/span&gt;

wget https://raw.githubusercontent.com/dokku/dokku/v0.24.10/bootstrap.sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;DOKKU_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v0.24.10 bash bootstrap.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then go to your server's IP and follow the web installer process.&lt;/p&gt;

&lt;p&gt;Now you have a working self-hosted PaaS that is ready to build and deploy your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying an app
&lt;/h3&gt;

&lt;p&gt;As I am using &lt;a href="https://adonisjs.com/"&gt;Adonis.js&lt;/a&gt; for this guide, I initiate a new app, a web starter template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init adonis-ts-app@latest hello-world

&lt;span class="c"&gt;# If using yarn&lt;/span&gt;
yarn create adonis-ts-app hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we have to initiate git repository for this app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello-world

&lt;span class="c"&gt;# initiate git&lt;/span&gt;
git init

&lt;span class="c"&gt;# stage all files&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# create first commit&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'Init commit'&lt;/span&gt;

&lt;span class="c"&gt;# add Dokku server as a remote&lt;/span&gt;
git remote add dokku dokku@&amp;lt;YOUR_SERVER_IP_ADDRESS_OR_DOMAIN&amp;gt;:hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part &lt;code&gt;hello-world&lt;/code&gt; in git remote address corresponds to a server app name that we should create on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on the Dokku host&lt;/span&gt;
dokku apps:create hello-world
&lt;span class="nt"&gt;-----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Creating hello-world...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Adonis.js to start we have to set some environment variables, Dokku provides an easy to use command for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on your computer&lt;/span&gt;
&lt;span class="c"&gt;# inside project folder&lt;/span&gt;
node ace generate:key
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; iGyX0deixdW7DkdJ9G9PbyyT8QaizXuK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on the Dokku host&lt;/span&gt;
dokku config:set hello-world &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;APP_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;iGyX0deixdW7DkdJ9G9PbyyT8QaizXuK &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Hello World'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;CACHE_VIEWS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;SESSION_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cookie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just before the deploy it is a good idea to create a &lt;code&gt;Procfile&lt;/code&gt; and describe how our PaaS should start our app, this file is a very common configurational file, read more about it &lt;a href="https://devcenter.heroku.com/articles/procfile"&gt;here&lt;/a&gt;. Don't forget to commit it to the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: node build/server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to deploy our app for the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push dokku master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a successful deployment Dokku will print you the address where you can access your app, if you chose port based deployments, your address will look like &lt;code&gt;http://&amp;lt;YOUR_SERVER_IP_ADDRESS&amp;gt;:&amp;lt;PORT&amp;gt;&lt;/code&gt;, if you chose hostname based deployments, it will look like this: &lt;code&gt;http://&amp;lt;APP_NAME&amp;gt;.&amp;lt;YOUR_SERVER_DOMAIN&amp;gt;&lt;/code&gt;. You can add a domain to the app later if &lt;a href="https://dokku.com/docs/configuration/domains/"&gt;needed&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding database
&lt;/h3&gt;

&lt;p&gt;Dokku supports multiple databases, MySQL, PostgreSQL, MongoDB, Redis and others.&lt;br&gt;
Here I will install PostgreSQL and then link it to my app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on the Dokku host&lt;/span&gt;
&lt;span class="c"&gt;# install the postgres plugin&lt;/span&gt;
&lt;span class="c"&gt;# plugin installation requires root, hence the user change&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-postgres.git

&lt;span class="c"&gt;# create a postgres service&lt;/span&gt;
dokku postgres:create hello-world-database

&lt;span class="c"&gt;# on the Dokku host&lt;/span&gt;
&lt;span class="c"&gt;# each official datastore offers a `link` method to link a service to any application&lt;/span&gt;
dokku postgres:link hello-world-database hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linking adds a new environment variable to the app, &lt;code&gt;DATABASE_URL&lt;/code&gt; that is a database connection string with all the credentials we need.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://docs.adonisjs.com/guides/database/introduction"&gt;Adonis.js&lt;/a&gt; to work with database we should install Lucid ORM and configure it properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @adonisjs/lucid@latest
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add @adonisjs/lucid

&lt;span class="c"&gt;# and then&lt;/span&gt;
node ace configure @adonisjs/lucid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation don't forget to add new database environment variables to Dokku, you can use &lt;code&gt;DATABASE_URL&lt;/code&gt; that was provided before or split it to separated values, connection string url follows a well known format, so it is not a problem to identify credentials and host address.&lt;/p&gt;

&lt;p&gt;To run database migrations on each deploy we should update our &lt;code&gt;Procfile&lt;/code&gt; by adding new values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: node build/server.js
release: node build/ace migration:run --force
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit new updates and run a deploy again!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push dokku master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encrypting connection
&lt;/h3&gt;

&lt;p&gt;Dokku is built on top of plugins. One of them uses Let's Encrypt to provide SSL certificates to Nginx proxy server.&lt;/p&gt;

&lt;p&gt;Installing the plugin is as simple as running a command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on Dokku host&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To encrypt the connection of your app run &lt;code&gt;dokku letsencrypt hello-world&lt;/code&gt; and then &lt;code&gt;dokku letsencrypt:cron-job --add&lt;/code&gt; to add a  crontab job that will renew certificates when needed.&lt;/p&gt;

&lt;p&gt;That's it, your app is now served using SSL!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Dokku is a great Heroku-like tool for build and deploy automation, it is universal and can be used almost by any company. It does not support clustering and brings some Docker problems with it, but well, if you need clustering for your app and have that many requests per second, then you may also have money for Heroku or others, or even your personal DevOps engineer.&lt;/p&gt;

&lt;p&gt;You may read more about dokku command and possibilities on their &lt;a href="https://dokku.com"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>adonisjs</category>
      <category>adonis</category>
    </item>
    <item>
      <title>Migrate AdonisJS v4 user passwords to v5</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Tue, 06 Jul 2021 16:15:10 +0000</pubDate>
      <link>https://dev.to/bitkidd/migrate-adonis-js-v4-user-passwords-to-v5-2m21</link>
      <guid>https://dev.to/bitkidd/migrate-adonis-js-v4-user-passwords-to-v5-2m21</guid>
      <description>&lt;p&gt;A new version of &lt;a href="https://adonisjs.com"&gt;Adonis.js&lt;/a&gt; isn't just a simple update, it is a complete revamp of all the core modules and structure including hashing mechanism.&lt;/p&gt;

&lt;p&gt;Prior the update Adonis.js used plain &lt;code&gt;bcrypt&lt;/code&gt; hashing implementation but now it became more standartized, the use of &lt;a href="https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md"&gt;PHC string format&lt;/a&gt; allows to incorporate different hashers and verify the hashes against the current configuration and then decide if the hash needs to be rehashed or not.&lt;/p&gt;

&lt;p&gt;This change leads to a situation when old v4 hashes will not be compatible with v5 and your users will not be able to login.&lt;/p&gt;

&lt;p&gt;The way to resolve this problem I'd describe in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Expand hasher with our own &lt;code&gt;legacy&lt;/code&gt; driver&lt;/li&gt;
&lt;li&gt;On user authentication attempt check if the password has been hashed using an old hasher, if yes, use our new &lt;code&gt;legacy&lt;/code&gt; driver&lt;/li&gt;
&lt;li&gt;Authenticate user and rehash password using a new hasher, in my case I'm using &lt;code&gt;argon2&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Expanding the hasher
&lt;/h3&gt;

&lt;p&gt;To expand the hasher we have to create a new local provider by running a corresponding command inside our projects folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:provider LegacyHasher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a new provider file inside &lt;code&gt;/providers&lt;/code&gt; folder. After the file has been generated, we have to add it to &lt;code&gt;.adonisrc.json&lt;/code&gt; into &lt;code&gt;providers&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;Before actually expending we have to create a new &lt;code&gt;Hash&lt;/code&gt; driver, as an example we can use the code provided in an official documentation &lt;a href="https://docs.adonisjs.com/guides/security/hashing#adding-a-custom-driver"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I created a separate folder inside &lt;code&gt;/providers&lt;/code&gt;, named it &lt;code&gt;LegacyHashDriver&lt;/code&gt; and placed my &lt;code&gt;legacy&lt;/code&gt; driver there (inside an &lt;code&gt;index.ts&lt;/code&gt; file).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bcrypt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HashDriverContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Implementation of custom bcrypt driver
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LegacyHashDriver&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;HashDriverContract&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Hash value
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Verify value
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hashedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plainValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plainValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hashedValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it depends on a &lt;code&gt;bcrypt&lt;/code&gt; package, you'll have to install it before running.&lt;/p&gt;

&lt;p&gt;Having created a new driver, we can now expand the &lt;code&gt;Hash&lt;/code&gt; core library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Application&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LegacyHashDriver&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./LegacyHashDriver&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LegacyHasherProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adonis/Core/Hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;legacy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;LegacyHashDriver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two additional things we have to do before proceeding to actual testing of implementation. We have to add our new hasher to &lt;code&gt;contracts/hash.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HashersList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BcryptConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BcryptContract&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;argon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ArgonConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ArgonContract&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;legacy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
      &lt;span class="nl"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HashDriverContract&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add it to &lt;code&gt;config/hash.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;legacy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;legacy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authenticating users with legacy hasher
&lt;/h3&gt;

&lt;p&gt;As user tries to login the first thing you do (after request validation) is user search, by email or username. When you find a corresponding record, you can check if the password hash has been generated using an old method, by testing it&lt;br&gt;
agains a simple regex. Then later verify it using the right hash driver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usesLegacyHasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sr"&gt;2&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;aby&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isMatchedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usesLegacyHasher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;isMatchedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;legacy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;isMatchedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rehashing old user password
&lt;/h3&gt;

&lt;p&gt;Rehashing user password on login is the most convenient way to migrate to a new driver. I do this after I checked all the security things, found the user and know that the password is hashed using an old method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// rehash user password&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usesLegacyHasher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internalServerError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can test it and it should work. You can expand hasher not only to migrate from v4 to v5, but even when you try to build your app on top of existing database.&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>adonis</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 things I learned while freelancing</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Mon, 14 Jun 2021 08:07:11 +0000</pubDate>
      <link>https://dev.to/bitkidd/5-things-a-freelancer-should-know-to-succeed-1ppc</link>
      <guid>https://dev.to/bitkidd/5-things-a-freelancer-should-know-to-succeed-1ppc</guid>
      <description>&lt;p&gt;Freelancers or independent contractors are in demand as never have been before, the pandemic changed how businesses work and hiring a remote worker has become a new normal.&lt;/p&gt;

&lt;p&gt;As a freelancer myself with years and years of remote work experience and sometimes as a client who hires for work, I decided to share some ideas and advices with those who just start or want to improve their experience and success rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be a human and read carefully
&lt;/h3&gt;

&lt;p&gt;From times to times I had to hire some professionals and this experience allowed me to understand how a client feels like when receives proposals.&lt;/p&gt;

&lt;p&gt;First of all I should say that more than 95% of freelancers just don't read job descriptions, they copy paste a prepared "promotional" text and send it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't do like this, ever.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This does not work and shows that you are not really interested, most of the times this makes the client remove your proposal and proceed with somebody else who reads the description, answers the questions, asks some questions and is proactive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn to discuss and convince
&lt;/h3&gt;

&lt;p&gt;The ability to discuss and convince somebody using structured arguments is very important.&lt;/p&gt;

&lt;p&gt;You should not always agree with a client, your main goal should be a good product and you as a professional hired to do your job should be able to propose some ideas and help the client choose the best option.&lt;/p&gt;

&lt;p&gt;So, next time you'll do something, just propose some ideas and changes backing them up with good reasoning, with some examples and etc. Trust me, this will give you a lot of credit and possibly your client will share you contact with someone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leave traces of agreements
&lt;/h3&gt;

&lt;p&gt;Leaving traces of agreements is important and how many times I've seen people ignore this, to be honest I ignored this too to the moment when a client just scammed me and left me without money. Freelancers do scam clients too, so this matches both sides.&lt;/p&gt;

&lt;p&gt;What does it actually mean, leaving traces of agreements?&lt;/p&gt;

&lt;p&gt;First of all, you should communicate on a platform where you got your job, usually the platforms act as an escrow, protecting your agreements and money, of course they take percentage for this, but what is better, to get your money or&lt;br&gt;
product, or be scammed and not pay for escrow?&lt;/p&gt;

&lt;p&gt;If you got your job or hired a worker somewhere else, sign a contract, there are multiple good examples on the internet.&lt;/p&gt;

&lt;p&gt;Talk and discuss using messengers where no one can edit their messages or via email, that is even better, as emails are usually considered first class proof of agreement in courts, but don't forget to mention email addresses in the contact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Never say a hard no
&lt;/h3&gt;

&lt;p&gt;I consider that saying a hard no is a pretty bad business approach when working for yourself.&lt;/p&gt;

&lt;p&gt;Even when you don't want to work with a client who asks for your help, just don't say no, say that you are busy and will be free in three months for example, or name a price that is reasonably high for this client, just don't say no.&lt;/p&gt;

&lt;p&gt;As you never know what will happen in the future and you don't what to burn all the bridges behind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do project for yourself
&lt;/h3&gt;

&lt;p&gt;Make imaginary projects for yourself or solve your problems with your own skillsets. This is really important, as it allows you to use a new tool, develop a new skill, check design trends or update portfolio.&lt;/p&gt;

&lt;p&gt;Maybe help your friends or someone who cannot afford your services. What is more important, this will help you understand how the projects are done, from planning to mockups, from idea and research to launch.&lt;/p&gt;

&lt;p&gt;Doing something for yourself is hard, this needs a very specific motivation boost, but this will help you in the future and will teach you something anyway.&lt;/p&gt;

</description>
      <category>freelance</category>
    </item>
    <item>
      <title>Through Sails and Rails to Adonis.js</title>
      <dc:creator>Chirill Ceban</dc:creator>
      <pubDate>Thu, 10 Sep 2020 06:20:57 +0000</pubDate>
      <link>https://dev.to/bitkidd/through-sails-and-rails-to-adonis-js-4on2</link>
      <guid>https://dev.to/bitkidd/through-sails-and-rails-to-adonis-js-4on2</guid>
      <description>&lt;p&gt;Even though Node.js has been around since 2009, 11 years so far, the web framework options are still very limited, basically you have two main paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To choose Express.js and then build something kinda reliable on top of it by yourself&lt;/li&gt;
&lt;li&gt;To choose something based on top of Express.js and built by somebody else &lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Combine it again and again 🤯
&lt;/h4&gt;

&lt;p&gt;To be honest I am not fan of the first path, if not say more, you have to combine multiple packages, choose template language, ORM, think about error handling, logging, folder structure, body parser, security, authentication, authorization, validation and many many other little aspects that are not really cool to deal with, basically you waste your developer time to a battle that may not be won. I tried this way, It blew my mind, I did not like it.&lt;/p&gt;

&lt;h4&gt;
  
  
  High hopes 🤞
&lt;/h4&gt;

&lt;p&gt;The second path is the path I tried multiple times. Sails.js was my hope at that time, being based on express.js it was pretty promising, but no luck. Then Feathers, LoopBack, Kraken, Keystone - no luck, all these tools were just okay really, nothing that may be compared to Ruby on Rails or Laravel.&lt;/p&gt;

&lt;h4&gt;
  
  
  A revelation 🤔
&lt;/h4&gt;

&lt;p&gt;It was 2014 outside, I lost my hope and started digging Ruby on Rails to finally stop and find something production ready, a developer heaven. That was a revelation of how a framework and an ecosystem around it should work and should be built.&lt;/p&gt;

&lt;p&gt;While still working with Rails full-time I was looking for something new in Node.js web-frameworks world that may catch my eye and make me somehow try it. I've been googling here and there and then finally found something new that was really different - Adonis.js.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adonis.js - the god of beauty on a Node.js Olympus ⛰️
&lt;/h4&gt;

&lt;p&gt;Adonis is built with developer happiness in mind, inspired by Laravel and Rails, very structured and opinionated from the very beginning, the new v5 uses Typescript everywhere (but you still can use plain JavaScript).&lt;/p&gt;

&lt;p&gt;It is created from the ground up by a very talented engineer &lt;a href="https://github.com/thetutlage"&gt;Harminder Virk&lt;/a&gt;, has a nice and vibrant community and multiple packages that extend possibilities even more.&lt;/p&gt;

&lt;p&gt;Basically, Adonis is an integrated system, and it serves you well in a myriad of situation. You'd like to create an API, no problem, just don't install views and then serve JSON directly, you'd like to serve views, no problem it has its own Edge temple engine, a very advanced one, you'd like to use PostgreSQL or MySQL or combine them, no problem, it has it's own Lucid ORM based on top of Knex.js and typed through out, it has validations, security, caching, routing, folder structure, service providers and many many other little things that will definitely make you happier. Using Adonis you don't have to spend hours and hours combining things, you take your idea on paper and implement it in a flawless way. &lt;/p&gt;

&lt;p&gt;I used Adonis for some of my pet and commercial project that I built for the last 3 years, it had it's own problems and caveats but the way it improves and develops makes me confident about its future.&lt;/p&gt;

&lt;p&gt;You may learn more about it here &lt;a href="https://preview.adonisjs.com"&gt;adonisjs.com&lt;/a&gt;, here &lt;a href="https://github.com/adonisjs/core/discussions"&gt;Github Discussions&lt;/a&gt; and here &lt;a href="https://twitter.com/adonisframework"&gt;@adonisjs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
