How to use server side rendering in order to add og meta-tags in react? - reactjs

What I need is to change the content of og:title in a dynamic way into index.blade.php by using express server. As you know, the only way to change the content of the tags is by using server side rendering because facebook cannot read og tags if they added by react helmet component. So, here what I did:
Into index.balde.php I added these lines:
<meta property="og:title" content="$OG_TITLE">
<meta property="og:description" content="$OG_DESCRIPTION">
Then I created server.js file in order to change the content of the tags dynamically through the server:
var express = require('express');
var app = express();
const path = require('path');
const fs = require('fs')
app.get('/profile', function(request, response) {
console.log("Entered the logic");
const filePath = path.resolve(__dirname, '../views', 'index.blade.php');
// read in the index.html file
fs.readFile(filePath, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
let result = null;
// replace the special strings with server generated strings
data = data.replace(/\$OG_TITLE/g, 'PROFILE TITLE');
result = data.replace(/\$OG_DESCRIPTION/g, "PROFILE DESCRIPTIONS");
//result = data.replace(/\$OG_IMAGE/g, 'https://i.imgur.com/V7irMl8.png');
response.send(result);
});
});
app.listen(5000, function(){
console.log('listening on *:' + 5000);
});
As a result, when I open www.testurl.com:5000/profile The content of the tags have been changed successfully, but my problem is how to make the content of the tags changed without need to add the port number in url, since the requested link should be without the port number. The user does not need to add port number into the URL.
Is there anyway in react/express to let the server know that when user call www.testurl.com/profile all event listener in the server on port 500 should be called (on other way when access the url without port number app.get('/profile',...) should be called)? Or there is a way to call the url with port number into the basic component to make the event each time user open any page inside the app?
I am really need a help, since i spent a lot of time in order to find a solution. Thanks.

Related

Redirect an array of URLs in Express JS

I want to assign an array of URLs from a React app (using Express server side rendering) and assign 301 redirects to each URL.
The slug/id format is changing and I need a way to ensure old links already indexed by Google our saved in the wild have a way to find their way to the new URL format.
This function will be a one-time use, as new URLs will follow the new slug/id format, but the redirects need to remain so that they can continue their redirects.
I know how to assign a single redirect to one URL using res.redirect but not an entire array.
(This is my first SO post. Tried to explain this as well as I can but apologies if there are gaps).
You can use a loop to create router.get functions, so something like:
const urls = ['/url1', '/url2', '/url3'];
urls.forEach(url => {
router.get(url, (req, res, next) => {
res.redirect(301, '/redirected-route');
})
)};
If they all need to go to different URLs rather than all to the same one, then you can define the redirect array as an array of objects with oldUrl, and newUrl properties to create a more dynamic redirect.
const urls = [
{ oldUrl: '/url1', newUrl: '/new-url1' },
{ oldUrl: '/url2', newUrl: '/new-url2' },
{ oldUrl: '/url3', newUrl: '/new-url3' }
];
urls.forEach(url => {
router.get(url.oldUrl, (req, res, next) => {
res.redirect(301, url.newUrl);
})
)};

How to S3 + Cloudfrond + 2 SPA react apps in subfolders?

I want to serve 2 different react apps in subfolders using S3 + Cloudfront
myapp.com/app-one/ <-- here is index.html
myapp.com/app-two/ <-- here is another index.html
I know how to configure create-react-app and react router to handle this.
https://create-react-app.dev/docs/deployment/#building-for-relative-paths
The problem is with configuring S3 + Cloudfront to handle redirects.
When you enter url in browser:
myapp.com/app-one/some-route - it should redirect to index.html of app-one
myapp.com/app-two/some-route - it should redirect to index.html of app-two
Unfortunately S3 lets you define only one fallback file.html (404) for Static website hosting
I also don't want to use hashrouter: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
I was struggling with the same problem for an angular application, and found some solutions, but the one that I found most useful is by using a Lambda#Edge function, this allows you to keep your static files inside the S3 bucket without opening to static hosting.
The only Lambda#Edge configuration that worked for me is the one from this answer. Here is the code:
var path = require('path');
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/second-app.*/, 'second-app/index.html');
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
What it basically does is to match the uri of your subfolder and redirect all request to the correct index.html file. If you have multiple sub folders you could simply add some conditions:
var path = require('path');
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// Extract the URI from the request
var olduri = request.uri;
var newuri = olduri
// Match any '/' that occurs at the end of a URI. Replace it with a default index
if(olduri.match(/first-sub-app.*/)){
newuri = olduri.replace(/first-sub-app.*/, 'first-sub-app/index.html');
} else if(olduri.match(/second-sub-app.*/)){
newuri = olduri.replace(/second-sub-app.*/, 'second-sub-app/index.html');
}
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
Check the original answer for further instructions on the setup and more details on how it works but it basically ignores any request with extensions and only redirects if it matches a specific subdirectory.

Displaying a var using AngularJS/HTML on the screen

