@strapi/admin 5.52.2 → 5.53.0

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.
Files changed (132) hide show
  1. package/dist/admin/admin/src/components/Filters.js +9 -9
  2. package/dist/admin/admin/src/components/Filters.js.map +1 -1
  3. package/dist/admin/admin/src/components/Filters.mjs +10 -10
  4. package/dist/admin/admin/src/components/Filters.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/Pagination.js +2 -2
  6. package/dist/admin/admin/src/components/Pagination.js.map +1 -1
  7. package/dist/admin/admin/src/components/Pagination.mjs +3 -3
  8. package/dist/admin/admin/src/components/Pagination.mjs.map +1 -1
  9. package/dist/admin/admin/src/components/SearchInput.js +7 -8
  10. package/dist/admin/admin/src/components/SearchInput.js.map +1 -1
  11. package/dist/admin/admin/src/components/SearchInput.mjs +8 -9
  12. package/dist/admin/admin/src/components/SearchInput.mjs.map +1 -1
  13. package/dist/admin/admin/src/components/Table.js +2 -2
  14. package/dist/admin/admin/src/components/Table.js.map +1 -1
  15. package/dist/admin/admin/src/components/Table.mjs +3 -3
  16. package/dist/admin/admin/src/components/Table.mjs.map +1 -1
  17. package/dist/admin/admin/src/hooks/useQueryParams.js +35 -1
  18. package/dist/admin/admin/src/hooks/useQueryParams.js.map +1 -1
  19. package/dist/admin/admin/src/hooks/useQueryParams.mjs +34 -2
  20. package/dist/admin/admin/src/hooks/useQueryParams.mjs.map +1 -1
  21. package/dist/admin/admin/src/translations/en.json.js +18 -0
  22. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  23. package/dist/admin/admin/src/translations/en.json.mjs +18 -0
  24. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  25. package/dist/admin/admin/src/utils/getFetchClient.js +118 -43
  26. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  27. package/dist/admin/admin/src/utils/getFetchClient.mjs +118 -45
  28. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  29. package/dist/admin/ee/admin/src/constants.js +6 -0
  30. package/dist/admin/ee/admin/src/constants.js.map +1 -1
  31. package/dist/admin/ee/admin/src/constants.mjs +6 -0
  32. package/dist/admin/ee/admin/src/constants.mjs.map +1 -1
  33. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js +40 -12
  34. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.js.map +1 -1
  35. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs +43 -15
  36. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/ListPage.mjs.map +1 -1
  37. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js +177 -0
  38. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.js.map +1 -0
  39. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs +175 -0
  40. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.mjs.map +1 -0
  41. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js +173 -0
  42. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.js.map +1 -0
  43. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs +151 -0
  44. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.mjs.map +1 -0
  45. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +10 -1
  46. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
  47. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +10 -1
  48. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
  49. package/dist/admin/index.js +4 -0
  50. package/dist/admin/index.js.map +1 -1
  51. package/dist/admin/index.mjs +2 -2
  52. package/dist/admin/shared/utils/audit-log-export.js +16 -0
  53. package/dist/admin/shared/utils/audit-log-export.js.map +1 -0
  54. package/dist/admin/shared/utils/audit-log-export.mjs +9 -0
  55. package/dist/admin/shared/utils/audit-log-export.mjs.map +1 -0
  56. package/dist/admin/src/components/Filters.d.ts +1 -0
  57. package/dist/admin/src/hooks/useQueryParams.d.ts +13 -1
  58. package/dist/admin/src/index.d.ts +1 -1
  59. package/dist/admin/src/utils/getFetchClient.d.ts +9 -2
  60. package/dist/admin/tests/store.d.ts +4 -0
  61. package/dist/admin/tests/utils.d.ts +4 -0
  62. package/dist/ee/admin/src/constants.d.ts +4 -0
  63. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/components/ExportCard.d.ts +10 -0
  64. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/hooks/useExportAuditLogs.d.ts +18 -0
  65. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +9 -0
  66. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts +1 -0
  67. package/dist/ee/server/src/audit-logs/controllers/audit-logs.d.ts.map +1 -1
  68. package/dist/ee/server/src/audit-logs/routes/audit-logs.d.ts.map +1 -1
  69. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts +24 -3
  70. package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
  71. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts +8 -2
  72. package/dist/ee/server/src/audit-logs/services/lifecycles.d.ts.map +1 -1
  73. package/dist/ee/server/src/audit-logs/utils/csv.d.ts +5 -0
  74. package/dist/ee/server/src/audit-logs/utils/csv.d.ts.map +1 -0
  75. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts +5 -0
  76. package/dist/ee/server/src/audit-logs/utils/export-token.d.ts.map +1 -0
  77. package/dist/ee/server/src/audit-logs/utils.d.ts +10 -0
  78. package/dist/ee/server/src/audit-logs/utils.d.ts.map +1 -0
  79. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts +2 -0
  80. package/dist/ee/server/src/audit-logs/validation/audit-logs.d.ts.map +1 -1
  81. package/dist/ee/server/src/config/admin-actions.d.ts.map +1 -1
  82. package/dist/ee/server/src/index.d.ts +1 -0
  83. package/dist/ee/server/src/index.d.ts.map +1 -1
  84. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js +50 -0
  85. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.js.map +1 -1
  86. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs +51 -1
  87. package/dist/server/ee/server/src/audit-logs/controllers/audit-logs.mjs.map +1 -1
  88. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js +13 -4
  89. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.js.map +1 -1
  90. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs +13 -4
  91. package/dist/server/ee/server/src/audit-logs/routes/audit-logs.mjs.map +1 -1
  92. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +201 -7
  93. package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
  94. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +201 -8
  95. package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
  96. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js +119 -34
  97. package/dist/server/ee/server/src/audit-logs/services/lifecycles.js.map +1 -1
  98. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs +119 -34
  99. package/dist/server/ee/server/src/audit-logs/services/lifecycles.mjs.map +1 -1
  100. package/dist/server/ee/server/src/audit-logs/utils/csv.js +33 -0
  101. package/dist/server/ee/server/src/audit-logs/utils/csv.js.map +1 -0
  102. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs +29 -0
  103. package/dist/server/ee/server/src/audit-logs/utils/csv.mjs.map +1 -0
  104. package/dist/server/ee/server/src/audit-logs/utils/export-token.js +33 -0
  105. package/dist/server/ee/server/src/audit-logs/utils/export-token.js.map +1 -0
  106. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs +29 -0
  107. package/dist/server/ee/server/src/audit-logs/utils/export-token.mjs.map +1 -0
  108. package/dist/server/ee/server/src/audit-logs/utils.js +14 -0
  109. package/dist/server/ee/server/src/audit-logs/utils.js.map +1 -0
  110. package/dist/server/ee/server/src/audit-logs/utils.mjs +12 -0
  111. package/dist/server/ee/server/src/audit-logs/utils.mjs.map +1 -0
  112. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js +11 -0
  113. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.js.map +1 -1
  114. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs +11 -1
  115. package/dist/server/ee/server/src/audit-logs/validation/audit-logs.mjs.map +1 -1
  116. package/dist/server/ee/server/src/config/admin-actions.js +8 -0
  117. package/dist/server/ee/server/src/config/admin-actions.js.map +1 -1
  118. package/dist/server/ee/server/src/config/admin-actions.mjs +8 -0
  119. package/dist/server/ee/server/src/config/admin-actions.mjs.map +1 -1
  120. package/dist/server/ee/server/src/index.js +10 -0
  121. package/dist/server/ee/server/src/index.js.map +1 -1
  122. package/dist/server/ee/server/src/index.mjs +10 -0
  123. package/dist/server/ee/server/src/index.mjs.map +1 -1
  124. package/dist/server/shared/utils/audit-log-export.js +22 -0
  125. package/dist/server/shared/utils/audit-log-export.js.map +1 -0
  126. package/dist/server/shared/utils/audit-log-export.mjs +12 -0
  127. package/dist/server/shared/utils/audit-log-export.mjs.map +1 -0
  128. package/dist/shared/contracts/audit-logs.d.ts +15 -1
  129. package/dist/shared/contracts/audit-logs.d.ts.map +1 -1
  130. package/dist/shared/utils/audit-log-export.d.ts +9 -0
  131. package/dist/shared/utils/audit-log-export.d.ts.map +1 -0
  132. package/package.json +7 -7
@@ -43,7 +43,12 @@ const useQueryParams = (initialParams)=>{
43
43
  ...nextParams
44
44
  };
45
45
  }
46
- navigate({
46
+ /**
47
+ * TODO V6: Encoding should be enabled in this step, via `{ encodeValuesOnly: true }`
48
+ * So the rest of the app doesn't have to worry about it,
49
+ * It's considered a breaking change because callers currently encode values themselves,
50
+ * including the user's custom code, and would end up double-encoding them.
51
+ */ navigate({
47
52
  search: qs.stringify(nextQuery, {
48
53
  encode: false
49
54
  })
@@ -62,6 +67,35 @@ const useQueryParams = (initialParams)=>{
62
67
  setQuery
63
68
  ];
64
69
  };
70
+ /**
71
+ * These functions are used to encode query values when writing them to the URL.
72
+ * As stated above, it should be handled by `useQueryParams` instead, but that would be a breaking change.
73
+ */ const deepEncodeQueryValues = (value)=>{
74
+ if (typeof value === 'string') {
75
+ return encodeURIComponent(value);
76
+ }
77
+ if (Array.isArray(value)) {
78
+ return value.map(deepEncodeQueryValues);
79
+ }
80
+ if (value !== null && typeof value === 'object') {
81
+ return Object.fromEntries(Object.entries(value).map(([key, entry])=>[
82
+ key,
83
+ deepEncodeQueryValues(entry)
84
+ ]));
85
+ }
86
+ return value;
87
+ };
88
+ const withEncodedUserParams = (query, nextParams)=>({
89
+ ...query.filters === undefined ? {} : {
90
+ filters: deepEncodeQueryValues(query.filters)
91
+ },
92
+ ...query._q === undefined ? {} : {
93
+ _q: deepEncodeQueryValues(query._q)
94
+ },
95
+ ...nextParams
96
+ });
65
97
 
98
+ exports.deepEncodeQueryValues = deepEncodeQueryValues;
66
99
  exports.useQueryParams = useQueryParams;
100
+ exports.withEncodedUserParams = withEncodedUserParams;
67
101
  //# sourceMappingURL=useQueryParams.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryParams.js","sources":["../../../../../admin/src/hooks/useQueryParams.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\n\nimport { parse, stringify } from 'qs';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\ntype QueryParams = Record<string, unknown>;\n\nconst useSearch = () => {\n const { search } = useLocation();\n\n return useMemo(() => search, [search]);\n};\n\nconst useQueryParams = <TQuery extends object = QueryParams>(initialParams?: TQuery) => {\n const search = useSearch();\n const navigate = useNavigate();\n\n const query = useMemo(() => {\n // TODO: investigate why sometimes we're getting the search with a leading `?` and sometimes not.\n const searchQuery = search.startsWith('?') ? search.slice(1) : search;\n if (!search && initialParams) {\n return initialParams;\n }\n\n return { ...initialParams, ...parse(searchQuery) } as TQuery;\n }, [search, initialParams]);\n\n const setQuery = useCallback(\n (nextParams: TQuery, method: 'push' | 'remove' = 'push', replace = false) => {\n let nextQuery: Partial<TQuery> = { ...query };\n\n if (method === 'remove') {\n Object.keys(nextParams).forEach((key) => {\n if (Object.prototype.hasOwnProperty.call(nextQuery, key)) {\n delete nextQuery[key as keyof TQuery];\n }\n });\n } else {\n nextQuery = { ...query, ...nextParams };\n }\n\n navigate({ search: stringify(nextQuery, { encode: false }) }, { replace });\n },\n [navigate, query]\n );\n\n return [{ query, rawQuery: search }, setQuery] as const;\n};\n\nexport { useQueryParams };\n"],"names":["useSearch","search","useLocation","useMemo","useQueryParams","initialParams","navigate","useNavigate","query","searchQuery","startsWith","slice","parse","setQuery","useCallback","nextParams","method","replace","nextQuery","Object","keys","forEach","key","prototype","hasOwnProperty","call","stringify","encode","rawQuery"],"mappings":";;;;;;AAOA,MAAMA,SAAAA,GAAY,IAAA;IAChB,MAAM,EAAEC,MAAM,EAAE,GAAGC,0BAAAA,EAAAA;IAEnB,OAAOC,aAAAA,CAAQ,IAAMF,MAAAA,EAAQ;AAACA,QAAAA;AAAO,KAAA,CAAA;AACvC,CAAA;AAEA,MAAMG,iBAAiB,CAAsCC,aAAAA,GAAAA;AAC3D,IAAA,MAAMJ,MAAAA,GAASD,SAAAA,EAAAA;AACf,IAAA,MAAMM,QAAAA,GAAWC,0BAAAA,EAAAA;AAEjB,IAAA,MAAMC,QAAQL,aAAAA,CAAQ,IAAA;;QAEpB,MAAMM,WAAAA,GAAcR,OAAOS,UAAU,CAAC,OAAOT,MAAAA,CAAOU,KAAK,CAAC,CAAA,CAAA,GAAKV,MAAAA;QAC/D,IAAI,CAACA,UAAUI,aAAAA,EAAe;YAC5B,OAAOA,aAAAA;AACT,QAAA;QAEA,OAAO;AAAE,YAAA,GAAGA,aAAa;AAAE,YAAA,GAAGO,SAAMH,WAAAA;AAAa,SAAA;IACnD,CAAA,EAAG;AAACR,QAAAA,MAAAA;AAAQI,QAAAA;AAAc,KAAA,CAAA;IAE1B,MAAMQ,QAAAA,GAAWC,kBACf,CAACC,UAAAA,EAAoBC,SAA4B,MAAM,EAAEC,UAAU,KAAK,GAAA;AACtE,QAAA,IAAIC,SAAAA,GAA6B;AAAE,YAAA,GAAGV;AAAM,SAAA;AAE5C,QAAA,IAAIQ,WAAW,QAAA,EAAU;AACvBG,YAAAA,MAAAA,CAAOC,IAAI,CAACL,UAAAA,CAAAA,CAAYM,OAAO,CAAC,CAACC,GAAAA,GAAAA;gBAC/B,IAAIH,MAAAA,CAAOI,SAAS,CAACC,cAAc,CAACC,IAAI,CAACP,WAAWI,GAAAA,CAAAA,EAAM;oBACxD,OAAOJ,SAAS,CAACI,GAAAA,CAAoB;AACvC,gBAAA;AACF,YAAA,CAAA,CAAA;QACF,CAAA,MAAO;YACLJ,SAAAA,GAAY;AAAE,gBAAA,GAAGV,KAAK;AAAE,gBAAA,GAAGO;AAAW,aAAA;AACxC,QAAA;QAEAT,QAAAA,CAAS;AAAEL,YAAAA,MAAAA,EAAQyB,aAAUR,SAAAA,EAAW;gBAAES,MAAAA,EAAQ;AAAM,aAAA;SAAG,EAAG;AAAEV,YAAAA;AAAQ,SAAA,CAAA;IAC1E,CAAA,EACA;AAACX,QAAAA,QAAAA;AAAUE,QAAAA;AAAM,KAAA,CAAA;IAGnB,OAAO;AAAC,QAAA;AAAEA,YAAAA,KAAAA;YAAOoB,QAAAA,EAAU3B;AAAO,SAAA;AAAGY,QAAAA;AAAS,KAAA;AAChD;;;;"}
1
+ {"version":3,"file":"useQueryParams.js","sources":["../../../../../admin/src/hooks/useQueryParams.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\n\nimport { parse, stringify } from 'qs';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\ntype QueryParams = Record<string, unknown>;\n\nconst useSearch = () => {\n const { search } = useLocation();\n\n return useMemo(() => search, [search]);\n};\n\nconst useQueryParams = <TQuery extends object = QueryParams>(initialParams?: TQuery) => {\n const search = useSearch();\n const navigate = useNavigate();\n\n const query = useMemo(() => {\n // TODO: investigate why sometimes we're getting the search with a leading `?` and sometimes not.\n const searchQuery = search.startsWith('?') ? search.slice(1) : search;\n if (!search && initialParams) {\n return initialParams;\n }\n\n return { ...initialParams, ...parse(searchQuery) } as TQuery;\n }, [search, initialParams]);\n\n const setQuery = useCallback(\n (nextParams: TQuery, method: 'push' | 'remove' = 'push', replace = false) => {\n let nextQuery: Partial<TQuery> = { ...query };\n\n if (method === 'remove') {\n Object.keys(nextParams).forEach((key) => {\n if (Object.prototype.hasOwnProperty.call(nextQuery, key)) {\n delete nextQuery[key as keyof TQuery];\n }\n });\n } else {\n nextQuery = { ...query, ...nextParams };\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step, via `{ encodeValuesOnly: true }`\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because callers currently encode values themselves,\n * including the user's custom code, and would end up double-encoding them.\n */\n navigate({ search: stringify(nextQuery, { encode: false }) }, { replace });\n },\n [navigate, query]\n );\n\n return [{ query, rawQuery: search }, setQuery] as const;\n};\n\n/**\n * These functions are used to encode query values when writing them to the URL.\n * As stated above, it should be handled by `useQueryParams` instead, but that would be a breaking change.\n */\n\nconst deepEncodeQueryValues = <T>(value: T): T => {\n if (typeof value === 'string') {\n return encodeURIComponent(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map(deepEncodeQueryValues) as T;\n }\n\n if (value !== null && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [key, deepEncodeQueryValues(entry)])\n ) as T;\n }\n\n return value;\n};\n\nconst withEncodedUserParams = <\n T extends object,\n TQuery extends { filters?: unknown; _q?: unknown },\n>(\n query: TQuery,\n nextParams: T\n): T & { filters?: TQuery['filters']; _q?: TQuery['_q'] } => ({\n ...(query.filters === undefined ? {} : { filters: deepEncodeQueryValues(query.filters) }),\n ...(query._q === undefined ? {} : { _q: deepEncodeQueryValues(query._q) }),\n ...nextParams,\n});\n\nexport { useQueryParams, deepEncodeQueryValues, withEncodedUserParams };\n"],"names":["useSearch","search","useLocation","useMemo","useQueryParams","initialParams","navigate","useNavigate","query","searchQuery","startsWith","slice","parse","setQuery","useCallback","nextParams","method","replace","nextQuery","Object","keys","forEach","key","prototype","hasOwnProperty","call","stringify","encode","rawQuery","deepEncodeQueryValues","value","encodeURIComponent","Array","isArray","map","fromEntries","entries","entry","withEncodedUserParams","filters","undefined","_q"],"mappings":";;;;;;AAOA,MAAMA,SAAAA,GAAY,IAAA;IAChB,MAAM,EAAEC,MAAM,EAAE,GAAGC,0BAAAA,EAAAA;IAEnB,OAAOC,aAAAA,CAAQ,IAAMF,MAAAA,EAAQ;AAACA,QAAAA;AAAO,KAAA,CAAA;AACvC,CAAA;AAEA,MAAMG,iBAAiB,CAAsCC,aAAAA,GAAAA;AAC3D,IAAA,MAAMJ,MAAAA,GAASD,SAAAA,EAAAA;AACf,IAAA,MAAMM,QAAAA,GAAWC,0BAAAA,EAAAA;AAEjB,IAAA,MAAMC,QAAQL,aAAAA,CAAQ,IAAA;;QAEpB,MAAMM,WAAAA,GAAcR,OAAOS,UAAU,CAAC,OAAOT,MAAAA,CAAOU,KAAK,CAAC,CAAA,CAAA,GAAKV,MAAAA;QAC/D,IAAI,CAACA,UAAUI,aAAAA,EAAe;YAC5B,OAAOA,aAAAA;AACT,QAAA;QAEA,OAAO;AAAE,YAAA,GAAGA,aAAa;AAAE,YAAA,GAAGO,SAAMH,WAAAA;AAAa,SAAA;IACnD,CAAA,EAAG;AAACR,QAAAA,MAAAA;AAAQI,QAAAA;AAAc,KAAA,CAAA;IAE1B,MAAMQ,QAAAA,GAAWC,kBACf,CAACC,UAAAA,EAAoBC,SAA4B,MAAM,EAAEC,UAAU,KAAK,GAAA;AACtE,QAAA,IAAIC,SAAAA,GAA6B;AAAE,YAAA,GAAGV;AAAM,SAAA;AAE5C,QAAA,IAAIQ,WAAW,QAAA,EAAU;AACvBG,YAAAA,MAAAA,CAAOC,IAAI,CAACL,UAAAA,CAAAA,CAAYM,OAAO,CAAC,CAACC,GAAAA,GAAAA;gBAC/B,IAAIH,MAAAA,CAAOI,SAAS,CAACC,cAAc,CAACC,IAAI,CAACP,WAAWI,GAAAA,CAAAA,EAAM;oBACxD,OAAOJ,SAAS,CAACI,GAAAA,CAAoB;AACvC,gBAAA;AACF,YAAA,CAAA,CAAA;QACF,CAAA,MAAO;YACLJ,SAAAA,GAAY;AAAE,gBAAA,GAAGV,KAAK;AAAE,gBAAA,GAAGO;AAAW,aAAA;AACxC,QAAA;AAEA;;;;;AAKC,UACDT,QAAAA,CAAS;AAAEL,YAAAA,MAAAA,EAAQyB,aAAUR,SAAAA,EAAW;gBAAES,MAAAA,EAAQ;AAAM,aAAA;SAAG,EAAG;AAAEV,YAAAA;AAAQ,SAAA,CAAA;IAC1E,CAAA,EACA;AAACX,QAAAA,QAAAA;AAAUE,QAAAA;AAAM,KAAA,CAAA;IAGnB,OAAO;AAAC,QAAA;AAAEA,YAAAA,KAAAA;YAAOoB,QAAAA,EAAU3B;AAAO,SAAA;AAAGY,QAAAA;AAAS,KAAA;AAChD;AAEA;;;IAKA,MAAMgB,wBAAwB,CAAIC,KAAAA,GAAAA;IAChC,IAAI,OAAOA,UAAU,QAAA,EAAU;AAC7B,QAAA,OAAOC,kBAAAA,CAAmBD,KAAAA,CAAAA;AAC5B,IAAA;IAEA,IAAIE,KAAAA,CAAMC,OAAO,CAACH,KAAAA,CAAAA,EAAQ;QACxB,OAAOA,KAAAA,CAAMI,GAAG,CAACL,qBAAAA,CAAAA;AACnB,IAAA;AAEA,IAAA,IAAIC,KAAAA,KAAU,IAAA,IAAQ,OAAOA,KAAAA,KAAU,QAAA,EAAU;AAC/C,QAAA,OAAOX,MAAAA,CAAOgB,WAAW,CACvBhB,MAAAA,CAAOiB,OAAO,CAACN,KAAAA,CAAAA,CAAOI,GAAG,CAAC,CAAC,CAACZ,GAAAA,EAAKe,MAAM,GAAK;AAACf,gBAAAA,GAAAA;gBAAKO,qBAAAA,CAAsBQ,KAAAA;AAAO,aAAA,CAAA,CAAA;AAEnF,IAAA;IAEA,OAAOP,KAAAA;AACT;AAEA,MAAMQ,qBAAAA,GAAwB,CAI5B9B,KAAAA,EACAO,UAAAA,IAC4D;AAC5D,QAAA,GAAIP,KAAAA,CAAM+B,OAAO,KAAKC,SAAAA,GAAY,EAAC,GAAI;YAAED,OAAAA,EAASV,qBAAAA,CAAsBrB,MAAM+B,OAAO;SAAG;AACxF,QAAA,GAAI/B,KAAAA,CAAMiC,EAAE,KAAKD,SAAAA,GAAY,EAAC,GAAI;YAAEC,EAAAA,EAAIZ,qBAAAA,CAAsBrB,MAAMiC,EAAE;SAAG;AACzE,QAAA,GAAG1B;KACL;;;;;;"}
@@ -41,7 +41,12 @@ const useQueryParams = (initialParams)=>{
41
41
  ...nextParams
42
42
  };
43
43
  }
44
- navigate({
44
+ /**
45
+ * TODO V6: Encoding should be enabled in this step, via `{ encodeValuesOnly: true }`
46
+ * So the rest of the app doesn't have to worry about it,
47
+ * It's considered a breaking change because callers currently encode values themselves,
48
+ * including the user's custom code, and would end up double-encoding them.
49
+ */ navigate({
45
50
  search: stringify(nextQuery, {
46
51
  encode: false
47
52
  })
@@ -60,6 +65,33 @@ const useQueryParams = (initialParams)=>{
60
65
  setQuery
61
66
  ];
62
67
  };
68
+ /**
69
+ * These functions are used to encode query values when writing them to the URL.
70
+ * As stated above, it should be handled by `useQueryParams` instead, but that would be a breaking change.
71
+ */ const deepEncodeQueryValues = (value)=>{
72
+ if (typeof value === 'string') {
73
+ return encodeURIComponent(value);
74
+ }
75
+ if (Array.isArray(value)) {
76
+ return value.map(deepEncodeQueryValues);
77
+ }
78
+ if (value !== null && typeof value === 'object') {
79
+ return Object.fromEntries(Object.entries(value).map(([key, entry])=>[
80
+ key,
81
+ deepEncodeQueryValues(entry)
82
+ ]));
83
+ }
84
+ return value;
85
+ };
86
+ const withEncodedUserParams = (query, nextParams)=>({
87
+ ...query.filters === undefined ? {} : {
88
+ filters: deepEncodeQueryValues(query.filters)
89
+ },
90
+ ...query._q === undefined ? {} : {
91
+ _q: deepEncodeQueryValues(query._q)
92
+ },
93
+ ...nextParams
94
+ });
63
95
 
64
- export { useQueryParams };
96
+ export { deepEncodeQueryValues, useQueryParams, withEncodedUserParams };
65
97
  //# sourceMappingURL=useQueryParams.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useQueryParams.mjs","sources":["../../../../../admin/src/hooks/useQueryParams.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\n\nimport { parse, stringify } from 'qs';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\ntype QueryParams = Record<string, unknown>;\n\nconst useSearch = () => {\n const { search } = useLocation();\n\n return useMemo(() => search, [search]);\n};\n\nconst useQueryParams = <TQuery extends object = QueryParams>(initialParams?: TQuery) => {\n const search = useSearch();\n const navigate = useNavigate();\n\n const query = useMemo(() => {\n // TODO: investigate why sometimes we're getting the search with a leading `?` and sometimes not.\n const searchQuery = search.startsWith('?') ? search.slice(1) : search;\n if (!search && initialParams) {\n return initialParams;\n }\n\n return { ...initialParams, ...parse(searchQuery) } as TQuery;\n }, [search, initialParams]);\n\n const setQuery = useCallback(\n (nextParams: TQuery, method: 'push' | 'remove' = 'push', replace = false) => {\n let nextQuery: Partial<TQuery> = { ...query };\n\n if (method === 'remove') {\n Object.keys(nextParams).forEach((key) => {\n if (Object.prototype.hasOwnProperty.call(nextQuery, key)) {\n delete nextQuery[key as keyof TQuery];\n }\n });\n } else {\n nextQuery = { ...query, ...nextParams };\n }\n\n navigate({ search: stringify(nextQuery, { encode: false }) }, { replace });\n },\n [navigate, query]\n );\n\n return [{ query, rawQuery: search }, setQuery] as const;\n};\n\nexport { useQueryParams };\n"],"names":["useSearch","search","useLocation","useMemo","useQueryParams","initialParams","navigate","useNavigate","query","searchQuery","startsWith","slice","parse","setQuery","useCallback","nextParams","method","replace","nextQuery","Object","keys","forEach","key","prototype","hasOwnProperty","call","stringify","encode","rawQuery"],"mappings":";;;;AAOA,MAAMA,SAAAA,GAAY,IAAA;IAChB,MAAM,EAAEC,MAAM,EAAE,GAAGC,WAAAA,EAAAA;IAEnB,OAAOC,OAAAA,CAAQ,IAAMF,MAAAA,EAAQ;AAACA,QAAAA;AAAO,KAAA,CAAA;AACvC,CAAA;AAEA,MAAMG,iBAAiB,CAAsCC,aAAAA,GAAAA;AAC3D,IAAA,MAAMJ,MAAAA,GAASD,SAAAA,EAAAA;AACf,IAAA,MAAMM,QAAAA,GAAWC,WAAAA,EAAAA;AAEjB,IAAA,MAAMC,QAAQL,OAAAA,CAAQ,IAAA;;QAEpB,MAAMM,WAAAA,GAAcR,OAAOS,UAAU,CAAC,OAAOT,MAAAA,CAAOU,KAAK,CAAC,CAAA,CAAA,GAAKV,MAAAA;QAC/D,IAAI,CAACA,UAAUI,aAAAA,EAAe;YAC5B,OAAOA,aAAAA;AACT,QAAA;QAEA,OAAO;AAAE,YAAA,GAAGA,aAAa;AAAE,YAAA,GAAGO,MAAMH,WAAAA;AAAa,SAAA;IACnD,CAAA,EAAG;AAACR,QAAAA,MAAAA;AAAQI,QAAAA;AAAc,KAAA,CAAA;IAE1B,MAAMQ,QAAAA,GAAWC,YACf,CAACC,UAAAA,EAAoBC,SAA4B,MAAM,EAAEC,UAAU,KAAK,GAAA;AACtE,QAAA,IAAIC,SAAAA,GAA6B;AAAE,YAAA,GAAGV;AAAM,SAAA;AAE5C,QAAA,IAAIQ,WAAW,QAAA,EAAU;AACvBG,YAAAA,MAAAA,CAAOC,IAAI,CAACL,UAAAA,CAAAA,CAAYM,OAAO,CAAC,CAACC,GAAAA,GAAAA;gBAC/B,IAAIH,MAAAA,CAAOI,SAAS,CAACC,cAAc,CAACC,IAAI,CAACP,WAAWI,GAAAA,CAAAA,EAAM;oBACxD,OAAOJ,SAAS,CAACI,GAAAA,CAAoB;AACvC,gBAAA;AACF,YAAA,CAAA,CAAA;QACF,CAAA,MAAO;YACLJ,SAAAA,GAAY;AAAE,gBAAA,GAAGV,KAAK;AAAE,gBAAA,GAAGO;AAAW,aAAA;AACxC,QAAA;QAEAT,QAAAA,CAAS;AAAEL,YAAAA,MAAAA,EAAQyB,UAAUR,SAAAA,EAAW;gBAAES,MAAAA,EAAQ;AAAM,aAAA;SAAG,EAAG;AAAEV,YAAAA;AAAQ,SAAA,CAAA;IAC1E,CAAA,EACA;AAACX,QAAAA,QAAAA;AAAUE,QAAAA;AAAM,KAAA,CAAA;IAGnB,OAAO;AAAC,QAAA;AAAEA,YAAAA,KAAAA;YAAOoB,QAAAA,EAAU3B;AAAO,SAAA;AAAGY,QAAAA;AAAS,KAAA;AAChD;;;;"}
1
+ {"version":3,"file":"useQueryParams.mjs","sources":["../../../../../admin/src/hooks/useQueryParams.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\n\nimport { parse, stringify } from 'qs';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\ntype QueryParams = Record<string, unknown>;\n\nconst useSearch = () => {\n const { search } = useLocation();\n\n return useMemo(() => search, [search]);\n};\n\nconst useQueryParams = <TQuery extends object = QueryParams>(initialParams?: TQuery) => {\n const search = useSearch();\n const navigate = useNavigate();\n\n const query = useMemo(() => {\n // TODO: investigate why sometimes we're getting the search with a leading `?` and sometimes not.\n const searchQuery = search.startsWith('?') ? search.slice(1) : search;\n if (!search && initialParams) {\n return initialParams;\n }\n\n return { ...initialParams, ...parse(searchQuery) } as TQuery;\n }, [search, initialParams]);\n\n const setQuery = useCallback(\n (nextParams: TQuery, method: 'push' | 'remove' = 'push', replace = false) => {\n let nextQuery: Partial<TQuery> = { ...query };\n\n if (method === 'remove') {\n Object.keys(nextParams).forEach((key) => {\n if (Object.prototype.hasOwnProperty.call(nextQuery, key)) {\n delete nextQuery[key as keyof TQuery];\n }\n });\n } else {\n nextQuery = { ...query, ...nextParams };\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step, via `{ encodeValuesOnly: true }`\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because callers currently encode values themselves,\n * including the user's custom code, and would end up double-encoding them.\n */\n navigate({ search: stringify(nextQuery, { encode: false }) }, { replace });\n },\n [navigate, query]\n );\n\n return [{ query, rawQuery: search }, setQuery] as const;\n};\n\n/**\n * These functions are used to encode query values when writing them to the URL.\n * As stated above, it should be handled by `useQueryParams` instead, but that would be a breaking change.\n */\n\nconst deepEncodeQueryValues = <T>(value: T): T => {\n if (typeof value === 'string') {\n return encodeURIComponent(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map(deepEncodeQueryValues) as T;\n }\n\n if (value !== null && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [key, deepEncodeQueryValues(entry)])\n ) as T;\n }\n\n return value;\n};\n\nconst withEncodedUserParams = <\n T extends object,\n TQuery extends { filters?: unknown; _q?: unknown },\n>(\n query: TQuery,\n nextParams: T\n): T & { filters?: TQuery['filters']; _q?: TQuery['_q'] } => ({\n ...(query.filters === undefined ? {} : { filters: deepEncodeQueryValues(query.filters) }),\n ...(query._q === undefined ? {} : { _q: deepEncodeQueryValues(query._q) }),\n ...nextParams,\n});\n\nexport { useQueryParams, deepEncodeQueryValues, withEncodedUserParams };\n"],"names":["useSearch","search","useLocation","useMemo","useQueryParams","initialParams","navigate","useNavigate","query","searchQuery","startsWith","slice","parse","setQuery","useCallback","nextParams","method","replace","nextQuery","Object","keys","forEach","key","prototype","hasOwnProperty","call","stringify","encode","rawQuery","deepEncodeQueryValues","value","encodeURIComponent","Array","isArray","map","fromEntries","entries","entry","withEncodedUserParams","filters","undefined","_q"],"mappings":"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"}
@@ -184,6 +184,7 @@ var en = {
184
184
  "Settings.permissions.auditLogs.action": "Action",
185
185
  "Settings.permissions.auditLogs.admin.auth.success": "Admin login",
186
186
  "Settings.permissions.auditLogs.admin.logout": "Admin logout",
187
+ "Settings.permissions.auditLogs.audit-log.export": "Export audit logs",
187
188
  "Settings.permissions.auditLogs.component.create": "Create component",
188
189
  "Settings.permissions.auditLogs.component.delete": "Delete component",
189
190
  "Settings.permissions.auditLogs.component.update": "Update component",
@@ -198,6 +199,15 @@ var en = {
198
199
  "Settings.permissions.auditLogs.entry.unpublish": "Unpublish entry{model, select, undefined {} other { ({model})}}",
199
200
  "Settings.permissions.auditLogs.entry.update": "Update entry{model, select, undefined {} other { ({model})}}",
200
201
  "Settings.permissions.auditLogs.filters.combobox.aria-label": "Search and select an option to filter",
202
+ "Settings.permissions.auditLogs.listview.export": "Export as CSV",
203
+ "Settings.permissions.auditLogs.listview.export.error": "The export failed. Please try again.",
204
+ "Settings.permissions.auditLogs.listview.export.error.forbidden": "You don't have the permissions to export audit logs.",
205
+ "Settings.permissions.auditLogs.listview.export.error.tooLarge": "Table too large to export. Please add filters and try again.",
206
+ "Settings.permissions.auditLogs.listview.export.leave": "Are you sure you want to leave? Your ongoing export will be canceled.",
207
+ "Settings.permissions.auditLogs.listview.export.progress.caption": "Preparing your export - {fetched, number} out of {total, plural, one {# event} other {# events}}",
208
+ "Settings.permissions.auditLogs.listview.export.progress.title": "Exporting audit logs",
209
+ "Settings.permissions.auditLogs.listview.export.ready.download": "Download CSV",
210
+ "Settings.permissions.auditLogs.listview.export.ready.title": "Export ready",
201
211
  "Settings.permissions.auditLogs.listview.header.subtitle": "Logs of all the activities that happened in your environment",
202
212
  "Settings.permissions.auditLogs.not-available": "Audit Logs is only available as part of a paid plan. Upgrade to get a searchable and filterable display of all activities.",
203
213
  "Settings.permissions.auditLogs.media.create": "Create media",
@@ -206,6 +216,14 @@ var en = {
206
216
  "Settings.permissions.auditLogs.payload": "Payload",
207
217
  "Settings.permissions.auditLogs.permission.create": "Create permission",
208
218
  "Settings.permissions.auditLogs.permission.delete": "Delete permission",
219
+ "Settings.permissions.auditLogs.release.create": "Create release",
220
+ "Settings.permissions.auditLogs.release.update": "Update release",
221
+ "Settings.permissions.auditLogs.release.delete": "Delete release",
222
+ "Settings.permissions.auditLogs.release.trigger": "Trigger release",
223
+ "Settings.permissions.auditLogs.release.entry.add": "Add entry to release",
224
+ "Settings.permissions.auditLogs.release.entry.update": "Change entry on release",
225
+ "Settings.permissions.auditLogs.release.entry.remove": "Remove entry from release",
226
+ "Settings.permissions.auditLogs.release.settings.update": "Update release settings",
209
227
  "Settings.permissions.auditLogs.permission.update": "Update permission",
210
228
  "Settings.permissions.auditLogs.role.create": "Create role",
211
229
  "Settings.permissions.auditLogs.role.delete": "Delete role",
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -180,6 +180,7 @@ var en = {
180
180
  "Settings.permissions.auditLogs.action": "Action",
181
181
  "Settings.permissions.auditLogs.admin.auth.success": "Admin login",
182
182
  "Settings.permissions.auditLogs.admin.logout": "Admin logout",
183
+ "Settings.permissions.auditLogs.audit-log.export": "Export audit logs",
183
184
  "Settings.permissions.auditLogs.component.create": "Create component",
184
185
  "Settings.permissions.auditLogs.component.delete": "Delete component",
185
186
  "Settings.permissions.auditLogs.component.update": "Update component",
@@ -194,6 +195,15 @@ var en = {
194
195
  "Settings.permissions.auditLogs.entry.unpublish": "Unpublish entry{model, select, undefined {} other { ({model})}}",
195
196
  "Settings.permissions.auditLogs.entry.update": "Update entry{model, select, undefined {} other { ({model})}}",
196
197
  "Settings.permissions.auditLogs.filters.combobox.aria-label": "Search and select an option to filter",
198
+ "Settings.permissions.auditLogs.listview.export": "Export as CSV",
199
+ "Settings.permissions.auditLogs.listview.export.error": "The export failed. Please try again.",
200
+ "Settings.permissions.auditLogs.listview.export.error.forbidden": "You don't have the permissions to export audit logs.",
201
+ "Settings.permissions.auditLogs.listview.export.error.tooLarge": "Table too large to export. Please add filters and try again.",
202
+ "Settings.permissions.auditLogs.listview.export.leave": "Are you sure you want to leave? Your ongoing export will be canceled.",
203
+ "Settings.permissions.auditLogs.listview.export.progress.caption": "Preparing your export - {fetched, number} out of {total, plural, one {# event} other {# events}}",
204
+ "Settings.permissions.auditLogs.listview.export.progress.title": "Exporting audit logs",
205
+ "Settings.permissions.auditLogs.listview.export.ready.download": "Download CSV",
206
+ "Settings.permissions.auditLogs.listview.export.ready.title": "Export ready",
197
207
  "Settings.permissions.auditLogs.listview.header.subtitle": "Logs of all the activities that happened in your environment",
198
208
  "Settings.permissions.auditLogs.not-available": "Audit Logs is only available as part of a paid plan. Upgrade to get a searchable and filterable display of all activities.",
199
209
  "Settings.permissions.auditLogs.media.create": "Create media",
@@ -202,6 +212,14 @@ var en = {
202
212
  "Settings.permissions.auditLogs.payload": "Payload",
203
213
  "Settings.permissions.auditLogs.permission.create": "Create permission",
204
214
  "Settings.permissions.auditLogs.permission.delete": "Delete permission",
215
+ "Settings.permissions.auditLogs.release.create": "Create release",
216
+ "Settings.permissions.auditLogs.release.update": "Update release",
217
+ "Settings.permissions.auditLogs.release.delete": "Delete release",
218
+ "Settings.permissions.auditLogs.release.trigger": "Trigger release",
219
+ "Settings.permissions.auditLogs.release.entry.add": "Add entry to release",
220
+ "Settings.permissions.auditLogs.release.entry.update": "Change entry on release",
221
+ "Settings.permissions.auditLogs.release.entry.remove": "Remove entry from release",
222
+ "Settings.permissions.auditLogs.release.settings.update": "Update release settings",
205
223
  "Settings.permissions.auditLogs.permission.update": "Update permission",
206
224
  "Settings.permissions.auditLogs.role.create": "Create role",
207
225
  "Settings.permissions.auditLogs.role.delete": "Delete role",
@@ -1 +1 @@
1
- {"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,6 +3,7 @@
3
3
  var pipe = require('lodash/fp/pipe');
4
4
  var qs = require('qs');
5
5
  var cookies = require('./cookies.js');
6
+ var jwt = require('./jwt.js');
6
7
 
7
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
9
 
@@ -80,33 +81,105 @@ const STORAGE_KEYS = {
80
81
  onTokenUpdate(token);
81
82
  }
82
83
  };
84
+ const getToken = ()=>{
85
+ const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);
86
+ if (fromLocalStorage) {
87
+ return JSON.parse(fromLocalStorage);
88
+ }
89
+ const fromCookie = cookies.getCookieValue(cookies.AUTH_COOKIE_NAME);
90
+ return fromCookie ?? null;
91
+ };
92
+ const ADMIN_REFRESH_LOCK = 'strapi-admin-access-token';
93
+ const FALLBACK_ADOPT_TIMEOUT_MS = 250;
94
+ const FALLBACK_ADOPT_POLL_MS = 25;
95
+ const postAccessToken = (backendURL)=>fetch(`${backendURL}/admin/access-token`, {
96
+ method: 'POST',
97
+ credentials: 'include',
98
+ headers: {
99
+ Accept: 'application/json',
100
+ 'Content-Type': 'application/json'
101
+ }
102
+ });
103
+ const adoptSharedAccessToken = (token)=>{
104
+ if (onTokenUpdate) {
105
+ onTokenUpdate(token);
106
+ }
107
+ return token;
108
+ };
109
+ /**
110
+ * Another tab may have refreshed already and written a newer access token to the
111
+ * shared localStorage/cookie. Per-tab Redux is not visible here — only shared
112
+ * storage counts.
113
+ */ const readValidAccessTokenFromSharedStorage = (staleToken)=>{
114
+ const stored = getToken();
115
+ if (!stored || stored === staleToken) {
116
+ return null;
117
+ }
118
+ const expiry = jwt.decodeAccessTokenExpiry(stored);
119
+ if (expiry === null || expiry <= Date.now()) {
120
+ return null;
121
+ }
122
+ return stored;
123
+ };
124
+ const waitForSharedAccessToken = async (staleToken)=>{
125
+ const timeoutAt = Date.now() + FALLBACK_ADOPT_TIMEOUT_MS;
126
+ while(Date.now() < timeoutAt){
127
+ await new Promise((resolve)=>{
128
+ setTimeout(resolve, FALLBACK_ADOPT_POLL_MS);
129
+ });
130
+ const adopted = readValidAccessTokenFromSharedStorage(staleToken);
131
+ if (adopted) {
132
+ return adoptSharedAccessToken(adopted);
133
+ }
134
+ }
135
+ return null;
136
+ };
137
+ const executeRefreshAccessToken = async (backendURL, tokenBeforeRefresh, { allowHttpRetry })=>{
138
+ const adopted = readValidAccessTokenFromSharedStorage(tokenBeforeRefresh);
139
+ if (adopted) {
140
+ return adoptSharedAccessToken(adopted);
141
+ }
142
+ let response = await postAccessToken(backendURL);
143
+ if (response.status === 401 && allowHttpRetry) {
144
+ const adoptedAfterFail = readValidAccessTokenFromSharedStorage(tokenBeforeRefresh);
145
+ if (adoptedAfterFail) {
146
+ return adoptSharedAccessToken(adoptedAfterFail);
147
+ }
148
+ const adoptedAfterWait = await waitForSharedAccessToken(tokenBeforeRefresh);
149
+ if (adoptedAfterWait) {
150
+ return adoptedAfterWait;
151
+ }
152
+ response = await postAccessToken(backendURL);
153
+ }
154
+ if (!response.ok) {
155
+ console.warn('[Auth] Token refresh failed with status:', response.status);
156
+ return null;
157
+ }
158
+ const result = await response.json();
159
+ const token = result?.data?.token;
160
+ if (!token) {
161
+ console.warn('[Auth] Token refresh response missing token');
162
+ return null;
163
+ }
164
+ storeToken(token);
165
+ return token;
166
+ };
83
167
  /**
84
168
  * Refresh the access token by calling the /admin/access-token endpoint.
85
169
  * This uses a low-level fetch to avoid recursion through the interceptor.
86
170
  * Returns the new token on success, or null on failure.
87
- */ const refreshAccessToken = async ()=>{
171
+ */ const refreshAccessToken = async (tokenUsedByFailedRequest)=>{
88
172
  const backendURL = window.strapi.backendURL;
173
+ const tokenBeforeRefresh = tokenUsedByFailedRequest ?? getToken();
89
174
  try {
90
- const response = await fetch(`${backendURL}/admin/access-token`, {
91
- method: 'POST',
92
- credentials: 'include',
93
- headers: {
94
- Accept: 'application/json',
95
- 'Content-Type': 'application/json'
96
- }
97
- });
98
- if (!response.ok) {
99
- console.warn('[Auth] Token refresh failed with status:', response.status);
100
- return null;
101
- }
102
- const result = await response.json();
103
- const token = result?.data?.token;
104
- if (!token) {
105
- console.warn('[Auth] Token refresh response missing token');
106
- return null;
175
+ if (typeof navigator !== 'undefined' && navigator.locks?.request) {
176
+ return await navigator.locks.request(ADMIN_REFRESH_LOCK, ()=>executeRefreshAccessToken(backendURL, tokenBeforeRefresh, {
177
+ allowHttpRetry: false
178
+ }));
107
179
  }
108
- storeToken(token);
109
- return token;
180
+ return await executeRefreshAccessToken(backendURL, tokenBeforeRefresh, {
181
+ allowHttpRetry: true
182
+ });
110
183
  } catch (error) {
111
184
  console.error('[Auth] Token refresh error:', error);
112
185
  return null;
@@ -119,9 +192,9 @@ const STORAGE_KEYS = {
119
192
  * @returns The new authentication token
120
193
  * @throws {Error} If the token refresh fails (e.g., refresh token expired)
121
194
  * @internal Exported for testing purposes
122
- */ const attemptTokenRefresh = async ()=>{
195
+ */ const attemptTokenRefresh = async (tokenUsedByFailedRequest)=>{
123
196
  if (!refreshPromise) {
124
- refreshPromise = refreshAccessToken().finally(()=>{
197
+ refreshPromise = refreshAccessToken(tokenUsedByFailedRequest).finally(()=>{
125
198
  refreshPromise = null;
126
199
  });
127
200
  }
@@ -150,14 +223,6 @@ class FetchError extends Error {
150
223
  const isFetchError = (error)=>{
151
224
  return error instanceof FetchError;
152
225
  };
153
- const getToken = ()=>{
154
- const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);
155
- if (fromLocalStorage) {
156
- return JSON.parse(fromLocalStorage);
157
- }
158
- const fromCookie = cookies.getCookieValue(cookies.AUTH_COOKIE_NAME);
159
- return fromCookie ?? null;
160
- };
161
226
  /**
162
227
  * @public
163
228
  * @param {FetchConfig} [defaultOptions={}] - Fetch Configs.
@@ -182,10 +247,10 @@ const getToken = ()=>{
182
247
  /**
183
248
  * Create default headers with the current token.
184
249
  * This is a function so we can get a fresh token after refresh.
185
- */ const getDefaultHeaders = ()=>({
250
+ */ const getDefaultHeaders = (token = getToken())=>({
186
251
  Accept: 'application/json',
187
252
  'Content-Type': 'application/json',
188
- Authorization: `Bearer ${getToken()}`
253
+ Authorization: `Bearer ${token}`
189
254
  });
190
255
  const isFormDataRequest = (body)=>body instanceof FormData;
191
256
  const addPrependingSlash = (url)=>url.charAt(0) !== '/' ? `/${url}` : url;
@@ -260,14 +325,14 @@ const getToken = ()=>{
260
325
  * Execute a fetch request with automatic token refresh on 401 errors.
261
326
  * @param url - The request URL (used to check if it's an auth path)
262
327
  * @param executeRequest - Function that performs the fetch (called again on retry with fresh headers)
263
- */ const withTokenRefresh = async (url, executeRequest)=>{
328
+ */ const withTokenRefresh = async (url, executeRequest, getRequestToken)=>{
264
329
  try {
265
330
  return await executeRequest();
266
331
  } catch (error) {
267
332
  // Only attempt refresh for 401 errors on non-auth paths
268
333
  if (isFetchError(error) && error.status === 401 && !isAuthPath(url)) {
269
334
  try {
270
- await attemptTokenRefresh();
335
+ await attemptTokenRefresh(getRequestToken());
271
336
  // Retry - executeRequest will call getDefaultHeaders() again, picking up the new token
272
337
  return await executeRequest();
273
338
  } catch {
@@ -308,10 +373,12 @@ const getToken = ()=>{
308
373
  get: async (url, options)=>{
309
374
  const createRequestUrl = makeCreateRequestUrl(options);
310
375
  const responseType = options?.responseType ?? 'json';
376
+ let requestToken = null;
311
377
  const executeRequest = async ()=>{
312
- const { Authorization } = getDefaultHeaders();
378
+ requestToken = getToken();
379
+ const { Authorization } = getDefaultHeaders(requestToken);
313
380
  // For non-JSON response types, omit content negotiation headers that imply JSON
314
- const defaultHeaders = responseType === 'json' ? getDefaultHeaders() : {
381
+ const defaultHeaders = responseType === 'json' ? getDefaultHeaders(requestToken) : {
315
382
  Authorization
316
383
  };
317
384
  const headers = new Headers({
@@ -326,13 +393,15 @@ const getToken = ()=>{
326
393
  });
327
394
  return responseInterceptor(response, options?.validateStatus, responseType);
328
395
  };
329
- return withTokenRefresh(url, executeRequest);
396
+ return withTokenRefresh(url, executeRequest, ()=>requestToken);
330
397
  },
331
398
  post: async (url, data, options)=>{
332
399
  const createRequestUrl = makeCreateRequestUrl(options);
400
+ let requestToken = null;
333
401
  const executeRequest = async ()=>{
402
+ requestToken = getToken();
334
403
  const headers = new Headers({
335
- ...getDefaultHeaders(),
404
+ ...getDefaultHeaders(requestToken),
336
405
  ...options?.headers
337
406
  });
338
407
  /**
@@ -350,13 +419,15 @@ const getToken = ()=>{
350
419
  });
351
420
  return responseInterceptor(response, options?.validateStatus);
352
421
  };
353
- return withTokenRefresh(url, executeRequest);
422
+ return withTokenRefresh(url, executeRequest, ()=>requestToken);
354
423
  },
355
424
  put: async (url, data, options)=>{
356
425
  const createRequestUrl = makeCreateRequestUrl(options);
426
+ let requestToken = null;
357
427
  const executeRequest = async ()=>{
428
+ requestToken = getToken();
358
429
  const headers = new Headers({
359
- ...getDefaultHeaders(),
430
+ ...getDefaultHeaders(requestToken),
360
431
  ...options?.headers
361
432
  });
362
433
  /**
@@ -374,13 +445,15 @@ const getToken = ()=>{
374
445
  });
375
446
  return responseInterceptor(response, options?.validateStatus);
376
447
  };
377
- return withTokenRefresh(url, executeRequest);
448
+ return withTokenRefresh(url, executeRequest, ()=>requestToken);
378
449
  },
379
450
  del: async (url, options)=>{
380
451
  const createRequestUrl = makeCreateRequestUrl(options);
452
+ let requestToken = null;
381
453
  const executeRequest = async ()=>{
454
+ requestToken = getToken();
382
455
  const headers = new Headers({
383
- ...getDefaultHeaders(),
456
+ ...getDefaultHeaders(requestToken),
384
457
  ...options?.headers
385
458
  });
386
459
  const response = await fetch(createRequestUrl(url), {
@@ -391,16 +464,18 @@ const getToken = ()=>{
391
464
  });
392
465
  return responseInterceptor(response, options?.validateStatus);
393
466
  };
394
- return withTokenRefresh(url, executeRequest);
467
+ return withTokenRefresh(url, executeRequest, ()=>requestToken);
395
468
  }
396
469
  };
397
470
  return fetchClient;
398
471
  };
399
472
 
473
+ exports.ADMIN_REFRESH_LOCK = ADMIN_REFRESH_LOCK;
400
474
  exports.FetchError = FetchError;
401
475
  exports.attemptTokenRefresh = attemptTokenRefresh;
402
476
  exports.getFetchClient = getFetchClient;
403
477
  exports.isFetchError = isFetchError;
478
+ exports.refreshAccessToken = refreshAccessToken;
404
479
  exports.setOnSessionExpired = setOnSessionExpired;
405
480
  exports.setOnTokenUpdate = setOnTokenUpdate;
406
481
  exports.storeToken = storeToken;