Merged PR 1674: Language switching issues
Related work items: #16185, #16630
This commit is contained in:
@@ -36,3 +36,6 @@ pages/polling.js
|
||||
# dev apis to no include
|
||||
|
||||
pages/api/endpoint/getaccounts_api.js
|
||||
|
||||
# certs
|
||||
certificates/*
|
||||
|
||||
@@ -2144,3 +2144,19 @@ export const createCRMTask = async (formValues) => {
|
||||
consoleLogger(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getPreferredLanguage = async (email) => {
|
||||
return axios
|
||||
.get(
|
||||
BASE_URL +
|
||||
"/api/endpoint/getpreferredlanguage_api?emailAddress=" +
|
||||
email
|
||||
)
|
||||
.then((res) => {
|
||||
return res.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return error.response;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useRouter } from "next/router";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { updateAccount } from "../../actions";
|
||||
import { setCookie } from "nookies";
|
||||
|
||||
const PersonalDetailsComplete = (props) => {
|
||||
const {
|
||||
@@ -17,14 +18,39 @@ const PersonalDetailsComplete = (props) => {
|
||||
const router = useRouter();
|
||||
const { locale } = router;
|
||||
|
||||
// let contactid = props.accountDetails.loggedinUserId;
|
||||
// let updateBody = props.props.props.form.personalDetailsForm.values;
|
||||
// let prefLang =
|
||||
// props.props.props.form.personalDetailsForm.values
|
||||
// .pinswg_preferredlanguage;
|
||||
|
||||
const { loggedinUserId } = props.accountDetails || {};
|
||||
const formValues = props.props?.props?.form?.personalDetailsForm?.values;
|
||||
const prefLang = formValues?.pinswg_preferredlanguage;
|
||||
|
||||
useEffect(() => {
|
||||
let contactid = props.accountDetails.loggedinUserId;
|
||||
let updateBody = props.props.props.form.personalDetailsForm.values;
|
||||
updateAccount(contactid, updateBody);
|
||||
}, [
|
||||
props.accountDetails.loggedinUserId,
|
||||
props.props.props.form.personalDetailsForm.values,
|
||||
]);
|
||||
// let contactid = props.accountDetails.loggedinUserId;
|
||||
// let updateBody = props.props.props.form.personalDetailsForm.values;
|
||||
// let prefLang =
|
||||
// props.props.props.form.personalDetailsForm.values
|
||||
// .pinswg_preferredlanguage;
|
||||
if (!loggedinUserId || !formValues) return;
|
||||
|
||||
console.log("=============== pref lang", prefLang);
|
||||
const userLang = prefLang == "846040000" ? "cy" : "en";
|
||||
setCookie(null, "pedw_locale", userLang, {
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
});
|
||||
|
||||
console.log("Set cookie to:", userLang);
|
||||
|
||||
async function doUpdate() {
|
||||
const response = await updateAccount(loggedinUserId, formValues);
|
||||
}
|
||||
|
||||
doUpdate();
|
||||
}, [loggedinUserId, formValues, prefLang]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -33,10 +59,7 @@ const PersonalDetailsComplete = (props) => {
|
||||
{/* <p className="govuk-body">{t("account:account-complete-para-2")}</p> */}
|
||||
|
||||
<p className="govuk-body">
|
||||
<Link
|
||||
href={"/myportal?q=" + props.accountDetails.loggedinUserId}
|
||||
className="govuk-link"
|
||||
>
|
||||
<Link href={"/"} className="govuk-link">
|
||||
{" "}
|
||||
{t("account:account-complete-return-to-portal-link")}
|
||||
</Link>
|
||||
|
||||
@@ -14,80 +14,9 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import NextAuth from "next-auth";
|
||||
import EmailProvider from "next-auth/providers/email";
|
||||
import { consoleLogger } from "../../../actions";
|
||||
import { consoleLogger, getPreferredLanguage } from "../../../actions";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
// For more information on each option (and a full list of options) go to
|
||||
// https://next-auth.js.org/configuration/options
|
||||
// export default NextAuth({
|
||||
// // https://next-auth.js.org/configuration/providers
|
||||
// providers: [
|
||||
// EmailProvider({
|
||||
// // server: {
|
||||
// // host: process.env.EMAIL_SERVER_HOST,
|
||||
// // port: process.env.EMAIL_SERVER_PORT,
|
||||
// // auth: {
|
||||
// // user: process.env.EMAIL_SERVER_USER,
|
||||
// // pass: process.env.EMAIL_SERVER_PASSWORD,
|
||||
// // },
|
||||
// // },
|
||||
// // from: process.env.EMAIL_FROM,
|
||||
// maxAge: 10 * 60, // Magic links are valid for 10 min only
|
||||
// async sendVerificationRequest({
|
||||
// identifier: email,
|
||||
// url,
|
||||
// provider: { server, from },
|
||||
// }) {
|
||||
// const { host } = new URL(url);
|
||||
// const templateId = "b1b5704b-9bb8-4deb-a75c-d887ca902661";
|
||||
// const emailAddress = email;
|
||||
// const personalisation = {
|
||||
// "emailAddress": email,
|
||||
// "signInLink": url,
|
||||
// "linkExpiry": 10 * 60,
|
||||
// };
|
||||
// const reference = "PEDW-SIGNIN";
|
||||
|
||||
// var NotifyClient =
|
||||
// require("notifications-node-client").NotifyClient;
|
||||
|
||||
// const notifyClient = new NotifyClient(
|
||||
// process.env.NOTIFY_API_KEY
|
||||
// );
|
||||
|
||||
// await notifyClient
|
||||
// .sendEmail(templateId, emailAddress, {
|
||||
// personalisation: personalisation,
|
||||
// reference: reference,
|
||||
// // emailReplyToId: emailReplyToId,
|
||||
// })
|
||||
// //.then((response) => console.log(response))
|
||||
// .catch((error) => consoleLogger(err));
|
||||
// },
|
||||
// }),
|
||||
// ],
|
||||
// adapter: PrismaAdapter(prisma),
|
||||
// secret: process.env.SECRET,
|
||||
// session: {
|
||||
// jwt: true,
|
||||
// pages: {},
|
||||
// theme: "dark",
|
||||
// debug: true,
|
||||
// },
|
||||
// pages: {
|
||||
// signIn: "/auth/signin",
|
||||
// //signOut: "/auth/signout",
|
||||
// error: "/auth/error", // Error code passed in query string as ?error=
|
||||
// verifyRequest: "/auth/verify-request", // (used for check email message)
|
||||
// newUser: "/account/register", // New users will be directed here on first sign in (leave the property out if not of interest)
|
||||
// },
|
||||
// callbacks: {
|
||||
// session: async (session, user) => {
|
||||
// //console.log(user);
|
||||
// return Promise.resolve(session);
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
const parseUrl = (url) => {
|
||||
const defaultUrl = new URL(
|
||||
@@ -169,7 +98,9 @@ const authOptions = (locale, req, res) => {
|
||||
"&token=" +
|
||||
searchParams.get("token") +
|
||||
"&email=" +
|
||||
encodeURIComponent(email)
|
||||
encodeURIComponent(email) +
|
||||
"&locale=" +
|
||||
effectiveLocale
|
||||
: url;
|
||||
|
||||
let formURL = appendParamsAndPathToNewUrl(url, newURL);
|
||||
@@ -191,23 +122,43 @@ const authOptions = (locale, req, res) => {
|
||||
const { host, port, protocol, token, searchParams } =
|
||||
new URL(url);
|
||||
|
||||
const baseDomain = `${protocol}//${host}`;
|
||||
const templateId = "b1b5704b-9bb8-4deb-a75c-d887ca902661";
|
||||
const templateIdcy = "0614ce53-cd5f-421f-a1a5-8c8486a9113a";
|
||||
const emailAddress = email;
|
||||
|
||||
const queryLocale = req?.query?.locale || req?.body?.locale;
|
||||
const cookieLocale = req.cookies["pedw_locale"];
|
||||
let locale = queryLocale || cookieLocale || "en";
|
||||
|
||||
console.log(
|
||||
"============================================================\n",
|
||||
"verification url----: " + url + "\n",
|
||||
"============================================================\n"
|
||||
);
|
||||
|
||||
const prefLang = await getPreferredLanguage(emailAddress);
|
||||
|
||||
const registeredPrefLanguage =
|
||||
prefLang.value.length > 0
|
||||
? prefLang.value[0].pinswg_preferredlanguage ===
|
||||
846040000
|
||||
? "cy"
|
||||
: "en"
|
||||
: "en";
|
||||
|
||||
const effectiveLocale = registeredPrefLanguage ?? locale;
|
||||
let newURL =
|
||||
locale == "cy"
|
||||
? process.env.CY_API_ROOT +
|
||||
effectiveLocale == "cy"
|
||||
? baseDomain +
|
||||
"/api/auth/callback/email?callbackUrl=" +
|
||||
encodeURIComponent(process.env.CY_API_ROOT) +
|
||||
encodeURIComponent(baseDomain + "/cy") +
|
||||
"&token=" +
|
||||
searchParams.get("token") +
|
||||
"&email=" +
|
||||
encodeURIComponent(email)
|
||||
encodeURIComponent(email) +
|
||||
"&locale=" +
|
||||
effectiveLocale
|
||||
: url;
|
||||
|
||||
let formURL = appendParamsAndPathToNewUrl(url, newURL);
|
||||
@@ -240,7 +191,7 @@ const authOptions = (locale, req, res) => {
|
||||
|
||||
await notifyClient
|
||||
.sendEmail(
|
||||
locale == "cy" ? templateIdcy : templateId,
|
||||
effectiveLocale == "cy" ? templateIdcy : templateId,
|
||||
emailAddress,
|
||||
{
|
||||
personalisation: personalisation,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @swagger
|
||||
* /api/endpoint/getemailaccountcheck_api:
|
||||
* get:
|
||||
* tags: [Login]
|
||||
* description: Returns portal login
|
||||
* summary: Phase 2
|
||||
* parameters:
|
||||
* - name: emailAddress
|
||||
* in: query
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: hello world
|
||||
*/
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
azureHeaders,
|
||||
consoleLogger,
|
||||
getToken,
|
||||
getPreferredLanguage,
|
||||
} from "../../../actions";
|
||||
|
||||
const WORDKEY = process.env.HASHKEY;
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
const hashAPIPath = (queryPath) => {
|
||||
var hashlink = CryptoJS.HmacSHA256(
|
||||
queryPath,
|
||||
CryptoJS.enc.Hex.parse(WORDKEY)
|
||||
);
|
||||
hashlink = hashlink.toString(CryptoJS.enc.Hex);
|
||||
return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink;
|
||||
};
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
var token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=pinswg_preferredlanguage,contactid";
|
||||
|
||||
console.log(queryUrl);
|
||||
// var queryUrl = "contacts/?$count=true&$select=emailaddress1, contactid";
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
})
|
||||
: res.status(400).json();
|
||||
|
||||
return apiResponse;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import CookieBanner from "../../components/cookieBanner";
|
||||
import Footer from "../../components/footer";
|
||||
import Header from "../../components/header";
|
||||
import ServiceBanner from "../../components/servicebanner";
|
||||
import { destroyCookie, setCookie } from "nookies";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { getCsrfToken } from "next-auth/react";
|
||||
|
||||
@@ -16,6 +18,14 @@ const Error = (props) => {
|
||||
|
||||
const { error } = useRouter().query;
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.clear(),
|
||||
destroyCookie(null, "next-auth.csrf-token", { path: "/" }),
|
||||
destroyCookie(null, "next-auth.callback-url", { path: "/" }),
|
||||
destroyCookie(null, "pedw_locale", { path: "/" }),
|
||||
destroyCookie(null, "pinsUser", { path: "/" });
|
||||
}, []);
|
||||
|
||||
const errors = {
|
||||
Signin: t("auth:auth-error-Signin-label"),
|
||||
OAuthSignin: t("auth:auth-error-OAuthSignin-label"),
|
||||
|
||||
@@ -7,6 +7,8 @@ import CookieBanner from "../../components/cookieBanner";
|
||||
import Footer from "../../components/footer";
|
||||
import Header from "../../components/header";
|
||||
import ServiceBanner from "../../components/servicebanner";
|
||||
import { getPreferredLanguage } from "../../actions";
|
||||
import { setCookie } from "nookies";
|
||||
|
||||
const SignIn = (props) => {
|
||||
let { t, lang } = useTranslation();
|
||||
@@ -25,6 +27,35 @@ const SignIn = (props) => {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
setButtonDisabled(true);
|
||||
|
||||
const email = event.target.email.value;
|
||||
|
||||
try {
|
||||
const res = await getPreferredLanguage(encodeURIComponent(email));
|
||||
const data = await res;
|
||||
|
||||
const userLang =
|
||||
data.value.length > 0
|
||||
? data.value[0].pinswg_preferredlanguage === 846040000
|
||||
? "cy"
|
||||
: "en"
|
||||
: "en";
|
||||
|
||||
// Modify callbackUrl hidden input to add/update lang param
|
||||
const url = new URL(event.target.callbackUrl.value);
|
||||
url.searchParams.set("locale", userLang);
|
||||
|
||||
setCookie(null, "pedw_locale", userLang, {
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 365,
|
||||
});
|
||||
|
||||
event.target.callbackUrl.value = url.toString();
|
||||
} catch (error) {
|
||||
console.error("Failed to get preferred language", error);
|
||||
}
|
||||
|
||||
// Submit form after updating callbackUrl
|
||||
event.target.submit();
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import {
|
||||
touch,
|
||||
SubmissionError,
|
||||
} from "redux-form";
|
||||
import { connect, submitError } from "react-redux";
|
||||
import { connect } from "react-redux";
|
||||
import { createCRMTask, sendEmail } from "../actions";
|
||||
|
||||
import { useState } from "react";
|
||||
@@ -527,7 +527,7 @@ export default connect(
|
||||
destroyOnUnmount: false,
|
||||
touchOnBlur: true,
|
||||
touchOnChange: true,
|
||||
onSubmitFail: (errors, dispatch, subnittError, props) => {
|
||||
onSubmitFail: (errors, dispatch, submitError, props) => {
|
||||
if (errors) {
|
||||
// Touch all fields with errors so validation messages appear
|
||||
const errorFields = Object.keys(errors);
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
touch,
|
||||
SubmissionError,
|
||||
} from "redux-form";
|
||||
import { connect, submitError } from "react-redux";
|
||||
import { connect } from "react-redux";
|
||||
import { createCRMTask, sendEmail } from "../../actions";
|
||||
|
||||
import { useState } from "react";
|
||||
@@ -528,7 +528,7 @@ export default connect(
|
||||
destroyOnUnmount: false,
|
||||
touchOnBlur: true,
|
||||
touchOnChange: true,
|
||||
onSubmitFail: (errors, dispatch, subnittError, props) => {
|
||||
onSubmitFail: (errors, dispatch, submitError, props) => {
|
||||
if (errors) {
|
||||
// Touch all fields with errors so validation messages appear
|
||||
const errorFields = Object.keys(errors);
|
||||
|
||||
@@ -222,9 +222,7 @@ export const getServerSideProps = wrapper.getServerSideProps(
|
||||
|
||||
// Determine preferred locale (cookie overrides)
|
||||
let preferredLocale =
|
||||
accountDetails.pinswg_preferredlanguage === "846040000"
|
||||
? "cy"
|
||||
: "en";
|
||||
accountDetails.pinswg_preferredlanguage === 846040000 ? "cy" : "en";
|
||||
if (cookies.pedw_locale) {
|
||||
preferredLocale = cookies.pedw_locale;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
const next = require("next");
|
||||
const { createServer } = require("http");
|
||||
const { createServer: createHttpServer } = require("http");
|
||||
const { createServer: createHttpsServer } = require("https");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const appInsights = require("applicationinsights");
|
||||
|
||||
const initAppInsights = (instrumentationKey) => {
|
||||
@@ -26,44 +29,75 @@ const startServer = async (config) => {
|
||||
};
|
||||
|
||||
const app = next(serverOptions);
|
||||
await app.prepare(); // prepare BEFORE starting the server
|
||||
await app.prepare();
|
||||
|
||||
const handleNextRequests = app.getRequestHandler();
|
||||
|
||||
const srv = createServer((req, res) => {
|
||||
const httpsOptions = {
|
||||
key: fs.readFileSync(
|
||||
path.join(__dirname, "certificates", "cert-key.pem")
|
||||
),
|
||||
cert: fs.readFileSync(path.join(__dirname, "certificates", "cert.pem")),
|
||||
};
|
||||
|
||||
// HTTPS server on port 3000
|
||||
const httpsServer = createHttpsServer(httpsOptions, (req, res) => {
|
||||
if (config.useAppInsights) {
|
||||
appInsights.defaultClient.trackNodeHttpRequest({
|
||||
request: req,
|
||||
response: res,
|
||||
});
|
||||
}
|
||||
|
||||
handleNextRequests(req, res);
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
srv.on("error", reject);
|
||||
srv.on("listening", () => resolve());
|
||||
srv.listen(config.port, config.hostname);
|
||||
// HTTP server on port 3001 for redirect
|
||||
const httpServer = createHttpServer((req, res) => {
|
||||
const host = req.headers.host?.split(":")[0] || "localhost";
|
||||
res.writeHead(301, {
|
||||
Location: `https://${host}:3000${req.url}`,
|
||||
});
|
||||
res.end();
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
httpsServer.on("error", reject);
|
||||
httpsServer.on("listening", () => {
|
||||
console.log(`HTTPS server listening on port ${config.port}`);
|
||||
resolve();
|
||||
});
|
||||
httpsServer.listen(config.port, config.hostname);
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
httpServer.on("error", reject);
|
||||
httpServer.on("listening", () => {
|
||||
console.log(
|
||||
`HTTP server listening on port ${config.httpPort} (redirecting to HTTPS)`
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
httpServer.listen(config.httpPort, config.hostname);
|
||||
});
|
||||
|
||||
// Graceful shutdown handling
|
||||
process.on("SIGTERM", () => {
|
||||
console.log("Received SIGTERM, shutting down gracefully...");
|
||||
srv.close(() => {
|
||||
console.log("Server closed.");
|
||||
process.exit(0);
|
||||
httpsServer.close(() => {
|
||||
httpServer.close(() => {
|
||||
console.log("Servers closed.");
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
};
|
||||
|
||||
// App configuration
|
||||
const startTime = Date.now();
|
||||
const serverConfig = {
|
||||
hostname: "0.0.0.0", // Required for Azure App Service
|
||||
port: parseInt(process.env.PORT, 10) || 3000,
|
||||
hostname: "0.0.0.0",
|
||||
port: 3000, // HTTPS port
|
||||
httpPort: 3001, // HTTP redirect port
|
||||
env: process.env.APP_ENV || process.env.NODE_ENV || "production",
|
||||
useAppInsights: initAppInsights(
|
||||
process.env.NEXT_PUBLIC_APPINSIGHTS_INSTRUMENTATIONKEY
|
||||
@@ -73,7 +107,7 @@ const serverConfig = {
|
||||
startServer(serverConfig)
|
||||
.then(() => {
|
||||
console.log(
|
||||
`Server ready on http://${serverConfig.hostname}:${serverConfig.port} [${serverConfig.env}]`
|
||||
`Server ready on https://${serverConfig.hostname}:${serverConfig.port} [${serverConfig.env}]`
|
||||
);
|
||||
|
||||
if (serverConfig.useAppInsights) {
|
||||
|
||||
Reference in New Issue
Block a user