I'm using MEAN Stack to construct my web application. (Mongo, Express, Angular, NodeJS) I have a server.js file, html file and a css file. My server.js generates a var number which I want to get rendered on the frontend, however I'm having some trouble doing that. Let me explain, on my html there a button I created, whenever the user clicks on that button, I want this specific var to be shown the screen, but it doesn't work.
Here is the code for the creation of the button:
Some Text
Below is the angularjs code for where I use the exact rendering to be occurred:
The Amount:
{{links.amountLinks}}
test this
My server.js code:(Please note I'm using jsdom module)
var jsdom = require("jsdom");
jsdom.env(
url,
["http://code.jquery.com/jquery.js"],
function (err, window) {
// console.log("there have been", window.$("a").length, "io.js releases!");
// alert("there have been", window.$("a").length, "io.js releases!");
console.log(window.$("a").length);
amountLinks = window.$("a").length;
json.amountLinks = amountLinks;
data = amountLinks;
});
Does anyone know how I can fix this?
The code can be fixed as follow:
jsdom.env(
url,
["http://code.jquery.com/jquery.js"],
function (err, window) {
// console.log("there have been", window.$("a").length, "io.js releases!");
// alert("there have been", window.$("a").length, "io.js releases!");
console.log(window.$("a").length);
amountLinks = window.$("a").length;
json.amountLinks = amountLinks;
data = amountLinks;
res.send(JSON.stringify(json, null, 4))
});
}

Is there a way to dump a thousand images somewhere and extract them using REST Api?

Here is the thing:-
I have over a thousand images saved locally in my mac. I have a landing page that mocks an ecommerce deal site. It would be tedious to have to manually type in the src url in the img tag for a thousand pictures. Thus, i thought i could somehow have this images dumped in a cloud storage or something and use REST api get method to extract these images in a response.data. Then assign it to a $scope variable and use ng-repeat to bind the images in my landing page view. Is this possible? If not, what are the alternatives? SQL database?
Appreciate your help. P.S. I am totally a beginner at web development.
Install node.js. It's Javascript for a server which should make it pretty easy since you already know Javascript.
On a Mac, you can install node like this:
brew install node
Use this node.js code (credit to codepedia.com, tweaked a little by me):
//include http, fs and url module
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
imageDir = './images/';
//create http server listening on port 3333
http.createServer(function (req, res) {
//use the url to parse the requested url and get the image name
var query = url.parse(req.url,true).query;
pic = query.image;
if (typeof pic === 'undefined') {
getImages(imageDir, function (err, files) {
var imageList = JSON.stringify(files);
res.writeHead(200, {'Content-type':'application/json'});
res.end(imageList);
});
} else {
//read the image using fs and send the image content back in the response
fs.readFile(imageDir + pic, function (err, content) {
if (err) {
res.writeHead(400, {'Content-type':'text/html'})
console.log(err);
res.end("No such image");
} else {
//specify the content type in the response will be an image
res.writeHead(200,{'Content-type':'image/jpg'});
res.end(content, "binary");
}
});
}
}).listen(3333);
console.log("Server running at http://localhost:3333/");
//get the list of jpg files in the image dir
function getImages(imageDir, callback) {
var fileType = '.jpg',
files = [], i;
fs.readdir(imageDir, function (err, list) {
for(i=0; i<list.length; i++) {
if(path.extname(list[i]) === fileType) {
files.push(list[i]); //store the file name into the array files
}
}
callback(err, files);
});
}
Run this from the command line to start you new image server (assuming you named the file "server.js"):
node server.js
You should see this text appear on the command line:
Server running at http://localhost:3333/
You can quickly test it by going to this address in your browser and you should see a JSON object showing you an array of all the filenames in the "./images" directory. By the way, this program assumes you're putting the images folder in the same directory as "server.js". You can put the images directory anywhere and just change the path of the variable "imageDir".
Now you can load the list of files from Angular using this code in your controller:
$http.get("http://localhost:3333", function(data) {
$scope.images = data;
});
In your view, you can now use an ng-repeat like this to display all the images:
<div ng-repeat="image in images" style="padding: 8px">
<img src="http://localhost:3333/image={{ image }}">
</div>
Note: this will work if you run it locally on your Mac or if you upload all the images to a server on which you can use Node.js.

Angular : show image from REST Service

After reseaches and tests, I still can't show an image form ReST API on my Angular App. I have images available on my ReST web service, why do I use a ReST service? Because in order to access you need to be authenticated (I use oAuth 2 protocol). When I use POSTMan (ReST client very usefull) everything works great, the image is displayed without doing nothing. But when I try to display it with Angular after a $http it doesn't work.
Here are the headers received form the service :
Content-Length → 51756
Content-Type → image/jpeg; charset=binary
Server → Apache/2.4.9 (Win64) PHP/5.5.12
X-Powered-By → PHP/5.5.12
Here is my Angular code :
var data64 = $base64.encode(unescape(encodeURIComponent(data)));
scope.src = 'data:image/jpeg;charset=binary;base64,' + data64;
and my HTML :
<img ng-src="{{src}}" border="0" />
For information I use angular-base64 (https://github.com/ninjatronic/angular-base64) for the encodage. Without "unescape" and "encodeURIComponent" I have an error, I've tried to remove white spaces but it still doesn't work.
Thank you :)
Seems that this will not work since you tell the browser that the image data is base64 encoded, but you also transformed it with unescape and encodeURIComponent.
Why don't you fetch your image data into a binary data structure (requires a modern browser), instead of into a string:
$http.get(req, {responseType: "arraybuffer"}).
success(function(data) {
$scope.src = 'data:image/jpeg;base64,' + _arrayBufferToBase64(data);
});
_arrayBufferToBase64 is defined here.
A different approach would be to install a request interceptor, recognize the image url and add the oauth headers for this case.
I tryed this way in angular 8+ and works fine:
imageToShow: any;
createImageFromBlob(image: Blob) {
let reader = new FileReader();
reader.addEventListener("load", () => {
this.imageToShow = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
and also call it like this:
getImageFromService() {
this.api.getImage(key).subscribe(data => {
this.createImageFromBlob(data);
}, error => {
console.log(error);
});
}

Resources