Nuxt module that adds another website as serverMiddleware - javascript

The idea
First you have a main Nuxt website like any other. Then you add my module to your project. My module then adds a subdomain "admin.example.com" to your project which is a fully fleshed out Nuxt based website all of its own but running from your projects process, so instead of making two website projects that have to be started separately the idea is that I can make my one website project and then one module project which adds the other website, turning three projects into two.
Code
module.js
this.addServerMiddleware(vhost('admin.website.com', adminApp));
adminApp.js
const {Nuxt, Builder} = require('nuxt');
const config = require('../admin/nuxt.config');
config.dev = true;
const nuxt = new Nuxt(config);
if (nuxt.options.dev) {
new Builder(nuxt).build()
.catch(function(error) {
console.log(error);
})
}
module.exports = nuxt.render;
Part of nuxt.config.js from the admin website
// __dirname leads to my modules directory
// process.cwd() leads to the main websites folder
module.exports = {
mode: 'universal',
srcDir: __dirname,
rootDir: __dirname,
modulesDir: process.cwd(),
The problem
Whenever I go to "admin.website.com" it results in the following message:
I've tried tonnes various changes and tweaks already but nothing seems to be working. As far as I can tell it might have to do with the fact that either A) the website gets built somewhere it doesn't find the generated resource files, or B) that I haven't prayed enough to the spaghetti monster.
I've tried exporting adminApp as an Express app that uses app.use(nuxt.render) but that results in the same error so I'm certain that's not the issue. I've tried using an Express app for api.website.com and that works just fine, so I know I'm like THIS |--| close to making this all work. Adding subdomains like this does work and each subdomain can be an Express app, but for some reason Nuxt can't find any resources when used this way. According to https://nuxtjs.org/api/nuxt-render/ nuxt.render(req, res) as you can see takes req and res as arguments, which should mean I don't need to use it via an Express app, serverMiddleware is already an Express app.
Thoughts?

I ran in this error before when serving apps through serverless. I think that you need to explicitly set a publicPath or a buildDir in nuxt.config.js for both apps to make setup work. Your problem could be that all the build assets are generated in the same folder, causing one of the apps to not find the build assets. The buildDir property is set on the nuxt.config.js and the publicPath setting is defined on the build property.
More info here:
https://nuxtjs.org/api/configuration-builddir
https://nuxtjs.org/api/configuration-build#publicpath

Related

Next JS 13 'next build' outputs app directory app/page.js and route group pages as 0 bytes and causes 404 error in production

