Google Spreadsheet Api not working on mobile devices - database

I'm using Google Spreadsheets as a lightweight database for my React App. The app is a wedding website and the idea is for users to be able to RSVP and the response gets saved in a Google Spreadsheet. See GitHub for a visual representation of what I mean: https://github.com/michelleroos/mimirgettingmarried
It's working as intended on desktop, meaning RSVP responses get saved to the sheet, but not on mobile and I can't figure out why.
Would love your input! If I could even just understand where to start looking that would be great but there is not a lot of information out there on Google Spreadsheet Api.
const express = require("express");
const app = express();
const { google } = require('googleapis');
const PORT = process.env.PORT || 3001; // use live port or the declared one
var cors = require('cors');
app.use(cors())
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const auth = new google.auth.GoogleAuth({
keyFile: "./config/credentials.json",
scopes: 'https://www.googleapis.com/auth/spreadsheets',
});
const spreadsheetId = "1-R0vjBCOD5hcipk9AQbVUwQAH_0dFvQB2qs5gxsxWg0";
const range = "rsvp";
// api endpoint returns a CB. We decide what to do w res thru CB
app.get("/api/rsvp", async (req, res) => {
// create client instance for auth
const client = await auth.getClient();
// create Google sheets API instance
const googleSheets = google.sheets({ version: "v4", auth: client })
// read rows
const getRows = await googleSheets.spreadsheets.values.get({
auth,
spreadsheetId,
range,
});
// express/app sends to the client (the body)
res.send(getRows.data.values);
});
app.post("/api/rsvp", async (req, res) => {
// create client instance for auth
const client = await auth.getClient();
// create Google sheets API instance
const googleSheets = google.sheets({ version: "v4", auth: client })
const writeRows = await googleSheets.spreadsheets.values.append({
auth,
spreadsheetId,
range,
valueInputOption: "USER_ENTERED", // parses info
resource: {
values: [
// Object.keys(req.body), // header
Object.values(req.body) // values
]
}
})
res.send(writeRows);
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});

Related

Issue connecting to token server with Firebase hosting

I am trying to deploy the Twilio video app to Firebase hosting. Everything works great, except when I try to connect to video conference it tells me the Twilio tokens are invalid.
I found that I need to set up google cloud functions to resolve this. How do you go about converting a server.js file to a cloud function?
Here is my code server.js code:
const express = require('express');
const app = express();
const path = require('path');
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
require('dotenv').config();
const MAX_ALLOWED_SESSION_DURATION = 14400;
const twilioAccountSid = process.env.TWILIO_ACCOUNT_SID;
const twilioApiKeySID = process.env.TWILIO_API_KEY_SID;
const twilioApiKeySecret = process.env.TWILIO_API_KEY_SECRET;
app.use(express.static(path.join(__dirname, 'build')));
app.get('/token', (req, res) => {
const { identity, roomName } = req.query;
const token = new AccessToken(twilioAccountSid, twilioApiKeySID, twilioApiKeySecret, {
ttl: MAX_ALLOWED_SESSION_DURATION,
});
token.identity = identity;
const videoGrant = new VideoGrant({ room: roomName });
token.addGrant(videoGrant);
res.send(token.toJwt());
console.log(`issued token for ${identity} in room ${roomName}`);
});
app.get('*', (_, res) => res.sendFile(path.join(__dirname, 'build/index.html')));
app.listen(8081, () => console.log('token server running on 8081'));
I'm thinking I can move that to the cloud functions index.js file and add the following to still connect to my .env file variables if I put the express function in here:
const functions = require('firebase-functions');
const config = functions.config();
// Porting envs from firebase config
for (const key in config.envs){
process.env[key.toUpperCase()] = config.envs[key];
}
To convert this to a Firebase cloud function, you need to remove server listeners and you have to setup a local Firebase environment to deploy and develop
Steps to convert cloud function
# Install firebase-tools
npm install -g firebase-tools
# Login and initialize project
firebase login
firebase init functions
# For local dev
firebase serve
# Deploy the function to cloud
firebase deploy
Your current code will look something like this after converting to cloud function
You can also make each routes into separate modules
const functions = require('firebase-functions');
const express = require('express');
const app = express();
const path = require('path');
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
require('dotenv').config();
const router = express.Router();
const MAX_ALLOWED_SESSION_DURATION = 14400;
const twilioAccountSid = process.env.TWILIO_ACCOUNT_SID;
const twilioApiKeySID = process.env.TWILIO_API_KEY_SID;
const twilioApiKeySecret = process.env.TWILIO_API_KEY_SECRET;
app.use(express.static(path.join(__dirname, 'build')));
router.get('/token', (req, res) => {
const { identity, roomName } = req.query;
const token = new AccessToken(twilioAccountSid, twilioApiKeySID, twilioApiKeySecret, {
ttl: MAX_ALLOWED_SESSION_DURATION,
});
token.identity = identity;
const videoGrant = new VideoGrant({ room: roomName });
token.addGrant(videoGrant);
res.send(token.toJwt());
console.log(`issued token for ${identity} in room ${roomName}`);
});
router.get('*', (_, res) => res.sendFile(path.join(__dirname, 'build/index.html')));
// Your cloud function will be like : https://<region>-<appname>.cloudfunctions.net/twilioApp
exports.twilioApp = functions.https.onRequest(router);
Please read the official documentation here

