@easy-cms/core 0.35.0 → 0.36.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 +528 -357
- package/dist/index.js +912 -318
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -56,328 +56,6 @@ declare const anyone: Access;
|
|
|
56
56
|
declare const isLoggedIn: Access;
|
|
57
57
|
declare const isAdmin: Access;
|
|
58
58
|
|
|
59
|
-
/** What the email functions in `auth.emails` receive. */
|
|
60
|
-
interface PasswordEmailArgs {
|
|
61
|
-
readonly user: AuthUser;
|
|
62
|
-
/** The page that sets the password; absent for `passwordChanged`. */
|
|
63
|
-
readonly url?: string;
|
|
64
|
-
/** The admin language for the text: `en` or `th`. */
|
|
65
|
-
readonly locale: string;
|
|
66
|
-
/** When the link stops working. */
|
|
67
|
-
readonly expiresAt?: Date;
|
|
68
|
-
/**
|
|
69
|
-
* Invitations when passwords are off (`auth.password: false`): the providers to sign in with,
|
|
70
|
-
* e.g. `['Google']`; `url` is then the login page.
|
|
71
|
-
*/
|
|
72
|
-
readonly sso?: readonly string[];
|
|
73
|
-
}
|
|
74
|
-
/** An email's content: a subject, and text and/or HTML. */
|
|
75
|
-
interface PasswordEmail {
|
|
76
|
-
readonly subject: string;
|
|
77
|
-
readonly text: string;
|
|
78
|
-
readonly html?: string;
|
|
79
|
-
}
|
|
80
|
-
type PasswordEmailFn = (args: PasswordEmailArgs) => PasswordEmail | Promise<PasswordEmail>;
|
|
81
|
-
/** The default emails, in English and Thai. */
|
|
82
|
-
declare const DEFAULT_PASSWORD_EMAILS: {
|
|
83
|
-
resetPassword: PasswordEmailFn;
|
|
84
|
-
invite: PasswordEmailFn;
|
|
85
|
-
passwordChanged: PasswordEmailFn;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Signing in to the admin with an outside account (`auth.providers`): Google, Microsoft, GitHub
|
|
90
|
-
* or any OpenID Connect provider. `@easy-cms/auth-oauth` makes these; Easy CMS runs the rest
|
|
91
|
-
* (the redirect, matching users, sessions).
|
|
92
|
-
*/
|
|
93
|
-
/** Values bound to one sign-in, checked when the browser comes back. */
|
|
94
|
-
interface AuthProviderRequest {
|
|
95
|
-
/** Where the provider sends the browser back: `<serverURL><api>/auth/<id>/callback`. */
|
|
96
|
-
readonly redirectUri: string;
|
|
97
|
-
readonly state: string;
|
|
98
|
-
/** For PKCE: the provider gets its S256 challenge. */
|
|
99
|
-
readonly codeVerifier: string;
|
|
100
|
-
/** For OpenID Connect: must come back in the ID token. */
|
|
101
|
-
readonly nonce: string;
|
|
102
|
-
}
|
|
103
|
-
/** Who signed in, as the provider says. */
|
|
104
|
-
interface AuthProviderProfile {
|
|
105
|
-
/** The provider's stable id for the account (`sub`). */
|
|
106
|
-
readonly subject: string;
|
|
107
|
-
readonly email: string | null;
|
|
108
|
-
/** The provider vouches for the email. Only verified emails are matched to users. */
|
|
109
|
-
readonly emailVerified: boolean;
|
|
110
|
-
readonly name?: string | undefined;
|
|
111
|
-
}
|
|
112
|
-
interface AuthProvider {
|
|
113
|
-
/** In URLs and stored identities, e.g. `google`: lowercase letters, digits and `-`. */
|
|
114
|
-
readonly id: string;
|
|
115
|
-
/** On the sign-in button, e.g. `Google`. */
|
|
116
|
-
readonly name: string;
|
|
117
|
-
/** The button's icon in the admin. Default `key`. */
|
|
118
|
-
readonly icon?: 'google' | 'microsoft' | 'github' | 'key';
|
|
119
|
-
/** The provider's page to send the browser to. */
|
|
120
|
-
authorizationURL(request: AuthProviderRequest): Promise<URL>;
|
|
121
|
-
/**
|
|
122
|
-
* After the browser comes back to `redirectUri` (`url`, with its query): checks the response
|
|
123
|
-
* and exchanges the code. Throws when anything does not check out.
|
|
124
|
-
*/
|
|
125
|
-
callback(request: AuthProviderRequest & {
|
|
126
|
-
readonly url: URL;
|
|
127
|
-
}): Promise<AuthProviderProfile>;
|
|
128
|
-
/** For Settings → SSO: what it connects to, never secrets. */
|
|
129
|
-
describe?(): Readonly<Record<string, string>>;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
interface Logger {
|
|
133
|
-
info(message: string): void;
|
|
134
|
-
warn(message: string): void;
|
|
135
|
-
error(message: string): void;
|
|
136
|
-
}
|
|
137
|
-
declare const consoleLogger: Logger;
|
|
138
|
-
declare const silentLogger: Logger;
|
|
139
|
-
|
|
140
|
-
/** A document as stored: relationships and uploads are ids, never populated. */
|
|
141
|
-
type RawDocument = Record<string, unknown> & {
|
|
142
|
-
id: ID;
|
|
143
|
-
};
|
|
144
|
-
interface PaginatedDocs<T> {
|
|
145
|
-
docs: T[];
|
|
146
|
-
totalDocs: number;
|
|
147
|
-
limit: number;
|
|
148
|
-
page: number;
|
|
149
|
-
totalPages: number;
|
|
150
|
-
hasNextPage: boolean;
|
|
151
|
-
hasPrevPage: boolean;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* How the adapter treats the database schema on startup.
|
|
155
|
-
* - `push`: apply schema changes directly (development).
|
|
156
|
-
* - `verify`: fail if migrations are pending or the config changed without a migration (production).
|
|
157
|
-
* - `skip`: do nothing (used by the migration CLI).
|
|
158
|
-
*/
|
|
159
|
-
type SchemaMode = 'push' | 'verify' | 'skip';
|
|
160
|
-
interface DatabaseInitArgs {
|
|
161
|
-
readonly config: ResolvedConfig;
|
|
162
|
-
/** Project root; relative paths (database file, migrations) resolve from here. */
|
|
163
|
-
readonly cwd: string;
|
|
164
|
-
readonly schema: SchemaMode;
|
|
165
|
-
readonly logger: Logger;
|
|
166
|
-
/** Whether the process can prompt the user (the CLI in a terminal). */
|
|
167
|
-
readonly interactive?: boolean;
|
|
168
|
-
}
|
|
169
|
-
interface FindArgs {
|
|
170
|
-
readonly collection: string;
|
|
171
|
-
readonly where?: Where | undefined;
|
|
172
|
-
/** Field paths; prefix with `-` for descending. */
|
|
173
|
-
readonly sort: readonly string[];
|
|
174
|
-
/** `0` returns every matching document. */
|
|
175
|
-
readonly limit: number;
|
|
176
|
-
readonly page: number;
|
|
177
|
-
}
|
|
178
|
-
interface MigrationInfo {
|
|
179
|
-
readonly name: string;
|
|
180
|
-
readonly applied: boolean;
|
|
181
|
-
}
|
|
182
|
-
interface CreateMigrationResult {
|
|
183
|
-
readonly name: string;
|
|
184
|
-
readonly file: string;
|
|
185
|
-
readonly statements: readonly string[];
|
|
186
|
-
}
|
|
187
|
-
/** A connected database bound to one config. Returned by `DatabaseAdapter.init`. */
|
|
188
|
-
interface Database {
|
|
189
|
-
find(args: FindArgs): Promise<PaginatedDocs<RawDocument>>;
|
|
190
|
-
findById(args: {
|
|
191
|
-
collection: string;
|
|
192
|
-
id: ID;
|
|
193
|
-
}): Promise<RawDocument | null>;
|
|
194
|
-
/** Documents of one collection by id, in any order. Used to populate relationships. */
|
|
195
|
-
findByIds(args: {
|
|
196
|
-
collection: string;
|
|
197
|
-
ids: readonly ID[];
|
|
198
|
-
}): Promise<RawDocument[]>;
|
|
199
|
-
count(args: {
|
|
200
|
-
collection: string;
|
|
201
|
-
where?: Where | undefined;
|
|
202
|
-
}): Promise<number>;
|
|
203
|
-
/** `data` holds every field, `createdAt` and `updatedAt`. Runs in one transaction. */
|
|
204
|
-
create(args: {
|
|
205
|
-
collection: string;
|
|
206
|
-
data: Record<string, unknown>;
|
|
207
|
-
}): Promise<RawDocument>;
|
|
208
|
-
/** `data` holds the whole document after merging. Runs in one transaction. */
|
|
209
|
-
update(args: {
|
|
210
|
-
collection: string;
|
|
211
|
-
id: ID;
|
|
212
|
-
data: Record<string, unknown>;
|
|
213
|
-
}): Promise<RawDocument>;
|
|
214
|
-
delete(args: {
|
|
215
|
-
collection: string;
|
|
216
|
-
id: ID;
|
|
217
|
-
}): Promise<void>;
|
|
218
|
-
findGlobal(args: {
|
|
219
|
-
slug: string;
|
|
220
|
-
}): Promise<Record<string, unknown> | null>;
|
|
221
|
-
updateGlobal(args: {
|
|
222
|
-
slug: string;
|
|
223
|
-
data: Record<string, unknown>;
|
|
224
|
-
}): Promise<Record<string, unknown>>;
|
|
225
|
-
/** Writes a migration file for the difference between the config and the latest migration. `null` when nothing changed. */
|
|
226
|
-
createMigration(args: {
|
|
227
|
-
name: string;
|
|
228
|
-
}): Promise<CreateMigrationResult | null>;
|
|
229
|
-
/** Applies pending migrations in order. Returns the names applied. */
|
|
230
|
-
migrate(): Promise<string[]>;
|
|
231
|
-
migrationStatus(): Promise<MigrationInfo[]>;
|
|
232
|
-
/**
|
|
233
|
-
* Copies the database to `file` (an absolute path) while it is in use, when the database can
|
|
234
|
-
* do that itself (SQLite). Others (Postgres) leave it out; use their own tools.
|
|
235
|
-
*/
|
|
236
|
-
readonly backup?: ((file: string) => Promise<void>) | undefined;
|
|
237
|
-
/** Raw table access for copying a whole database to another (`easy-cms copy`). */
|
|
238
|
-
readonly transfer?: DatabaseTransfer | undefined;
|
|
239
|
-
/** The prefix of its tables (`tablePrefix`), so a copy (a backup) can use the same. */
|
|
240
|
-
readonly tablePrefix?: string | undefined;
|
|
241
|
-
destroy(): Promise<void>;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Reads and writes stored rows as they are, ids and hidden fields included, bypassing validation
|
|
245
|
-
* and hooks. Only for moving a database: `copyDatabase()`.
|
|
246
|
-
*/
|
|
247
|
-
interface DatabaseTransfer {
|
|
248
|
-
/**
|
|
249
|
-
* Identifies the tables and columns the config produces, the same for every database kind.
|
|
250
|
-
* Two databases can be copied into each other when their hashes match.
|
|
251
|
-
*/
|
|
252
|
-
readonly schemaHash: string;
|
|
253
|
-
/** Every table, in an order that is safe to write. */
|
|
254
|
-
tables(): string[];
|
|
255
|
-
count(table: string): Promise<number>;
|
|
256
|
-
/** Rows in a stable order (by primary key), `offset` and `limit` for batches. */
|
|
257
|
-
read(table: string, offset: number, limit: number): Promise<Record<string, unknown>[]>;
|
|
258
|
-
write(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
|
|
259
|
-
/** After all writes, e.g. moves id sequences past the copied ids so new documents fit. */
|
|
260
|
-
finish(): Promise<void>;
|
|
261
|
-
}
|
|
262
|
-
/** What `sqlite()` / `postgres()` return and what `config.db` holds. */
|
|
263
|
-
interface DatabaseAdapter {
|
|
264
|
-
readonly name: string;
|
|
265
|
-
init(args: DatabaseInitArgs): Promise<Database>;
|
|
266
|
-
/** Hints for bundlers (Nitro, Next.js output tracing) about files static analysis cannot find. */
|
|
267
|
-
readonly bundle?: BundleHints;
|
|
268
|
-
}
|
|
269
|
-
interface BundleHints {
|
|
270
|
-
/** Absolute paths of files to ship with the server build, e.g. native binaries loaded dynamically. */
|
|
271
|
-
readonly traceInclude: readonly string[];
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/** An email to send. Addresses may be `name <address>`. */
|
|
275
|
-
interface EmailMessage {
|
|
276
|
-
readonly to: string | readonly string[];
|
|
277
|
-
readonly cc?: string | readonly string[];
|
|
278
|
-
readonly bcc?: string | readonly string[];
|
|
279
|
-
/** Default: the adapter's `from`. */
|
|
280
|
-
readonly from?: string;
|
|
281
|
-
readonly replyTo?: string;
|
|
282
|
-
readonly subject: string;
|
|
283
|
-
readonly html?: string;
|
|
284
|
-
readonly text?: string;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Sends email for Easy CMS and its plugins (form notifications, and more later). Set one in
|
|
288
|
-
* the config as `email`, e.g. `smtp()` from `@easy-cms/email-smtp`, or `consoleEmail()` in
|
|
289
|
-
* development. Throw to report a failure: the message is retried later.
|
|
290
|
-
*/
|
|
291
|
-
interface EmailAdapter {
|
|
292
|
-
/** What sends the email, e.g. `smtp`; shown to admins on the dashboard (System). */
|
|
293
|
-
readonly name?: string;
|
|
294
|
-
/** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
|
|
295
|
-
readonly from?: string | undefined;
|
|
296
|
-
send(message: EmailMessage & {
|
|
297
|
-
readonly from: string;
|
|
298
|
-
}): Promise<void>;
|
|
299
|
-
/**
|
|
300
|
-
* The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
|
|
301
|
-
* a password is set, not what it is.
|
|
302
|
-
*/
|
|
303
|
-
describe?(): readonly EmailSetting[];
|
|
304
|
-
/** Checks the connection without sending (Settings → Email); throws with the reason. */
|
|
305
|
-
verify?(): Promise<void>;
|
|
306
|
-
}
|
|
307
|
-
/** One setting an email adapter shows to admins (`describe()`). */
|
|
308
|
-
interface EmailSetting {
|
|
309
|
-
/**
|
|
310
|
-
* `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
|
|
311
|
-
* are translated in the admin; others are shown as they are.
|
|
312
|
-
*/
|
|
313
|
-
readonly key: string;
|
|
314
|
-
/** `null`: not set. */
|
|
315
|
-
readonly value: string | null;
|
|
316
|
-
/** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
|
|
317
|
-
readonly source?: string;
|
|
318
|
-
}
|
|
319
|
-
/** Prints emails to the log instead of sending them: for development and tests. */
|
|
320
|
-
declare function consoleEmail(options?: {
|
|
321
|
-
from?: string;
|
|
322
|
-
log?: (text: string) => void;
|
|
323
|
-
}): EmailAdapter & {
|
|
324
|
-
readonly sent: (EmailMessage & {
|
|
325
|
-
from: string;
|
|
326
|
-
})[];
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
interface ConfigIssue {
|
|
330
|
-
/** Where the problem is, e.g. `collections.posts.fields.slug.from`. */
|
|
331
|
-
readonly path: string;
|
|
332
|
-
readonly message: string;
|
|
333
|
-
/** How to fix it. */
|
|
334
|
-
readonly hint?: string;
|
|
335
|
-
}
|
|
336
|
-
declare class ConfigError extends Error {
|
|
337
|
-
readonly issues: readonly ConfigIssue[];
|
|
338
|
-
constructor(issues: readonly ConfigIssue[]);
|
|
339
|
-
static format(issues: readonly ConfigIssue[]): string;
|
|
340
|
-
}
|
|
341
|
-
/** Base class for errors that map to an HTTP status in the REST API. */
|
|
342
|
-
declare class EasyCMSError extends Error {
|
|
343
|
-
readonly status: number;
|
|
344
|
-
constructor(message: string, status: number);
|
|
345
|
-
}
|
|
346
|
-
interface FieldError {
|
|
347
|
-
/** Dotted path, e.g. `links.0.url`. */
|
|
348
|
-
readonly field: string;
|
|
349
|
-
readonly message: string;
|
|
350
|
-
}
|
|
351
|
-
declare class ValidationError extends EasyCMSError {
|
|
352
|
-
readonly errors: readonly FieldError[];
|
|
353
|
-
constructor(collection: string, errors: readonly FieldError[]);
|
|
354
|
-
}
|
|
355
|
-
declare class NotFoundError extends EasyCMSError {
|
|
356
|
-
constructor(collection: string, id: unknown);
|
|
357
|
-
}
|
|
358
|
-
/** A bad `where`, `sort` or unknown collection/global. */
|
|
359
|
-
declare class QueryError extends EasyCMSError {
|
|
360
|
-
constructor(message: string);
|
|
361
|
-
}
|
|
362
|
-
/** The database schema does not match the config (pending or missing migrations). */
|
|
363
|
-
declare class SchemaError extends EasyCMSError {
|
|
364
|
-
constructor(message: string);
|
|
365
|
-
}
|
|
366
|
-
/** Not logged in, or the session is invalid. */
|
|
367
|
-
declare class UnauthorizedError extends EasyCMSError {
|
|
368
|
-
constructor(message?: string);
|
|
369
|
-
}
|
|
370
|
-
/** Logged in, but not allowed to do this. */
|
|
371
|
-
declare class ForbiddenError extends EasyCMSError {
|
|
372
|
-
constructor(message?: string);
|
|
373
|
-
}
|
|
374
|
-
declare class TooManyRequestsError extends EasyCMSError {
|
|
375
|
-
constructor(message: string);
|
|
376
|
-
}
|
|
377
|
-
declare class PayloadTooLargeError extends EasyCMSError {
|
|
378
|
-
constructor(message: string);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
59
|
/** What a preview token opens: one document of a collection, or one global. */
|
|
382
60
|
type PreviewTarget = {
|
|
383
61
|
readonly collection: string;
|
|
@@ -553,42 +231,239 @@ declare class Auth {
|
|
|
553
231
|
ip?: string | undefined;
|
|
554
232
|
}): Promise<void>;
|
|
555
233
|
/**
|
|
556
|
-
* Emails a user a link to set their password: an invitation if they have none yet, else a
|
|
557
|
-
* reset link. For admins (the REST API checks the role). Returns which one was sent.
|
|
234
|
+
* Emails a user a link to set their password: an invitation if they have none yet, else a
|
|
235
|
+
* reset link. For admins (the REST API checks the role). Returns which one was sent.
|
|
236
|
+
*/
|
|
237
|
+
sendPasswordLink(userId: ID, options?: PasswordLinkOptions): Promise<PasswordPurpose>;
|
|
238
|
+
/** The user and purpose of a valid, unused password link, or `null`. */
|
|
239
|
+
checkPasswordToken(token: string): Promise<{
|
|
240
|
+
user: AuthUser;
|
|
241
|
+
purpose: PasswordPurpose;
|
|
242
|
+
} | null>;
|
|
243
|
+
/**
|
|
244
|
+
* Sets the password from a link, ends every session of the account and starts one here.
|
|
245
|
+
* Throws `ValidationError` when the link expired or was used, or the password is too short.
|
|
246
|
+
*/
|
|
247
|
+
resetPassword(args: {
|
|
248
|
+
token: string;
|
|
249
|
+
password: string;
|
|
250
|
+
ip?: string | undefined;
|
|
251
|
+
locale?: string | undefined;
|
|
252
|
+
}): Promise<Session & {
|
|
253
|
+
purpose: PasswordPurpose;
|
|
254
|
+
}>;
|
|
255
|
+
private passwordTokenUser;
|
|
256
|
+
private mailPasswordLink;
|
|
257
|
+
private localeOf;
|
|
258
|
+
private findByEmail;
|
|
259
|
+
/** The owner of an API key, limited by the key; `null` when unknown, expired or disabled. */
|
|
260
|
+
private verifyApiKey;
|
|
261
|
+
private startSession;
|
|
262
|
+
private findSession;
|
|
263
|
+
private toAuthUser;
|
|
264
|
+
private deleteExpiredSessions;
|
|
265
|
+
private windowStart;
|
|
266
|
+
private checkRateLimit;
|
|
267
|
+
private recordFailure;
|
|
268
|
+
private clearFailures;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface Logger {
|
|
272
|
+
info(message: string): void;
|
|
273
|
+
warn(message: string): void;
|
|
274
|
+
error(message: string): void;
|
|
275
|
+
}
|
|
276
|
+
declare const consoleLogger: Logger;
|
|
277
|
+
declare const silentLogger: Logger;
|
|
278
|
+
|
|
279
|
+
/** A document as stored: relationships and uploads are ids, never populated. */
|
|
280
|
+
type RawDocument = Record<string, unknown> & {
|
|
281
|
+
id: ID;
|
|
282
|
+
};
|
|
283
|
+
interface PaginatedDocs<T> {
|
|
284
|
+
docs: T[];
|
|
285
|
+
totalDocs: number;
|
|
286
|
+
limit: number;
|
|
287
|
+
page: number;
|
|
288
|
+
totalPages: number;
|
|
289
|
+
hasNextPage: boolean;
|
|
290
|
+
hasPrevPage: boolean;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* How the adapter treats the database schema on startup.
|
|
294
|
+
* - `push`: apply schema changes directly (development).
|
|
295
|
+
* - `verify`: fail if migrations are pending or the config changed without a migration (production).
|
|
296
|
+
* - `skip`: do nothing (used by the migration CLI).
|
|
297
|
+
*/
|
|
298
|
+
type SchemaMode = 'push' | 'verify' | 'skip';
|
|
299
|
+
interface DatabaseInitArgs {
|
|
300
|
+
readonly config: ResolvedConfig;
|
|
301
|
+
/** Project root; relative paths (database file, migrations) resolve from here. */
|
|
302
|
+
readonly cwd: string;
|
|
303
|
+
readonly schema: SchemaMode;
|
|
304
|
+
readonly logger: Logger;
|
|
305
|
+
/** Whether the process can prompt the user (the CLI in a terminal). */
|
|
306
|
+
readonly interactive?: boolean;
|
|
307
|
+
}
|
|
308
|
+
interface FindArgs {
|
|
309
|
+
readonly collection: string;
|
|
310
|
+
readonly where?: Where | undefined;
|
|
311
|
+
/** Field paths; prefix with `-` for descending. */
|
|
312
|
+
readonly sort: readonly string[];
|
|
313
|
+
/** `0` returns every matching document. */
|
|
314
|
+
readonly limit: number;
|
|
315
|
+
readonly page: number;
|
|
316
|
+
}
|
|
317
|
+
interface MigrationInfo {
|
|
318
|
+
readonly name: string;
|
|
319
|
+
readonly applied: boolean;
|
|
320
|
+
}
|
|
321
|
+
interface CreateMigrationResult {
|
|
322
|
+
readonly name: string;
|
|
323
|
+
readonly file: string;
|
|
324
|
+
readonly statements: readonly string[];
|
|
325
|
+
}
|
|
326
|
+
/** A connected database bound to one config. Returned by `DatabaseAdapter.init`. */
|
|
327
|
+
interface Database {
|
|
328
|
+
find(args: FindArgs): Promise<PaginatedDocs<RawDocument>>;
|
|
329
|
+
findById(args: {
|
|
330
|
+
collection: string;
|
|
331
|
+
id: ID;
|
|
332
|
+
}): Promise<RawDocument | null>;
|
|
333
|
+
/** Documents of one collection by id, in any order. Used to populate relationships. */
|
|
334
|
+
findByIds(args: {
|
|
335
|
+
collection: string;
|
|
336
|
+
ids: readonly ID[];
|
|
337
|
+
}): Promise<RawDocument[]>;
|
|
338
|
+
count(args: {
|
|
339
|
+
collection: string;
|
|
340
|
+
where?: Where | undefined;
|
|
341
|
+
}): Promise<number>;
|
|
342
|
+
/** `data` holds every field, `createdAt` and `updatedAt`. Runs in one transaction. */
|
|
343
|
+
create(args: {
|
|
344
|
+
collection: string;
|
|
345
|
+
data: Record<string, unknown>;
|
|
346
|
+
}): Promise<RawDocument>;
|
|
347
|
+
/** `data` holds the whole document after merging. Runs in one transaction. */
|
|
348
|
+
update(args: {
|
|
349
|
+
collection: string;
|
|
350
|
+
id: ID;
|
|
351
|
+
data: Record<string, unknown>;
|
|
352
|
+
}): Promise<RawDocument>;
|
|
353
|
+
delete(args: {
|
|
354
|
+
collection: string;
|
|
355
|
+
id: ID;
|
|
356
|
+
}): Promise<void>;
|
|
357
|
+
findGlobal(args: {
|
|
358
|
+
slug: string;
|
|
359
|
+
}): Promise<Record<string, unknown> | null>;
|
|
360
|
+
updateGlobal(args: {
|
|
361
|
+
slug: string;
|
|
362
|
+
data: Record<string, unknown>;
|
|
363
|
+
}): Promise<Record<string, unknown>>;
|
|
364
|
+
/** Writes a migration file for the difference between the config and the latest migration. `null` when nothing changed. */
|
|
365
|
+
createMigration(args: {
|
|
366
|
+
name: string;
|
|
367
|
+
}): Promise<CreateMigrationResult | null>;
|
|
368
|
+
/** Applies pending migrations in order. Returns the names applied. */
|
|
369
|
+
migrate(): Promise<string[]>;
|
|
370
|
+
migrationStatus(): Promise<MigrationInfo[]>;
|
|
371
|
+
/**
|
|
372
|
+
* Copies the database to `file` (an absolute path) while it is in use, when the database can
|
|
373
|
+
* do that itself (SQLite). Others (Postgres) leave it out; use their own tools.
|
|
374
|
+
*/
|
|
375
|
+
readonly backup?: ((file: string) => Promise<void>) | undefined;
|
|
376
|
+
/** Raw table access for copying a whole database to another (`easy-cms copy`). */
|
|
377
|
+
readonly transfer?: DatabaseTransfer | undefined;
|
|
378
|
+
/** The prefix of its tables (`tablePrefix`), so a copy (a backup) can use the same. */
|
|
379
|
+
readonly tablePrefix?: string | undefined;
|
|
380
|
+
destroy(): Promise<void>;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Reads and writes stored rows as they are, ids and hidden fields included, bypassing validation
|
|
384
|
+
* and hooks. Only for moving a database: `copyDatabase()`.
|
|
385
|
+
*/
|
|
386
|
+
interface DatabaseTransfer {
|
|
387
|
+
/**
|
|
388
|
+
* Identifies the tables and columns the config produces, the same for every database kind.
|
|
389
|
+
* Two databases can be copied into each other when their hashes match.
|
|
390
|
+
*/
|
|
391
|
+
readonly schemaHash: string;
|
|
392
|
+
/** Every table, in an order that is safe to write. */
|
|
393
|
+
tables(): string[];
|
|
394
|
+
count(table: string): Promise<number>;
|
|
395
|
+
/** Rows in a stable order (by primary key), `offset` and `limit` for batches. */
|
|
396
|
+
read(table: string, offset: number, limit: number): Promise<Record<string, unknown>[]>;
|
|
397
|
+
write(table: string, rows: readonly Record<string, unknown>[]): Promise<void>;
|
|
398
|
+
/** After all writes, e.g. moves id sequences past the copied ids so new documents fit. */
|
|
399
|
+
finish(): Promise<void>;
|
|
400
|
+
}
|
|
401
|
+
/** What `sqlite()` / `postgres()` return and what `config.db` holds. */
|
|
402
|
+
interface DatabaseAdapter {
|
|
403
|
+
readonly name: string;
|
|
404
|
+
init(args: DatabaseInitArgs): Promise<Database>;
|
|
405
|
+
/** Hints for bundlers (Nitro, Next.js output tracing) about files static analysis cannot find. */
|
|
406
|
+
readonly bundle?: BundleHints;
|
|
407
|
+
}
|
|
408
|
+
interface BundleHints {
|
|
409
|
+
/** Absolute paths of files to ship with the server build, e.g. native binaries loaded dynamically. */
|
|
410
|
+
readonly traceInclude: readonly string[];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** An email to send. Addresses may be `name <address>`. */
|
|
414
|
+
interface EmailMessage {
|
|
415
|
+
readonly to: string | readonly string[];
|
|
416
|
+
readonly cc?: string | readonly string[];
|
|
417
|
+
readonly bcc?: string | readonly string[];
|
|
418
|
+
/** Default: the adapter's `from`. */
|
|
419
|
+
readonly from?: string;
|
|
420
|
+
readonly replyTo?: string;
|
|
421
|
+
readonly subject: string;
|
|
422
|
+
readonly html?: string;
|
|
423
|
+
readonly text?: string;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Sends email for Easy CMS and its plugins (form notifications, and more later). Set one in
|
|
427
|
+
* the config as `email`, e.g. `smtp()` from `@easy-cms/email-smtp`, or `consoleEmail()` in
|
|
428
|
+
* development. Throw to report a failure: the message is retried later.
|
|
429
|
+
*/
|
|
430
|
+
interface EmailAdapter {
|
|
431
|
+
/** What sends the email, e.g. `smtp`; shown to admins on the dashboard (System). */
|
|
432
|
+
readonly name?: string;
|
|
433
|
+
/** The sender when a message has none, e.g. `Easy CMS <no-reply@example.com>`. */
|
|
434
|
+
readonly from?: string | undefined;
|
|
435
|
+
send(message: EmailMessage & {
|
|
436
|
+
readonly from: string;
|
|
437
|
+
}): Promise<void>;
|
|
438
|
+
/**
|
|
439
|
+
* The settings in use, shown to admins on Settings → Email. Never return secrets: say whether
|
|
440
|
+
* a password is set, not what it is.
|
|
558
441
|
*/
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
442
|
+
describe?(): readonly EmailSetting[];
|
|
443
|
+
/** Checks the connection without sending (Settings → Email); throws with the reason. */
|
|
444
|
+
verify?(): Promise<void>;
|
|
445
|
+
}
|
|
446
|
+
/** One setting an email adapter shows to admins (`describe()`). */
|
|
447
|
+
interface EmailSetting {
|
|
565
448
|
/**
|
|
566
|
-
*
|
|
567
|
-
*
|
|
449
|
+
* `host`, `port`, `secure`, `user`, `password`, `from` and `delivery` (`log`: printed, not sent)
|
|
450
|
+
* are translated in the admin; others are shown as they are.
|
|
568
451
|
*/
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}): Promise<Session & {
|
|
575
|
-
purpose: PasswordPurpose;
|
|
576
|
-
}>;
|
|
577
|
-
private passwordTokenUser;
|
|
578
|
-
private mailPasswordLink;
|
|
579
|
-
private localeOf;
|
|
580
|
-
private findByEmail;
|
|
581
|
-
/** The owner of an API key, limited by the key; `null` when unknown, expired or disabled. */
|
|
582
|
-
private verifyApiKey;
|
|
583
|
-
private startSession;
|
|
584
|
-
private findSession;
|
|
585
|
-
private toAuthUser;
|
|
586
|
-
private deleteExpiredSessions;
|
|
587
|
-
private windowStart;
|
|
588
|
-
private checkRateLimit;
|
|
589
|
-
private recordFailure;
|
|
590
|
-
private clearFailures;
|
|
452
|
+
readonly key: string;
|
|
453
|
+
/** `null`: not set. */
|
|
454
|
+
readonly value: string | null;
|
|
455
|
+
/** Where it comes from, e.g. `SMTP_HOST` or `smtp({ host })`. */
|
|
456
|
+
readonly source?: string;
|
|
591
457
|
}
|
|
458
|
+
/** Prints emails to the log instead of sending them: for development and tests. */
|
|
459
|
+
declare function consoleEmail(options?: {
|
|
460
|
+
from?: string;
|
|
461
|
+
log?: (text: string) => void;
|
|
462
|
+
}): EmailAdapter & {
|
|
463
|
+
readonly sent: (EmailMessage & {
|
|
464
|
+
from: string;
|
|
465
|
+
})[];
|
|
466
|
+
};
|
|
592
467
|
|
|
593
468
|
type Simplify<T> = {
|
|
594
469
|
[K in keyof T]: T[K];
|
|
@@ -979,11 +854,25 @@ declare class Roles {
|
|
|
979
854
|
private readonly config;
|
|
980
855
|
private readonly db;
|
|
981
856
|
private readonly log?;
|
|
857
|
+
/** Writes to the audit log (`audit`). */
|
|
858
|
+
private readonly audit?;
|
|
982
859
|
readonly enabled: boolean;
|
|
983
860
|
private readonly versions;
|
|
984
861
|
private cache;
|
|
985
862
|
private synced;
|
|
986
|
-
constructor(config: ResolvedConfig, db: Database, log?: ((message: string) => void) | undefined
|
|
863
|
+
constructor(config: ResolvedConfig, db: Database, log?: ((message: string) => void) | undefined,
|
|
864
|
+
/** Writes to the audit log (`audit`). */
|
|
865
|
+
audit?: ((entry: {
|
|
866
|
+
action: string;
|
|
867
|
+
target: string;
|
|
868
|
+
doc: ID;
|
|
869
|
+
title: string;
|
|
870
|
+
changes?: {
|
|
871
|
+
field: string;
|
|
872
|
+
before?: unknown;
|
|
873
|
+
after?: unknown;
|
|
874
|
+
}[];
|
|
875
|
+
}) => Promise<void>) | undefined);
|
|
987
876
|
/**
|
|
988
877
|
* Whether the user's role allows an operation: `true`, `false`, or for their own account
|
|
989
878
|
* (users without read or update on `users`) a constraint to it.
|
|
@@ -1220,6 +1109,8 @@ declare class EasyCMS<C extends Config = Config> {
|
|
|
1220
1109
|
readonly cwd: string;
|
|
1221
1110
|
/** Roles and their permissions (`auth.rbac`, Settings → Roles). */
|
|
1222
1111
|
readonly roles: Roles;
|
|
1112
|
+
/** Who did what (`audit`, Settings → Audit log). */
|
|
1113
|
+
readonly audit: AuditLog;
|
|
1223
1114
|
private readonly versions;
|
|
1224
1115
|
private readonly webhooks;
|
|
1225
1116
|
private readonly mailer;
|
|
@@ -1480,6 +1371,11 @@ declare class EasyCMS<C extends Config = Config> {
|
|
|
1480
1371
|
private withDrafts;
|
|
1481
1372
|
/** Sends webhook events for a change; `before` is the stored status before it, if any. */
|
|
1482
1373
|
private emit;
|
|
1374
|
+
/**
|
|
1375
|
+
* Writes the audit log entry for a change (`audit`): publishing, unpublishing and restoring
|
|
1376
|
+
* are told apart from other saves.
|
|
1377
|
+
*/
|
|
1378
|
+
private audited;
|
|
1483
1379
|
/** Records a version after a save, when the collection or global keeps versions. */
|
|
1484
1380
|
private saveVersion;
|
|
1485
1381
|
private hookArgs;
|
|
@@ -1703,6 +1599,257 @@ type Field = CustomField | TextField | TextareaField | NumberField | BooleanFiel
|
|
|
1703
1599
|
type FieldType = Field['type'];
|
|
1704
1600
|
declare const FIELD_TYPES: readonly ["text", "textarea", "number", "boolean", "date", "email", "json", "select", "slug", "richText", "upload", "relationship", "array", "group", "blocks"];
|
|
1705
1601
|
|
|
1602
|
+
/** Audit log settings (`audit`). */
|
|
1603
|
+
interface AuditConfig {
|
|
1604
|
+
/** Days to keep entries; older ones are deleted. `0`: keep them all. Default 365. */
|
|
1605
|
+
readonly keep?: number;
|
|
1606
|
+
/** Keep the values before and after a change, not only which fields changed. Default `true`. */
|
|
1607
|
+
readonly values?: boolean;
|
|
1608
|
+
/** Failed logins within an hour that the dashboard warns about. Default 20. */
|
|
1609
|
+
readonly failedLogins?: number;
|
|
1610
|
+
}
|
|
1611
|
+
/** A field that changed: values before and after, unless they are not kept. */
|
|
1612
|
+
interface AuditChange {
|
|
1613
|
+
field: string;
|
|
1614
|
+
before?: unknown;
|
|
1615
|
+
after?: unknown;
|
|
1616
|
+
}
|
|
1617
|
+
/** One entry (`GET <api>/admin/audit`). */
|
|
1618
|
+
interface AuditEntry {
|
|
1619
|
+
id: ID;
|
|
1620
|
+
/** When, as an ISO date. */
|
|
1621
|
+
at: string;
|
|
1622
|
+
/** E.g. `create`, `update`, `publish`, `delete`, `login`, `login.failed`, `role.update`. */
|
|
1623
|
+
action: string;
|
|
1624
|
+
/** A collection slug, `global:<slug>`, or an area such as `auth`, `roles`, `backups`. */
|
|
1625
|
+
target: string | null;
|
|
1626
|
+
/** The document's id. */
|
|
1627
|
+
doc: string | null;
|
|
1628
|
+
/** The document's title then (kept after it is deleted). */
|
|
1629
|
+
title: string | null;
|
|
1630
|
+
actor: {
|
|
1631
|
+
id: string | null;
|
|
1632
|
+
email: string | null;
|
|
1633
|
+
/** `user` (admin or REST), `api-key`, `system` (code), `scheduler`. */
|
|
1634
|
+
via: string;
|
|
1635
|
+
ip: string | null;
|
|
1636
|
+
userAgent: string | null;
|
|
1637
|
+
};
|
|
1638
|
+
changes: AuditChange[] | null;
|
|
1639
|
+
detail: Record<string, unknown> | null;
|
|
1640
|
+
}
|
|
1641
|
+
/** `GET <api>/admin/audit`: a page of entries, newest first. */
|
|
1642
|
+
interface AuditPage {
|
|
1643
|
+
docs: AuditEntry[];
|
|
1644
|
+
page: number;
|
|
1645
|
+
totalPages: number;
|
|
1646
|
+
totalDocs: number;
|
|
1647
|
+
}
|
|
1648
|
+
interface AuditFilter {
|
|
1649
|
+
action?: string | null;
|
|
1650
|
+
target?: string | null;
|
|
1651
|
+
doc?: string | null;
|
|
1652
|
+
/** An email, or part of one. */
|
|
1653
|
+
actor?: string | null;
|
|
1654
|
+
/** ISO dates. */
|
|
1655
|
+
from?: string | null;
|
|
1656
|
+
to?: string | null;
|
|
1657
|
+
}
|
|
1658
|
+
/** `POST <api>/admin/audit/verify`. */
|
|
1659
|
+
interface AuditVerification {
|
|
1660
|
+
checked: number;
|
|
1661
|
+
/** Entries whose signature does not match: changed after they were written. */
|
|
1662
|
+
invalid: ID[];
|
|
1663
|
+
/** Missing ids between entries: deleted entries, or (rarely) inserts that failed. */
|
|
1664
|
+
gaps: number;
|
|
1665
|
+
at: string;
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* The audit log (`audit` in the config): who did what, when, from where. Entries can't be
|
|
1669
|
+
* changed through Easy CMS; each is signed with the secret, so changes in the database show.
|
|
1670
|
+
* Available as `cms.audit`.
|
|
1671
|
+
*/
|
|
1672
|
+
declare class AuditLog {
|
|
1673
|
+
private readonly cms;
|
|
1674
|
+
/** Entries that could not be written since the server started. */
|
|
1675
|
+
failures: number;
|
|
1676
|
+
constructor(cms: EasyCMS);
|
|
1677
|
+
get enabled(): boolean;
|
|
1678
|
+
private get settings();
|
|
1679
|
+
/**
|
|
1680
|
+
* Writes an entry. The user defaults to the request's. Never throws: a failure is logged and
|
|
1681
|
+
* counted, and the dashboard says so.
|
|
1682
|
+
*/
|
|
1683
|
+
record(entry: {
|
|
1684
|
+
action: string;
|
|
1685
|
+
target?: string | null;
|
|
1686
|
+
doc?: ID | null;
|
|
1687
|
+
title?: string | null;
|
|
1688
|
+
user?: AuthUser | null;
|
|
1689
|
+
/** Who it was when there is no user, e.g. the email of a failed login. */
|
|
1690
|
+
email?: string | null;
|
|
1691
|
+
changes?: AuditChange[] | null;
|
|
1692
|
+
detail?: Record<string, unknown> | null;
|
|
1693
|
+
}): Promise<void>;
|
|
1694
|
+
/**
|
|
1695
|
+
* A change to a document or global: what changed between `before` and `after`. Internal
|
|
1696
|
+
* collections (sessions, versions…) are not logged.
|
|
1697
|
+
*/
|
|
1698
|
+
content(config: CollectionConfig | GlobalConfig, global: boolean, action: string, after: Record<string, unknown> | undefined, before: Record<string, unknown> | undefined, user: AuthUser | null): Promise<void>;
|
|
1699
|
+
/** Fields whose stored value changed. Hidden ones never (the password only as "changed"). */
|
|
1700
|
+
diff(fields: readonly Field[], before: Record<string, unknown>, after: Record<string, unknown>): AuditChange[];
|
|
1701
|
+
list(filter: AuditFilter, page?: number): Promise<AuditPage>;
|
|
1702
|
+
/** The filtered entries as CSV (the newest 10,000). */
|
|
1703
|
+
csv(filter: AuditFilter): Promise<string>;
|
|
1704
|
+
/** Checks every entry's signature, and that no ids are missing. */
|
|
1705
|
+
verify(): Promise<AuditVerification>;
|
|
1706
|
+
/**
|
|
1707
|
+
* Daily upkeep from `runJobs`: deletes entries older than `keep` days, then verifies the log.
|
|
1708
|
+
* Runs when the last check is more than a day old.
|
|
1709
|
+
*/
|
|
1710
|
+
upkeep(now?: Date): Promise<void>;
|
|
1711
|
+
/** What the dashboard warns about: many failed logins, entries not written, a failed check. */
|
|
1712
|
+
attention(now?: Date): Promise<({
|
|
1713
|
+
id: 'failed-logins';
|
|
1714
|
+
count: number;
|
|
1715
|
+
} | {
|
|
1716
|
+
id: 'audit-failures';
|
|
1717
|
+
count: number;
|
|
1718
|
+
} | {
|
|
1719
|
+
id: 'audit-tampered';
|
|
1720
|
+
invalid: number;
|
|
1721
|
+
at: string;
|
|
1722
|
+
})[]>;
|
|
1723
|
+
private sign;
|
|
1724
|
+
private lastVerification;
|
|
1725
|
+
private where;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
/** What the email functions in `auth.emails` receive. */
|
|
1729
|
+
interface PasswordEmailArgs {
|
|
1730
|
+
readonly user: AuthUser;
|
|
1731
|
+
/** The page that sets the password; absent for `passwordChanged`. */
|
|
1732
|
+
readonly url?: string;
|
|
1733
|
+
/** The admin language for the text: `en` or `th`. */
|
|
1734
|
+
readonly locale: string;
|
|
1735
|
+
/** When the link stops working. */
|
|
1736
|
+
readonly expiresAt?: Date;
|
|
1737
|
+
/**
|
|
1738
|
+
* Invitations when passwords are off (`auth.password: false`): the providers to sign in with,
|
|
1739
|
+
* e.g. `['Google']`; `url` is then the login page.
|
|
1740
|
+
*/
|
|
1741
|
+
readonly sso?: readonly string[];
|
|
1742
|
+
}
|
|
1743
|
+
/** An email's content: a subject, and text and/or HTML. */
|
|
1744
|
+
interface PasswordEmail {
|
|
1745
|
+
readonly subject: string;
|
|
1746
|
+
readonly text: string;
|
|
1747
|
+
readonly html?: string;
|
|
1748
|
+
}
|
|
1749
|
+
type PasswordEmailFn = (args: PasswordEmailArgs) => PasswordEmail | Promise<PasswordEmail>;
|
|
1750
|
+
/** The default emails, in English and Thai. */
|
|
1751
|
+
declare const DEFAULT_PASSWORD_EMAILS: {
|
|
1752
|
+
resetPassword: PasswordEmailFn;
|
|
1753
|
+
invite: PasswordEmailFn;
|
|
1754
|
+
passwordChanged: PasswordEmailFn;
|
|
1755
|
+
};
|
|
1756
|
+
|
|
1757
|
+
/**
|
|
1758
|
+
* Signing in to the admin with an outside account (`auth.providers`): Google, Microsoft, GitHub
|
|
1759
|
+
* or any OpenID Connect provider. `@easy-cms/auth-oauth` makes these; Easy CMS runs the rest
|
|
1760
|
+
* (the redirect, matching users, sessions).
|
|
1761
|
+
*/
|
|
1762
|
+
/** Values bound to one sign-in, checked when the browser comes back. */
|
|
1763
|
+
interface AuthProviderRequest {
|
|
1764
|
+
/** Where the provider sends the browser back: `<serverURL><api>/auth/<id>/callback`. */
|
|
1765
|
+
readonly redirectUri: string;
|
|
1766
|
+
readonly state: string;
|
|
1767
|
+
/** For PKCE: the provider gets its S256 challenge. */
|
|
1768
|
+
readonly codeVerifier: string;
|
|
1769
|
+
/** For OpenID Connect: must come back in the ID token. */
|
|
1770
|
+
readonly nonce: string;
|
|
1771
|
+
}
|
|
1772
|
+
/** Who signed in, as the provider says. */
|
|
1773
|
+
interface AuthProviderProfile {
|
|
1774
|
+
/** The provider's stable id for the account (`sub`). */
|
|
1775
|
+
readonly subject: string;
|
|
1776
|
+
readonly email: string | null;
|
|
1777
|
+
/** The provider vouches for the email. Only verified emails are matched to users. */
|
|
1778
|
+
readonly emailVerified: boolean;
|
|
1779
|
+
readonly name?: string | undefined;
|
|
1780
|
+
}
|
|
1781
|
+
interface AuthProvider {
|
|
1782
|
+
/** In URLs and stored identities, e.g. `google`: lowercase letters, digits and `-`. */
|
|
1783
|
+
readonly id: string;
|
|
1784
|
+
/** On the sign-in button, e.g. `Google`. */
|
|
1785
|
+
readonly name: string;
|
|
1786
|
+
/** The button's icon in the admin. Default `key`. */
|
|
1787
|
+
readonly icon?: 'google' | 'microsoft' | 'github' | 'key';
|
|
1788
|
+
/** The provider's page to send the browser to. */
|
|
1789
|
+
authorizationURL(request: AuthProviderRequest): Promise<URL>;
|
|
1790
|
+
/**
|
|
1791
|
+
* After the browser comes back to `redirectUri` (`url`, with its query): checks the response
|
|
1792
|
+
* and exchanges the code. Throws when anything does not check out.
|
|
1793
|
+
*/
|
|
1794
|
+
callback(request: AuthProviderRequest & {
|
|
1795
|
+
readonly url: URL;
|
|
1796
|
+
}): Promise<AuthProviderProfile>;
|
|
1797
|
+
/** For Settings → SSO: what it connects to, never secrets. */
|
|
1798
|
+
describe?(): Readonly<Record<string, string>>;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
interface ConfigIssue {
|
|
1802
|
+
/** Where the problem is, e.g. `collections.posts.fields.slug.from`. */
|
|
1803
|
+
readonly path: string;
|
|
1804
|
+
readonly message: string;
|
|
1805
|
+
/** How to fix it. */
|
|
1806
|
+
readonly hint?: string;
|
|
1807
|
+
}
|
|
1808
|
+
declare class ConfigError extends Error {
|
|
1809
|
+
readonly issues: readonly ConfigIssue[];
|
|
1810
|
+
constructor(issues: readonly ConfigIssue[]);
|
|
1811
|
+
static format(issues: readonly ConfigIssue[]): string;
|
|
1812
|
+
}
|
|
1813
|
+
/** Base class for errors that map to an HTTP status in the REST API. */
|
|
1814
|
+
declare class EasyCMSError extends Error {
|
|
1815
|
+
readonly status: number;
|
|
1816
|
+
constructor(message: string, status: number);
|
|
1817
|
+
}
|
|
1818
|
+
interface FieldError {
|
|
1819
|
+
/** Dotted path, e.g. `links.0.url`. */
|
|
1820
|
+
readonly field: string;
|
|
1821
|
+
readonly message: string;
|
|
1822
|
+
}
|
|
1823
|
+
declare class ValidationError extends EasyCMSError {
|
|
1824
|
+
readonly errors: readonly FieldError[];
|
|
1825
|
+
constructor(collection: string, errors: readonly FieldError[]);
|
|
1826
|
+
}
|
|
1827
|
+
declare class NotFoundError extends EasyCMSError {
|
|
1828
|
+
constructor(collection: string, id: unknown);
|
|
1829
|
+
}
|
|
1830
|
+
/** A bad `where`, `sort` or unknown collection/global. */
|
|
1831
|
+
declare class QueryError extends EasyCMSError {
|
|
1832
|
+
constructor(message: string);
|
|
1833
|
+
}
|
|
1834
|
+
/** The database schema does not match the config (pending or missing migrations). */
|
|
1835
|
+
declare class SchemaError extends EasyCMSError {
|
|
1836
|
+
constructor(message: string);
|
|
1837
|
+
}
|
|
1838
|
+
/** Not logged in, or the session is invalid. */
|
|
1839
|
+
declare class UnauthorizedError extends EasyCMSError {
|
|
1840
|
+
constructor(message?: string);
|
|
1841
|
+
}
|
|
1842
|
+
/** Logged in, but not allowed to do this. */
|
|
1843
|
+
declare class ForbiddenError extends EasyCMSError {
|
|
1844
|
+
constructor(message?: string);
|
|
1845
|
+
}
|
|
1846
|
+
declare class TooManyRequestsError extends EasyCMSError {
|
|
1847
|
+
constructor(message: string);
|
|
1848
|
+
}
|
|
1849
|
+
declare class PayloadTooLargeError extends EasyCMSError {
|
|
1850
|
+
constructor(message: string);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1706
1853
|
/** Built-in types a field type can be stored as. */
|
|
1707
1854
|
declare const FIELD_TYPE_BASES: readonly ["text", "textarea", "email", "number", "boolean", "date", "json"];
|
|
1708
1855
|
type FieldTypeBase = (typeof FIELD_TYPE_BASES)[number];
|
|
@@ -2273,10 +2420,15 @@ interface Config {
|
|
|
2273
2420
|
* `every`, admins can still back up by hand.
|
|
2274
2421
|
*/
|
|
2275
2422
|
readonly backups?: BackupsConfig;
|
|
2423
|
+
/**
|
|
2424
|
+
* The audit log (Settings → Audit log): who changed what and when, sign-ins, and admin
|
|
2425
|
+
* actions, kept for `keep` days. `true` for the defaults.
|
|
2426
|
+
*/
|
|
2427
|
+
readonly audit?: boolean | AuditConfig;
|
|
2276
2428
|
readonly plugins?: readonly Plugin[];
|
|
2277
2429
|
}
|
|
2278
2430
|
/** The config after plugins ran, validation passed and defaults were applied. */
|
|
2279
|
-
interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins'> {
|
|
2431
|
+
interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes' | 'admin' | 'upload' | 'auth' | 'collections' | 'globals' | 'endpoints' | 'plugins' | 'audit'> {
|
|
2280
2432
|
readonly cors: readonly string[] | '*';
|
|
2281
2433
|
/** `null` without localization. */
|
|
2282
2434
|
readonly localization: Required<LocalizationConfig> | null;
|
|
@@ -2284,6 +2436,8 @@ interface ResolvedConfig extends Omit<Config, 'cors' | 'localization' | 'routes'
|
|
|
2284
2436
|
readonly admin: Required<AdminConfig>;
|
|
2285
2437
|
readonly upload: Required<Omit<UploadConfig, 'storage' | 'fromURL'>> & Pick<UploadConfig, 'storage' | 'fromURL'>;
|
|
2286
2438
|
readonly auth: Required<AuthConfig>;
|
|
2439
|
+
/** `false` without an audit log. */
|
|
2440
|
+
readonly audit: Required<AuditConfig> | false;
|
|
2287
2441
|
readonly collections: readonly CollectionConfig[];
|
|
2288
2442
|
readonly globals: readonly GlobalConfig[];
|
|
2289
2443
|
readonly endpoints: readonly Endpoint[];
|
|
@@ -2734,6 +2888,7 @@ interface AdminSchema {
|
|
|
2734
2888
|
email: boolean;
|
|
2735
2889
|
roles: boolean;
|
|
2736
2890
|
sso: boolean;
|
|
2891
|
+
audit: boolean;
|
|
2737
2892
|
};
|
|
2738
2893
|
/** Roles from the admin are on (`auth.rbac`): documents have owners. */
|
|
2739
2894
|
rbac: boolean;
|
|
@@ -2776,6 +2931,22 @@ type AdminAttention =
|
|
|
2776
2931
|
id: 'backups';
|
|
2777
2932
|
failed: boolean;
|
|
2778
2933
|
lastDone: string | null;
|
|
2934
|
+
}
|
|
2935
|
+
/** Many failed logins in the last hour (`audit.failedLogins`): someone may be guessing. */
|
|
2936
|
+
| {
|
|
2937
|
+
id: 'failed-logins';
|
|
2938
|
+
count: number;
|
|
2939
|
+
}
|
|
2940
|
+
/** Audit log entries that could not be written since the server started. */
|
|
2941
|
+
| {
|
|
2942
|
+
id: 'audit-failures';
|
|
2943
|
+
count: number;
|
|
2944
|
+
}
|
|
2945
|
+
/** The last check of the audit log found entries changed in the database. */
|
|
2946
|
+
| {
|
|
2947
|
+
id: 'audit-tampered';
|
|
2948
|
+
invalid: number;
|
|
2949
|
+
at: string;
|
|
2779
2950
|
};
|
|
2780
2951
|
/** What the dashboard shows admins (`GET <api>/admin/status`). */
|
|
2781
2952
|
interface AdminStatus {
|
|
@@ -2837,4 +3008,4 @@ declare function validateConfig(config: Config): ConfigIssue[];
|
|
|
2837
3008
|
/** The version of `@easy-cms/core`, set when the package is built. */
|
|
2838
3009
|
declare const VERSION: string;
|
|
2839
3010
|
|
|
2840
|
-
export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBackup, type AdminBackups, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminDeliveries, type AdminDelivery, type AdminEmail, type AdminEmailDelivery, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminRole, type AdminRoleChange, type AdminRoleField, type AdminRoleTarget, type AdminRoleView, type AdminRoles, type AdminSchema, type AdminSso, 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 AuthProvider, type AuthProviderProfile, type AuthProviderRequest, type AuthUser, BUILTIN_COLLECTIONS, type BackupState, type BackupsConfig, 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 EmailCheck, type EmailField, type EmailMessage, type EmailSetting, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldRule, 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, ROLE_OPERATIONS, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoleOperation, type RolePermissions, Roles, type RoutesConfig, SESSION_COOKIE, SSO_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type SsoOutcome, type SsoProviderRef, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, type UserIdentity, 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, writeBackupFile };
|
|
3011
|
+
export { ADMIN_ICONS, API_KEYS, API_KEY_GLOBAL_OPERATIONS, API_KEY_OPERATIONS, type Access, type AccessArgs, type AccessOptions, type AdminAttention, type AdminBackup, type AdminBackups, type AdminBrand, type AdminCollection, type AdminComponent, type AdminComponentRef, type AdminConfig, type AdminDeliveries, type AdminDelivery, type AdminEmail, type AdminEmailDelivery, type AdminField, type AdminGlobal, type AdminIcon, type AdminLocale, type AdminPage, type AdminPageRef, type AdminRole, type AdminRoleChange, type AdminRoleField, type AdminRoleTarget, type AdminRoleView, type AdminRoles, type AdminSchema, type AdminSso, type AdminStatus, type AdminViewAccess, type AdminWebhookDelivery, type AdminWidgetRef, type AfterChangeHook, type AfterDeleteHook, type AfterReadHook, type ApiKeyContext, type ApiKeyGlobalOperation, type ApiKeyOperation, type ApiKeyPermissions, type ArrayField, type AuditChange, type AuditConfig, type AuditEntry, AuditLog, type AuditPage, type AuditVerification, Auth, type AuthConfig, type AuthProvider, type AuthProviderProfile, type AuthProviderRequest, type AuthUser, BUILTIN_COLLECTIONS, type BackupState, type BackupsConfig, 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 EmailCheck, type EmailField, type EmailMessage, type EmailSetting, type Endpoint, type EndpointRequest, FIELD_TYPES, FIELD_TYPE_BASES, type Field, type FieldAccess, type FieldAccessFn, type FieldAdmin, type FieldError, type FieldRule, 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, ROLE_OPERATIONS, type RawDocument, type ReadOptions, type RelationshipField, type ResolvedConfig, type RestHandler, type RestHandlerOptions, type RichTextDocument, type RichTextField, type Role, type RoleOperation, type RolePermissions, Roles, type RoutesConfig, SESSION_COOKIE, SSO_COOKIE, type ScheduledJob, SchemaError, type SchemaMode, type SelectField, type SelectOption, type Session, type SlugField, type SlugOf, type SsoOutcome, type SsoProviderRef, type StorageAdapter, type StoredFile, type TextField, type TextareaField, TooManyRequestsError, type TypedPlugin, UnauthorizedError, type UpdateInput, type UpdateOptions, type UploadConfig, type UploadField, type UploadFromURLConfig, type UserIdentity, 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, writeBackupFile };
|