How to serve two AngularJS apps with one Express server? - angularjs

I've searched all around and i've found a few ways of how to do this, but none seem to specifically fit my needs and i can't get them to work. My latest attempt is below, using express-subdomain with express and trying to server two separate AngularJS apps based on the incoming sub domain. This code currently seems to serve the correct app, but none of the Angular modules are included from what i can see. The browser console has many many errors of 'Uncaught SyntaxError: Unexpected token <'. Which from previous experience i believe means AngularJS was not loaded correctly. Keep in mind, i have no issue running only one AngularJS app, it is only when i try to bring in sub-domain and serve that second static app.
I've already tried vhost with similar failed results.
server.js
app.use(subdomain('app1',express.static(__dirname + '/public/app1')));
app.use(subdomain('app2',express.static(__dirname + '/public/app2')));
require('./app/routes')(app); // configure our routes
app.listen(port);
routes.js
app.get('*', function(req, res) {
var firstIndex = req.get('host').indexOf('.');
var subdomain = req.get('host').substr(0,firstIndex).toLowerCase();
if (subdomain === 'app1'){
res.sendFile(root + '/public/app1/views/index.html');
}else if (subdomain === 'app2'){
res.sendFile(root + '/public/app2/views/index.html');
}else{
res.sendFile(root + '/public/app1/views/notfound.html');
}
});

if (subdomain === 'app1'){
res.sendFile(root + '/public/app1/views/index.html');
This seems wrong.
This will send index.html to any request, i.e. request for js file.
Should be i.e.:
if (subdomain === 'app1' && req.url.indexOf('.') === -1){
} else if (subdomain === 'app2' && req.url.indexOf('.') === -1){
} else { /* return static asset */ }
...

If you create a general json whit diferent instance whit the diferents aplicacitions and create all the call, you can emulate this. This is the example:
const routes = {
routeApp1: {
appTest1:{
url: '/appTest1/appTest1',
file: './mock/appTest1.json',
},
appTest2:{
url: '/appTest1/appTest2',
file: './mock/appTest2.json',
},
}
routeApp2: {
appTest1:{
url: '/appTest2/appTest1',
file: './mock/appTest1.json',
},
appTest2:{
url: '/appTest2/appTest2',
file: './mock/appTest2.json',
},
}
};
app.get(routes.routeApp1.appTest1.url, (req, res) => {
res.send(JSON.stringify(require(routes.routeApp1.appTest1.file)));
});
app.get(routes.routeApp2.appTest2.url, (req, res) => {
res.send(JSON.stringify(require(routes.routeApp2.appTest2.file)));
});
app.listen(3000, () => {
console.log(`Mocks app listening on port ${port}!`);
});

Related

Fetching 301 Redirect Data In server.js (Express/NextJS)

I'm working on a project using express and next js and I've found a great example of how to setup an array of data for your redirects in your server.js file. However, if it is possible I would like to build a plugin within WordPress that will allow a user to submit data for redirects so that it could be managed by someone without technical knowledge. My question is, is it possible to fetch data within my server.js file to replace the data in this example?
const express = require('express')
const next = require('next')
const { join } = require('path')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
//This is the data I want to fetch through the WP Rest API
const redirects = [
{ from: '/old-link-1', to: '/new-link-1' },
{ from: '/old-link-2', to: 'https://externalsite.com/new-link-2' },
]
app.prepare().then(() => {
const server = express()
redirects.forEach(({ from, to, type = 301, method = 'get' }) => {
server[method](from, (req, res) => {
res.redirect(type, to)
})
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, err => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
Yes, I believe it's possible do do something like that.
This library would allow you to make an API request within express: https://github.com/request/request
Executed like so:
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Print the google web page.
}
})
The next step would be to create an endpoint in wordpress with all of the 301's that you want to create:
function my_custom_endpoint(){
return 'Hey look, its some data';
}
// Register the rest route here.
add_action( 'rest_api_init', function () {
register_rest_route( 'yournamespace/v1', 'my_custom_endpoint',array(
'methods' => 'GET',
'callback' => 'my_custom_endpoint'
));
});
Good luck, and happy coding!
So, just incase anyone else stumbles upon the problem of programmatically adding redirects that originate on a WordPress install this is how it can be done. My tech stack is React, Next.js with an Express server pulling in data from a WordPress install that is living elsewhere in the webs.
WordPress:
1) Create a blank WordPress plugin (Google is your friend)
2) Create an activation hook within your plugin to create a database(Again, Google with 'to_url' and 'from_url' for each entry.
3) Register a Rest Route within plugin (Like described above with Tanner's answer)
This Rest Route should be pulling your info from the database and returning it as an array in this format:
[
{ 'from': '/about', 'to': '/about-us' },
{ 'from': '/test3', 'to': '/banks/testing-page' },
]
4) Create a plugin admin page with a form that allows the users to add entries to this database. As your database grows your response from the rest api will grow and your redirects will seamlessly be included in your project.
5) In your React server.js you're going to need the following setup
const request = require("request");
let redirects;
request('https://yourwebsite.com/wp-json/YOUR-ROUTE/v2/redirects', function (error, response, body) {
if (!error && response.statusCode == 200) {
redirects = JSON.parse(body);
}
})
redirects.forEach(({ from, to, type = 301, method = 'get' }) => {
server[method](from, (req, res) => {
res.redirect(type, to)
})
});
Caveats: Make sure when you're manipulating data with forms in php you're taking proper precautions to sanitize and escape everything.

