I am making a next js application.
Deployment works fine in vercel.
For deploying the same project in another server, got help from https://stackoverflow.com/a/63660079/13270726 and used the same instructions in our app.
Deployed the out directory into server using ftp client.
Issue
-> When we enter into http://your-domain.com , it works fine. (Even page refresh also works fine in this page)
-> If we move to about page using the url, http://your-domain.com/about then it also works but on page refresh in the url http://your-domain.com/about results in the error,
-> This page refresh also results in the console error like,
Get http://your-domain.com/about Not found
next.config.js: (With public path)
const config = {
webpack: (config, { isServer }) => {
.
.
.
config.devServer = {
historyApiFallback: true
}
config.output.publicPath = "/"
return config;
}
}
module.exports = withPlugins(config);
The issue arises in page refresh only or when we manually type the url.. But while we navigate to it first time then the issue is not there.
Any good help would be much appreciated as I am stuck for long time..
Edit:
I have a server.js file and its code look like,
const dotenv = require("dotenv");
// import ENVs from ".env.local" and append to process
dotenv.config({ path: ".env.local" });
const express = require("express");
const address = require("address");
const chalk = require("chalk");
// create express web server instance
const app = express();
// pull out ENVs from process
const { LOCALHOST, PORT } = process.env;
// get the Local IP address
const LOCALIP = address.ip();
// tell express to serve up production assets from the out directory
app.use(express.static("out" + '/'));
app.get('/*', (req, res) => {
res.send('ok')
});
app.all('*', function(req, res) {
res.redirect('/index.html');
});
// tell express to listen for incoming connections on the specified PORT
app.listen(PORT, (err) => {
if (!err) {
// log the LOCALHOST and LOCALIP addresses where the app is running
console.log(
`\n${chalk.rgb(7, 54, 66).bgRgb(38, 139, 210)(" I ")} ${chalk.blue(
"Application is running at"
)} ${chalk.rgb(235, 220, 52).bold(LOCALHOST)} ${chalk.blue(
"or"
)} ${chalk.rgb(235, 220, 52).bold(`http://${LOCALIP}:${PORT}`)}\n`
);
} else {
console.err(`\nUnable to start server: ${err}`);
}
});
Related
I hope you all are well. I am having trouble with displaying my own forge data in the AutoDesk Forge reference application. My current .env file is as follows. However, whenever I launch it in http://localhost:9000/upload all I get in return is a blank empty screen.
FORGE_CLIENT_ID=STEHw2Qx... marked ...xrIJUeKRj6 #changed for post
FORGE_CLIENT_SECRET=A54... marked ...c348a #changed for post
FORGE_ENV=AutodeskProduction
FORGE_API_URL=https://developer.api.autodesk.com
FORGE_CALLBACK_URL=http://localhost:9000/oauth/callback
FORGE_BUCKET=cosmostool1.cosmosengineering.es #changed for post
ENV=local
#ADAPTER_TYPE=local
## Connect to Azure IoTHub and Time Series Insights
# ADAPTER_TYPE=azure
# AZURE_IOT_HUB_CONNECTION_STRING=
# AZURE_TSI_ENV=
#
## Azure Service Principle
# AZURE_CLIENT_ID=
# AZURE_APPLICATION_SECRET=
#
## Path to Device Model configuration File
# DEVICE_MODEL_JSON=
## End - Connect to Azure IoTHub and Time Series Insights
ADAPTER_TYPE=csv
CSV_MODEL_JSON=server/gateways/synthetic-data/device-models.json
CSV_DEVICE_JSON=server/gateways/synthetic-data/devices.json
CSV_DATA_END=2011-02-20T13:51:10.511Z #Format: YYYY-MM-DDTHH:MM:SS.000Z
CSV_DELIMITER="\t"
CSV_LINE_BREAK="\n"
CSV_TIMESTAMP_COLUMN="time"
if (process.env.ENV == "local") {
require("dotenv").config({
path: __dirname + "/../.env",
});
}
Because of this line at forge-dataviz-iot-reference-app/server/router/Index.js#L25, you must specify ENV=local before executing npm run dev. Otherwise, it won't read the content of .env.
if (process.env.ENV == "local") {
require("dotenv").config({
path: __dirname + "/../.env",
});
}
Or you can just change it to the below
require("dotenv").config({
path: __dirname + "/../.env",
});
Install dotenv
npm install dotenv
Create a config.js file in your directory and add the following code;
const dotenv = require('dotenv');
dotenv.config();
module.exports = {
// Set environment variables or hard-code here
azure: {
azure_conn_string: process.env.AZURE_IOT_HUB_EVENT_HUB_CONNECTION_STRING
}
};
Update your localserver.js file
const { app, router } = require("./app.js");
const config = require('./config');
app.use(router);
const server = require("http").createServer(app);
if (config.azure.azure_conn_string) {
require("./RealTimeApi.js").createSocketIOServer(server);
}
const PORT = process.env.PORT || 9000;
async function start() {
try { server.listen(PORT, () => { console.log(`localhost: ${PORT}`); }); } catch (error) { console.log(error); }
} start();
Problem Summary
I am working on the backend of a MERN stack app that pulls restaurant data from a mongo collection. When I run nodemon server inside of the backend folder, I obtain the error ERR_MODULE_NOT_FOUND:
[nodemon] 2.0.16
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node server index.js`
node:internal/errors:466
ErrorCaptureStackTrace(err);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '[redacted]/backend/dao/restaurants.DAO' imported from [redacted]/backend/api/restaurants.controller.js
at new NodeError (node:internal/errors:377:5)
at finalizeResolution (node:internal/modules/esm/resolve:405:11)
at moduleResolve (node:internal/modules/esm/resolve:966:10)
at defaultResolve (node:internal/modules/esm/resolve:1174:11)
at ESMLoader.resolve (node:internal/modules/esm/loader:605:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:318:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
at link (node:internal/modules/esm/module_job:78:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v18.2.0
[nodemon] app crashed - waiting for file changes before starting...
Problem Details
My Folder Structure
My folder structure looks like this
restaurant.route.js
import express from "express";
import RestaurantsCtrl from "./restaurants.controller.js";
// create router instance
const router = express.Router();
// respond to GET requests with hello
router.route("/").get(RestaurantsCtrl.apiGetRestaurants);
export default router;
restaurants.controller.js
import RestaurantsDAO from "../dao/restaurants.DAO";
export default class RestaurantsCtrl {
static async apiGetRestaurants(req, res, next) {
// if there is a number of restaurants to show per page passed in the URL, conver it to an integer. Otherwise set it to 20
const restaurantsPerPage = req.query.restaurantsPerPage ? parseInt(req.query.restaurantsPerPage, 10) : 20;
// if there is a page number passed in the URL, conver it to an integer. Otherwise set it to 0
const page = req.query.page ? parseInt(req.query.page, 10) : 0;
let filters = {};
// if we see filters passed in the URL, add it to the filters
if (req.query.cuisin) {
filters["cuisine"] = req.query.cuisine;
}
else if (req.query.zipcode) {
filters["zipcode"] = req.query.zipcode;
}
else if (req.query.name) {
filters["name"] = req.query.name;
}
// get the list of restaurants and a number of restaurants from the database
const { restaurantsList, totalNumRestaurants } = await RestaurantsDAO.getRestaurants({
filters,
page,
restaurantsPerPage,
});
// create a response object to send back to the client
let response = {
restaurants: restaurantsList,
page: page,
filters: filters,
entries_per_page: restaurantsPerPage,
total_results: totalNumRestaurants,
}
// send the response back to the client
res.json(response);
}
}
server.js
import express from "express";
import cors from "cors";
import restaurants from "./api/restaurants.route.js";
// Create a new express application instance
const app = express();
// apply middleware
app.use(cors());
// parse request body as JSON. Our server can accept JSON data in the body of a request
app.use(express.json());
// specify some routes. This is the path that will be used to access the API
app.use("/api/v1/restaurants", restaurants);
// If someone goes to a path that doesn't exist, return a 404 error
app.use("*", (req, res) => res.status(404).json({error : "Not found"}));
// export the application instance for use in the rest of the application
export default app
index.js
import app from "./server.js"
import mongodb from "mongodb";
import dotenv from "dotenv";
import RestaurantsDAO from "./dao/restaurants.DAO.js";
// Load environment variables from .env file, where API keys and passwords are configured
dotenv.config();
// Get access to the mongo client
const MongoClient = mongodb.MongoClient;
// set port
const port = process.env.PORT || 8000;
// Connect to the database
const a = MongoClient.connect(
// The URL of the database to connect to
process.env.RESTREVIEWS_DB_URI,
{
// The options to use when connecting to the database
maxPoolSize: 50,
wtimeoutMS: 2500,
useNewUrlParser: true,
}
)
// If the connection is not successful, throw an error
.catch(err => {
console.log(err.stack);
process.exit(1);
})
// If the connection is successful, console log a message
.then(async client => {
// Get a reference to the database
await RestaurantsDAO.injectDB(client);
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
});
I found the solution to this problem.
I needed to add .js at the end of restaurants.DAO in the line import RestaurantsDAO from "../dao/restaurants.DAO";
of the file restaurants.controller.js
so the problem i'm having is, there is a directory in my public_html file named blog that is not related to my nextJs app, so basically after i deploy the app on host, everything works fine until i choose to check my blog part, for example the url below:
www.sth.com/blog
when i get there, i get the default next 404 page, the problem is, there is actual pages there, and i want my nodeJs server to ignore that exact route so when user goes to www.sth.com/blog, node app kind of ignore it and let it load the basic html pages.
i think it has something to do with my server.js file so here's the code in server.js
also i hosted the app on cpanel if that's important.
const { createServer } = require('http')
const next = require('next')
const isDevMode = process.env.NODE_ENV !== 'production'
const port = process.env.PORT ? process.env.PORT : 3000
const nextjsApp = next({ dev: isDevMode })
const nextjsRequestHandler = nextjsApp.getRequestHandler()
nextjsApp
.prepare()
.then(() => {
createServer((req, res) => {
const url = new URL(req.url, "http://w.w")
nextjsRequestHandler(req, res, url)
}).listen(port, (err) => {
if (err) throw err
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
thank you in advance.
In your server configuration you have to check if request is meant for next or not. If not - you need to respond with your page, instead of passing request further to next:
(req, res) => {
const url = new URL(req.url, "http://w.w");
if (/^\/blog\//.test(url.pathname)) {
// send response with blog
} else {
// pass everything to Next
nextjsRequestHandler(req, res, url);
}
};
Another option would be to split next and not next in two different parts and route requests to them through reverse proxy.
I am using Node.js and Socket.io for my web application.
I want to broadcast a file which can be somehow large (>15 Mb)to all the connected sockets and then use it in my clients. Is there a way to do this?
PS: if you can have a demo with babylon.js + socket.io that would be awesome
EDIT:
As requested, my server code:
import { createServer } from 'http';
import { createSocketServer} from "./socket";
import cookieParser from "cookie-parser";
import express from "express";
import morgan from "morgan";
import path from "path";
const port = 3000;
// Create a new express application instance
const app: express.Application = express();
app.use(cookieParser());
app.use(morgan('dev'));
const server = createServer(app);
// create a socket.io server
createSocketServer(server);
app.use('/', express.static(path.join(__dirname, 'public')));
server.listen(port,'0.0.0.0', function () {
console.log('Server is listening on port ' + port + ' !');
});
Babylon Assets Loading code:
this.assetsManager = new BABYLON.AssetsManager(this.scene);
this.assetsManager.addMeshTask('obj task', '',
'http://192.168.0.100:3000/babylon-files/dir1/', 'objectFile.obj');
this.assetsManager.addMeshTask('mtl task', '',
'http://192.168.0.100:3000/babylon-files/dir1/', 'materialFile.mtl');
this.assetsManager.addTextureTask('text1 task',
'http://192.168.0.100:3000/babylon-files/dir1/texture1.jpg');
this.assetsManager.addTextureTask('text2 task',
'http://192.168.0.100:3000/babylon-files/dir1/texture2.jpg');
this.assetsManager.onFinish = ((tasks) => {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}).bind(this);
this.assetsManager.load();
You sould store the *.obj & *.mtl files on the same server.
Note that your node & webserver code cant run on the same port.
You should use a reverse proxy or listen on diffrent ports.
This prevents cross site issues.
For example: create in your webserver a directory that is public accessable: http://example.com/assets/babylon-files
In your node code you can now trigger clients to load files from that path:
// socket.io logic above
// waiting for connetions, auth, etc...
// tell conencted clients what the should load from
// http://example.com/assets/babylon-files
socket.broadcast.emit('loadAsset', 'house.obj');
socket.broadcast.emit('loadAsset', 'car.obj');
socket.broadcast.emit('loadAsset', 'wall.obj');
The client should looks something like this:
// listen for socket.io events from server here
io.on("loadAsset", (filename) => {
// tell babylon to load assets
BABYLON.SceneLoader.Load("/assets/babylon-files", filename, engine, function (scene) {
// do something with the scene
});
// - or -
// tell babylon to append assets
BABYLON.SceneLoader.Append("/assets/babylon-files", filename, function (scene) {
// do something with the scene
});
});
On the same way you can send binary data to the clients:
fs.readFile("/path/to/obj<or>mtl/file", (err, buff) => {
if (err) {
res.status(500).end();
return;
}
socket.binary(true).emit("loadAsset", buff);
});
I deployed my react app to /public directory in strapi, everything work's correctly but, when I refreshed page, strapi override my react-router routs.
So... how can I redirect strapi to open public directory when i use specific routs?
e.g redirect /posts to public directory?
Strapi /public folder is here to server public assets and not to host your front end application. And it's not a good practice to do that.
I had to write that before answering your question.
Here is how static files are served.
https://github.com/strapi/strapi/blob/master/packages/strapi/lib/middlewares/public/index.js
It uses the public middleware.
So you will have to create your own middleware by following this documentation.
https://strapi.io/documentation/3.x.x/advanced/middlewares.html#custom-middlewares
So in ./middelwares/custom/index.js add the following code:
const path = require('path');
module.exports = strapi => {
return {
initialize: function(cb) {
strapi.router.route({
method: 'GET',
path: '/post',
handler: [
async (ctx, next) => {
ctx.url = path.basename(`${ctx.url}/index.html`);
await next();
},
strapi.koaMiddlewares.static(strapi.config.middleware.settings.public.path || strapi.config.paths.static, {
maxage: strapi.config.middleware.settings.public.maxAge,
defer: true
})
]
});
cb();
}
};
};
Then you will have to enable your middleware.
You will have to update the ./config/custom.json file with the following code:
{
"myCustomConfiguration": "This configuration is accessible through strapi.config.myCustomConfiguration",
"custom": {
"enabled": true
}
}
That's it!
I build my Strapi and CRA (create-react-app) at the build time, and says I want to mount my react app under /dashboard path.
and the file structure is:
yourapp/
└── apps/
├── frontend (react app)
└── backend (strapi)
add a homepage property in frontend's package.json if you are using CRA, this will tell Webpack to add a prefix to your static assets, e.g
// in frontend's package.json
{
...
"homepage": "/dashboard"
}
move your built react app to a subfolder /dashboard of backend project, by modifying the yarn build script, I'm doing like this, be careful before copy/paste my code, there is a rm -rf cmd.
// package.json in root path
{
...
"scripts": {
"build": "yarn build:front && yarn build:back && rm -rf apps/backend/dashboard && mv apps/frontend/build apps/backend/dashboard",
...
}
}
add a custom middleware in Strapi to be your "view router", that will handle all requests to /dashboard/* to serve the react app assets under apps/backend/dashboard
create a file under <strapiapp>/middlewares/viewRouter/index.js
const path = require("path");
const koaStatic = require("koa-static");
const fs = require("fs");
module.exports = strapi => {
return {
async initialize() {
const { maxAge } = strapi.config.middleware.settings.public;
const basename = "/dashboard";
const dashboardDir = path.resolve(strapi.dir, "dashboard");
// Serve dashboard assets.
strapi.router.get(
`${basename}/*`,
async (ctx, next) => {
ctx.url = ctx.url.replace(/^\/dashboard/, "");
if (!ctx.url) ctx.url = basename;
await next();
},
koaStatic(dashboardDir, {
index: "index.html",
maxage: maxAge,
defer: false
})
);
const validRoutes = [
"/dashboard",
"/subpath1",
"/subpath2"
];
// server dashboard assets and all routers
strapi.router.get(`${basename}*`, ctx => {
const routePath = ctx.url.split("?")[0];
let fileName = ctx.url;
if (validRoutes.includes(routePath)) fileName = "index.html";
ctx.type = "html";
ctx.body = fs.createReadStream(
path.join(dashboardDir + `/${fileName}`)
);
});
}
};
};
enable the custom middleware in <strapiapp>/config/custom.json
{
"myCustomConfiguration": "This configuration is accessible through strapi.config.myCustomConfiguration",
"viewRouter": { // use the middleware name
"enabled": true
}
}
and visit http://localhost:1337/dashboard you'll see the react page.
The actual answer for strapi#4.3.2 is here
I faced the same problem. All you need to do are these two steps:
Create a custom middleware. I named it spa.js and put it in the folder /src/middlewares/spa.js (I am not sure about naming). I didn't have this folder before. I created it by myself. The file spa.js should contain a code like this:
module.exports = () => {
return async (ctx, next) => {
const url = ctx.url;
// Here you should process your redirects on index.html always,
// except URLs like `admin`, `content-manager`, `i18n`, `static`, `api`, `graphql`, `uploads` etc.
// These settings should be completely the same as in your Nginx config
// for SPA (Single Page Application). Usually, we set up `Location` in Nginx
if (!url.match(/\/admin|\/content-manager\/|\/i18n\/|\/static|\/graphql|\/uploads\/|\.json/)) {
ctx.url = '/';
}
// let strapi go further
await next();
};
};
Register your new middleware in /config/middlewares.js. I had this file and it contained only strings ('strapi::errors', 'strapi::security', 'strapi::cors',). I added an object with a resolve field with a relative path to my new middleware spa.js. There are different options for how you can set up this path, you can also use just a name.
My /config/middleware.js looks like this now:
module.exports = [
{
resolve: './src/middlewares/spa.js',
},
'strapi::errors',
'strapi::security',
'strapi::cors',
'strapi::poweredBy',
'strapi::logger',
'strapi::query',
'strapi::body',
'strapi::session',
'strapi::favicon',
'strapi::public',
];
Relaunch your server by strapi start. It should work. Routes after reloading any page that was reached by React-router before will work as they should work in SPA. All routes go to / (index.html)
UPD: Please, be careful. I see many routes for "internal use". For example, /content-manager/ and /i18n/. My admin panel didn't work unless I add the content-manager route. I suppose there may be many new routes in the future and we should mark in our middleware only allowed routes that are redirected and don't change behavior for other routes.