You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
6.5 KiB
205 lines
6.5 KiB
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.serialExportUsers = exports.validateOptions = void 0;
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const apiv2_1 = require("./apiv2");
|
|
const error_1 = require("./error");
|
|
const api_1 = require("./api");
|
|
const utils = require("./utils");
|
|
const apiClient = new apiv2_1.Client({
|
|
urlPrefix: api_1.googleOrigin,
|
|
});
|
|
const EXPORTED_JSON_KEYS = [
|
|
"localId",
|
|
"email",
|
|
"emailVerified",
|
|
"passwordHash",
|
|
"salt",
|
|
"displayName",
|
|
"photoUrl",
|
|
"lastLoginAt",
|
|
"createdAt",
|
|
"phoneNumber",
|
|
"disabled",
|
|
"customAttributes",
|
|
];
|
|
const EXPORTED_JSON_KEYS_RENAMING = {
|
|
lastLoginAt: "lastSignedInAt",
|
|
};
|
|
const EXPORTED_PROVIDER_USER_INFO_KEYS = [
|
|
"providerId",
|
|
"rawId",
|
|
"email",
|
|
"displayName",
|
|
"photoUrl",
|
|
];
|
|
const PROVIDER_ID_INDEX_MAP = new Map([
|
|
["google.com", 7],
|
|
["facebook.com", 11],
|
|
["twitter.com", 15],
|
|
["github.com", 19],
|
|
]);
|
|
function escapeComma(str) {
|
|
if (str.includes(",")) {
|
|
return `"${str}"`;
|
|
}
|
|
return str;
|
|
}
|
|
function convertToNormalBase64(data) {
|
|
return data.replace(/_/g, "/").replace(/-/g, "+");
|
|
}
|
|
function addProviderUserInfo(providerInfo, arr, startPos) {
|
|
arr[startPos] = providerInfo.rawId;
|
|
arr[startPos + 1] = providerInfo.email || "";
|
|
arr[startPos + 2] = escapeComma(providerInfo.displayName || "");
|
|
arr[startPos + 3] = providerInfo.photoUrl || "";
|
|
}
|
|
function transUserToArray(user) {
|
|
const arr = Array(27).fill("");
|
|
arr[0] = user.localId;
|
|
arr[1] = user.email || "";
|
|
arr[2] = user.emailVerified || false;
|
|
arr[3] = convertToNormalBase64(user.passwordHash || "");
|
|
arr[4] = convertToNormalBase64(user.salt || "");
|
|
arr[5] = escapeComma(user.displayName || "");
|
|
arr[6] = user.photoUrl || "";
|
|
for (let i = 0; i < (!user.providerUserInfo ? 0 : user.providerUserInfo.length); i++) {
|
|
const providerInfo = user.providerUserInfo[i];
|
|
if (providerInfo) {
|
|
const providerIndex = PROVIDER_ID_INDEX_MAP.get(providerInfo.providerId);
|
|
if (providerIndex) {
|
|
addProviderUserInfo(providerInfo, arr, providerIndex);
|
|
}
|
|
}
|
|
}
|
|
arr[23] = user.createdAt;
|
|
arr[24] = user.lastLoginAt;
|
|
arr[25] = user.phoneNumber;
|
|
arr[26] = user.disabled;
|
|
arr[27] = user.customAttributes;
|
|
return arr;
|
|
}
|
|
function transUserJson(user) {
|
|
const newUser = {};
|
|
const pickedUser = {};
|
|
for (const k of EXPORTED_JSON_KEYS) {
|
|
pickedUser[k] = user[k];
|
|
}
|
|
for (const [key, value] of Object.entries(pickedUser)) {
|
|
const newKey = EXPORTED_JSON_KEYS_RENAMING[key] || key;
|
|
newUser[newKey] = value;
|
|
}
|
|
if (newUser.passwordHash) {
|
|
newUser.passwordHash = convertToNormalBase64(newUser.passwordHash);
|
|
}
|
|
if (newUser.salt) {
|
|
newUser.salt = convertToNormalBase64(newUser.salt);
|
|
}
|
|
if (user.providerUserInfo) {
|
|
newUser.providerUserInfo = [];
|
|
for (const providerInfo of user.providerUserInfo) {
|
|
if (PROVIDER_ID_INDEX_MAP.has(providerInfo.providerId)) {
|
|
const picked = {};
|
|
for (const k of EXPORTED_PROVIDER_USER_INFO_KEYS) {
|
|
picked[k] = providerInfo[k];
|
|
}
|
|
newUser.providerUserInfo.push(picked);
|
|
}
|
|
}
|
|
}
|
|
return newUser;
|
|
}
|
|
function validateOptions(options, fileName) {
|
|
const exportOptions = {};
|
|
if (fileName === undefined) {
|
|
throw new error_1.FirebaseError("Must specify data file");
|
|
}
|
|
const extName = path.extname(fileName.toLowerCase());
|
|
if (extName === ".csv") {
|
|
exportOptions.format = "csv";
|
|
}
|
|
else if (extName === ".json") {
|
|
exportOptions.format = "json";
|
|
}
|
|
else if (options.format) {
|
|
const format = options.format.toLowerCase();
|
|
if (format === "csv" || format === "json") {
|
|
exportOptions.format = format;
|
|
}
|
|
else {
|
|
throw new error_1.FirebaseError("Unsupported data file format, should be csv or json");
|
|
}
|
|
}
|
|
else {
|
|
throw new error_1.FirebaseError("Please specify data file format in file name, or use `format` parameter");
|
|
}
|
|
return exportOptions;
|
|
}
|
|
exports.validateOptions = validateOptions;
|
|
function createWriteUsersToFile() {
|
|
let jsonSep = "";
|
|
return (userList, format, writeStream) => {
|
|
userList.map((user) => {
|
|
if (user.passwordHash && user.version !== 0) {
|
|
delete user.passwordHash;
|
|
delete user.salt;
|
|
}
|
|
if (format === "csv") {
|
|
writeStream.write(transUserToArray(user).join(",") + "," + os.EOL, "utf8");
|
|
}
|
|
else {
|
|
writeStream.write(jsonSep + JSON.stringify(transUserJson(user), null, 2), "utf8");
|
|
jsonSep = "," + os.EOL;
|
|
}
|
|
});
|
|
};
|
|
}
|
|
async function serialExportUsers(projectId, options) {
|
|
var _a;
|
|
if (!options.writeUsersToFile) {
|
|
options.writeUsersToFile = createWriteUsersToFile();
|
|
}
|
|
const postBody = {
|
|
targetProjectId: projectId,
|
|
maxResults: options.batchSize,
|
|
};
|
|
if (options.nextPageToken) {
|
|
postBody.nextPageToken = options.nextPageToken;
|
|
}
|
|
if (!options.timeoutRetryCount) {
|
|
options.timeoutRetryCount = 0;
|
|
}
|
|
try {
|
|
const ret = await apiClient.post("/identitytoolkit/v3/relyingparty/downloadAccount", postBody, {
|
|
skipLog: { resBody: true },
|
|
});
|
|
options.timeoutRetryCount = 0;
|
|
const userList = ret.body.users;
|
|
if (userList && userList.length > 0) {
|
|
options.writeUsersToFile(userList, options.format, options.writeStream);
|
|
utils.logSuccess("Exported " + userList.length + " account(s) successfully.");
|
|
if (!ret.body.nextPageToken) {
|
|
return;
|
|
}
|
|
options.nextPageToken = ret.body.nextPageToken;
|
|
return serialExportUsers(projectId, options);
|
|
}
|
|
}
|
|
catch (err) {
|
|
if (((_a = err.original) === null || _a === void 0 ? void 0 : _a.code) === "ETIMEDOUT") {
|
|
options.timeoutRetryCount++;
|
|
if (options.timeoutRetryCount > 5) {
|
|
return err;
|
|
}
|
|
return serialExportUsers(projectId, options);
|
|
}
|
|
if (err instanceof error_1.FirebaseError) {
|
|
throw err;
|
|
}
|
|
else {
|
|
throw new error_1.FirebaseError(`Failed to export accounts: ${err}`, { original: err });
|
|
}
|
|
}
|
|
}
|
|
exports.serialExportUsers = serialExportUsers;
|