Firebase not responding to cloud function

I am adding data to the realtime database with React JS, a contact form and the firebase initialized. That all works.
However, I'm trying to implement an email to be sent to me when a new contact form has been submitted. CURRENT PROBLEM: The cloud function is deployed yet when I submit the form (and realtime db is added to), nothing happens. Not even an error message in the firebase console.
Please can you take a look at my code and offer some advice as to how I can get the automatic emails sent.
const functions = require('firebase-functions')
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');
admin.initializeApp()
require('dotenv').config()
const email = process.env.REACT_APP_SENDER_EMAIL;
const pass = process.env.REACT_APP_SENDER_PASS;
exports.sendEmailNotification = functions.firestore.document('messages/{id}')
.onCreate((snap, ctx) => {
const data = snap.data();
let authData = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: email,
pass: pass
}
});
authData.sendMail({
from: data.email,
to: data.to,
subject: data.name + ' sent a message',
text: data.text,
}).then(res => console.log('email sent')).catch(err => console.log(err));
});
Your function needs to return a promise that resolves when all the asynchronous work is complete.
return authData.sendMail({
from: data.email,
to: data.to,
subject: data.name + ' sent a message',
text: data.text,
})
Returning this promise lets Cloud Functions know when it's safe to clean up and move on.
I first attempted creating this as a firebase cloud function as well, but I shifted towards building nodemailer on the server. Working in firebase cloud functions I was using the loophole of downgrading to node: 8 in package.json (which is deprecated) and I was being forced into making a Google firebase paid plan. Both items were driving me into a corner that I didn't want to be in.
This is the result of nodemailer in node.js thanks to https://www.youtube.com/watch?v=nF9g1825mwk
const express = require('express')
require('dotenv').config()
const bodyParser = require('body-parser')
const exphbs = require('express-handlebars')
const path = require('path')
const nodemailer = require('nodemailer')
const app = express()
const email_from = process.env.EMAIL_FROM;
const sender_pass = process.env.SENDER_PASS;
const email_to = process.env.EMAIL_TO;
// View engine setup
app.engine('handlebars', exphbs())
app.set('view engine', 'handlebars')
//body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//static folder
app.use('/public', express.static(path.join(__dirname, 'public')))
app.get('/', (req, res) => {
res.render('contact', { layout: false })
})
app.post('/send', (req, res) => {
const output = `
<p>You have a new submission</p>
<h3>Contact Details</h3>
<ul>
<li>Name: ${req.body.name}</li>
<li>Company: ${req.body.company}</li>
<li>Email: ${req.body.email}</li>
<li>Phone: ${req.body.phone}</li>
</ul>
<h3>Message</h3>
<p> ${req.body.message} </p> `;
async function main() {
// Generate test SMTP service account from ethereal.email
// Only needed if you don't have a real mail account for testing
// let testAccount = await nodemailer.createTestAccount();
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: email_from,
pass: sender_pass
},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: email_from,
to: email_to,
subject: 'New Submission from Dean Productions!',
text: 'new submission',
html: output,
});
console.log("Message sent: %s", info.messageId);
// Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321#example.com>
// Preview only available when sending through an Ethereal account
console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
// Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
res.render('contact', { layout: false, msg: 'Message has been sent!' })
}
main().catch(console.error);
})
app.listen(3000, () => console.log('Server started...'))

OAuth oidc getting access to user info on server side in SSR react app

