how to handle root path request with node.js on firebase hosting? - firebase

I'm developing web system using firebase hosting + functions.
Inspite of specifying rewrite rules on firebase.json,
a part of routing doesn't work.
root/
 ├ functions/
 │ ├index.js
 │ ├routes/
 │ │ └ index.js
 │ └views/
 │ ├ index.jade
 │ └ sub.jade
 └ public/
└index.html // this is created by default. I don't want to use this.
This is my firebase.json
"rewrites": [{
"source": "**",
"function": "app"
}],
And this is node.js code.
router.get('/', function(req, res, next) {
res.render('index');
});
router.get('/subdir', function(req, res, next) {
res.render('sub');
});
result
https://myurl/ -> public/index.html is shown.(not handled on node.js)
https://myurl/ -> handled on node.js
Do you know how to handle root path request with node.js on firebase hosting.

See https://firebase.google.com/docs/hosting/full-config#hosting_priority_order .
Priority order of Hosting responses
The different Firebase Hosting configuration options described on this page can sometimes overlap. If there is a conflict, Hosting determines its response using the following priority order:
Reserved namespaces that begin with a /__/* path segment
Configured redirects
Exact-match static content
Configured rewrites
Custom 404 page
Default 404 page
Exact-match static content is high priority order than Configured rewrites.
So https://myurl/ get static content /index.html.
Can you try remove public/index.html?
I tried. I can rewrite.
See https://github.com/zkohi/firebase-hosting-using-functions-samples .
And you should check https://firebase.google.com/docs/hosting/functions .

Make a dummy folder that is empty. eg.: dummyApiFolder
Make a very simple cloud function. eg.
exports.bla = functions.https.onRequest(async (req, res) => {
res.send("server bla");
});
Use the following hosting rules
"hosting": {
"target": "api",
"public": "dummyApiFolder",
"rewrites": [
{
"source": "**",
"function": "bla"
}
]
}
3.1. target is only needed if you have set this up in your .firebaserc file
3.2. "public": "dummyApiFolder", is needed. Otherwise the CLI won't allow the deployment
Now all requests should get forwarded to bla.
The reason I'm advising to start with a simple cloud function is, express can be setup incorrectly. This can also give you an error, but you end up not knowing if it's a firebase issue or a code issue!

It's simple! Just rename the index.html file in the public directory into some other name. Also, just in case: In the firebase.json file, Change the public property's value
from public to .

Related

Firebase Functions: route every URL through function and serve from "hosting"

I'm trying to get every url in a subdomain to be routed through a firebase function. This is my configuration:
The functions file:
const functions = require('firebase-functions');
exports.handleWebRequest = functions
.region('europe-west1')
.https
.onRequest((req, res) => {
res.send('Currently down.');
});
My firebase.json:
{
"hosting": {
"public": "dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "handleWebRequest"
}
]
},
"functions": {
"source": "firebase-functions",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
When I deploy the hosting and functions, and go to the URL in my browser, I expect to see "Currently down" for every route I open. However, every route except the root route shows the "Currently down" message, and the root route shows the index.html that I deployed.
So, in a nutshell:
/ shows my index.html (which I don't want)
/whatever shows "Currently down." (which is what I want)
The reason I'm trying to route everything through the function, is because I want to shield the website with HTTP Basic Auth, which is "not natively supported" by Firebase.
This is most probably because you still have the index.html file under the public folder.
As explained in the doc:
Note: The static files in the public directory take precedence over
the rewrites, so any static files will be served alongside the Cloud
Functions endpoints.
If you remove the index.html file it should work the way you expect.
Your function is deployed to the "europe-west1" region, but Firebase Hosting currently only supports the default "us-central1". This is stated in the documentation:
Important: Firebase Hosting supports Cloud Functions in us-central1 only.

firebase hosting blocking script due to CORS issue

I am using firebase hosting to host few scripts and trying to access them from another site. it naturally gets blocked due to CORS issues. based on my research on other forum threads etc i modified the firebase.json as below
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"headers": [ {
"source" : "**",
"headers" : [ {
"key" : "Access-Control-Allow-Origin",
"value" : "*"
} ]
}]
}
}
which essentially allow any url to access the resources hosted here. however, on trying to run my site i still see below
Access to XMLHttpRequest at 'https://oracle-bot-sdk.firebaseapp.com//loader.json'
from origin 'https://insurance-bot.moblize.it' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
what else is needed?
In addition to your firebase.json changes for cors, your firebase functions http / https function needs to also include the cors plugin.
Example
const cors = require('cors')({origin: true});
const functions = require('firebase-functions');
const app = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// Your app stuff here
// Send Response
res.status(200).send(<response data>);
});
});
Express App Example
import express from "express";
const cors = require('cors')({origin: true});
const app = express();
app.get('**', (req, res) => {
cors(req, res, () => {
// Your App Here
// Send response
res.status(200).send(<response data>);
});
});
More documentation Serve Dynamic Content with Cloud Functions - Create an HTTP function to your Hosting site (Cors is not mentioned in the documentation btw)
Is the site (https://insurance-bot.moblize.it/) that is calling to https://oracle-bot-sdk.firebaseapp.com a Firebase hosted app?
I only ask because with version 4.2+ of Firebase Tools allows you to setup Multisite hosting using the same Firebase Project. I am not sure if that would help your situation out at all. Just wanted to mention it.
In the error message:
insurance-bot.moblize.it/:1 Failed to load https://oracle-bot-sdk.firebaseapp.com//loader.json: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://insurance-bot.moblize.it' is therefore not allowed access.
I noticed an extra '/' in https://oracle-bot-sdk.firebaseapp.com//loader.json. I doubt that is the issue, but wanted to mention it.
There is something that you could try. Similar to the answers above but a little different:
"headers": [
{
"source": "*",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]
Also I would read some of the info here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Allow-Origin If you have not already.
I hope I was able to help in some way. Let me know.
My guess that you've mixed up firebase hosting and firebase cloud functions. Firebase hosting is made for hosting static websites and web apps. As you try to access from your website that is hosted on different domain your configuration for hosting is not applied. You mentioned that you host some scripts and it sounds like cloud functions. And good old CORS headers can help to your cloud functions like:
exports.corsEnabledFunction = (req, res) => {
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET");
res.set("Access-Control-Allow-Headers", "Content-Type");
res.set("Access-Control-Max-Age", "3600");
// Continue with function code
...
}
More info: https://cloud.google.com/functions/docs/writing/http#handling_cors_requests
Make sure you have the Blaze or Flame plan, I think Spark plan blocks external access, maybe for the same reason as it does with cloud functions
Cloud Functions for Firebase - Billing account not configured
Go to the Google Cloud Console: https://console.cloud.google.com/functions/
Click the checkbox next to the function on which you want to grant access.
Click Show Info Panel in the top right corner to show the Permissions tab.
Click Add member.
In the New members field, type allUsers.
Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
Click Save.
taken from: https://github.com/firebase/firebase-functions/issues/645#issuecomment-605835353
This was the best solution for me as posted above
Go to the Google Cloud Console: https://console.cloud.google.com/functions/
Click the checkbox next to the function on which you want to grant access.
Click Show Info Panel in the top right corner to show the Permissions tab.
Click Add member.
In the New members field, type allUsers.
Select the role Cloud Functions > Cloud Functions Invoker from the Select a role drop-down menu.
Click Save.
taken from: https://github.com/firebase/firebase-functions/issues/645#issuecomment-605835353
Try pasting this as it's directly from the documentation, Customize Hosting Behavior:
"hosting": {
// Add the "headers" section within "hosting".
"headers": [ {
"source" : "**/*.#(eot|otf|ttf|ttc|woff|font.css)",
"headers" : [ {
"key" : "Access-Control-Allow-Origin",
"value" : "*"
} ]
}
}
Firebase hosting CORS doesn't work WITH custom domain.
However, CORS API works with https://yyyyyyy.web.app/ or firebaseapp.com domain

