Do we need to use plugins for modularizing - hapi.js

Is it mandatory to use plugins for modularizing the code. Can we just create js files and put the handlers and routes in different js files and export them as needed to achieve modularization.

Plugins are just Hapi's way of modularizing application code.
For instance, in my application i wanted to perform https and www redirections on requests.
Initially the code looked like this -
server.ext({
type: 'onRequest',
method: function (request, reply) {
if (/^www\./.test(request.headers.host)) {
return reply()
.redirect('https' + '://' + request.headers.host.replace(/^www\./, '') + request.url.path)
.code(301);
} else {
reply.continue();
}
}
});
After that I created a plugin hapi-gate, thinking that other people like me would also have this need.
Now my code looks like this -
server.register({
register: require('hapi-gate'),
options: {https: true,
www: true} // will force https and www on all requests
})
You decide now which one looks cleaner and modularized..

Related

Strapi v4 Extending Server API for Plugins does not work

I am trying to follow the Strapi v4.0.0 guide on https://docs.strapi.io/developer-docs/latest/developer-resources/plugin-api-reference/server.html#entry-file for extending the users-permission plugin to add a custom route/controller, but so far have been unsuccessful. I add the custom files as stated in the docs, but there is no change in the UI.
I managed to get this to work for normal API highlighted in yellow, but was unable to do so for the users-permission plugin
In the previous version 3.6.8 this functionality was allowed through the extensions folder.
Am I missing something from the new guide, I even tried copying the files from node_modules > #strapi > plugin-users-permission and adding a new route and method to the exiting controller file but it still does not reflect the change in the section where we assign different route permission to roles. The user-permission plugin still shows the original routes, with no change.
Thanks,
I ran into this thread while researching pretty much the same issue, and I wanted to share my solution.
First of all, I found this portion of the documentation more useful than the one you referenced: https://docs.strapi.io/developer-docs/latest/development/plugins-extension.html
My goal was the write a new route to validate JWT tokens based on the comment made here: https://github.com/strapi/strapi/issues/3601#issuecomment-510810027 but updated for Strapi v4.
The solution turned out to be simple:
Create a new folder structure: ./src/extensions/user-permissions if it does not exist.
Create a new file ./src/extensions/user-permissions/strapi-server.js if it does not exist.
Add the following to the file:
module.exports = (plugin) => {
plugin.controllers.<controller>['<new method>'] = async (ctx) => {
// custom logic here
}
plugin.routes['content-api'].routes.push({
method: '<method>',
path: '/your/path',
handler: '<controller>.<new method>',
config: {
policies: [],
prefix: '',
},
});
return plugin;
};
If you're unsure what controllers are available, you can always check the API documentation or console.log(plugin) or console.log(plugin.controllers).
After the admin server restarts, you should see your new route under the user-permissions section as you would expect, and you can assign rights to it as you see fit.
My full strapi-server.js file including the logic to validate JWT:
module.exports = (plugin) => {
plugin.controllers.auth['tokenDecrypt'] = async (ctx) => {
// get token from the POST request
const {token} = ctx.request.body;
// check token requirement
if (!token) {
return ctx.badRequest('`token` param is missing')
}
try {
// decrypt the jwt
const obj = await strapi.plugin('users-permissions').service('jwt').verify(token);
// send the decrypted object
return obj;
} catch (err) {
// if the token is not a valid token it will throw and error
return ctx.badRequest(err.toString());
}
}
plugin.routes['content-api'].routes.push({
method: 'POST',
path: '/token/validation',
handler: 'auth.tokenDecrypt',
config: {
policies: [],
prefix: '',
},
});
return plugin;
};
When exporting routes you need to export the type, either content-api or admin. Look at the Strapi email plugin in node_modules for example, change the folder and file structure in your routes folder to match that and then you will be able to set permissions in the admin panel.
If your Strapi server is using Typescript, make sure that you name your extension files accordingly. So instead of strapi-server.js, you would need to name your file strapi-server.ts.

Nuxt.js env Property, understanding and how to use it?

following https://nuxtjs.org/api/configuration-env
I have been trying to set up my apiUrl in nuxt.config.js once for the whole project, like:
export default {
env: {
apiUrl: process.env.MY_REMOTE_CMS_API_URL || 'http://localhost:1337'
}
}
adding this in nuxt.config.js, I'd expect (and would like) to have apiUrl accessible everywhere in the project.
In particular, it is needed for the 3 following cases:
with axios, to generate static pages from dynamic urls (in nuxt.config.js)
generate: {
routes: function () {
return axios.get(apiUrl + '/posts')
.then((res) => {
return res.data.filter(page => {
return page.publish === true;
}).map(page => {
return {
route: '/news/' + page.slug
}
})
})
}
},
with apollo, to get data via graphql (in nuxt.config.js)
apollo: {
clientConfigs: {
default: {
httpEndpoint: apiUrl + '/graphql'
}
}
},
in every layout, page and components, as the base url of media:
<img :src="apiUrl + item.image.url" />
As you might see, only thing I need is to 'print' the actual base url of the cms.
I have also tried to access it with process.env.apiUrl, with no success.
The only way I was able to make it has been to create an extra plugin/apiUrl.js file, which injects the api url, and seems wrong to me as I am now setting the apiUrl twice in my project.
I asked this question in the past, but in a way less clear way. I was suggested to use dotenv, but from the docs it looks like adding an additional layer of complication that might not be necessary for a simpler setup.
Thanks.
I think dotenv module really is what you need.
This is my setup:
Project root has a .env file that contains
BASE_URL=https://www.myapi.com
require('dotenv').config() at top of nuxt.config.js
#nuxtjs/dotenv installed and added to buildModules of nuxt.config.js
env: { BASE_URL: process.env.BASE_URL} added to nuxt.config.js
axios: { baseURL: process.env.BASE_URL } added to nuxt.config.js (optional)
You should have access to your .env throughout the project. (process.env.BASE_URL)
I haven't used apollo, but you should be able to set the apollo endpoint with process.env.BASE_URL + '/graphql'
As of Nuxt 2.13, #nuxtjs/dotenv is not required anymore. Read here
The concept that I was missing is that you set up the same named variable in your server / pipeline, so that you have your (always local / never pushed) .env file and a same name variable remotely, not added to your repo (where the value can be the same or different)

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.

Laravel routes.php include file using Session

Not sure if this is possible, but here it goes.
What I am looking to do is include my "admin" routes as a separate file, only if the user is an admin (therefore a non admin will get a 404 error
routes.php
if( Session::get('user')->is_admin )
require_once('routes-admin.php');
if( Auth::check() )
require_once('routes-user.php');
Route::get('/', function() {
return view('home');
});
routes-admin.php
Route::get('admin', function() {
return view('admin-dashboard');
});
routes-user.php
Route::get('user', function() {
return view('user-dashboard');
});
What I am trying to do is avoid having the test repeated with every single Route
so if my user segment has 10 pages I currently need 30 lines of code dedicated to Auth::check() (the if, else and redirect if not), where I can instead have a single check on routes.php and the user will get a 404 if they don't belong
Is there a way to perform this check outside of the Route?
Perhaps you want to read documentation first?
Route::group(['middleware' => 'auth'], function()
{
Route::get('/', function()
{
// Uses Auth Middleware
});
Route::get('user/profile', function()
{
// Uses Auth Middleware
});
});
Above code does exactly what you need, is "person logged in?" let him go to page "whatever".
You can create middlewares (check if user is admin or basic user) yourself and apply on groups.
Example middleware
class BeforeMiddleware implements Middleware
{
public function handle($request, Closure $next)
{
// Perform action
return $next($request);
}
}
Do not get me wrong, just your approach is really not Laravel like. Try to see some open source projects done in L5 or even in L4. Try to use everything Taylor already done for you. Documentation is your firend here.
Following the response of #Kyslik for the middleware, you can "include" your own routes file in your RouteServiceProvider like the default routes file, the RouteServiceProvide is located in: app/Providers/RouteServiceProvider.php,
Find the section
require app_path('Http/routes.php');
and just replicate with the name of your routes file want to include

How to setup Sails.js routes to support pushstate with a SPA on the frontend

How to setup SailsJS routes to support pushstate, but still be able to serve requests prefixed with /api along with serving static assets as well?
Using:
Backbone 1.x with pushState: true
Sails 0.10.x
Suggested solution from this thread is to use the /* wildcard and redirect all to the index view.
/path/to/app/config/routes.js
'/*': {
view: 'index'
}
The problem is that this will redirect everything to index. including static file assets, the express.static middleware doesn't seem to have an effect, this route is somehow taking precedence.
Also, this prefix below doesn't have any effect, since the route above takes precedence, however the prefix works if I just remove the wildcard i.e '/': { view: 'index' }
/path/to/app/config/blueprint.js
module.exports.blueprints = {
...
prefix: '/api',
...
}
Obviously, this does not seem as a core SailsJS issue, but rather my minimal knowledge the expressjs router, since that's what sailsjs is using.
Theoretically, I could explicitly just list out all the assets prefixed routes and have a controller to serve all, since they are well known and static i.e. '/js*': { controller: 'AssetsController', action: 'serve', and so on for '/styles*', '/images*', '/templates*', '/fonts*', but I'm really hesitant on doing so, I'm hoping for a better practice solution.
Also that won't solve this routes /api problem with the wildcard '/*'
If you're willing to use the development version of Sails from GitHub, you can use the skipRegex route option to let your wildcard route ignore API requests, and the skipAssets option to have it ignore asset URLs:
'/*' : {view: 'index', skipAssets: true, skipRegex: /^\/api\/.*$/}
Otherwise, you can create a controller to serve your view, and add the code to skip unintentionally-matched URLs in the action code:
// api/controllers/StaticController.js
module.exports = {
index: function(req, res, next) {
if (req.path.match(/\..*/g) || req.path.match(/^\/api\/.*$/)) {
return next();
}
return res.view('index');
}
}
Then in /config/routes.js:
'/*': 'StaticController.index'
......
Try updating your config/404.js file.
Remove the following:
res.status(result.status);
res.render(viewFilePath, function (err) {
// If the view doesn't exist, or an error occured, send json
if (err) { return res.json(result, result.status); }
// Otherwise, serve the `views/404.*` page
res.render(viewFilePath);
});
Add this: res.redirect("/");
Cheers