strapi-plugin-magic-sessionmanager 4.5.0 → 4.5.1

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.
@@ -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-CKrO7KSQ.js");
10
- const useLicense = require("./useLicense-DHAFqFSZ.js");
9
+ const index = require("./index-BRESWp1b.js");
10
+ const useLicense = require("./useLicense-D_wQcoMn.js");
11
11
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
12
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
13
13
  const theme = {
@@ -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-DuVZXuJh.mjs";
8
- import { u as useLicense } from "./useLicense-Xzo6nyh3.mjs";
7
+ import { a as pluginId } from "./index-BbbrBv3t.mjs";
8
+ import { u as useLicense } from "./useLicense-Bszkymz3.mjs";
9
9
  const theme = {
10
10
  shadows: {
11
11
  sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
@@ -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-CKrO7KSQ.js");
8
+ const index = require("./index-BRESWp1b.js");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const icons = require("@strapi/icons");
11
- const useLicense = require("./useLicense-DHAFqFSZ.js");
11
+ const useLicense = require("./useLicense-D_wQcoMn.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 };
@@ -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-DuVZXuJh.mjs";
6
+ import { p as parseUserAgent, a as pluginId$1, g as getTranslation } from "./index-BbbrBv3t.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-Xzo6nyh3.mjs";
9
+ import { u as useLicense } from "./useLicense-Bszkymz3.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 = {
@@ -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-DuVZXuJh.mjs";
7
+ import { a as pluginId } from "./index-BbbrBv3t.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-CKrO7KSQ.js");
9
+ const index = require("./index-BRESWp1b.js");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
12
  const theme = {
@@ -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-CKrO7KSQ.js");
9
+ const index = require("./index-BRESWp1b.js");
10
10
  const OnlineUsersWidget = () => {
11
11
  const { formatMessage } = reactIntl.useIntl();
12
12
  const { get } = admin.useFetchClient();
@@ -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-DuVZXuJh.mjs";
7
+ import { g as getTranslation } from "./index-BbbrBv3t.mjs";
8
8
  const OnlineUsersWidget = () => {
9
9
  const { formatMessage } = useIntl();
10
10
  const { get } = useFetchClient();
@@ -7,8 +7,8 @@ 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-CKrO7KSQ.js");
11
- const useLicense = require("./useLicense-DHAFqFSZ.js");
10
+ const index = require("./index-BRESWp1b.js");
11
+ const useLicense = require("./useLicense-D_wQcoMn.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);
@@ -350,6 +350,14 @@ const SettingsPage = () => {
350
350
  lastSeenRateLimit: 30,
351
351
  retentionDays: 90,
352
352
  maxSessionAgeDays: 30,
353
+ // Grace window (ms) during which a freshly-issued JWT is accepted
354
+ // without a matching session row. Prevents strict-session enforcement
355
+ // from blocking the very first request after login. 0 disables.
356
+ sessionCreationGraceMs: 5e3,
357
+ // Opt-in fast path for the periodic idle-session cleanup: single
358
+ // SQL UPDATE instead of batched Document-Service writes. Bypasses
359
+ // lifecycle hooks; required for very large installations.
360
+ cleanupUseDbDirect: false,
353
361
  strictSessionEnforcement: false,
354
362
  trustedProxies: false,
355
363
  enableGeolocation: true,
@@ -397,7 +405,7 @@ const SettingsPage = () => {
397
405
  }
398
406
  });
399
407
  }
400
- setSettings(loadedSettings);
408
+ setSettings((prev) => ({ ...prev, ...loadedSettings }));
401
409
  } else {
402
410
  setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
403
411
  }
@@ -632,6 +640,45 @@ const SettingsPage = () => {
632
640
  ),
633
641
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: settings.retentionDays === -1 ? t("settings.general.retention.hintNever", "Old sessions deleted after never") : t("settings.general.retention.hint", "Old sessions deleted after {days}", { days: `${settings.retentionDays} days` }) })
634
642
  ] }) }),
