@easy-cms/core 0.29.0 → 0.30.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.
- package/dist/index.d.ts +59 -1
- package/dist/index.js +216 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1065,6 +1065,18 @@ declare class EasyCMS<C extends Config = Config> {
|
|
|
1065
1065
|
})[]>;
|
|
1066
1066
|
private pendingJobs;
|
|
1067
1067
|
private removeJob;
|
|
1068
|
+
/**
|
|
1069
|
+
* @internal One attempt now for a saved webhook delivery or email (the admin's Retry): removed
|
|
1070
|
+
* when it is sent, otherwise kept as failed with the new error.
|
|
1071
|
+
*/
|
|
1072
|
+
retryDelivery(kind: 'webhook' | 'email', id: ID): Promise<{
|
|
1073
|
+
ok: true;
|
|
1074
|
+
} | {
|
|
1075
|
+
ok: false;
|
|
1076
|
+
error: string;
|
|
1077
|
+
}>;
|
|
1078
|
+
/** Removes webhook deliveries and emails that failed more than 30 days ago. */
|
|
1079
|
+
private pruneFailedDeliveries;
|
|
1068
1080
|
/** @internal */
|
|
1069
1081
|
collection(slug: string): CollectionConfig;
|
|
1070
1082
|
private global;
|
|
@@ -2027,6 +2039,47 @@ declare function applyPlugins(input: Config): Promise<Config>;
|
|
|
2027
2039
|
*/
|
|
2028
2040
|
declare function resolveConfig(input: Config | ResolvedConfig): Promise<ResolvedConfig>;
|
|
2029
2041
|
|
|
2042
|
+
type DeliveryKind = 'webhook' | 'email';
|
|
2043
|
+
type DeliveryState = 'failed' | 'pending';
|
|
2044
|
+
interface DeliveryBase {
|
|
2045
|
+
id: ID;
|
|
2046
|
+
state: DeliveryState;
|
|
2047
|
+
attempts: number;
|
|
2048
|
+
/** The last attempt's error. */
|
|
2049
|
+
error: string | null;
|
|
2050
|
+
createdAt: string;
|
|
2051
|
+
updatedAt: string;
|
|
2052
|
+
/** When a pending one is tried again. */
|
|
2053
|
+
nextAttemptAt: string;
|
|
2054
|
+
}
|
|
2055
|
+
/** A webhook delivery as the admin shows it (`GET <api>/admin/deliveries?kind=webhook`). */
|
|
2056
|
+
interface AdminWebhookDelivery extends DeliveryBase {
|
|
2057
|
+
kind: 'webhook';
|
|
2058
|
+
url: string;
|
|
2059
|
+
event: string;
|
|
2060
|
+
/** What changed: a document of a collection, or a global. */
|
|
2061
|
+
collection?: string;
|
|
2062
|
+
doc?: ID;
|
|
2063
|
+
global?: string;
|
|
2064
|
+
/** The JSON body as sent. */
|
|
2065
|
+
body: string;
|
|
2066
|
+
}
|
|
2067
|
+
/** An email as the admin shows it: who to and the subject, not the content. */
|
|
2068
|
+
interface AdminEmailDelivery extends DeliveryBase {
|
|
2069
|
+
kind: 'email';
|
|
2070
|
+
to: string[];
|
|
2071
|
+
subject: string;
|
|
2072
|
+
}
|
|
2073
|
+
type AdminDelivery = AdminWebhookDelivery | AdminEmailDelivery;
|
|
2074
|
+
interface AdminDeliveries {
|
|
2075
|
+
docs: AdminDelivery[];
|
|
2076
|
+
totalDocs: number;
|
|
2077
|
+
page: number;
|
|
2078
|
+
totalPages: number;
|
|
2079
|
+
/** How many there are of each state, for the filter. */
|
|
2080
|
+
counts: Record<DeliveryState, number>;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2030
2083
|
/**
|
|
2031
2084
|
* Finds the file of an admin module (`admin.modules`): a package export or a path relative to
|
|
2032
2085
|
* the project root. Looks from the project root first, then from the server's entry file
|
|
@@ -2170,6 +2223,11 @@ interface AdminSchema {
|
|
|
2170
2223
|
modules: string[];
|
|
2171
2224
|
/** Files can be uploaded from links (`upload.fromURL`). */
|
|
2172
2225
|
uploadFromURL: boolean;
|
|
2226
|
+
/** For admins: which saved deliveries the admin can show (webhooks, emails). */
|
|
2227
|
+
deliveries?: {
|
|
2228
|
+
webhook: boolean;
|
|
2229
|
+
email: boolean;
|
|
2230
|
+
};
|
|
2173
2231
|
/** Pages this user may open (`admin.pages`). */
|
|
2174
2232
|
pages: AdminPageRef[];
|
|
2175
2233
|
/** Dashboard panels this user may see (`admin.dashboard`). */
|
|
@@ -2262,4 +2320,4 @@ declare function validateConfig(config: Config): ConfigIssue[];
|
|
|
2262
2320
|
/** The version of `@easy-cms/core`, set when the package is built. */
|
|
2263
2321
|
declare const VERSION: string;
|
|
2264
2322
|
|
|
2265
|
-
export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminSchema, type AdminStatus, type AdminViewAccess, type AdminWidgetRef, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ApiKeyContext, type ApiKeyGlobalOperation, type ApiKeyOperation, type ApiKeyPermissions, type ArrayField, Auth, type AuthConfig, type AuthUser, BUILTIN_COLLECTIONS, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinApiKeysCollection, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CliCommand, type CollectionAccess, type CollectionAdmin, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionListAdmin, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CopyProgress, type CopyResult, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, type CustomField, type CustomFieldTypes, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_PASSWORD_EMAILS, DEFAULT_TOKEN_EXPIRATION, type DashboardWidget, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DatabaseTransfer, type DateField, type DepthOptions, type DocumentOf, EXTENSIONS, EasyCMS, EasyCMSError, type EmailAdapter, type EmailField, type EmailMessage, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldType, type FieldTypeBase, type FieldTypeDefinition, type FieldTypeValidateContext, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, type FilterOptions, type FilterOptionsArgs, type FindArgs, type FindOptions, ForbiddenError, type GlobalAccess, type GlobalConfig, type GlobalDocument, type GlobalHooks, type GlobalInput, type GlobalSlug, type GroupField, type ID, INTERNAL_COLLECTIONS, type ImageSize, type InferCollection, type InferGlobal, type JsonField, type Label, type LivePreview, type LoadConfigOptions, type LocalStorageOptions, type LocalizationConfig, type Logger, type LoginArgs, MAX_DEPTH, MEDIA, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, type MediaDocument, type MediaSize, type MigrationInfo, NotFoundError, type NumberField, type Operation, type PaginatedDocs, type PasswordEmail, type PasswordEmailArgs, type PasswordEmailFn, type PasswordLinkOptions, PayloadTooLargeError, type Plugin, type PluginInfo, type PluginTypes, type PreviewTarget, type PreviewURL, QueryError, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoutesConfig, SESSION_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, VERSION, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, configSignature, consoleEmail, consoleLogger, copyDatabase, createEasyCMS, createRestHandler, createRootEndpointHandler, defineConfig, defineFieldType, definePlugin, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isHasMany, isLoggedIn, keyAllows, loadConfig, localStorage, mergeForUpdate, mimeAllowed, mimeAllowedBy, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword };
|
|
2323
|
+
export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminDeliveries, type AdminDelivery, type AdminEmailDelivery, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminSchema, type AdminStatus, type AdminViewAccess, type AdminWebhookDelivery, type AdminWidgetRef, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ApiKeyContext, type ApiKeyGlobalOperation, type ApiKeyOperation, type ApiKeyPermissions, type ArrayField, Auth, type AuthConfig, type AuthUser, BUILTIN_COLLECTIONS, type BeforeChangeHook, type BeforeDeleteHook, type BeforeValidateHook, type Block, type BlocksField, type BooleanField, type BuiltinApiKeysCollection, type BuiltinMediaCollection, type BuiltinUsersCollection, type BundleHints, CONFIG_FILE_NAMES, CSRF_COOKIE, CSRF_HEADER, type CliCommand, type CollectionAccess, type CollectionAdmin, type CollectionConfig, type CollectionDocument, type CollectionHooks, type CollectionListAdmin, type CollectionSlug, type Config, ConfigError, type ConfigIssue, type ContainerAdmin, type CopyProgress, type CopyResult, type CreateEasyCMSOptions, type CreateInput, type CreateMigrationResult, type CustomField, type CustomFieldTypes, DEFAULT_ADMIN_PATH, DEFAULT_API_PATH, DEFAULT_DEPTH, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_VERSIONS, DEFAULT_PASSWORD_EMAILS, DEFAULT_TOKEN_EXPIRATION, type DashboardWidget, type Database, type DatabaseAdapter, type DatabaseInitArgs, type DatabaseTransfer, type DateField, type DeliveryKind, type DeliveryState, type DepthOptions, type DocumentOf, EXTENSIONS, EasyCMS, EasyCMSError, type EmailAdapter, type EmailField, type EmailMessage, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldType, type FieldTypeBase, type FieldTypeDefinition, type FieldTypeValidateContext, type FieldValidate, type FieldValidateContext, type FieldValue, type FieldsInput, type FieldsValue, type FilterOptions, type FilterOptionsArgs, type FindArgs, type FindOptions, ForbiddenError, type GlobalAccess, type GlobalConfig, type GlobalDocument, type GlobalHooks, type GlobalInput, type GlobalSlug, type GroupField, type ID, INTERNAL_COLLECTIONS, type ImageSize, type InferCollection, type InferGlobal, type JsonField, type Label, type LivePreview, type LoadConfigOptions, type LocalStorageOptions, type LocalizationConfig, type Logger, type LoginArgs, MAX_DEPTH, MEDIA, MIN_PASSWORD_LENGTH, MIN_SECRET_LENGTH, type MediaDocument, type MediaSize, type MigrationInfo, NotFoundError, type NumberField, type Operation, type PaginatedDocs, type PasswordEmail, type PasswordEmailArgs, type PasswordEmailFn, type PasswordLinkOptions, PayloadTooLargeError, type Plugin, type PluginInfo, type PluginTypes, type PreviewTarget, type PreviewURL, QueryError, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoutesConfig, SESSION_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, VERSION, ValidationError, type Version, type VersionSummary, type VersionsConfig, WEBHOOK_EVENTS, type WebhookConfig, type WebhookEvent, type WebhookPayload, type Where, type WhereOperators, anyone, applyDefaults, applyPlugins, configSignature, consoleEmail, consoleLogger, copyDatabase, createEasyCMS, createRestHandler, createRootEndpointHandler, defineConfig, defineFieldType, definePlugin, fillMissing, findConfigFile, generateSlugs, generateTypes, hasRows, hashPassword, imageDimensions, importConfig, isAdmin, isHasMany, isLoggedIn, keyAllows, loadConfig, localStorage, mergeForUpdate, mimeAllowed, mimeAllowedBy, parseId, populate, readCookie, resolveAdminModule, resolveConfig, rowFields, silentLogger, singularize, slugify, sniffMimeType, validateConfig, validateFields, verifyPassword };
|
package/dist/index.js
CHANGED
|
@@ -1636,6 +1636,11 @@ var Mailer = class {
|
|
|
1636
1636
|
}
|
|
1637
1637
|
return { sent, failed };
|
|
1638
1638
|
}
|
|
1639
|
+
/** One attempt now for a saved email (the admin's Retry): `undefined` when sent, else the error. */
|
|
1640
|
+
async sendNow(message) {
|
|
1641
|
+
if (!this.adapter) return "email is not set up (`email` in the config)";
|
|
1642
|
+
return this.attempt(JSON.parse(message));
|
|
1643
|
+
}
|
|
1639
1644
|
track(delivery) {
|
|
1640
1645
|
const tracked = delivery.catch((error) => this.logger.error(`Email queue failed: ${error.message}`)).finally(() => this.pending.delete(tracked));
|
|
1641
1646
|
this.pending.add(tracked);
|
|
@@ -1876,6 +1881,15 @@ var Webhooks = class {
|
|
|
1876
1881
|
}
|
|
1877
1882
|
return { sent, failed };
|
|
1878
1883
|
}
|
|
1884
|
+
/**
|
|
1885
|
+
* One attempt now for a saved delivery (the admin's Retry), with the webhook's current headers
|
|
1886
|
+
* and secret.
|
|
1887
|
+
*/
|
|
1888
|
+
async sendNow(delivery) {
|
|
1889
|
+
const hook = this.hooks.find((h) => h.url === delivery.url);
|
|
1890
|
+
if (!hook) return { ok: false, error: "this webhook is no longer in the config" };
|
|
1891
|
+
return this.attempt(hook, delivery.event, delivery.body, delivery.delivery);
|
|
1892
|
+
}
|
|
1879
1893
|
track(delivery) {
|
|
1880
1894
|
const tracked = delivery.catch((error) => this.logger.error(`Webhook queue failed: ${error.message}`)).finally(() => this.pending.delete(tracked));
|
|
1881
1895
|
this.pending.add(tracked);
|
|
@@ -4425,6 +4439,7 @@ var EasyCMS = class {
|
|
|
4425
4439
|
*/
|
|
4426
4440
|
async runJobs(now = /* @__PURE__ */ new Date()) {
|
|
4427
4441
|
const scheduled = await this.runScheduled(now);
|
|
4442
|
+
await this.pruneFailedDeliveries(now);
|
|
4428
4443
|
return {
|
|
4429
4444
|
...scheduled,
|
|
4430
4445
|
webhooks: await this.retryWebhooks(now),
|
|
@@ -4653,6 +4668,51 @@ var EasyCMS = class {
|
|
|
4653
4668
|
if (!job) throw new NotFoundError("scheduled job", jobId);
|
|
4654
4669
|
await this.db.delete({ collection: SCHEDULED_JOBS, id: job.id });
|
|
4655
4670
|
}
|
|
4671
|
+
/**
|
|
4672
|
+
* @internal One attempt now for a saved webhook delivery or email (the admin's Retry): removed
|
|
4673
|
+
* when it is sent, otherwise kept as failed with the new error.
|
|
4674
|
+
*/
|
|
4675
|
+
async retryDelivery(kind, id) {
|
|
4676
|
+
const collection2 = kind === "webhook" ? WEBHOOK_DELIVERIES : EMAIL_DELIVERIES;
|
|
4677
|
+
const row = await this.db.findById({ collection: collection2, id });
|
|
4678
|
+
if (!row) throw new NotFoundError(collection2, id);
|
|
4679
|
+
const error = kind === "webhook" ? await this.webhooks.sendNow(row).then((result) => result.ok ? void 0 : result.error) : await this.mailer.sendNow(String(row.message));
|
|
4680
|
+
if (error === void 0) {
|
|
4681
|
+
await this.db.delete({ collection: collection2, id });
|
|
4682
|
+
return { ok: true };
|
|
4683
|
+
}
|
|
4684
|
+
const { id: _id, ...data } = row;
|
|
4685
|
+
await this.db.update({
|
|
4686
|
+
collection: collection2,
|
|
4687
|
+
id,
|
|
4688
|
+
data: {
|
|
4689
|
+
...data,
|
|
4690
|
+
attempts: Number(row.attempts ?? 0) + 1,
|
|
4691
|
+
state: "failed",
|
|
4692
|
+
error,
|
|
4693
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
4696
|
+
return { ok: false, error };
|
|
4697
|
+
}
|
|
4698
|
+
/** Removes webhook deliveries and emails that failed more than 30 days ago. */
|
|
4699
|
+
async pruneFailedDeliveries(now) {
|
|
4700
|
+
const before = new Date(now.getTime() - 30 * 864e5).toISOString();
|
|
4701
|
+
for (const collection2 of [WEBHOOK_DELIVERIES, EMAIL_DELIVERIES]) {
|
|
4702
|
+
if (!this.config.collections.some((c) => c.slug === collection2)) continue;
|
|
4703
|
+
for (; ; ) {
|
|
4704
|
+
const { docs } = await this.db.find({
|
|
4705
|
+
collection: collection2,
|
|
4706
|
+
where: { and: [{ state: { equals: "failed" } }, { updatedAt: { lt: before } }] },
|
|
4707
|
+
sort: ["updatedAt"],
|
|
4708
|
+
limit: 100,
|
|
4709
|
+
page: 1
|
|
4710
|
+
});
|
|
4711
|
+
for (const doc of docs) await this.db.delete({ collection: collection2, id: doc.id });
|
|
4712
|
+
if (docs.length < 100) break;
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4656
4716
|
// -------------------------------------------------------------------------
|
|
4657
4717
|
/** @internal */
|
|
4658
4718
|
collection(slug) {
|
|
@@ -5204,6 +5264,123 @@ async function readAdminModule(cms, index) {
|
|
|
5204
5264
|
return { body, etag: `"${createHash3("sha1").update(body).digest("base64url")}"` };
|
|
5205
5265
|
}
|
|
5206
5266
|
|
|
5267
|
+
// src/rest/admin-deliveries.ts
|
|
5268
|
+
var PER_PAGE = 50;
|
|
5269
|
+
var collectionOf = (kind) => kind === "webhook" ? WEBHOOK_DELIVERIES : EMAIL_DELIVERIES;
|
|
5270
|
+
var exists = (cms, kind) => cms.config.collections.some((c) => c.slug === collectionOf(kind));
|
|
5271
|
+
var isDeliveryKind = (value) => value === "webhook" || value === "email";
|
|
5272
|
+
function parse(text) {
|
|
5273
|
+
try {
|
|
5274
|
+
const value = JSON.parse(String(text ?? ""));
|
|
5275
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
5276
|
+
} catch {
|
|
5277
|
+
return {};
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
function toAdmin(kind, row) {
|
|
5281
|
+
const base = {
|
|
5282
|
+
id: row.id,
|
|
5283
|
+
state: row.state === "failed" ? "failed" : "pending",
|
|
5284
|
+
attempts: Number(row.attempts ?? 0),
|
|
5285
|
+
error: typeof row.error === "string" ? row.error : null,
|
|
5286
|
+
createdAt: String(row.createdAt ?? ""),
|
|
5287
|
+
updatedAt: String(row.updatedAt ?? ""),
|
|
5288
|
+
nextAttemptAt: String(row.nextAttemptAt ?? "")
|
|
5289
|
+
};
|
|
5290
|
+
if (kind === "webhook") {
|
|
5291
|
+
const payload = parse(row.body);
|
|
5292
|
+
return {
|
|
5293
|
+
...base,
|
|
5294
|
+
kind,
|
|
5295
|
+
url: String(row.url ?? ""),
|
|
5296
|
+
event: String(row.event ?? ""),
|
|
5297
|
+
...typeof payload.collection === "string" ? { collection: payload.collection } : {},
|
|
5298
|
+
...typeof payload.id === "string" || typeof payload.id === "number" ? { doc: payload.id } : {},
|
|
5299
|
+
...typeof payload.global === "string" ? { global: payload.global } : {},
|
|
5300
|
+
body: String(row.body ?? "")
|
|
5301
|
+
};
|
|
5302
|
+
}
|
|
5303
|
+
const message = parse(row.message);
|
|
5304
|
+
const to = message.to;
|
|
5305
|
+
return {
|
|
5306
|
+
...base,
|
|
5307
|
+
kind,
|
|
5308
|
+
to: Array.isArray(to) ? to.map(String) : typeof to === "string" ? [to] : [],
|
|
5309
|
+
subject: String(message.subject ?? "")
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
async function listDeliveries(cms, kind, state, page) {
|
|
5313
|
+
if (!exists(cms, kind))
|
|
5314
|
+
return { docs: [], totalDocs: 0, page: 1, totalPages: 0, counts: { failed: 0, pending: 0 } };
|
|
5315
|
+
const collection2 = collectionOf(kind);
|
|
5316
|
+
const [found, failed, pending] = await Promise.all([
|
|
5317
|
+
cms.db.find({
|
|
5318
|
+
collection: collection2,
|
|
5319
|
+
where: { state: { equals: state } },
|
|
5320
|
+
sort: ["-updatedAt"],
|
|
5321
|
+
limit: PER_PAGE,
|
|
5322
|
+
page
|
|
5323
|
+
}),
|
|
5324
|
+
cms.db.count({ collection: collection2, where: { state: { equals: "failed" } } }),
|
|
5325
|
+
cms.db.count({ collection: collection2, where: { state: { equals: "pending" } } })
|
|
5326
|
+
]);
|
|
5327
|
+
return {
|
|
5328
|
+
docs: found.docs.map((row) => toAdmin(kind, row)),
|
|
5329
|
+
totalDocs: found.totalDocs,
|
|
5330
|
+
page: found.page,
|
|
5331
|
+
totalPages: found.totalPages,
|
|
5332
|
+
counts: { failed, pending }
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5335
|
+
async function retryFailedDeliveries(cms, kind) {
|
|
5336
|
+
if (!exists(cms, kind)) return { sent: 0, failed: 0 };
|
|
5337
|
+
const collection2 = collectionOf(kind);
|
|
5338
|
+
const ids = [];
|
|
5339
|
+
for (let page = 1; ; page++) {
|
|
5340
|
+
const { docs, hasNextPage } = await cms.db.find({
|
|
5341
|
+
collection: collection2,
|
|
5342
|
+
where: { state: { equals: "failed" } },
|
|
5343
|
+
sort: ["updatedAt"],
|
|
5344
|
+
limit: 500,
|
|
5345
|
+
page
|
|
5346
|
+
});
|
|
5347
|
+
ids.push(...docs.map((row) => row.id));
|
|
5348
|
+
if (!hasNextPage) break;
|
|
5349
|
+
}
|
|
5350
|
+
let sent = 0;
|
|
5351
|
+
let failed = 0;
|
|
5352
|
+
for (const id of ids) {
|
|
5353
|
+
const result = await cms.retryDelivery(kind, id);
|
|
5354
|
+
if (result.ok) sent++;
|
|
5355
|
+
else failed++;
|
|
5356
|
+
}
|
|
5357
|
+
return { sent, failed };
|
|
5358
|
+
}
|
|
5359
|
+
async function deleteDelivery(cms, kind, id) {
|
|
5360
|
+
const collection2 = collectionOf(kind);
|
|
5361
|
+
if (!exists(cms, kind) || !await cms.db.findById({ collection: collection2, id }))
|
|
5362
|
+
throw new NotFoundError(collection2, id);
|
|
5363
|
+
await cms.db.delete({ collection: collection2, id });
|
|
5364
|
+
}
|
|
5365
|
+
async function deleteFailedDeliveries(cms, kind) {
|
|
5366
|
+
if (!exists(cms, kind)) return { deleted: 0 };
|
|
5367
|
+
const collection2 = collectionOf(kind);
|
|
5368
|
+
let deleted = 0;
|
|
5369
|
+
for (; ; ) {
|
|
5370
|
+
const { docs } = await cms.db.find({
|
|
5371
|
+
collection: collection2,
|
|
5372
|
+
where: { state: { equals: "failed" } },
|
|
5373
|
+
sort: ["updatedAt"],
|
|
5374
|
+
limit: 100,
|
|
5375
|
+
page: 1
|
|
5376
|
+
});
|
|
5377
|
+
for (const row of docs) await cms.db.delete({ collection: collection2, id: row.id });
|
|
5378
|
+
deleted += docs.length;
|
|
5379
|
+
if (docs.length < 100) break;
|
|
5380
|
+
}
|
|
5381
|
+
return { deleted };
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5207
5384
|
// src/rest/admin-schema.ts
|
|
5208
5385
|
var SETTINGS = /* @__PURE__ */ new Set([USERS, API_KEYS]);
|
|
5209
5386
|
function componentRef(component) {
|
|
@@ -5355,6 +5532,12 @@ async function adminSchema(cms, user, origin) {
|
|
|
5355
5532
|
globals: await Promise.all(cms.config.globals.map((g) => global(g, user, localized))),
|
|
5356
5533
|
modules: adminModuleUrls(cms),
|
|
5357
5534
|
uploadFromURL: cms.config.upload.fromURL !== void 0,
|
|
5535
|
+
...user.role === "admin" && !user.apiKey ? {
|
|
5536
|
+
deliveries: {
|
|
5537
|
+
webhook: cms.config.collections.some((c) => c.slug === WEBHOOK_DELIVERIES),
|
|
5538
|
+
email: cms.config.collections.some((c) => c.slug === EMAIL_DELIVERIES)
|
|
5539
|
+
}
|
|
5540
|
+
} : {},
|
|
5358
5541
|
pages: await pages(cms, user),
|
|
5359
5542
|
dashboard: await widgets(cms, user)
|
|
5360
5543
|
};
|
|
@@ -5391,7 +5574,7 @@ async function widgets(cms, user) {
|
|
|
5391
5574
|
}
|
|
5392
5575
|
|
|
5393
5576
|
// src/version.ts
|
|
5394
|
-
var VERSION = true ? "0.
|
|
5577
|
+
var VERSION = true ? "0.30.0" : "0.0.0-dev";
|
|
5395
5578
|
|
|
5396
5579
|
// src/rest/admin-status.ts
|
|
5397
5580
|
var DAY = 864e5;
|
|
@@ -5831,6 +6014,38 @@ async function route(cms, ctx, method, segments, options) {
|
|
|
5831
6014
|
}
|
|
5832
6015
|
return { body: await cms.runJobs() };
|
|
5833
6016
|
}
|
|
6017
|
+
if (first === "admin" && second === "deliveries") {
|
|
6018
|
+
if (!ctx.user) throw new UnauthorizedError();
|
|
6019
|
+
if (ctx.user.role !== "admin" || ctx.user.apiKey) throw new ForbiddenError();
|
|
6020
|
+
const [, , kind, id2, action, extra] = segments;
|
|
6021
|
+
if (kind === void 0) {
|
|
6022
|
+
if (method !== "GET") throw methodNotAllowed(ctx, "GET");
|
|
6023
|
+
const k = ctx.url.searchParams.get("kind") ?? "webhook";
|
|
6024
|
+
const state = ctx.url.searchParams.get("state") === "pending" ? "pending" : "failed";
|
|
6025
|
+
if (!isDeliveryKind(k)) throw new HttpError("kind must be webhook or email", 400);
|
|
6026
|
+
const page = Math.max(1, Number.parseInt(ctx.url.searchParams.get("page") ?? "1", 10) || 1);
|
|
6027
|
+
return { body: await listDeliveries(cms, k, state, page) };
|
|
6028
|
+
}
|
|
6029
|
+
if (!isDeliveryKind(kind) || extra !== void 0) throw new HttpError("Not found", 404);
|
|
6030
|
+
if (id2 === "retry" && action === void 0) {
|
|
6031
|
+
if (method !== "POST") throw methodNotAllowed(ctx, "POST");
|
|
6032
|
+
return { body: await retryFailedDeliveries(cms, kind) };
|
|
6033
|
+
}
|
|
6034
|
+
if (id2 === void 0) {
|
|
6035
|
+
if (method !== "DELETE") throw methodNotAllowed(ctx, "DELETE");
|
|
6036
|
+
return { body: await deleteFailedDeliveries(cms, kind) };
|
|
6037
|
+
}
|
|
6038
|
+
const parsed = parseId(id2);
|
|
6039
|
+
if (parsed === void 0) throw new HttpError("Not found", 404);
|
|
6040
|
+
if (action === "retry") {
|
|
6041
|
+
if (method !== "POST") throw methodNotAllowed(ctx, "POST");
|
|
6042
|
+
return { body: await cms.retryDelivery(kind, parsed) };
|
|
6043
|
+
}
|
|
6044
|
+
if (action !== void 0) throw new HttpError("Not found", 404);
|
|
6045
|
+
if (method !== "DELETE") throw methodNotAllowed(ctx, "DELETE");
|
|
6046
|
+
await deleteDelivery(cms, kind, parsed);
|
|
6047
|
+
return { body: { deleted: 1 } };
|
|
6048
|
+
}
|
|
5834
6049
|
if (first === "admin") {
|
|
5835
6050
|
if (method !== "GET") throw methodNotAllowed(ctx, "GET");
|
|
5836
6051
|
if (!ctx.user) throw new UnauthorizedError();
|