How to retrieve Firebase data using firebaseServer? - javascript

I created an app with Firebase authentication using Next.js' starter and setup the Firebase authentication and database. I can run the app just fine (log in and add messages).
Then I noticed it wasn't SSR'ing the Firebase data:
export default class Index extends Component {
static async getInitialProps({ req, query }) {
const user = req && req.session ? req.session.decodedToken : null;
// don't fetch anything from firebase if the user is not found
// const snap = user && await req.firebaseServer.database().ref('messages').once('value')
// const messages = snap && snap.val()
const messages = null;
return { user, messages };
}
I uncommented the code to see if I could SSR everything:
export default class Index extends Component {
static async getInitialProps({ req, query }) {
const user = req && req.session ? req.session.decodedToken : null;
// don't fetch anything from firebase if the user is not found
const snap =
user &&
(await req.firebaseServer
.database()
.ref("messages")
.once("value"));
const messages = snap && snap.val();
// const messages = null;
return { user, messages };
}
The user and snap have values but snap.val() is null.
How can I retrieve the messages via firebaseServer?
If it's any help, here's the whole file.

firebaseServer.database() === Realtime Database
The documents where being added to Cloud Firestore, not Realtime Database.
This pulls the documents from Cloud Firestore:
static async getInitialProps({ req, query }) {
const user = req && req.session ? req.session.decodedToken : null;
const snap =
user &&
(await req.firebaseServer
.firestore()
.collection("messages")
.get());
const messages = snap && snap.docs.map(d => d.data());
return { user, messages };
}

Related

Firebase Realtime data is only available after saving the edited source file?

I'm relatively new to RN / Javascript, so this might be a rookie mistake - strangely I couldn't find anything close to it on my research.
My setup: I'm developing a RN app with Expo and I'm using the Expo Go app to see any changes.
Since I want to enable cloud services, I'm using the Firebase Realtime database with the official packages in my app. So far, so good.
My issue: Every time I start the developement server (npm start) or reload the app with the 'r' shortcut on my Accounts screen (basic screen displaying the names of the accounts the user created), see attached screenshot, the app refuses to load the data from Realtime - instead I'm greeted with a 'undefined is not an object (evaluating 'obj['accounts']'). Once I hit 'STRG + S' on my keyboard in any file opened, the Expo Go app refreshes and the data is somehow found.
If anyone could help me with this issue, you would surely save my day. :)
CODE
My data is loaded from here (dataHandler.js):
// auth stuff
import { Auth, getAuth } from "firebase/auth";
// database stuff
import { db } from "./firebase";
import { get, onValue, ref } from 'firebase/database'
// more auth stuff
const auth = getAuth()
const userID = auth.currentUser?.uid;
// database Path for retrieving data
const databasePath = userID
export var cachedData = {};
// Gets data from the firebase server, set's it to a local value
export function getData() {
return onValue(ref(db, databasePath), querySnapshot => {
let data = querySnapshot.val() || {};
let downloadedData = {...data};
// set data to a public var
cachedData = downloadedData;
console.log('DEBUG: Data loaded from the server')
})
}
My account data is then loaded from here (accountData.js):
// load the data from dataHandler.js
import { cachedData } from "./dataHandler";
import { getAuth } from "firebase/auth";
const auth = getAuth()
const userID = auth.currentUser?.uid;
export function getAccountData() {
console.log('accountData receives = ', cachedData)
let obj = cachedData[userID];
let accounts = obj['accounts'];
console.log('getAccountData returns: ', accounts)
return accounts;
}
I'm calling the files here:
// experimental stuff
import { getData } from '../../core/dataHandler';
import { getAccountData } from '../../core/accountData'
const Accounts = () => {
// Downloads data on the app start
getData();
// load the data for the account
const accounts = getAccountData()
console.log('accountData = ', accounts)
const accountKeys = Object.keys(accounts)
const [ accountName, setAccountName ] = useState('')
return( <SomeView /> )
}

SvelteKit: How to call mongodb without using endpoints?

(1/9/2023) Update : SvelteKit now supports server only load functions and Form actions to send requests to the server.
I want to call my database, but I don't want it be able to get accessed by end users by them going to the API endpoint that I set up. I was wondering how I would be able to just call my database from a file in the lib folder and just returning the data there. When I try it I get the error global not defined:
lib/db.js:
import dotenv from "dotenv";
dotenv.config();
import { MongoClient } from "mongodb";
const uri = process.env["MONGODB_URI"];
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
};
let client;
let clientPromise;
if (!uri) {
throw new Error("Please add your Mongo URI to .env.local");
}
if (process.env["NODE_ENV"] === "development") {
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
client = new MongoClient(uri, options);
clientPromise = client.connect();
}
export default clientPromise;
routes/items/index.js:
import clientPromise from "$lib/db";
export async function get() {
const client = await clientPromise;
const db = client.db();
const data = await db.collection("items").find({}).toArray();
const items = data.map(({ name }) => ({ name }));
if (items) {
return {
body: {
items,
},
};
}
}
My attempt:
lib/stores/items.js
import clientPromise from "$lib/db";
import { writable } from "svelte/store";
export const items= writable([]);
const fetchItems = async () => {
const client = await clientPromise;
const db = client.db();
const data = await db.collection("items").find({}).toArray();
const items = data.map(({ name }) => ({ name }));
substances.set(items);
};
fetchItems();
Trying the above code in various places always yields a global not defined error in the client.
I found one question from someone with the same problem, but I couldn't figure out how to create a helper file.
Protecting API is done on back-end side. Usually it either server (like NodeJS) or tools Nginx/Apache (proxy, etc.). You're basically looking for Content-Security-Policy topic, which is vaporous but not related to SvelteKit.
Btw, calling DB directly from the Front-end wouldn't be secure and is not possible.
To get data from any database, you should create enpoint
For user authentication, you can create handle hook:
export async function handle({ request, resolve }) {
let user = await authenticate(request)
request.locals.user = user
request.locals.isAuthenticated = !!user
if (request.path.startsWith('/api')) {
if (!user) {
return {
status: 401,
body: JSON.stringify({
error: {
message: 'Unauthorized'
}
})
}
}
const response = await resolve(request)
return response
}

how to create a user with firebase without signing in [duplicate]

So I have this issue where every time I add a new user account, it kicks out the current user that is already signed in. I read the firebase api and it said that "If the new account was created, the user is signed in automatically" But they never said anything else about avoiding that.
//ADD EMPLOYEES
addEmployees: function(formData){
firebase.auth().createUserWithEmailAndPassword(formData.email, formData.password).then(function(data){
console.log(data);
});
},
I'm the admin and I'm adding accounts into my site. I would like it if I can add an account without being signed out and signed into the new account. Any way i can avoid this?
Update 20161110 - original answer below
Also, check out this answer for a different approach.
Original answer
This is actually possible.
But not directly, the way to do it is to create a second auth reference and use that to create users:
var config = {apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com"};
var secondaryApp = firebase.initializeApp(config, "Secondary");
secondaryApp.auth().createUserWithEmailAndPassword(em, pwd).then(function(firebaseUser) {
console.log("User " + firebaseUser.uid + " created successfully!");
//I don't know if the next statement is necessary
secondaryApp.auth().signOut();
});
If you don't specify which firebase connection you use for an operation it will use the first one by default.
Source for multiple app references.
EDIT
For the actual creation of a new user, it doesn't matter that there is nobody or someone else than the admin, authenticated on the second auth reference because for creating an account all you need is the auth reference itself.
The following hasn't been tested but it is something to think about
The thing you do have to think about is writing data to firebase. Common practice is that users can edit/update their own user info so when you use the second auth reference for writing this should work. But if you have something like roles or permissions for that user make sure you write that with the auth reference that has the right permissions. In this case, the main auth is the admin and the second auth is the newly created user.
Update 20161108 - original answer below
Firebase just released its firebase-admin SDK, which allows server-side code for this and other common administrative use-cases. Read the installation instructions and then dive into the documentation on creating users.
original answer
This is currently not possible. Creating an Email+Password user automatically signs that new user in.
I just created a Firebase Function that triggers when a Firestore document is Created (with rules write-only to admin user). Then use admin.auth().createUser() to create the new user properly.
export const createUser = functions.firestore
.document('newUsers/{userId}')
.onCreate(async (snap, context) => {
const userId = context.params.userId;
const newUser = await admin.auth().createUser({
disabled: false,
displayName: snap.get('displayName'),
email: snap.get('email'),
password: snap.get('password'),
phoneNumber: snap.get('phoneNumber')
});
// You can also store the new user in another collection with extra fields
await admin.firestore().collection('users').doc(newUser.uid).set({
uid: newUser.uid,
email: newUser.email,
name: newUser.displayName,
phoneNumber: newUser.phoneNumber,
otherfield: snap.get('otherfield'),
anotherfield: snap.get('anotherfield')
});
// Delete the temp document
return admin.firestore().collection('newUsers').doc(userId).delete();
});
You can Algo use functions.https.onCall()
exports.createUser= functions.https.onCall((data, context) => {
const uid = context.auth.uid; // Authorize as you want
// ... do the same logic as above
});
calling it.
const createUser = firebase.functions().httpsCallable('createUser');
createUser({userData: data}).then(result => {
// success or error handling
});
Swift 5: Simple Solution
First store the current user in a variable called originalUser
let originalUser = Auth.auth().currentUser
Then, in the completion handler of creating a new user, use the updateCurrentUser method to restore the original user
Auth.auth().updateCurrentUser(originalUser, completion: nil)
Here is a simple solution using web SDKs.
Create a cloud function (https://firebase.google.com/docs/functions)
import admin from 'firebase-admin';
import * as functions from 'firebase-functions';
const createUser = functions.https.onCall((data) => {
return admin.auth().createUser(data)
.catch((error) => {
throw new functions.https.HttpsError('internal', error.message)
});
});
export default createUser;
Call this function from your app
import firebase from 'firebase/app';
const createUser = firebase.functions().httpsCallable('createUser');
createUser({ email, password })
.then(console.log)
.catch(console.error);
Optionally, you can set user document information using the returned uid.
createUser({ email, password })
.then(({ data: user }) => {
return database
.collection('users')
.doc(user.uid)
.set({
firstname,
lastname,
created: new Date(),
});
})
.then(console.log)
.catch(console.error);
I got André's very clever workaround working in Objective-C using the Firebase iOS SDK:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"GoogleService-Info" ofType:#"plist"];
FIROptions *secondaryAppOptions = [[FIROptions alloc] initWithContentsOfFile:plistPath];
[FIRApp configureWithName:#"Secondary" options:secondaryAppOptions];
FIRApp *secondaryApp = [FIRApp appNamed:#"Secondary"];
FIRAuth *secondaryAppAuth = [FIRAuth authWithApp:secondaryApp];
[secondaryAppAuth createUserWithEmail:user.email
password:user.password
completion:^(FIRUser * _Nullable user, NSError * _Nullable error) {
[secondaryAppAuth signOut:nil];
}];
Update for Swift 4
I have tried a few different options to create multiple users from a single account, but this is by far the best and easiest solution.
Original answer by Nico
First Configure firebase in your AppDelegate.swift file
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FirebaseApp.configure(name: "CreatingUsersApp", options: FirebaseApp.app()!.options)
return true
}
Add the following code to action where you are creating the accounts.
if let secondaryApp = FirebaseApp.app(name: "CreatingUsersApp") {
let secondaryAppAuth = Auth.auth(app: secondaryApp)
// Create user in secondary app.
secondaryAppAuth.createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
print(error!)
} else {
//Print created users email.
print(user!.email!)
//Print current logged in users email.
print(Auth.auth().currentUser?.email ?? "default")
try! secondaryAppAuth.signOut()
}
}
}
}
You can use firebase function for add users.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const cors = require('cors')({
origin: true,
});
exports.AddUser = functions.https.onRequest(( req, res ) => {
// Grab the text parameter.
cors( req, res, () => {
let email = req.body.email;
let passwd = req.body.passwd;
let role = req.body.role;
const token = req.get('Authorization').split('Bearer ')[1];
admin.auth().verifyIdToken(token)
.then(
(decoded) => {
// return res.status(200).send( decoded )
return creatUser(decoded);
})
.catch((err) => {
return res.status(401).send(err)
});
function creatUser(user){
admin.auth().createUser({
email: email,
emailVerified: false,
password: passwd,
disabled: false
})
.then((result) => {
console.log('result',result);
return res.status(200).send(result);
}).catch((error) => {
console.log(error.message);
return res.status(400).send(error.message);
})
}
});
});
CreateUser(){
//console.log('Create User')
this.submitted = true;
if (this.myGroup.invalid) {
return;
}
let Email = this.myGroup.value.Email;
let Passwd = this.myGroup.value.Passwd;
let Role = 'myrole';
let TechNum = this.myGroup.value.TechNum;
let user = JSON.parse(localStorage.getItem('user'));
let role = user.role;
let AdminUid = user.uid;
let authToken = user.stsTokenManager.accessToken;
let httpHeaders = new HttpHeaders().set('Authorization', 'Bearer ' + authToken);
let options = { headers: httpHeaders };
let params = { email:Email,passwd:Passwd,role:Role };
this.httpClient.post('https://us-central1-myproject.cloudfunctions.net/AddUser', params, options)
.subscribe( val => {
//console.log('Response from cloud function', val );
let createdUser:any = val;
//console.log(createdUser.uid);
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${createdUser.uid}`);
const userUpdate = {
uid: createdUser.uid,
email: createdUser.email,
displayName: null,
photoURL: null,
emailVerified: createdUser.emailVerified,
role: Role,
TechNum:TechNum,
AccountAccess:this.AccountAccess,
UserStatus:'open',
OwnerUid:AdminUid,
OwnerUidRole:role,
RootAccountAccess:this.RootAccountAccess
}
userRef.set(userUpdate, {
merge: false
});
this.toastr.success('Success, user add','Success');
this.myGroup.reset();
this.submitted = false;
},
err => {
console.log('HTTP Error', err.error)
this.toastr.error(err.error,'Error')
},
() => console.log('HTTP request completed.')
);
}
On the web, this is due to unexpected behavior when you call createUserWithEmailAndPassword out of the registration context; e.g. inviting a new user to your app by creating a new user account.
Seems like, createUserWithEmailAndPassword method triggers a new refresh token and user cookies are updated too. (This side-effect is not documented)
Here is a workaround for Web SDK:
After creating the new user;
firebase.auth().updateCurrentUser (loggedInUser.current)
provided that you initiate loggedInUser with the original user beforehand.
Hey i had similar problem ,trying to create users through admin , as it is not possible to signUp user without signIn ,I created a work around ,adding it below with steps
Instead of signup create a node in firebase realtime db with email as key (firebase do not allow email as key so I have created a function to generate key from email and vice versa, I will attach the functions below)
Save a initial password field while saving user (can even hash it with bcrypt or something, if you prefer though it will be used one time only)
Now Once user try to login check if any node with that email (generate key from email) exist in the db and if so then match the password provided.
If the password matched delete the node and do authSignUpWithEmailandPassword with provided credentials.
User is registered successfully
//Sign In
firebaseDB.child("users").once("value", (snapshot) => {
const users = snapshot.val();
const userKey = emailToKey(data.email);
if (Object.keys(users).find((key) => key === userKey)) {
setError("user already exist");
setTimeout(() => {
setError(false);
}, 2000);
setLoading(false);
} else {
firebaseDB
.child(`users`)
.child(userKey)
.set({ email: data.email, initPassword: data.password })
.then(() => setLoading(false))
.catch(() => {
setLoading(false);
setError("Error in creating user please try again");
setTimeout(() => {
setError(false);
}, 2000);
});
}
});
//Sign Up
signUp = (data, setLoading, setError) => {
auth
.createUserWithEmailAndPassword(data.email, data.password)
.then((res) => {
const userDetails = {
email: res.user.email,
id: res.user.uid,
};
const key = emailToKey(data.email);
app
.database()
.ref(`users/${key}`)
.remove()
.then(() => {
firebaseDB.child("users").child(res.user.uid).set(userDetails);
setLoading(false);
})
.catch(() => {
setLoading(false);
setError("error while registering try again");
setTimeout(() => setError(false), 4000);
});
})
.catch((err) => {
setLoading(false);
setError(err.message);
setTimeout(() => setError(false), 4000);
});
};
//Function to create a valid firebase key from email and vice versa
const emailToKey = (email) => {
//firebase do not allow ".", "#", "$", "[", or "]"
let key = email;
key = key.replace(".", ",0,");
key = key.replace("#", ",1,");
key = key.replace("$", ",2,");
key = key.replace("[", ",3,");
key = key.replace("]", ",4,");
return key;
};
const keyToEmail = (key) => {
let email = key;
email = email.replace(",0,", ".");
email = email.replace(",1,", "#");
email = email.replace(",2,", "$");
email = email.replace(",3,", "[");
email = email.replace(",4,", "]");
return email;
};
If you want to do it in your front end create a second auth reference use it to create other users and sign out and delete that reference. If you do it this way you won't be signed out when creating a new user and you won't get the error that the default firebase app already exists.
const createOtherUser =()=>{
var config = {
//your firebase config
};
let secondaryApp = firebase.initializeApp(config, "secondary");
secondaryApp.auth().createUserWithEmailAndPassword(email, password).then((userCredential) => {
console.log(userCredential.user.uid);
}).then(secondaryApp.auth().signOut()
)
.then(secondaryApp.delete()
)
}
Update 19.05.2022 - using #angular/fire (latest available = v.7.3.0)
If you are not using firebase directly in your app, but use e.g. #angular/fire for auth purposes only, you can use the same approach as suggested earlier as follows with the #angular/fire library:
import { Auth, getAuth, createUserWithEmailAndPassword } from '#angular/fire/auth';
import { deleteApp, initializeApp } from '#angular/fire/app';
import { firebaseConfiguration } from '../config/app.config'; // <-- Your project's configuration here.
const tempApp = initializeApp(firebaseConfiguration, "tempApp");
const tempAppAuth = getAuth(tempApp);
await createUserWithEmailAndPassword(tempAppAuth, email, password)
.then(async (newUser) => {
resolve( () ==> {
// Do something, e.g. add user info to database
});
})
.catch(error => reject(error))
.finally( () => {
tempAppAuth.signOut()
.then( () => deleteApp(tempApp));
});
The Swift version:
FIRApp.configure()
// Creating a second app to create user without logging in
FIRApp.configure(withName: "CreatingUsersApp", options: FIRApp.defaultApp()!.options)
if let secondaryApp = FIRApp(named: "CreatingUsersApp") {
let secondaryAppAuth = FIRAuth(app: secondaryApp)
secondaryAppAuth?.createUser(...)
}
Here is a Swift 3 adaptaion of Jcabrera's answer :
let bundle = Bundle.main
let path = bundle.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FIROptions.init(contentsOfFile: path)
FIRApp.configure(withName: "Secondary", options: options!)
let secondary_app = FIRApp.init(named: "Secondary")
let second_auth = FIRAuth(app : secondary_app!)
second_auth?.createUser(withEmail: self.username.text!, password: self.password.text!)
{
(user,error) in
print(user!.email!)
print(FIRAuth.auth()?.currentUser?.email ?? "default")
}
If you are using Polymer and Firebase (polymerfire) see this answer: https://stackoverflow.com/a/46698801/1821603
Essentially you create a secondary <firebase-app> to handle the new user registration without affecting the current user.
Android solution (Kotlin):
1.You need FirebaseOptions BUILDER(!) for setting api key, db url, etc., and don't forget to call build() at the end
2.Make a secondary auth variable by calling FirebaseApp.initializeApp()
3.Get instance of FirebaseAuth by passing your newly created secondary auth, and do whatever you want (e.g. createUser)
// 1. you can find these in your project settings under general tab
val firebaseOptionsBuilder = FirebaseOptions.Builder()
firebaseOptionsBuilder.setApiKey("YOUR_API_KEY")
firebaseOptionsBuilder.setDatabaseUrl("YOUR_DATABASE_URL")
firebaseOptionsBuilder.setProjectId("YOUR_PROJECT_ID")
firebaseOptionsBuilder.setApplicationId("YOUR_APPLICATION_ID") //not sure if this one is needed
val firebaseOptions = firebaseOptionsBuilder.build()
// indeterminate progress dialog *ANKO*
val progressDialog = indeterminateProgressDialog(resources.getString(R.string.progressDialog_message_registering))
progressDialog.show()
// 2. second auth created by passing the context, firebase options and a string for secondary db name
val newAuth = FirebaseApp.initializeApp(this#ListActivity, firebaseOptions, Constants.secondary_db_auth)
// 3. calling the create method on our newly created auth, passed in getInstance
FirebaseAuth.getInstance(newAuth).createUserWithEmailAndPassword(email!!, password!!)
.addOnCompleteListener { it ->
if (it.isSuccessful) {
// 'it' is a Task<AuthResult>, so we can get our newly created user from result
val newUser = it.result.user
// store wanted values on your user model, e.g. email, name, phonenumber, etc.
val user = User()
user.email = email
user.name = name
user.created = Date().time
user.active = true
user.phone = phone
// set user model on /db_root/users/uid_of_created_user/, or wherever you want depending on your structure
FirebaseDatabase.getInstance().reference.child(Constants.db_users).child(newUser.uid).setValue(user)
// send newly created user email verification link
newUser.sendEmailVerification()
progressDialog.dismiss()
// sign him out
FirebaseAuth.getInstance(newAuth).signOut()
// DELETE SECONDARY AUTH! thanks, Jimmy :D
newAuth.delete()
} else {
progressDialog.dismiss()
try {
throw it.exception!!
// catch exception for already existing user (e-mail)
} catch (e: FirebaseAuthUserCollisionException) {
alert(resources.getString(R.string.exception_FirebaseAuthUserCollision), resources.getString(R.string.alertDialog_title_error)) {
okButton {
isCancelable = false
}
}.show()
}
}
}
For Android, i suggest a simpler way to do it, without having to provide api key, application id...etc by hand by just using the FirebaseOptions of the default instance.
val firebaseDefaultApp = Firebase.auth.app
val signUpAppName = firebaseDefaultApp.name + "_signUp"
val signUpApp = try {
FirebaseApp.initializeApp(
context,
firebaseDefaultApp.options,
signUpAppName
)
} catch (e: IllegalStateException) {
// IllegalStateException is throw if an app with the same name has already been initialized.
FirebaseApp.getInstance(signUpAppName)
}
// Here is the instance you can use to sign up without triggering auth state on the default Firebase.auth
val signUpFirebaseAuth = Firebase.auth(signUpApp)
How to use ?
signUpFirebaseAuth
.createUserWithEmailAndPassword(email, password)
.addOnSuccessListener {
// Optional, you can send verification email here if you need
// As soon as the sign up with sign in is over, we can sign out the current user
firebaseAuthSignUp.signOut()
}
.addOnFailureListener {
// Log
}
My solution to this question is to store the User Name/Email and password in a static class and then add a new user log out the new user and immediately log in as the admin user(id pass you saved). Works like a charm for me :D
This is a version for Kotlin:
fun createUser(mail: String, password: String) {
val opts = FirebaseOptions.fromResource(requireContext())
if (opts == null) return
val app = Firebase.initialize(requireContext(), opts, "Secondary")
FirebaseAuth.getInstance(app)
.createUserWithEmailAndPassword(mail, password)
.addOnSuccessListener {
app.delete()
doWhateverWithAccount(it)
}.addOnFailureListener {
app.delete()
showException(it)
}
}
It uses the configuration from your default Firebase application instance, just under a different name.
It also deletes the newly created instance afterwards, so you can call this multiple times without any exception about already existing Secondary application.

Firebase Function onDelete from database and storage

I want to be able to delete a folder in firebase storage while onDelete in functions is triggered.
here is my firebase node code, once deleted, it will trigger functions to delete the corresponding folder in firebase storage. I am allowing user to delete their message conversion that includes images. I was able to delete the folder without using the {friendId} but {friendId} is needed in case the user have conversions with two different users.
My Firebase storage is as follow
messages_image_from_friends/
iLJ6nGJodeat2HRi5Q2xdTUmZnw2/
MXGCZv96aVUkSHZeU8kNTZqTQ0n2/
image.png
and Firebase Functions
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firebase = admin.initializeApp();
exports.deletePhotos = functions.database.ref('/messagesFriends/{userId}/{friendId}')
.onDelete((snap, context) => {
const { userId } = context.params;
<---- const { friendId } = context.params.friendId; ????? ---- >
const bucket = firebase.storage().bucket();
return bucket.deleteFiles({
prefix: `messages_image_from_friends/${userId}/{friendId}`
}, function(err) {
if (err) {
console.log(err);
} else {
console.log(`All the Firebase Storage files in
messages_image_from_friends/${userId}/{friendId} have been deleted`);
}
});
});
Log states that {friendId} is undefined. How do i get {friendId} from exports into prefix.
I have tried "snapshot" and "then()" but do not really know how to implement it as I am new to functions. Please help.
Update!!! 9/12/2020
I was able to get this working by changing onDelete to functions.https.onCall to use hashmap instead.. hope this help others
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firebase = admin.initializeApp();
exports.deletePhotos = functions.https.onCall((data, context) => {
const userId = data.userId;
const friendId = data.friendId;
console.log(userId, friendId);
const bucket = firebase.storage().bucket();
return bucket.deleteFiles({
prefix: `messages_image_from_friends/`+userId+`/`+friendId+`/`
}, function(err) {
if (err) {
console.log(err);
} else {
console.log(`messages_image_from_friends/`+userId+`/`+friendId);
}
});
// return {response:"This means success"};
});
and the code to call the function from your android app
private FirebaseFunctions mFunctions;
protected void onCreate(Bundle savedInstanceState) {
mFunctions = FirebaseFunctions.getInstance();
////String userId is current firebase user id
////String friendId is from getIntent(), etc
deletePhotos(userId, friendId);
}
private Task<String> deletePhotos(String userId, String friendId) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("userId", userId);
data.put("friendId", friendId);
return mFunctions
.getHttpsCallable("deletePhotos")
.call(data)
.continueWith(new Continuation<HttpsCallableResult,
String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult>
task) throws Exception {
// This continuation runs on either success or
failure, but if the task
// has failed then getResult() will throw an
Exception which will be
// propagated down.
String result = (String)
task.getResult().getData();
return result;
}
});
}
MAKE SURE YOU MAKE A NEW FIREBASE INIT FOLDER..
I MADE THE MISTAKE OF REDEPLOYING THIS DIRECTLY IN CLOUD FUNCTION CONSOLE WHILE IT WAS CONNECTED AS onDelete and IT WAS UPDATING THE index.js ONLY INSTEAD OF THE WHOLE FUNCTION FOLDER. SO DON'T DO WHAT I DID BECAUSE YOU WILL GET A TypeError: Cannot read property 'origin' of undefined at /srv/node_modules/cors/lib/
HOPE THIS HELPS OTHERS!!!
Update 9/18/20
I was able to make it work with onDelete with this
'use-strict'
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firebase = admin.initializeApp();
exports.deletePhotos =
functions.database.ref('/messagesFriends/{userId}/{friendId}')
.onDelete((snap, context) => {
const userId = context.params.userId;
const friendId = context.params.friendId;
const bucket = firebase.storage().bucket();
console.log(userId + ' ' + friendId + " found");
return bucket.deleteFiles({
prefix: `messages_image_from_friends/`+userId+`/`+friendId
}, function(err) {
if (err) {
console.log(`messages_image_from_friends/`+userId+`/`+friendId + `
remove error`);
} else {
console.log(`messages_image_from_friends/`+userId+`/`+friendId + `
removed`);
}
});
});
context.params is an object whose properties are populated with each of the wildcards from the trigger path. You're not using it correctly.
const userId = context.params.userId;
const friendId = context.params.friendId;
I suggest reviewing the documentation for database triggers, especially the part on specifying the path:
You can specify a path component as a wildcard by surrounding it with curly brackets; ref('foo/{bar}') matches any child of /foo. The values of these wildcard path components are available within the EventContext.params object of your function. In this example, the value is available as event.params.bar.

Firebase real time database - subscription .on() does not trigger on client

I have a collection of notes in my Firebase realtime database.
My code is set to subscribe to modifications in the /notes path of the database.
When updating a note in the Firebase web console my client gets the updates, but when another client updates the note via ref.update() the data is not pushed to my client.
When updating in the Firebase web console, I am performing the update from one browser and watching the update in another browser.
How can this be? See code below.
export const startSubscribeNotes = () => {
return (dispatch, getState) => {
const uid = getState().auth.uid
const dbPath = 'notes'
database.ref(`${dbPath}`)
.orderByChild(`access/members/${uid}`)
.equalTo(true)
.on('value', (snapshot) => {
console.log('notes .on()')
let updatednotes = []
console.log('Existing notes', existingnotes)
snapshot.forEach( (data) => {
const note = {
id: data.key,
...data.val()
}
updatednotes.push(note)
})
dispatch(setnotes(notes))
})
}
}
What I have noticed is that if I change my access rules in the Firebase database I am able to get the updates, but can no longer restrict read access.
"notes": {
//entry-level access
".read": "
auth.uid !== null //&& query.equalTo === auth.uid
",
I can then loop all notes and collect the ones I should only be able to read.
const uid = getState().auth.uid
database.ref('notes')
.on('value', (snapshot) => {
const notes = []
snapshot.forEach((data) => {
const noteData = data.val()
if(noteData.access){
const authorArray = (noteData.access.author) ? [noteData.access.author] : []
const members = (noteData.access.members) ? noteData.access.members : {}
const membersArray = Object.keys(members)
const allUsers = authorArray.concat(membersArray)
const accessGranted = allUsers.includes(uid)
if(accessGranted){
const note = {
id: data.key,
...data.val()
}
notes.push(note)
}
}
})
if(notes.length > 0){ dispatch(setNotes(notes)) } //Dispatch if not empty
})
How can I apply the access rules and still get the updates pushed to my client?
I have checked that all clients can write to the database at /notes
Any help much appreciated!

Categories

Resources