nextblogkit 0.6.2 → 0.7.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.
Files changed (111) hide show
  1. package/README.md +83 -21
  2. package/dist/admin/index.cjs +366 -10
  3. package/dist/admin/index.cjs.map +1 -1
  4. package/dist/admin/index.d.cts +7 -3
  5. package/dist/admin/index.d.ts +7 -3
  6. package/dist/admin/index.js +365 -11
  7. package/dist/admin/index.js.map +1 -1
  8. package/dist/api/categories.cjs +32 -32
  9. package/dist/api/categories.cjs.map +1 -1
  10. package/dist/api/categories.d.cts +1 -1
  11. package/dist/api/categories.d.ts +1 -1
  12. package/dist/api/categories.js +6 -6
  13. package/dist/api/categories.js.map +1 -1
  14. package/dist/api/media.cjs +37 -30
  15. package/dist/api/media.cjs.map +1 -1
  16. package/dist/api/media.d.cts +1 -1
  17. package/dist/api/media.d.ts +1 -1
  18. package/dist/api/media.js +13 -6
  19. package/dist/api/media.js.map +1 -1
  20. package/dist/api/posts.cjs +39 -39
  21. package/dist/api/posts.cjs.map +1 -1
  22. package/dist/api/posts.d.cts +1 -1
  23. package/dist/api/posts.d.ts +1 -1
  24. package/dist/api/posts.js +6 -6
  25. package/dist/api/posts.js.map +1 -1
  26. package/dist/api/rss.cjs +3 -3
  27. package/dist/api/rss.js +2 -2
  28. package/dist/api/settings.cjs +13 -13
  29. package/dist/api/settings.cjs.map +1 -1
  30. package/dist/api/settings.d.cts +1 -1
  31. package/dist/api/settings.d.ts +1 -1
  32. package/dist/api/settings.js +5 -5
  33. package/dist/api/settings.js.map +1 -1
  34. package/dist/api/sitemap.cjs +3 -3
  35. package/dist/api/sitemap.js +2 -2
  36. package/dist/api/tokens.cjs +56 -0
  37. package/dist/api/tokens.cjs.map +1 -0
  38. package/dist/api/tokens.d.cts +22 -0
  39. package/dist/api/tokens.d.ts +22 -0
  40. package/dist/api/tokens.js +52 -0
  41. package/dist/api/tokens.js.map +1 -0
  42. package/dist/{chunk-6HKMZOI4.cjs → chunk-3BKPNOES.cjs} +8 -7
  43. package/dist/chunk-3BKPNOES.cjs.map +1 -0
  44. package/dist/{chunk-N5MKAD7J.cjs → chunk-DR7QNI32.cjs} +6 -2
  45. package/dist/chunk-DR7QNI32.cjs.map +1 -0
  46. package/dist/{chunk-QE4VLQYN.cjs → chunk-F47RPOTU.cjs} +13 -10
  47. package/dist/chunk-F47RPOTU.cjs.map +1 -0
  48. package/dist/{chunk-64HUVJOZ.js → chunk-JI2RK6KX.js} +80 -13
  49. package/dist/chunk-JI2RK6KX.js.map +1 -0
  50. package/dist/{chunk-R6MO3QIP.js → chunk-NSR7NYSB.js} +6 -5
  51. package/dist/chunk-NSR7NYSB.js.map +1 -0
  52. package/dist/{chunk-4PY224XM.js → chunk-O3XES5O2.js} +6 -3
  53. package/dist/chunk-O3XES5O2.js.map +1 -0
  54. package/dist/{chunk-4NKOJYWJ.js → chunk-OOUJYUGP.js} +8 -7
  55. package/dist/chunk-OOUJYUGP.js.map +1 -0
  56. package/dist/{chunk-A2S32RZN.js → chunk-OWWWTTUT.js} +8 -3
  57. package/dist/chunk-OWWWTTUT.js.map +1 -0
  58. package/dist/{chunk-E2QLTHKN.cjs → chunk-QBZLGBHQ.cjs} +11 -10
  59. package/dist/chunk-QBZLGBHQ.cjs.map +1 -0
  60. package/dist/{chunk-ZP5XRVVH.cjs → chunk-SUJT6LWH.cjs} +12 -7
  61. package/dist/chunk-SUJT6LWH.cjs.map +1 -0
  62. package/dist/{chunk-JM7QRXXK.js → chunk-TVHY4BR2.js} +10 -7
  63. package/dist/chunk-TVHY4BR2.js.map +1 -0
  64. package/dist/{chunk-JLPJKNRZ.js → chunk-UMIBGO4S.js} +18 -5
  65. package/dist/chunk-UMIBGO4S.js.map +1 -0
  66. package/dist/{chunk-U2ROR6AY.cjs → chunk-VWKVU3SE.cjs} +86 -12
  67. package/dist/chunk-VWKVU3SE.cjs.map +1 -0
  68. package/dist/{chunk-KDZER3PU.cjs → chunk-YTJQ426D.cjs} +19 -5
  69. package/dist/chunk-YTJQ426D.cjs.map +1 -0
  70. package/dist/cli/index.cjs +90 -19
  71. package/dist/components/index.cjs +3 -2
  72. package/dist/components/index.cjs.map +1 -1
  73. package/dist/components/index.d.cts +2 -1
  74. package/dist/components/index.d.ts +2 -1
  75. package/dist/components/index.js +3 -2
  76. package/dist/components/index.js.map +1 -1
  77. package/dist/db-OUSQPM53.js +3 -0
  78. package/dist/db-OUSQPM53.js.map +1 -0
  79. package/dist/db-RFY6O5UE.cjs +108 -0
  80. package/dist/db-RFY6O5UE.cjs.map +1 -0
  81. package/dist/editor/index.cjs +1 -0
  82. package/dist/editor/index.cjs.map +1 -1
  83. package/dist/editor/index.js +1 -0
  84. package/dist/editor/index.js.map +1 -1
  85. package/dist/{index-vjlZDWNr.d.cts → index-Bk8gOqBq.d.cts} +25 -21
  86. package/dist/{index-Cgzphklp.d.ts → index-DsnG2kdW.d.ts} +25 -21
  87. package/dist/index.cjs +47 -47
  88. package/dist/index.d.cts +3 -3
  89. package/dist/index.d.ts +3 -3
  90. package/dist/index.js +5 -5
  91. package/dist/lib/index.cjs +39 -35
  92. package/dist/lib/index.d.cts +2 -2
  93. package/dist/lib/index.d.ts +2 -2
  94. package/dist/lib/index.js +5 -5
  95. package/dist/{types-CBEEBR4A.d.ts → types-Cu515Egx.d.cts} +16 -1
  96. package/dist/{types-CBEEBR4A.d.cts → types-Cu515Egx.d.ts} +16 -1
  97. package/package.json +1 -1
  98. package/dist/chunk-4NKOJYWJ.js.map +0 -1
  99. package/dist/chunk-4PY224XM.js.map +0 -1
  100. package/dist/chunk-64HUVJOZ.js.map +0 -1
  101. package/dist/chunk-6HKMZOI4.cjs.map +0 -1
  102. package/dist/chunk-A2S32RZN.js.map +0 -1
  103. package/dist/chunk-E2QLTHKN.cjs.map +0 -1
  104. package/dist/chunk-JLPJKNRZ.js.map +0 -1
  105. package/dist/chunk-JM7QRXXK.js.map +0 -1
  106. package/dist/chunk-KDZER3PU.cjs.map +0 -1
  107. package/dist/chunk-N5MKAD7J.cjs.map +0 -1
  108. package/dist/chunk-QE4VLQYN.cjs.map +0 -1
  109. package/dist/chunk-R6MO3QIP.js.map +0 -1
  110. package/dist/chunk-U2ROR6AY.cjs.map +0 -1
  111. package/dist/chunk-ZP5XRVVH.cjs.map +0 -1
@@ -6,8 +6,9 @@ interface AdminLayoutProps {
6
6
  apiKey?: string;
7
7
  apiPath?: string;
8
8
  adminPath?: string;
9
+ basePath?: string;
9
10
  }
10
- declare function AdminLayout({ children, apiKey, apiPath, adminPath }: AdminLayoutProps): react_jsx_runtime.JSX.Element;
11
+ declare function AdminLayout({ children, apiKey, apiPath, adminPath, basePath }: AdminLayoutProps): react_jsx_runtime.JSX.Element;
11
12
 
12
13
  declare function Dashboard(): react_jsx_runtime.JSX.Element;
13
14
 
@@ -30,10 +31,13 @@ interface SEOPanelProps {
30
31
  title: string;
31
32
  slug: string;
32
33
  excerpt: string;
34
+ basePath?: string;
33
35
  }
34
- declare function SEOPanel({ seo, onChange, title, slug, excerpt }: SEOPanelProps): react_jsx_runtime.JSX.Element;
36
+ declare function SEOPanel({ seo, onChange, title, slug, excerpt, basePath }: SEOPanelProps): react_jsx_runtime.JSX.Element;
35
37
 
36
38
  declare function setApiBase(path: string): void;
39
+ declare function setBasePath(path: string): void;
40
+ declare function getBasePath(): string;
37
41
  declare function useAdminApi(): {
38
42
  get: (path: string) => Promise<any>;
39
43
  post: (path: string, body?: unknown) => Promise<any>;
@@ -41,4 +45,4 @@ declare function useAdminApi(): {
41
45
  del: (path: string) => Promise<any>;
42
46
  };
43
47
 
44
- export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, setApiBase, useAdminApi };
48
+ export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, getBasePath, setApiBase, setBasePath, useAdminApi };
@@ -6,8 +6,9 @@ interface AdminLayoutProps {
6
6
  apiKey?: string;
7
7
  apiPath?: string;
8
8
  adminPath?: string;
9
+ basePath?: string;
9
10
  }