Best setup/workflow for testing and deploying an application using Google Firebase Hosting and Functions

I built a small web application (www.suntax.de) with vuejs and hosted on Google Firebase. I use the Firebase Hosting, Database and Functions. I use Functions to do some calculation on the backend.
Everything is running now, but I had some trouble getting there and some things I would like to improve regarding my workflow.
Thus, I would like to explain you what I did and where I struggle now.
First I set up the vuejs applicationd deployed it to firebase. I added my custom domain and everything was doing fine.
Later I implemented the cloud function and want to make a call from my vuejs app.
After firebase deploy, I can call this function in the browser and it just works fine:
https://us-central1-suntax-6d0ea.cloudfunctions.net/calcEEGcompensation?year=2013&month=1&size=10
Now I thought, that I just call the URL of the function from my vuejs app. But then I got the following error message:
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
I was then reading that I had to add a rewritesection in the firebase.json:
Now my firebase.json looks like this:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
Now I was able to call my firebase function with the following URL:
https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10
After integrating the above URL in my vuejs application, the application is running fine after deployment to firebase server.
As I want to keep improving the application, I would like to test everything locally before deploying.
I know that I can run firebase hosting and functions locally by:
firebase serve --only functions,hosting
However, now my application has the hard coded call to my function https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10 and this again leads to the error [Error] Origin * is not allowed by Access-Control-Allow-Origin.
But also changing the URL to the local function
http://localhost:5001/suntax-6d0ea/us-central1/calcEEGcompensation?year=2013&month=1&size=10
leads to the error message
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
Some further reading brought me to the solution with cors. So I changed my function to:
const functions = require('firebase-functions');
const cors = require('cors')({origin: true});
exports.calcEEGcompensation = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const yearOfCommissioning = req.query.year;
const monthOfCommissioning = req.query.month;
const pvsystemsize = req.query.size;
...
res.send("hello");
});
});
This helped and everything works now:
- The deployed application is still running fine.
- I can run the application locally while calling the local function as well as the deployed function. I just have to change the URL of the function.
But this is now my question:
Can I solve this issue in a better way? If I test the vuejs application and the function locally, I have to change the function URL before deployment. And then I have to change it back while testing locally.
I was not able to test my application and function locally without cors.
My ideal solution would be to have a setup, that can be fully tested locally and which can be easily deployed with firebase deploy without any changes of URLs. Is this possible?
Thanks and best regards,
Christoph
I found the solution which is pretty simple and does exactly what I want. I have no idea why I did not figure it out before.
Here is what I did:
I just call the relative URL from my firebase hosting:
calcEEGcompensation?year=2013&month=1&size=10
and everything works fine if the rewrites are properly set in firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
After setting up everything like this, I can just execute firebase serve --only functions,hosting and I can test everything locally.
After executing firebase deploy, everything runs smoothly on the server.
I do not need cors.
Thanks #wonsuc for your answers.
Update(For Firebase Hosting):
Currently there is no workaround you can solve it with Firebase Hosting SDK.
But there is alternative way you can achieve this.
Try below code in your hosting source.
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
console.log('It's a local server!');
}
In my opinion, these are best way to check dev environment currently.
Therefore you should use location.hostname in your Firebase Hosting, and server.address() in Cloud Functions.
And define your Functions end point with constant variable.
const DEBUG = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
const FUNCTIONS_ENDPOINT_DEV = 'http://localhost:5001/';
const FUNCTIONS_ENDPOINT_PRD = 'https://us-central1-something.cloudfunctions.net/';
const FUNCTIONS_URL_CALC = 'calcEEGcompensation?year=2013&month=1&size=10';
var endpoint;
if (DEBUG) {
endpoint = FUNCTIONS_ENDPOINT_DEV + FUNCTIONS_URL_CALC;
} else {
endpoint = FUNCTIONS_ENDPOINT_PRD + FUNCTIONS_URL_CALC;
}
Original answer(For Cloud Functions for Firebase):
Have you tried node.js net module's server.address() function?
This method will tell you if your functions code is running on localhost or real deployed server.
For examples, you can use like this.
const server = app.listen(function() {
let host = server.address().address;
let port = server.address().port;
if (!host || host === '::') {
host = 'localhost:';
}
console.log('Server is running on %s%s', host, port);
});

