How to fix “Cannot GET” Error on Firebase Cloud Functions - reactjs

I have successfully configured my firebase. Inside my firebase functions folder I have the index.js which I have edited. Below is the code:
const functions = require('firebase-functions');
const app = require('express')();
const {
getAllTodos
} = require('./APIs/todos')
app.get('/todos', getAllTodos);
exports.api = functions.https.onRequest(app);
I have also created a todos.js file under APIs directory in the functions folder. I have written the getAllTodos function.
exports.getAllTodos = (request, response) => {
todos = [
{
'id': '1',
'title': 'greeting',
'body': 'Hello world from sharvin shah'
},
{
'id': '2',
'title': 'greeting2',
'body': 'Hello2 world2 from sharvin shah'
}
]
return response.json(todos);
}
I have successfully deployed it on my firebase. Using the ULR generated to view
https://us-central1-todoapp-f665a.cloudfunctions.net/api
I keep getting a
Cannot GET /
What could be wrong with my code? I have checked other questions that have to do with Cannot GET / error to no avail.
Thanks in advance for the help.

/api is the basic route of your express app. You should add /todos to get the expected response.
https://us-central1-todoapp-f665a.cloudfunctions.net/api/todos

Related

Can you fetch data in next.config

Is there any way to fetch data within the next.config.js file, I am trying to set up i18n but would like for the locales array to be fetched from a CMS rather than statically input by the developer.
From Next.js docs: (https://nextjs.org/docs/api-reference/next.config.js/introduction)
next.config.js is a regular Node.js module, not a JSON file. It gets used by the Next.js server and build phases, and it's not included in the browser build.
It means that you can do something like this:
module.exports = {
async headers() {
const response = await fetch("https://example.com/langs");
const dt = await response.json();
return [
{
source: "/api/dashboard",
headers: [{ key: "Lang", value: dt.map((d) => d.lng).join(",") }],
},
];
},
};
Obviously the above code can be done inside the endpoint handler but I just wanted to give an example to prove it's working.

Firebase functions not being invoked

I am trying to integrate Stripe payments on my webapp using Firebase. I have cloned the code from the repository here: https://github.com/firebase/functions-samples/tree/master/stripe and have followed the documentation here: https://firebase.google.com/docs/use-cases/payments
From reading the documentation, I assumed that when a customer signed in through firebase authentication, their details would be added to a stripe_customer collection in the firestore. I realised this wasn't the case, and manually added a user to test the save card functions. Then I received the following error : "Invalid value for stripe.confirmCardSetup intent secret: value should be a client_secret string. You specified: undefined"
I have a blaze plan for firebase and have configured. From following the steps in the documentation, I assumed this would be working. I'm sorry this question is so vague, but it seems at every corner I'm getting another issue. Is there something very obvious I am missing that is stopping this code from working? I am trying to implement this for a friends business as a favor, and am getting really confused with Firebase. I am coding in Angularjs. Would greatly appreciate any help on this!
This is the code for the function to create a customer
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const customer = await stripe.customers.create({ email: user.email });
const intent = await stripe.setupIntents.create({
customer: customer.id,
});
await admin.firestore().collection('stripe_customers').doc(user.uid).set({
customer_id: customer.id,
setup_secret: intent.client_secret,
});
return;
});
And this is the code being called in the controller:
const firebaseUI = new firebaseui.auth.AuthUI(firebase.auth());
const firebaseUiConfig = {
callbacks: {
signInSuccessWithAuthResult: function (authResult, redirectUrl) {
// User successfully signed in.
// Return type determines whether we continue the redirect automatically
// or whether we leave that to developer to handle.
return true;
},
uiShown: () => {
document.getElementById('loader').style.display = 'none';
},
},
signInFlow: 'popup',
signInSuccessUrl: '/checkout.html',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
],
credentialHelper: firebaseui.auth.CredentialHelper.NONE,
// Your terms of service url.
tosUrl: 'https://example.com/terms',
// Your privacy policy url.
privacyPolicyUrl: 'https://example.com/privacy',
};
firebase.auth().onAuthStateChanged((firebaseUser) => {
if (firebaseUser) {
currentUser = firebaseUser;
firebase
.firestore()
.collection('stripe_customers')
.doc(currentUser.uid)
.onSnapshot((snapshot) => {
if (snapshot.data()) {
customerData = snapshot.data();
startDataListeners();
document.getElementById('loader').style.display = 'none';
document.getElementById('content').style.display = 'block';
} else {
console.warn(
`No Stripe customer found in Firestore for user: ${currentUser.uid}`
);
}
});
} else {
document.getElementById('content').style.display = 'none';
firebaseUI.start('#firebaseui-auth-container', firebaseUiConfig);
}
});
The error you've supplied (below) implies that the key in your config isn't been pulled into your code. If you're running this locally you need to run the below any time you change your functions:config values.
firebase functions:config:get > .runtimeconfig.json
Check the doc's out about how to run your function locally:
Error
"Invalid value for stripe.confirmCardSetup intent secret: value should
be a client_secret string. You specified: undefined"

laravel reactjs pusher: presence channel response is 302