643
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
644
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.grace.title", "Post-Login Grace Period") }),
645
+ /* @__PURE__ */ jsxRuntime.jsxs(
646
+ designSystem.SingleSelect,
647
+ {
648
+ value: String(settings.sessionCreationGraceMs ?? 5e3),
649
+ onChange: (value) => handleChange("sessionCreationGraceMs", parseInt(value)),
650
+ children: [
651
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "0", children: t("settings.general.grace.off", "Off (strict from first request)") }),
652
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "2000", children: t("settings.general.grace.2s", "2 seconds") }),
653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5000", children: t("settings.general.grace.5s", "5 seconds (Recommended)") }),
654
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10000", children: t("settings.general.grace.10s", "10 seconds") }),
655
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30000", children: t("settings.general.grace.30s", "30 seconds") })
656
+ ]
657
+ }
658
+ ),
659
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t(
660
+ "settings.general.grace.hint",
661
+ "Accepts a freshly-issued JWT without a matching session row for this window, preventing strict-enforcement races right after login."
662
+ ) })
663
+ ] }) }),
664
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
665
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.cleanupMode.title", "Cleanup Strategy") }),
666
+ /* @__PURE__ */ jsxRuntime.jsxs(
667
+ designSystem.SingleSelect,
668
+ {
669
+ value: settings.cleanupUseDbDirect ? "db" : "document-service",
670
+ onChange: (value) => handleChange("cleanupUseDbDirect", value === "db"),
671
+ children: [
672
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "document-service", children: t("settings.general.cleanupMode.ds", "Document Service (Recommended, safe)") }),
673
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "db", children: t("settings.general.cleanupMode.db", "Direct SQL UPDATE (large-scale)") })
674
+ ]
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t(
678
+ "settings.general.cleanupMode.hint",
679
+ "Direct SQL drains the entire idle-session backlog in one statement but bypasses lifecycle hooks. Only switch if you run >50 k active sessions."
680
+ ) })
681
+ ] }) }),
635
682
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "danger100", style: { borderRadius: theme.borderRadius.md, border: `2px solid rgba(220, 38, 38, 0.2)` }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
636
683
  /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { style: { width: "18px", height: "18px", color: "var(--colors-danger600, #DC2626)", flexShrink: 0, marginTop: "2px" } }),
637
684
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1 }, children: [
@@ -5,8 +5,8 @@ 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-DuVZXuJh.mjs";
9
- import { u as useLicense } from "./useLicense-Xzo6nyh3.mjs";
8
+ import { a as pluginId, g as getTranslation } from "./index-BbbrBv3t.mjs";
9
+ import { u as useLicense } from "./useLicense-Bszkymz3.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
11
  const theme = {
12
12
  borderRadius: { md: "8px", lg: "12px" }
@@ -346,6 +346,14 @@ const SettingsPage = () => {
346
346
  lastSeenRateLimit: 30,
347
347
  retentionDays: 90,
348
348
  maxSessionAgeDays: 30,
349
+ // Grace window (ms) during which a freshly-issued JWT is accepted
350
+ // without a matching session row. Prevents strict-session enforcement
351
+ // from blocking the very first request after login. 0 disables.
352
+ sessionCreationGraceMs: 5e3,
353
+ // Opt-in fast path for the periodic idle-session cleanup: single
354
+ // SQL UPDATE instead of batched Document-Service writes. Bypasses
355
+ // lifecycle hooks; required for very large installations.
356
+ cleanupUseDbDirect: false,
349
357
  strictSessionEnforcement: false,
350
358
  trustedProxies: false,
351
359
  enableGeolocation: true,
@@ -393,7 +401,7 @@ const SettingsPage = () => {
393
401
  }
394
402
  });
395
403
  }
396
- setSettings(loadedSettings);
404
+ setSettings((prev) => ({ ...prev, ...loadedSettings }));
397
405
  } else {
398
406
  setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
399
407
  }
