strapi-plugin-magic-sessionmanager 4.5.1 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{Analytics-DQyamHg6.mjs → Analytics--qB2cKuD.mjs} +2 -2
- package/dist/_chunks/{Analytics-Cp-WjeV3.js → Analytics-DM4_UffY.js} +2 -2
- package/dist/_chunks/{App-CN4bKWV9.mjs → App-CL5q29Mi.mjs} +2 -2
- package/dist/_chunks/{App-BvYLeEbF.js → App-DF-VsbDW.js} +2 -2
- package/dist/_chunks/{License-Bgcm630d.mjs → License-D9piCp34.mjs} +1 -1
- package/dist/_chunks/{License-DPyeZQWz.js → License-Dmm1d3fQ.js} +1 -1
- package/dist/_chunks/{OnlineUsersWidget-E2FA7rGi.mjs → OnlineUsersWidget-DHsthkt0.mjs} +1 -1
- package/dist/_chunks/{OnlineUsersWidget-BYnqhN3O.js → OnlineUsersWidget-KchZ_ScS.js} +1 -1
- package/dist/_chunks/{Settings-Bt6wy131.js → Settings-CUNaxDWk.js} +255 -2
- package/dist/_chunks/{Settings-D9N7m11p.mjs → Settings-D6ILgR9X.mjs} +255 -2
- package/dist/_chunks/{UpgradePage-DM23ZYVa.mjs → UpgradePage-D697BVWo.mjs} +1 -1
- package/dist/_chunks/{UpgradePage-Bh21Lg-G.js → UpgradePage-Dwrv7g8L.js} +1 -1
- package/dist/_chunks/{index-BRESWp1b.js → index-CTxGMDHr.js} +6 -6
- package/dist/_chunks/{index-BbbrBv3t.mjs → index-CwxKazpc.mjs} +6 -6
- package/dist/_chunks/{useLicense-Bszkymz3.mjs → useLicense-B9WW9s_d.mjs} +1 -1
- package/dist/_chunks/{useLicense-D_wQcoMn.js → useLicense-BEbtA_Zo.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +124 -18
- package/dist/server/index.mjs +124 -18
- package/package.json +1 -1
|
@@ -4,8 +4,8 @@ import { useFetchClient } from "@strapi/strapi/admin";
|
|
|
4
4
|
import styled, { css, keyframes } from "styled-components";
|
|
5
5
|
import { Loader, Typography, Box, Flex, Badge } from "@strapi/design-system";
|
|
6
6
|
import { ChartBubble, Crown, User, Clock, Monitor } from "@strapi/icons";
|
|
7
|
-
import { a as pluginId } from "./index-
|
|
8
|
-
import { u as useLicense } from "./useLicense-
|
|
7
|
+
import { a as pluginId } from "./index-CwxKazpc.mjs";
|
|
8
|
+
import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
|
|
9
9
|
const theme = {
|
|
10
10
|
shadows: {
|
|
11
11
|
sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
|
|
@@ -6,8 +6,8 @@ const admin = require("@strapi/strapi/admin");
|
|
|
6
6
|
const styled = require("styled-components");
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
|
-
const index = require("./index-
|
|
10
|
-
const useLicense = require("./useLicense-
|
|
9
|
+
const index = require("./index-CTxGMDHr.js");
|
|
10
|
+
const useLicense = require("./useLicense-BEbtA_Zo.js");
|
|
11
11
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
12
12
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
13
13
|
const theme = {
|
|
@@ -3,10 +3,10 @@ import { useState, useEffect } from "react";
|
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
import { useFetchClient, useNotification, Page } from "@strapi/strapi/admin";
|
|
5
5
|
import styled, { css, keyframes } from "styled-components";
|
|
6
|
-
import { p as parseUserAgent, a as pluginId$1, g as getTranslation } from "./index-
|
|
6
|
+
import { p as parseUserAgent, a as pluginId$1, g as getTranslation } from "./index-CwxKazpc.mjs";
|
|
7
7
|
import { Modal, Flex, Box, Typography, Divider, Button, Loader, SingleSelect, SingleSelectOption, Thead, Tr, Th, Tbody, Td, Table, TextInput } from "@strapi/design-system";
|
|
8
8
|
import { Check, Information, Monitor, Server, Clock, Cross, Earth, Shield, Crown, Phone, Download, User, Eye, Trash, Search, Key } from "@strapi/icons";
|
|
9
|
-
import { u as useLicense } from "./useLicense-
|
|
9
|
+
import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
|
|
10
10
|
import { S as ShowHideButton, T as TertiaryButton, D as DangerButton, I as IconButtonPrimary, a as IconButtonWarning, b as IconButtonDanger } from "./StyledButtons-Cz8oYhmc.mjs";
|
|
11
11
|
import { useNavigate } from "react-router-dom";
|
|
12
12
|
const theme = {
|
|
@@ -5,10 +5,10 @@ const react = require("react");
|
|
|
5
5
|
const reactIntl = require("react-intl");
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
7
|
const styled = require("styled-components");
|
|
8
|
-
const index = require("./index-
|
|
8
|
+
const index = require("./index-CTxGMDHr.js");
|
|
9
9
|
const designSystem = require("@strapi/design-system");
|
|
10
10
|
const icons = require("@strapi/icons");
|
|
11
|
-
const useLicense = require("./useLicense-
|
|
11
|
+
const useLicense = require("./useLicense-BEbtA_Zo.js");
|
|
12
12
|
const StyledButtons = require("./StyledButtons-DDuxnYz8.js");
|
|
13
13
|
const reactRouterDom = require("react-router-dom");
|
|
14
14
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
@@ -4,7 +4,7 @@ import { Loader, Box, Alert, Flex, Typography, Button, Badge, Accordion } from "
|
|
|
4
4
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
5
5
|
import { ArrowClockwise, Duplicate, Download, User, Shield, Sparkle, ChartBubble } from "@strapi/icons";
|
|
6
6
|
import styled, { css, keyframes } from "styled-components";
|
|
7
|
-
import { a as pluginId } from "./index-
|
|
7
|
+
import { a as pluginId } from "./index-CwxKazpc.mjs";
|
|
8
8
|
const theme = {
|
|
9
9
|
borderRadius: { lg: "12px" }
|
|
10
10
|
};
|
|
@@ -6,7 +6,7 @@ const designSystem = require("@strapi/design-system");
|
|
|
6
6
|
const admin = require("@strapi/strapi/admin");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-CTxGMDHr.js");
|
|
10
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
11
11
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
12
12
|
const theme = {
|
|
@@ -4,7 +4,7 @@ import { useIntl } from "react-intl";
|
|
|
4
4
|
import { Box, Typography, Flex, Grid } from "@strapi/design-system";
|
|
5
5
|
import { Check, Cross, Clock, User } from "@strapi/icons";
|
|
6
6
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
7
|
-
import { g as getTranslation } from "./index-
|
|
7
|
+
import { g as getTranslation } from "./index-CwxKazpc.mjs";
|
|
8
8
|
const OnlineUsersWidget = () => {
|
|
9
9
|
const { formatMessage } = useIntl();
|
|
10
10
|
const { get } = useFetchClient();
|
|
@@ -6,7 +6,7 @@ const reactIntl = require("react-intl");
|
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const admin = require("@strapi/strapi/admin");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-CTxGMDHr.js");
|
|
10
10
|
const OnlineUsersWidget = () => {
|
|
11
11
|
const { formatMessage } = reactIntl.useIntl();
|
|
12
12
|
const { get } = admin.useFetchClient();
|
|
@@ -7,11 +7,85 @@ const designSystem = require("@strapi/design-system");
|
|
|
7
7
|
const admin = require("@strapi/strapi/admin");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
9
|
const styled = require("styled-components");
|
|
10
|
-
const index = require("./index-
|
|
11
|
-
const useLicense = require("./useLicense-
|
|
10
|
+
const index = require("./index-CTxGMDHr.js");
|
|
11
|
+
const useLicense = require("./useLicense-BEbtA_Zo.js");
|
|
12
12
|
const StyledButtons = require("./StyledButtons-DDuxnYz8.js");
|
|
13
13
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
14
14
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
15
|
+
const COUNTRIES = [
|
|
16
|
+
{ code: "DE", name: "Germany", flag: "🇩🇪" },
|
|
17
|
+
{ code: "AT", name: "Austria", flag: "🇦🇹" },
|
|
18
|
+
{ code: "CH", name: "Switzerland", flag: "🇨🇭" },
|
|
19
|
+
{ code: "US", name: "United States", flag: "🇺🇸" },
|
|
20
|
+
{ code: "CA", name: "Canada", flag: "🇨🇦" },
|
|
21
|
+
{ code: "MX", name: "Mexico", flag: "🇲🇽" },
|
|
22
|
+
{ code: "GB", name: "United Kingdom", flag: "🇬🇧" },
|
|
23
|
+
{ code: "IE", name: "Ireland", flag: "🇮🇪" },
|
|
24
|
+
{ code: "FR", name: "France", flag: "🇫🇷" },
|
|
25
|
+
{ code: "IT", name: "Italy", flag: "🇮🇹" },
|
|
26
|
+
{ code: "ES", name: "Spain", flag: "🇪🇸" },
|
|
27
|
+
{ code: "PT", name: "Portugal", flag: "🇵🇹" },
|
|
28
|
+
{ code: "NL", name: "Netherlands", flag: "🇳🇱" },
|
|
29
|
+
{ code: "BE", name: "Belgium", flag: "🇧🇪" },
|
|
30
|
+
{ code: "LU", name: "Luxembourg", flag: "🇱🇺" },
|
|
31
|
+
{ code: "SE", name: "Sweden", flag: "🇸🇪" },
|
|
32
|
+
{ code: "NO", name: "Norway", flag: "🇳🇴" },
|
|
33
|
+
{ code: "DK", name: "Denmark", flag: "🇩🇰" },
|
|
34
|
+
{ code: "FI", name: "Finland", flag: "🇫🇮" },
|
|
35
|
+
{ code: "PL", name: "Poland", flag: "🇵🇱" },
|
|
36
|
+
{ code: "CZ", name: "Czech Republic", flag: "🇨🇿" },
|
|
37
|
+
{ code: "SK", name: "Slovakia", flag: "🇸🇰" },
|
|
38
|
+
{ code: "HU", name: "Hungary", flag: "🇭🇺" },
|
|
39
|
+
{ code: "RO", name: "Romania", flag: "🇷🇴" },
|
|
40
|
+
{ code: "BG", name: "Bulgaria", flag: "🇧🇬" },
|
|
41
|
+
{ code: "GR", name: "Greece", flag: "🇬🇷" },
|
|
42
|
+
{ code: "HR", name: "Croatia", flag: "🇭🇷" },
|
|
43
|
+
{ code: "SI", name: "Slovenia", flag: "🇸🇮" },
|
|
44
|
+
{ code: "EE", name: "Estonia", flag: "🇪🇪" },
|
|
45
|
+
{ code: "LV", name: "Latvia", flag: "🇱🇻" },
|
|
46
|
+
{ code: "LT", name: "Lithuania", flag: "🇱🇹" },
|
|
47
|
+
{ code: "IS", name: "Iceland", flag: "🇮🇸" },
|
|
48
|
+
{ code: "TR", name: "Turkey", flag: "🇹🇷" },
|
|
49
|
+
{ code: "RU", name: "Russia", flag: "🇷🇺" },
|
|
50
|
+
{ code: "UA", name: "Ukraine", flag: "🇺🇦" },
|
|
51
|
+
{ code: "BR", name: "Brazil", flag: "🇧🇷" },
|
|
52
|
+
{ code: "AR", name: "Argentina", flag: "🇦🇷" },
|
|
53
|
+
{ code: "CL", name: "Chile", flag: "🇨🇱" },
|
|
54
|
+
{ code: "CO", name: "Colombia", flag: "🇨🇴" },
|
|
55
|
+
{ code: "AU", name: "Australia", flag: "🇦🇺" },
|
|
56
|
+
{ code: "NZ", name: "New Zealand", flag: "🇳🇿" },
|
|
57
|
+
{ code: "JP", name: "Japan", flag: "🇯🇵" },
|
|
58
|
+
{ code: "KR", name: "South Korea", flag: "🇰🇷" },
|
|
59
|
+
{ code: "CN", name: "China", flag: "🇨🇳" },
|
|
60
|
+
{ code: "HK", name: "Hong Kong", flag: "🇭🇰" },
|
|
61
|
+
{ code: "TW", name: "Taiwan", flag: "🇹🇼" },
|
|
62
|
+
{ code: "SG", name: "Singapore", flag: "🇸🇬" },
|
|
63
|
+
{ code: "IN", name: "India", flag: "🇮🇳" },
|
|
64
|
+
{ code: "ID", name: "Indonesia", flag: "🇮🇩" },
|
|
65
|
+
{ code: "TH", name: "Thailand", flag: "🇹🇭" },
|
|
66
|
+
{ code: "VN", name: "Vietnam", flag: "🇻🇳" },
|
|
67
|
+
{ code: "PH", name: "Philippines", flag: "🇵🇭" },
|
|
68
|
+
{ code: "MY", name: "Malaysia", flag: "🇲🇾" },
|
|
69
|
+
{ code: "AE", name: "United Arab Emirates", flag: "🇦🇪" },
|
|
70
|
+
{ code: "SA", name: "Saudi Arabia", flag: "🇸🇦" },
|
|
71
|
+
{ code: "IL", name: "Israel", flag: "🇮🇱" },
|
|
72
|
+
{ code: "EG", name: "Egypt", flag: "🇪🇬" },
|
|
73
|
+
{ code: "ZA", name: "South Africa", flag: "🇿🇦" },
|
|
74
|
+
{ code: "NG", name: "Nigeria", flag: "🇳🇬" },
|
|
75
|
+
{ code: "KE", name: "Kenya", flag: "🇰🇪" },
|
|
76
|
+
{ code: "MA", name: "Morocco", flag: "🇲🇦" }
|
|
77
|
+
];
|
|
78
|
+
const normalizeCountryCode = (raw) => {
|
|
79
|
+
if (typeof raw !== "string") return null;
|
|
80
|
+
const up = raw.trim().toUpperCase();
|
|
81
|
+
if (!/^[A-Z]{2}$/.test(up)) return null;
|
|
82
|
+
return up;
|
|
83
|
+
};
|
|
84
|
+
const formatCountry = (code) => {
|
|
85
|
+
if (!code) return "";
|
|
86
|
+
const match = COUNTRIES.find((c) => c.code === code);
|
|
87
|
+
return match ? `${match.flag} ${match.name} (${match.code})` : code;
|
|
88
|
+
};
|
|
15
89
|
const theme = {
|
|
16
90
|
borderRadius: { md: "8px", lg: "12px" }
|
|
17
91
|
};
|
|
@@ -331,6 +405,167 @@ const generateSecureKey = () => {
|
|
|
331
405
|
}
|
|
332
406
|
return key;
|
|
333
407
|
};
|
|
408
|
+
const GeofencingPanel = ({ settings, handleChange, t }) => {
|
|
409
|
+
const [pendingCode, setPendingCode] = react.useState("");
|
|
410
|
+
const mode = (() => {
|
|
411
|
+
if (Array.isArray(settings.allowedCountries) && settings.allowedCountries.length > 0) {
|
|
412
|
+
return "allowlist";
|
|
413
|
+
}
|
|
414
|
+
if (Array.isArray(settings.blockedCountries) && settings.blockedCountries.length > 0) {
|
|
415
|
+
return "blocklist";
|
|
416
|
+
}
|
|
417
|
+
return "allowlist";
|
|
418
|
+
})();
|
|
419
|
+
const activeField = mode === "allowlist" ? "allowedCountries" : "blockedCountries";
|
|
420
|
+
const activeList = Array.isArray(settings[activeField]) ? settings[activeField] : [];
|
|
421
|
+
const setMode = (newMode) => {
|
|
422
|
+
if (newMode === mode) return;
|
|
423
|
+
if (newMode === "allowlist") {
|
|
424
|
+
handleChange("blockedCountries", []);
|
|
425
|
+
} else {
|
|
426
|
+
handleChange("allowedCountries", []);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const addCode = (raw) => {
|
|
430
|
+
const code = normalizeCountryCode(raw);
|
|
431
|
+
if (!code) return;
|
|
432
|
+
if (activeList.includes(code)) return;
|
|
433
|
+
handleChange(activeField, [...activeList, code]);
|
|
434
|
+
setPendingCode("");
|
|
435
|
+
};
|
|
436
|
+
const removeCode = (code) => {
|
|
437
|
+
handleChange(activeField, activeList.filter((c) => c !== code));
|
|
438
|
+
};
|
|
439
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 5, children: [
|
|
440
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
441
|
+
designSystem.Box,
|
|
442
|
+
{
|
|
443
|
+
padding: 3,
|
|
444
|
+
background: settings.enableGeofencing ? "success100" : "neutral100",
|
|
445
|
+
hasRadius: true,
|
|
446
|
+
style: { borderLeft: `4px solid ${settings.enableGeofencing ? "#16A34A" : "#9CA3AF"}` },
|
|
447
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
448
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
449
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", style: { display: "block", marginBottom: "4px" }, children: t("settings.geofencing.enable.title", "Enable Geofencing") }),
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t(
|
|
451
|
+
"settings.geofencing.enable.description",
|
|
452
|
+
"When enabled, the pre-login guard compares the caller's country against the configured list and rejects logins that do not match."
|
|
453
|
+
) })
|
|
454
|
+
] }),
|
|
455
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
456
|
+
designSystem.Toggle,
|
|
457
|
+
{
|
|
458
|
+
onLabel: "On",
|
|
459
|
+
offLabel: "Off",
|
|
460
|
+
checked: !!settings.enableGeofencing,
|
|
461
|
+
onChange: () => handleChange("enableGeofencing", !settings.enableGeofencing)
|
|
462
|
+
}
|
|
463
|
+
)
|
|
464
|
+
] })
|
|
465
|
+
}
|
|
466
|
+
) }),
|
|
467
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.geofencing.mode.title", "Mode") }),
|
|
469
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
470
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
471
|
+
designSystem.Button,
|
|
472
|
+
{
|
|
473
|
+
variant: mode === "allowlist" ? "default" : "tertiary",
|
|
474
|
+
onClick: () => setMode("allowlist"),
|
|
475
|
+
disabled: !settings.enableGeofencing,
|
|
476
|
+
children: t("settings.geofencing.mode.allowlist", "Allowlist (only selected countries)")
|
|
477
|
+
}
|
|
478
|
+
),
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
480
|
+
designSystem.Button,
|
|
481
|
+
{
|
|
482
|
+
variant: mode === "blocklist" ? "default" : "tertiary",
|
|
483
|
+
onClick: () => setMode("blocklist"),
|
|
484
|
+
disabled: !settings.enableGeofencing,
|
|
485
|
+
children: t("settings.geofencing.mode.blocklist", "Blocklist (block selected countries)")
|
|
486
|
+
}
|
|
487
|
+
)
|
|
488
|
+
] }),
|
|
489
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: mode === "allowlist" ? t(
|
|
490
|
+
"settings.geofencing.mode.allowlist.hint",
|
|
491
|
+
"Only countries in the list may authenticate. Empty list = no geo restriction."
|
|
492
|
+
) : t(
|
|
493
|
+
"settings.geofencing.mode.blocklist.hint",
|
|
494
|
+
"Every country EXCEPT those in the list may authenticate."
|
|
495
|
+
) })
|
|
496
|
+
] }) }),
|
|
497
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
498
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: mode === "allowlist" ? t("settings.geofencing.allowed.title", "Allowed Countries") : t("settings.geofencing.blocked.title", "Blocked Countries") }),
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", style: { marginBottom: "12px", minHeight: "36px" }, children: activeList.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: t("settings.geofencing.empty", "No countries configured.") }) : activeList.map((code) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
500
|
+
designSystem.Badge,
|
|
501
|
+
{
|
|
502
|
+
onClick: settings.enableGeofencing ? () => removeCode(code) : void 0,
|
|
503
|
+
style: {
|
|
504
|
+
cursor: settings.enableGeofencing ? "pointer" : "not-allowed",
|
|
505
|
+
padding: "6px 10px",
|
|
506
|
+
background: mode === "allowlist" ? "#DCFCE7" : "#FEE2E2",
|
|
507
|
+
color: mode === "allowlist" ? "#15803D" : "#B91C1C"
|
|
508
|
+
},
|
|
509
|
+
children: [
|
|
510
|
+
formatCountry(code),
|
|
511
|
+
settings.enableGeofencing ? " ✕" : ""
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
code
|
|
515
|
+
)) }),
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
518
|
+
designSystem.SingleSelect,
|
|
519
|
+
{
|
|
520
|
+
disabled: !settings.enableGeofencing,
|
|
521
|
+
value: pendingCode || "",
|
|
522
|
+
onChange: (value) => {
|
|
523
|
+
setPendingCode(value);
|
|
524
|
+
if (value) addCode(value);
|
|
525
|
+
},
|
|
526
|
+
placeholder: t("settings.geofencing.pick", "Pick a country to add…"),
|
|
527
|
+
children: COUNTRIES.filter((c) => !activeList.includes(c.code)).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: c.code, children: [
|
|
528
|
+
c.flag,
|
|
529
|
+
" ",
|
|
530
|
+
c.name,
|
|
531
|
+
" (",
|
|
532
|
+
c.code,
|
|
533
|
+
")"
|
|
534
|
+
] }, c.code))
|
|
535
|
+
}
|
|
536
|
+
) }),
|
|
537
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
538
|
+
designSystem.TextInput,
|
|
539
|
+
{
|
|
540
|
+
disabled: !settings.enableGeofencing,
|
|
541
|
+
placeholder: t("settings.geofencing.manual", "Or type ISO-2 (e.g. JP)"),
|
|
542
|
+
value: pendingCode.length <= 2 ? pendingCode : "",
|
|
543
|
+
onChange: (e) => setPendingCode(e.target.value.toUpperCase().slice(0, 2)),
|
|
544
|
+
style: { width: "180px" }
|
|
545
|
+
}
|
|
546
|
+
),
|
|
547
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
548
|
+
designSystem.Button,
|
|
549
|
+
{
|
|
550
|
+
disabled: !settings.enableGeofencing || !normalizeCountryCode(pendingCode),
|
|
551
|
+
onClick: () => addCode(pendingCode),
|
|
552
|
+
children: t("settings.geofencing.add", "Add")
|
|
553
|
+
}
|
|
554
|
+
)
|
|
555
|
+
] })
|
|
556
|
+
] }) }),
|
|
557
|
+
!settings.enableGeofencing && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "default", closeLabel: "", title: "", onClose: () => {
|
|
558
|
+
}, children: t(
|
|
559
|
+
"settings.geofencing.disabled",
|
|
560
|
+
"Geofencing is currently disabled. Enable it above to enforce the configured country list."
|
|
561
|
+
) }) }),
|
|
562
|
+
settings.enableGeofencing && !settings.enableGeolocation && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "warning", closeLabel: "", title: "", onClose: () => {
|
|
563
|
+
}, children: t(
|
|
564
|
+
"settings.geofencing.needsGeolocation",
|
|
565
|
+
"Geofencing requires Geolocation to be enabled (Security tab)."
|
|
566
|
+
) }) })
|
|
567
|
+
] });
|
|
568
|
+
};
|
|
334
569
|
const SettingsPage = () => {
|
|
335
570
|
const { formatMessage } = reactIntl.useIntl();
|
|
336
571
|
const { get, post, put } = admin.useFetchClient();
|
|
@@ -1059,6 +1294,24 @@ const SettingsPage = () => {
|
|
|
1059
1294
|
] }) }) })
|
|
1060
1295
|
] }) })
|
|
1061
1296
|
] }),
|
|
1297
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Item, { value: "geofencing", children: [
|
|
1298
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1299
|
+
designSystem.Accordion.Trigger,
|
|
1300
|
+
{
|
|
1301
|
+
icon: icons.Shield,
|
|
1302
|
+
description: t("settings.geofencing.description", "Allow or block sign-ins by country"),
|
|
1303
|
+
children: t("settings.geofencing.title", "Geofencing")
|
|
1304
|
+
}
|
|
1305
|
+
) }),
|
|
1306
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1307
|
+
GeofencingPanel,
|
|
1308
|
+
{
|
|
1309
|
+
settings,
|
|
1310
|
+
handleChange,
|
|
1311
|
+
t
|
|
1312
|
+
}
|
|
1313
|
+
) }) })
|
|
1314
|
+
] }),
|
|
1062
1315
|
isAdvanced && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Item, { value: "email", children: [
|
|
1063
1316
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1064
1317
|
designSystem.Accordion.Trigger,
|
|
@@ -5,9 +5,83 @@ import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSe
|
|
|
5
5
|
import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
6
6
|
import { Check, Information, Cog, Trash, Shield, Code, Duplicate, Clock, Mail } from "@strapi/icons";
|
|
7
7
|
import styled, { css, keyframes } from "styled-components";
|
|
8
|
-
import { a as pluginId, g as getTranslation } from "./index-
|
|
9
|
-
import { u as useLicense } from "./useLicense-
|
|
8
|
+
import { a as pluginId, g as getTranslation } from "./index-CwxKazpc.mjs";
|
|
9
|
+
import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
|
|
10
10
|
import { D as DangerButton, S as ShowHideButton, G as GradientButton, C as CopyButton, T as TertiaryButton, c as SecondaryButton } from "./StyledButtons-Cz8oYhmc.mjs";
|
|
11
|
+
const COUNTRIES = [
|
|
12
|
+
{ code: "DE", name: "Germany", flag: "🇩🇪" },
|
|
13
|
+
{ code: "AT", name: "Austria", flag: "🇦🇹" },
|
|
14
|
+
{ code: "CH", name: "Switzerland", flag: "🇨🇭" },
|
|
15
|
+
{ code: "US", name: "United States", flag: "🇺🇸" },
|
|
16
|
+
{ code: "CA", name: "Canada", flag: "🇨🇦" },
|
|
17
|
+
{ code: "MX", name: "Mexico", flag: "🇲🇽" },
|
|
18
|
+
{ code: "GB", name: "United Kingdom", flag: "🇬🇧" },
|
|
19
|
+
{ code: "IE", name: "Ireland", flag: "🇮🇪" },
|
|
20
|
+
{ code: "FR", name: "France", flag: "🇫🇷" },
|
|
21
|
+
{ code: "IT", name: "Italy", flag: "🇮🇹" },
|
|
22
|
+
{ code: "ES", name: "Spain", flag: "🇪🇸" },
|
|
23
|
+
{ code: "PT", name: "Portugal", flag: "🇵🇹" },
|
|
24
|
+
{ code: "NL", name: "Netherlands", flag: "🇳🇱" },
|
|
25
|
+
{ code: "BE", name: "Belgium", flag: "🇧🇪" },
|
|
26
|
+
{ code: "LU", name: "Luxembourg", flag: "🇱🇺" },
|
|
27
|
+
{ code: "SE", name: "Sweden", flag: "🇸🇪" },
|
|
28
|
+
{ code: "NO", name: "Norway", flag: "🇳🇴" },
|
|
29
|
+
{ code: "DK", name: "Denmark", flag: "🇩🇰" },
|
|
30
|
+
{ code: "FI", name: "Finland", flag: "🇫🇮" },
|
|
31
|
+
{ code: "PL", name: "Poland", flag: "🇵🇱" },
|
|
32
|
+
{ code: "CZ", name: "Czech Republic", flag: "🇨🇿" },
|
|
33
|
+
{ code: "SK", name: "Slovakia", flag: "🇸🇰" },
|
|
34
|
+
{ code: "HU", name: "Hungary", flag: "🇭🇺" },
|
|
35
|
+
{ code: "RO", name: "Romania", flag: "🇷🇴" },
|
|
36
|
+
{ code: "BG", name: "Bulgaria", flag: "🇧🇬" },
|
|
37
|
+
{ code: "GR", name: "Greece", flag: "🇬🇷" },
|
|
38
|
+
{ code: "HR", name: "Croatia", flag: "🇭🇷" },
|
|
39
|
+
{ code: "SI", name: "Slovenia", flag: "🇸🇮" },
|
|
40
|
+
{ code: "EE", name: "Estonia", flag: "🇪🇪" },
|
|
41
|
+
{ code: "LV", name: "Latvia", flag: "🇱🇻" },
|
|
42
|
+
{ code: "LT", name: "Lithuania", flag: "🇱🇹" },
|
|
43
|
+
{ code: "IS", name: "Iceland", flag: "🇮🇸" },
|
|
44
|
+
{ code: "TR", name: "Turkey", flag: "🇹🇷" },
|
|
45
|
+
{ code: "RU", name: "Russia", flag: "🇷🇺" },
|
|
46
|
+
{ code: "UA", name: "Ukraine", flag: "🇺🇦" },
|
|
47
|
+
{ code: "BR", name: "Brazil", flag: "🇧🇷" },
|
|
48
|
+
{ code: "AR", name: "Argentina", flag: "🇦🇷" },
|
|
49
|
+
{ code: "CL", name: "Chile", flag: "🇨🇱" },
|
|
50
|
+
{ code: "CO", name: "Colombia", flag: "🇨🇴" },
|
|
51
|
+
{ code: "AU", name: "Australia", flag: "🇦🇺" },
|
|
52
|
+
{ code: "NZ", name: "New Zealand", flag: "🇳🇿" },
|
|
53
|
+
{ code: "JP", name: "Japan", flag: "🇯🇵" },
|
|
54
|
+
{ code: "KR", name: "South Korea", flag: "🇰🇷" },
|
|
55
|
+
{ code: "CN", name: "China", flag: "🇨🇳" },
|
|
56
|
+
{ code: "HK", name: "Hong Kong", flag: "🇭🇰" },
|
|
57
|
+
{ code: "TW", name: "Taiwan", flag: "🇹🇼" },
|
|
58
|
+
{ code: "SG", name: "Singapore", flag: "🇸🇬" },
|
|
59
|
+
{ code: "IN", name: "India", flag: "🇮🇳" },
|
|
60
|
+
{ code: "ID", name: "Indonesia", flag: "🇮🇩" },
|
|
61
|
+
{ code: "TH", name: "Thailand", flag: "🇹🇭" },
|
|
62
|
+
{ code: "VN", name: "Vietnam", flag: "🇻🇳" },
|
|
63
|
+
{ code: "PH", name: "Philippines", flag: "🇵🇭" },
|
|
64
|
+
{ code: "MY", name: "Malaysia", flag: "🇲🇾" },
|
|
65
|
+
{ code: "AE", name: "United Arab Emirates", flag: "🇦🇪" },
|
|
66
|
+
{ code: "SA", name: "Saudi Arabia", flag: "🇸🇦" },
|
|
67
|
+
{ code: "IL", name: "Israel", flag: "🇮🇱" },
|
|
68
|
+
{ code: "EG", name: "Egypt", flag: "🇪🇬" },
|
|
69
|
+
{ code: "ZA", name: "South Africa", flag: "🇿🇦" },
|
|
70
|
+
{ code: "NG", name: "Nigeria", flag: "🇳🇬" },
|
|
71
|
+
{ code: "KE", name: "Kenya", flag: "🇰🇪" },
|
|
72
|
+
{ code: "MA", name: "Morocco", flag: "🇲🇦" }
|
|
73
|
+
];
|
|
74
|
+
const normalizeCountryCode = (raw) => {
|
|
75
|
+
if (typeof raw !== "string") return null;
|
|
76
|
+
const up = raw.trim().toUpperCase();
|
|
77
|
+
if (!/^[A-Z]{2}$/.test(up)) return null;
|
|
78
|
+
return up;
|
|
79
|
+
};
|
|
80
|
+
const formatCountry = (code) => {
|
|
81
|
+
if (!code) return "";
|
|
82
|
+
const match = COUNTRIES.find((c) => c.code === code);
|
|
83
|
+
return match ? `${match.flag} ${match.name} (${match.code})` : code;
|
|
84
|
+
};
|
|
11
85
|
const theme = {
|
|
12
86
|
borderRadius: { md: "8px", lg: "12px" }
|
|
13
87
|
};
|
|
@@ -327,6 +401,167 @@ const generateSecureKey = () => {
|
|
|
327
401
|
}
|
|
328
402
|
return key;
|
|
329
403
|
};
|
|
404
|
+
const GeofencingPanel = ({ settings, handleChange, t }) => {
|
|
405
|
+
const [pendingCode, setPendingCode] = useState("");
|
|
406
|
+
const mode = (() => {
|
|
407
|
+
if (Array.isArray(settings.allowedCountries) && settings.allowedCountries.length > 0) {
|
|
408
|
+
return "allowlist";
|
|
409
|
+
}
|
|
410
|
+
if (Array.isArray(settings.blockedCountries) && settings.blockedCountries.length > 0) {
|
|
411
|
+
return "blocklist";
|
|
412
|
+
}
|
|
413
|
+
return "allowlist";
|
|
414
|
+
})();
|
|
415
|
+
const activeField = mode === "allowlist" ? "allowedCountries" : "blockedCountries";
|
|
416
|
+
const activeList = Array.isArray(settings[activeField]) ? settings[activeField] : [];
|
|
417
|
+
const setMode = (newMode) => {
|
|
418
|
+
if (newMode === mode) return;
|
|
419
|
+
if (newMode === "allowlist") {
|
|
420
|
+
handleChange("blockedCountries", []);
|
|
421
|
+
} else {
|
|
422
|
+
handleChange("allowedCountries", []);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
const addCode = (raw) => {
|
|
426
|
+
const code = normalizeCountryCode(raw);
|
|
427
|
+
if (!code) return;
|
|
428
|
+
if (activeList.includes(code)) return;
|
|
429
|
+
handleChange(activeField, [...activeList, code]);
|
|
430
|
+
setPendingCode("");
|
|
431
|
+
};
|
|
432
|
+
const removeCode = (code) => {
|
|
433
|
+
handleChange(activeField, activeList.filter((c) => c !== code));
|
|
434
|
+
};
|
|
435
|
+
return /* @__PURE__ */ jsxs(Grid.Root, { gap: 5, children: [
|
|
436
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(
|
|
437
|
+
Box,
|
|
438
|
+
{
|
|
439
|
+
padding: 3,
|
|
440
|
+
background: settings.enableGeofencing ? "success100" : "neutral100",
|
|
441
|
+
hasRadius: true,
|
|
442
|
+
style: { borderLeft: `4px solid ${settings.enableGeofencing ? "#16A34A" : "#9CA3AF"}` },
|
|
443
|
+
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
444
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", style: { display: "block", marginBottom: "4px" }, children: t("settings.geofencing.enable.title", "Enable Geofencing") }),
|
|
446
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t(
|
|
447
|
+
"settings.geofencing.enable.description",
|
|
448
|
+
"When enabled, the pre-login guard compares the caller's country against the configured list and rejects logins that do not match."
|
|
449
|
+
) })
|
|
450
|
+
] }),
|
|
451
|
+
/* @__PURE__ */ jsx(
|
|
452
|
+
Toggle,
|
|
453
|
+
{
|
|
454
|
+
onLabel: "On",
|
|
455
|
+
offLabel: "Off",
|
|
456
|
+
checked: !!settings.enableGeofencing,
|
|
457
|
+
onChange: () => handleChange("enableGeofencing", !settings.enableGeofencing)
|
|
458
|
+
}
|
|
459
|
+
)
|
|
460
|
+
] })
|
|
461
|
+
}
|
|
462
|
+
) }),
|
|
463
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
464
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.geofencing.mode.title", "Mode") }),
|
|
465
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
466
|
+
/* @__PURE__ */ jsx(
|
|
467
|
+
Button,
|
|
468
|
+
{
|
|
469
|
+
variant: mode === "allowlist" ? "default" : "tertiary",
|
|
470
|
+
onClick: () => setMode("allowlist"),
|
|
471
|
+
disabled: !settings.enableGeofencing,
|
|
472
|
+
children: t("settings.geofencing.mode.allowlist", "Allowlist (only selected countries)")
|
|
473
|
+
}
|
|
474
|
+
),
|
|
475
|
+
/* @__PURE__ */ jsx(
|
|
476
|
+
Button,
|
|
477
|
+
{
|
|
478
|
+
variant: mode === "blocklist" ? "default" : "tertiary",
|
|
479
|
+
onClick: () => setMode("blocklist"),
|
|
480
|
+
disabled: !settings.enableGeofencing,
|
|
481
|
+
children: t("settings.geofencing.mode.blocklist", "Blocklist (block selected countries)")
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
] }),
|
|
485
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: mode === "allowlist" ? t(
|
|
486
|
+
"settings.geofencing.mode.allowlist.hint",
|
|
487
|
+
"Only countries in the list may authenticate. Empty list = no geo restriction."
|
|
488
|
+
) : t(
|
|
489
|
+
"settings.geofencing.mode.blocklist.hint",
|
|
490
|
+
"Every country EXCEPT those in the list may authenticate."
|
|
491
|
+
) })
|
|
492
|
+
] }) }),
|
|
493
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
494
|
+
/* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: mode === "allowlist" ? t("settings.geofencing.allowed.title", "Allowed Countries") : t("settings.geofencing.blocked.title", "Blocked Countries") }),
|
|
495
|
+
/* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", style: { marginBottom: "12px", minHeight: "36px" }, children: activeList.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: t("settings.geofencing.empty", "No countries configured.") }) : activeList.map((code) => /* @__PURE__ */ jsxs(
|
|
496
|
+
Badge,
|
|
497
|
+
{
|
|
498
|
+
onClick: settings.enableGeofencing ? () => removeCode(code) : void 0,
|
|
499
|
+
style: {
|
|
500
|
+
cursor: settings.enableGeofencing ? "pointer" : "not-allowed",
|
|
501
|
+
padding: "6px 10px",
|
|
502
|
+
background: mode === "allowlist" ? "#DCFCE7" : "#FEE2E2",
|
|
503
|
+
color: mode === "allowlist" ? "#15803D" : "#B91C1C"
|
|
504
|
+
},
|
|
505
|
+
children: [
|
|
506
|
+
formatCountry(code),
|
|
507
|
+
settings.enableGeofencing ? " ✕" : ""
|
|
508
|
+
]
|
|
509
|
+
},
|
|
510
|
+
code
|
|
511
|
+
)) }),
|
|
512
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
513
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
|
|
514
|
+
SingleSelect,
|
|
515
|
+
{
|
|
516
|
+
disabled: !settings.enableGeofencing,
|
|
517
|
+
value: pendingCode || "",
|
|
518
|
+
onChange: (value) => {
|
|
519
|
+
setPendingCode(value);
|
|
520
|
+
if (value) addCode(value);
|
|
521
|
+
},
|
|
522
|
+
placeholder: t("settings.geofencing.pick", "Pick a country to add…"),
|
|
523
|
+
children: COUNTRIES.filter((c) => !activeList.includes(c.code)).map((c) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: c.code, children: [
|
|
524
|
+
c.flag,
|
|
525
|
+
" ",
|
|
526
|
+
c.name,
|
|
527
|
+
" (",
|
|
528
|
+
c.code,
|
|
529
|
+
")"
|
|
530
|
+
] }, c.code))
|
|
531
|
+
}
|
|
532
|
+
) }),
|
|
533
|
+
/* @__PURE__ */ jsx(
|
|
534
|
+
TextInput,
|
|
535
|
+
{
|
|
536
|
+
disabled: !settings.enableGeofencing,
|
|
537
|
+
placeholder: t("settings.geofencing.manual", "Or type ISO-2 (e.g. JP)"),
|
|
538
|
+
value: pendingCode.length <= 2 ? pendingCode : "",
|
|
539
|
+
onChange: (e) => setPendingCode(e.target.value.toUpperCase().slice(0, 2)),
|
|
540
|
+
style: { width: "180px" }
|
|
541
|
+
}
|
|
542
|
+
),
|
|
543
|
+
/* @__PURE__ */ jsx(
|
|
544
|
+
Button,
|
|
545
|
+
{
|
|
546
|
+
disabled: !settings.enableGeofencing || !normalizeCountryCode(pendingCode),
|
|
547
|
+
onClick: () => addCode(pendingCode),
|
|
548
|
+
children: t("settings.geofencing.add", "Add")
|
|
549
|
+
}
|
|
550
|
+
)
|
|
551
|
+
] })
|
|
552
|
+
] }) }),
|
|
553
|
+
!settings.enableGeofencing && /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Alert, { variant: "default", closeLabel: "", title: "", onClose: () => {
|
|
554
|
+
}, children: t(
|
|
555
|
+
"settings.geofencing.disabled",
|
|
556
|
+
"Geofencing is currently disabled. Enable it above to enforce the configured country list."
|
|
557
|
+
) }) }),
|
|
558
|
+
settings.enableGeofencing && !settings.enableGeolocation && /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Alert, { variant: "warning", closeLabel: "", title: "", onClose: () => {
|
|
559
|
+
}, children: t(
|
|
560
|
+
"settings.geofencing.needsGeolocation",
|
|
561
|
+
"Geofencing requires Geolocation to be enabled (Security tab)."
|
|
562
|
+
) }) })
|
|
563
|
+
] });
|
|
564
|
+
};
|
|
330
565
|
const SettingsPage = () => {
|
|
331
566
|
const { formatMessage } = useIntl();
|
|
332
567
|
const { get, post, put } = useFetchClient();
|
|
@@ -1055,6 +1290,24 @@ const SettingsPage = () => {
|
|
|
1055
1290
|
] }) }) })
|
|
1056
1291
|
] }) })
|
|
1057
1292
|
] }),
|
|
1293
|
+
/* @__PURE__ */ jsxs(Accordion.Item, { value: "geofencing", children: [
|
|
1294
|
+
/* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
|
|
1295
|
+
Accordion.Trigger,
|
|
1296
|
+
{
|
|
1297
|
+
icon: Shield,
|
|
1298
|
+
description: t("settings.geofencing.description", "Allow or block sign-ins by country"),
|
|
1299
|
+
children: t("settings.geofencing.title", "Geofencing")
|
|
1300
|
+
}
|
|
1301
|
+
) }),
|
|
1302
|
+
/* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsx(
|
|
1303
|
+
GeofencingPanel,
|
|
1304
|
+
{
|
|
1305
|
+
settings,
|
|
1306
|
+
handleChange,
|
|
1307
|
+
t
|
|
1308
|
+
}
|
|
1309
|
+
) }) })
|
|
1310
|
+
] }),
|
|
1058
1311
|
isAdvanced && /* @__PURE__ */ jsxs(Accordion.Item, { value: "email", children: [
|
|
1059
1312
|
/* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
|
|
1060
1313
|
Accordion.Trigger,
|
|
@@ -4,7 +4,7 @@ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
|
|
|
4
4
|
import styled from "styled-components";
|
|
5
5
|
import { Flex, Typography, Box, Badge, Button } from "@strapi/design-system";
|
|
6
6
|
import { Check, Cross, Sparkle, Lightning, Rocket } from "@strapi/icons";
|
|
7
|
-
import { a as pluginId } from "./index-
|
|
7
|
+
import { a as pluginId } from "./index-CwxKazpc.mjs";
|
|
8
8
|
const Container = styled(Box)`
|
|
9
9
|
padding: 32px;
|
|
10
10
|
max-width: 1400px;
|
|
@@ -6,7 +6,7 @@ const admin = require("@strapi/strapi/admin");
|
|
|
6
6
|
const styled = require("styled-components");
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-CTxGMDHr.js");
|
|
10
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
11
11
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
12
12
|
const Container = styled__default.default(designSystem.Box)`
|
|
@@ -444,7 +444,7 @@ const index = {
|
|
|
444
444
|
id: `${pluginId}.plugin.name`,
|
|
445
445
|
defaultMessage: pluginPkg.strapi.displayName
|
|
446
446
|
},
|
|
447
|
-
Component: () => Promise.resolve().then(() => require("./App-
|
|
447
|
+
Component: () => Promise.resolve().then(() => require("./App-DF-VsbDW.js")),
|
|
448
448
|
permissions: pluginPermissions
|
|
449
449
|
});
|
|
450
450
|
app.createSettingSection(
|
|
@@ -461,7 +461,7 @@ const index = {
|
|
|
461
461
|
},
|
|
462
462
|
id: "upgrade",
|
|
463
463
|
to: `${pluginId}/upgrade`,
|
|
464
|
-
Component: () => Promise.resolve().then(() => require("./UpgradePage-
|
|
464
|
+
Component: () => Promise.resolve().then(() => require("./UpgradePage-Dwrv7g8L.js")),
|
|
465
465
|
permissions: pluginPermissions
|
|
466
466
|
},
|
|
467
467
|
{
|
|
@@ -471,7 +471,7 @@ const index = {
|
|
|
471
471
|
},
|
|
472
472
|
id: "general",
|
|
473
473
|
to: `${pluginId}/general`,
|
|
474
|
-
Component: () => Promise.resolve().then(() => require("./Settings-
|
|
474
|
+
Component: () => Promise.resolve().then(() => require("./Settings-CUNaxDWk.js")),
|
|
475
475
|
permissions: pluginPermissions
|
|
476
476
|
},
|
|
477
477
|
{
|
|
@@ -481,7 +481,7 @@ const index = {
|
|
|
481
481
|
},
|
|
482
482
|
id: "analytics",
|
|
483
483
|
to: `${pluginId}/analytics`,
|
|
484
|
-
Component: () => Promise.resolve().then(() => require("./Analytics-
|
|
484
|
+
Component: () => Promise.resolve().then(() => require("./Analytics-DM4_UffY.js")),
|
|
485
485
|
permissions: pluginPermissions
|
|
486
486
|
},
|
|
487
487
|
{
|
|
@@ -491,7 +491,7 @@ const index = {
|
|
|
491
491
|
},
|
|
492
492
|
id: "license",
|
|
493
493
|
to: `${pluginId}/license`,
|
|
494
|
-
Component: () => Promise.resolve().then(() => require("./License-
|
|
494
|
+
Component: () => Promise.resolve().then(() => require("./License-Dmm1d3fQ.js")),
|
|
495
495
|
permissions: pluginPermissions
|
|
496
496
|
}
|
|
497
497
|
]
|
|
@@ -510,7 +510,7 @@ const index = {
|
|
|
510
510
|
defaultMessage: "Online Users"
|
|
511
511
|
},
|
|
512
512
|
component: async () => {
|
|
513
|
-
const component = await Promise.resolve().then(() => require("./OnlineUsersWidget-
|
|
513
|
+
const component = await Promise.resolve().then(() => require("./OnlineUsersWidget-KchZ_ScS.js"));
|
|
514
514
|
return component.default;
|
|
515
515
|
},
|
|
516
516
|
id: "online-users-widget",
|
|
@@ -441,7 +441,7 @@ const index = {
|
|
|
441
441
|
id: `${pluginId}.plugin.name`,
|
|
442
442
|
defaultMessage: pluginPkg.strapi.displayName
|
|
443
443
|
},
|
|
444
|
-
Component: () => import("./App-
|
|
444
|
+
Component: () => import("./App-CL5q29Mi.mjs"),
|
|
445
445
|
permissions: pluginPermissions
|
|
446
446
|
});
|
|
447
447
|
app.createSettingSection(
|
|
@@ -458,7 +458,7 @@ const index = {
|
|
|
458
458
|
},
|
|
459
459
|
id: "upgrade",
|
|
460
460
|
to: `${pluginId}/upgrade`,
|
|
461
|
-
Component: () => import("./UpgradePage-
|
|
461
|
+
Component: () => import("./UpgradePage-D697BVWo.mjs"),
|
|
462
462
|
permissions: pluginPermissions
|
|
463
463
|
},
|
|
464
464
|
{
|
|
@@ -468,7 +468,7 @@ const index = {
|
|
|
468
468
|
},
|
|
469
469
|
id: "general",
|
|
470
470
|
to: `${pluginId}/general`,
|
|
471
|
-
Component: () => import("./Settings-
|
|
471
|
+
Component: () => import("./Settings-D6ILgR9X.mjs"),
|
|
472
472
|
permissions: pluginPermissions
|
|
473
473
|
},
|
|
474
474
|
{
|
|
@@ -478,7 +478,7 @@ const index = {
|
|
|
478
478
|
},
|
|
479
479
|
id: "analytics",
|
|
480
480
|
to: `${pluginId}/analytics`,
|
|
481
|
-
Component: () => import("./Analytics
|
|
481
|
+
Component: () => import("./Analytics--qB2cKuD.mjs"),
|
|
482
482
|
permissions: pluginPermissions
|
|
483
483
|
},
|
|
484
484
|
{
|
|
@@ -488,7 +488,7 @@ const index = {
|
|
|
488
488
|
},
|
|
489
489
|
id: "license",
|
|
490
490
|
to: `${pluginId}/license`,
|
|
491
|
-
Component: () => import("./License-
|
|
491
|
+
Component: () => import("./License-D9piCp34.mjs"),
|
|
492
492
|
permissions: pluginPermissions
|
|
493
493
|
}
|
|
494
494
|
]
|
|
@@ -507,7 +507,7 @@ const index = {
|
|
|
507
507
|
defaultMessage: "Online Users"
|
|
508
508
|
},
|
|
509
509
|
component: async () => {
|
|
510
|
-
const component = await import("./OnlineUsersWidget-
|
|
510
|
+
const component = await import("./OnlineUsersWidget-DHsthkt0.mjs");
|
|
511
511
|
return component.default;
|
|
512
512
|
},
|
|
513
513
|
id: "online-users-widget",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
2
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
3
|
-
import { a as pluginId } from "./index-
|
|
3
|
+
import { a as pluginId } from "./index-CwxKazpc.mjs";
|
|
4
4
|
const useLicense = () => {
|
|
5
5
|
const { get } = useFetchClient();
|
|
6
6
|
const [isPremium, setIsPremium] = useState(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const react = require("react");
|
|
3
3
|
const admin = require("@strapi/strapi/admin");
|
|
4
|
-
const index = require("./index-
|
|
4
|
+
const index = require("./index-CTxGMDHr.js");
|
|
5
5
|
const useLicense = () => {
|
|
6
6
|
const { get } = admin.useFetchClient();
|
|
7
7
|
const [isPremium, setIsPremium] = react.useState(false);
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -9584,6 +9584,9 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
9584
9584
|
}
|
|
9585
9585
|
}, 3e3);
|
|
9586
9586
|
const sessionService = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9587
|
+
if (!strapi2.sessionManagerIntervals) {
|
|
9588
|
+
strapi2.sessionManagerIntervals = {};
|
|
9589
|
+
}
|
|
9587
9590
|
log.info("Running initial session cleanup...");
|
|
9588
9591
|
try {
|
|
9589
9592
|
const settings2 = await getPluginSettings$3(strapi2);
|
|
@@ -9593,23 +9596,50 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
9593
9596
|
} catch (cleanupErr) {
|
|
9594
9597
|
log.warn("Initial cleanup failed:", cleanupErr.message);
|
|
9595
9598
|
}
|
|
9596
|
-
const
|
|
9597
|
-
|
|
9599
|
+
const scheduleIdleCleanup = async () => {
|
|
9600
|
+
let intervalMs = 30 * 60 * 1e3;
|
|
9601
|
+
let useDbDirect = false;
|
|
9598
9602
|
try {
|
|
9599
|
-
const
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
});
|
|
9604
|
-
} catch (err) {
|
|
9605
|
-
log.error("Periodic cleanup error:", err);
|
|
9603
|
+
const settings2 = await getPluginSettings$3(strapi2);
|
|
9604
|
+
intervalMs = Math.max(5 * 60 * 1e3, settings2.cleanupInterval || intervalMs);
|
|
9605
|
+
useDbDirect = settings2.cleanupUseDbDirect === true;
|
|
9606
|
+
} catch {
|
|
9606
9607
|
}
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9608
|
+
const handle = setTimeout(async () => {
|
|
9609
|
+
try {
|
|
9610
|
+
const service = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9611
|
+
await service.cleanupInactiveSessions({ useDbDirect });
|
|
9612
|
+
} catch (err) {
|
|
9613
|
+
log.error("Periodic cleanup error:", err);
|
|
9614
|
+
}
|
|
9615
|
+
scheduleIdleCleanup();
|
|
9616
|
+
}, intervalMs);
|
|
9617
|
+
strapi2.sessionManagerIntervals.cleanupTimeout = handle;
|
|
9618
|
+
};
|
|
9619
|
+
await scheduleIdleCleanup();
|
|
9620
|
+
const RETENTION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9621
|
+
const scheduleRetention = async () => {
|
|
9622
|
+
let useDbDirect = false;
|
|
9623
|
+
try {
|
|
9624
|
+
const settings2 = await getPluginSettings$3(strapi2);
|
|
9625
|
+
useDbDirect = settings2.cleanupUseDbDirect === true;
|
|
9626
|
+
} catch {
|
|
9627
|
+
}
|
|
9628
|
+
const handle = setTimeout(async () => {
|
|
9629
|
+
try {
|
|
9630
|
+
const service = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9631
|
+
await service.deleteOldSessions({ useDbDirect });
|
|
9632
|
+
} catch (err) {
|
|
9633
|
+
log.error("Retention cleanup error:", err);
|
|
9634
|
+
}
|
|
9635
|
+
scheduleRetention();
|
|
9636
|
+
}, RETENTION_INTERVAL_MS);
|
|
9637
|
+
strapi2.sessionManagerIntervals.retentionTimeout = handle;
|
|
9638
|
+
};
|
|
9639
|
+
strapi2.sessionManagerIntervals.retentionStartup = setTimeout(() => {
|
|
9640
|
+
scheduleRetention();
|
|
9641
|
+
}, 5 * 60 * 1e3);
|
|
9642
|
+
log.info("[TIME] Dynamic cleanup + retention scheduled");
|
|
9613
9643
|
mountPreLoginGeoGuard({ strapi: strapi2, log });
|
|
9614
9644
|
mountFailedLoginLockout({ strapi: strapi2, log });
|
|
9615
9645
|
mountLogoutRoute({ strapi: strapi2, log, sessionService });
|
|
@@ -10341,9 +10371,10 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
10341
10371
|
if (!handle) continue;
|
|
10342
10372
|
try {
|
|
10343
10373
|
clearInterval(handle);
|
|
10344
|
-
|
|
10374
|
+
clearTimeout(handle);
|
|
10375
|
+
log.info(`[STOP] ${name} timer stopped`);
|
|
10345
10376
|
} catch (err) {
|
|
10346
|
-
log.warn(`Failed to stop ${name}
|
|
10377
|
+
log.warn(`Failed to stop ${name} timer:`, err.message);
|
|
10347
10378
|
}
|
|
10348
10379
|
}
|
|
10349
10380
|
strapi2.sessionManagerIntervals = {};
|
|
@@ -12210,6 +12241,81 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
12210
12241
|
throw err;
|
|
12211
12242
|
}
|
|
12212
12243
|
},
|
|
12244
|
+
/**
|
|
12245
|
+
* Permanently deletes sessions that have been inactive past the
|
|
12246
|
+
* configured retention window. Distinct from `deleteInactiveSessions`
|
|
12247
|
+
* (which deletes ALL inactive sessions) — this only drops rows older
|
|
12248
|
+
* than `retentionDays`, so recently-terminated sessions stay queryable
|
|
12249
|
+
* for audits.
|
|
12250
|
+
*
|
|
12251
|
+
* @param {Object} [options]
|
|
12252
|
+
* @param {number} [options.retentionDays] Overrides the stored setting.
|
|
12253
|
+
* @param {boolean} [options.useDbDirect] Fast-path via single SQL
|
|
12254
|
+
* DELETE. Bypasses lifecycle hooks; use only when necessary.
|
|
12255
|
+
* @returns {Promise<number>} Number of sessions deleted
|
|
12256
|
+
*/
|
|
12257
|
+
async deleteOldSessions({ retentionDays, useDbDirect } = {}) {
|
|
12258
|
+
try {
|
|
12259
|
+
const settings2 = await getPluginSettings$1(strapi2);
|
|
12260
|
+
const effectiveDays = Number.isFinite(retentionDays) ? retentionDays : settings2.retentionDays || 90;
|
|
12261
|
+
if (effectiveDays === -1) {
|
|
12262
|
+
log.debug("[RETENTION] retentionDays=-1 (forever) — skipping");
|
|
12263
|
+
return 0;
|
|
12264
|
+
}
|
|
12265
|
+
const cutoffDate = new Date(Date.now() - effectiveDays * 24 * 60 * 60 * 1e3);
|
|
12266
|
+
const wantDbDirect = useDbDirect ?? settings2.cleanupUseDbDirect === true;
|
|
12267
|
+
log.info(`[RETENTION] Deleting inactive sessions older than ${effectiveDays} days (before ${cutoffDate.toISOString()})`);
|
|
12268
|
+
if (wantDbDirect) {
|
|
12269
|
+
try {
|
|
12270
|
+
const deleted = await strapi2.db.connection("magic_sessions").where("is_active", false).andWhere(function whereOldEnough() {
|
|
12271
|
+
this.where("logout_time", "<", cutoffDate).orWhere(function whereNullLogout() {
|
|
12272
|
+
this.whereNull("logout_time").andWhere(function whereOldByActivity() {
|
|
12273
|
+
this.where("last_active", "<", cutoffDate).orWhere(function whereNullActivity() {
|
|
12274
|
+
this.whereNull("last_active").andWhere("login_time", "<", cutoffDate);
|
|
12275
|
+
});
|
|
12276
|
+
});
|
|
12277
|
+
});
|
|
12278
|
+
}).del();
|
|
12279
|
+
log.info(`[SUCCESS] Retention (db-direct) deleted ${deleted} old session(s)`);
|
|
12280
|
+
return deleted;
|
|
12281
|
+
} catch (err) {
|
|
12282
|
+
log.warn("[RETENTION] DB-direct delete failed, falling back to Document Service:", err.message);
|
|
12283
|
+
}
|
|
12284
|
+
}
|
|
12285
|
+
let deletedCount = 0;
|
|
12286
|
+
const BATCH = 200;
|
|
12287
|
+
while (true) {
|
|
12288
|
+
const batch = await strapi2.documents(SESSION_UID$1).findMany({
|
|
12289
|
+
filters: {
|
|
12290
|
+
isActive: false,
|
|
12291
|
+
$or: [
|
|
12292
|
+
{ logoutTime: { $lt: cutoffDate } },
|
|
12293
|
+
{ logoutTime: { $null: true }, lastActive: { $lt: cutoffDate } },
|
|
12294
|
+
{ logoutTime: { $null: true }, lastActive: { $null: true }, loginTime: { $lt: cutoffDate } }
|
|
12295
|
+
]
|
|
12296
|
+
},
|
|
12297
|
+
fields: ["documentId"],
|
|
12298
|
+
sort: { loginTime: "asc" },
|
|
12299
|
+
limit: BATCH
|
|
12300
|
+
});
|
|
12301
|
+
if (!batch || batch.length === 0) break;
|
|
12302
|
+
for (const session2 of batch) {
|
|
12303
|
+
try {
|
|
12304
|
+
await strapi2.documents(SESSION_UID$1).delete({ documentId: session2.documentId });
|
|
12305
|
+
deletedCount++;
|
|
12306
|
+
} catch (err) {
|
|
12307
|
+
log.debug(`[RETENTION] Failed to delete session ${session2.documentId}:`, err.message);
|
|
12308
|
+
}
|
|
12309
|
+
}
|
|
12310
|
+
if (batch.length < BATCH) break;
|
|
12311
|
+
}
|
|
12312
|
+
log.info(`[SUCCESS] Retention deleted ${deletedCount} old session(s)`);
|
|
12313
|
+
return deletedCount;
|
|
12314
|
+
} catch (err) {
|
|
12315
|
+
log.error("Error in retention cleanup:", err);
|
|
12316
|
+
return 0;
|
|
12317
|
+
}
|
|
12318
|
+
},
|
|
12213
12319
|
/**
|
|
12214
12320
|
* Permanently deletes all inactive sessions.
|
|
12215
12321
|
* Uses an inner scan loop that tolerates partial failures.
|
|
@@ -12255,7 +12361,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
12255
12361
|
}
|
|
12256
12362
|
};
|
|
12257
12363
|
};
|
|
12258
|
-
const version$1 = "4.5.
|
|
12364
|
+
const version$1 = "4.5.1";
|
|
12259
12365
|
const require$$2 = {
|
|
12260
12366
|
version: version$1
|
|
12261
12367
|
};
|
package/dist/server/index.mjs
CHANGED
|
@@ -9571,6 +9571,9 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
9571
9571
|
}
|
|
9572
9572
|
}, 3e3);
|
|
9573
9573
|
const sessionService = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9574
|
+
if (!strapi2.sessionManagerIntervals) {
|
|
9575
|
+
strapi2.sessionManagerIntervals = {};
|
|
9576
|
+
}
|
|
9574
9577
|
log.info("Running initial session cleanup...");
|
|
9575
9578
|
try {
|
|
9576
9579
|
const settings2 = await getPluginSettings$3(strapi2);
|
|
@@ -9580,23 +9583,50 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
9580
9583
|
} catch (cleanupErr) {
|
|
9581
9584
|
log.warn("Initial cleanup failed:", cleanupErr.message);
|
|
9582
9585
|
}
|
|
9583
|
-
const
|
|
9584
|
-
|
|
9586
|
+
const scheduleIdleCleanup = async () => {
|
|
9587
|
+
let intervalMs = 30 * 60 * 1e3;
|
|
9588
|
+
let useDbDirect = false;
|
|
9585
9589
|
try {
|
|
9586
|
-
const
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
});
|
|
9591
|
-
} catch (err) {
|
|
9592
|
-
log.error("Periodic cleanup error:", err);
|
|
9590
|
+
const settings2 = await getPluginSettings$3(strapi2);
|
|
9591
|
+
intervalMs = Math.max(5 * 60 * 1e3, settings2.cleanupInterval || intervalMs);
|
|
9592
|
+
useDbDirect = settings2.cleanupUseDbDirect === true;
|
|
9593
|
+
} catch {
|
|
9593
9594
|
}
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9595
|
+
const handle = setTimeout(async () => {
|
|
9596
|
+
try {
|
|
9597
|
+
const service = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9598
|
+
await service.cleanupInactiveSessions({ useDbDirect });
|
|
9599
|
+
} catch (err) {
|
|
9600
|
+
log.error("Periodic cleanup error:", err);
|
|
9601
|
+
}
|
|
9602
|
+
scheduleIdleCleanup();
|
|
9603
|
+
}, intervalMs);
|
|
9604
|
+
strapi2.sessionManagerIntervals.cleanupTimeout = handle;
|
|
9605
|
+
};
|
|
9606
|
+
await scheduleIdleCleanup();
|
|
9607
|
+
const RETENTION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9608
|
+
const scheduleRetention = async () => {
|
|
9609
|
+
let useDbDirect = false;
|
|
9610
|
+
try {
|
|
9611
|
+
const settings2 = await getPluginSettings$3(strapi2);
|
|
9612
|
+
useDbDirect = settings2.cleanupUseDbDirect === true;
|
|
9613
|
+
} catch {
|
|
9614
|
+
}
|
|
9615
|
+
const handle = setTimeout(async () => {
|
|
9616
|
+
try {
|
|
9617
|
+
const service = strapi2.plugin("magic-sessionmanager").service("session");
|
|
9618
|
+
await service.deleteOldSessions({ useDbDirect });
|
|
9619
|
+
} catch (err) {
|
|
9620
|
+
log.error("Retention cleanup error:", err);
|
|
9621
|
+
}
|
|
9622
|
+
scheduleRetention();
|
|
9623
|
+
}, RETENTION_INTERVAL_MS);
|
|
9624
|
+
strapi2.sessionManagerIntervals.retentionTimeout = handle;
|
|
9625
|
+
};
|
|
9626
|
+
strapi2.sessionManagerIntervals.retentionStartup = setTimeout(() => {
|
|
9627
|
+
scheduleRetention();
|
|
9628
|
+
}, 5 * 60 * 1e3);
|
|
9629
|
+
log.info("[TIME] Dynamic cleanup + retention scheduled");
|
|
9600
9630
|
mountPreLoginGeoGuard({ strapi: strapi2, log });
|
|
9601
9631
|
mountFailedLoginLockout({ strapi: strapi2, log });
|
|
9602
9632
|
mountLogoutRoute({ strapi: strapi2, log, sessionService });
|
|
@@ -10328,9 +10358,10 @@ var destroy$1 = async ({ strapi: strapi2 }) => {
|
|
|
10328
10358
|
if (!handle) continue;
|
|
10329
10359
|
try {
|
|
10330
10360
|
clearInterval(handle);
|
|
10331
|
-
|
|
10361
|
+
clearTimeout(handle);
|
|
10362
|
+
log.info(`[STOP] ${name} timer stopped`);
|
|
10332
10363
|
} catch (err) {
|
|
10333
|
-
log.warn(`Failed to stop ${name}
|
|
10364
|
+
log.warn(`Failed to stop ${name} timer:`, err.message);
|
|
10334
10365
|
}
|
|
10335
10366
|
}
|
|
10336
10367
|
strapi2.sessionManagerIntervals = {};
|
|
@@ -12197,6 +12228,81 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
12197
12228
|
throw err;
|
|
12198
12229
|
}
|
|
12199
12230
|
},
|
|
12231
|
+
/**
|
|
12232
|
+
* Permanently deletes sessions that have been inactive past the
|
|
12233
|
+
* configured retention window. Distinct from `deleteInactiveSessions`
|
|
12234
|
+
* (which deletes ALL inactive sessions) — this only drops rows older
|
|
12235
|
+
* than `retentionDays`, so recently-terminated sessions stay queryable
|
|
12236
|
+
* for audits.
|
|
12237
|
+
*
|
|
12238
|
+
* @param {Object} [options]
|
|
12239
|
+
* @param {number} [options.retentionDays] Overrides the stored setting.
|
|
12240
|
+
* @param {boolean} [options.useDbDirect] Fast-path via single SQL
|
|
12241
|
+
* DELETE. Bypasses lifecycle hooks; use only when necessary.
|
|
12242
|
+
* @returns {Promise<number>} Number of sessions deleted
|
|
12243
|
+
*/
|
|
12244
|
+
async deleteOldSessions({ retentionDays, useDbDirect } = {}) {
|
|
12245
|
+
try {
|
|
12246
|
+
const settings2 = await getPluginSettings$1(strapi2);
|
|
12247
|
+
const effectiveDays = Number.isFinite(retentionDays) ? retentionDays : settings2.retentionDays || 90;
|
|
12248
|
+
if (effectiveDays === -1) {
|
|
12249
|
+
log.debug("[RETENTION] retentionDays=-1 (forever) — skipping");
|
|
12250
|
+
return 0;
|
|
12251
|
+
}
|
|
12252
|
+
const cutoffDate = new Date(Date.now() - effectiveDays * 24 * 60 * 60 * 1e3);
|
|
12253
|
+
const wantDbDirect = useDbDirect ?? settings2.cleanupUseDbDirect === true;
|
|
12254
|
+
log.info(`[RETENTION] Deleting inactive sessions older than ${effectiveDays} days (before ${cutoffDate.toISOString()})`);
|
|
12255
|
+
if (wantDbDirect) {
|
|
12256
|
+
try {
|
|
12257
|
+
const deleted = await strapi2.db.connection("magic_sessions").where("is_active", false).andWhere(function whereOldEnough() {
|
|
12258
|
+
this.where("logout_time", "<", cutoffDate).orWhere(function whereNullLogout() {
|
|
12259
|
+
this.whereNull("logout_time").andWhere(function whereOldByActivity() {
|
|
12260
|
+
this.where("last_active", "<", cutoffDate).orWhere(function whereNullActivity() {
|
|
12261
|
+
this.whereNull("last_active").andWhere("login_time", "<", cutoffDate);
|
|
12262
|
+
});
|
|
12263
|
+
});
|
|
12264
|
+
});
|
|
12265
|
+
}).del();
|
|
12266
|
+
log.info(`[SUCCESS] Retention (db-direct) deleted ${deleted} old session(s)`);
|
|
12267
|
+
return deleted;
|
|
12268
|
+
} catch (err) {
|
|
12269
|
+
log.warn("[RETENTION] DB-direct delete failed, falling back to Document Service:", err.message);
|
|
12270
|
+
}
|
|
12271
|
+
}
|
|
12272
|
+
let deletedCount = 0;
|
|
12273
|
+
const BATCH = 200;
|
|
12274
|
+
while (true) {
|
|
12275
|
+
const batch = await strapi2.documents(SESSION_UID$1).findMany({
|
|
12276
|
+
filters: {
|
|
12277
|
+
isActive: false,
|
|
12278
|
+
$or: [
|
|
12279
|
+
{ logoutTime: { $lt: cutoffDate } },
|
|
12280
|
+
{ logoutTime: { $null: true }, lastActive: { $lt: cutoffDate } },
|
|
12281
|
+
{ logoutTime: { $null: true }, lastActive: { $null: true }, loginTime: { $lt: cutoffDate } }
|
|
12282
|
+
]
|
|
12283
|
+
},
|
|
12284
|
+
fields: ["documentId"],
|
|
12285
|
+
sort: { loginTime: "asc" },
|
|
12286
|
+
limit: BATCH
|
|
12287
|
+
});
|
|
12288
|
+
if (!batch || batch.length === 0) break;
|
|
12289
|
+
for (const session2 of batch) {
|
|
12290
|
+
try {
|
|
12291
|
+
await strapi2.documents(SESSION_UID$1).delete({ documentId: session2.documentId });
|
|
12292
|
+
deletedCount++;
|
|
12293
|
+
} catch (err) {
|
|
12294
|
+
log.debug(`[RETENTION] Failed to delete session ${session2.documentId}:`, err.message);
|
|
12295
|
+
}
|
|
12296
|
+
}
|
|
12297
|
+
if (batch.length < BATCH) break;
|
|
12298
|
+
}
|
|
12299
|
+
log.info(`[SUCCESS] Retention deleted ${deletedCount} old session(s)`);
|
|
12300
|
+
return deletedCount;
|
|
12301
|
+
} catch (err) {
|
|
12302
|
+
log.error("Error in retention cleanup:", err);
|
|
12303
|
+
return 0;
|
|
12304
|
+
}
|
|
12305
|
+
},
|
|
12200
12306
|
/**
|
|
12201
12307
|
* Permanently deletes all inactive sessions.
|
|
12202
12308
|
* Uses an inner scan loop that tolerates partial failures.
|
|
@@ -12242,7 +12348,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
12242
12348
|
}
|
|
12243
12349
|
};
|
|
12244
12350
|
};
|
|
12245
|
-
const version$1 = "4.5.
|
|
12351
|
+
const version$1 = "4.5.1";
|
|
12246
12352
|
const require$$2 = {
|
|
12247
12353
|
version: version$1
|
|
12248
12354
|
};
|
package/package.json
CHANGED