Requests API in React-boilerplate

I am using the boilerplate on https://github.com/react-boilerplate/react-boilerplate . The problem is that when I'm hitting API's It's returning error 404. I'm not able to get from where it is setting up the host (which is always going localhost).
no CORS error is also coming up on browser.
Prior to this I was working on create-react-app, there I simple put a "proxy" property in package.json and everything worked fine.
Today I set up this boilerplate for the first time and I would say it's a lil confusing _:)
You can specify API base url like this:
const API = process.env.NODE_ENV !== 'production' ? 'http://google.com' : 'http://localhost:5000'
So in development it will always point to localhost and in production it will point to other your prod server.
For people still searching,
all you need is to create something like this in server/index.js
app.get('/api/user', (req, res, next) => {
let parsedBody = JSON.parse(req.body)
res.send({ express: 'Hello From Express.' });
});
on client side request to /api/user
axios.get(`/api/user`)
.then(function (response) {
console.log("/api/user response", response);
})
.catch(function (error) {
console.log(error);
});
cheers

Ionic http get request (with Ionic) returns html data

I am using Ionic and making http request like this:
$http.get('//GLOBAL.IP/?username=' + username + '&content_type=json')
.then(function (result) {
$scope.days = [];
alert(JSON.stringify(result));
for (first in result.data.weeks) break;
var currentWeek = result.data.weeks[first];
var time = currentWeek.overall.as_time;
$scope.week = currentWeek;
for (day in currentWeek.days.sort().reverse()) {
if (day < 2) {
continue;
}
var currentDay = currentWeek.days[day];
$scope.days.push(currentDay);
if (currentDay.present) {
console.log(parseInt(currentDay.work_time_balance) + parseInt(currentWeek.overall.seconds));
}
}
}, function (result) {
alert(JSON.stringify(result));
$ionicPopup.alert({
title: 'Error',
template: 'Connection failed, try again later!'
});
})
.finally(function () {
// Stop the ion-refresher from spinning
$scope.$broadcast('scroll.refreshComplete');
});
While I am opening app in browser with command ionic serve, everything seems working okay, I've already fixed CORS and other stuff.
But when In mobile app, I am getting this (sorry for not providing plaintext)
UPDATE
$http.get('//GLOBAL.IP/?username=' + username + '&content_type=json')
.then(function (result) {
alert('Good');
}, function (result) {
alert('Bad');
})
This code returns me GOOD, but result.data is still with script tags.
When you are testing this app in working mode what is localhost, Its a local server running on same machine, right.
When you install or run app in your mobile device where it is pointing to? Do you know that, If code is okay then replacing localhost with IP of your machine should resolve this issue.
Many script tags with addRow in ionic http request response, was because of // in the beginning of uri, protocol was selecting successfully in browser, but not in compiled app.

NavBar address loading angular template but not root shell

