strapi-plugin-oidc 1.8.4 → 1.8.5

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/README.md CHANGED
@@ -48,7 +48,7 @@ module.exports = ({ env }) => ({
48
48
  OIDC_GROUP_FIELD: 'groups', // OIDC claim field containing group membership
49
49
  OIDC_GROUP_ROLE_MAP: '{}', // JSON map of group names to Strapi role names
50
50
  OIDC_REQUIRE_EMAIL_VERIFIED: true, // Reject logins when provider does not report email_verified=true (set false to disable)
51
- OIDC_TRUSTED_IP_HEADER: '', // Optional: header set by your CDN/proxy containing the real client IP (see note below); only honoured when server.proxy: true
51
+ OIDC_TRUSTED_IP_HEADER: '', // Optional: header set by your CDN/proxy containing the real client IP (see note below); only honoured when Koa proxy mode is enabled (see below)
52
52
  OIDC_FORCE_SECURE_COOKIES: false, // Set true when behind a trusted HTTPS proxy that Strapi can't auto-detect
53
53
  },
54
54
  },
@@ -67,9 +67,17 @@ module.exports = ({ env }) => ({
67
67
 
68
68
  ### Client IP attribution and reverse proxies
69
69
 
70
- The plugin logs client IPs for rate-limit buckets and audit logs. When Strapi runs behind a reverse proxy, **set `server.proxy: true`** so Koa trusts `X-Forwarded-For`; otherwise all IPs will be the proxy's.
70
+ The plugin logs client IPs for rate-limit buckets and audit logs. When Strapi runs behind a reverse proxy, enable Koa proxy mode so Strapi trusts `X-Forwarded-For`; otherwise all IPs will be the proxy's internal address.
71
71
 
72
- Set `OIDC_TRUSTED_IP_HEADER` to the header your CDN or proxy uses to forward the real client IP. The header is only honoured when `server.proxy: true` is set. Accepted values (all others are silently ignored):
72
+ In `config/server.ts`:
73
+
74
+ ```ts
75
+ proxy: {
76
+ koa: true,
77
+ },
78
+ ```
79
+
80
+ Set `OIDC_TRUSTED_IP_HEADER` to the header your CDN or proxy uses to forward the real client IP. The header is only honoured when Koa proxy mode is enabled. Accepted values (all others are silently ignored):
73
81
 
74
82
  | Header | Provider |
75
83
  | --------------------------- | ------------------------------------------------- |
@@ -205,7 +205,7 @@ const index = {
205
205
  defaultMessage: "Configuration"
206
206
  },
207
207
  Component: async () => {
208
- return await import("./index-BqWd-Iiq.mjs");
208
+ return await import("./index-DRFXk_MQ.mjs");
209
209
  },
210
210
  permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }]
211
211
  }
@@ -208,7 +208,7 @@ const index = {
208
208
  defaultMessage: "Configuration"
209
209
  },
210
210
  Component: async () => {
211
- return await Promise.resolve().then(() => require("./index-Bmg4eTYb.js"));
211
+ return await Promise.resolve().then(() => require("./index-DDCcJt16.js"));
212
212
  },
213
213
  permissions: [{ action: "plugin::strapi-plugin-oidc.read", subject: null }]
214
214
  }
@@ -7,7 +7,7 @@ const React = require("react");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
9
  const reactIntl = require("react-intl");
10
- const index = require("./index-Dk6TYtio.js");
10
+ const index = require("./index-C8nfr95D.js");
11
11
  const styled = require("styled-components");
12
12
  const lucideReact = require("lucide-react");
13
13
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -852,7 +852,7 @@ function TablePagination({ page, pageCount, onPageChange, total }) {
852
852
  ] }) });
853
853
  }
854
854
  const PAGE_SIZE$1 = 10;
