Deploy an Eleventy Blog on Cloudflare pages, part 4

Photo by Goofer on Unsplash

Deploy an Eleventy Blog on Cloudflare pages, part 4

with Strapi, MySQL, and Dokku on a Digital Ocean Droplet

Originally published at: victorfeight.com/2022/06/secure_dokku_dropl..

Welcome back!

╰(°▽°)╯

If you've been following along so far, you should have a Dokku instance on DigitalOcean that's been hardened from attackers (through SSHD configuration and Fail2Ban static IP allow-lists). You should also have a MySQL database server installed onto the instance. ✔

In this post, we'll be installing Strapi CMS onto our local machine, and then allow Strapi to talk to our local MySQL instance. Strapi is an open-source content management system that eases managing a custom blog by enabling multiple users and fine-grained permissions over adding, editing, or deleting posts.

Once it's set up, it's as easy as making a new post with your username through Strapi's dashboard, and the file will get posted into our database automatically. We can turn on the GraphQL Playground and start querying for our posts.

INFO: Hostname is blocked by MySQL Recently, I had an error when trying to connect to my remote mysql instance via MySQL Workbench. The error was along the lines of: Host is blocked because of many connection errors.

At this point, I ssh'd into my machine with ssh vic@apps.vicstech.xyz, entered my dokku db instance with dokku mysql:enter db and while in the dokku db bash instance, entered mysql -u root -p followed by my db password.

Once in, I ran FLUSH HOSTS; to empty the host table. Apparently this happens if more than max_connect_errors occur successively for a given host, MySQL will block them. By flushing the tables, further connection attempts from the host are allowed.

Sidequest: Installing NVM in Powershell

TIP: Strapi and Node versions

  • Strapi won't run on NodeJS odd numbered versions - which are for developers. If you're not using LTS, you can temporarily change to use 14.16.1 with nvm use 14.16.1 in an elevated powershell (assuming nvm is installed).

placeholder

TIP: The easiest way to install nvm (Node Version manager) is to use Chocolatey. 🍫

  1. Right Click Powershell icon in Start menu, select Run As Administrator.
  2. Confirm chocolatey is installed by running choco command in a new elevated Powershell.

placeholder

  1. If it's not installed, run the following command: Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

  2. Install nvm using choco by running: choco -y nvm

  3. Open a new, regular Powershell and install Node.js using nvm: nvm install 14.16.1

Great. We'll need to use Node 14.16.1 LTS in Powershell to ensure optimal Strapi and node compatibility.

Local Strapi backend Installation

Create a new directory in your project and name is strapi-dokku-backend.

We're going to download the latest Strapi code onto our local machine before pushing it to our dokku instance.

In powershell with node 14.16.1 enabled, run npm/npx to install the Strapi project named 'strapi-backend' locally:

npx create-strapi-app@latest strapi-backend
  • Choose manual Installation with arrow keys, press Enter
  • Choose mysql as default database client
  • Enter db as Database name.
  • Enter localhost as Host.
  • Enter 3306 as Port.
  • Enter root as username
  • Enter password used for local and remote root MySQL instances.
  • Enter N for "Enable SSL connection" option

INFO: You can retrieve this password by sshing into apps.vicstech.xyz and running: sudo cat /var/lib/dokku/services/mysql/db/ROOTPASSWORD on your Dokku instance.

  • If you've been following along, this password should now be associated with both the root user in your local MySQL Workbench, as well as the root user of our remote MySQl instance on the Dokku Digital Ocean droplet. If you entered something different as a local password, ensure DATABASE_PASSWORD in env is set to your remote instance ROOTPASSWORD, as that will take precedence in settings when Strapi is pushed onto dokku.

Next, run the following commands in the newly created strapi-backend directory:

cd strapi-backend
npm install
npm install strapi-admin

Powershell Output: placeholder

Strapi command reference:

Available commands in your project:

npm run develop
# Start Strapi in watch mode. (Changes in Strapi project files will trigger a server restart)

npm run start
# Start Strapi without watch mode.

npm run build
# Build Strapi admin panel.

npm run strapi
# Display all available commands.

# You can start by doing:
cd /mnt/c/Users/Vic/Documents/CODE_2020/ELEVENTY/strapi-dokku-backend/backend
npm run develop

Configuring our Local Strapi Backend

We can go ahead and give it a whirl by running the following commands in the Strapi strapi-backend folder:

npm run build
npm run start

If everything went smoothly, you should get a Windows Firewall notification asking to allow Strapi permission to serve its admin panel. Go ahead and allow it. placeholder

As per Strapi developer docs quick start guide:

Once the installation is complete, your browser automatically opens a new tab. By completing the form, you create your own account. Once done, you become the first administator user of this Strapi application. Welcome aboard, commander! 🥳

Complete this form to create the first Administrator user and click Let's Start!

placeholder

Now you'll have access to the Strapi admin panel. Navigate to http://localhost:1337/admin in your browser and you should see a success message: placeholder

Now we've got a local Strapi server running (strapi-backend), configured to use MySQL with a database named db through port 3306.

Install GraphQL plugin and configure it locally

We're going to install the GraphQL playground plugin and configure it to be used both locally or remotely.

Press Ctrl-C to cancel any running Strapi instance, then run the following in your strapi-backend project directory:

npm run strapi install graphql

Output:

Next, create a plugins.js file at /config/plugins.js (in your root strapi-backend directory):

// path: ./config/plugins.js

module.exports = {
  //
  graphql: {
    config: {
      endpoint: "/graphql",
      shadowCRUD: true,
      playgroundAlways: true,
      depthLimit: 7,
      amountLimit: 100,
      apolloServer: {
        introspection: true,
      },
    },
  },
};

This will enable Strapi's GraphQL playground both locally and remotely (by default, it's turned off when deployed remotely), and set its endpoint to /graphql.

Output: placeholder

Now, go ahead and rebuild Strapi with GraphQL installed, and run in develop mode:

npm run build
npm run develop

Creating a Collection Type for Articles

INFO: Develop Mode Develop mode allows us to add new collection types, and add fields to these new collection types.

We're going to create a data structure for our future blog content, add some entries and publish them so that the API for the content can be consumed. We do this by first creating collection types for Article with the content-type builder and create the fields to display when adding new entries.

  • Navigate to Content-Types Builder under Plugins in the left-hand menu.
  • Click the "+ Create new collection type" link
  • Name it Article and click continue
  • Add a Text field (short text) and name it title
  • Click the "+ Add another field" button
  • Add a Rich Text field and name it content
  • Click the "+ Add another field" button
  • Add a Date field (date) and name it date
  • Click the "+ Add another field" button
  • Add a Text field (short text) and name it author
  • Click the "+ Add another field" button
  • Add a Text field (short text) and name it excerpt
  • Click the "+ Add another field" button
  • Add a Text field (short text) and name it categories
  • Click the "Finish" button
  • Click the Save button and wait for Strapi to restart

placeholder

For more information, see the Strapi Quick Start guide.

Set roles and permissions on our Strapi API

Next, we'll set roles and permission on our API and and then test querying our new posts in GraphQL.

Click Content Manager on the left menu. Navigate to Article under Collection Types

Click "Create New Entry".

Fill in this information in the fields specified : title: This is my lovely title content: Hope you enjoy this great content. author: vrfeight data pick a date of your choice exerpt: Short description categories: test

Click Save.

In order to send requests to the Strapi API, we need public permissions on our articles to make them accessible.

  • Navigate to Settings under General left menu.
  • Navigate to Roles under Users & Permissions Plugin.
  • Click the Public Role
  • Scroll down under Permissions, find Articles
  • Tick the find and findone boxes.
  • Click Save

placeholder

Now that we have a collection type with content, and permissions on the articles, we can finally test querying our API. 🎉

Database configuration with .env variables

We'll finish setting everything up locally and confirm that local MySQL database querying works in the GraphQL playground.

Now, the next part's important, so sip your tea and roll up your sleeves. 🍵👕

TIP: Strapi local .env to remote settings

  • Strapi uses an environment variable for Host, Port, APP_KEYS, and JWT_SECRET. When pushed to dokku, all these environment variables can be processed once configured on the server for our strapi-backend application, in addition to any other variables set in the .env file.

Our authentication strategy is based on the use of Users & Permissions plugin API access.

You will see in the database.js file's module.exports function there is a connection property holding an object with these configuration values (password will be unique, set upon Strapi installation):

require("dotenv").config();

module.exports = ({ env }) => ({
  connection: {
    client: "mysql",
    connection: {
      // host: env('DATABASE_HOST', 'localhost' ),
      host: env("DATABASE_HOST", "localhost"),
      port: env.int("DATABASE_PORT", 3306),
      database: env("DATABASE_NAME", "db"),
      user: env("DATABASE_USERNAME", "root"),
      password: env("DATABASE_PASSWORD", "e754d62fcfk5f929"),
      ssl: env.bool("DATABASE_SSL", false),
    },
  },
});

The way this works, is that the local Strapi server will check the first argument value (ie DATABASE_PORT or DATABASE_HOST), and if these values are not found in the .env file, then use the second argument provided (3306 or localhost)

A default Strapi .env file might look like this: placeholder

Thus, we can start by testing our local server with this default .env file, and afterward add database variables in .env with information from our remote mysql server.

This will allow us to query from either a local database (MySQL Workbench) or our remote MySQL instance by commenting or uncommenting Database variables in .env.

TIP: Remote Dokku-mysql environment vars

  • Later, when we push Strapi onto our remote dokku instance, we will use dokku config to set these same environment variables remotely, so that Strapi can pick up our remote dokku-mysql database. 😎

Testing the Strapi API Locally via GraphQL

To query your data, navigate to http://localhost:1337/api/articles/1

You should get back a JSON response: placeholder

Navigate to localhost:1337/graphql Welcome to your personal GraphQL Playground. 🙂

Try this query:

# Write your query or mutation here
query {
  article(id: "1") {
    data {
      id
      attributes {
        title
        content
        date
        author
        excerpt
      }
    }
  }
}

Output: placeholder

Success. We've just successfully queried our local MySQL database through Strapi's GraphQL playground.

Great job. 🎈

Still to Come...

Next time, we'll be deploying and confiuring Strapi on our Digital Ocean droplet, and we'll use the Dokku Plugin for LetsEncrypt to obtain a free certificate for our domain vicstech.xyz as well as our new strapi-backend subdomain we'll be creating.

Then, we'll practice querying our remote database with GraphQL, and start setting up an 11ty front-end to retrieve entries from the database .

Hope to see you soon.