I'm trying to make a reactjs application where an user can only login to one device at the time with the same user credentials. Unfortunately it isn't working.
I'm trying to authenticate a presence channel with reactjs to laravel but I get a 302 response.
reactjs:
Pusher.logToConsole = true;
var pusher = new Pusher("9028d58568392772df59", {
cluster: "eu",
forceTLS: true,
authEndpoint: '/broadcasting/auth',
auth: {
headers: {
'X-CSRF-Token': csrf_token
}
}
});
var channel = pusher.subscribe("presence-HandleCredentials");
channel.bind("sameCredentials", function(data) {
console.log(data);
alert(JSON.stringify(data));
});
channel:
Broadcast::channel('App.User', function ($user, $id = 1) {
return (int) $user->id === (int) $id;
});
broadcast:
public function boot()
{
Broadcast::routes(['middleware' => ['auth:web']]);
require base_path('routes/channels.php');
}
When I added this ['middleware' => ['auth:web']] I got the error. Before I added that I got a 403 error.
in the config\app.php I uncommented App\Providers\BroadcastServiceProvider::class,
Are there any tutorials out there that are build with laravel and reactjs for a presence channel?
does anyone know how to get past this 302 redirect?
Recently had the same issue with my laravel-websockets and laravel echo.
In my case I was unable to solve the 302, as Broadcast was unable to authenticate my logged in user. I was trying to subscribe to my private channel. So the workaround i found was that i manually created a POST route in web.php as "/broadcasting/auth". This is what my front-end requests to. So The updated code in web.php is as follows.
Route::post('/broadcasting/auth', function(Request $request){
$pusher = new Pusher\Pusher(
env('PUSHER_APP_KEY'),
env('PUSHER_APP_SECRET'),
env('PUSHER_APP_ID'),
array(
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => false,
'host' => env('APP_URL'),
'port' => 6001,
'scheme' => 'http',
)
);
return $pusher->socket_auth($request->request->get('channel_name'),$request->request->get('socket_id'));
});
I was creating my own websocket that is why i had to mention the host & port within the options, you don't need to use it if you are Using Pusher. You can also add other middlewares to the routes if needed.
You have to comment out the following line in app/providers/BroadcastServiceProvider:
public function boot()
{
// Broadcast::routes();
require base_path('routes/channels.php');
}
so that the request can reach my broadcasting/auth route in web.php.
Try this. now this should return a 200 when the broadcasting/auth is requested by your client end with response of an auth code. Do let me know if this solves your problem.

Getting “TypeError: failed to fetch” when sending an email with SendGrid on ReactJS project

I am trying to send email with SendGrid in ReactJS project.
This is my componnet:
//Email.js
import React from 'react'
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg).catch(error => {alert(error.toString()); });
export const Email= () => (
<h1>Email Sending Page</h1>
)
When I am trying to run the app with "npm start" on localhost, the email is not sent and I got the error message "TypeError: Failed to fetch".
But, if I am using this code:
//Email.js
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg)
and do this command: "node Email.js" the mail is sent. It works only this way and I cannot understand why.
I tried any solution that I could find but nothing works.
(I tried even to put the api_key hardcoded in the code just for the test and I got the same result).
EDIT
After looking around a bit I found out that you can't use Sendgrid to send email directly from the browser.
Sendgrid won't let you send an email directly using Javascript in the
browser.
You will need to have a server set-up and use the server to send the
email instead (using your favourite back-end framework/language,
Node.js, php, Java, etc.).
The steps for sending a mail will be similar to this:
Write email details in the React application
Send a POST request to
your server endpoint (for example, /sendemail) with the email data
(recipient, title, content, etc.) Receive Email data in the server and
send it to Sendgrid api Here is the official Sendgrid documentation
regarding their CORS policy:
https://sendgrid.com/docs/for-developers/sending-email/cors/
Source: React unable to send email with SendGrid
EDIT 2
If you want to implement Sendgrid without actually building and deploying a server, you can use a simple Firebase function which is free to host.
I know this may look intimidating but in reality its pretty easy. Also I just put this together real quick so if anything doesn't work out for you, shoot me a comment.
Follow steps 1-3 on the getting started page for firebase functions. It is pretty straightforward and you end up with the firebase tools CLI installed.
Navigate to the functions/ folder inside your project on the command line/terminal.
Install the Sendgrid and cors libraries in the functions folder
npm i #sendgrid/mail cors
Add your Sendgrid API key to your firebase environment with the following command in your project:
firebase functions:config:set sendgrid.key="THE API KEY"
Copy this into your functions/index.js file:
const functions = require("firebase-functions");
const cors = require("cors")({ origin: true });
const sgMail = require("#sendgrid/mail");
exports.sendEmail = functions.https.onRequest((req, res) => {
sgMail.setApiKey(functions.config().sendgrid.api);
return cors(req, res, () => {
const { msg } = req.body;
sgMail.send(msg).catch(error => {
alert(error.toString());
});
res.status(200).send(msg);
});
});
Save it and run firebase deploy --only functions on the command line. Your function should now be live at https://us-central1-<project-id>.cloudfunctions.net/sendEmail
Now change your React file to:
//Email.js
import React, { useEffect } from 'react'
export const Email= () => {
useEffect(() => {
const sendEmail = async() => {
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
const response = await fetch(
'https://us-central1-FIREBASE-PROJECT-ID-HERE.cloudfunctions.net/sendEmail', {
method: 'POST',
body: JSON.stringify(msg),
headers: {
'Content-Type': 'application/json'
}
});
console.log("response", response);
}
sendEmail();
}, []);
return <h1>Email Sending Page</h1>
}
And thats it! You basically have a server side function without making a server and its free!
Feel free to ignore this if you don't feel like putting in the work but if you need any help, let me know.

Error: User credentials required in Google Cloud Print API

I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.

Resources