firebase-tools
Version:
Command-Line Interface for Firebase
131 lines (130 loc) • 7.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CRASHLYTICS_TELEMETRY_SERVICE = exports.CRASHLYTICS_TELEMETRY_RESOURCE_TYPE = exports.CRASHLYTICS_TELEMETRY_SINK_ID = exports.CRASHLYTICS_TELEMETRY_BUCKET_ID = void 0;
exports.onboardCrashlyticsWeb = onboardCrashlyticsWeb;
const ensureApiEnabled_1 = require("../ensureApiEnabled");
const error_1 = require("../error");
const experiments = require("../experiments");
const cloudbilling_1 = require("../gcp/cloudbilling");
const cloudlogging_1 = require("../gcp/cloudlogging");
const alerts_1 = require("./alerts");
const firebasetelemetry_1 = require("./firebasetelemetry");
const types_1 = require("./types");
const utils_1 = require("../utils");
const apikeys_1 = require("../gcp/apikeys");
const cloudtrace_1 = require("../gcp/cloudtrace");
const apps_1 = require("../management/apps");
const logger_1 = require("../logger");
const prompt_1 = require("../prompt");
const requireAuth_1 = require("../requireAuth");
exports.CRASHLYTICS_TELEMETRY_BUCKET_ID = "firebase-telemetry";
exports.CRASHLYTICS_TELEMETRY_SINK_ID = "firebase-telemetry-routing";
exports.CRASHLYTICS_TELEMETRY_RESOURCE_TYPE = "firebasetelemetry.googleapis.com/App";
exports.CRASHLYTICS_TELEMETRY_SERVICE = "firebasetelemetry.googleapis.com";
async function resolveAuthenticatedUserEmail(options) {
if (options.user?.email) {
return options.user.email;
}
try {
return (await (0, requireAuth_1.requireAuth)(options)) ?? undefined;
}
catch (err) {
logger_1.logger.debug(`[crashlytics] Failed to resolve authenticated user email: ${(0, error_1.getErrMsg)(err)}`);
return undefined;
}
}
async function onboardCrashlyticsWeb(projectId, appId, options = {}) {
const billingEnabled = await (0, cloudbilling_1.checkBillingEnabled)(projectId);
if (!billingEnabled && options.nonInteractive) {
throw new error_1.FirebaseError(`Crashlytics requires the Blaze plan, but project ${projectId} is not on the Blaze plan. ` +
`Please visit https://console.cloud.google.com/billing/linkedaccount?project=${projectId} to upgrade your project.`);
}
else if (!billingEnabled) {
await (0, cloudbilling_1.enableBilling)(projectId, "Crashlytics");
}
(0, utils_1.logLabeledBullet)("crashlytics", "Enabling required telemetry APIs...");
const requiredApis = [
(0, ensureApiEnabled_1.ensure)(projectId, exports.CRASHLYTICS_TELEMETRY_SERVICE, "crashlytics", false),
(0, ensureApiEnabled_1.ensure)(projectId, "firebasetelemetryadmin.googleapis.com", "crashlytics", false),
];
if (experiments.isEnabled("crashlyticsWebBreadcrumbs")) {
requiredApis.push((0, ensureApiEnabled_1.ensure)(projectId, "cloudtrace.googleapis.com", "crashlytics", false));
}
await Promise.all(requiredApis);
(0, utils_1.logLabeledSuccess)("crashlytics", "Telemetry APIs enabled.");
const appConfig = await (0, apps_1.getAppConfig)(appId, apps_1.AppPlatform.WEB);
if ("apiKey" in appConfig && appConfig.apiKey) {
(0, utils_1.logLabeledBullet)("crashlytics", "Ensuring Crashlytics Telemetry API is permitted in API key restrictions...");
try {
await (0, apikeys_1.updateAppApiKeyRestriction)({
apiKey: appConfig.apiKey,
service: exports.CRASHLYTICS_TELEMETRY_SERVICE,
});
(0, utils_1.logLabeledSuccess)("crashlytics", "API key restrictions updated for Crashlytics Telemetry.");
}
catch (err) {
(0, utils_1.logLabeledWarning)("crashlytics", err instanceof Error ? err.message : String(err));
}
}
else {
(0, utils_1.logLabeledWarning)("crashlytics", `No API key found for this app. If you configure an API key later, ` +
`please rerun this command or manually add '${exports.CRASHLYTICS_TELEMETRY_SERVICE}' to its allowed APIs in the Google Cloud Console if the key is restricted.`);
}
(0, utils_1.logLabeledBullet)("crashlytics", `Setting up Cloud Logging bucket '${exports.CRASHLYTICS_TELEMETRY_BUCKET_ID}'...`);
const bucket = await (0, cloudlogging_1.createOrUpdateLogBucket)(projectId, exports.CRASHLYTICS_TELEMETRY_BUCKET_ID, "global", true);
(0, utils_1.logLabeledSuccess)("crashlytics", "Cloud Logging bucket configured.");
const destination = `logging.googleapis.com/projects/${projectId}/locations/global/buckets/${exports.CRASHLYTICS_TELEMETRY_BUCKET_ID}`;
const filter = `resource.type="${exports.CRASHLYTICS_TELEMETRY_RESOURCE_TYPE}"`;
(0, utils_1.logLabeledBullet)("crashlytics", `Setting up Cloud Logging routing sink '${exports.CRASHLYTICS_TELEMETRY_SINK_ID}'...`);
const sink = await (0, cloudlogging_1.createOrUpdateLogSink)(projectId, exports.CRASHLYTICS_TELEMETRY_SINK_ID, destination, filter);
(0, utils_1.logLabeledSuccess)("crashlytics", "Cloud Logging routing sink configured.");
if (experiments.isEnabled("crashlyticsWebBreadcrumbs")) {
(0, utils_1.logLabeledBullet)("crashlytics", "Provisioning Cloud Trace storage...");
try {
await (0, cloudtrace_1.provisionTraceStorage)(projectId);
(0, utils_1.logLabeledSuccess)("crashlytics", "Cloud Trace storage provisioned.");
}
catch (err) {
(0, utils_1.logLabeledWarning)("crashlytics", (0, error_1.getErrMsg)(err));
}
}
(0, utils_1.logLabeledBullet)("crashlytics", "Configuring Crashlytics telemetry for web app...");
const config = await (0, firebasetelemetry_1.createOrUpdateTelemetryConfig)(projectId, appId, `projects/${projectId}/locations/global/buckets/${exports.CRASHLYTICS_TELEMETRY_BUCKET_ID}`, 1);
(0, utils_1.logLabeledSuccess)("crashlytics", "Crashlytics telemetry configured successfully.");
if (!experiments.isEnabled("crashlyticsWebAlerts") || options.nonInteractive) {
return { bucket, sink, config };
}
const userEmail = await resolveAuthenticatedUserEmail(options);
if (!userEmail) {
(0, utils_1.logLabeledWarning)("crashlytics", "Unable to determine authenticated user email for alert setup. You can configure alerts later in the Firebase Console.");
return { bucket, sink, config };
}
const selectedAlerts = await (0, prompt_1.checkbox)({
message: "Which email alerts would you like to enable? (Optional)",
choices: [
{
name: "New issues (Notify when a new issue is detected)",
value: types_1.AlertType.ALERT_TYPE_NEW_ISSUE,
checked: true,
},
{
name: "Regressed issues (Notify when a closed issue reoccurs)",
value: types_1.AlertType.ALERT_TYPE_REGRESSED_ISSUE,
checked: true,
},
],
});
if (selectedAlerts.length === 0) {
return { bucket, sink, config };
}
(0, utils_1.logLabeledBullet)("crashlytics", `Setting up Crashlytics email alerts for ${userEmail}...`);
try {
const alertPolicies = await (0, alerts_1.enableAlerts)(projectId, appId, selectedAlerts, userEmail);
(0, utils_1.logLabeledSuccess)("crashlytics", "Crashlytics email alerts configured successfully.");
return { bucket, sink, config, alertPolicies };
}
catch (err) {
(0, utils_1.logLabeledWarning)("crashlytics", (0, error_1.getErrMsg)(err));
return { bucket, sink, config };
}
}