@headroom-cms/admin-api 0.1.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/dist/auth.cjs +73 -0
- package/dist/auth.d.cts +23 -0
- package/dist/auth.d.ts +23 -0
- package/dist/auth.js +36 -0
- package/dist/index.cjs +640 -0
- package/dist/index.d.cts +757 -0
- package/dist/index.d.ts +757 -0
- package/dist/index.js +601 -0
- package/dist/node.cjs +78 -0
- package/dist/node.d.cts +644 -0
- package/dist/node.d.ts +644 -0
- package/dist/node.js +41 -0
- package/dist/seed.cjs +175 -0
- package/dist/seed.d.cts +415 -0
- package/dist/seed.d.ts +415 -0
- package/dist/seed.js +143 -0
- package/package.json +57 -0
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
interface components {
|
|
2
|
+
schemas: {
|
|
3
|
+
APIKeyCreateResult: {
|
|
4
|
+
/** Format: int64 */
|
|
5
|
+
createdAt: number;
|
|
6
|
+
key: string;
|
|
7
|
+
keyId: string;
|
|
8
|
+
label: string;
|
|
9
|
+
};
|
|
10
|
+
APIKeyList: {
|
|
11
|
+
keys?: components["schemas"]["APIKeyMetadata"][];
|
|
12
|
+
};
|
|
13
|
+
APIKeyMetadata: {
|
|
14
|
+
/** Format: int64 */
|
|
15
|
+
createdAt: number;
|
|
16
|
+
keyId: string;
|
|
17
|
+
label: string;
|
|
18
|
+
/** Format: int64 */
|
|
19
|
+
lastUsedAt?: number | null;
|
|
20
|
+
};
|
|
21
|
+
AdminBlockTypeList: {
|
|
22
|
+
blockTypes?: components["schemas"]["BlockType"][];
|
|
23
|
+
};
|
|
24
|
+
AdminCollectionList: {
|
|
25
|
+
collections?: components["schemas"]["Collection"][];
|
|
26
|
+
};
|
|
27
|
+
AdminContentList: {
|
|
28
|
+
cursor?: string;
|
|
29
|
+
hasMore: boolean;
|
|
30
|
+
items: components["schemas"]["ContentMetadata"][];
|
|
31
|
+
};
|
|
32
|
+
AuditEvent: {
|
|
33
|
+
action: string;
|
|
34
|
+
adminId: string;
|
|
35
|
+
/** Format: int64 */
|
|
36
|
+
createdAt: number;
|
|
37
|
+
details?: string;
|
|
38
|
+
errorMsg?: string;
|
|
39
|
+
eventId: string;
|
|
40
|
+
host?: string;
|
|
41
|
+
status: string;
|
|
42
|
+
/** Format: int64 */
|
|
43
|
+
updatedAt?: number;
|
|
44
|
+
};
|
|
45
|
+
AuditEventList: {
|
|
46
|
+
hasMore: boolean;
|
|
47
|
+
items: components["schemas"]["AuditEvent"][];
|
|
48
|
+
};
|
|
49
|
+
BlockType: {
|
|
50
|
+
fields?: components["schemas"]["FieldDef"][];
|
|
51
|
+
icon?: string;
|
|
52
|
+
label: string;
|
|
53
|
+
name: string;
|
|
54
|
+
previewTemplate?: string;
|
|
55
|
+
};
|
|
56
|
+
Collection: {
|
|
57
|
+
fields?: components["schemas"]["FieldDef"][];
|
|
58
|
+
label: string;
|
|
59
|
+
labelSingular: string;
|
|
60
|
+
name: string;
|
|
61
|
+
singleton?: boolean;
|
|
62
|
+
slug?: string;
|
|
63
|
+
version?: number;
|
|
64
|
+
};
|
|
65
|
+
CompleteUploadRequest: {
|
|
66
|
+
alt?: string;
|
|
67
|
+
caption?: string;
|
|
68
|
+
filename: string;
|
|
69
|
+
};
|
|
70
|
+
Content: {
|
|
71
|
+
body?: {
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
};
|
|
74
|
+
collection: string;
|
|
75
|
+
contentId: string;
|
|
76
|
+
coverUrl?: string;
|
|
77
|
+
/** Format: int64 */
|
|
78
|
+
lastPublishedAt?: number;
|
|
79
|
+
/** Format: int64 */
|
|
80
|
+
publishedAt?: number;
|
|
81
|
+
slug?: string;
|
|
82
|
+
snippet?: string;
|
|
83
|
+
tags?: string[];
|
|
84
|
+
title: string;
|
|
85
|
+
};
|
|
86
|
+
ContentDetail: {
|
|
87
|
+
content: components["schemas"]["Content"];
|
|
88
|
+
draft?: components["schemas"]["DraftContent"];
|
|
89
|
+
publishedBlockId?: string;
|
|
90
|
+
};
|
|
91
|
+
ContentMetadata: {
|
|
92
|
+
collection: string;
|
|
93
|
+
contentId: string;
|
|
94
|
+
coverUrl?: string;
|
|
95
|
+
/** Format: int64 */
|
|
96
|
+
lastDraftAt?: number;
|
|
97
|
+
lastDraftBlockId?: string;
|
|
98
|
+
/** Format: int64 */
|
|
99
|
+
lastPublishedAt?: number;
|
|
100
|
+
/** Format: int64 */
|
|
101
|
+
publishedAt?: number;
|
|
102
|
+
publishedBlockId?: string;
|
|
103
|
+
slug?: string;
|
|
104
|
+
snippet?: string;
|
|
105
|
+
tags?: string[];
|
|
106
|
+
title: string;
|
|
107
|
+
};
|
|
108
|
+
CreateAPIKeyRequest: {
|
|
109
|
+
label: string;
|
|
110
|
+
};
|
|
111
|
+
CreateContentRequest: {
|
|
112
|
+
collection: string;
|
|
113
|
+
params: components["schemas"]["SaveDraftParams"];
|
|
114
|
+
};
|
|
115
|
+
CreateSiteRequest: {
|
|
116
|
+
host: string;
|
|
117
|
+
name: string;
|
|
118
|
+
};
|
|
119
|
+
CreateWebhookRequest: {
|
|
120
|
+
description?: string;
|
|
121
|
+
enabled?: boolean;
|
|
122
|
+
events: string[];
|
|
123
|
+
url: string;
|
|
124
|
+
};
|
|
125
|
+
DraftContent: {
|
|
126
|
+
blockId: string;
|
|
127
|
+
body?: {
|
|
128
|
+
[key: string]: unknown;
|
|
129
|
+
};
|
|
130
|
+
collection: string;
|
|
131
|
+
contentId: string;
|
|
132
|
+
coverUrl?: string;
|
|
133
|
+
/** Format: int64 */
|
|
134
|
+
createdAt: number;
|
|
135
|
+
createdBy: string;
|
|
136
|
+
slug?: string;
|
|
137
|
+
snippet?: string;
|
|
138
|
+
tags?: string[];
|
|
139
|
+
title?: string;
|
|
140
|
+
};
|
|
141
|
+
DraftVersion: {
|
|
142
|
+
/** Format: int64 */
|
|
143
|
+
createdAt: number;
|
|
144
|
+
createdBy: string;
|
|
145
|
+
title?: string;
|
|
146
|
+
};
|
|
147
|
+
DraftVersionList: {
|
|
148
|
+
versions?: components["schemas"]["DraftVersion"][];
|
|
149
|
+
};
|
|
150
|
+
Error: {
|
|
151
|
+
code: string;
|
|
152
|
+
error: string;
|
|
153
|
+
};
|
|
154
|
+
FieldDef: {
|
|
155
|
+
label: string;
|
|
156
|
+
name: string;
|
|
157
|
+
options?: {
|
|
158
|
+
[key: string]: unknown;
|
|
159
|
+
};
|
|
160
|
+
type: string;
|
|
161
|
+
};
|
|
162
|
+
Media: {
|
|
163
|
+
alt?: string;
|
|
164
|
+
caption?: string;
|
|
165
|
+
filename: string;
|
|
166
|
+
height?: number;
|
|
167
|
+
mediaId: string;
|
|
168
|
+
mimeType: string;
|
|
169
|
+
/** Format: int64 */
|
|
170
|
+
size: number;
|
|
171
|
+
/** Format: int64 */
|
|
172
|
+
uploadedAt: number;
|
|
173
|
+
url: string;
|
|
174
|
+
width?: number;
|
|
175
|
+
};
|
|
176
|
+
MediaList: {
|
|
177
|
+
cursor?: string;
|
|
178
|
+
hasMore: boolean;
|
|
179
|
+
items: components["schemas"]["Media"][];
|
|
180
|
+
};
|
|
181
|
+
RetryDeliveryResult: {
|
|
182
|
+
deliveryId: string;
|
|
183
|
+
};
|
|
184
|
+
RotateSecretResult: {
|
|
185
|
+
secret: string;
|
|
186
|
+
};
|
|
187
|
+
SaveDraftParams: {
|
|
188
|
+
body: {
|
|
189
|
+
[key: string]: unknown;
|
|
190
|
+
};
|
|
191
|
+
coverUrl?: string;
|
|
192
|
+
slug?: string;
|
|
193
|
+
snippet?: string;
|
|
194
|
+
tags?: string[];
|
|
195
|
+
title?: string;
|
|
196
|
+
};
|
|
197
|
+
SaveDraftResult: {
|
|
198
|
+
blockId: string;
|
|
199
|
+
contentId: string;
|
|
200
|
+
/** Format: int64 */
|
|
201
|
+
createdAt: number;
|
|
202
|
+
};
|
|
203
|
+
Site: {
|
|
204
|
+
admins?: components["schemas"]["SiteAdmin"][];
|
|
205
|
+
/** Format: int64 */
|
|
206
|
+
createdAt: number;
|
|
207
|
+
host: string;
|
|
208
|
+
name: string;
|
|
209
|
+
schemaVersion: number;
|
|
210
|
+
status: string;
|
|
211
|
+
/** Format: int64 */
|
|
212
|
+
updatedAt: number;
|
|
213
|
+
};
|
|
214
|
+
SiteAdmin: {
|
|
215
|
+
id: string;
|
|
216
|
+
/** @enum {string} */
|
|
217
|
+
role: "admin" | "editor";
|
|
218
|
+
};
|
|
219
|
+
SiteList: {
|
|
220
|
+
items?: components["schemas"]["Site"][];
|
|
221
|
+
};
|
|
222
|
+
UpdateAPIKeyRequest: {
|
|
223
|
+
label: string;
|
|
224
|
+
};
|
|
225
|
+
UpdateAdminsRequest: {
|
|
226
|
+
admins: components["schemas"]["SiteAdmin"][];
|
|
227
|
+
};
|
|
228
|
+
UpdateMediaRequest: {
|
|
229
|
+
alt?: string;
|
|
230
|
+
caption?: string;
|
|
231
|
+
};
|
|
232
|
+
UpdateSiteRequest: {
|
|
233
|
+
name?: string;
|
|
234
|
+
status?: string;
|
|
235
|
+
};
|
|
236
|
+
UpdateWebhookRequest: {
|
|
237
|
+
description?: string;
|
|
238
|
+
enabled?: boolean;
|
|
239
|
+
events?: string[];
|
|
240
|
+
url?: string;
|
|
241
|
+
};
|
|
242
|
+
UploadURLRequest: {
|
|
243
|
+
contentType: string;
|
|
244
|
+
filename: string;
|
|
245
|
+
/** Format: int64 */
|
|
246
|
+
size: number;
|
|
247
|
+
};
|
|
248
|
+
UploadURLResult: {
|
|
249
|
+
/** Format: int64 */
|
|
250
|
+
expiresAt: number;
|
|
251
|
+
mediaId: string;
|
|
252
|
+
uploadUrl: string;
|
|
253
|
+
};
|
|
254
|
+
Webhook: {
|
|
255
|
+
/** Format: int64 */
|
|
256
|
+
createdAt: number;
|
|
257
|
+
description?: string;
|
|
258
|
+
enabled: boolean;
|
|
259
|
+
events: string[];
|
|
260
|
+
host?: string;
|
|
261
|
+
secret?: string;
|
|
262
|
+
/** Format: int64 */
|
|
263
|
+
updatedAt?: number;
|
|
264
|
+
url: string;
|
|
265
|
+
webhookId: string;
|
|
266
|
+
};
|
|
267
|
+
WebhookDelivery: {
|
|
268
|
+
attempts?: number;
|
|
269
|
+
/** Format: int64 */
|
|
270
|
+
createdAt: number;
|
|
271
|
+
deliveryId: string;
|
|
272
|
+
/** Format: int64 */
|
|
273
|
+
duration?: number;
|
|
274
|
+
error?: string;
|
|
275
|
+
event: string;
|
|
276
|
+
payload?: string;
|
|
277
|
+
responseBody?: string;
|
|
278
|
+
responseStatus?: number;
|
|
279
|
+
status: string;
|
|
280
|
+
webhookId: string;
|
|
281
|
+
};
|
|
282
|
+
WebhookDeliveryList: {
|
|
283
|
+
deliveries: components["schemas"]["WebhookDelivery"][];
|
|
284
|
+
hasMore: boolean;
|
|
285
|
+
};
|
|
286
|
+
WebhookList: {
|
|
287
|
+
webhooks?: components["schemas"]["Webhook"][];
|
|
288
|
+
};
|
|
289
|
+
WebhookTestResult: {
|
|
290
|
+
/** Format: int64 */
|
|
291
|
+
durationMs: number;
|
|
292
|
+
error?: string;
|
|
293
|
+
requestPayload: string;
|
|
294
|
+
responseBody?: string;
|
|
295
|
+
statusCode?: number;
|
|
296
|
+
success: boolean;
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
responses: never;
|
|
300
|
+
parameters: never;
|
|
301
|
+
requestBodies: never;
|
|
302
|
+
headers: never;
|
|
303
|
+
pathItems: never;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
type Site = components["schemas"]["Site"];
|
|
307
|
+
type Collection = components["schemas"]["Collection"] & {
|
|
308
|
+
relationships?: RelationshipDef[];
|
|
309
|
+
singletonContentId?: string;
|
|
310
|
+
};
|
|
311
|
+
type FieldDefinition = components["schemas"]["FieldDef"];
|
|
312
|
+
type ContentItem = components["schemas"]["ContentMetadata"] & {
|
|
313
|
+
coverMediaId?: string;
|
|
314
|
+
relationships?: Record<string, ContentRef[]>;
|
|
315
|
+
};
|
|
316
|
+
type SaveDraftResult = components["schemas"]["SaveDraftResult"];
|
|
317
|
+
type ApiKeyItem = components["schemas"]["APIKeyMetadata"];
|
|
318
|
+
type ApiKeyCreateResponse = components["schemas"]["APIKeyCreateResult"];
|
|
319
|
+
interface AdminMediaItem {
|
|
320
|
+
mediaId: string;
|
|
321
|
+
filename: string;
|
|
322
|
+
mimeType: string;
|
|
323
|
+
size: number;
|
|
324
|
+
width?: number;
|
|
325
|
+
height?: number;
|
|
326
|
+
uploadedAt: number;
|
|
327
|
+
url: string;
|
|
328
|
+
alt?: string;
|
|
329
|
+
caption?: string;
|
|
330
|
+
tags: string[];
|
|
331
|
+
userTags: string[];
|
|
332
|
+
folderId?: string;
|
|
333
|
+
urls?: Record<string, string>;
|
|
334
|
+
}
|
|
335
|
+
interface RelationshipDef {
|
|
336
|
+
name: string;
|
|
337
|
+
label: string;
|
|
338
|
+
targetCollection: string;
|
|
339
|
+
multiple: boolean;
|
|
340
|
+
}
|
|
341
|
+
interface ContentRef {
|
|
342
|
+
contentId: string;
|
|
343
|
+
collection: string;
|
|
344
|
+
slug: string;
|
|
345
|
+
title: string;
|
|
346
|
+
}
|
|
347
|
+
interface CollectionInput {
|
|
348
|
+
name: string;
|
|
349
|
+
label: string;
|
|
350
|
+
labelSingular?: string;
|
|
351
|
+
slug?: string;
|
|
352
|
+
singleton?: boolean;
|
|
353
|
+
fields: FieldDefinition[];
|
|
354
|
+
relationships?: RelationshipDef[];
|
|
355
|
+
}
|
|
356
|
+
interface SaveDraftParams {
|
|
357
|
+
title?: string;
|
|
358
|
+
slug?: string;
|
|
359
|
+
snippet?: string;
|
|
360
|
+
tags?: string[];
|
|
361
|
+
coverUrl?: string;
|
|
362
|
+
coverMediaId?: string;
|
|
363
|
+
coverWidth?: number;
|
|
364
|
+
coverHeight?: number;
|
|
365
|
+
body?: Record<string, unknown>;
|
|
366
|
+
createVersion?: boolean;
|
|
367
|
+
relationships?: Record<string, string[]>;
|
|
368
|
+
}
|
|
369
|
+
interface PaginatedResponse<T> {
|
|
370
|
+
items: T[];
|
|
371
|
+
cursor: string | null;
|
|
372
|
+
hasMore: boolean;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Subset of HeadroomAdminClient used by seed helpers. */
|
|
376
|
+
interface SeedClient {
|
|
377
|
+
createSite(host: string, name: string): Promise<Site>;
|
|
378
|
+
createCollection(host: string, input: CollectionInput): Promise<Collection>;
|
|
379
|
+
uploadFromUrl(host: string, url: string, filename: string): Promise<AdminMediaItem>;
|
|
380
|
+
listContent(host: string, params?: {
|
|
381
|
+
collection?: string;
|
|
382
|
+
}): Promise<PaginatedResponse<ContentItem>>;
|
|
383
|
+
createContent(host: string, params: {
|
|
384
|
+
collection: string;
|
|
385
|
+
params: SaveDraftParams;
|
|
386
|
+
}): Promise<SaveDraftResult>;
|
|
387
|
+
saveDraft(host: string, contentId: string, draft: SaveDraftParams): Promise<SaveDraftResult>;
|
|
388
|
+
publishContent(host: string, contentId: string): Promise<ContentItem>;
|
|
389
|
+
listApiKeys(host: string): Promise<ApiKeyItem[]>;
|
|
390
|
+
createApiKey(host: string, label: string): Promise<ApiKeyCreateResponse>;
|
|
391
|
+
}
|
|
392
|
+
type MediaMap = Map<string, {
|
|
393
|
+
mediaId: string;
|
|
394
|
+
url: string;
|
|
395
|
+
filename: string;
|
|
396
|
+
width: number;
|
|
397
|
+
height: number;
|
|
398
|
+
}>;
|
|
399
|
+
/** Idempotent site creation — swallows 409 conflict. */
|
|
400
|
+
declare function ensureSite(client: SeedClient, host: string, name: string): Promise<void>;
|
|
401
|
+
/** Idempotent collection creation — swallows 409 per collection. */
|
|
402
|
+
declare function ensureCollections(client: SeedClient, host: string, collections: CollectionInput[]): Promise<void>;
|
|
403
|
+
/** Batch upload images from URLs. Returns a sourceURL → media info map. */
|
|
404
|
+
declare function uploadMediaUrls(client: SeedClient, host: string, urls: string[]): Promise<MediaMap>;
|
|
405
|
+
/** Upsert content items by slug: update if exists, create if new, then publish. */
|
|
406
|
+
declare function upsertContent(client: SeedClient, host: string, collection: string, items: {
|
|
407
|
+
draft: SaveDraftParams;
|
|
408
|
+
tags: string[];
|
|
409
|
+
}[]): Promise<void>;
|
|
410
|
+
/** Upsert singleton: find existing → saveDraft → publish. No createContent needed. */
|
|
411
|
+
declare function upsertSingleton(client: SeedClient, host: string, collection: string, draft: SaveDraftParams): Promise<void>;
|
|
412
|
+
/** Ensure at least one API key exists; returns the key string. */
|
|
413
|
+
declare function ensureApiKey(client: SeedClient, host: string, label: string, existingKeyFromEnv?: string): Promise<string>;
|
|
414
|
+
|
|
415
|
+
export { type MediaMap, type SeedClient, ensureApiKey, ensureCollections, ensureSite, uploadMediaUrls, upsertContent, upsertSingleton };
|
package/dist/seed.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
var HeadroomApiError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
constructor(status, code, message) {
|
|
6
|
+
super(message || code);
|
|
7
|
+
this.name = "HeadroomApiError";
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
/** Alias for `message` — eases migration from admin's ApiError which used `detail`. */
|
|
12
|
+
get detail() {
|
|
13
|
+
return this.message;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/seed.ts
|
|
18
|
+
function filenameFromUrl(url) {
|
|
19
|
+
const pathname = new URL(url).pathname;
|
|
20
|
+
const base = pathname.split("/").pop() || "image";
|
|
21
|
+
return base.includes(".") ? base : `${base}.jpg`;
|
|
22
|
+
}
|
|
23
|
+
async function ensureSite(client, host, name) {
|
|
24
|
+
try {
|
|
25
|
+
await client.createSite(host, name);
|
|
26
|
+
console.log(` Site created: ${host}`);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
if (e instanceof HeadroomApiError && e.status === 409) {
|
|
29
|
+
console.log(` Site already exists: ${host}`);
|
|
30
|
+
} else {
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function ensureCollections(client, host, collections) {
|
|
36
|
+
for (const coll of collections) {
|
|
37
|
+
try {
|
|
38
|
+
await client.createCollection(host, coll);
|
|
39
|
+
console.log(` Created: ${coll.name}`);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
if (e instanceof HeadroomApiError && e.status === 409) {
|
|
42
|
+
console.log(` Already exists: ${coll.name}`);
|
|
43
|
+
} else {
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function uploadMediaUrls(client, host, urls) {
|
|
50
|
+
const mediaMap = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const url of urls) {
|
|
52
|
+
try {
|
|
53
|
+
const filename = filenameFromUrl(url);
|
|
54
|
+
const media = await client.uploadFromUrl(host, url, filename);
|
|
55
|
+
mediaMap.set(url, {
|
|
56
|
+
mediaId: media.mediaId,
|
|
57
|
+
url: media.url,
|
|
58
|
+
filename: media.filename,
|
|
59
|
+
width: media.width ?? 0,
|
|
60
|
+
height: media.height ?? 0
|
|
61
|
+
});
|
|
62
|
+
console.log(` Uploaded: ${filename} \u2192 ${media.mediaId}`);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.warn(
|
|
65
|
+
` Warning: failed to upload ${url}: ${e.message}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return mediaMap;
|
|
70
|
+
}
|
|
71
|
+
async function upsertContent(client, host, collection, items) {
|
|
72
|
+
const existing = await client.listContent(host, { collection });
|
|
73
|
+
const slugMap = /* @__PURE__ */ new Map();
|
|
74
|
+
for (const item of existing.items) {
|
|
75
|
+
if (item.slug) {
|
|
76
|
+
slugMap.set(item.slug, item);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const item of items) {
|
|
80
|
+
const slug = item.draft.slug;
|
|
81
|
+
const title = item.draft.title || slug || "untitled";
|
|
82
|
+
const existingItem = slug ? slugMap.get(slug) : void 0;
|
|
83
|
+
try {
|
|
84
|
+
if (existingItem) {
|
|
85
|
+
const draftParams = { ...item.draft };
|
|
86
|
+
if (item.tags.length > 0) {
|
|
87
|
+
draftParams.tags = item.tags;
|
|
88
|
+
}
|
|
89
|
+
await client.saveDraft(host, existingItem.contentId, draftParams);
|
|
90
|
+
await client.publishContent(host, existingItem.contentId);
|
|
91
|
+
console.log(` Updated & published: ${title}`);
|
|
92
|
+
} else {
|
|
93
|
+
const result = await client.createContent(host, {
|
|
94
|
+
collection,
|
|
95
|
+
params: item.draft
|
|
96
|
+
});
|
|
97
|
+
if (item.tags.length > 0) {
|
|
98
|
+
await client.saveDraft(host, result.contentId, {
|
|
99
|
+
...item.draft,
|
|
100
|
+
tags: item.tags
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
await client.publishContent(host, result.contentId);
|
|
104
|
+
console.log(` Created & published: ${title}`);
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.error(` Failed: ${title} -`, e.message);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function upsertSingleton(client, host, collection, draft) {
|
|
112
|
+
const existing = await client.listContent(host, { collection });
|
|
113
|
+
if (existing.items.length === 0) {
|
|
114
|
+
console.error(
|
|
115
|
+
` Could not find existing singleton content for: ${collection}`
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const id = existing.items[0].contentId;
|
|
120
|
+
await client.saveDraft(host, id, draft);
|
|
121
|
+
await client.publishContent(host, id);
|
|
122
|
+
console.log(` Singleton updated & published: ${collection}`);
|
|
123
|
+
}
|
|
124
|
+
async function ensureApiKey(client, host, label, existingKeyFromEnv) {
|
|
125
|
+
const existingKeys = await client.listApiKeys(host);
|
|
126
|
+
if (existingKeys.length > 0 && existingKeyFromEnv) {
|
|
127
|
+
console.log(
|
|
128
|
+
` ${existingKeys.length} API key(s) already exist. Reusing from .env.`
|
|
129
|
+
);
|
|
130
|
+
return existingKeyFromEnv;
|
|
131
|
+
}
|
|
132
|
+
const keyResult = await client.createApiKey(host, label);
|
|
133
|
+
console.log(` API key created.`);
|
|
134
|
+
return keyResult.key;
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
ensureApiKey,
|
|
138
|
+
ensureCollections,
|
|
139
|
+
ensureSite,
|
|
140
|
+
uploadMediaUrls,
|
|
141
|
+
upsertContent,
|
|
142
|
+
upsertSingleton
|
|
143
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@headroom-cms/admin-api",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js",
|
|
9
|
+
"require": "./dist/index.cjs"
|
|
10
|
+
},
|
|
11
|
+
"./auth": {
|
|
12
|
+
"types": "./dist/auth.d.ts",
|
|
13
|
+
"import": "./dist/auth.js",
|
|
14
|
+
"require": "./dist/auth.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./node": {
|
|
17
|
+
"types": "./dist/node.d.ts",
|
|
18
|
+
"import": "./dist/node.js",
|
|
19
|
+
"require": "./dist/node.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./seed": {
|
|
22
|
+
"types": "./dist/seed.d.ts",
|
|
23
|
+
"import": "./dist/seed.js",
|
|
24
|
+
"require": "./dist/seed.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"dev": "tsup --watch",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"generate:types": "bash scripts/generate-types.sh"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "~24.10.13",
|
|
43
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
44
|
+
"openapi-typescript": "^7.10.1",
|
|
45
|
+
"tsup": "~8.5.0",
|
|
46
|
+
"typescript": "~5.9.3",
|
|
47
|
+
"vitest": "~4.0.18"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"amazon-cognito-identity-js": "^6.3.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"amazon-cognito-identity-js": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|