How to include storage service account key in function - firebase

I'm trying to include service account key into my storage function to be able to get long lived signed url by following out of date example here
https://github.com/firebase/functions-samples/blob/b404482342906ee1b46dddb4c75667685ab098a1/generate-thumbnail/functions/index.js#L21
I have downloaded my key from IAM which is in JSON format. I have tried to save it right next to my function
-functions/storage/resizeProfileImg.js
-functions/storage/service-account-credentials.json
-functions/index.js
-functions/admin.js
where resizeProfileImg.js is my function and call it like this
const { Storage } = require('#google-cloud/storage');
const storage = new Storage({ projectId: projectId ,keyFilename: './service-account-credentials.json'})
but after deployment when the function is triggered then I get an error
Error: ENOENT: no such file or directory, open '/srv/service-account-credentials.json'
I have even tried to add it in constant like this
const serviceAccountCredentials = require('./accountKey/service-account-credentials.json')
const { Storage } = require('#google-cloud/storage');
const storage = new Storage({ projectId: projectId ,keyFilename: serviceAccountCredentials})
but then I get an error
TypeError: Path must be a string. Received { type: 'service_account',...
Any idea how to do this properly

In Cloud Functions, the current directory . isn't where your source file is located. It's where the functions folder was deployed. Since your credentials file is in a subdirectory called "storage", you will need to use that in the path.
const serviceAccountCredentials = require('./storage/service-account-credentials.json')

Related

Firebase Storage with Google Actions

I am having some issues connecting my firebase storage with my google action. I need to be able to "download" the json files inside in order to be able to read and pick out what a user may need given data that they provide when they call the action.
Below is the code that I currently have, complied from the different APIs and other stackoverflow questions I have found.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
var storage = require('#google-cloud/storage');
const gcs = storage({projectId: 'aur-healthcare-group'});
const bucket = gcs.bucket('gs://aur-healthcare-group');
admin.storage().bucket().file('aur-healthcare-group/aur_members.json').download(function(errr, contents){
if(!err){
var jsObjext = JSON.parse(contents.toString('utf8'));
}
});
The current error I am receiving is "code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause. When I check the logs I only get the above mentioned message again.
I believe that I am not accessing my firebase storage correctly and have trouble finding a good resource on how to access this correctly. Would somebody be able to give me an example of how to access the storage correctly so I will be able to apply it to my project?
Since you're running in Firebase Functions, you shouldn't need to require the #google-cloud/storage dependency directly. Rather, you can get the correctly authenticated storage component via admin.storage()
Following that, you shouldn't download the file to your function, as you would be better off reading directly into memory via a readStream.
With regards to your existing code error, it may be because you're checking if (!err) when the callback variable is errr.
I've done this in the past and here's a code snippet of how I achieved it. It's written in Typescript specifically, but I think you should be able to port it to JS if you're using that directly.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'
import { Bucket } from '#google-cloud/storage';
admin.initializeApp()
const db = admin.firestore()
const bucket = admin.storage().bucket('project-id.appspot.com') // Use your project-id here.
const readFile = async (bucket: Bucket, fileName: string) => {
const stream = bucket.file(fileName).createReadStream();
return new Promise((resolve, reject) => {
let buffer = '';
stream.on('data', function(d: string) {
buffer += d;
}).on('end', function() {
resolve(buffer)
});
})
}
app.handle('my-intent-handler', async (conv) => {
const contents = await readArticle(bucket, 'filename.txt')
conv.add(`Your content is ${contents}`)
})
exports.fulfillment = functions.https.onRequest(app)

Firebase cloud function cannot upload simple txt file

i'm trying to upload a simple test file from my working directory to firebase storage. To do so i created this code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.Storage = functions.https.onCall((data, context) => {
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const bucket = storage.bucket('myapp.appspot.com');
const options = {
destination: 'Test_Folder/hello_world.dog'
};
bucket.upload('./tst.txt', options).then(function(data) {
const file = data[0];
if (file) {
return file;
}else{
throw new Error("Irgendwas geht ned")
}
}).catch(e);
return 200;
});
Unfortunately firebase is always saying:
Error: ENOENT: no such file or directory, stat '/tst.txt'
My File is located in ./tst.txt in my project
My Goal is to generate a text file inside my cloudfunction and upload them to firebase storage to store it. No files are currently stored in storage. Now i want to be able to upload a file which is already created.
This is how my files are organized:
The Firebase CLI will deploy all of the files in the functions folder, except for node_modules. Your tst.txt file isn't in there - it's one folder higher. So it's not even being deployed. You will have to move it into functions in order to make it available to the function at runtime.