I have SSR react app using redux-oidc I can successfuly log-in using IdentityServer4 as an authorization server with PKCE Authorization Code Flow.
I can get user data on client side of react application but I don't know how to do it on server side of same react app.
I want to show logged-in version of page on first load and for that I need to get user data during the first request.
const Koa = require('koa')
const next = require('next')
const Router = require('koa-router')
const jwt = require('koa-jwt')
const jwksRsa = require('jwks-rsa')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = new Koa()
const router = new Router()
server.use(jwt({
secret: jwksRsa.koaJwtSecret({
jwksUri: 'http://localhost:5000/.well-known/openid-configuration/jwks'
}),
issuer: 'http://localhost:5000',
algorithms: [ 'RS256' ],
passthrough: true
}));
router.get('*', async ctx => {
console.log(ctx.state)
console.log(ctx.header.cookie)
await handle(ctx.req, ctx.res)
ctx.respond = false
})
server.use(async (ctx, next) => {
ctx.res.statusCode = 200
await next()
})
server.use(router.routes())
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`)
})
})
Logicaly only data I can work with during first request are cookies I have just these three .AspNetCore.Identity.Application (id token), idsrv.session and CookieConsent.
How to use them to get user data? User info endpoint requires access token which I don't have because it is stored in sessionStorage on client.
There must be something I am missing.

Puppeteer, ExpressJS, React SSR

i am trying to achieve Server Side Rendering aka SSR for my react app. I created an express app with puppeteer and got endpoint with following snippets.
const port = 3003
app.get('*', async (req, res) => {
try {
const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();
const local_url = `http://localhost:${port}${req.originalUrl}`;
await page.goto(local_url, {
waitUntil: "networkidle0",
});
const html = await page.content()
await browser.close()
res.send(html);
} catch(e) {
console.log(e);
res.send("ERROR");
}
});
app.listen(process.env.PORT || port, () => {
console.log(`Web run ${port}`);
});
I just don't know why this is not working. Do I have to run my react app(run on 3000) separate and point local_url variable to there? like
const local_url = `http://localhost:3000${req.originalUrl}`;
You are in a loop where you are requesting same url over and over.
Your react app is in port 3000, and this express app is in port 3003.
You can make things a bit easy to read if you seperate them.
You have to run the react server on port 3000 and point to that otherwise it won't know where to browse.
const react_port = 3000;
const server_port = 3003;
const local_url = `http://localhost:${react_port}${req.originalUrl}`;
and vice varsa.

Google Cloud speech with React Native

I am trying to use Google Cloud Speech API so I can pass audio file and receive the translated text but I am stuck to the integration. I already have api key and everything needed but can't find how to use it from react native. In the documentation there is only explanation for node.js (from javascript part). Also there are several libraries out dated or supporting only the one OS. Someone succeeded in that?
The node.js example from the documentation:
// Imports the Google Cloud client library
const Speech = require('#google-cloud/speech');
// Your Google Cloud Platform project ID
const projectId = 'YOUR_PROJECT_ID';
// Instantiates a client
const speechClient = Speech({
projectId: projectId
});
// The name of the audio file to transcribe
const fileName = './resources/audio.raw';
// The audio file's encoding and sample rate
const options = {
encoding: 'LINEAR16',
sampleRate: 16000
};
// Detects speech in the audio file
speechClient.recognize(fileName, options)
.then((results) => {
const transcription = results[0];
console.log(`Transcription: ${transcription}`);
});
You could deploy the code using google app engine and make a post req from react-native.
Also need to configure and use google cloud storage to store the audio file for conversion.
Here is my server code.
const format = require('util').format;
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const requestHttp = require('request');
const {Storage} = require('#google-cloud/storage');
// Instantiate a storage client
const storage = new Storage();
// const upload = multer();
const app = express();
// Imports the Google Cloud client library
const speech = require('#google-cloud/speech');
// Creates a client
const client = new speech.SpeechClient();
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
const encoding = 'LINEAR16';
const sampleRateHertz = 16000;
const languageCode = 'en-US';
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024, // no larger than 5mb, you can change as needed.
},
});
const bucket = storage.bucket(process.env.GCLOUD_STORAGE_BUCKET);
app.post('/upload', upload.single('file') , async (req, res) => {
const file = await req.file
if (!file) {
const error = new Error('Please upload a file')
error.httpStatusCode = 400
return next(error)
}
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
resumable: false,
});
blobStream.on('error', err => {
next(err);
});
blobStream.on('finish', async () => {
// The public URL can be used to directly access the file via HTTP.
const publicUrl = await format(
`https://storage.googleapis.com/${bucket.name}/${blob.name}`
);
const request = {
config: {
encoding: encoding,
sampleRateHertz: sampleRateHertz,
languageCode: languageCode,
},
audio: {
uri: 'gs://YOUR-Bucket-Name/File-name.ext'
}
};
// Stream the audio to the Google Cloud Speech API
const [response] = await client.recognize(request);
const transcription = response.results
.map(result => result.alternatives[0].transcript)
.join('\n');
console.log(`Transcription: `, transcription);
res.status(200)
.send({
success: 'true',
message: 'Text retrieved successfully',
text: transcription
})
.end();
});
blobStream.end(req.file.buffer);
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
deploy this server to Heroku then from your react native app send the post or get request to this server and get the result on your app.
To send post or get request use Axios library https://github.com/axios/axios

Resources