10
- declare function AdminLayout({ children, apiKey, apiPath, adminPath }: AdminLayoutProps): react_jsx_runtime.JSX.Element;
11
+ declare function AdminLayout({ children, apiKey, apiPath, adminPath, basePath }: AdminLayoutProps): react_jsx_runtime.JSX.Element;
11
12
 
12
13
  declare function Dashboard(): react_jsx_runtime.JSX.Element;
13
14
 
@@ -30,10 +31,13 @@ interface SEOPanelProps {
30
31
  title: string;
31
32
  slug: string;
32
33
  excerpt: string;
34
+ basePath?: string;
33
35
  }
34
- declare function SEOPanel({ seo, onChange, title, slug, excerpt }: SEOPanelProps): react_jsx_runtime.JSX.Element;
36
+ declare function SEOPanel({ seo, onChange, title, slug, excerpt, basePath }: SEOPanelProps): react_jsx_runtime.JSX.Element;
35
37
 
36
38
  declare function setApiBase(path: string): void;
39
+ declare function setBasePath(path: string): void;
40
+ declare function getBasePath(): string;
37
41
  declare function useAdminApi(): {
38
42
  get: (path: string) => Promise<any>;
39
43
  post: (path: string, body?: unknown) => Promise<any>;
@@ -41,4 +45,4 @@ declare function useAdminApi(): {
41
45
  del: (path: string) => Promise<any>;
42
46
  };
43
47
 
44
- export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, setApiBase, useAdminApi };
48
+ export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, getBasePath, setApiBase, setBasePath, useAdminApi };
@@ -21,9 +21,16 @@ import CodeBlockLowlight from '@tiptap/extension-code-block';
21
21
 
22
22
  // src/admin/AdminLayout.tsx
23
23
  var _apiBase = "/api/blog";
24
+ var _basePath = "/blog";
24
25
  function setApiBase(path) {
25
26
  _apiBase = path;
26
27
  }
28
+ function setBasePath(path) {
29
+ _basePath = path;
30
+ }
31
+ function getBasePath() {
32
+ return _basePath;
33
+ }
27
34
  function getApiKey() {
28
35
  if (typeof window === "undefined") return "";
29
36
  return sessionStorage.getItem("nbk_api_key") || "";
@@ -84,7 +91,7 @@ function buildNavItems(adminPath) {
84
91
  { label: "Settings", href: `${adminPath}/settings`, icon: "\u2699\uFE0F" }
85
92
  ];
86
93
  }
