@byline/cli 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/LICENSE +373 -0
- package/README.md +23 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +72 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +36 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +76 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/context.d.ts +38 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +37 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/pg-url.d.ts +11 -0
- package/dist/lib/pg-url.d.ts.map +1 -0
- package/dist/lib/pg-url.js +22 -0
- package/dist/lib/pg-url.js.map +1 -0
- package/dist/manifest/deps.d.ts +29 -0
- package/dist/manifest/deps.d.ts.map +1 -0
- package/dist/manifest/deps.js +97 -0
- package/dist/manifest/deps.js.map +1 -0
- package/dist/manifest/env.d.ts +18 -0
- package/dist/manifest/env.d.ts.map +1 -0
- package/dist/manifest/env.js +38 -0
- package/dist/manifest/env.js.map +1 -0
- package/dist/phases/db-init.d.ts +3 -0
- package/dist/phases/db-init.d.ts.map +1 -0
- package/dist/phases/db-init.js +163 -0
- package/dist/phases/db-init.js.map +1 -0
- package/dist/phases/db.d.ts +11 -0
- package/dist/phases/db.d.ts.map +1 -0
- package/dist/phases/db.js +93 -0
- package/dist/phases/db.js.map +1 -0
- package/dist/phases/deps.d.ts +3 -0
- package/dist/phases/deps.d.ts.map +1 -0
- package/dist/phases/deps.js +115 -0
- package/dist/phases/deps.js.map +1 -0
- package/dist/phases/env.d.ts +3 -0
- package/dist/phases/env.d.ts.map +1 -0
- package/dist/phases/env.js +172 -0
- package/dist/phases/env.js.map +1 -0
- package/dist/phases/host.d.ts +3 -0
- package/dist/phases/host.d.ts.map +1 -0
- package/dist/phases/host.js +99 -0
- package/dist/phases/host.js.map +1 -0
- package/dist/phases/index.d.ts +7 -0
- package/dist/phases/index.d.ts.map +1 -0
- package/dist/phases/index.js +40 -0
- package/dist/phases/index.js.map +1 -0
- package/dist/phases/preflight.d.ts +4 -0
- package/dist/phases/preflight.d.ts.map +1 -0
- package/dist/phases/preflight.js +81 -0
- package/dist/phases/preflight.js.map +1 -0
- package/dist/phases/routes.d.ts +3 -0
- package/dist/phases/routes.d.ts.map +1 -0
- package/dist/phases/routes.js +145 -0
- package/dist/phases/routes.js.map +1 -0
- package/dist/phases/scaffold.d.ts +3 -0
- package/dist/phases/scaffold.d.ts.map +1 -0
- package/dist/phases/scaffold.js +113 -0
- package/dist/phases/scaffold.js.map +1 -0
- package/dist/phases/stub.d.ts +3 -0
- package/dist/phases/stub.d.ts.map +1 -0
- package/dist/phases/stub.js +25 -0
- package/dist/phases/stub.js.map +1 -0
- package/dist/phases/ui.d.ts +3 -0
- package/dist/phases/ui.d.ts.map +1 -0
- package/dist/phases/ui.js +93 -0
- package/dist/phases/ui.js.map +1 -0
- package/dist/phases/wire/index.d.ts +3 -0
- package/dist/phases/wire/index.d.ts.map +1 -0
- package/dist/phases/wire/index.js +67 -0
- package/dist/phases/wire/index.js.map +1 -0
- package/dist/phases/wire/root-tsx.d.ts +3 -0
- package/dist/phases/wire/root-tsx.d.ts.map +1 -0
- package/dist/phases/wire/root-tsx.js +57 -0
- package/dist/phases/wire/root-tsx.js.map +1 -0
- package/dist/phases/wire/server-ts.d.ts +3 -0
- package/dist/phases/wire/server-ts.d.ts.map +1 -0
- package/dist/phases/wire/server-ts.js +54 -0
- package/dist/phases/wire/server-ts.js.map +1 -0
- package/dist/phases/wire/shared.d.ts +34 -0
- package/dist/phases/wire/shared.d.ts.map +1 -0
- package/dist/phases/wire/shared.js +2 -0
- package/dist/phases/wire/shared.js.map +1 -0
- package/dist/phases/wire/start-ts.d.ts +3 -0
- package/dist/phases/wire/start-ts.d.ts.map +1 -0
- package/dist/phases/wire/start-ts.js +149 -0
- package/dist/phases/wire/start-ts.js.map +1 -0
- package/dist/phases/wire/tsconfig.d.ts +3 -0
- package/dist/phases/wire/tsconfig.d.ts.map +1 -0
- package/dist/phases/wire/tsconfig.js +105 -0
- package/dist/phases/wire/tsconfig.js.map +1 -0
- package/dist/phases/wire/vite-config.d.ts +3 -0
- package/dist/phases/wire/vite-config.d.ts.map +1 -0
- package/dist/phases/wire/vite-config.js +46 -0
- package/dist/phases/wire/vite-config.js.map +1 -0
- package/dist/prompts.d.ts +34 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +49 -0
- package/dist/prompts.js.map +1 -0
- package/dist/runner.d.ts +5 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +91 -0
- package/dist/runner.js.map +1 -0
- package/dist/state.d.ts +18 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +68 -0
- package/dist/state.js.map +1 -0
- package/dist/templates/byline/admin.config.ts +41 -0
- package/dist/templates/byline/i18n.ts +47 -0
- package/dist/templates/byline/routes.ts +28 -0
- package/dist/templates/byline/seed.ts +19 -0
- package/dist/templates/byline/seeds/admin.ts +62 -0
- package/dist/templates/byline/server.config.ts +92 -0
- package/dist/templates/byline-examples/admin.config.ts +74 -0
- package/dist/templates/byline-examples/blocks/photo-block.ts +59 -0
- package/dist/templates/byline-examples/blocks/richtext-block.ts +35 -0
- package/dist/templates/byline-examples/collections/doc-example-flat-locale-all.ts +373 -0
- package/dist/templates/byline-examples/collections/doc-example-flat-locale-en.ts +283 -0
- package/dist/templates/byline-examples/collections/doc-example-tree-locale-all.ts +278 -0
- package/dist/templates/byline-examples/collections/doc-example-tree-locale-en.ts +205 -0
- package/dist/templates/byline-examples/collections/docs/admin.tsx +204 -0
- package/dist/templates/byline-examples/collections/docs/components/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/docs/components/feature-formatter.tsx +10 -0
- package/dist/templates/byline-examples/collections/docs/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/docs/index.ts +10 -0
- package/dist/templates/byline-examples/collections/docs/schema.ts +209 -0
- package/dist/templates/byline-examples/collections/docs-categories/admin.tsx +78 -0
- package/dist/templates/byline-examples/collections/docs-categories/components/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/docs-categories/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/docs-categories/index.ts +10 -0
- package/dist/templates/byline-examples/collections/docs-categories/schema.ts +33 -0
- package/dist/templates/byline-examples/collections/media/admin.tsx +188 -0
- package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +330 -0
- package/dist/templates/byline-examples/collections/media/components/media-thumbnail.tsx +63 -0
- package/dist/templates/byline-examples/collections/media/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/media/index.ts +10 -0
- package/dist/templates/byline-examples/collections/media/schema.ts +157 -0
- package/dist/templates/byline-examples/collections/news/admin.tsx +192 -0
- package/dist/templates/byline-examples/collections/news/components/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/news/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/news/index.ts +10 -0
- package/dist/templates/byline-examples/collections/news/schema.ts +91 -0
- package/dist/templates/byline-examples/collections/news-categories/admin.tsx +78 -0
- package/dist/templates/byline-examples/collections/news-categories/components/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/news-categories/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/news-categories/index.ts +10 -0
- package/dist/templates/byline-examples/collections/news-categories/schema.ts +33 -0
- package/dist/templates/byline-examples/collections/pages/admin.tsx +183 -0
- package/dist/templates/byline-examples/collections/pages/components/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/pages/hooks/.gitkeep +0 -0
- package/dist/templates/byline-examples/collections/pages/index.ts +10 -0
- package/dist/templates/byline-examples/collections/pages/schema.ts +96 -0
- package/dist/templates/byline-examples/components/length-indicator.tsx +138 -0
- package/dist/templates/byline-examples/components/pill.tsx +38 -0
- package/dist/templates/byline-examples/components/summary-length.tsx +39 -0
- package/dist/templates/byline-examples/fields/available-languages-field.ts +90 -0
- package/dist/templates/byline-examples/fields/lexical-richtext-compact.ts +88 -0
- package/dist/templates/byline-examples/i18n.ts +47 -0
- package/dist/templates/byline-examples/routes.ts +28 -0
- package/dist/templates/byline-examples/scripts/regenerate-media.ts +275 -0
- package/dist/templates/byline-examples/seed.ts +25 -0
- package/dist/templates/byline-examples/seeds/admin.ts +62 -0
- package/dist/templates/byline-examples/seeds/doc-categories.ts +71 -0
- package/dist/templates/byline-examples/seeds/docs.ts +293 -0
- package/dist/templates/byline-examples/seeds/news-categories.ts +71 -0
- package/dist/templates/byline-examples/server.config.ts +179 -0
- package/dist/templates/host/vite.config.ts +41 -0
- package/dist/templates/migrations/0000_condemned_kronos.sql +324 -0
- package/dist/templates/migrations/0001_sudden_phantom_reporter.sql +1 -0
- package/dist/templates/migrations/meta/0000_snapshot.json +2793 -0
- package/dist/templates/migrations/meta/0001_snapshot.json +2799 -0
- package/dist/templates/migrations/meta/_journal.json +20 -0
- package/dist/templates/routes/(byline)/admin/account/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/collections/$collection/$id/api.tsx +16 -0
- package/dist/templates/routes/(byline)/admin/collections/$collection/$id/history.tsx +19 -0
- package/dist/templates/routes/(byline)/admin/collections/$collection/$id/index.tsx +16 -0
- package/dist/templates/routes/(byline)/admin/collections/$collection/create.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/collections/$collection/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/permissions/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/roles/$id/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/roles/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/route.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/users/$id/index.tsx +11 -0
- package/dist/templates/routes/(byline)/admin/users/index.tsx +11 -0
- package/dist/templates/routes/(byline)/sign-in.tsx +11 -0
- package/dist/templates/ui-byline/blocks/photo-block/index.tsx +80 -0
- package/dist/templates/ui-byline/blocks/richtext-block/index.tsx +46 -0
- package/dist/templates/ui-byline/components/admonition/index.tsx +40 -0
- package/dist/templates/ui-byline/components/code/code-serializer.tsx +20 -0
- package/dist/templates/ui-byline/components/code/code.tsx +50 -0
- package/dist/templates/ui-byline/components/code/index.module.scss +137 -0
- package/dist/templates/ui-byline/components/code/index.ts +2 -0
- package/dist/templates/ui-byline/components/code/types.ts +5 -0
- package/dist/templates/ui-byline/components/code/utils.ts +20 -0
- package/dist/templates/ui-byline/components/heading-anchor/heading-anchor.tsx +69 -0
- package/dist/templates/ui-byline/components/heading-anchor/index.ts +1 -0
- package/dist/templates/ui-byline/components/heading-anchor/utils.ts +15 -0
- package/dist/templates/ui-byline/components/inline-image/index.tsx +109 -0
- package/dist/templates/ui-byline/components/layout/index.tsx +63 -0
- package/dist/templates/ui-byline/components/link/lang-link.tsx +70 -0
- package/dist/templates/ui-byline/components/link/link-field.tsx +298 -0
- package/dist/templates/ui-byline/components/link/link-lexical.tsx +191 -0
- package/dist/templates/ui-byline/components/list/index.ts +2 -0
- package/dist/templates/ui-byline/components/list/list-item.tsx +32 -0
- package/dist/templates/ui-byline/components/list/list.tsx +17 -0
- package/dist/templates/ui-byline/components/responsive-image/index.tsx +205 -0
- package/dist/templates/ui-byline/components/richtext-lexical/index.tsx +31 -0
- package/dist/templates/ui-byline/components/richtext-lexical/serialize/index.tsx +249 -0
- package/dist/templates/ui-byline/components/richtext-lexical/serialize/richtext-node-formats.ts +66 -0
- package/dist/templates/ui-byline/components/richtext-lexical/serialize/types.ts +48 -0
- package/dist/templates/ui-byline/components/richtext-lexical/serialize/utils.ts +15 -0
- package/dist/templates/ui-byline/components/table-cell/index.tsx +36 -0
- package/dist/templates/ui-byline/components/vimeo/index.tsx +21 -0
- package/dist/templates/ui-byline/components/youtube/index.tsx +22 -0
- package/dist/templates/ui-byline/render-blocks.tsx +71 -0
- package/dist/templates/ui-byline/types/i18n.ts +14 -0
- package/dist/templates/ui-byline/utils/image-sources.ts +102 -0
- package/dist/templates/ui-byline/utils/to-kebab-case.ts +5 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/diff.d.ts +4 -0
- package/dist/ui/diff.d.ts.map +1 -0
- package/dist/ui/diff.js +23 -0
- package/dist/ui/diff.js.map +1 -0
- package/dist/ui/grid.d.ts +7 -0
- package/dist/ui/grid.d.ts.map +1 -0
- package/dist/ui/grid.js +24 -0
- package/dist/ui/grid.js.map +1 -0
- package/dist/ui/logger.d.ts +14 -0
- package/dist/ui/logger.d.ts.map +1 -0
- package/dist/ui/logger.js +30 -0
- package/dist/ui/logger.js.map +1 -0
- package/dist/ui/snippet.d.ts +2 -0
- package/dist/ui/snippet.d.ts.map +1 -0
- package/dist/ui/snippet.js +7 -0
- package/dist/ui/snippet.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Regenerate media — example Byline script.
|
|
11
|
+
*
|
|
12
|
+
* Iterates the `media` collection, re-uploads each original through the
|
|
13
|
+
* core upload service so Sharp regenerates the variant set declared in
|
|
14
|
+
* the current schema, then updates the document with the fresh
|
|
15
|
+
* `storedFile` via `@byline/client`.
|
|
16
|
+
*
|
|
17
|
+
* Run after changing the variant set (e.g. switching `format: 'webp'`
|
|
18
|
+
* to `'avif'` in `byline/collections/media/schema.ts`) to bring existing
|
|
19
|
+
* assets in line with the new pipeline:
|
|
20
|
+
*
|
|
21
|
+
* pnpm tsx --env-file=.env byline/scripts/regenerate-media.ts
|
|
22
|
+
*
|
|
23
|
+
* The script orchestrates the same two-step flow the admin UI uses
|
|
24
|
+
* for an existing document — upload (createDocument: false) followed by
|
|
25
|
+
* a document update — but as a single "one-shot" Client SDK pass per
|
|
26
|
+
* media item. Variants and the original written under fresh paths;
|
|
27
|
+
* the previous original + variant files are deleted once the document
|
|
28
|
+
* has been re-pointed at the new value.
|
|
29
|
+
*
|
|
30
|
+
* Bypass-MIME images (SVG / GIF) are passed through untouched — Sharp
|
|
31
|
+
* does not produce variants for them, so the only effect is a new
|
|
32
|
+
* storagePath on a re-stored copy.
|
|
33
|
+
*
|
|
34
|
+
* Status preservation: `updateDocument` always stamps new versions with
|
|
35
|
+
* the workflow's default status (`getDefaultStatus`, i.e. the first
|
|
36
|
+
* declared status — `'draft'` for media). After the update we step the
|
|
37
|
+
* new version forward through `changeStatus` until it lands in the same
|
|
38
|
+
* slot the original occupied. Skipping this leaves the previously-
|
|
39
|
+
* published version (with the old variant set) as what
|
|
40
|
+
* `current_published_documents` returns to public readers.
|
|
41
|
+
*
|
|
42
|
+
* Currently requires the local storage provider — pulls bytes back via
|
|
43
|
+
* the provider's `uploadDir`. An S3-capable variant would add a
|
|
44
|
+
* `download(storagePath)` to `IStorageProvider` and route through that.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import 'dotenv/config'
|
|
48
|
+
import '../server.config.js'
|
|
49
|
+
|
|
50
|
+
import fs from 'node:fs'
|
|
51
|
+
import path from 'node:path'
|
|
52
|
+
|
|
53
|
+
import { createSuperAdminContext } from '@byline/auth'
|
|
54
|
+
import { type CollectionHandle, createBylineClient } from '@byline/client'
|
|
55
|
+
import {
|
|
56
|
+
type FieldUploadContext,
|
|
57
|
+
getCollectionDefinition,
|
|
58
|
+
getServerConfig,
|
|
59
|
+
getWorkflowStatuses,
|
|
60
|
+
type StoredFileValue,
|
|
61
|
+
} from '@byline/core'
|
|
62
|
+
import { uploadField as coreUploadField } from '@byline/core/services'
|
|
63
|
+
import { extractImageMeta, generateImageVariants, isBypassMimeType } from '@byline/storage-local'
|
|
64
|
+
|
|
65
|
+
const COLLECTION_PATH = 'media'
|
|
66
|
+
const FIELD_NAME = 'image'
|
|
67
|
+
|
|
68
|
+
async function run(): Promise<void> {
|
|
69
|
+
const config = getServerConfig()
|
|
70
|
+
const definition = getCollectionDefinition(COLLECTION_PATH)
|
|
71
|
+
if (!definition) {
|
|
72
|
+
throw new Error(`Collection '${COLLECTION_PATH}' is not registered.`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const storage = config.storage
|
|
76
|
+
if (!storage) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`regenerate-media: no storage provider configured on ServerConfig. ` +
|
|
79
|
+
`Set storage in byline/server.config.ts.`
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
// Local-only escape hatch — we read bytes back from disk via uploadDir.
|
|
83
|
+
// Lift this when IStorageProvider grows a download primitive.
|
|
84
|
+
const uploadDir = (storage as { uploadDir?: unknown }).uploadDir
|
|
85
|
+
if (typeof uploadDir !== 'string') {
|
|
86
|
+
throw new Error(
|
|
87
|
+
'regenerate-media currently requires the local storage provider ' +
|
|
88
|
+
'(no remote download primitive on IStorageProvider yet).'
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const requestContext = createSuperAdminContext({ id: 'regenerate-media-script' })
|
|
93
|
+
const client = createBylineClient({ config, requestContext })
|
|
94
|
+
|
|
95
|
+
const { id: collectionId, version: collectionVersion } =
|
|
96
|
+
await client.resolveCollectionRecord(COLLECTION_PATH)
|
|
97
|
+
|
|
98
|
+
const handle = client.collection(COLLECTION_PATH)
|
|
99
|
+
|
|
100
|
+
// Snapshot the full set up-front. Each update bumps `updated_at` and
|
|
101
|
+
// reorders the default sort, so paging through a moving window would
|
|
102
|
+
// either skip or re-visit rows. Capture `status` so we can restore
|
|
103
|
+
// each doc to its original workflow slot after the regenerated draft
|
|
104
|
+
// is written (see `restoreStatus` below).
|
|
105
|
+
const allDocs: {
|
|
106
|
+
id: string
|
|
107
|
+
path: string
|
|
108
|
+
status: string
|
|
109
|
+
fields: Record<string, any>
|
|
110
|
+
}[] = []
|
|
111
|
+
const pageSize = 100
|
|
112
|
+
for (let page = 1; ; page++) {
|
|
113
|
+
const result = await handle.find({
|
|
114
|
+
page,
|
|
115
|
+
pageSize,
|
|
116
|
+
status: 'any',
|
|
117
|
+
_bypassBeforeRead: true,
|
|
118
|
+
})
|
|
119
|
+
for (const d of result.docs) {
|
|
120
|
+
allDocs.push({
|
|
121
|
+
id: d.id,
|
|
122
|
+
path: d.path,
|
|
123
|
+
status: d.status,
|
|
124
|
+
fields: d.fields as Record<string, any>,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
if (result.docs.length < pageSize) break
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(`regenerate-media: found ${allDocs.length} document(s) in '${COLLECTION_PATH}'.`)
|
|
131
|
+
|
|
132
|
+
const baseUploadCtx: Omit<FieldUploadContext, 'requestContext'> = {
|
|
133
|
+
db: config.db,
|
|
134
|
+
definition,
|
|
135
|
+
collectionId,
|
|
136
|
+
collectionVersion,
|
|
137
|
+
collectionPath: COLLECTION_PATH,
|
|
138
|
+
fieldName: FIELD_NAME,
|
|
139
|
+
storage,
|
|
140
|
+
logger: client.logger,
|
|
141
|
+
defaultLocale: config.i18n.content.defaultLocale,
|
|
142
|
+
slugifier: config.slugifier,
|
|
143
|
+
imageProcessor: {
|
|
144
|
+
extractMeta: extractImageMeta,
|
|
145
|
+
isBypassMimeType,
|
|
146
|
+
generateVariants: async ({ buffer, mimeType, storedFile, storage, upload, logger }) => {
|
|
147
|
+
const dir = (storage as { uploadDir?: unknown }).uploadDir as string
|
|
148
|
+
const absoluteOriginalPath = path.join(dir, storedFile.storagePath)
|
|
149
|
+
return generateImageVariants(
|
|
150
|
+
buffer,
|
|
151
|
+
mimeType,
|
|
152
|
+
absoluteOriginalPath,
|
|
153
|
+
dir,
|
|
154
|
+
upload.sizes ?? [],
|
|
155
|
+
logger
|
|
156
|
+
)
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let processed = 0
|
|
162
|
+
let skipped = 0
|
|
163
|
+
|
|
164
|
+
for (const doc of allDocs) {
|
|
165
|
+
const image = doc.fields[FIELD_NAME] as StoredFileValue | undefined | null
|
|
166
|
+
if (image == null || !image.storagePath) {
|
|
167
|
+
console.log(` - skip ${doc.id} (${doc.path}) — no image value`)
|
|
168
|
+
skipped += 1
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const sourceAbsolutePath = path.join(uploadDir, image.storagePath)
|
|
173
|
+
let buffer: Buffer
|
|
174
|
+
try {
|
|
175
|
+
buffer = await fs.promises.readFile(sourceAbsolutePath)
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error(
|
|
178
|
+
` ! skip ${doc.id} (${doc.path}) — failed to read original at ${sourceAbsolutePath}:`,
|
|
179
|
+
err
|
|
180
|
+
)
|
|
181
|
+
skipped += 1
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const uploadResult = await coreUploadField(
|
|
186
|
+
{ ...baseUploadCtx, requestContext },
|
|
187
|
+
{
|
|
188
|
+
buffer,
|
|
189
|
+
originalFilename: image.originalFilename || image.filename,
|
|
190
|
+
mimeType: image.mimeType,
|
|
191
|
+
fileSize: buffer.byteLength,
|
|
192
|
+
shouldCreateDocument: false,
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
const nextFields = { ...doc.fields, [FIELD_NAME]: uploadResult.storedFile }
|
|
197
|
+
await handle.update(doc.id, nextFields)
|
|
198
|
+
|
|
199
|
+
// `updateDocument` always stamps the new version with the workflow's
|
|
200
|
+
// default status (the first key — 'draft' for media), so without this
|
|
201
|
+
// step the public read path keeps returning the prior published
|
|
202
|
+
// version with the old variant set. Walk the new version forward to
|
|
203
|
+
// the slot the doc originally occupied.
|
|
204
|
+
await restoreStatus(handle, definition, doc.id, doc.status)
|
|
205
|
+
|
|
206
|
+
// Best-effort orphan cleanup. Failures are non-fatal — the new
|
|
207
|
+
// value is already persisted, the doc is consistent, the unused
|
|
208
|
+
// bytes are just dead weight on disk.
|
|
209
|
+
const orphans: string[] = [
|
|
210
|
+
image.storagePath,
|
|
211
|
+
...(image.variants ?? []).map((v) => v.storagePath).filter((p): p is string => Boolean(p)),
|
|
212
|
+
]
|
|
213
|
+
for (const orphan of orphans) {
|
|
214
|
+
try {
|
|
215
|
+
await storage.delete(orphan)
|
|
216
|
+
} catch (err) {
|
|
217
|
+
console.warn(` ! failed to delete orphan ${orphan}:`, err)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const variantSummary =
|
|
222
|
+
uploadResult.storedFile.variants?.map((v) => `${v.name}:${v.format ?? '?'}`).join(', ') ??
|
|
223
|
+
'(none)'
|
|
224
|
+
console.log(
|
|
225
|
+
` - ${doc.id} (${doc.path}) → ${uploadResult.storedFile.storagePath} [${variantSummary}]`
|
|
226
|
+
)
|
|
227
|
+
processed += 1
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log(
|
|
231
|
+
`regenerate-media: done. processed=${processed}, skipped=${skipped}, total=${allDocs.length}.`
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Walk the workflow ladder forward from the new version's status (the
|
|
237
|
+
* workflow's first slot, set by `updateDocument`) to `targetStatus`.
|
|
238
|
+
*
|
|
239
|
+
* `validateStatusTransition` allows ±1-step moves and reset-to-first, so
|
|
240
|
+
* jumping straight from 'draft' to 'archived' is rejected — we step
|
|
241
|
+
* through 'published' on the way. A target equal to the current status
|
|
242
|
+
* is a no-op (transition validation accepts identical-status moves but
|
|
243
|
+
* we skip the work).
|
|
244
|
+
*
|
|
245
|
+
* Throws if `targetStatus` is not declared on the collection's workflow,
|
|
246
|
+
* since that signals a stale snapshot or a workflow definition that has
|
|
247
|
+
* been edited since the docs were created.
|
|
248
|
+
*/
|
|
249
|
+
async function restoreStatus(
|
|
250
|
+
handle: CollectionHandle,
|
|
251
|
+
definition: Parameters<typeof getWorkflowStatuses>[0],
|
|
252
|
+
documentId: string,
|
|
253
|
+
targetStatus: string
|
|
254
|
+
): Promise<void> {
|
|
255
|
+
const statuses = getWorkflowStatuses(definition).map((s) => s.name)
|
|
256
|
+
const targetIndex = statuses.indexOf(targetStatus)
|
|
257
|
+
if (targetIndex === -1) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`restoreStatus: status '${targetStatus}' is not declared on collection ` +
|
|
260
|
+
`'${definition.path}' (declared: ${statuses.join(', ')}).`
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
// The new version is always at index 0 (workflow default). Anything at
|
|
264
|
+
// index > 0 needs that many forward transitions.
|
|
265
|
+
for (let i = 1; i <= targetIndex; i++) {
|
|
266
|
+
await handle.changeStatus(documentId, statuses[i] as string)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
run()
|
|
271
|
+
.then(() => process.exit(0))
|
|
272
|
+
.catch((err) => {
|
|
273
|
+
console.error('regenerate-media failed:', err)
|
|
274
|
+
process.exit(1)
|
|
275
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Initialize Byline config by importing the server config
|
|
10
|
+
import 'dotenv/config'
|
|
11
|
+
import './server.config.js'
|
|
12
|
+
|
|
13
|
+
import { seedAdmin } from './seeds/admin.js'
|
|
14
|
+
import { seedDocsCategories } from './seeds/doc-categories.js'
|
|
15
|
+
import { seedDocs } from './seeds/docs.js'
|
|
16
|
+
import { seedNewsCategories } from './seeds/news-categories.js'
|
|
17
|
+
|
|
18
|
+
async function run() {
|
|
19
|
+
await seedAdmin()
|
|
20
|
+
await seedDocsCategories()
|
|
21
|
+
await seedNewsCategories()
|
|
22
|
+
await seedDocs()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
run()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Super-admin bootstrap seed.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent. Reads credentials from env (`BYLINE_SUPERADMIN_EMAIL`,
|
|
13
|
+
* `BYLINE_SUPERADMIN_PASSWORD`) and calls the built-in
|
|
14
|
+
* `seedSuperAdmin` helper from `@byline/admin/admin-users` against an
|
|
15
|
+
* `AdminStore` built on the application's single connection pool.
|
|
16
|
+
*
|
|
17
|
+
* The admin account it produces:
|
|
18
|
+
* - `is_super_admin: true` — bypasses every ability check
|
|
19
|
+
* - `is_enabled: true` — ready to sign in immediately
|
|
20
|
+
* - `is_email_verified: true` — skip the verification gate for bootstrap
|
|
21
|
+
*
|
|
22
|
+
* In any non-dev deployment: change the credentials in env, run the seed
|
|
23
|
+
* once, then immediately change the password from inside the admin UI.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { AdminStore } from '@byline/admin'
|
|
27
|
+
import { seedSuperAdmin } from '@byline/admin/admin-users'
|
|
28
|
+
import { getBylineCore } from '@byline/core'
|
|
29
|
+
|
|
30
|
+
export async function seedAdmin() {
|
|
31
|
+
const email = process.env.BYLINE_SUPERADMIN_EMAIL
|
|
32
|
+
const password = process.env.BYLINE_SUPERADMIN_PASSWORD
|
|
33
|
+
|
|
34
|
+
if (!email || !password) {
|
|
35
|
+
console.warn(
|
|
36
|
+
'Skipping admin seed: BYLINE_SUPERADMIN_EMAIL and BYLINE_SUPERADMIN_PASSWORD ' +
|
|
37
|
+
'must both be set (see .env.example).'
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const bylineCore = getBylineCore<AdminStore>()
|
|
43
|
+
|
|
44
|
+
if (!bylineCore.adminStore) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
'seedAdmin: bylineCore.adminStore is not configured. ' +
|
|
47
|
+
'Pass adminStore to initBylineCore() in byline/server.config.ts.'
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = await seedSuperAdmin(bylineCore.adminStore, { email, password })
|
|
52
|
+
|
|
53
|
+
const parts: string[] = []
|
|
54
|
+
if (result.created.role) parts.push('role')
|
|
55
|
+
if (result.created.user) parts.push('user')
|
|
56
|
+
if (result.created.assignment) parts.push('assignment')
|
|
57
|
+
if (parts.length === 0) {
|
|
58
|
+
console.log(`Super-admin already present (${email}) — no changes.`)
|
|
59
|
+
} else {
|
|
60
|
+
console.log(`Super-admin seed: created ${parts.join(', ')} for ${email}.`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { getCollectionDefinition, getDefaultStatus, getServerConfig, slugify } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
const categories = [
|
|
12
|
+
{
|
|
13
|
+
name: { en: 'Whitepaper' },
|
|
14
|
+
description: { en: 'In-depth articles and research papers.' },
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: { en: 'Tutorial' },
|
|
18
|
+
description: { en: 'Step-by-step guides and how-tos.' },
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: { en: 'Framework' },
|
|
22
|
+
description: { en: 'Libraries, frameworks, and toolkits.' },
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
export async function seedDocsCategories() {
|
|
27
|
+
const db = getServerConfig().db
|
|
28
|
+
|
|
29
|
+
const collectionDefinition = getCollectionDefinition('docs-categories')
|
|
30
|
+
|
|
31
|
+
if (!collectionDefinition) {
|
|
32
|
+
console.error('Collection definition not found for "docs-categories"')
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `initBylineCore` already registered the collection row via
|
|
37
|
+
// `ensureCollections()` when byline/server.config was imported, so we
|
|
38
|
+
// look the row up rather than re-inserting (which would violate the
|
|
39
|
+
// unique-path constraint).
|
|
40
|
+
const existing = await db.queries.collections.getCollectionByPath('docs-categories')
|
|
41
|
+
if (!existing) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"seedDocsCategories: expected the 'docs-categories' collection to be registered by initBylineCore()"
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const categoriesCollection = {
|
|
48
|
+
id: existing.id as string,
|
|
49
|
+
name: existing.path as string,
|
|
50
|
+
version: (existing.version as number | undefined) ?? 1,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`Seeding into Docs Categories collection (${categoriesCollection.name})`)
|
|
54
|
+
|
|
55
|
+
for (const category of categories) {
|
|
56
|
+
const seedPath = slugify(category.name.en, {
|
|
57
|
+
locale: 'en',
|
|
58
|
+
collectionPath: 'docs-categories',
|
|
59
|
+
})
|
|
60
|
+
await db.commands.documents.createDocumentVersion({
|
|
61
|
+
collectionId: categoriesCollection.id,
|
|
62
|
+
collectionVersion: categoriesCollection.version,
|
|
63
|
+
collectionConfig: collectionDefinition,
|
|
64
|
+
action: 'create',
|
|
65
|
+
documentData: category,
|
|
66
|
+
path: seedPath,
|
|
67
|
+
status: getDefaultStatus(collectionDefinition),
|
|
68
|
+
})
|
|
69
|
+
console.log(` - seeded category: ${category.name.en} (${seedPath})`)
|
|
70
|
+
}
|
|
71
|
+
}
|