@beechcms/api 0.6.1 → 0.6.3
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/assets/dashboard/assets/{chart-CspfxV2U.js → chart-pTKbc60T.js} +1 -1
- package/assets/dashboard/assets/{index-DtDfJyaF.js → index-DQ68KtLl.js} +87 -87
- package/assets/dashboard/assets/{pie-chart-recharts-Bx2IXAjA.js → pie-chart-recharts-BHRffVvU.js} +1 -1
- package/assets/dashboard/assets/{timeseries-chart-recharts-BCWufwI1.js → timeseries-chart-recharts-BL2OaRk_.js} +1 -1
- package/assets/dashboard/index.html +1 -1
- package/dist/index.js +124 -57
- package/dist/shared/db/repositories/d1-user.repository.d.ts +1 -0
- package/dist/shared/email/templates/automation-mail.d.ts +0 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -188,7 +188,8 @@ async function applyPrivacy(data, seed) {
|
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
190
|
if (privacy === "hash" && value != null) {
|
|
191
|
-
|
|
191
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
192
|
+
result[alias] = await sha256hex(serialized);
|
|
192
193
|
} else {
|
|
193
194
|
result[alias] = value;
|
|
194
195
|
}
|
|
@@ -860,6 +861,7 @@ async function updateHandler(context) {
|
|
|
860
861
|
// src/shared/storage/upload.ts
|
|
861
862
|
async function deleteR2Objects(c, objectKeys) {
|
|
862
863
|
const { bucket, mediaRepository, systemStatsRepository } = c.var;
|
|
864
|
+
const untrackFailures = [];
|
|
863
865
|
await Promise.all(
|
|
864
866
|
objectKeys.map(async (key) => {
|
|
865
867
|
const media = await mediaRepository.getByKey(key).catch(() => null);
|
|
@@ -875,9 +877,15 @@ async function deleteR2Objects(c, objectKeys) {
|
|
|
875
877
|
}
|
|
876
878
|
} catch (err) {
|
|
877
879
|
console.warn(`Failed to untrack media object: ${key}`, err);
|
|
880
|
+
untrackFailures.push(key);
|
|
878
881
|
}
|
|
879
882
|
})
|
|
880
883
|
);
|
|
884
|
+
if (untrackFailures.length > 0) {
|
|
885
|
+
throw new Error(
|
|
886
|
+
`${untrackFailures.length} media row(s) now out of sync (R2 object deleted but DB untrack/decrement failed): ${untrackFailures.join(", ")}`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
881
889
|
}
|
|
882
890
|
|
|
883
891
|
// src/shared/utils/media-utils.ts
|
|
@@ -885,11 +893,14 @@ var MEDIA_URL_PATTERN = /\/api\/media\/([^?#]+)/;
|
|
|
885
893
|
function extractMediaKey(mediaUrl, cdnUrl) {
|
|
886
894
|
const urlStr = String(mediaUrl);
|
|
887
895
|
if (cdnUrl) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
896
|
+
try {
|
|
897
|
+
const cdnOrigin = new URL(cdnUrl).origin;
|
|
898
|
+
const mediaUrlParsed = new URL(urlStr);
|
|
899
|
+
if (mediaUrlParsed.origin === cdnOrigin) {
|
|
900
|
+
const keyPart = mediaUrlParsed.pathname.replace(/^\//, "");
|
|
901
|
+
return keyPart ? decodeURIComponent(keyPart) : null;
|
|
902
|
+
}
|
|
903
|
+
} catch {
|
|
893
904
|
}
|
|
894
905
|
}
|
|
895
906
|
const match = MEDIA_URL_PATTERN.exec(urlStr);
|
|
@@ -1859,7 +1870,7 @@ function resolveEmailLocale(raw) {
|
|
|
1859
1870
|
import { sha256hex as sha256hex3 } from "@beechcms/core";
|
|
1860
1871
|
var PASSWORD_RESET_TOKEN_EXPIRY_SECONDS = 30 * 60;
|
|
1861
1872
|
async function requestPasswordReset(context) {
|
|
1862
|
-
const { env, req } = context;
|
|
1873
|
+
const { env, req, executionCtx } = context;
|
|
1863
1874
|
const useSmtp = env.EMAIL_PROVIDER === "smtp";
|
|
1864
1875
|
if (!useSmtp && !env.RESEND_API_KEY) {
|
|
1865
1876
|
return context.json({ error: "Service not available" }, 503);
|
|
@@ -1902,21 +1913,28 @@ async function requestPasswordReset(context) {
|
|
|
1902
1913
|
const baseUrl = (env.APP_URL ?? new URL(req.url).origin).replace(/\/$/, "");
|
|
1903
1914
|
const resetUrl = `${baseUrl}/admin/reset-password?token=${resetToken}`;
|
|
1904
1915
|
const smtpBaseUrl = env.SMTP_HOST ? `http://${env.SMTP_HOST}:${env.SMTP_PORT ?? "8025"}` : void 0;
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1916
|
+
const sendNotification = async () => {
|
|
1917
|
+
try {
|
|
1918
|
+
await sendPasswordResetEmail({
|
|
1919
|
+
to: normalizedEmail,
|
|
1920
|
+
resetUrl,
|
|
1921
|
+
locale: emailLocale,
|
|
1922
|
+
apiKey: env.RESEND_API_KEY ?? "",
|
|
1923
|
+
from: env.EMAIL_FROM,
|
|
1924
|
+
isDev: env.ENV !== "production",
|
|
1925
|
+
provider: env.EMAIL_PROVIDER,
|
|
1926
|
+
smtpBaseUrl
|
|
1927
|
+
});
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
if (env.ENV !== "production") {
|
|
1930
|
+
console.error("[password-reset] Failed to send email:", error);
|
|
1931
|
+
}
|
|
1919
1932
|
}
|
|
1933
|
+
};
|
|
1934
|
+
try {
|
|
1935
|
+
executionCtx.waitUntil(sendNotification());
|
|
1936
|
+
} catch {
|
|
1937
|
+
void sendNotification();
|
|
1920
1938
|
}
|
|
1921
1939
|
return context.json({ success: true });
|
|
1922
1940
|
}
|
|
@@ -2153,6 +2171,26 @@ setupApp.post("/auth/setup", async (context) => {
|
|
|
2153
2171
|
});
|
|
2154
2172
|
}
|
|
2155
2173
|
}
|
|
2174
|
+
const passwordHash = await context.get("hashProvider").hash(password);
|
|
2175
|
+
const normalizedEmail = email.trim().toLowerCase();
|
|
2176
|
+
const normalizedName = typeof name === "string" ? name.trim() : null;
|
|
2177
|
+
const normalizedSurname = typeof surname === "string" ? surname.trim() : null;
|
|
2178
|
+
const created = await context.get("userRepository").createInitialAdmin({
|
|
2179
|
+
id: context.get("idGenerator").uuid(),
|
|
2180
|
+
email: normalizedEmail,
|
|
2181
|
+
passwordHash,
|
|
2182
|
+
role: "admin",
|
|
2183
|
+
name: normalizedName,
|
|
2184
|
+
surname: normalizedSurname
|
|
2185
|
+
});
|
|
2186
|
+
if (!created) {
|
|
2187
|
+
return publicProblem(context, {
|
|
2188
|
+
type: "setup-already-done",
|
|
2189
|
+
title: "Setup already completed",
|
|
2190
|
+
status: 403,
|
|
2191
|
+
detail: "An administrator account already exists. Initial setup can only be performed once."
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2156
2194
|
if (track === "developer" && loadDemoData === true) {
|
|
2157
2195
|
await context.get("demoDataRepository").loadDemoData();
|
|
2158
2196
|
const layout = {
|
|
@@ -2238,18 +2276,6 @@ setupApp.post("/auth/setup", async (context) => {
|
|
|
2238
2276
|
};
|
|
2239
2277
|
await context.get("dashboardLayoutRepository").upsert("default", layout, "system");
|
|
2240
2278
|
}
|
|
2241
|
-
const passwordHash = await context.get("hashProvider").hash(password);
|
|
2242
|
-
const normalizedEmail = email.trim().toLowerCase();
|
|
2243
|
-
const normalizedName = typeof name === "string" ? name.trim() : null;
|
|
2244
|
-
const normalizedSurname = typeof surname === "string" ? surname.trim() : null;
|
|
2245
|
-
await context.get("userRepository").create({
|
|
2246
|
-
id: context.get("idGenerator").uuid(),
|
|
2247
|
-
email: normalizedEmail,
|
|
2248
|
-
passwordHash,
|
|
2249
|
-
role: "admin",
|
|
2250
|
-
name: normalizedName,
|
|
2251
|
-
surname: normalizedSurname
|
|
2252
|
-
});
|
|
2253
2279
|
if (track === "normal" && company && typeof company === "object") {
|
|
2254
2280
|
const c = company;
|
|
2255
2281
|
const companyName = c.name.trim();
|
|
@@ -3131,7 +3157,8 @@ async function deleteSeedMediaObjects(context, slug, seed, schemaMutator) {
|
|
|
3131
3157
|
}
|
|
3132
3158
|
}
|
|
3133
3159
|
if (r2Keys.length > 0) {
|
|
3134
|
-
await deleteR2Objects(context, r2Keys).catch(() => {
|
|
3160
|
+
await deleteR2Objects(context, r2Keys).catch((error) => {
|
|
3161
|
+
console.warn(`Seed drop for '${slug}' left media rows out of sync:`, error);
|
|
3135
3162
|
});
|
|
3136
3163
|
}
|
|
3137
3164
|
} catch {
|
|
@@ -3620,10 +3647,13 @@ function interpolate(template, context, defaultValue = "", onMissing) {
|
|
|
3620
3647
|
if (val == null || val === "") {
|
|
3621
3648
|
return defaultValue;
|
|
3622
3649
|
}
|
|
3623
|
-
return String(val);
|
|
3650
|
+
return escapeHtml(String(val));
|
|
3624
3651
|
};
|
|
3625
3652
|
return template.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, replacer);
|
|
3626
3653
|
}
|
|
3654
|
+
function escapeHtml(input) {
|
|
3655
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3656
|
+
}
|
|
3627
3657
|
function resolvePath(obj, path) {
|
|
3628
3658
|
if (path in obj && obj[path] !== void 0) {
|
|
3629
3659
|
return obj[path];
|
|
@@ -5046,7 +5076,8 @@ webhooksApp.post("/qstash", async (context) => {
|
|
|
5046
5076
|
try {
|
|
5047
5077
|
const isValid = await receiver.verify({
|
|
5048
5078
|
signature,
|
|
5049
|
-
body
|
|
5079
|
+
body,
|
|
5080
|
+
url: context.req.url
|
|
5050
5081
|
});
|
|
5051
5082
|
if (!isValid) {
|
|
5052
5083
|
return context.text("Invalid signature", 401);
|
|
@@ -5304,7 +5335,7 @@ function publicRateLimitMiddleware() {
|
|
|
5304
5335
|
const remaining = path.slice("/api/v1/public/".length);
|
|
5305
5336
|
const firstSegment = remaining.split("/")[0];
|
|
5306
5337
|
if (firstSegment && firstSegment !== "health" && firstSegment !== "schema" && firstSegment !== "schema.html") {
|
|
5307
|
-
seed = firstSegment;
|
|
5338
|
+
seed = c.get("seedRegistry").get(firstSegment) ? firstSegment : "invalid-seed";
|
|
5308
5339
|
}
|
|
5309
5340
|
}
|
|
5310
5341
|
const key = `${getClientIp(c.req)}:${seed}:${limiterName}`;
|
|
@@ -5392,7 +5423,11 @@ function applyPublicPolicies(data, seed) {
|
|
|
5392
5423
|
const { public: isPublic, visibility } = resolvePolicies7(branch);
|
|
5393
5424
|
if (!isPublic) continue;
|
|
5394
5425
|
if (visibility === "hidden") continue;
|
|
5395
|
-
|
|
5426
|
+
if (visibility === "masked") {
|
|
5427
|
+
result[branch.alias] = typeof value === "string" && value.length > 0 ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : null;
|
|
5428
|
+
} else {
|
|
5429
|
+
result[branch.alias] = value;
|
|
5430
|
+
}
|
|
5396
5431
|
}
|
|
5397
5432
|
return result;
|
|
5398
5433
|
}
|
|
@@ -5447,6 +5482,7 @@ async function readSingleEntry(input) {
|
|
|
5447
5482
|
}
|
|
5448
5483
|
|
|
5449
5484
|
// src/public/query-builder.ts
|
|
5485
|
+
import { resolvePolicies as resolvePolicies8 } from "@beechcms/core";
|
|
5450
5486
|
var PUBLIC_FILTER_OPERATORS = /* @__PURE__ */ new Set([
|
|
5451
5487
|
"eq",
|
|
5452
5488
|
"neq",
|
|
@@ -5526,6 +5562,12 @@ function toEngineFilters2(seed, parsedFilter) {
|
|
|
5526
5562
|
if (!parsedFilter || parsedFilter.where.length === 0) return [];
|
|
5527
5563
|
return parsedFilter.where.map((cond) => {
|
|
5528
5564
|
const branch = seed.branches.find((b) => b.alias === cond.field);
|
|
5565
|
+
if (branch) {
|
|
5566
|
+
const { public: isPublic, filter: filterable } = resolvePolicies8(branch);
|
|
5567
|
+
if (!isPublic || !filterable) {
|
|
5568
|
+
throw new TypeError(`Invalid filter: field '${cond.field}' is not filterable`);
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5529
5571
|
const type = branch ? mapBranchToFilterType(branch.type) : SYSTEM_COLUMNS2.has(cond.field) ? "system" : "text";
|
|
5530
5572
|
return {
|
|
5531
5573
|
column: cond.field,
|
|
@@ -5633,7 +5675,7 @@ async function publicReadHandler(context) {
|
|
|
5633
5675
|
}
|
|
5634
5676
|
|
|
5635
5677
|
// src/public/public-add.ts
|
|
5636
|
-
import { isValidContentStatus as isValidContentStatus3, SlugConflictError as SlugConflictError2 } from "@beechcms/core";
|
|
5678
|
+
import { isValidContentStatus as isValidContentStatus3, resolvePolicies as resolvePolicies9, SlugConflictError as SlugConflictError2 } from "@beechcms/core";
|
|
5637
5679
|
|
|
5638
5680
|
// src/public/slug-utils.ts
|
|
5639
5681
|
import { slugify as slugify3, generateEntrySlug } from "@beechcms/core";
|
|
@@ -5718,6 +5760,13 @@ async function publicAddHandler(context) {
|
|
|
5718
5760
|
if (!isValidContentStatus3(statusValue)) {
|
|
5719
5761
|
return publicProblem(context, { type: "invalid-status", title: "Bad Request", status: 400, detail: "Invalid status. Allowed values are: draft, review, published" });
|
|
5720
5762
|
}
|
|
5763
|
+
const sensitiveAliases = Object.keys(rawData).filter((alias) => {
|
|
5764
|
+
const branch = seed.branches.find((b) => b.alias === alias);
|
|
5765
|
+
return branch != null && resolvePolicies9(branch).public === false;
|
|
5766
|
+
});
|
|
5767
|
+
if (sensitiveAliases.length > 0) {
|
|
5768
|
+
return publicProblem(context, { type: "sensitive-field-edit", title: "Unprocessable Entity", status: 422, detail: `Cannot write internal fields: ${sensitiveAliases.join(", ")}` });
|
|
5769
|
+
}
|
|
5721
5770
|
const sanitized = sanitizePublicPayload(seed, rawData, { operation: "create", allowNull: false, requireAtLeastOneValidField: true, enforceRequiredFields: true });
|
|
5722
5771
|
if (!sanitized.ok) {
|
|
5723
5772
|
if (sanitized.status === 422) {
|
|
@@ -5725,8 +5774,17 @@ async function publicAddHandler(context) {
|
|
|
5725
5774
|
}
|
|
5726
5775
|
return publicProblem(context, { type: sanitized.code, title: "Bad Request", status: 400, detail: sanitized.message, errors: sanitized.details });
|
|
5727
5776
|
}
|
|
5777
|
+
let privacyData;
|
|
5778
|
+
try {
|
|
5779
|
+
privacyData = await applyPrivacy(sanitized.data, seed);
|
|
5780
|
+
} catch (error) {
|
|
5781
|
+
if (error instanceof PrivacyPolicyError) {
|
|
5782
|
+
return publicProblem(context, { type: "policy-not-implemented", title: "Not Implemented", status: 501, detail: error.message });
|
|
5783
|
+
}
|
|
5784
|
+
throw error;
|
|
5785
|
+
}
|
|
5728
5786
|
const idempotencyKey = parseIdempotencyKey(context.req.header("Idempotency-Key"));
|
|
5729
|
-
const finalSlug = pickSlug(body,
|
|
5787
|
+
const finalSlug = pickSlug(body, privacyData) || context.get("idGenerator").uuid().slice(0, 8);
|
|
5730
5788
|
const repository = context.get("repository");
|
|
5731
5789
|
const idempotencyRepository = context.get("idempotencyRepository");
|
|
5732
5790
|
try {
|
|
@@ -5750,7 +5808,7 @@ async function publicAddHandler(context) {
|
|
|
5750
5808
|
}
|
|
5751
5809
|
const id = context.get("idGenerator").uuid();
|
|
5752
5810
|
try {
|
|
5753
|
-
await repository.create(seed, id, finalSlug, statusValue,
|
|
5811
|
+
await repository.create(seed, id, finalSlug, statusValue, privacyData);
|
|
5754
5812
|
} catch (error) {
|
|
5755
5813
|
if (error instanceof SlugConflictError2) {
|
|
5756
5814
|
return publicProblem(context, { type: "slug-conflict", title: "Conflict", status: 409, detail: `An entry with slug '${finalSlug}' already exists for content type '${seedSlug}'.` });
|
|
@@ -5763,7 +5821,7 @@ async function publicAddHandler(context) {
|
|
|
5763
5821
|
}
|
|
5764
5822
|
context.get("notificationService").notify({
|
|
5765
5823
|
title: `${seed.label}: New entry`,
|
|
5766
|
-
message: `A new entry ("${
|
|
5824
|
+
message: `A new entry ("${privacyData.title || privacyData.name || finalSlug}") has been added via the public API.`,
|
|
5767
5825
|
type: "success"
|
|
5768
5826
|
});
|
|
5769
5827
|
return context.json(responseBody, 201);
|
|
@@ -5774,7 +5832,7 @@ async function publicAddHandler(context) {
|
|
|
5774
5832
|
}
|
|
5775
5833
|
|
|
5776
5834
|
// src/public/public-edit.ts
|
|
5777
|
-
import { isValidContentStatus as isValidContentStatus4, resolvePolicies as
|
|
5835
|
+
import { isValidContentStatus as isValidContentStatus4, resolvePolicies as resolvePolicies10, EntryNotFoundError as EntryNotFoundError7 } from "@beechcms/core";
|
|
5778
5836
|
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5779
5837
|
function errorMessage(context, error) {
|
|
5780
5838
|
if (context.env.ENV !== "production" && error instanceof Error) return error.message;
|
|
@@ -5824,7 +5882,7 @@ function resolveData(context, seed, body) {
|
|
|
5824
5882
|
const sensitiveAliases = Object.keys(rawData).filter((alias) => {
|
|
5825
5883
|
const branch = seed.branches.find((b) => b.alias === alias);
|
|
5826
5884
|
if (!branch) return false;
|
|
5827
|
-
const policies =
|
|
5885
|
+
const policies = resolvePolicies10(branch);
|
|
5828
5886
|
return policies.privacy !== "plain" || policies.public === false;
|
|
5829
5887
|
});
|
|
5830
5888
|
if (sensitiveAliases.length > 0) {
|
|
@@ -6782,21 +6840,12 @@ var D1ContentRepository = class extends BaseD1Repository {
|
|
|
6782
6840
|
const draftTableName = this.getTableName(seed.slug, true);
|
|
6783
6841
|
const mRelBranches = multiRelBranches(seed);
|
|
6784
6842
|
const mRelAliases = new Set(mRelBranches.map((b) => b.alias));
|
|
6785
|
-
|
|
6786
|
-
try {
|
|
6787
|
-
const existing = await this.database.prepare(`SELECT _touched_fields FROM ${draftTableName} WHERE entry_id = ?`).bind(entryId).first();
|
|
6788
|
-
if (existing && existing._touched_fields) {
|
|
6789
|
-
existingTouched = JSON.parse(existing._touched_fields);
|
|
6790
|
-
}
|
|
6791
|
-
} catch {
|
|
6792
|
-
}
|
|
6793
|
-
const currentTouched = new Set(existingTouched);
|
|
6843
|
+
const thisCallTouched = [];
|
|
6794
6844
|
for (const branch of seed.branches) {
|
|
6795
6845
|
if (Object.hasOwn(data, branch.alias)) {
|
|
6796
|
-
|
|
6846
|
+
thisCallTouched.push(branch.alias);
|
|
6797
6847
|
}
|
|
6798
6848
|
}
|
|
6799
|
-
const updatedTouchedFields = Array.from(currentTouched);
|
|
6800
6849
|
const columnNames = ["entry_id"];
|
|
6801
6850
|
const placeholders = ["?"];
|
|
6802
6851
|
const queryBindings = [entryId];
|
|
@@ -6813,8 +6862,14 @@ var D1ContentRepository = class extends BaseD1Repository {
|
|
|
6813
6862
|
}
|
|
6814
6863
|
columnNames.push("_touched_fields");
|
|
6815
6864
|
placeholders.push("?");
|
|
6816
|
-
queryBindings.push(JSON.stringify(
|
|
6817
|
-
updateClauses.push(`_touched_fields =
|
|
6865
|
+
queryBindings.push(JSON.stringify(thisCallTouched));
|
|
6866
|
+
updateClauses.push(`_touched_fields = (
|
|
6867
|
+
SELECT json_group_array(value) FROM (
|
|
6868
|
+
SELECT value FROM json_each(COALESCE(_touched_fields, '[]'))
|
|
6869
|
+
UNION
|
|
6870
|
+
SELECT value FROM json_each(excluded._touched_fields)
|
|
6871
|
+
)
|
|
6872
|
+
)`);
|
|
6818
6873
|
updateClauses.push("updated_at = (unixepoch())");
|
|
6819
6874
|
const sql = `
|
|
6820
6875
|
INSERT INTO ${draftTableName} (${columnNames.join(", ")})
|
|
@@ -7304,6 +7359,18 @@ var D1UserRepository = class {
|
|
|
7304
7359
|
async create(user) {
|
|
7305
7360
|
await this.db.prepare("INSERT INTO users (id, email, password_hash, role, name, surname) VALUES (?, ?, ?, ?, ?, ?)").bind(user.id, user.email, user.passwordHash, user.role, user.name, user.surname).run();
|
|
7306
7361
|
}
|
|
7362
|
+
async createInitialAdmin(user) {
|
|
7363
|
+
try {
|
|
7364
|
+
await this.db.batch([
|
|
7365
|
+
this.db.prepare("INSERT INTO setup_completed (id) VALUES (1)"),
|
|
7366
|
+
this.db.prepare("INSERT INTO users (id, email, password_hash, role, name, surname) VALUES (?, ?, ?, ?, ?, ?)").bind(user.id, user.email, user.passwordHash, user.role, user.name, user.surname)
|
|
7367
|
+
]);
|
|
7368
|
+
return true;
|
|
7369
|
+
} catch (err) {
|
|
7370
|
+
if (err instanceof Error && err.message.includes("UNIQUE constraint failed")) return false;
|
|
7371
|
+
throw err;
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
7307
7374
|
async updateProfile(userId, fields) {
|
|
7308
7375
|
const columnAssignments = [];
|
|
7309
7376
|
const boundValues = [];
|
|
@@ -6,6 +6,7 @@ export declare class D1UserRepository implements IUserRepository {
|
|
|
6
6
|
findById(userId: string): Promise<UserRecord | null>;
|
|
7
7
|
findByEmail(email: string): Promise<UserRecord | null>;
|
|
8
8
|
create(user: NewUserInput): Promise<void>;
|
|
9
|
+
createInitialAdmin(user: NewUserInput): Promise<boolean>;
|
|
9
10
|
updateProfile(userId: string, fields: {
|
|
10
11
|
name?: string;
|
|
11
12
|
surname?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beechcms/api",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/factory.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@aws-sdk/client-s3": "^3.995.0",
|
|
22
22
|
"@aws-sdk/s3-request-presigner": "^3.995.0",
|
|
23
|
-
"@beechcms/core": "^0.6.
|
|
23
|
+
"@beechcms/core": "^0.6.3",
|
|
24
24
|
"@upstash/qstash": "^2.11.0",
|
|
25
25
|
"bcryptjs": "^2.4.3",
|
|
26
26
|
"hono": "^4.12.21",
|