@@ -628,6 +636,45 @@ const SettingsPage = () => {
628
636
  ),
629
637
  /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: settings.retentionDays === -1 ? t("settings.general.retention.hintNever", "Old sessions deleted after never") : t("settings.general.retention.hint", "Old sessions deleted after {days}", { days: `${settings.retentionDays} days` }) })
630
638
  ] }) }),
639
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
640
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.grace.title", "Post-Login Grace Period") }),
641
+ /* @__PURE__ */ jsxs(
642
+ SingleSelect,
643
+ {
644
+ value: String(settings.sessionCreationGraceMs ?? 5e3),
645
+ onChange: (value) => handleChange("sessionCreationGraceMs", parseInt(value)),
646
+ children: [
647
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "0", children: t("settings.general.grace.off", "Off (strict from first request)") }),
648
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "2000", children: t("settings.general.grace.2s", "2 seconds") }),
649
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "5000", children: t("settings.general.grace.5s", "5 seconds (Recommended)") }),
650
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10000", children: t("settings.general.grace.10s", "10 seconds") }),
651
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30000", children: t("settings.general.grace.30s", "30 seconds") })
652
+ ]
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t(
656
+ "settings.general.grace.hint",
657
+ "Accepts a freshly-issued JWT without a matching session row for this window, preventing strict-enforcement races right after login."
658
+ ) })
659
+ ] }) }),
660
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
661
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.cleanupMode.title", "Cleanup Strategy") }),
662
+ /* @__PURE__ */ jsxs(
663
+ SingleSelect,
664
+ {
665
+ value: settings.cleanupUseDbDirect ? "db" : "document-service",
666
+ onChange: (value) => handleChange("cleanupUseDbDirect", value === "db"),
667
+ children: [
668
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "document-service", children: t("settings.general.cleanupMode.ds", "Document Service (Recommended, safe)") }),
669
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "db", children: t("settings.general.cleanupMode.db", "Direct SQL UPDATE (large-scale)") })
670
+ ]
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t(
674
+ "settings.general.cleanupMode.hint",
675
+ "Direct SQL drains the entire idle-session backlog in one statement but bypasses lifecycle hooks. Only switch if you run >50 k active sessions."
676
+ ) })
677
+ ] }) }),
631
678
  /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "danger100", style: { borderRadius: theme.borderRadius.md, border: `2px solid rgba(220, 38, 38, 0.2)` }, children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-start", children: [
632
679
  /* @__PURE__ */ jsx(Trash, { style: { width: "18px", height: "18px", color: "var(--colors-danger600, #DC2626)", flexShrink: 0, marginTop: "2px" } }),
633
680
  /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
