UNPKG

firebase-tools

Version:
131 lines (130 loc) • 7.47 kB
"use strict"; 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 }; } }