Firebase Cloud Functions with Firebase Hosting : firebase.json "rewrites"

Iam trying to create a Dynamic page using cloud functions, but when i serve using cli in local, Iam getting the index.html page from public folder.
I tried deploying the functions,hosting in firebase but the problem stands still, were iam getting the index.html page from public folder
So for iam trying the following codes - I think i must be wrong with "rewrites" part.
/functions/index.js
const functions = require('firebase-functions');
exports.bigben = functions.https.onRequest((req, res) => {
const hours = (new Date().getHours() % 12) + 1 // london is UTC + 1hr;
res.status(200).send(`<!doctype html>
<head>
<title>Time</title>
</head>
<body>
${'BONG '.repeat(hours)}
</body>
</html>`);
});
firebase.json
{
"hosting": {
"public": "public",
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/", "function": "bigben"
} ]
}
}
What iam trying to do is when we hit https://example.firebaseapp.com/ , we have to get bigben functions response data.
Please some one give your hands!
Firebase Hosting will always prefer exact matches for static content over rewrites for performance reasons. Delete index.html from the public directory and your rewrite should begin working.

Firebase set up rewrites to pass current user to Cloud Function

I'm trying to use Cloud Functions for Firebase to serve content and I'd like to detect whether a user is logged in or not. I've set up a rewrite in my firebase.json that looks like this:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"rewrites": [{
"source": "**",
"function": "getProfile"
}]
}
}
This works fine and I'm serving appropriate content based on the path that's being requested. However, because I'm not doing anything on the client side (i.e. I'm using rewrites rather than client-side redirects), I'm missing the opportunity to get the current user from a client-side script.
Is there some way I can use a header or a property of the request object so that I can serve different content to logged in vs. non-logged in users in my server-side rewrites scenario?
Firebase Hosting passes along any cookie named __session when it calls a Cloud Function. An easy way to do this is to simply listen for ID tokens in your web app and set the cookie appropriately:
firebase.auth().onIdTokenChanged(user => {
if (user) {
user.getIdToken().then(token => {
document.cookie = `__session=${token};max-age=3600`;
});
} else {
document.cookie = '__session=;max-age=0';
}
});
Then, in your Cloud Function, you can parse the ID token out of the cookie and verify it using code like in this sample.

Resources