@@ -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-CKrO7KSQ.js");
9
+ const index = require("./index-BRESWp1b.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)`
@@ -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-DuVZXuJh.mjs";
7
+ import { a as pluginId } from "./index-BbbrBv3t.mjs";
8
8
  const Container = styled(Box)`
9
9
  padding: 32px;
10
10
  max-width: 1400px;
@@ -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-Bir8yK_r.js")),
447
+ Component: () => Promise.resolve().then(() => require("./App-BvYLeEbF.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-KqUN7mDh.js")),
464
+ Component: () => Promise.resolve().then(() => require("./UpgradePage-Bh21Lg-G.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-CO3-iggu.js")),
474
+ Component: () => Promise.resolve().then(() => require("./Settings-Bt6wy131.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-B7t0WvG7.js")),
484
+ Component: () => Promise.resolve().then(() => require("./Analytics-Cp-WjeV3.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-B56Xklj2.js")),
494
+ Component: () => Promise.resolve().then(() => require("./License-DPyeZQWz.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-DTEzguhS.js"));
513
+ const component = await Promise.resolve().then(() => require("./OnlineUsersWidget-BYnqhN3O.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-LfsjcXXp.mjs"),
444
+ Component: () => import("./App-CN4bKWV9.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-TBx1l2mQ.mjs"),
461
+ Component: () => import("./UpgradePage-DM23ZYVa.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-Bz3lPBJ6.mjs"),
471
+ Component: () => import("./Settings-D9N7m11p.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-DmlZwyVu.mjs"),
481
+ Component: () => import("./Analytics-DQyamHg6.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-OToijT0s.mjs"),
491
+ Component: () => import("./License-Bgcm630d.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-Fpc0aH2z.mjs");
510
+ const component = await import("./OnlineUsersWidget-E2FA7rGi.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-DuVZXuJh.mjs";
3
+ import { a as pluginId } from "./index-BbbrBv3t.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-CKrO7KSQ.js");
4
+ const index = require("./index-BRESWp1b.js");
5
5
  const useLicense = () => {
6
6
  const { get } = admin.useFetchClient();
7
7
  const [isPremium, setIsPremium] = react.useState(false);
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-CKrO7KSQ.js");
2
+ const index = require("../_chunks/index-BRESWp1b.js");
3
3
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-DuVZXuJh.mjs";
1
+ import { i } from "../_chunks/index-BbbrBv3t.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -11732,15 +11732,27 @@ var settings$1 = {
11732
11732
  let settings2 = await pluginStore.get({ key: "settings" });
11733
11733
  if (!settings2) {
11734
11734
  settings2 = {
11735
+ // Timing & cleanup
11735
11736
  inactivityTimeout: 15,
11736
11737
  cleanupInterval: 30,
11737
11738
  lastSeenRateLimit: 30,
11738
11739
  retentionDays: 90,
11739
11740
  maxSessionAgeDays: 30,
11741
+ // Grace window (ms) during which a freshly-issued JWT is accepted
11742
+ // without a matching session row — see JWT-verify wrapper in bootstrap.
11743
+ sessionCreationGraceMs: 5e3,
11744
+ // Opt-in: use a single SQL UPDATE for idle-session cleanup
11745
+ // (bypasses Document Service lifecycle, required for very large installs).
11746
+ cleanupUseDbDirect: false,
11747
+ // Geo / security
11740
11748
  enableGeolocation: true,
11741
11749
  enableSecurityScoring: true,
11742
11750
  blockSuspiciousSessions: false,
11743
11751
  maxFailedLogins: 5,
11752
+ enableGeofencing: false,
11753
+ allowedCountries: [],
11754
+ blockedCountries: [],
11755
+ // Notifications
11744
11756
  enableEmailAlerts: false,
11745
11757
  alertOnSuspiciousLogin: true,
11746
11758
  alertOnNewLocation: true,
@@ -11748,11 +11760,10 @@ var settings$1 = {
11748
11760
  enableWebhooks: false,
11749
11761
  discordWebhookUrl: "",
11750
11762
  slackWebhookUrl: "",
11751
- enableGeofencing: false,
11752
- allowedCountries: [],
11753
- blockedCountries: [],
11763
+ // Session policy
11754
11764
  strictSessionEnforcement: false,
11755
11765
  trustedProxies: false,
11766
+ // Email templates
11756
11767
  emailTemplates: {
11757
11768
  suspiciousLogin: { subject: "", html: "", text: "" },
11758
11769
  newLocation: { subject: "", html: "", text: "" },
@@ -11785,16 +11796,26 @@ var settings$1 = {
11785
11796
  type: "plugin",
11786
11797
  name: "magic-sessionmanager"
11787
11798
  });
11799
+ const graceRaw = body.sessionCreationGraceMs;
11800
+ const grace = Number.isFinite(parseInt(graceRaw)) ? Math.max(0, Math.min(parseInt(graceRaw), 3e4)) : 5e3;
11788
11801
  const sanitizedSettings = {
11802
+ // Timing & cleanup
11789
11803
  inactivityTimeout: Math.max(1, Math.min(parseInt(body.inactivityTimeout) || 15, 1440)),
11790
11804
  cleanupInterval: Math.max(5, Math.min(parseInt(body.cleanupInterval) || 30, 1440)),
11791
11805
  lastSeenRateLimit: Math.max(5, Math.min(parseInt(body.lastSeenRateLimit) || 30, 300)),
11792
11806
  retentionDays: Math.max(1, Math.min(parseInt(body.retentionDays) || 90, 365)),
11793
11807
  maxSessionAgeDays: Math.max(1, Math.min(parseInt(body.maxSessionAgeDays) || 30, 365)),
11808
+ sessionCreationGraceMs: grace,
11809
+ cleanupUseDbDirect: !!body.cleanupUseDbDirect,
11810
+ // Geo / security
11794
11811
  enableGeolocation: !!body.enableGeolocation,
11795
11812
  enableSecurityScoring: !!body.enableSecurityScoring,
11796
11813
  blockSuspiciousSessions: !!body.blockSuspiciousSessions,
11797
11814
  maxFailedLogins: Math.max(1, Math.min(parseInt(body.maxFailedLogins) || 5, 100)),
11815
+ enableGeofencing: !!body.enableGeofencing,
11816
+ allowedCountries: sanitizeCountryList(body.allowedCountries),
11817
+ blockedCountries: sanitizeCountryList(body.blockedCountries),
11818
+ // Notifications
11798
11819
  enableEmailAlerts: !!body.enableEmailAlerts,
11799
11820
  alertOnSuspiciousLogin: !!body.alertOnSuspiciousLogin,
11800
11821
  alertOnNewLocation: !!body.alertOnNewLocation,
@@ -11802,11 +11823,10 @@ var settings$1 = {
11802
11823
  enableWebhooks: !!body.enableWebhooks,
11803
11824
  discordWebhookUrl: sanitizeWebhookUrl(body.discordWebhookUrl, "discord"),
11804
11825
  slackWebhookUrl: sanitizeWebhookUrl(body.slackWebhookUrl, "slack"),
11805
- enableGeofencing: !!body.enableGeofencing,
11806
- allowedCountries: sanitizeCountryList(body.allowedCountries),
11807
- blockedCountries: sanitizeCountryList(body.blockedCountries),
11826
+ // Session policy
11808
11827
  strictSessionEnforcement: !!body.strictSessionEnforcement,
11809
11828
  trustedProxies: !!body.trustedProxies,
11829
+ // Email templates
11810
11830
  emailTemplates: sanitizeEmailTemplates(body.emailTemplates)
11811
11831
  };
11812
11832
  await pluginStore.set({
@@ -11719,15 +11719,27 @@ var settings$1 = {
11719
11719
  let settings2 = await pluginStore.get({ key: "settings" });
11720
11720
  if (!settings2) {
11721
11721
  settings2 = {
11722
+ // Timing & cleanup
11722
11723
  inactivityTimeout: 15,
11723
11724
  cleanupInterval: 30,
11724
11725
  lastSeenRateLimit: 30,
11725
11726
  retentionDays: 90,
11726
11727
  maxSessionAgeDays: 30,
11728
+ // Grace window (ms) during which a freshly-issued JWT is accepted
11729
+ // without a matching session row — see JWT-verify wrapper in bootstrap.
11730
+ sessionCreationGraceMs: 5e3,
11731
+ // Opt-in: use a single SQL UPDATE for idle-session cleanup
11732
+ // (bypasses Document Service lifecycle, required for very large installs).
11733
+ cleanupUseDbDirect: false,
11734
+ // Geo / security
11727
11735
  enableGeolocation: true,
11728
11736
  enableSecurityScoring: true,
11729
11737
  blockSuspiciousSessions: false,
11730
11738
  maxFailedLogins: 5,
11739
+ enableGeofencing: false,
11740
+ allowedCountries: [],
11741
+ blockedCountries: [],
11742
+ // Notifications
11731
11743
  enableEmailAlerts: false,
11732
11744
  alertOnSuspiciousLogin: true,
11733
11745
  alertOnNewLocation: true,
@@ -11735,11 +11747,10 @@ var settings$1 = {
11735
11747
  enableWebhooks: false,
11736
11748
  discordWebhookUrl: "",
11737
11749
  slackWebhookUrl: "",
11738
- enableGeofencing: false,
11739
- allowedCountries: [],
11740
- blockedCountries: [],
11750
+ // Session policy
11741
11751
  strictSessionEnforcement: false,
11742
11752
  trustedProxies: false,
11753
+ // Email templates
11743
11754
  emailTemplates: {
11744
11755
  suspiciousLogin: { subject: "", html: "", text: "" },
11745
11756
  newLocation: { subject: "", html: "", text: "" },
@@ -11772,16 +11783,26 @@ var settings$1 = {
11772
11783
  type: "plugin",
11773
11784
  name: "magic-sessionmanager"
11774
11785
  });
11786
+ const graceRaw = body.sessionCreationGraceMs;
11787
+ const grace = Number.isFinite(parseInt(graceRaw)) ? Math.max(0, Math.min(parseInt(graceRaw), 3e4)) : 5e3;
11775
11788
  const sanitizedSettings = {
11789
+ // Timing & cleanup
11776
11790
  inactivityTimeout: Math.max(1, Math.min(parseInt(body.inactivityTimeout) || 15, 1440)),
11777
11791
  cleanupInterval: Math.max(5, Math.min(parseInt(body.cleanupInterval) || 30, 1440)),
11778
11792
  lastSeenRateLimit: Math.max(5, Math.min(parseInt(body.lastSeenRateLimit) || 30, 300)),
11779
11793
  retentionDays: Math.max(1, Math.min(parseInt(body.retentionDays) || 90, 365)),
11780
11794
  maxSessionAgeDays: Math.max(1, Math.min(parseInt(body.maxSessionAgeDays) || 30, 365)),
11795
+ sessionCreationGraceMs: grace,
11796
+ cleanupUseDbDirect: !!body.cleanupUseDbDirect,
11797
+ // Geo / security
11781
11798
  enableGeolocation: !!body.enableGeolocation,
11782
11799
  enableSecurityScoring: !!body.enableSecurityScoring,
11783
11800
  blockSuspiciousSessions: !!body.blockSuspiciousSessions,
11784
11801
  maxFailedLogins: Math.max(1, Math.min(parseInt(body.maxFailedLogins) || 5, 100)),
11802
+ enableGeofencing: !!body.enableGeofencing,
11803
+ allowedCountries: sanitizeCountryList(body.allowedCountries),
11804
+ blockedCountries: sanitizeCountryList(body.blockedCountries),
11805
+ // Notifications
11785
11806
  enableEmailAlerts: !!body.enableEmailAlerts,
11786
11807
  alertOnSuspiciousLogin: !!body.alertOnSuspiciousLogin,
11787
11808
  alertOnNewLocation: !!body.alertOnNewLocation,
@@ -11789,11 +11810,10 @@ var settings$1 = {
11789
11810
  enableWebhooks: !!body.enableWebhooks,
11790
11811
  discordWebhookUrl: sanitizeWebhookUrl(body.discordWebhookUrl, "discord"),
11791
11812
  slackWebhookUrl: sanitizeWebhookUrl(body.slackWebhookUrl, "slack"),
11792
- enableGeofencing: !!body.enableGeofencing,
11793
- allowedCountries: sanitizeCountryList(body.allowedCountries),
11794
- blockedCountries: sanitizeCountryList(body.blockedCountries),
11813
+ // Session policy
11795
11814
  strictSessionEnforcement: !!body.strictSessionEnforcement,
11796
11815
  trustedProxies: !!body.trustedProxies,
11816
+ // Email templates
11797
11817
  emailTemplates: sanitizeEmailTemplates(body.emailTemplates)
11798
11818
  };
11799
11819
  await pluginStore.set({
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.5.0",
2
+ "version": "4.5.1",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",