@goodfellow-cms/next 0.3.0 → 0.4.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.
- package/README.md +2 -2
- package/bin/goodfellow-next.js +49 -4
- package/dist/admin.d.ts +9 -2
- package/dist/admin.js +3 -2
- package/dist/config.d.ts +9 -0
- package/dist/config.js +18 -1
- package/dist/export.d.ts +12 -0
- package/dist/export.js +24 -1
- package/dist/server.d.ts +57 -0
- package/dist/server.js +45 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @goodfellow-cms/next
|
|
2
2
|
|
|
3
|
-
Goodfellow pages and the admin panel in a Next.js App Router site, exported as static files. Links use `next/link` and images `next/image`.
|
|
3
|
+
Goodfellow pages and the admin panel in a Next.js App Router site, exported as static files, or with a server of its own that publishes for editors who sign in with the site's own sign-in (`@goodfellow-cms/next/server`). Links use `next/link` and images `next/image`.
|
|
4
4
|
|
|
5
5
|
Start from the Next.js starter: `npm create goodfellow@latest my-site -- --template next`.
|
|
6
6
|
|
|
7
7
|
[Goodfellow](https://github.com/goodfellow-cms/goodfellow-cms) is a website builder for people who aren't developers, with no server or database: pages are files in your own GitHub or GitLab repository, edited in a visual editor built on [Puck](https://puckeditor.com). To start a new site, run `npm create goodfellow@latest my-site`. See the [documentation](https://goodfellow-cms.github.io/goodfellow-cms/) for everything else.
|
|
8
8
|
|
|
9
|
-
More in the docs: [Next.js](https://goodfellow-cms.github.io/goodfellow-cms/docs/nextjs).
|
|
9
|
+
More in the docs: [Next.js](https://goodfellow-cms.github.io/goodfellow-cms/docs/nextjs) and [Full-stack Next.js sites](https://goodfellow-cms.github.io/goodfellow-cms/docs/full-stack-nextjs).
|
|
10
10
|
|
|
11
11
|
## License
|
|
12
12
|
|
package/bin/goodfellow-next.js
CHANGED
|
@@ -2,13 +2,32 @@
|
|
|
2
2
|
// After `next build`: `goodfellow-next finish out` fixes the export's prefetch files on Windows,
|
|
3
3
|
// writes the calendar files of collections of events and the pages at redirects' old addresses, and the search index for sites with a search block. `index` only writes the index.
|
|
4
4
|
// `goodfellow-next update` updates Goodfellow, as `goodfellow update` does for other sites.
|
|
5
|
+
// `goodfellow-next content link|copy [folder]` puts a content repository's files in place, for a site whose
|
|
6
|
+
// content is kept in a repository of its own.
|
|
7
|
+
import { rm } from "node:fs/promises";
|
|
8
|
+
import { relative } from "node:path";
|
|
5
9
|
import { parseArgs } from "node:util";
|
|
6
10
|
import { formatUpdateResult } from "@goodfellow-cms/core";
|
|
7
|
-
import { update } from "@goodfellow-cms/core/node";
|
|
8
|
-
import { writeSearchIndex } from "@goodfellow-cms/core/search-index";
|
|
9
|
-
import { fixSegmentFiles, writeCalendarFiles, writeRedirectFiles } from "@goodfellow-cms/next/export";
|
|
11
|
+
import { ContentRepositoryError, setUpContentRepository, update } from "@goodfellow-cms/core/node";
|
|
12
|
+
import { writePagesSearchIndex, writeSearchIndex } from "@goodfellow-cms/core/search-index";
|
|
13
|
+
import { builtPages, fixSegmentFiles, writeCalendarFiles, writeRedirectFiles } from "@goodfellow-cms/next/export";
|
|
10
14
|
|
|
11
15
|
const [command, dir = "out"] = process.argv.slice(2);
|
|
16
|
+
// A full-stack site (withGoodfellow(config, { server: true })): Next.js serves its pages and sends redirects
|
|
17
|
+
// itself, so the calendar files and the search index are written into public/, which `next start` and hosts
|
|
18
|
+
// serve as static files, as they are for an export.
|
|
19
|
+
if (command === "finish" && dir === "--server") {
|
|
20
|
+
await rm("public/calendars", { recursive: true, force: true });
|
|
21
|
+
const calendars = await writeCalendarFiles("public");
|
|
22
|
+
if (calendars > 0) console.log(`Wrote ${calendars} calendar files, in public/calendars`);
|
|
23
|
+
const indexed = await writePagesSearchIndex(await builtPages(), "public/pagefind");
|
|
24
|
+
console.log(
|
|
25
|
+
indexed === undefined
|
|
26
|
+
? "No page has a search block, so there's nothing to index"
|
|
27
|
+
: `Indexed ${indexed} pages for search, in public/pagefind`,
|
|
28
|
+
);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
12
31
|
if (command === "update") {
|
|
13
32
|
const { values } = parseArgs({
|
|
14
33
|
args: process.argv.slice(3),
|
|
@@ -19,19 +38,45 @@ if (command === "update") {
|
|
|
19
38
|
console.log(formatUpdateResult(result));
|
|
20
39
|
process.exit(result.state === "failed" ? 1 : 0);
|
|
21
40
|
}
|
|
41
|
+
if (command === "content") {
|
|
42
|
+
const [action, from] = process.argv.slice(3);
|
|
43
|
+
if (action === "link" || action === "copy") {
|
|
44
|
+
try {
|
|
45
|
+
const folder =
|
|
46
|
+
relative(process.cwd(), await setUpContentRepository(".", { from, copy: action === "copy" })) || ".";
|
|
47
|
+
console.log(
|
|
48
|
+
action === "copy"
|
|
49
|
+
? `Copied content/ and public/media/ from ${folder}`
|
|
50
|
+
: `Linked content/ and public/media/ to ${folder}, so the site's content is the content repository's`,
|
|
51
|
+
);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (!(error instanceof ContentRepositoryError)) throw error;
|
|
55
|
+
console.error(error.message);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
22
60
|
if (command !== "finish" && command !== "index") {
|
|
23
61
|
console.error(
|
|
24
62
|
[
|
|
25
63
|
"Usage: goodfellow-next finish [folder]",
|
|
64
|
+
" goodfellow-next finish --server",
|
|
26
65
|
" goodfellow-next index [folder]",
|
|
27
66
|
" goodfellow-next update [--to fixes|automatic|latest|<release>] [--publish]",
|
|
67
|
+
" goodfellow-next content link|copy [folder]",
|
|
28
68
|
"",
|
|
29
69
|
"finish Gets the exported site in folder (default: out) ready to publish: puts the files",
|
|
30
70
|
" Next.js uses to load pages ahead where browsers look for them, writes calendar files",
|
|
31
71
|
" for collections of events and pages that send visitors on from old addresses, and",
|
|
32
|
-
" indexes it for search.",
|
|
72
|
+
" indexes it for search. With --server, for a full-stack site, writes the calendar",
|
|
73
|
+
" files into public/calendars and indexes the pages next build rendered into",
|
|
74
|
+
" public/pagefind instead.",
|
|
33
75
|
"index Only indexes the exported site for its search block.",
|
|
34
76
|
"update Updates Goodfellow, and blocks from its registry, if the site still builds.",
|
|
77
|
+
"content For a site whose content is kept in a repository of its own: links content/ and",
|
|
78
|
+
" public/media/ to the content repository in folder (default: content-repository),",
|
|
79
|
+
" or copies them from it, as builds do.",
|
|
35
80
|
].join("\n"),
|
|
36
81
|
);
|
|
37
82
|
process.exit(1);
|
package/dist/admin.d.ts
CHANGED
|
@@ -10,12 +10,19 @@ export interface GoodfellowAdminProps {
|
|
|
10
10
|
siteUrl?: string;
|
|
11
11
|
/** Replaces any of the admin panel's text, for rewording or translation. */
|
|
12
12
|
strings?: Partial<Strings>;
|
|
13
|
+
/**
|
|
14
|
+
* On a full-stack site, the address of its `goodfellowApi()` route, such as
|
|
15
|
+
* `/api/goodfellow`, without the base path. Editors then sign in with the
|
|
16
|
+
* site's own sign-in, and its server publishes for them. `next dev` still
|
|
17
|
+
* saves to the files on disk.
|
|
18
|
+
*/
|
|
19
|
+
server?: string;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* The admin panel, for a Next.js site's `app/admin/page.tsx`, which must be a
|
|
16
23
|
* Client Component (`"use client"`) that imports the site's config.
|
|
17
24
|
* In `next dev` it saves to the files on disk; on the live site, editors sign
|
|
18
|
-
* in to the config's git backend.
|
|
25
|
+
* in to the config's git backend, or on a full-stack site (`server`), with the site's own sign-in.
|
|
19
26
|
*/
|
|
20
|
-
export declare function GoodfellowAdmin({ config, siteUrl, strings }: GoodfellowAdminProps): import("react").JSX.Element | null;
|
|
27
|
+
export declare function GoodfellowAdmin({ config, siteUrl, strings, server }: GoodfellowAdminProps): import("react").JSX.Element | null;
|
|
21
28
|
//#endregion
|
package/dist/admin.js
CHANGED
|
@@ -40,9 +40,9 @@ async function setUp(siteUrl) {
|
|
|
40
40
|
* The admin panel, for a Next.js site's `app/admin/page.tsx`, which must be a
|
|
41
41
|
* Client Component (`"use client"`) that imports the site's config.
|
|
42
42
|
* In `next dev` it saves to the files on disk; on the live site, editors sign
|
|
43
|
-
* in to the config's git backend.
|
|
43
|
+
* in to the config's git backend, or on a full-stack site (`server`), with the site's own sign-in.
|
|
44
44
|
*/
|
|
45
|
-
function GoodfellowAdmin({ config, siteUrl = `${BASE_PATH}/`, strings }) {
|
|
45
|
+
function GoodfellowAdmin({ config, siteUrl = `${BASE_PATH}/`, strings, server }) {
|
|
46
46
|
const [setup, setSetup] = useState(null);
|
|
47
47
|
useEffect(() => {
|
|
48
48
|
let current = true;
|
|
@@ -64,6 +64,7 @@ function GoodfellowAdmin({ config, siteUrl = `${BASE_PATH}/`, strings }) {
|
|
|
64
64
|
store: setup.store,
|
|
65
65
|
registry: `${BASE_PATH}/__goodfellow/api/registry/{name}.json`
|
|
66
66
|
},
|
|
67
|
+
...server && { server: `${BASE_PATH}${server}` },
|
|
67
68
|
preview: {
|
|
68
69
|
stylesheets: setup.stylesheets,
|
|
69
70
|
themeCss: theme_css_default,
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import { NextConfig } from "next";
|
|
|
3
3
|
export interface GoodfellowNextOptions {
|
|
4
4
|
/** The site's folder, holding `content/` and `public/media/`. Defaults to the folder Next.js runs in. */
|
|
5
5
|
root?: string;
|
|
6
|
+
/**
|
|
7
|
+
* For a full-stack site, with a server of its own: builds it for `next start`
|
|
8
|
+
* or a host that runs Next.js, rather than as static files, and includes
|
|
9
|
+
* `content/` and `public/media/` with its server, which reads them for pages
|
|
10
|
+
* built on request and calendar files.
|
|
11
|
+
*/
|
|
12
|
+
server?: boolean;
|
|
6
13
|
}
|
|
7
14
|
/**
|
|
8
15
|
* Sets up Next.js for a Goodfellow site, in `next.config.ts`:
|
|
@@ -16,6 +23,8 @@ export interface GoodfellowNextOptions {
|
|
|
16
23
|
* - Serves images as they are unless the site sets an image loader, since static files can't be optimized on request.
|
|
17
24
|
* - Adds the site's redirects (Site Settings → Redirects) ahead of the site's own, for hosts that
|
|
18
25
|
* send them, such as Vercel. Others get the pages `goodfellow-next finish` writes at old addresses.
|
|
26
|
+
* - With `{ server: true }`, builds a full-stack site for a server instead of as static
|
|
27
|
+
* files: see `goodfellowApi()` in `@goodfellow-cms/next/server`.
|
|
19
28
|
* - In `next dev`, runs the admin panel's local backend, so publishing saves to
|
|
20
29
|
* the files on disk. It's never part of a build.
|
|
21
30
|
* - In `next dev`, turns off React's debug channel (`experimental.reactDebugChannel`)
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CALENDARS_DIR, allPages, loadSiteContent, normalizeBase, redirectFrom, redirectProblems } from "@goodfellow-cms/core";
|
|
2
|
-
import { DEV_API_PREFIX, fileSystemSource, handleDevApi, isLocalOrigin, localFileStore, localRegistryDir } from "@goodfellow-cms/core/node";
|
|
2
|
+
import { DEV_API_PREFIX, contentRepositoryProblem, fileSystemSource, handleDevApi, isLocalOrigin, localFileStore, localRegistryDir } from "@goodfellow-cms/core/node";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
//#region src/config.ts
|
|
@@ -91,6 +91,8 @@ async function siteRedirects(root) {
|
|
|
91
91
|
* - Serves images as they are unless the site sets an image loader, since static files can't be optimized on request.
|
|
92
92
|
* - Adds the site's redirects (Site Settings → Redirects) ahead of the site's own, for hosts that
|
|
93
93
|
* send them, such as Vercel. Others get the pages `goodfellow-next finish` writes at old addresses.
|
|
94
|
+
* - With `{ server: true }`, builds a full-stack site for a server instead of as static
|
|
95
|
+
* files: see `goodfellowApi()` in `@goodfellow-cms/next/server`.
|
|
94
96
|
* - In `next dev`, runs the admin panel's local backend, so publishing saves to
|
|
95
97
|
* the files on disk. It's never part of a build.
|
|
96
98
|
* - In `next dev`, turns off React's debug channel (`experimental.reactDebugChannel`)
|
|
@@ -101,6 +103,8 @@ function withGoodfellow(nextConfig = {}, options = {}) {
|
|
|
101
103
|
const root = resolve(options.root ?? process.cwd());
|
|
102
104
|
const basePath = nextBasePath(nextConfig.basePath ?? process.env.GOODFELLOW_BASE);
|
|
103
105
|
return async (phase) => {
|
|
106
|
+
const problem = contentRepositoryProblem(root);
|
|
107
|
+
if (problem) throw new Error(problem);
|
|
104
108
|
const config = {
|
|
105
109
|
trailingSlash: true,
|
|
106
110
|
...nextConfig,
|
|
@@ -111,6 +115,19 @@ function withGoodfellow(nextConfig = {}, options = {}) {
|
|
|
111
115
|
GOODFELLOW_BASE_PATH: basePath
|
|
112
116
|
}
|
|
113
117
|
};
|
|
118
|
+
if (phase !== PHASE_DEVELOPMENT_SERVER && options.server) return {
|
|
119
|
+
...config,
|
|
120
|
+
images: nextConfig.images,
|
|
121
|
+
outputFileTracingIncludes: {
|
|
122
|
+
...nextConfig.outputFileTracingIncludes,
|
|
123
|
+
"/**": [
|
|
124
|
+
...nextConfig.outputFileTracingIncludes?.["/**"] ?? [],
|
|
125
|
+
"./content/**/*",
|
|
126
|
+
"./public/media/**/*"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
redirects: async () => [...await siteRedirects(root), ...await nextConfig.redirects?.() ?? []]
|
|
130
|
+
};
|
|
114
131
|
if (phase !== PHASE_DEVELOPMENT_SERVER) return {
|
|
115
132
|
output: "export",
|
|
116
133
|
...config,
|
package/dist/export.d.ts
CHANGED
|
@@ -19,4 +19,16 @@ export declare function writeRedirectFiles(outDir: string, root?: string): Promi
|
|
|
19
19
|
* prefetch is "not found". Returns how many files it moved; none elsewhere.
|
|
20
20
|
*/
|
|
21
21
|
export declare function fixSegmentFiles(outDir: string): Promise<number>;
|
|
22
|
+
/** A page `next build` rendered ahead: its address on the site, and its HTML. */
|
|
23
|
+
export interface BuiltPage {
|
|
24
|
+
url: string;
|
|
25
|
+
html: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The pages `next build` rendered ahead for a full-stack site (`withGoodfellow(config,
|
|
29
|
+
* { server: true })`), at their addresses on the site, without its base path, as the
|
|
30
|
+
* Search block looks them up. Next.js lists them in `.next/prerender-manifest.json`
|
|
31
|
+
* and keeps their HTML in `.next/server/app/`. The admin panel and Next.js's own pages are left out.
|
|
32
|
+
*/
|
|
33
|
+
export declare function builtPages(root?: string): Promise<BuiltPage[]>;
|
|
22
34
|
//#endregion
|
package/dist/export.js
CHANGED
|
@@ -88,5 +88,28 @@ async function fixSegmentFiles(outDir) {
|
|
|
88
88
|
await visit(outDir);
|
|
89
89
|
return moved;
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The pages `next build` rendered ahead for a full-stack site (`withGoodfellow(config,
|
|
93
|
+
* { server: true })`), at their addresses on the site, without its base path, as the
|
|
94
|
+
* Search block looks them up. Next.js lists them in `.next/prerender-manifest.json`
|
|
95
|
+
* and keeps their HTML in `.next/server/app/`. The admin panel and Next.js's own pages are left out.
|
|
96
|
+
*/
|
|
97
|
+
async function builtPages(root = process.cwd()) {
|
|
98
|
+
const next = join(resolve(root), ".next");
|
|
99
|
+
const manifest = JSON.parse(await readFile(join(next, "prerender-manifest.json"), "utf8"));
|
|
100
|
+
const trailingSlash = JSON.parse(await readFile(join(next, "required-server-files.json"), "utf8")).config?.trailingSlash === true;
|
|
101
|
+
const pages = [];
|
|
102
|
+
for (const route of Object.keys(manifest.routes ?? {})) {
|
|
103
|
+
if (route.startsWith("/_") || route === "/admin" || route.startsWith("/admin/")) continue;
|
|
104
|
+
const file = join(next, "server/app", `${route === "/" ? "/index" : route}.html`);
|
|
105
|
+
if (!await exists(file)) continue;
|
|
106
|
+
const url = route === "/" ? "/" : trailingSlash ? `${route}/` : route;
|
|
107
|
+
pages.push({
|
|
108
|
+
url,
|
|
109
|
+
html: await readFile(file, "utf8")
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return pages;
|
|
113
|
+
}
|
|
91
114
|
//#endregion
|
|
92
|
-
export { fixSegmentFiles, writeCalendarFiles, writeRedirectFiles };
|
|
115
|
+
export { builtPages, fixSegmentFiles, writeCalendarFiles, writeRedirectFiles };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { GoodfellowConfig } from "@goodfellow-cms/core";
|
|
2
|
+
import { AppApiOptions } from "@goodfellow-cms/core/node";
|
|
3
|
+
import { ServerKeys } from "@goodfellow-cms/ai";
|
|
4
|
+
//#region src/server.d.ts
|
|
5
|
+
/** The environment variable the server's token is read from, unless `goodfellowApi({ token })` gives one. */
|
|
6
|
+
export declare const GIT_TOKEN_VARIABLE = "GOODFELLOW_GIT_TOKEN";
|
|
7
|
+
export interface GoodfellowApiOptions {
|
|
8
|
+
/** The site's `goodfellow.config.tsx`, whose `backend` says where the site is stored. */
|
|
9
|
+
config: GoodfellowConfig;
|
|
10
|
+
/**
|
|
11
|
+
* A token that can read and publish to the site's repository, such as a
|
|
12
|
+
* fine-grained GitHub token or a GitLab project access token. It never
|
|
13
|
+
* reaches a browser. Defaults to the `GOODFELLOW_GIT_TOKEN` environment variable.
|
|
14
|
+
*/
|
|
15
|
+
token?: string | (() => Promise<string>);
|
|
16
|
+
/**
|
|
17
|
+
* Who's signed in, from the site's own sign-in (such as Better Auth's
|
|
18
|
+
* `auth.api.getSession({ headers: request.headers })`): their name, email
|
|
19
|
+
* address and role, or `null` if nobody is, or `false` if they may not edit.
|
|
20
|
+
*/
|
|
21
|
+
authorize: AppApiOptions["authorize"];
|
|
22
|
+
/** Where the admin panel sends people to sign in, such as `/sign-in?next=/admin/`. */
|
|
23
|
+
signIn: string;
|
|
24
|
+
/** Where the admin panel's Sign out button goes, if the site has an address for it. */
|
|
25
|
+
signOut?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Keys for AI services, such as `{ anthropic: process.env.ANTHROPIC_API_KEY }`,
|
|
28
|
+
* so the AI assistant's requests go through the server and editors need no
|
|
29
|
+
* keys of their own. Services without a key are left out.
|
|
30
|
+
*/
|
|
31
|
+
ai?: ServerKeys;
|
|
32
|
+
/** Told about failures editors only hear about as "something went wrong". Defaults to `console.error`. */
|
|
33
|
+
onError?: (error: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
/** The route parameters of a catch-all route named `[...path]`. */
|
|
36
|
+
interface RouteContext {
|
|
37
|
+
params: Promise<{
|
|
38
|
+
path?: string[];
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The admin panel's API on a full-stack Next.js site, for
|
|
43
|
+
* `app/api/goodfellow/[...path]/route.ts`:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* export const { GET, POST } = goodfellowApi({ config, authorize, signIn: "/sign-in" });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* The admin panel (`<GoodfellowAdmin server="/api/goodfellow" />`) reads and
|
|
50
|
+
* publishes through it, and the server reaches the repository with its own
|
|
51
|
+
* token, naming each editor as the author of what they publish.
|
|
52
|
+
*/
|
|
53
|
+
export declare function goodfellowApi(options: GoodfellowApiOptions): {
|
|
54
|
+
GET: (request: Request, context: RouteContext) => Promise<Response>;
|
|
55
|
+
POST: (request: Request, context: RouteContext) => Promise<Response>;
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { appApi } from "@goodfellow-cms/core/node";
|
|
2
|
+
import { aiServer } from "@goodfellow-cms/ai";
|
|
3
|
+
//#region src/server.ts
|
|
4
|
+
/** The environment variable the server's token is read from, unless `goodfellowApi({ token })` gives one. */
|
|
5
|
+
const GIT_TOKEN_VARIABLE = "GOODFELLOW_GIT_TOKEN";
|
|
6
|
+
/**
|
|
7
|
+
* The admin panel's API on a full-stack Next.js site, for
|
|
8
|
+
* `app/api/goodfellow/[...path]/route.ts`:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* export const { GET, POST } = goodfellowApi({ config, authorize, signIn: "/sign-in" });
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The admin panel (`<GoodfellowAdmin server="/api/goodfellow" />`) reads and
|
|
15
|
+
* publishes through it, and the server reaches the repository with its own
|
|
16
|
+
* token, naming each editor as the author of what they publish.
|
|
17
|
+
*/
|
|
18
|
+
function goodfellowApi(options) {
|
|
19
|
+
let api;
|
|
20
|
+
function handler() {
|
|
21
|
+
if (api) return api;
|
|
22
|
+
const { backend } = options.config;
|
|
23
|
+
if (!backend?.server) throw new Error("goodfellowApi() needs the config's `backend`, such as github({ repo }) or gitlab({ project }).");
|
|
24
|
+
const token = options.token || process.env["GOODFELLOW_GIT_TOKEN"] || (() => {
|
|
25
|
+
throw new Error(`goodfellowApi() needs a token: set GOODFELLOW_GIT_TOKEN, or pass \`token\`.`);
|
|
26
|
+
})();
|
|
27
|
+
const ai = options.ai && aiServer(options.ai);
|
|
28
|
+
api = appApi({
|
|
29
|
+
git: backend.server({ token }),
|
|
30
|
+
authorize: options.authorize,
|
|
31
|
+
signIn: options.signIn,
|
|
32
|
+
...options.signOut && { signOut: options.signOut },
|
|
33
|
+
...ai && ai.services.length > 0 && { ai },
|
|
34
|
+
...options.onError && { onError: options.onError }
|
|
35
|
+
});
|
|
36
|
+
return api;
|
|
37
|
+
}
|
|
38
|
+
const route = async (request, context) => handler()(request, ((await context.params).path ?? []).join("/"));
|
|
39
|
+
return {
|
|
40
|
+
GET: route,
|
|
41
|
+
POST: route
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { GIT_TOKEN_VARIABLE, goodfellowApi };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodfellow-cms/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Goodfellow pages and the admin panel in a Next.js site.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"goodfellow",
|
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
"import": "./dist/export.js",
|
|
44
44
|
"default": "./dist/export.js"
|
|
45
45
|
},
|
|
46
|
+
"./server": {
|
|
47
|
+
"types": "./dist/server.d.ts",
|
|
48
|
+
"import": "./dist/server.js",
|
|
49
|
+
"default": "./dist/server.js"
|
|
50
|
+
},
|
|
46
51
|
"./package.json": "./package.json"
|
|
47
52
|
},
|
|
48
53
|
"bin": {
|
|
@@ -53,15 +58,16 @@
|
|
|
53
58
|
"dist"
|
|
54
59
|
],
|
|
55
60
|
"dependencies": {
|
|
56
|
-
"@goodfellow-cms/
|
|
57
|
-
"@goodfellow-cms/react": "0.
|
|
61
|
+
"@goodfellow-cms/ai": "0.4.0",
|
|
62
|
+
"@goodfellow-cms/react": "0.4.0",
|
|
63
|
+
"@goodfellow-cms/core": "0.4.0"
|
|
58
64
|
},
|
|
59
65
|
"peerDependencies": {
|
|
60
66
|
"@puckeditor/core": "0.23.0",
|
|
61
67
|
"next": "^16.0.0",
|
|
62
68
|
"react": "^19.0.0",
|
|
63
69
|
"react-dom": "^19.0.0",
|
|
64
|
-
"@goodfellow-cms/admin": "0.
|
|
70
|
+
"@goodfellow-cms/admin": "0.4.0"
|
|
65
71
|
},
|
|
66
72
|
"devDependencies": {
|
|
67
73
|
"@puckeditor/core": "0.23.0",
|
|
@@ -71,7 +77,9 @@
|
|
|
71
77
|
"next": "16.3.8",
|
|
72
78
|
"react": "19.3.0",
|
|
73
79
|
"react-dom": "19.3.0",
|
|
74
|
-
"@goodfellow-cms/admin": "0.
|
|
80
|
+
"@goodfellow-cms/admin": "0.4.0",
|
|
81
|
+
"@goodfellow-cms/github": "0.4.0",
|
|
82
|
+
"@goodfellow-cms/gitlab": "0.4.0"
|
|
75
83
|
},
|
|
76
84
|
"engines": {
|
|
77
85
|
"node": ">=22"
|