From 1b2f8e29c6ede33fa5730682ac60bf729ed3e2b1 Mon Sep 17 00:00:00 2001 From: robbond Date: Wed, 8 Apr 2026 12:17:28 +0100 Subject: [PATCH] defect fix for language and login - crm precedence then current session --- components/account/personaldetailsComplete.js | 3 +- components/header.js | 11 +- pages/api/auth/[...nextauth].js | 123 +++++++++++------- pages/api/auth/resolve-locale.js | 43 ++++++ pages/api/endpoint/getportallogin_api.js | 2 +- pages/auth/signin.js | 49 +++++-- 6 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 pages/api/auth/resolve-locale.js diff --git a/components/account/personaldetailsComplete.js b/components/account/personaldetailsComplete.js index 6a50ef86..ff2199c0 100644 --- a/components/account/personaldetailsComplete.js +++ b/components/account/personaldetailsComplete.js @@ -40,8 +40,7 @@ const PersonalDetailsComplete = (props) => { console.log("=============== pref lang", prefLang); setCookie(null, "pedw_locale", userLang, { - path: "/", - maxAge: 60 * 60 * 24 * 365 + path: "/" }); console.log("Set cookie to:", userLang); diff --git a/components/header.js b/components/header.js index d7ae3032..80e158fa 100644 --- a/components/header.js +++ b/components/header.js @@ -64,7 +64,7 @@ const Header = (props) => { "/myportal/contactus", "/unsubscribe/[watchlistid]", "/unsubscribeall/[watchlistid]", - "/status", + "/status" ]; const hasContactLink = [ @@ -72,7 +72,7 @@ const Header = (props) => { "/dns/help", "/dns/contact-us", "/dns/applications", - "/dns/application-view", + "/dns/application-view" ]; let domainSwitch; @@ -91,7 +91,7 @@ const Header = (props) => { destroyCookie(null, "pedw_locale", { path: "/" }), destroyCookie(null, "pinsUser", { path: "/" }), signOut({ - callbackUrl: locale == "cy" ? "/cy/allgofnodi" : "/logout", + callbackUrl: locale == "cy" ? "/cy/allgofnodi" : "/logout" })); }; @@ -136,8 +136,7 @@ const Header = (props) => { // Set cookie first setCookie(null, "pedw_locale", newLocale, { - path: "/", - maxAge: 60 * 60 * 24 * 365, // 1 year + path: "/" }); // Then navigate with the new locale @@ -270,7 +269,7 @@ const mapDispatchToProps = (dispatch) => { return { setLogout: () => { dispatch(setLogout()); - }, + } }; }; diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].js index 7a713e47..9e017930 100644 --- a/pages/api/auth/[...nextauth].js +++ b/pages/api/auth/[...nextauth].js @@ -15,9 +15,12 @@ import { PrismaClient } from "@prisma/client"; import NextAuth from "next-auth"; import EmailProvider from "next-auth/providers/email"; import { consoleLogger } from "../../../actions/core/logger"; +import { getPortalLogin } from "../../../actions/services/accountService"; const prisma = new PrismaClient(); +const WELSH_LANGUAGE_CODE = 846040000; + const appendParamsAndPathToNewUrl = (fromUrl, toUrl) => { const fromUrlObj = new URL(fromUrl); const params = fromUrlObj.searchParams; @@ -34,14 +37,64 @@ const appendParamsAndPathToNewUrl = (fromUrl, toUrl) => { return toUrlObj.toString(); }; -const resolveLocale = (req) => +const resolveRequestLocale = (req) => req?.query?.locale || req?.body?.locale || req?.cookies?.pedw_locale || "en"; +const resolveCrmLocale = async (email) => { + if (!email) return null; + + try { + const portalUserObj = await getPortalLogin(email); + const preferredLanguage = + portalUserObj?.value?.[0]?.pinswg_preferredlanguage; + + if (preferredLanguage === WELSH_LANGUAGE_CODE) { + return "cy"; + } + + if (preferredLanguage != null) { + return "en"; + } + + return null; + } catch (error) { + consoleLogger(error); + return null; + } +}; + +const resolveEffectiveLocale = async (req, email) => { + const crmLocale = await resolveCrmLocale(email); + if (crmLocale) return crmLocale; + + return resolveRequestLocale(req); +}; + +const buildLocalizedVerificationUrl = ({ url, email, effectiveLocale }) => { + const { host, protocol, searchParams } = new URL(url); + const baseDomain = `${protocol}//${host}`; + + const newURL = + effectiveLocale === "cy" + ? baseDomain + + "/api/auth/callback/email?callbackUrl=" + + encodeURIComponent(baseDomain + "/cy") + + "&token=" + + searchParams.get("token") + + "&email=" + + encodeURIComponent(email) + + "&locale=" + + effectiveLocale + : url; + + return appendParamsAndPathToNewUrl(url, newURL); +}; + const authOptions = (req, res) => { - const locale = resolveLocale(req); + const requestLocale = resolveRequestLocale(req); return { providers: [ @@ -50,7 +103,10 @@ const authOptions = (req, res) => { name: "emailAPI", type: "email", async sendVerificationRequest({ identifier: email, url }) { - const { host, protocol, searchParams } = new URL(url); + const effectiveLocale = await resolveEffectiveLocale( + req, + email + ); console.log( "============================================================\n", @@ -58,23 +114,11 @@ const authOptions = (req, res) => { "============================================================\n" ); - const baseDomain = `${protocol}//${host}`; - const effectiveLocale = resolveLocale(req); - - const newURL = - effectiveLocale === "cy" - ? baseDomain + - "/api/auth/callback/email?callbackUrl=" + - encodeURIComponent(baseDomain + "/cy") + - "&token=" + - searchParams.get("token") + - "&email=" + - encodeURIComponent(email) + - "&locale=" + - effectiveLocale - : url; - - const formURL = appendParamsAndPathToNewUrl(url, newURL); + const formURL = buildLocalizedVerificationUrl({ + url, + email, + effectiveLocale + }); console.log( "============================================================\n", @@ -90,12 +134,13 @@ const authOptions = (req, res) => { EmailProvider({ maxAge: 2 * 60 * 60, async sendVerificationRequest({ identifier: email, url }) { - const { host, protocol, searchParams } = new URL(url); - - const baseDomain = `${protocol}//${host}`; const templateId = "b1b5704b-9bb8-4deb-a75c-d887ca902661"; const templateIdcy = "0614ce53-cd5f-421f-a1a5-8c8486a9113a"; - const effectiveLocale = resolveLocale(req); + + const effectiveLocale = await resolveEffectiveLocale( + req, + email + ); console.log( "============================================================\n", @@ -103,20 +148,11 @@ const authOptions = (req, res) => { "============================================================\n" ); - const newURL = - effectiveLocale === "cy" - ? baseDomain + - "/api/auth/callback/email?callbackUrl=" + - encodeURIComponent(baseDomain + "/cy") + - "&token=" + - searchParams.get("token") + - "&email=" + - encodeURIComponent(email) + - "&locale=" + - effectiveLocale - : url; - - const formURL = appendParamsAndPathToNewUrl(url, newURL); + const formURL = buildLocalizedVerificationUrl({ + url, + email, + effectiveLocale + }); console.log( "============================================================\n", @@ -178,11 +214,11 @@ const authOptions = (req, res) => { } }, pages: { - signIn: (locale === "cy" ? "/cy" : "") + "/auth/signin", - error: (locale === "cy" ? "/cy" : "") + "/auth/error", + signIn: (requestLocale === "cy" ? "/cy" : "") + "/auth/signin", + error: (requestLocale === "cy" ? "/cy" : "") + "/auth/error", verifyRequest: - (locale === "cy" ? "/cy" : "") + "/auth/verify-request", - newUser: (locale === "cy" ? "/cy" : "") + "/account/register" + (requestLocale === "cy" ? "/cy" : "") + "/auth/verify-request", + newUser: (requestLocale === "cy" ? "/cy" : "") + "/account/register" }, callbacks: { session: async (session, user) => { @@ -196,9 +232,8 @@ const authOptions = (req, res) => { if (url.startsWith("/")) return `${baseUrl}${url}`; if (new URL(url).origin === baseUrl) return url; - const effectiveLocale = resolveLocale(req); const newUrl = - effectiveLocale === "cy" + requestLocale === "cy" ? process.env.CY_API_ROOT : process.env.NEXTAUTH_URL; diff --git a/pages/api/auth/resolve-locale.js b/pages/api/auth/resolve-locale.js new file mode 100644 index 00000000..f89bf9df --- /dev/null +++ b/pages/api/auth/resolve-locale.js @@ -0,0 +1,43 @@ +import { getPortalLogin } from "../../../actions/services/accountService"; +import { consoleLogger } from "../../../actions/core/logger"; + +const WELSH_LANGUAGE_CODE = 846040000; + +const resolveRequestLocale = (req) => + req?.query?.locale || + req?.body?.locale || + req?.cookies?.pedw_locale || + "en"; + +export default async function handler(req, res) { + if (req.method !== "POST") { + return res.status(405).json({ message: "Method not allowed" }); + } + + const email = String(req.body?.email || "") + .trim() + .toLowerCase(); + const sessionLocale = resolveRequestLocale(req); + + if (!email) { + return res.status(200).json({ locale: sessionLocale }); + } + + try { + const portalUserObj = await getPortalLogin(email); + const preferredLanguage = + portalUserObj?.value?.[0]?.pinswg_preferredlanguage; + + const locale = + preferredLanguage === WELSH_LANGUAGE_CODE + ? "cy" + : preferredLanguage != null + ? "en" + : sessionLocale; + + return res.status(200).json({ locale }); + } catch (error) { + consoleLogger(error); + return res.status(200).json({ locale: sessionLocale }); + } +} diff --git a/pages/api/endpoint/getportallogin_api.js b/pages/api/endpoint/getportallogin_api.js index 3e7e3a97..00871021 100644 --- a/pages/api/endpoint/getportallogin_api.js +++ b/pages/api/endpoint/getportallogin_api.js @@ -64,7 +64,7 @@ export default async function ApiProxy(req, res) { const queryUrl = "contacts?$filter=emailaddress1 eq '" + emailAddress + - "' and statuscode eq 1&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname"; + "' and statuscode eq 1&$count=true&$select=pinswg_preferredlanguage,emailaddress1,contactid,yomifullname,firstname,lastname"; const relayPolicy = RELAY_POLICY_STRICT_LOGIN; diff --git a/pages/auth/signin.js b/pages/auth/signin.js index b0113660..2de256c8 100644 --- a/pages/auth/signin.js +++ b/pages/auth/signin.js @@ -19,17 +19,48 @@ const SignIn = (props) => { event.preventDefault(); setButtonDisabled(true); - const currentLocale = lang || "en"; - const url = new URL(event.target.callbackUrl.value); - url.searchParams.set("locale", currentLocale); + try { + const email = String(event.target.email.value || "").trim(); + const currentLocale = lang || "en"; - setCookie(null, "pedw_locale", currentLocale, { - path: "/", - maxAge: 60 * 60 * 24 * 365 - }); + const response = await fetch("/api/auth/resolve-locale", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + email, + locale: currentLocale + }) + }); - event.target.callbackUrl.value = url.toString(); - event.target.submit(); + const data = await response.json(); + const resolvedLocale = + data?.locale === "cy" || data?.locale === "en" + ? data.locale + : currentLocale; + + const url = new URL(event.target.callbackUrl.value); + url.searchParams.set("locale", resolvedLocale); + + setCookie(null, "pedw_locale", resolvedLocale, { + path: "/" + }); + + event.target.callbackUrl.value = url.toString(); + event.target.submit(); + } catch (error) { + const fallbackLocale = lang || "en"; + const url = new URL(event.target.callbackUrl.value); + url.searchParams.set("locale", fallbackLocale); + + setCookie(null, "pedw_locale", fallbackLocale, { + path: "/" + }); + + event.target.callbackUrl.value = url.toString(); + event.target.submit(); + } }; return (