@blocofy/cli 0.1.10 → 0.1.14
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/blocofy.mjs +158 -14
- package/lib/confirm.mjs +22 -0
- package/lib/content-sync.mjs +25 -6
- package/lib/theme-sync.mjs +42 -0
- package/package.json +1 -1
package/bin/blocofy.mjs
CHANGED
|
@@ -12,18 +12,25 @@ import { existsSync } from "node:fs";
|
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
13
|
import { createInterface } from "node:readline/promises";
|
|
14
14
|
|
|
15
|
-
import { pushContent } from "../lib/content-sync.mjs";
|
|
15
|
+
import { pullContent, pushContent } from "../lib/content-sync.mjs";
|
|
16
16
|
import { credentialsPath, loadCredentials, saveCredentials } from "../lib/credentials.mjs";
|
|
17
17
|
import { startDevServer } from "../lib/dev-server.mjs";
|
|
18
18
|
import { readLocalTemplates } from "../lib/local-theme.mjs";
|
|
19
19
|
import { githubNote, statusLine, syncScopeNote } from "../lib/messages.mjs";
|
|
20
|
-
import { fetchDevSession, pullTheme, pushTheme } from "../lib/theme-sync.mjs";
|
|
20
|
+
import { fetchDevSession, fetchSiteStatus, fetchWhoami, publishInstance, pullTheme, pushTheme } from "../lib/theme-sync.mjs";
|
|
21
|
+
import { isAffirmative, livePushDecision } from "../lib/confirm.mjs";
|
|
21
22
|
import { hyperlink, openUrl } from "../lib/term.mjs";
|
|
22
23
|
import { isValidToken, isValidUrl, normalizeUrl } from "../lib/validate.mjs";
|
|
23
24
|
|
|
24
|
-
const VERSION = "0.1.
|
|
25
|
+
const VERSION = "0.1.14";
|
|
25
26
|
const args = process.argv.slice(2);
|
|
26
27
|
|
|
28
|
+
/** Human label for a resolved site: "Name (slug)" or just the slug. */
|
|
29
|
+
function siteLabel(site) {
|
|
30
|
+
if (!site) return "";
|
|
31
|
+
return site.name ? `${site.name} (${site.slug})` : site.slug;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
/** Parse `--key value` and `--flag` (boolean) arguments. */
|
|
28
35
|
function parseFlags(rest) {
|
|
29
36
|
const flags = {};
|
|
@@ -55,7 +62,8 @@ Usage
|
|
|
55
62
|
blocofy theme dev [dir] [--port <n>] [--no-sync]
|
|
56
63
|
Start a dev server and print 3 auto-reloading views — Local, live-domain
|
|
57
64
|
Preview, and the theme Editor. Press l / p / e to open each, q to quit.
|
|
58
|
-
Edit a file and save → every open view reloads.
|
|
65
|
+
Edit a file and save → every open view reloads. Saves sync to a DRAFT theme
|
|
66
|
+
only (never the live site). (dir defaults to cwd)
|
|
59
67
|
--port <n> local port (default 3030)
|
|
60
68
|
--no-sync local preview only (skip draft sync + remote views)
|
|
61
69
|
|
|
@@ -63,16 +71,30 @@ Usage
|
|
|
63
71
|
Download the live theme to disk. (dir defaults to cwd)
|
|
64
72
|
--draft pull the draft theme (what 'theme dev' syncs into) instead of live
|
|
65
73
|
|
|
66
|
-
blocofy theme push [dir] [--draft]
|
|
67
|
-
Write the local theme to the site (create/update; no delete
|
|
68
|
-
|
|
74
|
+
blocofy theme push [dir] [--draft] [--yes]
|
|
75
|
+
Write the local theme to the LIVE site IMMEDIATELY (create/update; no delete,
|
|
76
|
+
no preview). Asks for confirmation first; non-interactive shells must pass --yes.
|
|
77
|
+
--draft write to a draft theme instead — preview & publish it from the
|
|
78
|
+
admin panel (recommended; never touches the live site)
|
|
79
|
+
--yes confirm the live push without prompting (for CI / agents)
|
|
80
|
+
|
|
81
|
+
blocofy theme publish [--instance <id>]
|
|
82
|
+
Publish a draft theme to the LIVE site. With no flag, publishes the draft that
|
|
83
|
+
'theme dev' / 'theme push --draft' writes into. The server refuses to publish a
|
|
84
|
+
theme that has no pages (it would 404) — so publishing is always safe.
|
|
85
|
+
--instance <id> publish a specific theme instance
|
|
86
|
+
|
|
87
|
+
blocofy status
|
|
88
|
+
Show the live theme, page distribution per instance, drafts, and a health flag
|
|
89
|
+
(ok / live_instance_empty / pages_split). Run before/after publishing.
|
|
69
90
|
|
|
70
|
-
blocofy pages push [dir]
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
blocofy pages pull [dir] / pages push [dir]
|
|
92
|
+
pull: download published pages → pages/<slug>.json.
|
|
93
|
+
push: write pages/*.json to the site. Updates EXISTING pages only —
|
|
94
|
+
never creates or deletes a page; unchanged pages are skipped.
|
|
73
95
|
|
|
74
|
-
blocofy settings push [dir]
|
|
75
|
-
|
|
96
|
+
blocofy settings pull [dir] / settings push [dir]
|
|
97
|
+
Download / upload config/settings.json (theme tokens/settings + color schemes).
|
|
76
98
|
|
|
77
99
|
blocofy --version
|
|
78
100
|
blocofy --help
|
|
@@ -80,7 +102,8 @@ Usage
|
|
|
80
102
|
Examples
|
|
81
103
|
blocofy login --url https://store.myblocofy.com --token bcf_xxxxxxxx
|
|
82
104
|
blocofy theme pull && blocofy theme dev
|
|
83
|
-
blocofy theme push --draft
|
|
105
|
+
blocofy theme push --draft && blocofy theme publish
|
|
106
|
+
blocofy status
|
|
84
107
|
|
|
85
108
|
Auth: ~/.blocofy/credentials.json (from \`login\`), or BLOCOFY_URL + BLOCOFY_TOKEN env vars.
|
|
86
109
|
The CLI does not build assets — bring your own (npm/Vite/Tailwind); the platform serves
|
|
@@ -143,6 +166,16 @@ async function login(rest) {
|
|
|
143
166
|
|
|
144
167
|
saveCredentials({ url, token });
|
|
145
168
|
console.log(`✓ Saved credentials → ${credentialsPath()}`);
|
|
169
|
+
|
|
170
|
+
// Token'ın GERÇEK site'ını göster — site sunucuda TOKEN'dan çözülür, URL kozmetik.
|
|
171
|
+
// Yanlış tenant'ın token'ıyla login olduysan ("klarosa URL + ksc token") hemen
|
|
172
|
+
// görürsün. whoami yoksa/erişilemezse sessiz geç.
|
|
173
|
+
try {
|
|
174
|
+
const who = await fetchWhoami({ url, token });
|
|
175
|
+
if (who?.site) console.log(` Site: ${siteLabel(who.site)} — your theme commands target this tenant.`);
|
|
176
|
+
} catch {
|
|
177
|
+
/* best-effort */
|
|
178
|
+
}
|
|
146
179
|
console.log(`Next: cd into your theme directory, then run blocofy theme dev`);
|
|
147
180
|
}
|
|
148
181
|
|
|
@@ -176,6 +209,48 @@ async function themePush(rest) {
|
|
|
176
209
|
}
|
|
177
210
|
const creds = requireCreds();
|
|
178
211
|
const draft = Boolean(flags.draft);
|
|
212
|
+
|
|
213
|
+
// Hedef tenant'ı çöz ve GÖSTER — site sunucuda TOKEN'dan çözülür (URL kozmetik),
|
|
214
|
+
// yanlış-tenant'a yazımı görünür kılar ("klarosa sandım, ksc'ye yazdım"). whoami
|
|
215
|
+
// yoksa/erişilemezse sessiz geç; etiketi confirmation mesajlarında da kullan.
|
|
216
|
+
let site = null;
|
|
217
|
+
try {
|
|
218
|
+
site = (await fetchWhoami({ url: creds.url, token: creds.token })).site;
|
|
219
|
+
} catch {
|
|
220
|
+
/* best-effort */
|
|
221
|
+
}
|
|
222
|
+
const target = site ? siteLabel(site) : creds.url;
|
|
223
|
+
if (site) {
|
|
224
|
+
console.log(`→ ${draft ? "Pushing to a draft" : "Pushing to the LIVE theme"} of ${target}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Canlı push (flag'siz) ANINDA canlı temayı değiştirir (önizleme yok). Agent/CI
|
|
228
|
+
// kazara canlıya basmasın diye açık onay şart (#431 L2).
|
|
229
|
+
const decision = livePushDecision({
|
|
230
|
+
draft,
|
|
231
|
+
yes: Boolean(flags.yes),
|
|
232
|
+
confirm: Boolean(flags.confirm),
|
|
233
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
234
|
+
});
|
|
235
|
+
if (decision.mustAbort) {
|
|
236
|
+
console.error(`⚠ 'theme push' writes to the LIVE theme of ${target} immediately (no preview).`);
|
|
237
|
+
console.error(` Non-interactive shell: pass --yes to confirm, or use --draft for a safe draft.`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
if (decision.needsPrompt) {
|
|
241
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
242
|
+
let answer;
|
|
243
|
+
try {
|
|
244
|
+
answer = await rl.question(`⚠ Push to the LIVE theme of ${target}? Immediate, no preview. [y/N] `);
|
|
245
|
+
} finally {
|
|
246
|
+
rl.close();
|
|
247
|
+
}
|
|
248
|
+
if (!isAffirmative(answer)) {
|
|
249
|
+
console.error("Aborted. Use `--draft` to push to a draft, or `--yes` to confirm the live push.");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
179
254
|
const result = await pushTheme({ dir, url: creds.url, token: creds.token, draft });
|
|
180
255
|
if (result.draft) {
|
|
181
256
|
console.log(`Pushed to draft theme #${result.instanceId} (${result.created} created, ${result.updated} updated).`);
|
|
@@ -210,6 +285,14 @@ async function contentPush(scope, rest) {
|
|
|
210
285
|
}
|
|
211
286
|
}
|
|
212
287
|
|
|
288
|
+
async function contentPull(scope, rest) {
|
|
289
|
+
const positional = rest.filter((a) => !a.startsWith("--"));
|
|
290
|
+
const dir = resolve(positional[0] ?? process.cwd());
|
|
291
|
+
const creds = requireCreds();
|
|
292
|
+
const { count } = await pullContent({ dir, url: creds.url, token: creds.token, scope });
|
|
293
|
+
console.log(`Downloaded ${count} ${scope === "settings" ? "settings" : "page"} file(s) → ${dir}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
213
296
|
async function themeDev(rest) {
|
|
214
297
|
const flags = parseFlags(rest);
|
|
215
298
|
const positional = rest.filter((a) => !a.startsWith("--"));
|
|
@@ -241,7 +324,9 @@ async function themeDev(rest) {
|
|
|
241
324
|
const previewUrl = session ? `${session.previewUrl}&hr=${port}` : null;
|
|
242
325
|
const editorUrl = session ? `${session.editorUrl}&hr=${port}` : null;
|
|
243
326
|
|
|
244
|
-
|
|
327
|
+
// Çözülen site'ı göster (session TOKEN'dan çözer) → hangi tenant'ı düzenlediğin belli olsun.
|
|
328
|
+
const siteLine = session?.site ? `${siteLabel(session.site)} · ` : "";
|
|
329
|
+
console.log(`\nblocofy theme dev — ${siteLine}${creds.url} (token ${creds.token.slice(0, 8)}…)\n`);
|
|
245
330
|
console.log(` (l) Local ${hyperlink(localUrl)}`);
|
|
246
331
|
if (previewUrl) console.log(` (p) Preview ${hyperlink(previewUrl)}`);
|
|
247
332
|
if (editorUrl) console.log(` (e) Editor ${hyperlink(editorUrl)}`);
|
|
@@ -348,6 +433,45 @@ async function themeDev(rest) {
|
|
|
348
433
|
}
|
|
349
434
|
}
|
|
350
435
|
|
|
436
|
+
async function themePublish(rest) {
|
|
437
|
+
const flags = parseFlags(rest);
|
|
438
|
+
const creds = requireCreds();
|
|
439
|
+
let instanceId = Number(flags.instance);
|
|
440
|
+
if (!Number.isInteger(instanceId) || instanceId <= 0) {
|
|
441
|
+
// Belirtilmediyse: `theme dev` / `theme push --draft`'ın yazdığı taslağı yayınla.
|
|
442
|
+
const session = await fetchDevSession({ url: creds.url, token: creds.token });
|
|
443
|
+
instanceId = session.draftInstanceId;
|
|
444
|
+
}
|
|
445
|
+
const result = await publishInstance({ url: creds.url, token: creds.token, instanceId });
|
|
446
|
+
console.log(
|
|
447
|
+
`✓ Theme #${result.published} is now LIVE${result.cloned ? " (pages cloned from the previous live theme)" : ""}.`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function status() {
|
|
452
|
+
const creds = requireCreds();
|
|
453
|
+
const s = await fetchSiteStatus({ url: creds.url, token: creds.token });
|
|
454
|
+
const live = s.live_theme_instance;
|
|
455
|
+
console.log(`\nSite: ${s.site.slug} (id ${s.site.id})`);
|
|
456
|
+
console.log(
|
|
457
|
+
live
|
|
458
|
+
? `Live theme: #${live.id}${live.name ? ` ${live.name}` : ""} — ${live.template_count} files, ${s.pages_on_live} pages`
|
|
459
|
+
: `Live theme: none`,
|
|
460
|
+
);
|
|
461
|
+
console.log(`Health: ${s.health}`);
|
|
462
|
+
if (s.health === "live_instance_empty") {
|
|
463
|
+
console.error(
|
|
464
|
+
` ⚠ The live theme has no pages — the site will 404. Publish a theme with content: blocofy theme publish`,
|
|
465
|
+
);
|
|
466
|
+
} else if (s.health === "pages_split") {
|
|
467
|
+
console.warn(` ⚠ ${s.orphaned_pages} page(s) live on a non-live instance (orphan).`);
|
|
468
|
+
}
|
|
469
|
+
if (Array.isArray(s.drafts) && s.drafts.length > 0) {
|
|
470
|
+
console.log(`Drafts: ${s.drafts.map((d) => `#${d.id}${d.name ? ` ${d.name}` : ""}`).join(", ")}`);
|
|
471
|
+
}
|
|
472
|
+
console.log("");
|
|
473
|
+
}
|
|
474
|
+
|
|
351
475
|
const [first, ...rest] = args;
|
|
352
476
|
|
|
353
477
|
if (first === "--version" || first === "-v") {
|
|
@@ -374,11 +498,31 @@ if (first === "--version" || first === "-v") {
|
|
|
374
498
|
console.error(error?.message ?? error);
|
|
375
499
|
process.exit(1);
|
|
376
500
|
});
|
|
501
|
+
} else if (first === "theme" && rest[0] === "publish") {
|
|
502
|
+
themePublish(rest.slice(1)).catch((error) => {
|
|
503
|
+
console.error(error?.message ?? error);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
});
|
|
506
|
+
} else if (first === "status") {
|
|
507
|
+
status().catch((error) => {
|
|
508
|
+
console.error(error?.message ?? error);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
});
|
|
511
|
+
} else if (first === "pages" && rest[0] === "pull") {
|
|
512
|
+
contentPull("pages", rest.slice(1)).catch((error) => {
|
|
513
|
+
console.error(error?.message ?? error);
|
|
514
|
+
process.exit(1);
|
|
515
|
+
});
|
|
377
516
|
} else if (first === "pages" && rest[0] === "push") {
|
|
378
517
|
contentPush("pages", rest.slice(1)).catch((error) => {
|
|
379
518
|
console.error(error?.message ?? error);
|
|
380
519
|
process.exit(1);
|
|
381
520
|
});
|
|
521
|
+
} else if (first === "settings" && rest[0] === "pull") {
|
|
522
|
+
contentPull("settings", rest.slice(1)).catch((error) => {
|
|
523
|
+
console.error(error?.message ?? error);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
});
|
|
382
526
|
} else if (first === "settings" && rest[0] === "push") {
|
|
383
527
|
contentPush("settings", rest.slice(1)).catch((error) => {
|
|
384
528
|
console.error(error?.message ?? error);
|
package/lib/confirm.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canlı `theme push` onay kararı (#431 L2). `theme push` (flag'siz) site'in CANLI
|
|
3
|
+
* temasına ANINDA yazar (önizleme yok) — bir agent/CI kazara canlı temayı bozabilir.
|
|
4
|
+
* Bu yüzden canlı push açık onay ister:
|
|
5
|
+
* - `--draft` → onay gerekmez (zaten canlıya yazmaz, taslağa yazar).
|
|
6
|
+
* - `--yes` / `--confirm` → açık onay (CI/agent için).
|
|
7
|
+
* - interaktif TTY → y/N sorulur.
|
|
8
|
+
* - non-TTY (script/agent) + onay yok → ABORT (sessizce canlıya basılmaz).
|
|
9
|
+
*
|
|
10
|
+
* Saf karar (yan etkisiz, test edilebilir): { autoApproved, needsPrompt, mustAbort }.
|
|
11
|
+
*/
|
|
12
|
+
export function livePushDecision({ draft, yes, confirm, isTTY }) {
|
|
13
|
+
if (draft || yes || confirm) return { autoApproved: true, needsPrompt: false, mustAbort: false };
|
|
14
|
+
if (!isTTY) return { autoApproved: false, needsPrompt: false, mustAbort: true };
|
|
15
|
+
return { autoApproved: false, needsPrompt: true, mustAbort: false };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** y/N yanıtını yorumla (yes/y → onay). */
|
|
19
|
+
export function isAffirmative(answer) {
|
|
20
|
+
const a = String(answer ?? "").trim().toLowerCase();
|
|
21
|
+
return a === "y" || a === "yes";
|
|
22
|
+
}
|
package/lib/content-sync.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { join, relative, sep } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* `blocofy pages push`
|
|
6
|
-
* (`pages/<slug>.json` + `config/settings.json`) platformun
|
|
7
|
-
* endpoint'
|
|
8
|
-
* Güvenlik platformda: yalnız mevcut sayfayı
|
|
5
|
+
* `blocofy pages pull/push` + `settings pull/push` (#119). CLI'nın yerel içerik
|
|
6
|
+
* dosyalarını (`pages/<slug>.json` + `config/settings.json`) platformun
|
|
7
|
+
* `/api/dev/content` endpoint'iyle çeker/gönderir. Tema KODU `theme pull/push`'a
|
|
8
|
+
* gider; bu yalnız içerik+ayar. Güvenlik platformda: push yalnız mevcut sayfayı
|
|
9
|
+
* günceller, yeni oluşturmaz, silmez.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
async function errorText(res) {
|
|
@@ -56,3 +57,21 @@ export async function pushContent({ dir, url, token, scope }) {
|
|
|
56
57
|
if (!res.ok) throw new Error(await errorText(res));
|
|
57
58
|
return { ...(await res.json()), fileCount: Object.keys(files).length };
|
|
58
59
|
}
|
|
60
|
+
|
|
61
|
+
/** Site içeriğini diske çek (pull). scope "pages" | "settings" | "all". Yazılan dosya sayısı. */
|
|
62
|
+
export async function pullContent({ dir, url, token, scope }) {
|
|
63
|
+
const base = url.replace(/\/+$/, "");
|
|
64
|
+
const res = await fetch(`${base}/api/dev/content?scope=${encodeURIComponent(scope)}`, {
|
|
65
|
+
headers: { authorization: `Bearer ${token}` },
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
68
|
+
const { files } = await res.json();
|
|
69
|
+
let count = 0;
|
|
70
|
+
for (const [path, content] of Object.entries(files ?? {})) {
|
|
71
|
+
const full = join(dir, ...path.split("/"));
|
|
72
|
+
mkdirSync(dirname(full), { recursive: true });
|
|
73
|
+
writeFileSync(full, typeof content === "string" ? content : "");
|
|
74
|
+
count += 1;
|
|
75
|
+
}
|
|
76
|
+
return { count };
|
|
77
|
+
}
|
package/lib/theme-sync.mjs
CHANGED
|
@@ -40,6 +40,21 @@ export async function pullTheme({ dir, url, token, draft = false }) {
|
|
|
40
40
|
return { count };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Token'ın GERÇEK site'ını çözer (`GET /api/dev/whoami`). Site sunucuda TOKEN'dan
|
|
45
|
+
* çözülür — login URL'i kozmetik. CLI bunu `login`'de (doğrula+göster) ve `push`
|
|
46
|
+
* öncesi (hedef tenant'ı yaz) çağırır; yanlış-tenant'a yazımı görünür kılar.
|
|
47
|
+
* `{ site: { id, slug, name }, liveThemeId }`.
|
|
48
|
+
*/
|
|
49
|
+
export async function fetchWhoami({ url, token }) {
|
|
50
|
+
const base = url.replace(/\/+$/, "");
|
|
51
|
+
const res = await fetch(`${base}/api/dev/whoami`, {
|
|
52
|
+
headers: { authorization: `Bearer ${token}` },
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
55
|
+
return res.json();
|
|
56
|
+
}
|
|
57
|
+
|
|
43
58
|
/**
|
|
44
59
|
* Dev session bilgisi (#119 `theme dev`): platform draft instance'ı hazırlar ve
|
|
45
60
|
* 3 görünümün URL'lerini döner — `{ draftInstanceId, previewUrl, editorUrl, site }`.
|
|
@@ -69,3 +84,30 @@ export async function pushTheme({ dir, url, token, draft = false }) {
|
|
|
69
84
|
if (!res.ok) throw new Error(await errorText(res));
|
|
70
85
|
return res.json();
|
|
71
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Bir taslak tema instance'ını CANLIYA al (`POST /api/dev/publish`). Sunucu guard'ı
|
|
90
|
+
* içi-sayfasız bir instance'ı reddeder ya da canlının sayfalarını klonlar (#431) —
|
|
91
|
+
* yayın sonrası site asla 404'e düşmez. `{ ok, published, cloned }` döner.
|
|
92
|
+
*/
|
|
93
|
+
export async function publishInstance({ url, token, instanceId }) {
|
|
94
|
+
const base = url.replace(/\/+$/, "");
|
|
95
|
+
const res = await fetch(`${base}/api/dev/publish`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
98
|
+
body: JSON.stringify({ instanceId }),
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
101
|
+
return res.json();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Site sağlık/durum özeti (`GET /api/dev/site`) — `blocofy status`. Canlı tema instance'ı,
|
|
106
|
+
* instance-başına sayfa dağılımı, taslaklar ve health döner.
|
|
107
|
+
*/
|
|
108
|
+
export async function fetchSiteStatus({ url, token }) {
|
|
109
|
+
const base = url.replace(/\/+$/, "");
|
|
110
|
+
const res = await fetch(`${base}/api/dev/site`, { headers: { authorization: `Bearer ${token}` } });
|
|
111
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
112
|
+
return res.json();
|
|
113
|
+
}
|
package/package.json
CHANGED