@jant/core 0.3.45 → 0.3.47
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/bin/commands/db/execute-file.js +12 -4
- package/bin/commands/db/rehearse.js +2 -2
- package/bin/commands/export.js +12 -4
- package/bin/commands/import-site.js +99 -305
- package/bin/commands/migrate.js +36 -69
- package/bin/commands/reset-password.js +10 -4
- package/bin/commands/site/export.js +59 -248
- package/bin/commands/site/snapshot/export.js +58 -45
- package/bin/commands/site/snapshot/import.js +104 -52
- package/bin/lib/node-env.js +100 -0
- package/bin/lib/runtime-target.js +64 -0
- package/bin/lib/site-snapshot.js +185 -54
- package/bin/lib/sql-export.js +19 -2
- package/dist/{app-C-L7wL6o.js → app-3REcR-3U.js} +332 -190
- package/dist/app-B67XOEyo.js +6 -0
- package/dist/client/.vite/manifest.json +2 -2
- package/dist/client/_assets/{client-auth-Dcon89Av.js → client-auth-Ce5WEAVS.js} +236 -183
- package/dist/client/_assets/client-s71Js1Cu.css +2 -0
- package/dist/{github-sync-CQ1x271f.js → export-ZBlfKSKm.js} +12 -439
- package/dist/github-sync-C593r22F.js +4 -0
- package/dist/github-sync-bL1hnx3Q.js +428 -0
- package/dist/index.js +3 -2
- package/dist/node.js +5 -4
- package/package.json +3 -2
- package/src/__tests__/helpers/export-fixtures.ts +0 -1
- package/src/__tests__/import-site-command.test.ts +18 -0
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -0
- package/src/client/components/__tests__/jant-settings-general.test.ts +70 -0
- package/src/client/components/jant-compose-dialog.ts +7 -6
- package/src/client/components/jant-compose-editor.ts +6 -5
- package/src/client/components/jant-settings-general.ts +164 -22
- package/src/client/components/settings-types.ts +4 -6
- package/src/client/random-uuid.ts +23 -0
- package/src/client-auth.ts +1 -1
- package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
- package/src/db/__tests__/migration-rehearsal.test.ts +2 -5
- package/src/db/backfills/0004_register_apple_touch_media_rows.sql +65 -0
- package/src/db/migrations/0021_thankful_phalanx.sql +16 -0
- package/src/db/migrations/meta/0021_snapshot.json +2121 -0
- package/src/db/migrations/meta/_journal.json +7 -0
- package/src/db/migrations/pg/0019_gray_natasha_romanoff.sql +20 -0
- package/src/db/migrations/pg/meta/0019_snapshot.json +2718 -0
- package/src/db/migrations/pg/meta/_journal.json +7 -0
- package/src/db/pg/schema.ts +21 -26
- package/src/db/rehearsal-fixtures/demo-current.json +1 -1
- package/src/db/schema.ts +16 -20
- package/src/i18n/__tests__/middleware.test.ts +43 -1
- package/src/i18n/coverage.generated.ts +17 -0
- package/src/i18n/i18n.ts +18 -2
- package/src/i18n/index.ts +3 -0
- package/src/i18n/locales/settings/en.po +16 -11
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +17 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +16 -11
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/i18n/locales.ts +84 -2
- package/src/i18n/middleware.ts +25 -16
- package/src/i18n/supported-locales.ts +153 -0
- package/src/lib/__tests__/csp-builder.test.ts +19 -2
- package/src/lib/__tests__/feed.test.ts +242 -1
- package/src/lib/__tests__/post-meta.test.ts +0 -1
- package/src/lib/__tests__/view.test.ts +0 -1
- package/src/lib/csp-builder.ts +28 -10
- package/src/lib/feed.ts +153 -3
- package/src/middleware/__tests__/secure-headers.test.ts +89 -0
- package/src/middleware/auth.ts +1 -1
- package/src/middleware/secure-headers.ts +47 -1
- package/src/node/__tests__/cli-runtime-target.test.ts +110 -2
- package/src/node/__tests__/cli-site-snapshot.test.ts +308 -13
- package/src/node/__tests__/cli-site-token-env.test.ts +2 -7
- package/src/node/__tests__/cli-snapshot-meta.test.ts +85 -0
- package/src/node/__tests__/cli-sql-export.test.ts +49 -0
- package/src/node/index.ts +1 -0
- package/src/preset.css +8 -2
- package/src/routes/api/__tests__/settings.test.ts +3 -2
- package/src/routes/api/github-sync.tsx +1 -1
- package/src/routes/api/settings.ts +4 -1
- package/src/routes/auth/signin.tsx +6 -0
- package/src/routes/pages/archive.tsx +4 -2
- package/src/services/__tests__/post.test.ts +19 -19
- package/src/services/__tests__/search.test.ts +0 -1
- package/src/services/__tests__/settings.test.ts +22 -3
- package/src/services/bootstrap.ts +7 -3
- package/src/services/collection.ts +3 -3
- package/src/services/export.ts +0 -3
- package/src/services/navigation.ts +0 -2
- package/src/services/path.ts +1 -38
- package/src/services/post.ts +32 -66
- package/src/services/search.ts +0 -6
- package/src/services/settings.ts +47 -6
- package/src/services/site-admin.ts +6 -1
- package/src/styles/ui.css +12 -23
- package/src/types/entities.ts +0 -1
- package/src/ui/color-themes.ts +1 -1
- package/src/ui/dash/settings/GeneralContent.tsx +17 -19
- package/src/ui/dash/settings/SettingsRootContent.tsx +17 -28
- package/src/ui/feed/NoteCard.tsx +1 -11
- package/src/ui/feed/__tests__/timeline-cards.test.ts +1 -1
- package/src/ui/pages/HomePage.tsx +1 -4
- package/src/ui/pages/PostPage.tsx +2 -0
- package/bin/commands/collections.js +0 -268
- package/bin/commands/media.js +0 -302
- package/bin/commands/posts.js +0 -262
- package/bin/commands/search.js +0 -53
- package/bin/commands/settings.js +0 -93
- package/bin/lib/http-api.js +0 -223
- package/bin/lib/media-upload.js +0 -206
- package/dist/app-Hvqe7Ks_.js +0 -5
- package/dist/client/_assets/client-DDs6NzB3.css +0 -2
- package/src/__tests__/bin/content-cli.test.ts +0 -179
- package/src/__tests__/bin/media-cli.test.ts +0 -192
- /package/dist/{github-api-BkRWnqMx.js → github-api-Bh0PH3zr.js} +0 -0
- /package/dist/{github-app-WeadXMb8.js → github-app-D0GvNnqp.js} +0 -0
package/bin/commands/migrate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { resolve, dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
@@ -10,12 +10,15 @@ import {
|
|
|
10
10
|
applyPgBackfills,
|
|
11
11
|
} from "../lib/migration-runner.js";
|
|
12
12
|
import { loadNodeRuntime } from "../lib/load-node-runtime.js";
|
|
13
|
+
import { loadNodeEnvFile } from "../lib/node-env.js";
|
|
13
14
|
import { openNodeSqlite, resolveDatabaseDialect } from "../lib/node-sqlite.js";
|
|
14
15
|
import {
|
|
16
|
+
bootstrapCliRuntime,
|
|
15
17
|
getCliRuntimeLabel,
|
|
16
|
-
resolveCliRuntime,
|
|
17
18
|
} from "../lib/runtime-target.js";
|
|
18
19
|
|
|
20
|
+
export { loadNodeEnvFile };
|
|
21
|
+
|
|
19
22
|
export function isMigrationDebugEnabled(env = process.env) {
|
|
20
23
|
return env.JANT_DEBUG_MIGRATE === "1";
|
|
21
24
|
}
|
|
@@ -42,38 +45,6 @@ export function describeNodeDatabaseTarget(databaseUrl) {
|
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
export function loadNodeEnvFile(envPath, env = process.env) {
|
|
46
|
-
const result = {
|
|
47
|
-
envPath,
|
|
48
|
-
found: false,
|
|
49
|
-
assignedKeys: [],
|
|
50
|
-
skippedKeys: [],
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const content = readFileSync(envPath, "utf8");
|
|
55
|
-
result.found = true;
|
|
56
|
-
for (const line of content.split("\n")) {
|
|
57
|
-
const trimmed = line.trim();
|
|
58
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
59
|
-
const eqIdx = trimmed.indexOf("=");
|
|
60
|
-
if (eqIdx < 1) continue;
|
|
61
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
62
|
-
const value = trimmed.slice(eqIdx + 1).trim();
|
|
63
|
-
if (key in env) {
|
|
64
|
-
result.skippedKeys.push(key);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
env[key] = value;
|
|
68
|
-
result.assignedKeys.push(key);
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
// .env.node not found
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
48
|
function logMigrationDebug(message) {
|
|
78
49
|
console.log(`[jant:migrate] ${message}`);
|
|
79
50
|
}
|
|
@@ -222,7 +193,7 @@ export async function run(argv) {
|
|
|
222
193
|
console.log(" --local Force local D1 instead of DATABASE_URL");
|
|
223
194
|
console.log(" --remote Run against remote D1");
|
|
224
195
|
console.log(
|
|
225
|
-
" --node Force Node runtime
|
|
196
|
+
" --node Force Node runtime even if DATABASE_URL is unset",
|
|
226
197
|
);
|
|
227
198
|
console.log(
|
|
228
199
|
" --config Wrangler config file (default: wrangler.toml)",
|
|
@@ -232,20 +203,20 @@ export async function run(argv) {
|
|
|
232
203
|
console.log(" --persist-to Local D1 state directory override");
|
|
233
204
|
console.log("");
|
|
234
205
|
console.log(
|
|
235
|
-
"
|
|
206
|
+
"`.env.node` next to your project (or in packages/core/) is auto-loaded.",
|
|
207
|
+
);
|
|
208
|
+
console.log(
|
|
209
|
+
"If DATABASE_URL or DATA_DIR is then set and no runtime flag is passed,",
|
|
236
210
|
);
|
|
211
|
+
console.log("this command uses the Node database runtime.");
|
|
237
212
|
process.exit(0);
|
|
238
213
|
}
|
|
239
214
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
nodeEnvLoadResult = loadNodeEnvFile(envPath);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const runtime = resolveCliRuntime(values);
|
|
215
|
+
// bootstrapCliRuntime auto-loads `.env.node` (so DATABASE_URL/DATA_DIR
|
|
216
|
+
// resolve without sourcing the file) and prints a one-line banner with
|
|
217
|
+
// the chosen target.
|
|
218
|
+
const { runtime, envLoad } = bootstrapCliRuntime(values);
|
|
219
|
+
const nodeEnvLoadResult = envLoad;
|
|
249
220
|
const debugMigrate = isMigrationDebugEnabled();
|
|
250
221
|
const databaseUrl = process.env.DATABASE_URL ?? "";
|
|
251
222
|
const databaseDialect =
|
|
@@ -255,30 +226,26 @@ export async function run(argv) {
|
|
|
255
226
|
|
|
256
227
|
if (debugMigrate) {
|
|
257
228
|
logMigrationDebug(`cli.runtime=${runtime}`);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
`cli.node_env.database_url_source=${databaseUrlSource}`,
|
|
279
|
-
);
|
|
280
|
-
logMigrationDebug(`cli.node_env.data_dir_source=${dataDirSource}`);
|
|
281
|
-
}
|
|
229
|
+
const databaseUrlSource = nodeEnvLoadResult?.assignedKeys.includes(
|
|
230
|
+
"DATABASE_URL",
|
|
231
|
+
)
|
|
232
|
+
? ".env.node"
|
|
233
|
+
: process.env.DATABASE_URL
|
|
234
|
+
? "process.env"
|
|
235
|
+
: "<unset>";
|
|
236
|
+
const dataDirSource = nodeEnvLoadResult?.assignedKeys.includes("DATA_DIR")
|
|
237
|
+
? ".env.node"
|
|
238
|
+
: process.env.DATA_DIR
|
|
239
|
+
? "process.env"
|
|
240
|
+
: "<unset>";
|
|
241
|
+
const envPath = nodeEnvLoadResult?.envPath ?? "<unknown>";
|
|
242
|
+
const envState = nodeEnvLoadResult?.found ? "loaded" : "missing";
|
|
243
|
+
const skippedKeys = nodeEnvLoadResult?.skippedKeys.join(", ") || "<none>";
|
|
244
|
+
logMigrationDebug(`cli.node_env.path=${envPath}`);
|
|
245
|
+
logMigrationDebug(`cli.node_env.state=${envState}`);
|
|
246
|
+
logMigrationDebug(`cli.node_env.skipped_keys=${skippedKeys}`);
|
|
247
|
+
logMigrationDebug(`cli.node_env.database_url_source=${databaseUrlSource}`);
|
|
248
|
+
logMigrationDebug(`cli.node_env.data_dir_source=${dataDirSource}`);
|
|
282
249
|
|
|
283
250
|
if (runtime === "node") {
|
|
284
251
|
logMigrationDebug(`cli.node.dialect=${databaseDialect ?? "<unset>"}`);
|
|
@@ -4,8 +4,8 @@ import { executeD1, queryD1 } from "../lib/d1-query.js";
|
|
|
4
4
|
import { openNodeDatabase } from "../lib/node-database.js";
|
|
5
5
|
import { loadNodeRuntime } from "../lib/load-node-runtime.js";
|
|
6
6
|
import {
|
|
7
|
+
bootstrapCliRuntime,
|
|
7
8
|
getCliRuntimeLabel,
|
|
8
|
-
resolveCliRuntime,
|
|
9
9
|
} from "../lib/runtime-target.js";
|
|
10
10
|
import { resolveCliSite } from "../lib/site-selection.js";
|
|
11
11
|
|
|
@@ -18,6 +18,7 @@ export async function run(argv) {
|
|
|
18
18
|
env: { type: "string" },
|
|
19
19
|
host: { type: "string" },
|
|
20
20
|
local: { type: "boolean", default: false },
|
|
21
|
+
node: { type: "boolean", default: false },
|
|
21
22
|
"path-prefix": { type: "string" },
|
|
22
23
|
"persist-to": { type: "string" },
|
|
23
24
|
remote: { type: "boolean", default: false },
|
|
@@ -28,13 +29,14 @@ export async function run(argv) {
|
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
if (values.help) {
|
|
31
|
-
console.log("Usage: jant reset-password [--local | --remote]");
|
|
32
|
+
console.log("Usage: jant reset-password [--local | --remote | --node]");
|
|
32
33
|
console.log("");
|
|
33
34
|
console.log("Generate a password reset token (expires in 15 minutes).");
|
|
34
35
|
console.log("");
|
|
35
36
|
console.log("Options:");
|
|
36
37
|
console.log(" --local Force local D1 instead of DATABASE_URL");
|
|
37
38
|
console.log(" --remote Run against remote D1 database (default: local)");
|
|
39
|
+
console.log(" --node Force Node runtime even if DATABASE_URL is unset");
|
|
38
40
|
console.log(" --site Target site id");
|
|
39
41
|
console.log(" --host Target site host");
|
|
40
42
|
console.log(" --url Target site URL");
|
|
@@ -45,12 +47,16 @@ export async function run(argv) {
|
|
|
45
47
|
console.log(" --persist-to Local D1 state directory override");
|
|
46
48
|
console.log("");
|
|
47
49
|
console.log(
|
|
48
|
-
"
|
|
50
|
+
"`.env.node` next to your project (or in packages/core/) is auto-loaded.",
|
|
49
51
|
);
|
|
52
|
+
console.log(
|
|
53
|
+
"If DATABASE_URL or DATA_DIR is then set and no runtime flag is passed,",
|
|
54
|
+
);
|
|
55
|
+
console.log("this command uses the Node database runtime.");
|
|
50
56
|
process.exit(0);
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
const runtime =
|
|
59
|
+
const { runtime } = bootstrapCliRuntime(values);
|
|
54
60
|
|
|
55
61
|
const token = randomBytes(32).toString("hex");
|
|
56
62
|
const hash = createHash("sha256").update(token).digest("hex");
|
|
@@ -6,22 +6,8 @@ import {
|
|
|
6
6
|
CLI_API_TOKEN_ENV_VAR,
|
|
7
7
|
getCliApiToken,
|
|
8
8
|
} from "../../lib/cli-api-token.js";
|
|
9
|
-
import { openNodeDatabase } from "../../lib/node-database.js";
|
|
10
|
-
import { loadNodeRuntime } from "../../lib/load-node-runtime.js";
|
|
11
9
|
import { pullSiteExportZipBytes } from "../../lib/site-pull-media.js";
|
|
12
10
|
|
|
13
|
-
function describeLocalExportSource(input) {
|
|
14
|
-
if (input.siteUrl) {
|
|
15
|
-
return input.siteUrl;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (input.siteDomain?.host) {
|
|
19
|
-
return `https://${input.siteDomain.host}${input.siteDomain.pathPrefix || ""}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return `site "${input.site.key}"`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
11
|
async function exportRemoteSite(url, token) {
|
|
26
12
|
const response = await fetch(`${url.replace(/\/$/, "")}/api/export/hugo`, {
|
|
27
13
|
method: "POST",
|
|
@@ -83,189 +69,46 @@ function logPullProgress(event) {
|
|
|
83
69
|
}
|
|
84
70
|
}
|
|
85
71
|
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (sitePathPrefix && pathname.startsWith(`${sitePathPrefix}/`)) {
|
|
119
|
-
pathname = pathname.slice(sitePathPrefix.length + 1);
|
|
120
|
-
} else {
|
|
121
|
-
pathname = pathname.replace(/^\/+/, "");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!pathname.startsWith("media/") && !pathname.startsWith("favicon/")) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return pathname;
|
|
129
|
-
} catch {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function readStorageBody(body) {
|
|
135
|
-
const reader = body.getReader();
|
|
136
|
-
const chunks = [];
|
|
137
|
-
let totalLength = 0;
|
|
138
|
-
|
|
139
|
-
for (;;) {
|
|
140
|
-
const { done, value } = await reader.read();
|
|
141
|
-
if (done) break;
|
|
142
|
-
chunks.push(value);
|
|
143
|
-
totalLength += value.length;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const bytes = new Uint8Array(totalLength);
|
|
147
|
-
let offset = 0;
|
|
148
|
-
for (const chunk of chunks) {
|
|
149
|
-
bytes.set(chunk, offset);
|
|
150
|
-
offset += chunk.length;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return bytes;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function createLocalAssetLoader(storage, appConfig) {
|
|
157
|
-
if (!storage) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return async ({ resolvedUrl }) => {
|
|
162
|
-
const storageKey = getStorageKeyFromUrl(resolvedUrl, appConfig);
|
|
163
|
-
if (!storageKey) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const object = await storage.get(storageKey);
|
|
168
|
-
if (!object?.body) {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
bytes: await readStorageBody(object.body),
|
|
174
|
-
contentType: object.contentType || "",
|
|
175
|
-
};
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function exportLocalSite(env = process.env) {
|
|
180
|
-
const nodeDatabase = await openNodeDatabase(env);
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const {
|
|
184
|
-
createExportService,
|
|
185
|
-
createNodeCliRuntime,
|
|
186
|
-
resolveConfig,
|
|
187
|
-
buildThemeStyle,
|
|
188
|
-
BUILTIN_COLOR_THEMES,
|
|
189
|
-
BUILTIN_FONT_THEMES,
|
|
190
|
-
getCjkSerifCssVariables,
|
|
191
|
-
getFontThemeCssVariables,
|
|
192
|
-
} = await loadNodeRuntime();
|
|
193
|
-
const runtime = await createNodeCliRuntime(nodeDatabase.bindings);
|
|
194
|
-
const allSettings = await runtime.services.settings.getAll();
|
|
195
|
-
const navItems = await runtime.services.navItems.list();
|
|
196
|
-
const appConfig = resolveConfig(nodeDatabase.bindings, allSettings);
|
|
197
|
-
const activeTheme = BUILTIN_COLOR_THEMES.find(
|
|
198
|
-
(theme) => theme.id === (appConfig.themeId || appConfig.defaultThemeId),
|
|
199
|
-
);
|
|
200
|
-
const fontTheme = appConfig.fontThemeId
|
|
201
|
-
? BUILTIN_FONT_THEMES.find((theme) => theme.id === appConfig.fontThemeId)
|
|
202
|
-
: undefined;
|
|
203
|
-
const fontOverrides = {
|
|
204
|
-
...getCjkSerifCssVariables(appConfig.siteLanguage),
|
|
205
|
-
...(fontTheme ? getFontThemeCssVariables(fontTheme) : {}),
|
|
206
|
-
};
|
|
207
|
-
const themeCss = buildThemeStyle(
|
|
208
|
-
activeTheme,
|
|
209
|
-
appConfig.themeMode,
|
|
210
|
-
fontOverrides,
|
|
211
|
-
);
|
|
212
|
-
const appleTouchKey = allSettings.SITE_FAVICON_APPLE_TOUCH || "";
|
|
213
|
-
const exportService = createExportService(
|
|
214
|
-
runtime.services,
|
|
215
|
-
{
|
|
216
|
-
siteName: appConfig.siteName,
|
|
217
|
-
siteUrl: appConfig.siteUrl,
|
|
218
|
-
siteDescription: appConfig.siteDescription,
|
|
219
|
-
siteLanguage: appConfig.siteLanguage,
|
|
220
|
-
showJantBrandingOnHome: appConfig.showJantBrandingOnHome,
|
|
221
|
-
homeDefaultView: appConfig.homeDefaultView,
|
|
222
|
-
mainRssFeed: appConfig.mainRssFeed,
|
|
223
|
-
siteFooter: appConfig.siteFooter,
|
|
224
|
-
showHeaderAvatar: appConfig.showHeaderAvatar,
|
|
225
|
-
siteAvatarUrl: appConfig.siteAvatarUrl,
|
|
226
|
-
faviconIcoBase64: allSettings.SITE_FAVICON_ICO || undefined,
|
|
227
|
-
appleTouchIconStorageKey: appleTouchKey || undefined,
|
|
228
|
-
faviconVersion: appConfig.faviconVersion,
|
|
229
|
-
themeId: appConfig.themeId,
|
|
230
|
-
defaultThemeId: appConfig.defaultThemeId,
|
|
231
|
-
fontThemeId: appConfig.fontThemeId,
|
|
232
|
-
themeMode: appConfig.themeMode,
|
|
233
|
-
noindex: appConfig.noindex,
|
|
234
|
-
themeCss,
|
|
235
|
-
customCss: appConfig.customCSS,
|
|
236
|
-
r2PublicUrl: appConfig.r2PublicUrl,
|
|
237
|
-
s3PublicUrl: appConfig.s3PublicUrl,
|
|
238
|
-
localPublicUrl: appConfig.localPublicUrl,
|
|
239
|
-
imageTransformUrl: appConfig.imageTransformUrl,
|
|
240
|
-
sitePathPrefix: appConfig.sitePathPrefix,
|
|
241
|
-
navItems,
|
|
242
|
-
pageSize: appConfig.pageSize,
|
|
243
|
-
archivePageSize: appConfig.archivePageSize,
|
|
244
|
-
rssFeedLimit: appConfig.rssFeedLimit,
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
storage: runtime.storage,
|
|
248
|
-
},
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
zip: await exportService.generateHugoSite(),
|
|
253
|
-
assetLoader: createLocalAssetLoader(runtime.storage, appConfig),
|
|
254
|
-
source: describeLocalExportSource({
|
|
255
|
-
site: runtime.currentSite,
|
|
256
|
-
siteDomain: runtime.currentSiteDomain,
|
|
257
|
-
siteUrl: appConfig.siteUrl,
|
|
258
|
-
}),
|
|
259
|
-
};
|
|
260
|
-
} finally {
|
|
261
|
-
await nodeDatabase.close();
|
|
262
|
-
}
|
|
72
|
+
function printUsage() {
|
|
73
|
+
console.log("Usage: jant site export <url> [options]");
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log("Export a Jant site as a Hugo ZIP archive or directory.");
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log("Arguments:");
|
|
78
|
+
console.log(" <url> Jant site URL (required)");
|
|
79
|
+
console.log("");
|
|
80
|
+
console.log("Options:");
|
|
81
|
+
console.log(
|
|
82
|
+
" --output, -o Output ZIP path (default: jant-site-export.zip)",
|
|
83
|
+
);
|
|
84
|
+
console.log(
|
|
85
|
+
" --directory, -d Export directly to a directory for hugo serve/debugging",
|
|
86
|
+
);
|
|
87
|
+
console.log(
|
|
88
|
+
" --pull-media Download referenced media into static/media/ (default: on)",
|
|
89
|
+
);
|
|
90
|
+
console.log(" --no-pull-media Skip the media pull and keep original URLs");
|
|
91
|
+
console.log(" --token API token (overrides JANT_API_TOKEN)");
|
|
92
|
+
console.log("");
|
|
93
|
+
console.log("Authentication:");
|
|
94
|
+
console.log(` export ${CLI_API_TOKEN_ENV_VAR}=jnt_your_token`);
|
|
95
|
+
console.log(" jant site export https://your-site.example");
|
|
96
|
+
console.log("");
|
|
97
|
+
console.log("Examples:");
|
|
98
|
+
console.log(
|
|
99
|
+
" jant site export https://your-site.example -o ./export.zip",
|
|
100
|
+
);
|
|
101
|
+
console.log(
|
|
102
|
+
" jant site export https://your-site.example -d ./jant-site && cd ./jant-site && hugo serve",
|
|
103
|
+
);
|
|
263
104
|
}
|
|
264
105
|
|
|
265
106
|
export async function run(argv) {
|
|
266
107
|
const noPullMedia = argv.includes("--no-pull-media");
|
|
267
|
-
const
|
|
268
|
-
|
|
108
|
+
const filteredArgv = argv.filter((arg) => arg !== "--no-pull-media");
|
|
109
|
+
const { values, positionals } = parseArgs({
|
|
110
|
+
args: filteredArgv,
|
|
111
|
+
allowPositionals: true,
|
|
269
112
|
options: {
|
|
270
113
|
directory: {
|
|
271
114
|
type: "string",
|
|
@@ -279,49 +122,28 @@ export async function run(argv) {
|
|
|
279
122
|
default: "jant-site-export.zip",
|
|
280
123
|
},
|
|
281
124
|
token: { type: "string" },
|
|
282
|
-
url: { type: "string" },
|
|
283
125
|
},
|
|
284
126
|
});
|
|
285
127
|
|
|
286
128
|
if (values.help) {
|
|
287
|
-
|
|
288
|
-
console.log("");
|
|
289
|
-
console.log("Export a Jant site as a Hugo ZIP archive or directory.");
|
|
290
|
-
console.log("");
|
|
291
|
-
console.log("Modes:");
|
|
292
|
-
console.log(
|
|
293
|
-
" Local No --url; exports from the local Node database runtime",
|
|
294
|
-
);
|
|
295
|
-
console.log(
|
|
296
|
-
` Remote --url requires ${CLI_API_TOKEN_ENV_VAR} or --token`,
|
|
297
|
-
);
|
|
298
|
-
console.log("");
|
|
299
|
-
console.log("Options:");
|
|
300
|
-
console.log(" --url Remote Jant site URL");
|
|
301
|
-
console.log(
|
|
302
|
-
" --output, -o Output ZIP path (default: jant-site-export.zip)",
|
|
303
|
-
);
|
|
304
|
-
console.log(
|
|
305
|
-
" --directory, -d Export directly to a directory for hugo serve/debugging",
|
|
306
|
-
);
|
|
307
|
-
console.log(
|
|
308
|
-
" --pull-media Download referenced media into static/media/ (default: on)",
|
|
309
|
-
);
|
|
310
|
-
console.log(
|
|
311
|
-
" --no-pull-media Skip the media pull and keep original URLs",
|
|
312
|
-
);
|
|
313
|
-
console.log(" --token API token for remote export");
|
|
314
|
-
console.log("");
|
|
315
|
-
console.log("Authentication:");
|
|
316
|
-
console.log(` export ${CLI_API_TOKEN_ENV_VAR}=jnt_your_token`);
|
|
317
|
-
console.log(" jant site export --url https://your-site.com");
|
|
318
|
-
console.log("");
|
|
319
|
-
console.log("Examples:");
|
|
320
|
-
console.log(" jant site export --directory ./jant-site");
|
|
321
|
-
console.log(" cd ./jant-site && hugo serve");
|
|
129
|
+
printUsage();
|
|
322
130
|
process.exit(0);
|
|
323
131
|
}
|
|
324
132
|
|
|
133
|
+
const url = positionals[0];
|
|
134
|
+
if (!url) {
|
|
135
|
+
console.error("Error: site URL is required");
|
|
136
|
+
console.error("");
|
|
137
|
+
printUsage();
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
if (positionals.length > 1) {
|
|
141
|
+
console.error(
|
|
142
|
+
`Error: unexpected extra arguments: ${positionals.slice(1).join(" ")}`,
|
|
143
|
+
);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
325
147
|
if (values.directory && values.output !== "jant-site-export.zip") {
|
|
326
148
|
console.error("Error: use either --output or --directory, not both");
|
|
327
149
|
process.exit(1);
|
|
@@ -334,40 +156,29 @@ export async function run(argv) {
|
|
|
334
156
|
const token = getCliApiToken(process.env, values.token);
|
|
335
157
|
const pullMedia = values["pull-media"] ?? !noPullMedia;
|
|
336
158
|
|
|
337
|
-
if (
|
|
159
|
+
if (!token) {
|
|
338
160
|
console.error(
|
|
339
|
-
`Error:
|
|
161
|
+
`Error: site export requires ${CLI_API_TOKEN_ENV_VAR} or --token`,
|
|
340
162
|
);
|
|
341
163
|
process.exit(1);
|
|
342
164
|
}
|
|
343
165
|
|
|
344
|
-
console.log(
|
|
345
|
-
values.url
|
|
346
|
-
? `Exporting site from ${values.url}...`
|
|
347
|
-
: "Exporting site from the local runtime...",
|
|
348
|
-
);
|
|
166
|
+
console.log(`Exporting site from ${url}...`);
|
|
349
167
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
zip: await exportRemoteSite(values.url, token),
|
|
353
|
-
assetLoader: null,
|
|
354
|
-
source: values.url,
|
|
355
|
-
}
|
|
356
|
-
: await exportLocalSite(process.env);
|
|
357
|
-
let zip = exported.zip;
|
|
168
|
+
const zipBytes = await exportRemoteSite(url, token);
|
|
169
|
+
let zip = zipBytes;
|
|
358
170
|
let pullStats = null;
|
|
359
171
|
|
|
360
172
|
if (pullMedia) {
|
|
361
173
|
console.log("Preparing pull-media export ZIP...");
|
|
362
174
|
const pulled = await pullSiteExportZipBytes(zip, {
|
|
363
|
-
assetLoader:
|
|
175
|
+
assetLoader: null,
|
|
364
176
|
logger: logPullProgress,
|
|
365
177
|
});
|
|
366
178
|
zip = pulled.zipBytes;
|
|
367
179
|
pullStats = pulled.stats;
|
|
368
180
|
}
|
|
369
181
|
|
|
370
|
-
const source = exported.source;
|
|
371
182
|
if (outputDirectory) {
|
|
372
183
|
let existingEntries = [];
|
|
373
184
|
try {
|
|
@@ -393,12 +204,12 @@ export async function run(argv) {
|
|
|
393
204
|
mkdirSync(dirname(fullPath), { recursive: true });
|
|
394
205
|
writeFileSync(fullPath, Buffer.from(bytes));
|
|
395
206
|
}
|
|
396
|
-
console.log(`Exported site from ${
|
|
207
|
+
console.log(`Exported site from ${url} to ${values.directory}`);
|
|
397
208
|
console.log(`Preview with: cd ${values.directory} && hugo serve`);
|
|
398
209
|
} else {
|
|
399
210
|
console.log(`Writing ${values.output}...`);
|
|
400
211
|
writeFileSync(output, Buffer.from(zip));
|
|
401
|
-
console.log(`Exported site from ${
|
|
212
|
+
console.log(`Exported site from ${url} to ${values.output}`);
|
|
402
213
|
}
|
|
403
214
|
|
|
404
215
|
if (pullStats) {
|