Merge branch 'main' into MNTOR-2935-free-scan-auth-and-noauth
|
@ -984,3 +984,4 @@ floating-banner-dismiss-button-label = Ne, díky
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Nové jméno, vzhled a ještě více způsobů, jak <b>získat zpět své soukromí</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Zavřít
|
||||
loading-accessibility = Načítání
|
||||
|
|
|
@ -867,3 +867,4 @@ floating-banner-dismiss-button-label = Dim diolch
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Enw, golwg newydd a rhagor o ffyrdd i <b>adennill eich preifatrwydd</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = Iawn
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Cau
|
||||
loading-accessibility = Llwytho
|
||||
|
|
|
@ -788,3 +788,4 @@ floating-banner-dismiss-button-label = Nein, danke
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Neuer Name, neues Aussehen und noch mehr Möglichkeiten, um <b>Ihre Privatsphäre zurückzugewinnen</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Schließen
|
||||
loading-accessibility = Wird geladen…
|
||||
|
|
|
@ -872,3 +872,4 @@ floating-banner-dismiss-button-label = Όχι, ευχαριστώ
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Νέο όνομα, εμφάνιση και ακόμα περισσότεροι τρόποι <b>διεκδίκησης του απορρήτου σας</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Απόρριψη
|
||||
loading-accessibility = Φόρτωση
|
||||
|
|
|
@ -805,3 +805,4 @@ floating-banner-dismiss-button-label = No, gracias
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Nuevo nombre, apariencia e incluso más formas de <b>recuperar tu privacidad</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = Aceptar
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Descartar
|
||||
loading-accessibility = Cargando
|
||||
|
|
|
@ -779,3 +779,4 @@ floating-banner-dismiss-button-label = Non merci
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b> : un nouveau nom, une nouvelle interface et encore de nouvelles façons de <b>reprendre le contrôle de votre vie privée</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Ignorer
|
||||
loading-accessibility = Chargement…
|
||||
|
|
|
@ -841,3 +841,4 @@ floating-banner-dismiss-button-label = Köszönöm, nem
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Új név, kinézet és még több módja annak, hogy <b>visszaszerezze a magánszféráját</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Eltüntetés
|
||||
loading-accessibility = Betöltés
|
||||
|
|
|
@ -820,3 +820,4 @@ floating-banner-dismiss-button-label = Tidak, terima kasih
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Nama baru, tampilan, dan lebih banyak cara untuk <b>mendapatkan kembali privasi Anda</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = Oke
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Tutup
|
||||
loading-accessibility = Memuat
|
||||
|
|
|
@ -767,3 +767,4 @@ floating-banner-dismiss-button-label = No grazie
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: nuovo nome, nuovo look e ancora più modi per <b>riprendere possesso della tua privacy</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Chiudi
|
||||
loading-accessibility = Caricamento…
|
||||
|
|
|
@ -836,3 +836,4 @@ floating-banner-dismiss-button-label = Nee, bedankt
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: nieuwe naam, vormgeving en nog meer manieren om <b>uw privacy op te eisen</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Sluiten
|
||||
loading-accessibility = Laden
|
||||
|
|
|
@ -837,3 +837,4 @@ floating-banner-dismiss-button-label = Não, obrigado
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Um novo nome, visual e ainda mais formas de <b>recuperar a sua privacidade</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = Ok
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Dispensar
|
||||
loading-accessibility = A carregar
|
||||
|
|
|
@ -820,3 +820,4 @@ floating-banner-dismiss-button-label = Нет, спасибо
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Новое имя, внешний вид и ещё больше способов <b>восстановить вашу приватность</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Скрыть
|
||||
loading-accessibility = Загрузка
|
||||
|
|
|
@ -900,3 +900,4 @@ floating-banner-dismiss-button-label = Ne, hvala
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Novo ime, podoba in še več načinov za <b>ponovno pridobitev zasebnosti</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = V redu
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Opusti
|
||||
loading-accessibility = Nalaganje
|
||||
|
|
|
@ -841,3 +841,4 @@ floating-banner-dismiss-button-label = Nej tack
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>: Nytt namn, utseende och ännu fler sätt att <b>återställa din integritet</b>.
|
||||
banner-monitor-rebrand-dismiss-button-label = OK
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = Ignorera
|
||||
loading-accessibility = Laddar
|
||||
|
|
|
@ -735,3 +735,4 @@ floating-banner-dismiss-button-label = 不要,謝謝
|
|||
banner-monitor-rebrand-text = <b>{ -brand-mozilla-monitor }</b>:全新名稱、外觀與更多<b>奪回隱私權</b>的方式。
|
||||
banner-monitor-rebrand-dismiss-button-label = 確定
|
||||
banner-monitor-rebrand-dismiss-button-tooltip = 知道了!
|
||||
loading-accessibility = 載入中
|
||||
|
|
|
@ -48,8 +48,8 @@
|
|||
"npm": "10.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.496.0",
|
||||
"@aws-sdk/lib-storage": "^3.496.0",
|
||||
"@aws-sdk/client-s3": "^3.511.0",
|
||||
"@aws-sdk/lib-storage": "^3.511.0",
|
||||
"@fluent/bundle": "^0.18.0",
|
||||
"@fluent/langneg": "^0.7.0",
|
||||
"@fluent/react": "^0.15.2",
|
||||
|
@ -59,9 +59,9 @@
|
|||
"@leeoniya/ufuzzy": "^1.0.14",
|
||||
"@mozilla/glean": "4.0.0",
|
||||
"@next/third-parties": "^14.1.0",
|
||||
"@sentry/nextjs": "^7.99.0",
|
||||
"@sentry/nextjs": "^7.100.1",
|
||||
"@sentry/node": "^7.58.1",
|
||||
"@sentry/tracing": "^7.99.0",
|
||||
"@sentry/tracing": "^7.100.1",
|
||||
"@types/jsdom": "^21.1.5",
|
||||
"@types/node": "^20.11.17",
|
||||
"@types/react": "^18.2.48",
|
||||
|
|
|
@ -9,8 +9,7 @@ import { useEffect, useRef, useState } from "react";
|
|||
import { ProgressBar } from "../../../../../components/client/ProgressBar";
|
||||
import styles from "./FindExposures.module.scss";
|
||||
import { useL10n } from "../../../../../hooks/l10n";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useGa } from "../../../../../hooks/useGa";
|
||||
import { sendGAEvent } from "../../../../../components/client/GoogleAnalyticsWorkaround";
|
||||
|
||||
export type Props = {
|
||||
dataBrokerCount: number;
|
||||
|
@ -84,7 +83,6 @@ export const FindExposures = ({
|
|||
progressRange: [labelSwitchThreshold, 100],
|
||||
});
|
||||
|
||||
const { gtag } = useGa();
|
||||
const pathName = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
|
@ -115,11 +113,11 @@ export const FindExposures = ({
|
|||
.then((result) => {
|
||||
if (result.status && result.status === "finished") {
|
||||
setScanFinished(true);
|
||||
gtag.record({
|
||||
type: "event",
|
||||
name: "free_scan_completed",
|
||||
params: scanCompletedTelemetryParams,
|
||||
});
|
||||
sendGAEvent(
|
||||
"event",
|
||||
"free_scan_completed",
|
||||
scanCompletedTelemetryParams,
|
||||
);
|
||||
}
|
||||
setCheckingScanProgress(false);
|
||||
})
|
||||
|
@ -135,11 +133,7 @@ export const FindExposures = ({
|
|||
userTimeSpentRef.current.endTime = Date.now();
|
||||
findExposuresTelemetryParams.exit_time =
|
||||
userTimeSpentRef.current.endTime - userTimeSpentRef.current.startTime;
|
||||
gtag.record({
|
||||
type: "event",
|
||||
name: "exited_scan",
|
||||
params: findExposuresTelemetryParams,
|
||||
});
|
||||
sendGAEvent("event", "exited_scan", findExposuresTelemetryParams);
|
||||
router.push(previousRoute);
|
||||
}
|
||||
|
||||
|
@ -153,7 +147,6 @@ export const FindExposures = ({
|
|||
scanFinished,
|
||||
percentageSteps,
|
||||
previousRoute,
|
||||
gtag,
|
||||
pathName,
|
||||
searchParams,
|
||||
]);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import { NextRequest } from "next/server";
|
||||
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";
|
||||
|
@ -72,20 +73,19 @@ export const authOptions: AuthOptions = {
|
|||
async jwt({ token, account, profile, trigger }) {
|
||||
if (trigger === "update") {
|
||||
// Refresh the user data from FxA, in case e.g. new subscriptions got added:
|
||||
const subscriber = await getSubscriberByFxaUid(
|
||||
const subscriberFromDb = await getSubscriberByFxaUid(
|
||||
token.subscriber?.fxa_uid ?? "",
|
||||
);
|
||||
profile = subscriber.fxa_profile_json as FxaProfile;
|
||||
|
||||
if (token.subscriber?.fxa_uid) {
|
||||
const updatedSubscriberData = await getSubscriberByFxaUid(
|
||||
token.subscriber.fxa_uid,
|
||||
);
|
||||
if (subscriberFromDb) {
|
||||
profile = subscriberFromDb.fxa_profile_json as FxaProfile;
|
||||
|
||||
// MNTOR-2599 The breach_resolution object can get pretty big,
|
||||
// causing the session token cookie to balloon in size,
|
||||
// eventually resulting in a 400 Bad Request due to headers being too large.
|
||||
delete updatedSubscriberData.breach_resolution;
|
||||
token.subscriber = updatedSubscriberData;
|
||||
delete (subscriberFromDb as Partial<SubscriberRow>).breach_resolution;
|
||||
token.subscriber =
|
||||
subscriberFromDb as unknown as SerializedSubscriber;
|
||||
}
|
||||
}
|
||||
if (profile) {
|
||||
|
@ -117,8 +117,8 @@ export const authOptions: AuthOptions = {
|
|||
// MNTOR-2599 The breach_resolution object can get pretty big,
|
||||
// causing the session token cookie to balloon in size,
|
||||
// eventually resulting in a 400 Bad Request due to headers being too large.
|
||||
delete existingUser.breach_resolution;
|
||||
token.subscriber = existingUser;
|
||||
delete (existingUser as Partial<SubscriberRow>).breach_resolution;
|
||||
token.subscriber = existingUser as unknown as SerializedSubscriber;
|
||||
if (account.access_token && account.refresh_token) {
|
||||
const updatedUser = await updateFxAData(
|
||||
existingUser,
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { ReactLocalization } from "@fluent/react";
|
||||
import { SubscriberRow } from "knex/types/tables";
|
||||
import { resetUnverifiedEmailAddress } from "../../../db/tables/emailAddresses.js";
|
||||
import { sendEmail, getVerificationUrl } from "../../../utils/email";
|
||||
import { getStringLookup } from "../../../utils/fluent.js";
|
||||
import { getTemplate } from "../../../views/emails/email2022.js";
|
||||
import { verifyPartial } from "../../../views/emails/emailVerify.js";
|
||||
import { Subscriber } from "../../deprecated/(authenticated)/user/breaches/breaches";
|
||||
|
||||
export async function sendVerificationEmail(
|
||||
user: Subscriber,
|
||||
user: SubscriberRow,
|
||||
emailId: number,
|
||||
l10n: ReactLocalization,
|
||||
) {
|
||||
|
|
|
@ -136,6 +136,9 @@ export async function PUT(
|
|||
const subscriber = await getSubscriberByEmail(
|
||||
subscriberRow.primary_email,
|
||||
);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for given email.");
|
||||
}
|
||||
|
||||
const onerepProfileId = await getOnerepProfileId(subscriber.id);
|
||||
|
||||
|
|
|
@ -219,7 +219,10 @@ export async function POST(request: NextRequest) {
|
|||
});
|
||||
|
||||
// get current profiledata
|
||||
const currentFxAProfile = subscriber?.fxa_profile_json || {};
|
||||
// Typed as `any` because `subscriber` used to be typed as `any`, and
|
||||
// making that type more specific was enough work just by itself:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const currentFxAProfile: any = subscriber?.fxa_profile_json;
|
||||
|
||||
// merge new event into existing profile data
|
||||
for (const key in updatedProfileFromEvent) {
|
||||
|
@ -249,8 +252,25 @@ export async function POST(request: NextRequest) {
|
|||
updateFromEvent,
|
||||
});
|
||||
|
||||
const refreshToken = subscriber.fxa_refresh_token;
|
||||
const accessToken = subscriber.fxa_access_token;
|
||||
if (refreshToken === null || accessToken === null) {
|
||||
logger.error("failed_changing_password", {
|
||||
subscriber_id: subscriber.id,
|
||||
fxa_refresh_token: subscriber.fxa_refresh_token,
|
||||
fxa_access_token: subscriber.fxa_access_token,
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ success: false, message: "failed_changing_password" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
// MNTOR-1932: Change password should revoke sessions
|
||||
await revokeOAuthTokens(subscriber);
|
||||
await revokeOAuthTokens({
|
||||
fxa_access_token: accessToken,
|
||||
fxa_refresh_token: refreshToken,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case FXA_SUBSCRIPTION_CHANGE_EVENT: {
|
||||
|
@ -291,7 +311,7 @@ export async function POST(request: NextRequest) {
|
|||
|
||||
captureException(
|
||||
new Error(`No OneRep profile Id found, subscriber: ${
|
||||
subscriber.id as string
|
||||
subscriber.id
|
||||
}\n
|
||||
Event: ${event}\n
|
||||
updateFromEvent: ${JSON.stringify(updatedSubscriptionFromEvent)}`),
|
||||
|
@ -351,7 +371,7 @@ export async function POST(request: NextRequest) {
|
|||
|
||||
captureException(
|
||||
new Error(`No OneRep profile Id found, subscriber: ${
|
||||
subscriber.id as string
|
||||
subscriber.id
|
||||
}\n
|
||||
Event: ${event}\n
|
||||
updateFromEvent: ${JSON.stringify(
|
||||
|
|
|
@ -6,10 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|||
import { authOptions } from "../../../../utils/auth";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { logger } from "../../../../../functions/server/logging";
|
||||
import {
|
||||
BreachBulkResolutionRequest,
|
||||
Subscriber,
|
||||
} from "../../../../../deprecated/(authenticated)/user/breaches/breaches.js";
|
||||
import { BreachBulkResolutionRequest } from "../../../../../deprecated/(authenticated)/user/breaches/breaches.js";
|
||||
import { getBreaches } from "../../../../../functions/server/getBreaches";
|
||||
import { getAllEmailsAndBreaches } from "../../../../../../utils/breaches";
|
||||
import {
|
||||
|
@ -30,9 +27,12 @@ export async function PUT(req: NextRequest): Promise<NextResponse> {
|
|||
}
|
||||
|
||||
try {
|
||||
const subscriber: Subscriber = await getSubscriberByFxaUid(
|
||||
const subscriber = await getSubscriberByFxaUid(
|
||||
session.user.subscriber.fxa_uid,
|
||||
);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for the current session.");
|
||||
}
|
||||
const allBreaches = await getBreaches();
|
||||
const { dataType: dataTypeToResolve }: BreachBulkResolutionRequest =
|
||||
await req.json();
|
||||
|
@ -42,7 +42,10 @@ export async function PUT(req: NextRequest): Promise<NextResponse> {
|
|||
allBreaches,
|
||||
);
|
||||
|
||||
const currentBreachResolution = subscriber.breach_resolution || {}; // get this from existing breach resolution if available
|
||||
// Typed as `any` because `subscriber` used to be typed as `any`, and making
|
||||
// that type more specific was enough work just by itself:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const currentBreachResolution: any = subscriber.breach_resolution || {}; // get this from existing breach resolution if available
|
||||
|
||||
for (const verifiedEmail of verifiedEmails) {
|
||||
const currentEmail = verifiedEmail.email;
|
||||
|
@ -83,6 +86,9 @@ export async function PUT(req: NextRequest): Promise<NextResponse> {
|
|||
subscriber,
|
||||
currentBreachResolution,
|
||||
);
|
||||
if (!updatedSubscriber) {
|
||||
throw new Error("Could not retrieve updated subscriber data.");
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
|
|
|
@ -6,10 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|||
import { getToken } from "next-auth/jwt";
|
||||
|
||||
import { logger } from "../../../../functions/server/logging";
|
||||
import {
|
||||
BreachResolutionRequest,
|
||||
Subscriber,
|
||||
} from "../../../../deprecated/(authenticated)/user/breaches/breaches.js";
|
||||
import { BreachResolutionRequest } from "../../../../deprecated/(authenticated)/user/breaches/breaches.js";
|
||||
import { getBreaches } from "../../../../functions/server/getBreaches";
|
||||
import { getAllEmailsAndBreaches } from "../../../../../utils/breaches";
|
||||
import {
|
||||
|
@ -24,9 +21,7 @@ export async function GET(req: NextRequest) {
|
|||
if (typeof token?.subscriber?.fxa_uid === "string") {
|
||||
// Signed in
|
||||
try {
|
||||
const subscriber: Subscriber = await getSubscriberByFxaUid(
|
||||
token.subscriber?.fxa_uid,
|
||||
);
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
const allBreaches = await getBreaches();
|
||||
const breaches = await getAllEmailsAndBreaches(subscriber, allBreaches);
|
||||
const successResponse = {
|
||||
|
@ -48,9 +43,10 @@ export async function PUT(req: NextRequest) {
|
|||
const token = await getToken({ req });
|
||||
if (typeof token?.subscriber?.fxa_uid === "string") {
|
||||
try {
|
||||
const subscriber: Subscriber = await getSubscriberByFxaUid(
|
||||
token.subscriber?.fxa_uid,
|
||||
);
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const allBreaches = await getBreaches();
|
||||
const j = await req.json();
|
||||
const {
|
||||
|
@ -125,7 +121,10 @@ export async function PUT(req: NextRequest) {
|
|||
// */
|
||||
|
||||
const currentBreachDataTypes = currentBreaches[0].DataClasses; // get this from existing breaches
|
||||
const currentBreachResolution = subscriber.breach_resolution || {}; // get this from existing breach resolution if available
|
||||
// Typed as `any` because `subscriber` used to be typed as `any`, and
|
||||
// making that type more specific was enough work just by itself:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const currentBreachResolution: any = subscriber.breach_resolution || {}; // get this from existing breach resolution if available
|
||||
const isResolved =
|
||||
resolutionsChecked.length === currentBreachDataTypes.length;
|
||||
currentBreachResolution[affectedEmail] = {
|
||||
|
@ -146,6 +145,9 @@ export async function PUT(req: NextRequest) {
|
|||
subscriber,
|
||||
currentBreachResolution,
|
||||
);
|
||||
if (!updatedSubscriber) {
|
||||
throw new Error("Could not retrieve updated subscriber data.");
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
|
|
|
@ -14,7 +14,6 @@ import { sendVerificationEmail } from "../../../utils/email";
|
|||
import { validateEmailAddress } from "../../../../../utils/emailAddress";
|
||||
import { getL10n } from "../../../../functions/server/l10n";
|
||||
import { initEmail } from "../../../../../utils/email";
|
||||
import { Subscriber } from "../../../../deprecated/(authenticated)/user/breaches/breaches";
|
||||
import { CONST_MAX_NUM_ADDRESSES } from "../../../../../constants";
|
||||
|
||||
interface EmailAddRequest {
|
||||
|
@ -28,11 +27,10 @@ export async function POST(req: NextRequest) {
|
|||
if (typeof token?.subscriber?.fxa_uid === "string") {
|
||||
try {
|
||||
const body: EmailAddRequest = await req.json();
|
||||
const subscriber = (await getSubscriberByFxaUid(
|
||||
token.subscriber?.fxa_uid,
|
||||
)) as Subscriber & {
|
||||
email_addresses: Array<{ id: number; email: string }>;
|
||||
};
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const emailCount = 1 + (subscriber.email_addresses?.length ?? 0); // primary + verified + unverified emails
|
||||
const validatedEmail = validateEmailAddress(body.email);
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ export async function POST(req: NextRequest) {
|
|||
try {
|
||||
const { emailId }: EmailDeleteRequest = await req.json();
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const existingEmail = await getEmailById(emailId);
|
||||
|
||||
if (existingEmail?.subscriber_id !== subscriber.id) {
|
||||
|
|
|
@ -25,6 +25,9 @@ export async function POST(req: NextRequest) {
|
|||
try {
|
||||
const { emailId }: EmailResendRequest = await req.json();
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const existingEmail = await getUserEmails(subscriber.id);
|
||||
|
||||
const filteredEmail = existingEmail.filter(
|
||||
|
|
|
@ -26,6 +26,9 @@ export async function POST(req: NextRequest) {
|
|||
const { communicationOption }: EmailUpdateCommOptionRequest =
|
||||
await req.json();
|
||||
const subscriber = await getSubscriberByFxaUid(token.subscriber?.fxa_uid);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
// 0 = Send breach alerts to the corresponding affected emails.
|
||||
// 1 = Send all breach alerts to user's primary email address.
|
||||
const allEmailsToPrimary = Number(communicationOption) === 1 ?? false;
|
||||
|
|
|
@ -86,6 +86,9 @@ export async function POST(
|
|||
session.user.subscriber.fxa_uid,
|
||||
);
|
||||
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
if (!subscriber.onerep_profile_id) {
|
||||
// Create OneRep profile
|
||||
const profileId = await createProfile(profileData);
|
||||
|
|
|
@ -43,6 +43,9 @@ export async function GET(
|
|||
const subscriber = await getSubscriberByFxaUid(
|
||||
session.user.subscriber?.fxa_uid,
|
||||
);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const profileId = await getOnerepProfileId(subscriber.id);
|
||||
|
||||
const latestScan = await getLatestOnerepScanResults(profileId);
|
||||
|
|
|
@ -31,6 +31,9 @@ export async function GET() {
|
|||
const subscriber = await getSubscriberByFxaUid(
|
||||
session.user.subscriber?.fxa_uid,
|
||||
);
|
||||
if (!subscriber) {
|
||||
throw new Error("No subscriber found for current session.");
|
||||
}
|
||||
const profileId = await getOnerepProfileId(subscriber.id);
|
||||
|
||||
const scanResults = await getLatestOnerepScanResults(profileId);
|
||||
|
|
|
@ -31,6 +31,9 @@ import { GAParams } from "@next/third-parties/dist/types/google";
|
|||
import Script, { ScriptProps } from "next/script";
|
||||
import { useEffect } from "react";
|
||||
|
||||
// We don't send Analytics events in tests:
|
||||
/* c8 ignore start */
|
||||
|
||||
let currDataLayerName: string | undefined = undefined;
|
||||
|
||||
/**
|
||||
|
@ -39,9 +42,9 @@ let currDataLayerName: string | undefined = undefined;
|
|||
* @param props
|
||||
*/
|
||||
export const GoogleAnalyticsWorkaround = (
|
||||
props: GAParams & { nonce: ScriptProps["nonce"] },
|
||||
props: GAParams & { nonce?: ScriptProps["nonce"]; debugMode?: boolean },
|
||||
) => {
|
||||
const { gaId, dataLayerName = "dataLayer", nonce } = props;
|
||||
const { gaId, dataLayerName = "dataLayer", nonce, debugMode } = props;
|
||||
|
||||
if (currDataLayerName === undefined) {
|
||||
currDataLayerName = dataLayerName;
|
||||
|
@ -70,7 +73,7 @@ export const GoogleAnalyticsWorkaround = (
|
|||
function gtag(){window['${dataLayerName}'].push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', '${gaId}');`,
|
||||
gtag('config', '${gaId}', { 'debug_mode': ${debugMode} });`,
|
||||
}}
|
||||
nonce={nonce}
|
||||
/>
|
||||
|
@ -83,17 +86,22 @@ export const GoogleAnalyticsWorkaround = (
|
|||
);
|
||||
};
|
||||
|
||||
export const sendGAEvent = (...args: object[]) => {
|
||||
export const sendGAEvent = (type: "event", eventName: string, args: object) => {
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currDataLayerName === undefined) {
|
||||
console.warn(`@next/third-parties: GA has not been initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window[currDataLayerName]) {
|
||||
window[currDataLayerName].push(...args);
|
||||
window.gtag(type, eventName, args);
|
||||
} else {
|
||||
console.warn(
|
||||
`@next/third-parties: GA dataLayer ${currDataLayerName} does not exist`,
|
||||
);
|
||||
}
|
||||
};
|
||||
/* c8 ignore stop */
|
||||
|
|
|
@ -16,13 +16,10 @@ import {
|
|||
hasPremium,
|
||||
} from "../../functions/universal/user";
|
||||
import styles from "./UpsellBadge.module.scss";
|
||||
// TODO: The use of `useGA` is restricted and will be cleaned up
|
||||
// together with MNTOR-2335.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useGa } from "../../hooks/useGa";
|
||||
import { useTelemetry } from "../../hooks/useTelemetry";
|
||||
import { CountryCodeContext } from "../../../contextProviders/country-code";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { sendGAEvent } from "./GoogleAnalyticsWorkaround";
|
||||
|
||||
export type UpsellButtonProps = {
|
||||
monthlySubscriptionUrl: string;
|
||||
|
@ -38,7 +35,6 @@ export function UpsellButton(
|
|||
label: string;
|
||||
},
|
||||
) {
|
||||
const { gtag } = useGa();
|
||||
const recordTelemetry = useTelemetry();
|
||||
const pathname = usePathname();
|
||||
|
||||
|
@ -50,13 +46,9 @@ export function UpsellButton(
|
|||
button_id: "nav_upsell",
|
||||
});
|
||||
}
|
||||
gtag.record({
|
||||
type: "event",
|
||||
name: "premium_upsell_modal",
|
||||
params: {
|
||||
sendGAEvent("event", "premium_upsell_modal", {
|
||||
action: isOpen ? "opened" : "closed",
|
||||
page_location: pathname,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 4.1 KiB |
После Ширина: | Высота: | Размер: 5.9 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/absolutepeoplesearch.com.png
Normal file
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 6.3 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/advancedbackgroundchecks.com.png
Normal file
После Ширина: | Высота: | Размер: 7.2 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/affordablebackgroundchecks.com.png
Normal file
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 7.4 KiB |
После Ширина: | Высота: | Размер: 7.3 KiB |
После Ширина: | Высота: | Размер: 16 KiB |
После Ширина: | Высота: | Размер: 13 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/backgroundcheckers.net.png
Normal file
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 9.7 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 7.2 KiB |
После Ширина: | Высота: | Размер: 6.2 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 5.7 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 13 KiB |
После Ширина: | Высота: | Размер: 7.7 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 8.9 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/fastbackgroundcheck.com.png
Normal file
После Ширина: | Высота: | Размер: 6.9 KiB |
После Ширина: | Высота: | Размер: 6.5 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/freebackgroundcheck.org.png
Normal file
После Ширина: | Высота: | Размер: 16 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/freepeopledirectory.com.png
Normal file
После Ширина: | Высота: | Размер: 6.4 KiB |
После Ширина: | Высота: | Размер: 9.5 KiB |
После Ширина: | Высота: | Размер: 8.2 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 5.8 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 6.1 KiB |
После Ширина: | Высота: | Размер: 8.7 KiB |
После Ширина: | Высота: | Размер: 6.5 KiB |
После Ширина: | Высота: | Размер: 8.4 KiB |
После Ширина: | Высота: | Размер: 3.1 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 8.6 KiB |
После Ширина: | Высота: | Размер: 6.0 KiB |
После Ширина: | Высота: | Размер: 9.5 KiB |
После Ширина: | Высота: | Размер: 7.0 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 6.2 KiB |
После Ширина: | Высота: | Размер: 5.8 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
После Ширина: | Высота: | Размер: 7.1 KiB |
После Ширина: | Высота: | Размер: 8.5 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 6.3 KiB |
После Ширина: | Высота: | Размер: 7.0 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/people-background-check.com.png
Normal file
После Ширина: | Высота: | Размер: 5.8 KiB |
Двоичные данные
src/app/components/client/assets/data-brokers/people.yellowpages.com.png
Normal file
После Ширина: | Высота: | Размер: 5.5 KiB |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 9.0 KiB |
После Ширина: | Высота: | Размер: 11 KiB |
После Ширина: | Высота: | Размер: 4.5 KiB |
После Ширина: | Высота: | Размер: 4.8 KiB |