Using the Next.js 13 experimental appDir feature, I have been able to port all my pages from the pages directory to the app directory and everything works well in the dev environment but the issue is that I have been getting 404 error in production.
I decided to troubleshoot by running next build locally and noticed that
Next JS was building all the nested route group paths with the index path page app/page.js as 0 byte which in turn causes the 404 error for all those pages.
NOTE:
In my next.config.js file, I have the experimental appDir flag configured while I have also deleted the pages directory because all the pages have now been moved to the app directory.
Here is a look at my next.config.js file:
/** #type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
...
};
module.exports = nextConfig;
I have tried to look at the official documentation for possible configurations and fixes but I haven't found anything to help.
What can I do?
Thank you.
EDIT:
So on further trials,
I decided to eliminate the route group to test if it would actually build successfully and it did. I did that by changing (dashboard) folder name to dashboard which in turn added '/dashboard' to my routes which I don't want. I need the route group in the app directory for shared layouts across the app without changing the pathname. For instance, the route group directory
Example:
app/(dashboard)/meeting/page.js still has a route of "/meeting" instead of this directory; app/dashboard/meeting/page.js which now has a route of "/dashboard/meeting".
After going through a similar issue on the vercel/next repository on Github, I discovered that the error in this particular issue occurred because the output: "standalone" option was set in the next.config.js file of the project but I do not have that option set in mine. Besides, the particular fix for this issue still noted the particular error with route groups which is what I am facing.
The baffling thing about my issue however is that the app/page.js meant to replace pages/index.js with the route "/" also returns 404 in production in addition with the 404 errors encountered with the route group pages.
I tweeted about the issue and luckily for me, Lee Robinson, the VP of Developer Experience at Vercel saw it and took a look at the repository. The issue was caused because I used i18n with appDir in the next.config.js file like this:
const nextConfig = {
experimental: {
appDir: true,
},
i18n: {
locales: ['en'],
defaultLocale: 'en',
},
...
}
Since the app only supports English for now and we are not planning to add any languages, I decided to remove it and all the 404 errors disappeared. Here is the updated next.config.js file:
const nextConfig = {
experimental: {
appDir: true,
},
...
}
If you need to support multiple languages within the app directory using Next JS 13, Here is a guide written here by Vercel.
Thank you.
I had the same issue. Removing the following from my next.config.js did the trick:
i18n: {
locales: ['en'],
defaultLocale: 'en',
}

React App Not Loading When Using http-proxy-middleware

Alright, I've about reached the end of my sanity on this one.
So, I have a basic React frontend w/ an Express backend. React is running off of localhost:3000, the backend is running off of localhost:3030. Following along on a guide for setting up some Spotify integration, everything works fine up until I hit the portion on setting up a proxy. (I have a slightly different setup from the Spotify guide, all my stuff runs through /spotify/auth rather than /auth)
I installed http-proxy-middleware, created the setupProxy.js in my /src folder, and if I ever try to load up localhost:3000 as normal, I get nothing-- my app doesn't load at all.
The only way to have the app appear again is to remove the file. The one on the spotify guide is a bit out of date as far as I can tell anyway, but even using suggestions found elsewhere, I've gotten no luck. Here is the current setup I have for my setupProxy.js file:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
"/spotify/**",
createProxyMiddleware({
target: "http://localhost:3030",
changeOrigin: true,
})
);
};
I've even removed the actual fetch that would be making use of the proxy and still have no luck loading my page. I am also unable to use "proxy": "http://localhost:3030" in my package.json as it throws:
Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
- options.allowedHosts[0] should be a non-empty string."
Managed to solve my problem, though I am still unsure of why it works.
The issue I was running into stems from using "type": "module" in the package.json. I was using correct import statements in all of my backend, as well as tried to use it for the setupProxy.js as well, however this would always result in the issue from the question. After removing the line and swapping out the imports for requires in my backend, everything started working.
It seems like a strange compatibility issue, but there's probably a much better explanation.
Had the same problem as you where my react app wasn't loading because of http-proxy-middleware. Different problem/solution, but for people that also had this problem, and were following this youtube video https://www.youtube.com/watch?v=hxyp_LkKDdk
The tutorial has
const proxy = require("http-proxy-middleware")
instead of
const {createProxyMiddleware} = require("http-proxy-middleware")
After I made that change, my issue was solved. Don't forget to change "proxy" to "createProxyMiddleware" in app.use() as well

How to get full list of Gatsby.js routes/endpoints in Cypress tests

I am currently writing some E2E tests with Cypress for a Gatsby based project.
For one test in particular, i'd like to loop through all pages of my Gatsby site, and in order to achieve this, I need a test fixture (e.g. endpoints.json) which includes an array of all urls.
I've tried the following methods (but all have limitations):
1. Running a node script to check the folder structure in the src/pages folder
Limitation - This doesn't account for dynamically generated pages in gatsby-node.js with graphql
2. Running a node script to scrape URLs in the sitemap.xml file generated with gatsby-plugin-sitemap
Limitation - The plugin only generated a sitemap.xml file in prod builds and not in dev (cypress runs a dev server)
Would be more than grateful if anyone has a suggestion for how we would get a full list of Gatsby endpoints in this environment.
You might just want to generate a file in the desired format on-build using the data in GraphQL:
// gatsby-node.js
const path = require("path")
const fs = require("fs").promises
exports.onPostBuild = async ({ graphql }) => {
const { data } = await graphql(`
{
pages: allSitePage {
nodes {
path
}
}
}
`)
return fs.writeFile(
path.resolve(__dirname, "all-pages.txt"),
data.pages.nodes.map(node => node.path).join("\n")
)
}
This creates a .txt file with each page’s path on a line. You could also just write out the data as JSON by passing in JSON.stringify(data.pages) as the second argument to writeFile, though.
There is a pretty cool new plugin for Cypress that will allow you to get the endpoints in your fixtures and env variable, called gatsby-cypress-endpoints. coreyward's answer is perfectly valid, but this should allow you to get the endpoints only when you run Cypress.
Please be aware that with more recent versions of Gatsby (particularly v3) you may have to set the env variable CI=true for the suggested solutions to work.
I believe this is because of changes to the development experience where pages & queries are processed on-demand instead of upfront.
The following flags FAST_DEV / QUERY_ON_DEMAND may be responsible for this behaviour.

Electron app Error when moving build folder

I am currently working on an Electron-App using NPM that unzips a file to a selected directory. An Installer basically. Now when I build the application it still works fine. I simply start the setup.exe in my win-unpacked folder and everything goes down smoothly. Now when I move the win-unpacked folder to a different directory my app runs fine but when it starts the unzipping process it throws the following error:
I have noticed, that the first file-path displayed (for some reason) doesnt use utf8, but all the others are being displayed correctly (should be an ä). Also I have allready tried deleting the node_modules folder and then running npm i to reinstall them. Still having the same issue.
The following is the code that starts the unzipping process:
const path = require('path');
const ipcRenderer = require('electron').ipcRenderer;
const Unzip = require('./unzip');
const os = require('os');
const fs = require('fs');
$('#information_next').click(function () {
var extractPath = $('#input_select').val();
let filepath;
const platform = os.platform();
const nodeName = platform == 'win32' ? 'wcd.node' : (platform == 'darwin' ? 'mcd.node' : 'lcd.node');
let customData = require("bindings")(nodeName);
let zip = h2a(customData.customData());
if(os.platform() == 'darwin') {
filepath = path.join(__dirname, '..', '..', '..', '..', 'ZIP.zip');
} else {
filepath = path.join(__dirname, '..', '..', 'ZIP.zip');
}
var xPath = path.join.apply(null, extractPath.split('\\'));
var unzip = new Unzip(filepath, xPath.toString());
unzip.extract(extractPath, zip, 100, (percentage) => {
// Code for Progressbar
finish = true;
setTimeout(function () {
$('.main_wrapper').addClass('hidden');
$('.main7').removeClass('hidden');
}, 1500);
}).catch((e) => {
$('.main6').addClass('hidden');
$('.main_install_error').prop('hidden', false);
});
});
Here I am using the bindings module to require a .node file which passes a string to my app. And this seems to be the module that causes the Error.
I have been trying to resolve this for hours now, but nothing I've found online works. Also I couldn't find anyone who had the same Error that I have. Any suggestions on how to fix this would be appreciated.
Greetings
Matt.S
EDIT:
I might have just figured out the main problem. The bindings module contains a function that looks for the module root. This path is displayed in the first line of the ERROR. However since this app has allready been built, all the source code is inside of an app.asar file. Bindings can't seem to distinguish between the .asar file and an ordinary folder. So even tho the path is correct, it doesn't work. The reason why it worked in the original win-unpacked is because bindings (if it cant find the module-root) moves up througth the directory until it finds the root. And since the original win-unpacked folder is inside my project directory bindings uses the un-built module-root.
I was able to reproduce this error with the bindings module.
It seems to have alot of problems with the electron framework which can cause such behaviour.
The 'root directory' Problem
You answered this yourself with your edit of the original question, but for the sake of delivering a complete answer, I am including this too
I simply start the setup.exe in my win-unpacked folder and everything goes down smoothly. Now when I move the win-unpacked folder to a different directory my app runs fine but when it starts the unzipping process it throws an error.
This has an interesting reason. Since your dist directory (the build destination of your project) is inside your working project, the bindings module assumes your working directory is the root directory of your built app. Thus it is able to resolve the path to your module and everything works fine. Once your built app is placed somewhere else, the bindings module isn't able to find the root directory of your app and throws the error you linked.
The 'file://' Problem
Another problem of the bindings module is handling paths with the 'file' protocol.
Someone already went ahead and created an issue (+ a pull request) for this problem, so you could modify your local installation of this module, even though I would discourage taking such actions.
My personal advice:
The current state of the bindings module makes it unattractive for use together with the Electron framework. I heard it even has problems handling umlauts properly, so your best bet in your specific situation is to get rid of it. You could make a small local module which wraps your .node binary and makes it as easy to require as all your other node modules.
This is an amazing article about creating your own module.

How can I read files from a subdirectory in a deployed Meteor app?

I am currently making a Meteor app and am having trouble reading files from the private subdirectory. I have been following a couple different tutorials and managed to get it to work flawlessly when I run the meteor app locally. This question (Find absolute base path of the project directory) helped me come up with using process.env.PWD to access the root directory, and from there I use .join() to access the private folder and the pertinent file inside. However, when I deployed this code, the website crashes on startup. I am very confident that it is an issue with process.env.PWD, so I am wondering what the proper method of getting Meteor's root directory on a deployed app is.
//code to run on server at startup
var path = Npm.require('path')
//I also tried using the below line (which was recommended in another Stackoverflow question) to no avail
//var meteor_root = Npm.require('fs').realpathSync( process.cwd() + '/../' );
var apnagent = Meteor.require("apnagent"),
agent = new apnagent.Agent();
agent.set('cert file', path.join(process.env.PWD, "private", "certificate-file.pem"))
agent.set('key file', path.join(process.env.PWD, "private", "devkey-file.pem"))
In development mode the file structure is different than after bundling, so you should never rely on it. Particularly, you should not access your files directly like you're doing with path methods.
Loading private assets is described in this section of Meteor's documentation. It mostly boils down to this method:
Assets.getBinary("certificate-file.pem");
and it's getText counterpart.
As for configuring APN agent, see this section of documentation. You don't have to configure the agent by passing file path as cert file param. Instead you may pass the raw data returned by Assets methods directly as cert. The same holds for key file ~ key pair and other settings.
As an alternative, you would need to submit your files independently to the production server to a different folder than your Meteor app and use their global path. This, however, would not be possible for cloud providers like Heroku, so it's better to use assets in the intended way.

Categories