855
- const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
855
+ const EMAIL_REGEX$1 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
856
856
  function Whitelist({
857
857
  users,
858
858
  useWhitelist,
@@ -893,7 +893,7 @@ function Whitelist({
893
893
  if (!Array.isArray(parsed)) throw new Error();
894
894
  const emails = parsed.filter((item) => item?.email).map(
895
895
  (item) => String(item.email).trim().toLowerCase()
896
- ).filter((email2) => EMAIL_REGEX.test(email2));
896
+ ).filter((email2) => EMAIL_REGEX$1.test(email2));
897
897
  const count = await onImport(emails);
898
898
  if (count === 0) {
899
899
  toggleNotification({
@@ -935,7 +935,7 @@ function Whitelist({
935
935
  type: "text",
936
936
  disabled: loading,
937
937
  value: email,
938
- hasError: Boolean(email && !EMAIL_REGEX.test(email)),
938
+ hasError: Boolean(email && !EMAIL_REGEX$1.test(email)),
939
939
  onChange: (e) => setEmail(e.currentTarget.value),
940
940
  placeholder: formatMessage(index.getTrad("whitelist.email.placeholder")),
941
941
  style: { fontSize: "1.4rem", lineHeight: "2.2rem" }
@@ -946,7 +946,7 @@ function Whitelist({
946
946
  {
947
947
  size: "S",
948
948
  startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
949
- disabled: loading || email.trim() === "" || !EMAIL_REGEX.test(email),
949
+ disabled: loading || email.trim() === "" || !EMAIL_REGEX$1.test(email),
950
950
  loading,
951
951
  onClick: onSaveEmail,
952
952
  children: formatMessage(index.getTrad("page.add"))
@@ -1066,6 +1066,153 @@ function Whitelist({
1066
1066
  ] })
1067
1067
  ] });
1068
1068
  }
1069
+ const AUDIT_ACTIONS = [
1070
+ "login_success",
1071
+ "login_failure",
1072
+ "missing_code",
1073
+ "state_mismatch",
1074
+ "nonce_mismatch",
1075
+ "token_exchange_failed",
1076
+ "whitelist_rejected",
1077
+ "email_not_verified",
1078
+ "id_token_invalid",
1079
+ "logout",
1080
+ "session_expired",
1081
+ "user_created"
1082
+ ];
1083
+ const IP_REGEX = /^(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::))$/;
1084
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1085
+ function FilterBar({
1086
+ filters,
1087
+ hasActiveFilters,
1088
+ onFiltersChange,
1089
+ onResetPage,
1090
+ onClear
1091
+ }) {
1092
+ const { formatMessage } = reactIntl.useIntl();
1093
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1094
+ designSystem.Box,
1095
+ {
1096
+ background: "neutral100",
1097
+ hasRadius: true,
1098
+ padding: 4,
1099
+ marginBottom: 4,
1100
+ borderColor: "neutral200",
1101
+ borderWidth: "1px",
1102
+ borderStyle: "solid",
1103
+ children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 3, children: [
1105
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { size: "1.6rem" }) }),
1106
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", children: formatMessage(index.getTrad("auditlog.filters")) })
1107
+ ] }),
1108
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsx(
1110
+ TagDateInput,
1111
+ {
1112
+ placeholder: formatMessage(index.getTrad("auditlog.filters.createdAt")),
1113
+ value: filters.createdAt ?? [],
1114
+ onChange: (selections) => {
1115
+ onFiltersChange((prev) => {
1116
+ if (selections.length === 0) {
1117
+ const { createdAt, ...rest } = prev;
1118
+ return rest;
1119
+ }
1120
+ return { ...prev, createdAt: selections };
1121
+ });
1122
+ onResetPage();
1123
+ },
1124
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Calendar, { width: "1.4rem", height: "1.4rem" }) })
1125
+ }
1126
+ ),
1127
+ /* @__PURE__ */ jsxRuntime.jsx(
1128
+ TagInput,
1129
+ {
1130
+ value: filters.action ?? [],
1131
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, action: value })),
1132
+ options: AUDIT_ACTIONS,
1133
+ placeholder: formatMessage(index.getTrad("auditlog.filters.action")),
1134
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { size: "1.4rem" }) })
1135
+ }
1136
+ ),
1137
+ /* @__PURE__ */ jsxRuntime.jsx(
1138
+ TagInput,
1139
+ {
1140
+ value: filters.email ?? [],
1141
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, email: value })),
1142
+ placeholder: formatMessage(index.getTrad("auditlog.filters.email")),
1143
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Mail, { width: "1.4rem", height: "1.4rem" }) }),
1144
+ validate: (v) => EMAIL_REGEX.test(v)
1145
+ }
1146
+ ),
1147
+ /* @__PURE__ */ jsxRuntime.jsx(
1148
+ TagInput,
1149
+ {
1150
+ value: filters.ip ?? [],
1151
+ onChange: (value) => onFiltersChange((prev) => ({ ...prev, ip: value })),
1152
+ placeholder: formatMessage(index.getTrad("auditlog.filters.ip")),
1153
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Server, { size: "1.4rem" }) }),
1154
+ validate: (v) => IP_REGEX.test(v)
1155
+ }
1156
+ ),
1157
+ hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(SizedButton, { size: "S", variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}), onClick: onClear, children: formatMessage(index.getTrad("auditlog.filters.clear")) })
1158
+ ] })
1159
+ ]
1160
+ }
1161
+ );
1162
+ }
1163
+ const DETAILS_TEXT_STYLE = {
1164
+ display: "block",
1165
+ overflow: "hidden",
1166
+ textOverflow: "ellipsis",
1167
+ whiteSpace: "nowrap",
1168
+ maxWidth: "180px",
1169
+ cursor: "help"
1170
+ };
1171
+ function LogTable({ records, loading, hasActiveFilters }) {
1172
+ const { formatMessage } = reactIntl.useIntl();
1173
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: "100%" }, children: [
1174
+ /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
1175
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1176
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
1177
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
1178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
1179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
1180
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
1181
+ ] }) }),
1182
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
1183
+ records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
1184
+ records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" }, children: [
1185
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
1186
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1187
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
1188
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
1189
+ icons.Information,
1190
+ {
1191
+ "aria-hidden": true,
1192
+ style: { cursor: "help" },
1193
+ width: "1.4rem",
1194
+ height: "1.4rem",
1195
+ fill: "primary600"
1196
+ }
1197
+ ) })
1198
+ ] }) }),
1199
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
1200
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
1201
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
1202
+ ] }, record.id))
1203
+ ] })
1204
+ ] }),
1205
+ loading && records.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1206
+ designSystem.Flex,
1207
+ {
1208
+ justifyContent: "center",
1209
+ alignItems: "center",
1210
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
1211
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
1212
+ }
1213
+ )
1214
+ ] });
1215
+ }
1069
1216
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
1070
1217
  function getDefaultExportFromCjs(x) {
1071
1218
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
@@ -3607,30 +3754,6 @@ function requireLib() {
3607
3754
  }
3608
3755
  var libExports = /* @__PURE__ */ requireLib();
3609
3756
  const qs = /* @__PURE__ */ getDefaultExportFromCjs(libExports);
3610
- const AUDIT_ACTIONS = [
3611
- "login_success",
3612
- "login_failure",
3613
- "missing_code",
3614
- "state_mismatch",
3615
- "nonce_mismatch",
3616
- "token_exchange_failed",
3617
- "whitelist_rejected",
3618
- "email_not_verified",
3619
- "id_token_invalid",
3620
- "logout",
3621
- "session_expired",
3622
- "user_created"
3623
- ];
3624
- const PAGE_SIZE = 10;
3625
- const MIN_SPINNER_MS = 400;
3626
- const DETAILS_TEXT_STYLE = {
3627
- display: "block",
3628
- overflow: "hidden",
3629
- textOverflow: "ellipsis",
3630
- whiteSpace: "nowrap",
3631
- maxWidth: "180px",
3632
- cursor: "help"
3633
- };
3634
3757
  function toWireFilters(f) {
3635
3758
  const out = {};
3636
3759
  if (f.action?.length) out.action = { $or: f.action.map((v) => ({ $eq: v })) };
@@ -3647,11 +3770,11 @@ function buildQueryString(params) {
3647
3770
  const { filters, ...rest } = params;
3648
3771
  return qs.stringify(
3649
3772
  { ...rest, filters: filters ? toWireFilters(filters) : void 0 },
3650
- {
3651
- encodeValuesOnly: true
3652
- }
3773
+ { encodeValuesOnly: true }
3653
3774
  );
3654
3775
  }
3776
+ const PAGE_SIZE = 10;
3777
+ const MIN_SPINNER_MS = 400;
3655
3778
  function useDebounced(value, delay = 300) {
3656
3779
  const [debounced, setDebounced] = React.useState(value);
3657
3780
  React.useEffect(() => {
@@ -3660,10 +3783,8 @@ function useDebounced(value, delay = 300) {
3660
3783
  }, [value, delay]);
3661
3784
  return debounced;
3662
3785
  }
3663
- function AuditLog({ title } = {}) {
3664
- const { formatMessage } = reactIntl.useIntl();
3665
- const { get: get2, del } = admin.useFetchClient();
3666
- const { toggleNotification } = admin.useNotification();
3786
+ function useAuditLogs(page, filters) {
3787
+ const { get: get2 } = admin.useFetchClient();
3667
3788
  const [records, setRecords] = React.useState([]);
3668
3789
  const [pagination, setPagination] = React.useState({
3669
3790
  page: 1,
@@ -3671,9 +3792,7 @@ function AuditLog({ title } = {}) {
3671
3792
  total: 0,
3672
3793
  pageCount: 1
3673
3794
  });
3674
- const [page, setPage] = React.useState(1);
3675
3795
  const [loading, setLoading] = React.useState(true);
3676
- const [filters, setFilters] = React.useState({});
3677
3796
  const fetchGenRef = React.useRef(0);
3678
3797
  const debouncedFilters = useDebounced(filters);
3679
3798
  const fetchLogs = React.useCallback(
@@ -3707,6 +3826,15 @@ function AuditLog({ title } = {}) {
3707
3826
  React.useEffect(() => {
3708
3827
  fetchLogs(page, debouncedFilters);
3709
3828
  }, [fetchLogs, page, debouncedFilters]);
3829
+ return { records, pagination, loading };
3830
+ }
3831
+ function AuditLog({ title } = {}) {
3832
+ const { formatMessage } = reactIntl.useIntl();
3833
+ const { del } = admin.useFetchClient();
3834
+ const { toggleNotification } = admin.useNotification();
3835
+ const [page, setPage] = React.useState(1);
3836
+ const [filters, setFilters] = React.useState({});
3837
+ const { records, pagination, loading } = useAuditLogs(page, filters);
3710
3838
  const handleClearAll = async () => {
3711
3839
  try {
3712
3840
  await del("/strapi-plugin-oidc/audit-logs");
@@ -3727,9 +3855,7 @@ function AuditLog({ title } = {}) {
3727
3855
  try {
3728
3856
  const cookieMatch = document.cookie.match(/(?:^|;\s*)jwtToken=([^;]+)/);
3729
3857
  const token = cookieMatch ? decodeURIComponent(cookieMatch[1]) : "";
3730
- const queryString = buildQueryString({
3731
- filters
3732
- });
3858
+ const queryString = buildQueryString({ filters });
3733
3859
  const response = await fetch(`/strapi-plugin-oidc/audit-logs/export?${queryString}`, {
3734
3860
  headers: { Authorization: `Bearer ${token}` }
3735
3861
  });
@@ -3797,135 +3923,17 @@ function AuditLog({ title } = {}) {
3797
3923
  )
3798
3924
  ] })
3799
3925
  ] }),
3800
- /* @__PURE__ */ jsxRuntime.jsxs(
3801
- designSystem.Box,
3926
+ /* @__PURE__ */ jsxRuntime.jsx(
3927
+ FilterBar,
3802
3928
  {
3803
- background: "neutral100",
3804
- hasRadius: true,
3805
- padding: 4,
3806
- marginBottom: 4,
3807
- borderColor: "neutral200",
3808
- borderWidth: "1px",
3809
- borderStyle: "solid",
3810
- children: [
3811
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", marginBottom: 3, children: [
3812
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { size: "1.6rem" }) }),
3813
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h3", children: formatMessage(index.getTrad("auditlog.filters")) })
3814
- ] }),
3815
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
3816
- /* @__PURE__ */ jsxRuntime.jsx(
3817
- TagDateInput,
3818
- {
3819
- placeholder: formatMessage(index.getTrad("auditlog.filters.createdAt")),
3820
- value: filters.createdAt ?? [],
3821
- onChange: (selections) => {
3822
- setFilters((prev) => {
3823
- if (selections.length === 0) {
3824
- const { createdAt: _removed, ...rest } = prev;
3825
- return rest;
3826
- }
3827
- return { ...prev, createdAt: selections };
3828
- });
3829
- setPage(1);
3830
- },
3831
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Calendar, { width: "1.4rem", height: "1.4rem" }) })
3832
- }
3833
- ),
3834
- /* @__PURE__ */ jsxRuntime.jsx(
3835
- TagInput,
3836
- {
3837
- value: filters.action ?? [],
3838
- onChange: (value) => setFilters((prev) => ({ ...prev, action: value })),
3839
- options: AUDIT_ACTIONS,
3840
- placeholder: formatMessage(index.getTrad("auditlog.filters.action")),
3841
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardList, { size: "1.4rem" }) })
3842
- }
3843
- ),
3844
- /* @__PURE__ */ jsxRuntime.jsx(
3845
- TagInput,
3846
- {
3847
- value: filters.email ?? [],
3848
- onChange: (value) => setFilters((prev) => ({ ...prev, email: value })),
3849
- placeholder: formatMessage(index.getTrad("auditlog.filters.email")),
3850
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Mail, { width: "1.4rem", height: "1.4rem" }) }),
3851
- validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)
3852
- }
3853
- ),
3854
- /* @__PURE__ */ jsxRuntime.jsx(
3855
- TagInput,
3856
- {
3857
- value: filters.ip ?? [],
3858
- onChange: (value) => setFilters((prev) => ({ ...prev, ip: value })),
3859
- placeholder: formatMessage(index.getTrad("auditlog.filters.ip")),
3860
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Server, { size: "1.4rem" }) }),
3861
- validate: (v) => /^(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::))$/.test(
3862
- v
3863
- )
3864
- }
3865
- ),
3866
- hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
3867
- SizedButton,
3868
- {
3869
- size: "S",
3870
- variant: "danger-light",
3871
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}),
3872
- onClick: clearFilters,
3873
- children: formatMessage(index.getTrad("auditlog.filters.clear"))
3874
- }
3875
- )
3876
- ] })
3877
- ]
3929
+ filters,
3930
+ hasActiveFilters,
3931
+ onFiltersChange: setFilters,
3932
+ onResetPage: () => setPage(1),
3933
+ onClear: clearFilters
3878
3934
  }