87
- function AdminLayout({ children, apiKey, apiPath, adminPath = "/admin/blog" }) {
94
+ function AdminLayout({ children, apiKey, apiPath, adminPath = "/admin/blog", basePath = "/blog" }) {
88
95
  const [isAuthenticated, setIsAuthenticated] = useState(false);
89
96
  const [inputKey, setInputKey] = useState("");
90
97
  const [sidebarOpen, setSidebarOpen] = useState(true);
@@ -93,7 +100,10 @@ function AdminLayout({ children, apiKey, apiPath, adminPath = "/admin/blog" }) {
93
100
  if (apiPath) {
94
101
  setApiBase(apiPath);
95
102
  }
96
- }, [apiPath]);
103
+ if (basePath) {
104
+ setBasePath(basePath);
105
+ }
106
+ }, [apiPath, basePath]);
97
107
  useEffect(() => {
98
108
  if (typeof window !== "undefined") {
99
109
  setCurrentPath(window.location.pathname);
@@ -431,7 +441,7 @@ function PostList() {
431
441
  /* @__PURE__ */ jsx(
432
442
  "a",
433
443
  {
434
- href: `/blog/${post.slug}`,
444
+ href: `${getBasePath()}/${post.slug}`,
435
445
  target: "_blank",
436
446
  rel: "noopener noreferrer",
437
447
  className: "nbk-btn nbk-btn-sm",
@@ -979,6 +989,7 @@ function BlogEditor({
979
989
  const lastSavedRef = useRef("");
980
990
  const defaultUpload = useCallback(async (file) => {
981
991
  if (!uploadImage) {
992
+ console.warn("[NextBlogKit] No uploadImage handler provided. Using blob URL \u2014 image will not persist across page reloads. Configure Cloudflare R2 for persistent image storage.");
982
993
  return { url: URL.createObjectURL(file), alt: file.name };
983
994
  }
984
995
  return uploadImage(file);
@@ -1432,7 +1443,7 @@ function stripTags(html) {
1432
1443
  function slugify(text) {
1433
1444
  return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-");
1434
1445
  }
1435
- function SEOPanel({ seo, onChange, title, slug, excerpt }) {
1446
+ function SEOPanel({ seo, onChange, title, slug, excerpt, basePath = "/blog" }) {
1436
1447
  const metaTitle = seo.metaTitle || "";
1437
1448
  const metaDescription = seo.metaDescription || "";
1438
1449
  const focusKeyword = seo.focusKeyword || "";
@@ -1441,7 +1452,7 @@ function SEOPanel({ seo, onChange, title, slug, excerpt }) {
1441
1452
  const noIndex = seo.noIndex || false;
1442
1453
  const displayTitle = metaTitle || title || "Post Title";
1443
1454
  const displayDesc = metaDescription || excerpt || "Post description will appear here...";
1444
- const displayUrl = `/blog/${slug || "post-url"}`;
1455
+ const displayUrl = `${basePath}/${slug || "post-url"}`;
1445
1456
  const titleLength = displayTitle.length;
1446
1457
  const descLength = displayDesc.length;
1447
1458
  const titleColor = titleLength >= 50 && titleLength <= 60 ? "nbk-count-good" : titleLength > 70 ? "nbk-count-bad" : "nbk-count-warn";
@@ -1653,10 +1664,20 @@ function PostEditor({ postId }) {
1653
1664
  [postId]
1654
1665
  );
1655
1666
  const uploadImage = async (file) => {
1656
- const formData = new FormData();
1657
- formData.append("file", file);
1658
- const res = await api.post("/media", formData);
1659
- return { url: res.data.url, alt: res.data.alt || file.name };
1667
+ try {
1668
+ const formData = new FormData();
1669
+ formData.append("file", file);
1670
+ const res = await api.post("/media", formData);
1671
+ return { url: res.data.url, alt: res.data.alt || file.name };
1672
+ } catch (err) {
1673
+ const msg = err.message || "Upload failed";
1674
+ if (msg.includes("R2") || msg.includes("STORAGE_NOT_CONFIGURED")) {
1675
+ setError("Image upload requires Cloudflare R2 configuration. Set R2 environment variables or use an external image URL instead.");
1676
+ } else {
1677
+ setError(msg);
1678
+ }
1679
+ throw err;
1680
+ }
1660
1681
  };
1661
1682
  if (loading) {
1662
1683
  return /* @__PURE__ */ jsxs("div", { className: "nbk-post-editor", children: [
@@ -1878,7 +1899,8 @@ function PostEditor({ postId }) {
1878
1899
  onChange: setSeo,
1879
1900
  title,
1880
1901
  slug,
1881
- excerpt
1902
+ excerpt,
1903
+ basePath: getBasePath()
1882
1904
  }
1883
1905
  )
1884
1906
  ] })
@@ -2237,6 +2259,333 @@ function CategoryManager() {
2237
2259
  ] })
2238
2260
  ] });
2239
2261
  }
2262
+ function ApiTokensSection() {
2263
+ const api = useAdminApi();
2264
+ const [tokens, setTokens] = useState([]);
2265
+ const [loading, setLoading] = useState(true);
2266
+ const [tokenName, setTokenName] = useState("");
2267
+ const [generating, setGenerating] = useState(false);
2268
+ const [newToken, setNewToken] = useState("");
2269
+ const [copied, setCopied] = useState(false);
2270
+ const [error, setError] = useState("");
2271
+ const [showDialog, setShowDialog] = useState(false);
2272
+ const [revoking, setRevoking] = useState(null);
2273
+ const fetchTokens = useCallback(async () => {
2274
+ try {
2275
+ const res = await api.get("/tokens");
2276
+ setTokens(res.data || []);
2277
+ } catch (err) {
2278
+ setError(err.message || "Failed to load tokens");
2279
+ } finally {
2280
+ setLoading(false);
2281
+ }
2282
+ }, []);
2283
+ useEffect(() => {
2284
+ fetchTokens();
2285
+ }, [fetchTokens]);
2286
+ const handleGenerate = async () => {
2287
+ if (!tokenName.trim()) return;
2288
+ setGenerating(true);
2289
+ setError("");
2290
+ try {
2291
+ const res = await api.post("/tokens", { name: tokenName.trim() });
2292
+ setNewToken(res.data.plainToken);
2293
+ setTokenName("");
2294
+ fetchTokens();
2295
+ } catch (err) {
2296
+ setError(err.message || "Failed to generate token");
2297
+ } finally {
2298
+ setGenerating(false);
2299
+ }
2300
+ };
2301
+ const handleRevoke = async (id) => {
2302
+ if (!confirm("Are you sure you want to revoke this token? Any integrations using it will stop working.")) return;
2303
+ setRevoking(id);
2304
+ setError("");
2305
+ try {
2306
+ await api.del(`/tokens?id=${id}`);
2307
+ setTokens((prev) => prev.filter((t) => t._id !== id));
2308
+ } catch (err) {
2309
+ setError(err.message || "Failed to revoke token");
2310
+ } finally {
2311
+ setRevoking(null);
2312
+ }
2313
+ };
2314
+ const copyToken = () => {
2315
+ navigator.clipboard.writeText(newToken);
2316
+ setCopied(true);
2317
+ setTimeout(() => setCopied(false), 2e3);
2318
+ };
2319
+ const formatDate = (d) => {
2320
+ if (!d) return "Never";
2321
+ return new Date(d).toLocaleDateString("en-US", {
2322
+ month: "short",
2323
+ day: "numeric",
2324
+ year: "numeric",
2325
+ hour: "2-digit",
2326
+ minute: "2-digit"
2327
+ });
2328
+ };
2329
+ return /* @__PURE__ */ jsxs("div", { className: "nbk-settings-section", children: [
2330
+ /* @__PURE__ */ jsx("h2", { className: "nbk-section-title", children: "API Tokens" }),
2331
+ /* @__PURE__ */ jsx("p", { style: { color: "var(--nbk-text-muted)", fontSize: "0.875rem", marginBottom: "1rem" }, children: "Generate tokens for external services (CI pipelines, automation tools, CMS integrations). Tokens can access the API like the master key but cannot manage other tokens." }),
2332
+ error && /* @__PURE__ */ jsx("div", { className: "nbk-error", style: { marginBottom: "1rem" }, children: error }),
2333
+ newToken && /* @__PURE__ */ jsxs("div", { style: {
2334
+ background: "var(--nbk-bg-secondary)",
2335
+ border: "1px solid var(--nbk-warning, #f59e0b)",
2336
+ borderRadius: "var(--nbk-radius)",
2337
+ padding: "1rem",
2338
+ marginBottom: "1rem"
2339
+ }, children: [
2340
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: "0.5rem", color: "var(--nbk-warning, #f59e0b)" }, children: "Save this token \u2014 it will only be shown once" }),
2341
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", alignItems: "center" }, children: [
2342
+ /* @__PURE__ */ jsx("code", { style: {
2343
+ flex: 1,
2344
+ background: "var(--nbk-bg)",
2345
+ padding: "0.5rem 0.75rem",
2346
+ borderRadius: "var(--nbk-radius)",
2347
+ border: "1px solid var(--nbk-border)",
2348
+ fontSize: "0.813rem",
2349
+ fontFamily: "var(--nbk-font-code)",
2350
+ wordBreak: "break-all"
2351
+ }, children: newToken }),
2352
+ /* @__PURE__ */ jsx("button", { onClick: copyToken, className: "nbk-btn nbk-btn-primary", style: { whiteSpace: "nowrap" }, children: copied ? "Copied!" : "Copy" })
2353
+ ] }),
2354
+ /* @__PURE__ */ jsx(
2355
+ "button",
2356
+ {
2357
+ onClick: () => setNewToken(""),
2358
+ style: {
2359
+ marginTop: "0.5rem",
2360
+ background: "none",
2361
+ border: "none",
2362
+ color: "var(--nbk-text-muted)",
2363
+ cursor: "pointer",
2364
+ fontSize: "0.813rem",
2365
+ padding: 0
2366
+ },
2367
+ children: "Dismiss"
2368
+ }
2369
+ )
2370
+ ] }),
2371
+ !showDialog ? /* @__PURE__ */ jsx("button", { onClick: () => setShowDialog(true), className: "nbk-btn nbk-btn-primary", style: { marginBottom: "1rem" }, children: "Generate New Token" }) : /* @__PURE__ */ jsxs("div", { style: {
2372
+ display: "flex",
2373
+ gap: "0.5rem",
2374
+ marginBottom: "1rem",
2375
+ alignItems: "flex-end"
2376
+ }, children: [
2377
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
2378
+ /* @__PURE__ */ jsx("label", { className: "nbk-label", children: "Token Name" }),
2379
+ /* @__PURE__ */ jsx(
2380
+ "input",
2381
+ {
2382
+ type: "text",
2383
+ value: tokenName,
2384
+ onChange: (e) => setTokenName(e.target.value),
2385
+ className: "nbk-input",
2386
+ placeholder: "e.g. CI Pipeline, n8n Automation",
2387
+ onKeyDown: (e) => e.key === "Enter" && handleGenerate(),
2388
+ autoFocus: true
2389
+ }
2390
+ )
2391
+ ] }),
2392
+ /* @__PURE__ */ jsx("button", { onClick: handleGenerate, className: "nbk-btn nbk-btn-primary", disabled: generating || !tokenName.trim(), children: generating ? "Generating..." : "Generate" }),
2393
+ /* @__PURE__ */ jsx(
2394
+ "button",
2395
+ {
2396
+ onClick: () => {
2397
+ setShowDialog(false);
2398
+ setTokenName("");
2399
+ },
2400
+ className: "nbk-btn",
2401
+ style: { background: "var(--nbk-bg-secondary)", border: "1px solid var(--nbk-border)" },
2402
+ children: "Cancel"
2403
+ }
2404
+ )
2405
+ ] }),
2406
+ loading ? /* @__PURE__ */ jsx("div", { style: { color: "var(--nbk-text-muted)" }, children: "Loading tokens..." }) : tokens.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "var(--nbk-text-muted)", fontSize: "0.875rem" }, children: "No API tokens generated yet." }) : /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs("table", { style: {
2407
+ width: "100%",
2408
+ borderCollapse: "collapse",
2409
+ fontSize: "0.875rem"
2410
+ }, children: [
2411
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { style: { borderBottom: "2px solid var(--nbk-border)" }, children: [
2412
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Name" }),
2413
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Key Prefix" }),
2414
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Last Used" }),
2415
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Created" }),
2416
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "right", padding: "0.5rem 0.75rem", fontWeight: 600 } })
2417
+ ] }) }),
2418
+ /* @__PURE__ */ jsx("tbody", { children: tokens.map((t) => /* @__PURE__ */ jsxs("tr", { style: { borderBottom: "1px solid var(--nbk-border)" }, children: [
2419
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem" }, children: t.name }),
2420
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem" }, children: /* @__PURE__ */ jsxs("code", { style: { fontFamily: "var(--nbk-font-code)", fontSize: "0.813rem" }, children: [
2421
+ t.prefix,
2422
+ "..."
2423
+ ] }) }),
2424
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem", color: "var(--nbk-text-muted)" }, children: formatDate(t.lastUsedAt) }),
2425
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem", color: "var(--nbk-text-muted)" }, children: formatDate(t.createdAt) }),
2426
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem", textAlign: "right" }, children: /* @__PURE__ */ jsx(
2427
+ "button",
2428
+ {
2429
+ onClick: () => handleRevoke(t._id),
2430
+ disabled: revoking === t._id,
2431
+ style: {
2432
+ background: "none",
2433
+ border: "1px solid var(--nbk-danger, #ef4444)",
2434
+ color: "var(--nbk-danger, #ef4444)",
2435
+ padding: "0.25rem 0.75rem",
2436
+ borderRadius: "var(--nbk-radius)",
2437
+ cursor: "pointer",
2438
+ fontSize: "0.813rem"
2439
+ },
2440
+ children: revoking === t._id ? "Revoking..." : "Revoke"
2441
+ }
2442
+ ) })
2443
+ ] }, t._id)) })
2444
+ ] }) })
2445
+ ] });
2446
+ }
2447
+ function ApiReferenceSection() {
2448
+ const [open, setOpen] = useState(false);
2449
+ const [copiedCurl, setCopiedCurl] = useState(false);
2450
+ const [copiedJson, setCopiedJson] = useState(false);
2451
+ const sampleJson = `{
2452
+ "title": "My Blog Post",
2453
+ "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Hello world!" }] }],
2454
+ "contentHTML": "<p>Hello world!</p>",
2455
+ "excerpt": "A short summary of the post",
2456
+ "status": "published",
2457
+ "categories": ["tech"],
2458
+ "tags": ["nextjs", "blog"],
2459
+ "author": {
2460
+ "name": "John Doe",
2461
+ "bio": "Software engineer",
2462
+ "avatar": "https://example.com/avatar.jpg"
2463
+ },
2464
+ "seo": {
2465
+ "metaTitle": "My Blog Post | MySite",
2466
+ "metaDescription": "A short summary for search engines",
2467
+ "focusKeyword": "blog post"
2468
+ }
2469
+ }`;
2470
+ const sampleCurl = `curl -X POST https://yoursite.com/api/blog/posts \\
2471
+ -H "Authorization: Bearer nbk_your-token-here" \\
2472
+ -H "Content-Type: application/json" \\
2473
+ -d '{ "title": "My Post", "content": [{"type":"paragraph","content":[{"type":"text","text":"Hello!"}]}], "contentHTML": "<p>Hello!</p>", "status": "published" }'`;
2474
+ const copyText = (text, setCopied) => {
2475
+ navigator.clipboard.writeText(text);
2476
+ setCopied(true);
2477
+ setTimeout(() => setCopied(false), 2e3);
2478
+ };
2479
+ return /* @__PURE__ */ jsxs("div", { className: "nbk-settings-section", children: [
2480
+ /* @__PURE__ */ jsxs(
2481
+ "h2",
2482
+ {
2483
+ className: "nbk-section-title",
2484
+ onClick: () => setOpen(!open),
2485
+ style: { cursor: "pointer", userSelect: "none", display: "flex", alignItems: "center", gap: "0.5rem" },
2486
+ children: [
2487
+ /* @__PURE__ */ jsx("span", { style: { transform: open ? "rotate(90deg)" : "rotate(0deg)", transition: "transform 0.2s", display: "inline-block" }, children: "\u25B6" }),
2488
+ "API Reference"
2489
+ ]
2490
+ }
2491
+ ),
2492
+ open && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1rem" }, children: [
2493
+ /* @__PURE__ */ jsx("h3", { style: { fontSize: "0.938rem", fontWeight: 600, marginBottom: "0.75rem" }, children: "Create Post \u2014 POST /api/blog/posts" }),
2494
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem" }, children: [
2495
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "0.25rem" }, children: [
2496
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.813rem", fontWeight: 600, color: "var(--nbk-text-muted)" }, children: "Sample JSON Body" }),
2497
+ /* @__PURE__ */ jsx(
2498
+ "button",
2499
+ {
2500
+ onClick: () => copyText(sampleJson, setCopiedJson),
2501
+ style: {
2502
+ background: "none",
2503
+ border: "1px solid var(--nbk-border)",
2504
+ borderRadius: "var(--nbk-radius)",
2505
+ padding: "0.125rem 0.5rem",
2506
+ cursor: "pointer",
2507
+ fontSize: "0.75rem",
2508
+ color: "var(--nbk-text-muted)"
2509
+ },
2510
+ children: copiedJson ? "Copied!" : "Copy"
2511
+ }
2512
+ )
2513
+ ] }),
2514
+ /* @__PURE__ */ jsx("pre", { style: {
2515
+ background: "var(--nbk-bg-secondary)",
2516
+ border: "1px solid var(--nbk-border)",
2517
+ borderRadius: "var(--nbk-radius)",
2518
+ padding: "0.75rem",
2519
+ overflow: "auto",
2520
+ fontSize: "0.813rem",
2521
+ fontFamily: "var(--nbk-font-code)",
2522
+ lineHeight: 1.5,
2523
+ maxHeight: "400px"
2524
+ }, children: sampleJson })
2525
+ ] }),
2526
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem" }, children: [
2527
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "0.25rem" }, children: [
2528
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.813rem", fontWeight: 600, color: "var(--nbk-text-muted)" }, children: "Sample curl Command" }),
2529
+ /* @__PURE__ */ jsx(
2530
+ "button",
2531
+ {
2532
+ onClick: () => copyText(sampleCurl, setCopiedCurl),
2533
+ style: {
2534
+ background: "none",
2535
+ border: "1px solid var(--nbk-border)",
2536
+ borderRadius: "var(--nbk-radius)",
2537
+ padding: "0.125rem 0.5rem",
2538
+ cursor: "pointer",
2539
+ fontSize: "0.75rem",
2540
+ color: "var(--nbk-text-muted)"
2541
+ },
2542
+ children: copiedCurl ? "Copied!" : "Copy"
2543
+ }
2544
+ )
2545
+ ] }),
2546
+ /* @__PURE__ */ jsx("pre", { style: {
2547
+ background: "var(--nbk-bg-secondary)",
2548
+ border: "1px solid var(--nbk-border)",
2549
+ borderRadius: "var(--nbk-radius)",
2550
+ padding: "0.75rem",
2551
+ overflow: "auto",
2552
+ fontSize: "0.813rem",
2553
+ fontFamily: "var(--nbk-font-code)",
2554
+ lineHeight: 1.5
2555
+ }, children: sampleCurl })
2556
+ ] }),
2557
+ /* @__PURE__ */ jsx("h3", { style: { fontSize: "0.938rem", fontWeight: 600, marginBottom: "0.75rem" }, children: "Field Reference" }),
2558
+ /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "0.813rem" }, children: [
2559
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { style: { borderBottom: "2px solid var(--nbk-border)" }, children: [
2560
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Field" }),
2561
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Type" }),
2562
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Required" }),
2563
+ /* @__PURE__ */ jsx("th", { style: { textAlign: "left", padding: "0.5rem 0.75rem", fontWeight: 600 }, children: "Description" })
2564
+ ] }) }),
2565
+ /* @__PURE__ */ jsx("tbody", { children: [
2566
+ ["title", "string", "Yes", "Post title"],
2567
+ ["content", "BlockContent[]", "No", "TipTap JSON content blocks"],
2568
+ ["contentHTML", "string", "No", "HTML version of the content"],
2569
+ ["excerpt", "string", "No", "Short summary (auto-generated if omitted)"],
2570
+ ["slug", "string", "No", "URL slug (auto-generated from title if omitted)"],
2571
+ ["status", '"draft" | "published" | "scheduled"', "No", 'Defaults to "draft"'],
2572
+ ["categories", "string[]", "No", "Category slugs"],
2573
+ ["tags", "string[]", "No", "Tag strings"],
2574
+ ["author", "{ name, bio?, avatar?, url? }", "No", "Post author info"],
2575
+ ["seo", "{ metaTitle?, metaDescription?, focusKeyword?, ... }", "No", "SEO metadata"],
2576
+ ["coverImage", "{ _id, url, alt?, caption? }", "No", "Cover image reference"],
2577
+ ["publishedAt", "ISO date string", "No", 'Publish date (auto-set when status is "published")'],
2578
+ ["scheduledAt", "ISO date string", "No", "Schedule date for future publishing"]
2579
+ ].map(([field, type, required, desc]) => /* @__PURE__ */ jsxs("tr", { style: { borderBottom: "1px solid var(--nbk-border)" }, children: [
2580
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem" }, children: /* @__PURE__ */ jsx("code", { style: { fontFamily: "var(--nbk-font-code)", fontSize: "0.813rem" }, children: field }) }),
2581
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem", color: "var(--nbk-text-muted)" }, children: type }),
2582
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem" }, children: required }),
2583
+ /* @__PURE__ */ jsx("td", { style: { padding: "0.5rem 0.75rem", color: "var(--nbk-text-muted)" }, children: desc })
2584
+ ] }, field)) })
2585
+ ] }) })
2586
+ ] })
2587
+ ] });
2588
+ }
2240
2589
  function SettingsPage() {
2241
2590
  const api = useAdminApi();
2242
2591
  const [settings, setSettings] = useState({});
@@ -2429,10 +2778,15 @@ function SettingsPage() {
2429
2778
  placeholder: "/* Custom CSS styles */"
2430
2779
  }
2431
2780
  ) })
2781
+ ] }),
2782
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "2rem" }, children: [
2783
+ /* @__PURE__ */ jsx("h2", { className: "nbk-page-title", style: { fontSize: "1.25rem", marginBottom: "1rem" }, children: "API Access" }),
2784
+ /* @__PURE__ */ jsx(ApiTokensSection, {}),
2785
+ /* @__PURE__ */ jsx(ApiReferenceSection, {})
2432
2786
  ] })
2433
2787
  ] });
2434
2788
  }
2435
2789
 
2436
- export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, setApiBase, useAdminApi };
2790
+ export { AdminLayout, CategoryManager, Dashboard, MediaLibrary, PostEditor, PostList, SEOPanel, SettingsPage, getBasePath, setApiBase, setBasePath, useAdminApi };
2437
2791
  //# sourceMappingURL=index.js.map
2438
2792
  //# sourceMappingURL=index.js.map