mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
feat(i18n): update medical verification label to include sea service verification
fix(layouts): import baseApi in BackofficeLayout and PortalLayout for API state management refactor(auth): remove clearCurrentProfile call on logout and improve cookie removal logic chore(auth-storage): enhance cookie management with secure and sameSite attributes, and streamline cleanup process
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,7 @@ export const en = {
|
||||
applications: 'Applications',
|
||||
paymentConfig: 'Payment Config',
|
||||
analytics: 'Analytics',
|
||||
medicalVerification: 'Medical Verification',
|
||||
medicalVerification: 'Medical and Sea Service Verification',
|
||||
locations: 'Locations',
|
||||
configuration: 'Configuration',
|
||||
profile: 'Profile',
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AppHeader, AppSidebar } from '@ema-platform/ui';
|
||||
import type { NavItem, NavSection } from '@ema-platform/ui';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
import { AppTopNav, filterByPermissions } from '@ema-platform/ui';
|
||||
import { useGetQueueCountsQuery } from '@ema-platform/api';
|
||||
import { baseApi, useGetQueueCountsQuery } from '@ema-platform/api';
|
||||
import { usePermissions } from '@ema-platform/auth';
|
||||
import { SUPPORTED_LANGUAGES } from '../i18n/config';
|
||||
import { useAppDispatch, useAppSelector } from '../store/hooks';
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useDispatch } from "react-redux";
|
||||
import { notify, AppHeader, AppSidebar } from "@ema-platform/ui";
|
||||
import type { NavItem } from "@ema-platform/ui";
|
||||
import { BrandMark, logout } from "@ema-platform/auth";
|
||||
import { useGetUnseenNotificationsQuery } from "@ema-platform/api";
|
||||
import { baseApi, useGetUnseenNotificationsQuery } from "@ema-platform/api";
|
||||
import { SUPPORTED_LANGUAGES } from "../i18n/config";
|
||||
import { useAppSelector } from "../store/hooks";
|
||||
|
||||
@@ -172,6 +172,7 @@ export function PortalLayout() {
|
||||
|
||||
const handleLogout = () => {
|
||||
dispatch(logout());
|
||||
dispatch(baseApi.util.resetApiState());
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ const authSlice = createSlice({
|
||||
state.isAuthenticated = false;
|
||||
state.currentProfile = null;
|
||||
authStorage.clear();
|
||||
clearCurrentProfile();
|
||||
},
|
||||
setToken(state, action: PayloadAction<string>) {
|
||||
state.token = action.payload;
|
||||
|
||||
@@ -1,6 +1,75 @@
|
||||
import Cookies from "js-cookie";
|
||||
const rememberMe = localStorage.getItem("rememberMe") === "true";
|
||||
const COOKIE_EXPIRES_DAYS = rememberMe ? 15 : 1;
|
||||
|
||||
function getCookieExpiresDays(): number {
|
||||
if (typeof window === "undefined") return 1;
|
||||
try {
|
||||
const rememberMe = localStorage.getItem("rememberMe") === "true";
|
||||
return rememberMe ? 15 : 1;
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const isHttps =
|
||||
typeof window !== "undefined" && window.location.protocol === "https:";
|
||||
|
||||
function removeCookieCompletely(name: string) {
|
||||
if (typeof document === "undefined") return;
|
||||
|
||||
const paths = ["/", ""];
|
||||
const samesites: Array<"strict" | "lax" | "none" | undefined> = [
|
||||
"strict",
|
||||
"lax",
|
||||
undefined,
|
||||
];
|
||||
const secures = [true, false];
|
||||
|
||||
for (const path of paths) {
|
||||
for (const secure of secures) {
|
||||
for (const sameSite of samesites) {
|
||||
try {
|
||||
Cookies.remove(name, { path, secure, sameSite });
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pastDate = "Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
const expireStrings = [
|
||||
`${name}=; path=/; expires=${pastDate}; max-age=0`,
|
||||
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Strict`,
|
||||
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Strict; Secure`,
|
||||
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Lax`,
|
||||
`${name}=; path=/; expires=${pastDate}; max-age=0; SameSite=Lax; Secure`,
|
||||
`${name}=; expires=${pastDate}; max-age=0`,
|
||||
];
|
||||
|
||||
try {
|
||||
const domain = window.location.hostname;
|
||||
const domainParts = domain.split(".");
|
||||
if (domainParts.length > 1) {
|
||||
expireStrings.push(
|
||||
`${name}=; path=/; domain=${domain}; expires=${pastDate}; max-age=0`
|
||||
);
|
||||
expireStrings.push(
|
||||
`${name}=; path=/; domain=.${domain}; expires=${pastDate}; max-age=0`
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
expireStrings.forEach((str) => {
|
||||
try {
|
||||
document.cookie = str;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface Backend {
|
||||
get(k: string): string | null;
|
||||
set(k: string, v: string): void;
|
||||
@@ -15,15 +84,14 @@ const localStore: Backend = {
|
||||
|
||||
const cookieStore: Backend = {
|
||||
get: (k) => Cookies.get(k) ?? null,
|
||||
|
||||
set: (k, v) =>
|
||||
Cookies.set(k, v, {
|
||||
path: "/",
|
||||
secure: true,
|
||||
secure: isHttps,
|
||||
sameSite: "strict",
|
||||
expires: COOKIE_EXPIRES_DAYS,
|
||||
expires: getCookieExpiresDays(),
|
||||
}),
|
||||
remove: (k) => Cookies.remove(k, { path: "/" }),
|
||||
remove: (k) => removeCookieCompletely(k),
|
||||
};
|
||||
|
||||
const AUTH_KEY_NAMES = [
|
||||
@@ -34,6 +102,8 @@ const AUTH_KEY_NAMES = [
|
||||
"current-profile",
|
||||
];
|
||||
|
||||
const KNOWN_PREFIXES = ["ema-backoffice", "ema-portal", "ema-auth", ""];
|
||||
|
||||
let _prefix = "ema-auth";
|
||||
let backend: Backend = localStore;
|
||||
|
||||
@@ -41,7 +111,7 @@ export function configureAuthStorage(prefix: string, useCookies = false) {
|
||||
_prefix = prefix;
|
||||
backend = useCookies ? cookieStore : localStore;
|
||||
if (useCookies) {
|
||||
// one-time cleanup: drop pre-migration localStorage leftovers so tokens live in cookies only
|
||||
// drop pre-migration localStorage leftovers so tokens live in cookies only
|
||||
AUTH_KEY_NAMES.forEach((k) => localStorage.removeItem(`${prefix}-${k}`));
|
||||
}
|
||||
}
|
||||
@@ -78,17 +148,36 @@ export const authStorage = {
|
||||
backend.set(key("current-profile"), JSON.stringify(p)),
|
||||
removeProfile: () => backend.remove(key("current-profile")),
|
||||
clear: () => {
|
||||
[
|
||||
key("auth-token"),
|
||||
key("refresh-token"),
|
||||
key("auth-user"),
|
||||
key("profile-id"),
|
||||
key("current-profile"),
|
||||
key("profile-id"),
|
||||
].forEach((k) => backend.remove(k));
|
||||
Object.keys(Cookies.get()).forEach((name) => {
|
||||
Cookies.remove(name);
|
||||
Cookies.remove(name, { path: "/" });
|
||||
const prefixes = Array.from(new Set([_prefix, ...KNOWN_PREFIXES]));
|
||||
prefixes.forEach((p) => {
|
||||
AUTH_KEY_NAMES.forEach((k) => {
|
||||
const fullKey = p ? `${p}-${k}` : k;
|
||||
removeCookieCompletely(fullKey);
|
||||
try {
|
||||
localStorage.removeItem(fullKey);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
localStorage.removeItem("rememberMe");
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof Cookies !== "undefined") {
|
||||
const allCookies = Cookies.get();
|
||||
if (allCookies) {
|
||||
Object.keys(allCookies).forEach((name) => {
|
||||
removeCookieCompletely(name);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user