3879
3935
  ),
3880
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: "100%" }, children: [
3881
- /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3882
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
3883
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
3884
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
3885
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
3886
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
3887
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
3888
- ] }) }),
3889
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
3890
- records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
3891
- records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(
3892
- designSystem.Tr,
3893
- {
3894
- style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" },
3895
- children: [
3896
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3897
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3898
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
3899
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
3900
- icons.Information,
3901
- {
3902
- "aria-hidden": true,
3903
- style: { cursor: "help" },
3904
- width: "1.4rem",
3905
- height: "1.4rem",
3906
- fill: "primary600"
3907
- }
3908
- ) })
3909
- ] }) }),
3910
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
3911
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3912
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3913
- ]
3914
- },
3915
- record.id
3916
- ))
3917
- ] })
3918
- ] }),
3919
- loading && records.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3920
- designSystem.Flex,
3921
- {
3922
- justifyContent: "center",
3923
- alignItems: "center",
3924
- style: { position: "absolute", inset: 0, pointerEvents: "none" },
3925
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
3926
- }
3927
- )
3928
- ] }),
3936
+ /* @__PURE__ */ jsxRuntime.jsx(LogTable, { records, loading, hasActiveFilters }),
3929
3937
  /* @__PURE__ */ jsxRuntime.jsx(
3930
3938
  TablePagination,
3931
3939
  {
@@ -4059,19 +4067,15 @@ function downloadJson(basename, data) {
4059
4067
  a.click();
4060
4068
  URL.revokeObjectURL(url);
4061
4069
  }
4070
+ const defaultSnapshot = {
4071
+ oidcRoles: [],
4072
+ users: [],
4073
+ useWhitelist: false,
4074
+ enforceOIDC: false
4075
+ };
4062
4076
  const initialState = {
4063
- current: {
4064
- oidcRoles: [],
4065
- users: [],
4066
- useWhitelist: false,
4067
- enforceOIDC: false
4068
- },
4069
- initial: {
4070
- oidcRoles: [],
4071
- users: [],
4072
- useWhitelist: false,
4073
- enforceOIDC: false
4074
- },
4077
+ current: { ...defaultSnapshot },
4078
+ initial: { ...defaultSnapshot },
4075
4079
  roles: [],
4076
4080
  enforceOIDCConfig: null,
4077
4081
  auditLogEnabled: true,