Remove AppConstants
To make the migration as safe as possible, in places where it was used to access environment variables that were absolutely required, I replaced it with a function that forces the caller to explicitly enumerate the environment variables they specifically expect, allows them to access them in a type-safe way, and throws if the named environment variables aren't set. (This is slightly different than the previous behaviour, where AppConstants would only log the missing variable without throwing, but I checked that all these variables should be set in production.)
This commit is contained in:
Родитель
3e6611491f
Коммит
cda0f5a570
|
@ -6,8 +6,6 @@ src/db/**/*.js
|
|||
src/emails/**/*.js
|
||||
|
||||
# TODO NEXT.JS MIGRATION:
|
||||
# These files are remnants of our Express app:
|
||||
src/appConstants.js
|
||||
|
||||
# These should be ignored anyway
|
||||
coverage/
|
||||
|
|
|
@ -43,8 +43,6 @@ const customJestConfig = {
|
|||
"<rootDir>/src/apiMocks/mockData.ts",
|
||||
"<rootDir>/src/(.+).stories.(ts|tsx)",
|
||||
"<rootDir>/.storybook/",
|
||||
// Old, pre-Next.js code assumed to be working:
|
||||
"<rootDir>/src/appConstants.js",
|
||||
],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
|
|
|
@ -47,13 +47,11 @@ afterEach(() => {
|
|||
|
||||
global.TextEncoder = TextEncoder;
|
||||
|
||||
// Jest doesn't like the top-level await in AppConstants, so we mock it. In
|
||||
// time we can hopefully phase out the entire file and just use dotenv-flow
|
||||
// and process.env directly.
|
||||
jest.mock("./src/appConstants.js", () => {
|
||||
// Jest doesn't like the top-level await in envVars.ts, so we mock it.
|
||||
jest.mock("./src/envVars", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require("dotenv-flow").config();
|
||||
return {
|
||||
...process.env,
|
||||
getEnvVarsOrThrow: () => process.env,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -7,7 +7,6 @@ import { AuthOptions, Profile as FxaProfile, User } from "next-auth";
|
|||
import { SubscriberRow } from "knex/types/tables";
|
||||
import { logger } from "../../functions/server/logging";
|
||||
|
||||
import AppConstants from "../../../appConstants.js";
|
||||
import {
|
||||
getSubscriberByFxaUid,
|
||||
updateFxAData,
|
||||
|
@ -26,6 +25,15 @@ import { SerializedSubscriber } from "../../../next-auth";
|
|||
import { record } from "../../functions/server/glean";
|
||||
import { renderEmail } from "../../../emails/renderEmail";
|
||||
import { SignupReportEmail } from "../../../emails/templates/signupReport/SignupReportEmail";
|
||||
import { getEnvVarsOrThrow } from "../../../envVars";
|
||||
|
||||
const envVars = getEnvVarsOrThrow([
|
||||
"OAUTH_AUTHORIZATION_URI",
|
||||
"OAUTH_TOKEN_URI",
|
||||
"OAUTH_CLIENT_ID",
|
||||
"OAUTH_CLIENT_SECRET",
|
||||
"OAUTH_PROFILE_URI",
|
||||
]);
|
||||
|
||||
const fxaProviderConfig: OAuthConfig<FxaProfile> = {
|
||||
// As per https://mozilla.slack.com/archives/C4D36CAJW/p1683642497940629?thread_ts=1683642325.465929&cid=C4D36CAJW,
|
||||
|
@ -36,7 +44,7 @@ const fxaProviderConfig: OAuthConfig<FxaProfile> = {
|
|||
name: "Mozilla accounts",
|
||||
type: "oauth",
|
||||
authorization: {
|
||||
url: AppConstants.OAUTH_AUTHORIZATION_URI,
|
||||
url: envVars.OAUTH_AUTHORIZATION_URI,
|
||||
params: {
|
||||
scope: "profile https://identity.mozilla.com/account/subscriptions",
|
||||
access_type: "offline",
|
||||
|
@ -45,11 +53,11 @@ const fxaProviderConfig: OAuthConfig<FxaProfile> = {
|
|||
max_age: 0,
|
||||
},
|
||||
},
|
||||
token: AppConstants.OAUTH_TOKEN_URI,
|
||||
// userinfo: AppConstants.OAUTH_PROFILE_URI,
|
||||
token: envVars.OAUTH_TOKEN_URI,
|
||||
// userinfo: envVars.OAUTH_PROFILE_URI,
|
||||
userinfo: {
|
||||
request: async (context) => {
|
||||
const response = await fetch(AppConstants.OAUTH_PROFILE_URI, {
|
||||
const response = await fetch(envVars.OAUTH_PROFILE_URI, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.tokens.access_token ?? ""}`,
|
||||
},
|
||||
|
@ -57,8 +65,8 @@ const fxaProviderConfig: OAuthConfig<FxaProfile> = {
|
|||
return (await response.json()) as FxaProfile;
|
||||
},
|
||||
},
|
||||
clientId: AppConstants.OAUTH_CLIENT_ID,
|
||||
clientSecret: AppConstants.OAUTH_CLIENT_SECRET,
|
||||
clientId: envVars.OAUTH_CLIENT_ID,
|
||||
clientSecret: envVars.OAUTH_CLIENT_SECRET,
|
||||
// Parse data returned by FxA's /userinfo/
|
||||
profile: (profile) => {
|
||||
return convertFxaProfile(profile);
|
||||
|
@ -309,6 +317,6 @@ export function bearerToken(req: NextRequest) {
|
|||
}
|
||||
|
||||
export function isAdmin(email: string) {
|
||||
const admins = AppConstants.ADMINS?.split(",") ?? [];
|
||||
const admins = (process.env.ADMINS ?? "").split(",") ?? [];
|
||||
return admins.includes(email);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import {
|
|||
} from "../../../functions/server/onerep";
|
||||
import { bearerToken } from "../../utils/auth";
|
||||
import { revokeOAuthTokens } from "../../../../utils/fxa";
|
||||
import appConstants from "../../../../appConstants";
|
||||
import { changeSubscription } from "../../../functions/server/changeSubscription";
|
||||
import { deleteAccount } from "../../../functions/server/deleteAccount";
|
||||
import { record } from "../../../functions/server/glean";
|
||||
|
@ -43,7 +42,7 @@ const MONITOR_PREMIUM_CAPABILITY = "monitor";
|
|||
* @returns {Promise<Array<jwt.JwtPayload> | undefined>} keys an array of FxA JWT keys
|
||||
*/
|
||||
const getJwtPubKey = async () => {
|
||||
const jwtKeyUri = `${appConstants.OAUTH_ACCOUNT_URI}/jwks`;
|
||||
const jwtKeyUri = `${process.env.OAUTH_ACCOUNT_URI}/jwks`;
|
||||
try {
|
||||
const response = await fetch(jwtKeyUri, {
|
||||
headers: {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import { getToken } from "next-auth/jwt";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import AppConstants from "../../../../../appConstants";
|
||||
|
||||
import { getSubscriberByFxaUid } from "../../../../../db/tables/subscribers";
|
||||
import { addSubscriberUnverifiedEmailHash } from "../../../../../db/tables/emailAddresses";
|
||||
|
@ -108,6 +107,6 @@ export async function POST(req: NextRequest) {
|
|||
}
|
||||
} else {
|
||||
// Not Signed in, redirect to home
|
||||
return NextResponse.redirect(AppConstants.SERVER_URL, 301);
|
||||
return NextResponse.redirect(process.env.SERVER_URL ?? "/", 301);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import { getToken } from "next-auth/jwt";
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { logger } from "../../../../functions/server/logging";
|
||||
import AppConstants from "../../../../../appConstants";
|
||||
import {
|
||||
getSubscriberByFxaUid,
|
||||
deleteResolutionsWithEmail,
|
||||
|
@ -50,7 +49,7 @@ export async function POST(req: NextRequest) {
|
|||
existingEmail.email,
|
||||
);
|
||||
return NextResponse.redirect(
|
||||
AppConstants.SERVER_URL + "/user/settings",
|
||||
process.env.SERVER_URL + "/user/settings",
|
||||
301,
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
if (typeof process.env.NEXT_RUNTIME === "undefined" && typeof process.env.STORYBOOK === "undefined") {
|
||||
// Next.js already loads env vars by itself, and dotenv-flow will throw an
|
||||
// error if loaded in that context (about `fs` not existing), so only load
|
||||
// it if we're not running in a Next.js-context (e.g. cron jobs):
|
||||
await import("dotenv-flow/config");
|
||||
}
|
||||
|
||||
// TODO: these vars were copy/pasted from the old app-constants.js and should be cleaned up
|
||||
const requiredEnvVars = [
|
||||
'ADMINS',
|
||||
'APP_ENV',
|
||||
'DATABASE_URL',
|
||||
'DELETE_UNVERIFIED_SUBSCRIBERS_TIMER',
|
||||
'EMAIL_FROM',
|
||||
'HIBP_API_ROOT',
|
||||
'HIBP_KANON_API_ROOT',
|
||||
'HIBP_KANON_API_TOKEN',
|
||||
'HIBP_NOTIFY_TOKEN',
|
||||
'HIBP_THROTTLE_DELAY',
|
||||
'HIBP_THROTTLE_MAX_TRIES',
|
||||
'FXA_SETTINGS_URL',
|
||||
'NODE_ENV',
|
||||
'OAUTH_ACCOUNT_URI',
|
||||
'OAUTH_AUTHORIZATION_URI',
|
||||
'OAUTH_CLIENT_ID',
|
||||
'OAUTH_CLIENT_SECRET',
|
||||
'OAUTH_PROFILE_URI',
|
||||
'OAUTH_TOKEN_URI',
|
||||
'SERVER_URL',
|
||||
'SES_CONFIG_SET',
|
||||
'SMTP_URL',
|
||||
'SUPPORTED_LOCALES'
|
||||
]
|
||||
|
||||
const optionalEnvVars = [
|
||||
'FX_REMOTE_SETTINGS_WRITER_PASS',
|
||||
'FX_REMOTE_SETTINGS_WRITER_SERVER',
|
||||
'FX_REMOTE_SETTINGS_WRITER_USER',
|
||||
'HIBP_BREACH_DOMAIN_BLOCKLIST',
|
||||
'PREMIUM_PRODUCT_ID',
|
||||
'PG_HOST',
|
||||
'NEXTAUTH_REDIRECT_URL'
|
||||
]
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const AppConstants = { }
|
||||
|
||||
if (!process.env.SERVER_URL && (process.env.APP_ENV) === 'heroku') {
|
||||
process.env.SERVER_URL = `https://${process.env.HEROKU_APP_NAME}.herokuapp.com`
|
||||
}
|
||||
|
||||
for (const v of requiredEnvVars) {
|
||||
const value = process.env[v]
|
||||
if (value === undefined) {
|
||||
console.warn(`Required environment variable was not set: ${v}`)
|
||||
} else {
|
||||
AppConstants[v] = value
|
||||
}
|
||||
}
|
||||
|
||||
optionalEnvVars.forEach(key => {
|
||||
const value = process.env[key]
|
||||
if (value) AppConstants[key] = value
|
||||
})
|
||||
|
||||
export default AppConstants.NODE_ENV === 'test'
|
||||
? AppConstants
|
||||
: Object.freeze(AppConstants)
|
|
@ -5,12 +5,14 @@
|
|||
import type { Profile } from "next-auth";
|
||||
import type { EmailAddressRow, SubscriberRow } from "knex/types/tables";
|
||||
import createDbConnection from "../connect";
|
||||
import AppConstants from "../../appConstants.js";
|
||||
import { SerializedSubscriber } from "../../next-auth.js";
|
||||
import { getFeatureFlagData } from "./featureFlags";
|
||||
import { getEnvVarsOrThrow } from "../../envVars";
|
||||
|
||||
const knex = createDbConnection();
|
||||
const { DELETE_UNVERIFIED_SUBSCRIBERS_TIMER } = AppConstants;
|
||||
const { DELETE_UNVERIFIED_SUBSCRIBERS_TIMER } = getEnvVarsOrThrow([
|
||||
"DELETE_UNVERIFIED_SUBSCRIBERS_TIMER",
|
||||
]);
|
||||
const MONITOR_PREMIUM_CAPABILITY = "monitor";
|
||||
|
||||
// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
if (
|
||||
typeof process.env.NEXT_RUNTIME === "undefined" &&
|
||||
typeof process.env.STORYBOOK === "undefined"
|
||||
) {
|
||||
// Next.js already loads env vars by itself, and dotenv-flow will throw an
|
||||
// error if loaded in that context (about `fs` not existing), so only load
|
||||
// it if we're not running in a Next.js-context (e.g. cron jobs):
|
||||
await import("dotenv-flow/config");
|
||||
}
|
||||
|
||||
export function getEnvVarsOrThrow<EnvVarNames extends string>(
|
||||
envVars: EnvVarNames[],
|
||||
): Record<EnvVarNames, string> {
|
||||
const envVarsRecord: Record<EnvVarNames, string> = {} as never;
|
||||
for (const varName of envVars) {
|
||||
const value = process.env[varName];
|
||||
if (typeof value !== "string") {
|
||||
throw new Error(
|
||||
`Required environment variable was not set: [${varName}].`,
|
||||
);
|
||||
}
|
||||
envVarsRecord[varName] = value;
|
||||
}
|
||||
return envVarsRecord;
|
||||
}
|
|
@ -33,7 +33,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import AppConstants from "../../appConstants";
|
||||
import * as HIBP from "../../utils/hibp";
|
||||
|
||||
type RemoteSettingsBreach = Pick<
|
||||
|
@ -41,11 +40,29 @@ type RemoteSettingsBreach = Pick<
|
|||
"Name" | "Domain" | "BreachDate" | "PwnCount" | "AddedDate" | "DataClasses"
|
||||
>;
|
||||
|
||||
const FX_REMOTE_SETTINGS_WRITER_USER =
|
||||
process.env.FX_REMOTE_SETTINGS_WRITER_USER;
|
||||
const FX_REMOTE_SETTINGS_WRITER_PASS =
|
||||
process.env.FX_REMOTE_SETTINGS_WRITER_PASS;
|
||||
const FX_REMOTE_SETTINGS_WRITER_SERVER =
|
||||
process.env.FX_REMOTE_SETTINGS_WRITER_SERVER;
|
||||
|
||||
if (
|
||||
!FX_REMOTE_SETTINGS_WRITER_USER ||
|
||||
!FX_REMOTE_SETTINGS_WRITER_PASS ||
|
||||
!FX_REMOTE_SETTINGS_WRITER_SERVER
|
||||
) {
|
||||
console.error(
|
||||
"updatebreaches requires FX_REMOTE_SETTINGS_WRITER_SERVER, FX_REMOTE_SETTINGS_WRITER_USER, FX_REMOTE_SETTINGS_WRITER_PASS.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const BREACHES_COLLECTION = "fxmonitor-breaches";
|
||||
const FX_RS_COLLECTION = `${AppConstants.FX_REMOTE_SETTINGS_WRITER_SERVER}/buckets/main-workspace/collections/${BREACHES_COLLECTION}`;
|
||||
const FX_RS_COLLECTION = `${FX_REMOTE_SETTINGS_WRITER_SERVER}/buckets/main-workspace/collections/${BREACHES_COLLECTION}`;
|
||||
const FX_RS_RECORDS = `${FX_RS_COLLECTION}/records`;
|
||||
const FX_RS_WRITER_USER = AppConstants.FX_REMOTE_SETTINGS_WRITER_USER;
|
||||
const FX_RS_WRITER_PASS = AppConstants.FX_REMOTE_SETTINGS_WRITER_PASS;
|
||||
const FX_RS_WRITER_USER = FX_REMOTE_SETTINGS_WRITER_USER;
|
||||
const FX_RS_WRITER_PASS = FX_REMOTE_SETTINGS_WRITER_PASS;
|
||||
|
||||
async function whichBreachesAreNotInRemoteSettingsYet(
|
||||
breaches: HIBP.HibpGetBreachesResponse,
|
||||
|
@ -90,17 +107,6 @@ async function requestReviewOnBreachesCollection() {
|
|||
return response.json();
|
||||
}
|
||||
|
||||
if (
|
||||
!AppConstants.FX_REMOTE_SETTINGS_WRITER_USER ||
|
||||
!AppConstants.FX_REMOTE_SETTINGS_WRITER_PASS ||
|
||||
!AppConstants.FX_REMOTE_SETTINGS_WRITER_SERVER
|
||||
) {
|
||||
console.error(
|
||||
"updatebreaches requires FX_REMOTE_SETTINGS_WRITER_SERVER, FX_REMOTE_SETTINGS_WRITER_USER, FX_REMOTE_SETTINGS_WRITER_PASS.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const allHibpBreaches = await HIBP.fetchHibpBreaches();
|
||||
const verifiedSiteBreaches = allHibpBreaches.filter((breach) => {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import AppConstants from "../appConstants";
|
||||
import packageJson from "../../package.json";
|
||||
|
||||
export type VersionData = {
|
||||
|
@ -23,7 +22,7 @@ if (!fs.existsSync(versionJsonPath)) {
|
|||
const versionJson = {
|
||||
source: packageJson.homepage,
|
||||
version: packageJson.version,
|
||||
NODE_ENV: AppConstants.NODE_ENV,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
|
@ -33,7 +32,7 @@ if (!fs.existsSync(versionJsonPath)) {
|
|||
}
|
||||
|
||||
export function vers(): VersionData {
|
||||
if (AppConstants.APP_ENV === "heroku") {
|
||||
if (process.env.APP_ENV === "heroku") {
|
||||
/* eslint-disable no-process-env */
|
||||
return {
|
||||
commit: process.env.HEROKU_SLUG_COMMIT!,
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
|
||||
import { createTransport, Transporter } from "nodemailer";
|
||||
|
||||
import AppConstants from "../appConstants.js";
|
||||
import { SentMessageInfo } from "nodemailer/lib/smtp-transport/index.js";
|
||||
import { getEnvVarsOrThrow } from "../envVars";
|
||||
|
||||
// The SMTP transport object. This is initialized to a nodemailer transport
|
||||
// object while reading SMTP credentials, or to a dummy function in debug mode.
|
||||
let gTransporter: Transporter<SentMessageInfo>;
|
||||
|
||||
async function initEmail(smtpUrl = AppConstants.SMTP_URL) {
|
||||
const envVars = getEnvVarsOrThrow(["SMTP_URL", "EMAIL_FROM", "SES_CONFIG_SET"]);
|
||||
|
||||
async function initEmail(smtpUrl = envVars.SMTP_URL) {
|
||||
// Allow a debug mode that will log JSON instead of sending emails.
|
||||
if (!smtpUrl) {
|
||||
console.info("smtpUrl-empty", {
|
||||
|
@ -42,14 +44,14 @@ async function sendEmail(
|
|||
throw new Error("SMTP transport not initialized");
|
||||
}
|
||||
|
||||
const emailFrom = AppConstants.EMAIL_FROM;
|
||||
const emailFrom = envVars.EMAIL_FROM;
|
||||
const mailOptions = {
|
||||
from: emailFrom,
|
||||
to: recipient,
|
||||
subject,
|
||||
html,
|
||||
headers: {
|
||||
"x-ses-configuration-set": AppConstants.SES_CONFIG_SET,
|
||||
"x-ses-configuration-set": envVars.SES_CONFIG_SET,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,13 @@
|
|||
import crypto from "crypto";
|
||||
import { URL } from "url";
|
||||
|
||||
import AppConstants from "../appConstants.js";
|
||||
import { getEnvVarsOrThrow } from "../envVars";
|
||||
const envVars = getEnvVarsOrThrow([
|
||||
"OAUTH_CLIENT_ID",
|
||||
"OAUTH_CLIENT_SECRET",
|
||||
"OAUTH_TOKEN_URI",
|
||||
"OAUTH_ACCOUNT_URI",
|
||||
]);
|
||||
|
||||
/**
|
||||
* @see https://mozilla.github.io/ecosystem-platform/api#tag/Oauth/operation/postOauthDestroy
|
||||
|
@ -30,11 +36,11 @@ async function destroyOAuthToken(
|
|||
) {
|
||||
const tokenBody: FxaPostOauthDestroyRequestBody = {
|
||||
...tokenData,
|
||||
client_id: AppConstants.OAUTH_CLIENT_ID,
|
||||
client_secret: AppConstants.OAUTH_CLIENT_SECRET,
|
||||
client_id: envVars.OAUTH_CLIENT_ID,
|
||||
client_secret: envVars.OAUTH_CLIENT_SECRET,
|
||||
};
|
||||
|
||||
const fxaTokenOrigin = new URL(AppConstants.OAUTH_TOKEN_URI).origin;
|
||||
const fxaTokenOrigin = new URL(envVars.OAUTH_TOKEN_URI).origin;
|
||||
const tokenUrl = `${fxaTokenOrigin}/v1/oauth/destroy`;
|
||||
const tokenOptions = {
|
||||
method: "POST",
|
||||
|
@ -132,10 +138,10 @@ type FxaPostOauthTokenResponseSuccessRefreshToken = {
|
|||
async function refreshOAuthTokens(
|
||||
refreshToken: string,
|
||||
): Promise<FxaPostOauthTokenResponseSuccessRefreshToken> {
|
||||
const subscriptionIdUrl = `${AppConstants.OAUTH_ACCOUNT_URI}/oauth/token`;
|
||||
const subscriptionIdUrl = `${envVars.OAUTH_ACCOUNT_URI}/oauth/token`;
|
||||
const body: FxaPostOauthTokenRequestBody = {
|
||||
client_id: AppConstants.OAUTH_CLIENT_ID,
|
||||
client_secret: AppConstants.OAUTH_CLIENT_SECRET,
|
||||
client_id: envVars.OAUTH_CLIENT_ID,
|
||||
client_secret: envVars.OAUTH_CLIENT_SECRET,
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: refreshToken,
|
||||
ttl: 604800, // request 7 days ttl
|
||||
|
@ -175,7 +181,7 @@ type FxaGetOauthSubscribptionsActiveResponseSuccess = Array<{
|
|||
async function getSubscriptions(
|
||||
bearerToken: string,
|
||||
): Promise<FxaGetOauthSubscribptionsActiveResponseSuccess | null> {
|
||||
const subscriptionIdUrl = `${AppConstants.OAUTH_ACCOUNT_URI}/oauth/subscriptions/active`;
|
||||
const subscriptionIdUrl = `${envVars.OAUTH_ACCOUNT_URI}/oauth/subscriptions/active`;
|
||||
try {
|
||||
const response = await fetch(subscriptionIdUrl, {
|
||||
headers: {
|
||||
|
@ -221,7 +227,7 @@ type FxaGetOauthMozillaSubscribptionsCustomerBillingAndSubscriptionsResponseSucc
|
|||
async function getBillingAndSubscriptions(
|
||||
bearerToken: string,
|
||||
): Promise<FxaGetOauthMozillaSubscribptionsCustomerBillingAndSubscriptionsResponseSuccess | null> {
|
||||
const subscriptionIdUrl = `${AppConstants.OAUTH_ACCOUNT_URI}/oauth/mozilla-subscriptions/customer/billing-and-subscriptions`;
|
||||
const subscriptionIdUrl = `${envVars.OAUTH_ACCOUNT_URI}/oauth/mozilla-subscriptions/customer/billing-and-subscriptions`;
|
||||
|
||||
try {
|
||||
const response = await fetch(subscriptionIdUrl, {
|
||||
|
@ -253,13 +259,13 @@ async function deleteSubscription(bearerToken: string): Promise<boolean> {
|
|||
if (
|
||||
sub &&
|
||||
sub.productId &&
|
||||
sub.productId === AppConstants.PREMIUM_PRODUCT_ID
|
||||
sub.productId === process.env.PREMIUM_PRODUCT_ID
|
||||
) {
|
||||
subscriptionId = sub.subscriptionId;
|
||||
}
|
||||
}
|
||||
if (subscriptionId) {
|
||||
const deleteUrl = `${AppConstants.OAUTH_ACCOUNT_URI}/oauth/subscriptions/active/${subscriptionId}`;
|
||||
const deleteUrl = `${envVars.OAUTH_ACCOUNT_URI}/oauth/subscriptions/active/${subscriptionId}`;
|
||||
const response = await fetch(deleteUrl, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
|
@ -294,13 +300,13 @@ async function applyCoupon(
|
|||
if (
|
||||
sub &&
|
||||
sub.productId &&
|
||||
sub.productId === AppConstants.PREMIUM_PRODUCT_ID
|
||||
sub.productId === process.env.PREMIUM_PRODUCT_ID
|
||||
) {
|
||||
subscriptionId = sub.subscriptionId;
|
||||
}
|
||||
}
|
||||
if (subscriptionId) {
|
||||
const applyCouponUrl = `${AppConstants.OAUTH_ACCOUNT_URI}/oauth/subscriptions/coupon/apply`;
|
||||
const applyCouponUrl = `${envVars.OAUTH_ACCOUNT_URI}/oauth/subscriptions/coupon/apply`;
|
||||
const response = await fetch(applyCouponUrl, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import AppConstants from "../appConstants.js";
|
||||
import { getAllBreaches, knex } from "../db/tables/breaches";
|
||||
import { isUsingMockHIBPEndpoint } from "../app/functions/universal/mock.ts";
|
||||
import { BreachRow, EmailAddressRow, SubscriberRow } from "knex/types/tables";
|
||||
|
@ -14,13 +13,20 @@ import {
|
|||
getQaToggleRow,
|
||||
} from "../db/tables/qa_customs.ts";
|
||||
import { redisClient, REDIS_ALL_BREACHES_KEY } from "../db/redis/client.ts";
|
||||
import { getEnvVarsOrThrow } from "../envVars.ts";
|
||||
const {
|
||||
HIBP_THROTTLE_MAX_TRIES,
|
||||
HIBP_THROTTLE_DELAY,
|
||||
HIBP_API_ROOT,
|
||||
HIBP_KANON_API_ROOT,
|
||||
HIBP_KANON_API_TOKEN,
|
||||
} = AppConstants;
|
||||
} = getEnvVarsOrThrow([
|
||||
"HIBP_THROTTLE_MAX_TRIES",
|
||||
"HIBP_THROTTLE_DELAY",
|
||||
"HIBP_API_ROOT",
|
||||
"HIBP_KANON_API_ROOT",
|
||||
"HIBP_KANON_API_TOKEN",
|
||||
]);
|
||||
|
||||
// TODO: fix hardcode
|
||||
const HIBP_USER_AGENT = "monitor/1.0.0";
|
||||
|
@ -73,8 +79,10 @@ async function _throttledFetch(
|
|||
} else {
|
||||
tryCount++;
|
||||
await new Promise((resolve) =>
|
||||
// @ts-ignore HIBP_THROTTLE_DELAY should be defined
|
||||
setTimeout(resolve, HIBP_THROTTLE_DELAY * tryCount),
|
||||
setTimeout(
|
||||
resolve,
|
||||
Number.parseInt(HIBP_THROTTLE_DELAY, 10) * tryCount,
|
||||
),
|
||||
);
|
||||
return await _throttledFetch(url, reqOptions, tryCount);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче