@plank-cms/plank 0.26.1 → 0.27.0

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.
@@ -12,8 +12,8 @@
12
12
  href="https://fonts.googleapis.com/css2?family=Google+Sans:ital,opsz,wght@0,17..18,400..700;1,17..18,400..700&display=swap"
13
13
  rel="stylesheet"
14
14
  />
15
- <script type="module" crossorigin src="/admin/assets/index-BLYpKKlP.js"></script>
16
- <link rel="stylesheet" crossorigin href="/admin/assets/index-BWA7n9y8.css">
15
+ <script type="module" crossorigin src="/admin/assets/index-C-XGjg93.js"></script>
16
+ <link rel="stylesheet" crossorigin href="/admin/assets/index-BTElP7oS.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { randomBytes } from "crypto";
7
7
  import { resolve, join } from "path";
8
8
  import fs from "fs-extra";
9
9
  import { execa } from "execa";
10
- var PACKAGE_VERSION = "0.26.1";
10
+ var PACKAGE_VERSION = "0.27.0";
11
11
  function generateSecret() {
12
12
  return randomBytes(32).toString("hex");
13
13
  }
@@ -106,7 +106,7 @@ import { dirname, join as join2, resolve as resolve2 } from "path";
106
106
  async function start() {
107
107
  config({ path: resolve2(process.cwd(), ".env") });
108
108
  process.env.PLANK_ADMIN_DIST = join2(dirname(fileURLToPath(import.meta.url)), "admin");
109
- const { start: startServer } = await import("./server-7LSVEAFA.js");
109
+ const { start: startServer } = await import("./server-HPYV4HWB.js");
110
110
  await startServer();
111
111
  }
112
112
 
@@ -651,7 +651,7 @@ import express from "express";
651
651
  import cors from "cors";
652
652
  import helmet from "helmet";
653
653
  import { join as join4, dirname as dirname2 } from "path";
654
- import { fileURLToPath as fileURLToPath2 } from "url";
654
+ import { fileURLToPath as fileURLToPath3 } from "url";
655
655
 
656
656
  // ../core/dist/routes/auth.js
657
657
  import { Router } from "express";
@@ -2445,6 +2445,32 @@ async function getProvider() {
2445
2445
  }
2446
2446
  var upload = multer({ storage: multer.memoryStorage() });
2447
2447
 
2448
+ // ../core/dist/lib/publicAuthorSlug.js
2449
+ function slugifySegment(value) {
2450
+ return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/^-+|-+$/g, "");
2451
+ }
2452
+ function baseSlugFromUser(input) {
2453
+ const fullName = [input.firstName, input.lastName].map((value) => value?.trim() ?? "").filter(Boolean).join(" ");
2454
+ const base = fullName || input.email.split("@")[0] || "author";
2455
+ return slugifySegment(base) || "author";
2456
+ }
2457
+ async function resolveUniquePublicAuthorSlug(input, excludeUserId) {
2458
+ const baseSlug = baseSlugFromUser(input);
2459
+ let slug = baseSlug;
2460
+ let suffix = 2;
2461
+ while (true) {
2462
+ const { rows } = await pool_default.query(`SELECT id
2463
+ FROM plank_users
2464
+ WHERE public_author_slug = $1
2465
+ AND ($2::text IS NULL OR id != $2)
2466
+ LIMIT 1`, [slug, excludeUserId ?? null]);
2467
+ if (!rows[0])
2468
+ return slug;
2469
+ slug = `${baseSlug}-${suffix}`;
2470
+ suffix += 1;
2471
+ }
2472
+ }
2473
+
2448
2474
  // ../core/dist/controllers/auth.js
2449
2475
  var LoginSchema = z.object({
2450
2476
  email: z.email(),
@@ -2456,6 +2482,8 @@ var Login2FASchema = z.object({
2456
2482
  });
2457
2483
  var RegisterSchema = z.object({
2458
2484
  email: z.email(),
2485
+ firstName: z.string().trim().min(1).max(100),
2486
+ lastName: z.string().trim().min(1).max(100),
2459
2487
  password: z.string().min(8)
2460
2488
  });
2461
2489
  var RATE_LIMIT_WINDOW_MS = 15 * 60 * 1e3;
@@ -2712,7 +2740,7 @@ async function register(req, res) {
2712
2740
  res.status(400).json({ errors: flattenError(parsed.error, (i2) => i2.message) });
2713
2741
  return;
2714
2742
  }
2715
- const { email, password } = parsed.data;
2743
+ const { email, firstName, lastName, password } = parsed.data;
2716
2744
  const hashed = await bcrypt.hash(password, 12);
2717
2745
  const { rows: roleRows } = await pool_default.query("SELECT id, name FROM plank_roles WHERE name = $1", ["Super Admin"]);
2718
2746
  const superAdminRole = roleRows[0];
@@ -2721,7 +2749,10 @@ async function register(req, res) {
2721
2749
  return;
2722
2750
  }
2723
2751
  const id = createId();
2724
- await pool_default.query("INSERT INTO plank_users (id, email, password, role_id) VALUES ($1, $2, $3, $4)", [id, email, hashed, superAdminRole.id]);
2752
+ const publicAuthorSlug = await resolveUniquePublicAuthorSlug({ email, firstName, lastName });
2753
+ await pool_default.query(`INSERT INTO plank_users
2754
+ (id, email, password, role_id, first_name, last_name, public_author_slug)
2755
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [id, email, hashed, superAdminRole.id, firstName, lastName, publicAuthorSlug]);
2725
2756
  res.status(201).json({ id, email });
2726
2757
  }
2727
2758
 
@@ -3966,34 +3997,6 @@ var deleteEntry = async (req, res) => {
3966
3997
  import bcrypt2 from "bcryptjs";
3967
3998
  import { randomBytes as randomBytes6 } from "crypto";
3968
3999
  import { z as z4, flattenError as flattenError4 } from "zod";
3969
-
3970
- // ../core/dist/lib/publicAuthorSlug.js
3971
- function slugifySegment(value) {
3972
- return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/^-+|-+$/g, "");
3973
- }
3974
- function baseSlugFromUser(input) {
3975
- const fullName = [input.firstName, input.lastName].map((value) => value?.trim() ?? "").filter(Boolean).join(" ");
3976
- const base = fullName || input.email.split("@")[0] || "author";
3977
- return slugifySegment(base) || "author";
3978
- }
3979
- async function resolveUniquePublicAuthorSlug(input, excludeUserId) {
3980
- const baseSlug = baseSlugFromUser(input);
3981
- let slug = baseSlug;
3982
- let suffix = 2;
3983
- while (true) {
3984
- const { rows } = await pool_default.query(`SELECT id
3985
- FROM plank_users
3986
- WHERE public_author_slug = $1
3987
- AND ($2::text IS NULL OR id != $2)
3988
- LIMIT 1`, [slug, excludeUserId ?? null]);
3989
- if (!rows[0])
3990
- return slug;
3991
- slug = `${baseSlug}-${suffix}`;
3992
- suffix += 1;
3993
- }
3994
- }
3995
-
3996
- // ../core/dist/controllers/users.js
3997
4000
  var CreateUserSchema = z4.object({
3998
4001
  email: z4.email(),
3999
4002
  password: z4.string().min(8),
@@ -4907,6 +4910,82 @@ async function updateNamespaceSettings(req, res) {
4907
4910
  res.json(maskSettings(namespace, updated));
4908
4911
  }
4909
4912
 
4913
+ // ../core/dist/lib/version.js
4914
+ import { readFile as readFile2 } from "fs/promises";
4915
+ import { fileURLToPath as fileURLToPath2 } from "url";
4916
+ var PACKAGE_NAME = "@plank-cms/plank";
4917
+ var CHANGELOG_BASE_URL = "https://github.com/plank-cms/plank/releases";
4918
+ var UPDATE_COMMAND = "npm run update";
4919
+ var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;
4920
+ var CACHE_TTL_MS = 1e3 * 60 * 30;
4921
+ var packageJsonUrl = new URL("../../package.json", import.meta.url);
4922
+ var cachedVersionCheck = null;
4923
+ function normalizeVersion(value) {
4924
+ return value.trim().replace(/^v/i, "").split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
4925
+ }
4926
+ function compareVersions(a2, b3) {
4927
+ const left = normalizeVersion(a2);
4928
+ const right = normalizeVersion(b3);
4929
+ const maxLength = Math.max(left.length, right.length);
4930
+ for (let index = 0; index < maxLength; index += 1) {
4931
+ const leftPart = left[index] ?? 0;
4932
+ const rightPart = right[index] ?? 0;
4933
+ if (leftPart > rightPart)
4934
+ return 1;
4935
+ if (leftPart < rightPart)
4936
+ return -1;
4937
+ }
4938
+ return 0;
4939
+ }
4940
+ function getChangelogUrl(version) {
4941
+ return version ? `${CHANGELOG_BASE_URL}/tag/${version}` : CHANGELOG_BASE_URL;
4942
+ }
4943
+ async function readCurrentVersion() {
4944
+ const packageJsonPath = fileURLToPath2(packageJsonUrl);
4945
+ const raw = await readFile2(packageJsonPath, "utf8");
4946
+ const parsed = JSON.parse(raw);
4947
+ return parsed.version ?? "0.0.0";
4948
+ }
4949
+ async function getVersionCheck() {
4950
+ if (cachedVersionCheck && cachedVersionCheck.expiresAt > Date.now()) {
4951
+ return cachedVersionCheck.value;
4952
+ }
4953
+ const currentVersion = await readCurrentVersion();
4954
+ let latestVersion = null;
4955
+ try {
4956
+ const response = await fetch(REGISTRY_URL, {
4957
+ signal: AbortSignal.timeout(4e3),
4958
+ headers: {
4959
+ Accept: "application/json"
4960
+ }
4961
+ });
4962
+ if (response.ok) {
4963
+ const payload = await response.json();
4964
+ latestVersion = payload.version ?? null;
4965
+ }
4966
+ } catch {
4967
+ }
4968
+ const value = {
4969
+ currentVersion,
4970
+ latestVersion,
4971
+ updateAvailable: latestVersion ? compareVersions(latestVersion, currentVersion) > 0 : false,
4972
+ changelogUrl: getChangelogUrl(latestVersion),
4973
+ updateCommand: UPDATE_COMMAND,
4974
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
4975
+ };
4976
+ cachedVersionCheck = {
4977
+ expiresAt: Date.now() + CACHE_TTL_MS,
4978
+ value
4979
+ };
4980
+ return value;
4981
+ }
4982
+
4983
+ // ../core/dist/controllers/version.js
4984
+ async function getVersionInfo(_req, res) {
4985
+ const versionInfo = await getVersionCheck();
4986
+ res.json(versionInfo);
4987
+ }
4988
+
4910
4989
  // ../core/dist/routes/admin.js
4911
4990
  var router2 = Router2();
4912
4991
  router2.use(authenticate);
@@ -4962,6 +5041,7 @@ router2.delete("/media/:id", authorize("media:delete"), deleteMedia);
4962
5041
  router2.get("/modes", getAppModes);
4963
5042
  router2.get("/client-settings", getClientSettings);
4964
5043
  router2.get("/editorial-mode", getEditorialMode);
5044
+ router2.get("/version", getVersionInfo);
4965
5045
  router2.get("/settings/:namespace", authorize("settings:overview:read"), getNamespaceSettings);
4966
5046
  router2.put("/settings/:namespace", authorize("settings:overview:write"), updateNamespaceSettings);
4967
5047
  router2.get("/webhooks", authorize("settings:webhooks:read"), listWebhooks);
@@ -5675,7 +5755,7 @@ if (isDev) {
5675
5755
  app.get("/admin/*path", (_req, res) => res.redirect(adminDevUrl));
5676
5756
  app.get("/admin", (_req, res) => res.redirect(adminDevUrl));
5677
5757
  } else {
5678
- const adminDist = process.env.PLANK_ADMIN_DIST ?? join4(dirname2(fileURLToPath2(import.meta.url)), "../public/admin");
5758
+ const adminDist = process.env.PLANK_ADMIN_DIST ?? join4(dirname2(fileURLToPath3(import.meta.url)), "../public/admin");
5679
5759
  app.use("/admin", express.static(adminDist));
5680
5760
  app.get("/admin/*path", (_req, res) => res.sendFile(join4(adminDist, "index.html")));
5681
5761
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plank-cms/plank",
3
- "version": "0.26.1",
3
+ "version": "0.27.0",
4
4
  "description": "Self-hosted headless CMS. Deploy in minutes on your own infrastructure.",
5
5
  "type": "module",
6
6
  "files": [
@@ -55,9 +55,9 @@
55
55
  "devDependencies": {
56
56
  "@types/fs-extra": "^11.0.4",
57
57
  "tsup": "^8.5.0",
58
- "@plank-cms/db": "0.26.1",
59
- "@plank-cms/schema": "0.26.1",
60
- "@plank-cms/core": "0.26.1"
58
+ "@plank-cms/core": "0.27.0",
59
+ "@plank-cms/schema": "0.27.0",
60
+ "@plank-cms/db": "0.27.0"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsup",