@glw907/cairn-cms 0.3.0 → 0.3.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.
|
@@ -46,7 +46,7 @@ export interface AdminCollectionList {
|
|
|
46
46
|
error?: string;
|
|
47
47
|
}
|
|
48
48
|
/** List every collection's markdown files. A failed listing degrades to an inline error. */
|
|
49
|
-
export declare function adminListLoad(adapter: CairnAdapter): Promise<{
|
|
49
|
+
export declare function adminListLoad(event: PlatformEvent, adapter: CairnAdapter): Promise<{
|
|
50
50
|
collections: AdminCollectionList[];
|
|
51
51
|
}>;
|
|
52
52
|
export interface LoginData {
|
|
@@ -68,7 +68,7 @@ export interface EditData {
|
|
|
68
68
|
saved: boolean;
|
|
69
69
|
error: string | null;
|
|
70
70
|
}
|
|
71
|
-
export declare function editLoad(event: {
|
|
71
|
+
export declare function editLoad(event: PlatformEvent & {
|
|
72
72
|
params: {
|
|
73
73
|
type: string;
|
|
74
74
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAUL,KAAK,MAAM,EAEZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAwD,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEhG,OAAO,EAAuC,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAErG,4FAA4F;AAC5F,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/sveltekit/index.ts"],"names":[],"mappings":"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"}
|
package/dist/sveltekit/index.js
CHANGED
|
@@ -15,6 +15,29 @@ import { listMarkdown, readRaw, commitFile, installationToken } from '../github'
|
|
|
15
15
|
import { serializeMarkdown } from '../content';
|
|
16
16
|
import { findCollection, frontmatterFromForm } from '../adapter';
|
|
17
17
|
const EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
18
|
+
/**
|
|
19
|
+
* Mint a GitHub App installation token for *reads* when the App is configured, else undefined
|
|
20
|
+
* (reads then fall back to anonymous). Authenticated reads get the 5000/hr limit; anonymous
|
|
21
|
+
* reads share GitHub's 60/hr-per-IP budget across Cloudflare's egress IPs, so they 403 in prod.
|
|
22
|
+
* A mint failure degrades gracefully to anonymous rather than 500ing — unlike the commit path,
|
|
23
|
+
* where a missing App is fatal, a read can still succeed unauthenticated.
|
|
24
|
+
*/
|
|
25
|
+
async function readToken(env) {
|
|
26
|
+
if (!env?.GITHUB_APP_ID || !env.GITHUB_APP_INSTALLATION_ID || !env.GITHUB_APP_PRIVATE_KEY_B64) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return await installationToken({
|
|
31
|
+
appId: env.GITHUB_APP_ID,
|
|
32
|
+
installationId: env.GITHUB_APP_INSTALLATION_ID,
|
|
33
|
+
privateKeyB64: env.GITHUB_APP_PRIVATE_KEY_B64,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error('read token mint failed; falling back to anonymous read:', err);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
18
41
|
/**
|
|
19
42
|
* Branding + session for every admin page. `siteName` flows from the adapter without pulling
|
|
20
43
|
* its plugin graph into client bundles — the import stays server-side in the layout load.
|
|
@@ -26,10 +49,11 @@ export function adminLayoutLoad(event, adapter) {
|
|
|
26
49
|
return { editor: event.locals.editor, siteName: adapter.siteName, pathname: event.url.pathname };
|
|
27
50
|
}
|
|
28
51
|
/** List every collection's markdown files. A failed listing degrades to an inline error. */
|
|
29
|
-
export async function adminListLoad(adapter) {
|
|
52
|
+
export async function adminListLoad(event, adapter) {
|
|
53
|
+
const token = await readToken(event.platform?.env);
|
|
30
54
|
const collections = await Promise.all(adapter.collections.map(async ({ type, label, dir }) => {
|
|
31
55
|
try {
|
|
32
|
-
return { type, label, files: await listMarkdown(adapter.backend, dir) };
|
|
56
|
+
return { type, label, files: await listMarkdown(adapter.backend, dir, token) };
|
|
33
57
|
}
|
|
34
58
|
catch (err) {
|
|
35
59
|
// A failed listing (rate limit, network) shouldn't 500 the whole admin.
|
|
@@ -48,9 +72,9 @@ export async function editLoad(event, adapter) {
|
|
|
48
72
|
const collection = findCollection(adapter, event.params.type);
|
|
49
73
|
if (!collection)
|
|
50
74
|
throw error(404, 'Unknown collection');
|
|
51
|
-
|
|
75
|
+
const token = await readToken(event.platform?.env);
|
|
52
76
|
const path = `${collection.dir}/${event.params.id}.md`;
|
|
53
|
-
const raw = await readRaw(adapter.backend, path);
|
|
77
|
+
const raw = await readRaw(adapter.backend, path, token);
|
|
54
78
|
if (raw === null)
|
|
55
79
|
throw error(404, 'Content not found');
|
|
56
80
|
// Split frontmatter from body server-side; the editor form binds to the frontmatter and
|
package/package.json
CHANGED
|
@@ -47,6 +47,29 @@ interface PlatformEvent {
|
|
|
47
47
|
|
|
48
48
|
const EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Mint a GitHub App installation token for *reads* when the App is configured, else undefined
|
|
52
|
+
* (reads then fall back to anonymous). Authenticated reads get the 5000/hr limit; anonymous
|
|
53
|
+
* reads share GitHub's 60/hr-per-IP budget across Cloudflare's egress IPs, so they 403 in prod.
|
|
54
|
+
* A mint failure degrades gracefully to anonymous rather than 500ing — unlike the commit path,
|
|
55
|
+
* where a missing App is fatal, a read can still succeed unauthenticated.
|
|
56
|
+
*/
|
|
57
|
+
async function readToken(env: AdminEnv | undefined): Promise<string | undefined> {
|
|
58
|
+
if (!env?.GITHUB_APP_ID || !env.GITHUB_APP_INSTALLATION_ID || !env.GITHUB_APP_PRIVATE_KEY_B64) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return await installationToken({
|
|
63
|
+
appId: env.GITHUB_APP_ID,
|
|
64
|
+
installationId: env.GITHUB_APP_INSTALLATION_ID,
|
|
65
|
+
privateKeyB64: env.GITHUB_APP_PRIVATE_KEY_B64,
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error('read token mint failed; falling back to anonymous read:', err);
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
50
73
|
// ── /admin layout ──────────────────────────────────────────────────────────
|
|
51
74
|
|
|
52
75
|
export interface AdminLayoutData {
|
|
@@ -79,11 +102,15 @@ export interface AdminCollectionList {
|
|
|
79
102
|
}
|
|
80
103
|
|
|
81
104
|
/** List every collection's markdown files. A failed listing degrades to an inline error. */
|
|
82
|
-
export async function adminListLoad(
|
|
105
|
+
export async function adminListLoad(
|
|
106
|
+
event: PlatformEvent,
|
|
107
|
+
adapter: CairnAdapter,
|
|
108
|
+
): Promise<{ collections: AdminCollectionList[] }> {
|
|
109
|
+
const token = await readToken(event.platform?.env);
|
|
83
110
|
const collections = await Promise.all(
|
|
84
111
|
adapter.collections.map(async ({ type, label, dir }): Promise<AdminCollectionList> => {
|
|
85
112
|
try {
|
|
86
|
-
return { type, label, files: await listMarkdown(adapter.backend, dir) };
|
|
113
|
+
return { type, label, files: await listMarkdown(adapter.backend, dir, token) };
|
|
87
114
|
} catch (err) {
|
|
88
115
|
// A failed listing (rate limit, network) shouldn't 500 the whole admin.
|
|
89
116
|
return { type, label, files: [], error: err instanceof Error ? err.message : 'Failed to load' };
|
|
@@ -123,15 +150,15 @@ export interface EditData {
|
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
export async function editLoad(
|
|
126
|
-
event: { params: { type: string; id: string }; url: URL },
|
|
153
|
+
event: PlatformEvent & { params: { type: string; id: string }; url: URL },
|
|
127
154
|
adapter: CairnAdapter,
|
|
128
155
|
): Promise<EditData> {
|
|
129
156
|
const collection = findCollection(adapter, event.params.type);
|
|
130
157
|
if (!collection) throw error(404, 'Unknown collection');
|
|
131
158
|
|
|
132
|
-
|
|
159
|
+
const token = await readToken(event.platform?.env);
|
|
133
160
|
const path = `${collection.dir}/${event.params.id}.md`;
|
|
134
|
-
const raw = await readRaw(adapter.backend, path);
|
|
161
|
+
const raw = await readRaw(adapter.backend, path, token);
|
|
135
162
|
if (raw === null) throw error(404, 'Content not found');
|
|
136
163
|
|
|
137
164
|
// Split frontmatter from body server-side; the editor form binds to the frontmatter and
|