【发布时间】:2020-03-09 15:18:35
【问题描述】:
我有这个脚本可以更改我们用于测试目的的现有 FireBase 身份验证用户的所有密码,以便将用户重置为“干净”状态。
var admin = require('firebase-admin');
// Input args
var jsonKey = process.argv[2];
var projectId = process.argv[3];
var newPassword = process.argv[4];
var serviceAccount = require(jsonKey);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://" + projectId + ".firebaseio.com"
});
// FIXME Only for debug purposes
var index = 1;
/**
* https://firebase.google.com/docs/auth/admin/manage-users#update_a_user
*/
function nextUser(uid, timeout) {
setTimeout(() => {
admin.auth().updateUser(uid, {
password : newPassword
})
.then(function() {
console.log(index++ + ') Successfully updated user', uid);
})
.catch(function(error) {
console.log(index++ + ') Error updating user:', error);
});
}, timeout);
}
/**
* https://firebase.google.com/docs/auth/admin/manage-users#list_all_users
* https://stackoverflow.com/a/58028558
*/
function listAllUsers(nextPageToken) {
let timeout = 0;
admin.auth().listUsers(1000, nextPageToken)
.then(function (listUsersResult) {
console.log("Retrieved " + listUsersResult.users.length + " users");
listUsersResult.users.forEach(function (userRecord) {
timeout = timeout + 100
nextUser(userRecord.uid, timeout)
});
if (listUsersResult.pageToken) {
listAllUsers(listUsersResult.pageToken);
}
})
.catch(function (error) {
console.log('Error listing users:', error);
});
}
listAllUsers();
脚本从本地机器启动
node <filename.js> <path of jsonkey> <projectid> <new password>
正如您所见,迭代器运行时超时,这是在我遇到“更新帐户信息的超出配额”问题后需要的,我尝试使用this answer 解决该问题
脚本运行良好,所有用户都被正确处理(我为这种调试添加了一个顺序计数器)并且没有抛出“配额”错误。问题是在脚本结束时,shell 提示符没有退出,进程仍然挂起,我需要一个CTRL+C 来杀死它。除了这种行为令人讨厌的部分之外,这还阻止了一个更广泛的脚本,该脚本调用这个脚本并且不会继续执行下一行命令。
看来setTimeout函数全部执行完毕后,主进程并没有自动关闭。
我该如何解决这个问题?
编辑
我尝试了不同的方法:
- https://repl.it/repls/RoundedAnchoredDos,不行,firebase-app在所有用户处理完之前就被删除了
- https://repl.it/repls/DelightfulUnfoldedHexagon,工作,它是一个同步版本,一次处理一个用户,在最后一个用户 firebase 被删除后
第二个版本有效,但速度很慢。所以我从初始脚本开始,我添加了一个“监视器”,等待所有用户完成(使用非常脏的计数器)并在最后关闭脚本
var admin = require('firebase-admin');
// Input args
var jsonKey = process.argv[2];
var projectId = process.argv[3];
var newPassword = process.argv[4];
var serviceAccount = require(jsonKey);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://" + projectId + ".firebaseio.com"
});
// Index to count all users that have been queued for processing
var queued = 0;
// Index to count all user that have completed the task
var completed = 0;
/**
* https://firebase.google.com/docs/auth/admin/manage-users#update_a_user
*/
function nextUser(uid, timeout) {
setTimeout(() => {
/* ------ Process User Implementation ------ */
admin.auth().updateUser(uid, {
password : newPassword
})
.then(function() {
console.log(++completed + ') Successfully updated user ', uid);
})
.catch(function(error) {
console.log(++completed + ') Error updating user:', error);
});
/* ------ Process User Implementation ------ */
}, timeout);
queued++;
}
/**
* https://firebase.google.com/docs/auth/admin/manage-users#list_all_users
* https://stackoverflow.com/a/58028558
*/
function listAllUsers(nextPageToken) {
let timeout = 0;
admin.auth().listUsers(1000, nextPageToken)
.then(function (listUsersResult) {
console.log("Retrieved " + listUsersResult.users.length + " users");
listUsersResult.users.forEach(function (userRecord) {
timeout = timeout + 100
nextUser(userRecord.uid, timeout)
});
if (listUsersResult.pageToken) {
listAllUsers(listUsersResult.pageToken);
} else {
waitForEnd();
}
})
.catch(function (error) {
console.log('Error listing users:', error);
});
}
function waitForEnd() {
if(completed < queued) {
console.log("> " + completed + "/" + queued + " completed, waiting...");
setTimeout(waitForEnd, 5000);
} else {
console.log("> " + completed + "/" + queued + " completed, exiting...");
admin.app().delete();
}
}
listAllUsers();
有效,速度很快。对我来说足够了,因为它是一个测试脚本????
【问题讨论】:
-
我不知道 Firebase,但有什么方法需要关闭数据库连接吗?当您认为代码完成时,node.js 不会自动退出这一事实意味着某些网络连接、计时器或其他异步操作仍处于打开状态或待处理状态。我所看到的可能是您的应用程序中可能是一个开放的数据库连接。
-
我认为如果你使用 async/await 语法和标准的“sleep”机制来分隔调用,这对你自己来说会更容易。当您的循环完成后,您可以在最后一次更新完成后调用 admin.app().delete() 来关闭 admin SDK。标准循环还可以防止可能的内存不足情况同时启动过多的这些延迟调用。
-
@DougStevenson 我按照你的建议尝试了异步/等待,这是当前代码:repl.it/repls/RoundedAnchoredDos 我尝试了很多次,但我无法找到一个完全有效的解决方案。首先,我必须删除所有内部方法以尊重睡眠(现在所有代码都在 main 方法中)。现在的问题是在处理所有用户之前调用
admin.app().delete(); -
@DougStevenson 这是一个更“同步”的版本,它不需要睡眠,因为所有调用都是链接的:repl.it/repls/DelightfulUnfoldedHexagon 当然现在非常慢,因为每次只进行 1 个调用。但至少它有效,当用户完成时,该过程正确结束。
标签: javascript node.js firebase firebase-authentication firebase-admin