I am using Node.JS with Express, Angular.JS and the node module connect-roles for ACL. I want to allow a user with user.status of "Platinum" to access "Platinum" but not "Gold" and vice versa.
I have the ACL part working, if I enter /Platinum into the navigation bar I can't access /Gold, but when I try to access /Platinum I only get the template but not the root shell, so what comes up is this:
You made it!
You have the {{status}} status!
If I click on a link in angular to /Platinum, everything works as it should. If I enter any neutral address in the navigation bar, everything works as it should.
This should be an easy fix, but I've not figured it out.
Here is the code that sets up authorizations, I'm pretty sure everything here is okay.
ConnectRoles = require('connect-roles')
var user = new ConnectRoles({
failureHandler: function(req, res, action){
var accept = req.headers.accept || '';
res.status(403);
if(accept.indexOf('html')) {
res.render('access-denied', {action: action});
} else {
res.send('Access Denied - You don\'t have permission to: ' + action);
}
}
});
var app = express();
app.use(user.middleware());
// Setting up user authorizations,
// i.e. if req.user.status = "Platinum", they are given Platinum status
user.use('Platinum', function(req) {
if (req.user.status == 'Platinum') {
return true;
}
});
user.use('Gold', function(req) {
if (req.user.status == 'Gold') {
return true;
}
});
user.use('Admin', function(req) {
if (req.user.status == 'Admin') {
return true;
}
});
That sets up authorizations, now the problem lies below with the routing.
app.post('/login', passport.authenticate('local',
{ successRedirect: '/', failureRedirect: '/login' }));
app.get('/Platinum', user.is('Platinum'), function(req, res) {
//Obviously the code below is wrong.
res.render('templates/support/Platinum');
});
app.get('/Gold', user.is('Gold'), function(req, res) {
res.render('templates/support/Gold');
});
The way you are configuring your routes on server side (using express) is not correct. For a single page app like AngularJS, you need to do all of the routing for pages on the client (i.e. in Angular). The server still defines routes for API requests (e.g. getting and posting data) and static resources (index.html, partial HTML files, images, javascript, fonts, etc), though.
Thus the following code is wrong in your server side JS:
app.get('/Platinum', user.is('Platinum'), function(req, res) {
//Obviously the code below is wrong.
res.render('templates/support/Platinum');
});
app.get('/Gold', user.is('Gold'), function(req, res) {
res.render('templates/support/Gold');
});
Just remove those lines.
Instead, you need to define the routes that the server will handle, such as your /login post one first, and how to get static files (I suggest prefixing them all with /pub in the URL). Then you need to do something like the technique in this answer to return your index.html page if no routes are matched.
That way, when a user types http://localhost:port/Gold, express will see there is no route defined for /Gold, so it will return index.html, which will load AngularJS, run your Angular app, which will then look at the URL and see if that matches any of the routes your AngularJS app has configured, and if so, fetch the partial for that page and insert it into your ng-view (if using the core router).

I am using MEAN.IO stack, why can't I hit my data-access layer using require?

So, I am using mean.io and for some reason, my routes.js never hits my 'index.all' method, or the 'exports.all' function, even though I require the functions from the server-side controller. Also, my routing is done using angular-ui-router. Does anybody know how to call a backend method from routing in MEAN.IO? I keep using:
'use strict';
module.exports = function(System, app, auth, database) {
// Home route
var index = require('../controllers/index');
app.route('/test').get(index.all);
app.route('/')
.get(index.render);
};
I would like to hit 'index.all' but even if I navigate to /test, it still gets
index.render. Does anybody know why?
Here is the controllers file:
'use strict';
var mean = require('meanio');
var mongoose = require('mongoose');
var Composition = mongoose.model('Composition');
exports.render = function(req, res) {
console.log(req.user);
var modules = [];
// Preparing angular modules list with dependencies
for (var name in mean.modules) {
modules.push({
name: name,
module: 'mean.' + name,
angularDependencies: mean.modules[name].angularDependencies
});
}
function isAdmin() {
return req.user && req.user.roles.indexOf('admin') !== -1;
}
// Send some basic starting info to the view
res.render('index', {
user: req.user ? {
name: req.user.name,
_id: req.user._id,
username: req.user.username,
roles: req.user.roles
} : {},
modules: modules,
isAdmin: isAdmin,
adminEnabled: isAdmin() && mean.moduleEnabled('mean-admin')
});
};
exports.all = function(req, res) {
console.log(req.user);
Composition.find({user: req.user}, 'title description').sort('-created').populate('user', 'name username').exec(function(err, compositions) {
if (err) {
return res.jsonp(500, {
error: 'Cannot list the compositions'
});
}
res.jsonp(compositions);
});
};
Is this a front-end or backend problem? Thanks for any advice that might be helpful.
You are navigating. So are you hitting the link in the browser url? Then you should try localhost:3000/test instead of localhost:3000/#!/test.
The urls of the form localhost:3000:/#!/<something> are angular routes. Look up angular routing and views. It is better to use angular views than server side rendering. Do angular routing for test and add a view corresponding to it. Fetch the dynamic data in the view using the regular $http.get calls.
Check this tutorial for routing and adding views in angular

Resources