Generating a PDF when a document is created in Firebase Cloud Firestore

I'm developing an app that creates a PDF based on a web form.
I am currently attempting to use pdfmake to generate the PDFs based on a firestore document create trigger
import * as functions from 'firebase-functions';
const admin = require('firebase-admin);
admin.initializeApp();
const PdfPrinter = require('pdfmake');
const fs = require('fs');
export const createPDF = functions.firestore
.document('pdfs/{pdf}')
.onCreate(async (snap, context) => {
var pdfName = context.params.pdf;
var printer = new PdfPrinter();
var docDefinition = {
// Pdf Definitions
};
var options = {
// Pdf Options
};
var pdfDoc = printer.createPdfKitDocument(docDefinition, options);
pdfDoc.pipe(fs.createWriteStream('tempDoc.pdf'));
await pdfDoc.end();
// Upload to Firebase Storage
const bucket = admin.storage().bucket('myproject.appspot.com');
bucket.upload('tempDoc.pdf', {
destination: pdfName + '.pdf',
});
return fs.unlinkSync('document.pdf');
});
The trigger is called, however i get the error "Error: ENOENT: no such file or directory, stat 'document.pdf'"
I have tried it with the onCreate function being async and without.
Any help is greatly appreciated
It's not possible to write to any file location in Cloud Functions outside of /tmp. If your code needs to write a file, it should build paths off of os.tmpdir() as described in the documentation:
The only writeable part of the filesystem is the /tmp directory, which
you can use to store temporary files in a function instance. This is a
local disk mount point known as a "tmpfs" volume in which data written
to the volume is stored in memory. Note that it will consume memory
resources provisioned for the function.
The rest of the file system is read-only and accessible to the
function.

Firebase Authentication for Realm Cloud using JWT and a Google Cloud Function

I am attempting to use JWTs to authenticate into Realm Cloud. I am using Firebase as the authentication service and am attempting to create a Google Cloud Function to generate the JWT. I generated the private and public keys using the terminal command "ssh-keygen". Realm's JWT tutorial suggested the following line of code to read the key file:
const key = fs.readFileSync('./functions/id_rsa', 'utf8');
I copied the private key over to the project, pointed the code above to the file, but when I deploy the Google Cloud Function, I received the following error message:
âš  functions[myAuthFunction(us-central1)]: Deployment error. Function
failed on loading user code. Error message: Code in file index.js
can't be loaded. Is there a syntax error in your code? Detailed stack
trace: Error: ENOENT: no such file or directory, open
'./functions/id_rsa'
My project structure is as follows:
Picture
I have attempted to ask on Realm's forums but I haven't received much help. The entire cloud function they suggested is:
const functions = require("firebase-functions");
const jwt = require('jsonwebtoken');
const fs = require('fs');
const key = fs.readFileSync(’pathToMyPrivateKeyFile');
exports.myAuthFunction = functions.https.onCall((data, context) => {
const uid = context.auth.uid
const payload = { userId: uid }
const token = jwt.sign(payload, { key: key, passphrase: "your-passphrase" }, { algorithm: 'RS256'}),
return { token:token }
});
In summary, how can the google cloud function read my private key file in my project? The public key is stored inside my Realm Cloud dashboard for the specific instance.
Sources:
Realm Cloud JWT Firebase tutorial
Try passing just ./id_rsa or id_rsa. I believe the path root is where the index.js file is.

How to set the destination for file.download() in Google Cloud Storage?

The Google Cloud Storage documentation for download() suggests that a destination folder can be specified:
file.download({
destination: '/Users/me/Desktop/file-backup.txt'
}, function(err) {});
No matter what value I put in my file is always downloaded to Firebase Cloud Storage at the root level. This question says that the path can't have an initial slash but changing the example to
file.download({
destination: 'Users/me/Desktop/file-backup.txt'
}, function(err) {});
doesn't make a difference.
Changing the destination to
file.download({
destination: ".child('Test_Folder')",
})
resulted in an error message:
EROFS: read-only file system, open '.child('Test_Folder')'
What is the correct syntax for a Cloud Storage destination (folder and filename)?
Changing the bucket from myapp.appspot.com to myapp.appspot.com/Test_Folder resulted in an error message:
Cannot parse JSON response
Also, the example path appears to specify a location on a personal computer's hard drive. It seems odd to set up a Cloud Storage folder for Desktop. Does this imply that there's a way to specify a destination somewhere other than Cloud Storage?
Here's my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const bucket = storage.bucket('myapp.appspot.com');
bucket.upload('./hello_world.ogg')
.then(function(data) {
const file = data[0];
file.download({
destination: 'Test_Folder/hello_dog.ogg',
})
.then(function(data) {
const contents = data[0];
console.log("File uploaded.");
})
.catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
return 0;
});
According to the documentation:
The only writeable part of the filesystem is the /tmp directory, which
you can use to store temporary files in a function instance. This is a
local disk mount point known as a "tmpfs" volume in which data written
to the volume is stored in memory. Note that it will consume memory
resources provisioned for the function.
The rest of the file system is read-only and accessible to the
function.
You should use os.tmpdir() to get the best writable directory for the current runtime.
Thanks Doug, the code is working now:
exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const bucket = storage.bucket('myapp.appspot.com');
const options = {
destination: 'Test_Folder/hello_world.dog'
};
bucket.upload('hello_world.ogg', options)
.then(function(data) {
const file = data[0];
});
return 0;
});
The function gets the file hello_world.ogg from the functions folder of my project, then writes it to Test_Folder in my Firebase Cloud Storage, and changes the name of the file to hello_world.dog. I copied the download URL and audio file plays perfectly.
Yesterday I thought it seemed odd that writing a file to Cloud Storage was called download(), when upload() made more sense. :-)
You can download the files from Google Cloud Storage to your computer using the following code or command
Install python on your PC
Install GCS on your PC
pip install google-cloud-storage
kesaktopdi.appspot.com
Download .json file and save it in /home/login/ folder
Change your account
https://console.cloud.google.com/apis/credentials/serviceaccountkey?project=kesaktopdi
import os
ACCOUNT_ID='kesaktopdi'
os.environ["GOOGLE_APPLICATION_CREDENTIALS"]="/home/login/" + ACCOUNT_ID + ".json"
def download_blob(bucket_name, source_blob_name, destination_file_name):
storage_client = storage.Client()
bucket = storage_client.get_bucket(bucket_name)
blob = bucket.blob(source_blob_name)
blob.download_to_filename(destination_file_name)
#print('Blob {} downloaded to {}.'.format(source_blob_name,destination_file_name))
download_blob(ACCOUNT_ID +'.appspot.com', #account link
'user.txt', #file location on the server
'/home/login/kesaktopdi.txt') #file storage on a computer
You can also download files from the Google Cloud Storage server to your computer using the following command.
file location on the server file storage on a computer
gsutil -m cp -r gs://kesaktopdi.appspot.com/text.txt /home/login
The program was created by the APIuz team https://t.me/apiuz

Resources