@simmalugnt-se/payload-content-health 0.2.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ - The work list at `/admin/content-health` requires Admin access. Payload leaves custom Admin views
6
+ public, so a visitor who was not signed in saw the list, filled with what anonymous access could
7
+ read. Now they are sent to the login and back, and a signed-in user without Admin access to
8
+ Payload's unauthorized page.
9
+
3
10
  ## 0.2.0
4
11
 
5
12
  - Work list at `/admin/content-health`, linked from the nav: every finding, filterable by check,
package/README.md CHANGED
@@ -115,8 +115,9 @@ or block the unsaved form no longer has opens the nearest enclosing field instea
115
115
 
116
116
  With `@simmalugnt-se/payload-editor-assistant` (0.5.0 or later), rows in the collections the
117
117
  assistant works in also get "Fix with the assistant": the same link with a ready prompt that the
118
- assistant puts in its message box. The editor sends it. The packages do not import each other; they
119
- share these names:
118
+ assistant puts in its message box. The editor sends it. To write a missing alt text, the assistant
119
+ needs to see the image: allow `media.view` on the media collection (0.6.0 or later). The packages
120
+ do not import each other; they share these names:
120
121
 
121
122
  | Name | Direction | Meaning |
122
123
  | -------------------------------------------- | ----------------------- | ---------------------------------------- |
package/dist/links.d.ts CHANGED
@@ -21,6 +21,21 @@ export declare const SELECT_EVENT = "content-health:select";
21
21
  * `config.admin.custom.editorAssistant.collections`. The work list offers the assistant only there.
22
22
  */
23
23
  export declare const ASSISTANT_CUSTOM_KEY = "editorAssistant";
24
+ /**
25
+ * Where to send a visitor who may not use Admin, or null to show the work list. Payload leaves
26
+ * custom views public, so the view checks this itself, as Payload does for its own: to the login
27
+ * with a way back when signed out, to Payload's unauthorized page when signed in without access.
28
+ */
29
+ export declare function accessRedirect(args: {
30
+ routes: {
31
+ admin: string;
32
+ login: `/${string}`;
33
+ unauthorized: `/${string}`;
34
+ };
35
+ canAccessAdmin: boolean;
36
+ signedIn: boolean;
37
+ searchParams?: Record<string, string | string[] | undefined>;
38
+ }): string | null;
24
39
  export declare function assistantCollections(adminCustom: unknown): string[];
25
40
  /** The document in the finding's locale with its field brought into view, and a prompt when given. */
26
41
  export declare function editURL(adminRoute: string, finding: Finding, ask?: string): string;
package/dist/links.js CHANGED
@@ -27,6 +27,31 @@ export const SELECT_EVENT = "content-health:select";
27
27
  * `config.admin.custom.editorAssistant.collections`. The work list offers the assistant only there.
28
28
  */
29
29
  export const ASSISTANT_CUSTOM_KEY = "editorAssistant";
30
+ /**
31
+ * Where to send a visitor who may not use Admin, or null to show the work list. Payload leaves
32
+ * custom views public, so the view checks this itself, as Payload does for its own: to the login
33
+ * with a way back when signed out, to Payload's unauthorized page when signed in without access.
34
+ */
35
+ export function accessRedirect(args) {
36
+ const { routes } = args;
37
+ if (args.canAccessAdmin) {
38
+ return null;
39
+ }
40
+ if (args.signedIn) {
41
+ return formatAdminURL({ adminRoute: routes.admin, path: routes.unauthorized });
42
+ }
43
+ const query = new URLSearchParams();
44
+ for (const [key, value] of Object.entries(args.searchParams ?? {})) {
45
+ if (key === "redirect")
46
+ continue;
47
+ for (const item of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
48
+ query.append(key, item);
49
+ }
50
+ }
51
+ const back = `${VIEW_PATH}${query.size ? `?${query}` : ""}`;
52
+ const login = formatAdminURL({ adminRoute: routes.admin, path: routes.login });
53
+ return `${login}?${new URLSearchParams({ redirect: back })}`;
54
+ }
30
55
  export function assistantCollections(adminCustom) {
31
56
  const entry = adminCustom?.[ASSISTANT_CUSTOM_KEY];
32
57
  const collections = entry?.collections;
@@ -4,5 +4,6 @@ import "../styles/view.css";
4
4
  * The work list: every finding with filters on check, collection and locale. Each row opens the
5
5
  * document in the finding's locale with the field selected; where the editor assistant works, a
6
6
  * second link opens it with a ready prompt too. Scans once per visit; the filters work in the browser.
7
+ * Only for visitors who may use Admin: Payload leaves custom views public.
7
8
  */
8
9
  export declare function ContentHealthView({ initPageResult, params, searchParams, }: AdminViewServerProps): Promise<import("react").JSX.Element>;
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { DefaultTemplate } from "@payloadcms/next/templates";
3
3
  import { Gutter, SetStepNav } from "@payloadcms/ui";
4
+ import { redirect } from "next/navigation";
4
5
  import { ASK, CHECK_LABELS, findingDetail, LIST_COPY, uiLanguage } from "../copy.js";
5
- import { assistantCollections, editURL, readFilters } from "../links.js";
6
+ import { accessRedirect, assistantCollections, editURL, readFilters } from "../links.js";
6
7
  import { scanContentHealth } from "../scan.js";
7
8
  import { CHECK_IDS } from "../types.js";
8
9
  import { WorkList } from "./WorkList.js";
@@ -11,9 +12,19 @@ import "../styles/view.css";
11
12
  * The work list: every finding with filters on check, collection and locale. Each row opens the
12
13
  * document in the finding's locale with the field selected; where the editor assistant works, a
13
14
  * second link opens it with a ready prompt too. Scans once per visit; the filters work in the browser.
15
+ * Only for visitors who may use Admin: Payload leaves custom views public.
14
16
  */
15
17
  export async function ContentHealthView({ initPageResult, params, searchParams, }) {
16
18
  const { req, locale, permissions, visibleEntities } = initPageResult;
19
+ const denied = accessRedirect({
20
+ routes: { admin: req.payload.config.routes.admin, ...req.payload.config.admin.routes },
21
+ canAccessAdmin: Boolean(permissions?.canAccessAdmin),
22
+ signedIn: Boolean(req.user),
23
+ searchParams,
24
+ });
25
+ if (denied) {
26
+ redirect(denied);
27
+ }
17
28
  const language = uiLanguage(req.i18n?.language);
18
29
  const copy = LIST_COPY[language];
19
30
  const { config } = req.payload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simmalugnt-se/payload-content-health",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Content health checks and dashboard widgets for Payload Admin: alt texts, SEO fields and more",
5
5
  "keywords": [
6
6
  "payload",