Skip to content

Instantly share code, notes, and snippets.

@dayitv89
Last active June 28, 2021 16:53
Show Gist options
  • Save dayitv89/4234e0bda63f409dd3e6db12f14c91e8 to your computer and use it in GitHub Desktop.
Save dayitv89/4234e0bda63f409dd3e6db12f14c91e8 to your computer and use it in GitHub Desktop.
AdonisJS 4.1 essentials

403 CSRF issue for APIs or webhooks

On file config/shield.js at csrf key default is filterUris: [],

change this to filterUris: [/^\/webhooks\//, /^\/api\/v1/],

Add csrf values

at head section add {{ cspMeta() }}

at form add {{ csrfField() }}

API Request response logging

Use (@adonisjs/http-logger)[https://github.com/adonisjs/adonis-http-logger]

Database logging

On file config/database.js, find your database like mysql and add another line as debug: true

or debug: Env.get('DB_LOG', false) and set DB_LOG value at .env file

Create .env file if not exist, mostly used like heroku

on heroku use ENV_SILENT=true or

write this code on server.js

if (process.env.CREATE_ENV == '1') {
  const fs = require('fs')
  try {
    if (!fs.existsSync('.env') && fs.existsSync('.env.example')) {
      var data = fs.readFileSync('.env.example');
      fs.writeFileSync('.env', data);
    }
  } catch(err) {
    console.error(err)
  }
}

Adonis database create and drop by cli

adonis db:drop and adonis db:create follow below code for these cmds

adonis make:command db:create
adonis make:command db:drop

→ Open start/app.js and add below lines into commands

 const commands = [
   'App/Commands/DbCreate',
   'App/Commands/DbDrop'
   ]

Deploy:

  • either use PM2 & nginx for managing the services well. RECOMMENDED
  • or you can run by the default OS services. I'm using ubuntu 20.04 amd64 on ec2-t2.micro and everything is preinstall with image.
# TO Run the service on port 80 as deamon thread
sudo PORT=80 nohup node server.js &

#To run the service on 3000 port and point to 80.
PORT=3000 nohup node server.js &
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000

# to kill the process run 
ps -ef | grep "node"
kill -9 <pid>
// app/middleware/arena.js
// add server middleware as open `start/kernel.js`
// const serverMiddleware = ['Adonis/Middleware/Static', 'Adonis/Middleware/Cors', 'App/Middleware/Arena'];
'use strict'
const BullArena = require('bull-arena');
// const redis = {
// port: /* Your redis port */,
// host: /* Your redis host domain*/,
// password: /* Your redis password */,
// }
const type = 'bee';
const serveArena = BullArena(
{
queues: [
{
// redis,
type,
name: 'welcome_email_queue',
hostId: 'thejamworld'
}
]
},
{
// // Make the arena dashboard become available at {my-site.com}/arena.
basePath: '/arena',
// // Let express handle the listening.
disableListen: true
}
);
class Arena {
async handle (ctx, next) {
await new Promise(resolve => serveArena(ctx.request.request, ctx.response.response, resolve))
await next();
}
}
module.exports = Arena
// app/Commands/DbCreate.js command as `adonis db:create`
// create this command as `adonis make:command db:create` and paste this code into `app/Commands/DbCreate.js` file and
// 👉 Register command as follows
//→ Open start/app.js
//→ Add App/Commands/DbCreate to commands array
//→ const commands = [
// 'App/Commands/DbCreate',
// 'App/Commands/DbDrop'
// ]
'use strict'
const { Command } = require('@adonisjs/ace')
const Env = use("Env")
const Knex = use("knex")
class DbCreate extends Command {
static get signature () {
return 'db:create'
}
static get description () {
return 'Create Database'
}
async handle () {
const config = require(process.cwd() + '/config/database');
config[config.connection].connection.database = null;
const knex = Knex(config[config.connection]);
await knex.raw(`CREATE DATABASE IF NOT EXISTS ${Env.get("DB_DATABASE")}`);
await knex.destroy();
this.info('DB created');
}
}
module.exports = DbCreate
// app/Commands/DbDrop.js command as `adonis db:drop`
// create this command as `adonis make:command db:drop` and paste this code into `app/Commands/DbDrop.js` file and
// 👉 Register command as follows
//→ Open start/app.js
//→ Add App/Commands/DbCreate to commands array
//→ const commands = [
// 'App/Commands/DbCreate',
// 'App/Commands/DbDrop'
// ]
'use strict'
const { Command } = require('@adonisjs/ace')
const Env = use("Env")
const Knex = use("knex")
class DbDrop extends Command {
static get signature () {
return 'db:drop'
}
static get description () {
return 'Drop database'
}
async handle () {
const config = require(process.cwd() + '/config/database');
const knex = Knex(config[config.connection]);
await knex.raw(`DROP DATABASE ${Env.get("DB_DATABASE")}`);
await knex.destroy();
this.info('DB dropped');
}
}
module.exports = DbDrop
// App/Helper/Paginate.js
'use strict';
class Paginate {
static pageNumber({ request }) {
const data = request.only(['page']);
return data.page || 1;
}
static do(obj) {
const page = obj.pages.page;
const totalPages = obj.pages.total == obj.pages.perPage ? 1 : parseInt(obj.pages.total / obj.pages.perPage) + 1;
const prePage = page > 1 && page <= totalPages ? page - 1 : null;
const nextPage = page < totalPages ? page + 1 : null;
return { ...obj.pages, totalPages, prePage, nextPage, currentPage: page };
}
static html(data, { maxShowPages = 10, route = '?page=' } = {}) {
if (maxShowPages < 6) {
maxShowPages = 6;
}
console.log(data);
if (data.totalPages > 1) {
let prePageView = '';
if (data.prePage) {
prePageView = `<li class="page-item"><a class="page-link" href="${route}${data.prePage}">&laquo;</a></li>`;
}
let nextPageView = '';
if (data.nextPage) {
nextPageView = `<li class="page-item"><a class="page-link" href="${route}${data.nextPage}">&raquo;</a></li>`;
}
let pageView = '';
// for special cases when pages are greater than maxShowPages, and UI being distorted.
if (data.totalPages > maxShowPages) {
const pagesIndex = [1]; //push first page
let availableSlots = maxShowPages - 3; // first index: 1, last index: data.totalPages, one for ...
let startIndex = data.currentPage - maxShowPages / 2 >= 2 ? data.currentPage - 2 : 2;
//push 0/... at left side
if (startIndex != 2) {
pagesIndex.push(0);
availableSlots--;
}
let endIndex = availableSlots + startIndex;
//#1: check if last few pages remain then it should not remain availableSlots for `0/...` at right side
if (startIndex + availableSlots >= data.totalPages - 1) {
startIndex = data.totalPages - availableSlots - 1;
endIndex = data.totalPages;
}
for (let i = startIndex; i < endIndex; i++) {
pagesIndex.push(i);
}
// check if remain availableSlots, meaning need to insert `0/...` at right side,
// Note: #1 condition must not enter to this situation, hence #1 condition introduced.
if (pagesIndex.length == maxShowPages - 2) {
pagesIndex.push(0);
}
pagesIndex.push(data.totalPages); // finally push the last page
// Now just iterate and push elements, 0 denotes `...`
pagesIndex.forEach((p) => {
if (p == 0) {
pageView += '<li class="page-item"><a class="page-link" href="#">...</a></li>';
return;
}
let activeClass = '';
if (data.currentPage == p) {
activeClass = 'active';
}
pageView += `<li class="page-item ${activeClass}"><a class="page-link" href="${route}${p}">${p}</a></li>`;
});
} else {
// for normal cases when pages are less than maxShowPages
for (let p = 1; p <= data.totalPages; p++) {
let activeClass = '';
if (data.currentPage == p) {
activeClass = 'active';
}
pageView += `<li class="page-item ${activeClass}"><a class="page-link" href="${route}${p}">${p}</a></li>`;
}
}
return {
data,
show: true,
html: `<div>
<ul class="pagination">
${prePageView}
${pageView}
${nextPageView}
</ul>
</div>`,
};
}
return { data, show: false, html: '' };
}
static doHTML(obj, options) {
return this.html(this.do(obj), options);
}
}
module.exports = Paginate;

Pagination

Create Paginate.js at path App/Helper/Paginate.js, find this file with this gist.

Inside controller

'use strict';

const Company = use('App/Models/Company');
const Paginate = use('App/Helper/Paginate');

class CompanyController {
  async index(ctx) {
    const companies = await Company.query().paginate(Paginate.pageNumber(ctx));
    return ctx.view.render('dashboard.home', {
      companies: companies.toJSON(),
      paginate: Paginate.doHTML(companies),
    });
  }
}

Inside Edge/Html view

@if(paginate.show)
  Total Results: {{paginate.data.total}}
  {{{paginate.html}}}
  @endif

  <table class="table table-striped">
    <thead>
      <tr>
      <th scope="col">Id</th>
      <th scope="col">Company Name</th>
      </tr>
    </thead>
    <tbody>
      @each(company in companies.data)
        <tr>
          <td>{{company.id}}</td>
          <td>{{company.name}}</td>
        </tr>
      @else
        <tr>
          <th scope="row" colspan="6"><h5 class="text-center">No data available</h5></th>
        </tr>
      @endeach
    </tbody>
  </table>
// start/queue.js
'use strict'
/**
* Start the queue listener for the consumer process
* a.k.a your main application
*
* @version 2.0.0
* @adonis-version 4.0+
*/
const Queue = use('Queue');
Queue.processing().then(
success => { console.log(success.message) },
error => {
console.error(error.message);
process.exit(1);
}
);

Add workers in AdonisJS

To use worker use bee-queue and bull-arena.

  • bee-queue for queue management with redis and
  • bull-arena for UI of queue managment, see Arena.js for UI implementation, you can add password session too for open UI.

for queue management https://github.com/stitchng/adonis-queue is better option which is on the top of bee-queue.

and for invoke adonis as worker see https://github.com/ReactiveXYZ-Dev/Adonis-Queue-Pro-Test/blob/master/queue_server.js, also see Queue.js

const { Ignitor } = require('@adonisjs/ignitor')

new Ignitor(require('@adonisjs/fold'))
    .appRoot(__dirname)
    .preLoad('start/queue')
    .fire()
    .catch(console.error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment