Hapi,js - Proper way to include a plugin inside a plugin - hapi.js

I have this Hapi.js server that requires 2 endpoints to do Basic auth(using the hapi-auth-basic module). Each endpoints logic for checking the "username/password" is different, so i've broken these 2 things into their own plugins.
Currently this is how i am registering the the plugins:
...
server.register([Basic,
...
require('./auth/register-device'),
require('./auth/sender'),
...
], (err) => {
....
While this works, i have to make sure that the Basic plugin is also being registered.
I tried to register the Basic plugin in my plugins register method here(which i've removed and moved to the above file):
https://github.com/salty-pig/Salty-Pig/blob/master/auth/sender.js#L29 , but when i did that for both plugins, i got the error that the Basic plugin was already registered.
I guess i'm wondering what the best practice here should be. In my case, this works since i'm not distributing these "auth" plugins.
Question 2 would be: If i had a plugin that i wanted to make into a npm module, that needed to include another plugin, what are the best practices for that.
thanks

If you are writing a plugin that depends on other plugins, you could use the Hapi server.dependency API:
exports.register = function (server, options, next) {
server.dependency('hapi-auth-basic', function(server, next) {
server.register([
// register plugins that depend on hapi-auth-basic here
], (err) => {
});
});
next();
};
You can supply server.dependency with an array to specify multiple dependencies.

Related

How to check if server build has been updated in SSR Nuxt application

I dont know how to solve the problem if SSR Nuxt app in browser is not compatible with server side build cause build has been updated. It means that user have old version of application in the browser and needs to refresh the page. I found something like this: https://dev-clone.nuxtjs.app/alejandroakbal/632139
So I have created pwa-update.js file in the plugins dir and register it in the nuxt.config.js.
But I dont see any console.log() in the console. Dont understand how to use it and if it is the right way to do it.
Implementaion looks like pwa-update.js
export default async (context) => {
const workbox = await window.$workbox;
if (!workbox) {
console.debug("Workbox couldn't be loaded.");
return;
} else {
console.log('Workbox has been loaded.'); // Dont see any message.
}
workbox.addEventListener('installed', (event) => {
if (!event.isUpdate) {
console.log('The PWA is on the latest version.');
return;
}
console.log('There is an update for the PWA, reloading...');
// window.location.reload();
});
};
nuxt.config.js
plugins: [
{ src: '~/plugins/pwa-update.js', mode: 'client' },
],
If you're regenerating your service worker using workbox during your development build process, a new service worker will be installed after every build. You can check this in your browser's dev tools. I believe the workbox generated service worker callls skipWaiting() in order to install new service workers immediately.
The client should get the new resources automagically because of webpack JS chunk name changes (assuming you're using webpack, the JS chunks it generates get new names after every new build, for cache busting purposes) and service worker version changes (workbox auto-increments service worker version, busting that cache as well). In other words, SSR or not, you won't need to worry about any version mismatches so long as you're using workbox to generate your service worker for you.

Implement Express Static in Apostrophe CMS

this is an Apostrophe CMS question entirely. Piggy-backing off this question, which was never answered, I decided to ask my question here on Stack Overflow. I could not find the topic here.
https://forum.apostrophecms.org/t/performance-engineering/61/2
With that in mind, ApostropheCMS is a very cool in-editor CMS that is built on an express server, but I can not figure out how to access what would be, in a typical express setup, the app.js file.
This npm module does exactly what we need to implement.
https://www.npmjs.com/package/express-static-gzip
The code to add to express:
var express = require('express');
var expressStaticGzip = require('express-static-gzip');
var app = express();
app.use('/', expressStaticGzip('/my/rootFolder/', {
enableBrotli: true,
customCompressions: [{
encodingName: 'deflate',
fileExtension: 'zz'
}],
orderPreference: ['br']
}));
1) How can I add this to a standard Apostrophe setup?
or
2) Is there already a method built into apostropheCMS that enables brotli and gzip?
First, Node.js code is not the best place to do this. You will get better performance if you implement it in production via your production reverse proxy, such as nginx, and do not implement it in dev at all. Since running Node behind a proxy is always best practice, that should be a viable option for you.
However, that being said, this can be done in Node too. And perhaps you have a use case like allowing pre-zipped files to be served in this way whether the proxy is present or not.
The servePublicAssets method of the apostrophe-assets module is responsible for serving /public via express.static. You can change it out at project level:
// in your PROJECT LEVEL lib/modules/apostrophe-assets/index.js file.
// DO NOT modify node_modules/apostrophe, you do not need to do that.
// DO NOT copy the entire index.js.
// This is all you need to override just this ONE method.
const expressStaticGzip = require('express-static-gzip');
module.exports = {
construct: function(self, options) {
self.servePublicAssets = function() {
const middleware = [];
if (self.lessMiddleware) {
middleware.push(self.lessMiddleware);
}
middleware.push(expressStaticGzip(
self.apos.rootDir + '/public',
{
enableBrotli: true,
customCompressions: [
{
encodingName: 'deflate',
fileExtension: 'zz'
}
],
orderPreference: ['br']
}
));
self.expressMiddleware = {
when: 'beforeRequired',
middleware: middleware
};
};
}
};
We do override an entire method here rather than just injecting different middleware. It would be nice if Apostrophe didn't assume you wanted express.static but rather consulted an option allowing you to inject alternative middleware. That would make a good PR.

created hook for vuex / nuxtClientInit?

I was wondering whats the best way to do something like nuxtClientInit. I'm trying to load the Auth State as early as possible on the client and save it in my vuex store but not on the server side. It would be great if vuex had something like the created hook for components but that doesn't exist to my knowledge.
How could I achieve this behavior? One way could be calling an action from a layout but is that the best way?
I understand the nuxt team are working on a nuxtClientInit feature but before they release that you could just make your own. To understand the workflow that nuxt undertakes when there is a request you can look at the lifecycle here. This shows that nuxtServerInit is called first then middleware. During this middleware call nuxt.config.js is served and this contains your custom configuration. Part of this is 'plugins' which as the docs say,
This option lets you define JavaScript plugins that should be run
before instantiating the root Vue.js application.
So if you write a plugin to call a store action you can then get and set your local storage from there. So, start with a nuxt-client-init plugin:
//nuxt-client-init.client.js
export default async context => {
await context.store.dispatch('nuxtClientInit', context)
}
then add the plugin to nuxt.config.js:
//nuxt.config.js
plugins: [
'~/plugins/nuxt-client-init.client.js'
],
If you notice the naming convention here, the .client.js part of the plugin tells nuxt this is a client only plugin and is shorthand for '{ src: '~/plugins/nuxt-client-init.js', mode: 'client' }' which since nuxt 2.4 is the way to define the old '{ src: '~/plugins/nuxt-client-init.js', ssr: false }'. Anyway, you now have a call to your store so you can have an action to call from local storage and set a state item.
//store/index.js
export const actions = {
nuxtClientInit({ commit }, { req }) {
const autho = localStorage.getItem('auth._token.local') //or whatever yours is called
commit('SET_AUTHO', autho)
console.log('From nuxtClientInit - '+autho)
}
}
You probably need to restart your app for that to all take effect but you are then getting and using your Auth State without any of that pesky nuxtServerInit business.

Vue Cli 3 how to use the official PWA plugin ( Service Worker )

on my first vue project attempting to wrestle with the official PWA plugin ( https://github.com/yyx990803/register-service-worker ).
My specific problem: capturing the registered service worker and using it for anything. The github readme shows the exact file that is produced, and there seems to be zero documentation about how to work with this service worker once it is instantiated ( do I capture the registration instance? if so, how? )
I found this issue: https://github.com/vuejs/vue-cli/issues/1481
and am providing a better place to talk about this, as I haven't been able to find any example code or clear documentation about how to work with this.
If anyone has some sample code, please share. Vue and the new cli are incredible tools, documenting things like this is a necessary step forward to increasing the adoption of the platform
As already pointed out, it's more of a "service workers" issue than a "vue cli" one.
First of all, to make sure we're on the same page, here's what the boilerplate content of registerServiceWorker.js should look like (vue cli 3, official pwa plugin):
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.\n'
)
},
cached () {
console.log('Content has been cached for offline use.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
If you haven't changed the BASE_URL variable in your .env file, then it should correspond to the root of your vue app. You have to create a file named service-worker.js in the public folder (so that it's copied into your output directory on build).
Now, it is important to understand that all the code in the registerServiceWorker.js file does is register a service worker and provide a few hooks into its lifecycle. Those are typically used for debugging purposes and not to actually program the service worker. You can understand it by noticing that the registerServiceWorker.js file will be bundled into the app.js file and run in the main thread.
The vue-cli 3 official PWA plugin is based on Google's workbox, so to use the service worker, you'll have to first create a file named vue.config.js at the root of your project and copy the following code in it:
// vue.config.js
module.exports = {
// ...other vue-cli plugin options...
pwa: {
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'public/service-worker.js',
// ...other Workbox options...
}
}
}
If you already have created a vue.config.js file, then you just have to add the pwa attribute to the config object. Those settings will allow you to create your custom service worker located at public/service-worker.js and have workbox inject some code in it: the precache manifest. It's a .js file where a list of references to your compiled static assets is stored in a variable typically named self.__precacheManifest. You have to build your app in production mode in order to make sure that this is the case.
As it is generated automatically by workbox when you build in production mode, the precache manifest is very important for caching your Vue app shell because static assets are usually broken down into chunks at compile time and it would be very tedious for you to reference those chunks in the service worker each time you (re)build the app.
To precache the static assets, you can put this code at the beginning of your service-worker.js file (you can also use a try/catch statement):
if (workbox) {
console.log(`Workbox is loaded`);
workbox.precaching.precacheAndRoute(self.__precacheManifest);
}
else {
console.log(`Workbox didn't load`);
}
You can then continue programming your service worker normally in the same file, either by using the basic service worker API or by using workbox's API. Of course, don't hesitate to combine the two methods.
I hope it helps !
as an addition to the answer above: I wrote a small guide on how to go further and add some functionality to the custom service-worker, using the setup above. You can find it here.
Four main things to keep in mind:
configure Workbox in vue.config.js to InjectManifest mode, pointing the swSrc key to a custom service-worker file in /src
In this custom service-worker, some lines will be added automatically in the Build process for importing the precache-manifest and workbox CDN. Following lines need to be added in the custom service-worker.js file to actually precache the manifest files:
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
Listen to registration events in the registerServiceWorker.js file. You can use the registration object that is passed as first argument to the event handlers to post messages to the service-worker.js file:
...
updated(registration) {
console.log("New content is available; please refresh.");
let worker = registration.waiting
worker.postMessage({action: 'skipWaiting'})
},
...
Subscribe to messages in the service-worker.js file and act accordingly:
self.addEventListener("message", (e)=>{
if (e.data.action=='skipWaiting') self.skipWaiting()
})
Hope this helps someone.

How do you test dependency injection in angular?

I use typescript and angular.
So for example in controllers I have notation
a)
export function Controller($scope: Scope) {}
(<any>Controller).$inject = ['$scope'];
b)
export class Controller {
constructor(private $scope) {}
}
App.directive('someCoolDirective', function() {
return {
restrict: 'E',
etc...
controller: <any[]>['$scope', SomeCoolDirective.Controller],
etc...
};
});
c)
App.directive('someDirectives', <any[]>[ '$parse', '$rootScope', '$compile', 'userPermissions', function($parse, $rootScope, $compile, userPermissions) { }
d)
services...
As you see there are different ways of specifing dependencies for DI after minification.
But we are only humans, I tend to forget (Controller).$inject, or I add some dependency and forgot to add in array. If I don't specify it, it works, till minification. Than it is pain in the neck to find it out. Sometimes there is even no error in browser console.
Do you know some tool, that checks all controllers,services, directives and verifies that there is correct $inject ['$scope',... etc.] notation? That it matches all required dependencies? I want to automate in on out build server.
Why don't you hack the Typescript compiler and do it yourself? Then publish it somewhere (e.g. Github) so that it's usable for poor people like me ;)