@jwilleke/ngdpbase-types 5.20.0 → 5.22.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/lib/packages/ngdpbase-types/index.d.ts +5 -0
- package/lib/src/mail/MailProvider.d.ts +2 -0
- package/lib/src/managers/AddonsManager.d.ts +3 -8
- package/lib/src/managers/AttachmentManager.d.ts +64 -12
- package/lib/src/managers/AuthManager.d.ts +24 -4
- package/lib/src/managers/BackgroundJobManager.d.ts +71 -1
- package/lib/src/managers/BaseManager.d.ts +20 -0
- package/lib/src/managers/BaseManager.js +9 -0
- package/lib/src/managers/CommentManager.d.ts +10 -1
- package/lib/src/managers/ConfigurationManager.d.ts +16 -1
- package/lib/src/managers/DatabaseManager.d.ts +53 -16
- package/lib/src/managers/DatabaseManager.js +172 -38
- package/lib/src/managers/EmailManager.d.ts +42 -0
- package/lib/src/managers/FootnoteManager.d.ts +10 -1
- package/lib/src/managers/NotificationManager.d.ts +13 -0
- package/lib/src/managers/OrganizationManager.d.ts +40 -6
- package/lib/src/managers/PersonManager.d.ts +106 -9
- package/lib/src/managers/RoleManager.d.ts +19 -1
- package/lib/src/managers/UserManager.d.ts +15 -0
- package/lib/src/providers/BaseAttachmentProvider.d.ts +14 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +26 -2
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +12 -0
- package/lib/src/providers/SqliteDatabaseProvider.js +50 -7
- package/lib/src/providers/SqliteJobStateProvider.d.ts +10 -0
- package/lib/src/providers/attachmentFilesBackup.d.ts +52 -0
- package/lib/src/providers/sqliteBackup.d.ts +6 -0
- package/lib/src/providers/sqliteBackup.js +16 -6
- package/lib/src/security/permissions.generated.d.ts +4 -0
- package/lib/src/types/Asset.d.ts +10 -0
- package/lib/src/types/Config.d.ts +11 -0
- package/lib/src/types/Organization.d.ts +7 -0
- package/lib/src/types/Person.d.ts +27 -3
- package/lib/src/types/PersonProvider.d.ts +6 -0
- package/lib/src/types/WikiEngine.d.ts +9 -21
- package/lib/src/utils/addonConfigLayer.d.ts +19 -0
- package/lib/src/utils/addonConfigLayer.js +45 -3
- package/lib/src/utils/addonPageSync.d.ts +24 -0
- package/lib/src/utils/administrators.d.ts +21 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +4 -0
- package/lib/src/utils/auditEventNames.generated.js +4 -0
- package/lib/src/utils/backupCrypto.d.ts +28 -0
- package/lib/src/utils/backupKey.d.ts +23 -0
- package/lib/src/utils/backupKey.js +37 -0
- package/lib/src/utils/configPaths.d.ts +55 -0
- package/lib/src/utils/configPaths.js +133 -0
- package/lib/src/utils/contactAccess.d.ts +58 -0
- package/lib/src/utils/databaseKey.d.ts +7 -0
- package/lib/src/utils/databaseKey.js +7 -0
- package/lib/src/utils/directoryBackup.d.ts +78 -0
- package/lib/src/utils/emailForLog.d.ts +20 -0
- package/lib/src/utils/maintenanceState.d.ts +153 -0
- package/lib/src/utils/pluginFormatters.d.ts +6 -1
- package/lib/src/utils/pluginFormatters.js +5 -2
- package/lib/src/utils/privateStoreTakeout.d.ts +2 -2
- package/lib/src/utils/redactSecrets.js +31 -11
- package/lib/src/utils/sensitiveValues.d.ts +49 -3
- package/lib/src/utils/sensitiveValues.js +139 -11
- package/lib/src/utils/siteRestore.d.ts +110 -0
- package/lib/src/utils/siteRestore.js +309 -0
- package/lib/src/utils/treeArchive.d.ts +44 -0
- package/lib/src/utils/username.d.ts +7 -1
- package/package.json +1 -1
|
@@ -30,3 +30,8 @@ export type { Migration } from '../../src/providers/sqliteMigrations.js';
|
|
|
30
30
|
export type { AddonDatabaseDeclaration, DatabaseEncrypt } from '../../src/utils/addonDatabaseRules.js';
|
|
31
31
|
export type { default as ExportManager } from '../../src/managers/ExportManager.js';
|
|
32
32
|
export type { default as PolicyDecisionPoint } from '../../src/security/PolicyDecisionPoint.js';
|
|
33
|
+
export type { default as PersonManager, LinkOfferOutcome } from '../../src/managers/PersonManager.js';
|
|
34
|
+
export type { default as OrganizationManager } from '../../src/managers/OrganizationManager.js';
|
|
35
|
+
export type { Person, PersonAffiliation, PersonInput, PersonUpdate } from '../../src/types/Person.js';
|
|
36
|
+
export type { Organization, OrganizationInput, OrganizationUpdate } from '../../src/types/Organization.js';
|
|
37
|
+
export type { ContactRefusedError } from '../../src/utils/contactAccess.js';
|
|
@@ -30,6 +30,8 @@ export interface MailProvider {
|
|
|
30
30
|
/**
|
|
31
31
|
* Development transport — writes the email to the server log instead of
|
|
32
32
|
* sending it. Useful for local installs where no SMTP server is available.
|
|
33
|
+
* Like every log line, it names the recipient's domain only, and any address
|
|
34
|
+
* in the body is cut to its domain (#1916).
|
|
33
35
|
*/
|
|
34
36
|
export declare class ConsoleMailProvider implements MailProvider {
|
|
35
37
|
send(message: MailMessage): Promise<void>;
|
|
@@ -17,11 +17,6 @@ import type { WikiEngine } from '../types/WikiEngine.js';
|
|
|
17
17
|
import type { User } from '../types/User.js';
|
|
18
18
|
import { type StoreOwnerState } from '../utils/privateStoreDoor.js';
|
|
19
19
|
import { type AddonHost } from '../utils/addonHost.js';
|
|
20
|
-
/**
|
|
21
|
-
* #1883: what visitors see while a site-critical add-on that did not load
|
|
22
|
-
* holds the site in maintenance mode. Empty leaves only the page's heading.
|
|
23
|
-
*/
|
|
24
|
-
export declare const SITE_CRITICAL_PUBLIC_MESSAGE_KEY = "ngdpbase.managers.addons-manager.site-critical.public-message";
|
|
25
20
|
/**
|
|
26
21
|
* Type passed to `AddonModule.profileSection()` (#534). Matches what
|
|
27
22
|
* `UserManager.getUser()` returns at runtime — `User` minus the password
|
|
@@ -369,9 +364,9 @@ declare class AddonsManager extends BaseManager {
|
|
|
369
364
|
* not load, whatever the reason. Admins get a notification at the most
|
|
370
365
|
* severe level naming it and the reason, which says how to fix it where the
|
|
371
366
|
* reason does. A site-critical add-on (`ngdpbase.addons.<slug>.site-critical`)
|
|
372
|
-
* also holds the whole site in maintenance mode through
|
|
373
|
-
*
|
|
374
|
-
* details, and administrators can still sign in and reach /admin. Once per
|
|
367
|
+
* also holds the whole site in maintenance mode through the one door
|
|
368
|
+
* (`engine.maintenance`, #1906): visitors see only the site's maintenance
|
|
369
|
+
* message, with no details, and administrators can still sign in and reach /admin. Once per
|
|
375
370
|
* add-on per start. Nothing is opened in its place.
|
|
376
371
|
*/
|
|
377
372
|
private recordLoadFailure;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import BaseManager, { BackupData, type ManagerStats } from './BaseManager.js';
|
|
1
|
+
import BaseManager, { BackupData, type ManagerBackupOptions, type ManagerRestoreOptions, type ManagerStats } from './BaseManager.js';
|
|
2
|
+
import type { AttachmentFilesBackupData, AttachmentFilesBackupOutcome } from '../providers/attachmentFilesBackup.js';
|
|
3
|
+
import type { PartOutcome } from '../utils/siteRestore.js';
|
|
2
4
|
import { type ActorContext } from '../context/ActorContext.js';
|
|
3
5
|
/**
|
|
4
6
|
* Marks an error as "the audit record could not be written", not "the action
|
|
@@ -7,13 +9,16 @@ import { type ActorContext } from '../context/ActorContext.js';
|
|
|
7
9
|
export declare const AUDIT_WRITE_FAILED = "EAUDITWRITE";
|
|
8
10
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
9
11
|
import type { CatalogSource, CatalogQuery, CatalogPage, CreativeWork, SchemaType, RebuildOpts } from '../types/Schema.js';
|
|
10
|
-
import type {
|
|
12
|
+
import type { User } from '../providers/BaseAttachmentProvider.js';
|
|
13
|
+
import { type AssetQuery, type AssetRecord } from '../types/Asset.js';
|
|
11
14
|
import type { StoreFileEntry, StoreFileLocation } from '../types/Provider.js';
|
|
12
15
|
/**
|
|
13
16
|
* Base attachment provider interface
|
|
14
17
|
*/
|
|
15
18
|
interface BaseAttachmentProvider {
|
|
16
|
-
initialize(
|
|
19
|
+
initialize(options?: {
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
}): Promise<void>;
|
|
17
22
|
storeAttachment(fileBuffer: Buffer, fileInfo: FileInfo, metadata: AttachmentMetadataInput, user: User | null): Promise<AttachmentMetadata>;
|
|
18
23
|
getAttachment(attachmentId: string): Promise<{
|
|
19
24
|
buffer: Buffer;
|
|
@@ -30,6 +35,12 @@ interface BaseAttachmentProvider {
|
|
|
30
35
|
getThumbnail?(id: string, size: string): Promise<Buffer | null>;
|
|
31
36
|
backup(): Promise<unknown>;
|
|
32
37
|
restore(backupData: unknown): Promise<void>;
|
|
38
|
+
/** #1876: the files themselves beside the backup document, and their staged restore. */
|
|
39
|
+
backupFiles?(directory: string, stem: string): Promise<AttachmentFilesBackupOutcome>;
|
|
40
|
+
stageFilesRestore?(providerBackup: Record<string, unknown>, files: AttachmentFilesBackupData, directory: string, siteRestore: string): Promise<{
|
|
41
|
+
skippedVaults: string[];
|
|
42
|
+
}>;
|
|
43
|
+
takeRestoreAtStart?(): PartOutcome | null;
|
|
33
44
|
shutdown(): Promise<void>;
|
|
34
45
|
getProviderInfo(): {
|
|
35
46
|
features: string[];
|
|
@@ -95,13 +106,6 @@ export interface UploadOptions {
|
|
|
95
106
|
/** Store id when private with no private page; default from ConfigurationManager defaultstoreid */
|
|
96
107
|
store?: string;
|
|
97
108
|
}
|
|
98
|
-
/**
|
|
99
|
-
* User object interface
|
|
100
|
-
*/
|
|
101
|
-
export interface User {
|
|
102
|
-
name: string;
|
|
103
|
-
email?: string;
|
|
104
|
-
}
|
|
105
109
|
/**
|
|
106
110
|
* Mention object (WebPage reference)
|
|
107
111
|
*/
|
|
@@ -148,6 +152,8 @@ export interface AttachmentMetadataInput {
|
|
|
148
152
|
export interface AttachmentBackupData extends BackupData {
|
|
149
153
|
providerClass: string | null;
|
|
150
154
|
providerBackup?: unknown;
|
|
155
|
+
/** The files themselves, as archives beside the document (#1876). */
|
|
156
|
+
files?: AttachmentFilesBackupData;
|
|
151
157
|
data?: null;
|
|
152
158
|
}
|
|
153
159
|
declare class AttachmentManager extends BaseManager implements CatalogSource {
|
|
@@ -302,6 +308,16 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
|
|
|
302
308
|
* @returns {Promise<AttachmentMetadata>} Attachment metadata
|
|
303
309
|
*/
|
|
304
310
|
uploadAttachment(fileBuffer: Buffer, fileInfo: FileInfo, ctx: ActorContext, options?: UploadOptions): Promise<AttachmentMetadata>;
|
|
311
|
+
/**
|
|
312
|
+
* The username an upload is recorded under (#1926): the person the context
|
|
313
|
+
* names. Asked after policy has allowed the upload and before anything is
|
|
314
|
+
* written; it decides nothing about authority, only whether there is
|
|
315
|
+
* someone to record. No one uploads anonymously (operator, 2026-10-20, on
|
|
316
|
+
* #1852), so the anonymous subject — a share visitor included — and a job
|
|
317
|
+
* with no person behind it are refused. A refusal reads 'Permission denied'
|
|
318
|
+
* so the route answers it as one (401 for an anonymous caller).
|
|
319
|
+
*/
|
|
320
|
+
private static uploaderOf;
|
|
305
321
|
/** A file is added to a private page only by someone who may edit it: the page's own decision (#1398). */
|
|
306
322
|
private assertMayEditPage;
|
|
307
323
|
/**
|
|
@@ -446,6 +462,40 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
|
|
|
446
462
|
* is findable exactly as a public one is.
|
|
447
463
|
*/
|
|
448
464
|
searchPrivateStoreAttachments(query: AssetQuery, ctx: ActorContext): Promise<AssetRecord[]>;
|
|
465
|
+
/**
|
|
466
|
+
* {@link searchPrivateStoreAttachments}, kept apart by vault (#1852): the
|
|
467
|
+
* files layer needs to say which vault each file is in. The same stores,
|
|
468
|
+
* the same container rule, the same matching.
|
|
469
|
+
*/
|
|
470
|
+
readableVaultAssetRecords(ctx: ActorContext, query?: AssetQuery): Promise<Array<{
|
|
471
|
+
owner: string;
|
|
472
|
+
vault: string;
|
|
473
|
+
files: AssetRecord[];
|
|
474
|
+
}>>;
|
|
475
|
+
/**
|
|
476
|
+
* The shared pool as asset records (#1852), every one of them: the
|
|
477
|
+
* provider's own mapping, so the files layer and asset search show a
|
|
478
|
+
* public file the same way. Whether the requester may read the pool is the
|
|
479
|
+
* caller's question (`asset-read`).
|
|
480
|
+
*/
|
|
481
|
+
sharedPoolAssetRecords(): Promise<AssetRecord[]>;
|
|
482
|
+
/**
|
|
483
|
+
* Every vault's files, for an operator inventory with no viewer (#1852,
|
|
484
|
+
* the files migration dry run), the way {@link findVaultPagesUsingPoolFiles}
|
|
485
|
+
* walks them: an unsealed vault's index entries; for any other vault
|
|
486
|
+
* (sealed, or not yet opened) only the files on disk and their size, since
|
|
487
|
+
* its index can't be read without its owner's key. Reads only. The entries
|
|
488
|
+
* are for the files layer to count; they are never shown.
|
|
489
|
+
*/
|
|
490
|
+
vaultFileInventory(): Promise<Array<{
|
|
491
|
+
owner: string;
|
|
492
|
+
vault: string;
|
|
493
|
+
/** Whether its index could be read: an unsealed vault. */
|
|
494
|
+
readable: boolean;
|
|
495
|
+
entries: StoreFileEntry[] | null;
|
|
496
|
+
filesOnDisk: number;
|
|
497
|
+
bytesOnDisk: number;
|
|
498
|
+
}>>;
|
|
449
499
|
/**
|
|
450
500
|
* Move every unencrypted private file record out of the global
|
|
451
501
|
* `attachment-metadata.json` and into its store's own index, at start-up.
|
|
@@ -1000,7 +1050,7 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
|
|
|
1000
1050
|
*
|
|
1001
1051
|
* @returns {Promise<AttachmentBackupData>}
|
|
1002
1052
|
*/
|
|
1003
|
-
backup(): Promise<AttachmentBackupData>;
|
|
1053
|
+
backup(options?: ManagerBackupOptions): Promise<AttachmentBackupData>;
|
|
1004
1054
|
/**
|
|
1005
1055
|
* Restore manager data from backup
|
|
1006
1056
|
* Delegates to provider's restore method
|
|
@@ -1008,7 +1058,9 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
|
|
|
1008
1058
|
* @param {AttachmentBackupData} backupData - Backup data from backup() method
|
|
1009
1059
|
* @returns {Promise<void>}
|
|
1010
1060
|
*/
|
|
1011
|
-
restore(backupData: AttachmentBackupData): Promise<void>;
|
|
1061
|
+
restore(backupData: AttachmentBackupData, options?: ManagerRestoreOptions): Promise<void>;
|
|
1062
|
+
/** Its restore is staged for the next start with the rest of a site restore (#1876). */
|
|
1063
|
+
restoreIsStaged(): boolean;
|
|
1012
1064
|
/**
|
|
1013
1065
|
* Shutdown the manager
|
|
1014
1066
|
* @returns {Promise<void>}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @see {@link https://github.com/jwilleke/ngdpbase/issues/396}
|
|
31
31
|
*/
|
|
32
32
|
import BaseManager from './BaseManager.js';
|
|
33
|
-
import type { BackupData } from './BaseManager.js';
|
|
33
|
+
import type { BackupData, ManagerBackupOptions, ManagerRestoreOptions } from './BaseManager.js';
|
|
34
34
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
35
35
|
import type { PermissionSubject } from './UserManager.js';
|
|
36
36
|
import type { JobContext } from '../context/JobContext.js';
|
|
@@ -396,6 +396,8 @@ declare class AuthManager extends BaseManager {
|
|
|
396
396
|
private factorCache;
|
|
397
397
|
/** The credentials store (#1524); null when it could not be opened. */
|
|
398
398
|
private credentials;
|
|
399
|
+
/** `ngdpbase.auth.credentials.file`, resolved at start: what a backup copies (#1876). */
|
|
400
|
+
private credentialsFile;
|
|
399
401
|
constructor(engine: WikiEngine);
|
|
400
402
|
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
401
403
|
/**
|
|
@@ -1126,7 +1128,7 @@ declare class AuthManager extends BaseManager {
|
|
|
1126
1128
|
* The link only opens the approval page; the button there approves, so a
|
|
1127
1129
|
* mail scanner opening links approves nothing. Limited to one per minute.
|
|
1128
1130
|
*/
|
|
1129
|
-
sendEmailApproval(handle: string, binding: string): Promise<'sent' | 'too-soon' | 'unavailable'>;
|
|
1131
|
+
sendEmailApproval(handle: string, binding: string): Promise<'sent' | 'too-soon' | 'unavailable' | 'failed'>;
|
|
1130
1132
|
/** What the approval page shows about the waiting sign-in; null for an unknown or used link. */
|
|
1131
1133
|
approvalDetails(token: string): {
|
|
1132
1134
|
username: string;
|
|
@@ -1260,7 +1262,25 @@ declare class AuthManager extends BaseManager {
|
|
|
1260
1262
|
isEnabled(providerId: string): boolean;
|
|
1261
1263
|
/** Returns all registered providers (for admin UI). */
|
|
1262
1264
|
getProviders(): AuthProvider[];
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
+
/**
|
|
1266
|
+
* The provider list, and the credentials store (#1876): passkeys, TOTP,
|
|
1267
|
+
* verified email and phone, devices, linked identities and proxy grants,
|
|
1268
|
+
* as its own file beside the document, __always encrypted under
|
|
1269
|
+
* `NGDPBASE_BACKUP_KEY`__ (`<stem>.credentials.tar.enc`). Without the key the
|
|
1270
|
+
* store is left out and the backup is reported incomplete; it is never in a
|
|
1271
|
+
* backup unencrypted. The rows stay signed with `NGDPBASE_CREDENTIALS_KEY`,
|
|
1272
|
+
* which is not in the backup: a site restored under another credentials key
|
|
1273
|
+
* holds them in quarantine (#1633).
|
|
1274
|
+
*/
|
|
1275
|
+
backup(options?: ManagerBackupOptions): Promise<BackupData>;
|
|
1276
|
+
/**
|
|
1277
|
+
* Stage the credentials store from a backup for the next start, with the
|
|
1278
|
+
* rest of a site restore (#1876). A backup that left it out (no backup key
|
|
1279
|
+
* when it was taken) stages nothing: the store on the site stays, and the
|
|
1280
|
+
* restore's notification names it as not restored.
|
|
1281
|
+
*/
|
|
1282
|
+
restore(backupData: BackupData, options?: ManagerRestoreOptions): Promise<void>;
|
|
1283
|
+
/** Staged for the next start with the rest of a site restore (#1876). */
|
|
1284
|
+
restoreIsStaged(): boolean;
|
|
1265
1285
|
}
|
|
1266
1286
|
export default AuthManager;
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import BaseManager from './BaseManager.js';
|
|
1
|
+
import BaseManager, { type BackupData, type ManagerBackupOptions, type ManagerRestoreOptions } from './BaseManager.js';
|
|
2
2
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
3
|
import { type JobContext } from '../context/JobContext.js';
|
|
4
4
|
import { type ScheduleInput } from '../utils/schedule.js';
|
|
5
5
|
import { type CatchUp, type Overlap } from '../utils/jobSlots.js';
|
|
6
6
|
import { type JobRunRecord, type JobStateProvider } from '../providers/jobState.js';
|
|
7
|
+
/** Who holds a slot lock: this host, this process, this start. */
|
|
8
|
+
/**
|
|
9
|
+
* One run between a backup and the restore of it (#1876): what admins are
|
|
10
|
+
* told was done on the site and is no longer in the restored job state.
|
|
11
|
+
*/
|
|
12
|
+
export interface JobRunSince {
|
|
13
|
+
jobId: string;
|
|
14
|
+
startedAt: string;
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
status: JobRunRecord['status'];
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
7
19
|
/**
|
|
8
20
|
* Callback supplied to job run functions so they can push live progress
|
|
9
21
|
* messages that the client can display while polling.
|
|
@@ -202,10 +214,29 @@ declare class BackgroundJobManager extends BaseManager {
|
|
|
202
214
|
/** Set once shutdown begins: no slot starts after it (#1717). */
|
|
203
215
|
private stopping;
|
|
204
216
|
private handedOff;
|
|
217
|
+
/**
|
|
218
|
+
* #1892: why no job runs — the application database is configured but could
|
|
219
|
+
* not be opened safely, and the site is in maintenance mode. Jobs still
|
|
220
|
+
* register; none starts, on a schedule or by hand, until a restart opens it.
|
|
221
|
+
*/
|
|
222
|
+
private held;
|
|
205
223
|
/** Each running job's abort, by run id. */
|
|
206
224
|
private controllers;
|
|
207
225
|
/** For each running scheduled run: write it as interrupted and let go of its slot. */
|
|
208
226
|
private handoffs;
|
|
227
|
+
/** `ngdpbase.jobs.state.dir`, when job state is files: what a backup copies (#1876). */
|
|
228
|
+
private stateDir;
|
|
229
|
+
/**
|
|
230
|
+
* The job state was put back from a backup at this start (#1876): every
|
|
231
|
+
* job then starts from now — no slot since the backup is caught up, and a
|
|
232
|
+
* run unfinished in the backup is closed, never resumed or retried. Jobs
|
|
233
|
+
* are never re-run because of a restore.
|
|
234
|
+
*/
|
|
235
|
+
private restoredAtStart;
|
|
236
|
+
/** Jobs whose restored state has been settled, since this start. */
|
|
237
|
+
private settledAfterRestore;
|
|
238
|
+
/** The runs found in the live file state before a restore replaced it (#1876). */
|
|
239
|
+
private runsReplacedAtStart;
|
|
209
240
|
/**
|
|
210
241
|
* @param options - for tests: a state provider in place of the configured
|
|
211
242
|
* one, and the clock.
|
|
@@ -384,6 +415,45 @@ declare class BackgroundJobManager extends BaseManager {
|
|
|
384
415
|
/** Whether the person who started a run still holds the job's permission — by policy, as at the time it is asked (P2). */
|
|
385
416
|
private stillHolds;
|
|
386
417
|
/** A run a restart cut off and that is not run again: recorded as failed, audited, and told to the admins. Never silent. */
|
|
418
|
+
/**
|
|
419
|
+
* A scheduled job's first look after its state was restored (#1876): a run
|
|
420
|
+
* the backup caught unfinished is closed as failed, never resumed or
|
|
421
|
+
* retried, and the next slot is the next one from now, so no slot between
|
|
422
|
+
* the backup and the restore is caught up. The restore's notification
|
|
423
|
+
* lists the runs that had happened since the backup.
|
|
424
|
+
*/
|
|
425
|
+
private settleAfterRestore;
|
|
426
|
+
/**
|
|
427
|
+
* Job state in a backup (#1876). Files (`ngdpbase.jobs.state.dir`): the
|
|
428
|
+
* state, run history and checkpoints as `<stem>.jobs.tar` beside the
|
|
429
|
+
* document, without the slot locks, which belong to the server that holds
|
|
430
|
+
* them. In the database: nothing here — it is in the database backup.
|
|
431
|
+
*/
|
|
432
|
+
backup(options?: ManagerBackupOptions): Promise<BackupData>;
|
|
433
|
+
/** File job state is staged for the next start with the rest of a site restore (#1876). Database job state comes back with the database. */
|
|
434
|
+
restore(backupData: BackupData, options?: ManagerRestoreOptions): Promise<void>;
|
|
435
|
+
restoreIsStaged(): boolean;
|
|
436
|
+
/**
|
|
437
|
+
* Put staged file job state in place, at start, before it is opened
|
|
438
|
+
* (#1876). The runs in the live state since the backup was taken are read
|
|
439
|
+
* first, before it is replaced, for the restore's notification.
|
|
440
|
+
*/
|
|
441
|
+
private applyRestoreAtStart;
|
|
442
|
+
/**
|
|
443
|
+
* The job runs the live application database held, since the backup, when
|
|
444
|
+
* this start's restore replaced it (#1876): read from the copy DatabaseManager
|
|
445
|
+
* kept, which is the database as it was at the moment of the swap — so runs
|
|
446
|
+
* between staging and the restart are named too.
|
|
447
|
+
*/
|
|
448
|
+
private readReplacedDatabaseRuns;
|
|
449
|
+
/** The runs in the file job state at `stateDir` started after `since`, read from disk. */
|
|
450
|
+
private readLiveRunsSince;
|
|
451
|
+
/** The runs of every registered job started after `since`, from the live state (#1876): read before a restore replaces it. */
|
|
452
|
+
runsSince(since: string): Promise<JobRunSince[]>;
|
|
453
|
+
/** The runs the live file state held before a restore replaced it at this start (#1876). */
|
|
454
|
+
getRunsReplacedAtStart(): JobRunSince[];
|
|
455
|
+
/** A job's display name, or its id when no job of that id is registered. */
|
|
456
|
+
displayNameOf(jobId: string): string;
|
|
387
457
|
private reportCutOff;
|
|
388
458
|
/**
|
|
389
459
|
* Get the current state of a run by runId.
|
|
@@ -57,6 +57,12 @@ export interface ManagerBackupOptions {
|
|
|
57
57
|
directory: string;
|
|
58
58
|
/** The document's filename without `.json.gz` / `.json`. */
|
|
59
59
|
stem: string;
|
|
60
|
+
/**
|
|
61
|
+
* The backup a restore takes of the live data first (#1892 D2). A database
|
|
62
|
+
* that can't be opened is left out and recorded in the manager's data, not
|
|
63
|
+
* failed: the restore keeps its file beside the restored one instead.
|
|
64
|
+
*/
|
|
65
|
+
safetyBackup?: boolean;
|
|
60
66
|
}
|
|
61
67
|
/** What BackupManager hands each manager's `restore()` (#1723): who asked, and the directory the backup document was read from. */
|
|
62
68
|
export interface ManagerRestoreOptions {
|
|
@@ -72,6 +78,13 @@ export interface ManagerRestoreOptions {
|
|
|
72
78
|
replaceStaged?: boolean;
|
|
73
79
|
/** The backup of the live data BackupManager took just before this restore (#1723, D6), by filename. */
|
|
74
80
|
safetyBackup?: string;
|
|
81
|
+
/**
|
|
82
|
+
* The site restore manifest this restore is staged under (#1876). A
|
|
83
|
+
* manager whose restore is staged (`restoreIsStaged()`) stages its files
|
|
84
|
+
* beside its live ones and records its part there; the next start applies
|
|
85
|
+
* every part or none (`src/utils/siteRestore.ts`).
|
|
86
|
+
*/
|
|
87
|
+
siteRestore?: string;
|
|
75
88
|
}
|
|
76
89
|
/**
|
|
77
90
|
* Base class for all managers
|
|
@@ -335,6 +348,13 @@ declare abstract class BaseManager {
|
|
|
335
348
|
* }
|
|
336
349
|
*/
|
|
337
350
|
restore(backupData: BackupData, _options?: ManagerRestoreOptions): Promise<void>;
|
|
351
|
+
/**
|
|
352
|
+
* Whether this manager's `restore()` stages for the next start instead of
|
|
353
|
+
* writing into the running site (#1723, #1876). BackupManager stages every
|
|
354
|
+
* such manager in a site restore together, all or nothing, after a backup
|
|
355
|
+
* of the live data. The default is false: the restore writes live.
|
|
356
|
+
*/
|
|
357
|
+
restoreIsStaged(): boolean;
|
|
338
358
|
/**
|
|
339
359
|
* Return a plain-text string suitable for use as MarqueePlugin banner text.
|
|
340
360
|
*
|
|
@@ -299,6 +299,15 @@ class BaseManager {
|
|
|
299
299
|
throw new Error(`${this.constructor.name}: No backup data provided for restore`);
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Whether this manager's `restore()` stages for the next start instead of
|
|
304
|
+
* writing into the running site (#1723, #1876). BackupManager stages every
|
|
305
|
+
* such manager in a site restore together, all or nothing, after a backup
|
|
306
|
+
* of the live data. The default is false: the restore writes live.
|
|
307
|
+
*/
|
|
308
|
+
restoreIsStaged() {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
302
311
|
/**
|
|
303
312
|
* Return a plain-text string suitable for use as MarqueePlugin banner text.
|
|
304
313
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import BaseManager from './BaseManager.js';
|
|
1
|
+
import BaseManager, { type BackupData, type ManagerBackupOptions, type ManagerRestoreOptions } from './BaseManager.js';
|
|
2
2
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
3
|
import type { PageComment } from '../types/Comment.js';
|
|
4
4
|
import { type ActorContext } from '../context/ActorContext.js';
|
|
@@ -10,6 +10,15 @@ export default class CommentManager extends BaseManager {
|
|
|
10
10
|
private readonly outsideCheck;
|
|
11
11
|
constructor(engine: WikiEngine);
|
|
12
12
|
initialize(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* The public folder is in the backup (#1876), as `<stem>.comments.tar` beside
|
|
15
|
+
* the document: the public pages' comments. A vault page's are in its vault, which
|
|
16
|
+
* PageManager backs up with the vault, sealed as stored.
|
|
17
|
+
*/
|
|
18
|
+
backup(options?: ManagerBackupOptions): Promise<BackupData>;
|
|
19
|
+
/** Staged for the next start with the rest of a site restore (#1876); the live folder is untouched. */
|
|
20
|
+
restore(backupData: BackupData, options?: ManagerRestoreOptions): Promise<void>;
|
|
21
|
+
restoreIsStaged(): boolean;
|
|
13
22
|
isEnabled(): boolean;
|
|
14
23
|
/** The public folder and the vaults, as configured now (#1839). */
|
|
15
24
|
private roots;
|
|
@@ -248,6 +248,14 @@ declare class ConfigurationManager extends BaseManager {
|
|
|
248
248
|
* const appName = configManager.getProperty('ngdpbase.application-name', 'MyWiki');
|
|
249
249
|
*/
|
|
250
250
|
getProperty(key: string, defaultValue?: unknown): unknown;
|
|
251
|
+
/**
|
|
252
|
+
* #1889: a value as configured, every `$NAME` reference in it unresolved,
|
|
253
|
+
* for the one kind of reader that must see that a secret IS a reference:
|
|
254
|
+
* AuthManager refuses an outside provider whose client secret is written
|
|
255
|
+
* into configuration rather than named in the environment. Never for
|
|
256
|
+
* display: a literal secret would come back as itself.
|
|
257
|
+
*/
|
|
258
|
+
getUnresolvedProperty(key: string, defaultValue?: unknown): unknown;
|
|
251
259
|
/**
|
|
252
260
|
* #896: read a property from the SHIPPED defaults only, ignoring instance
|
|
253
261
|
* custom-config overrides. Used by vocabulary providers whose seed must not
|
|
@@ -320,7 +328,10 @@ declare class ConfigurationManager extends BaseManager {
|
|
|
320
328
|
* **`$$literal` escape** — for the rare config value that genuinely
|
|
321
329
|
* starts with `$`. `"$$abc"` resolves to `"$abc"`.
|
|
322
330
|
*
|
|
323
|
-
*
|
|
331
|
+
* Inside a map or list (#1889), the bare `$VAR` form and the `$$` escape
|
|
332
|
+
* apply to every string, strictly, the error naming the full path
|
|
333
|
+
* (`key.map-key.field`); the brace form does not. Other non-string values
|
|
334
|
+
* pass through unchanged. Resolution happens at lookup
|
|
324
335
|
* time (per-`getProperty()` call), not at config load — so tests that
|
|
325
336
|
* mutate `process.env` mid-run see the new value on the next read.
|
|
326
337
|
*
|
|
@@ -332,6 +343,10 @@ declare class ConfigurationManager extends BaseManager {
|
|
|
332
343
|
* @throws When the bare `$VAR` form references an unset env var.
|
|
333
344
|
*/
|
|
334
345
|
private resolveEnvRef;
|
|
346
|
+
/** A string inside a map or list (#1889): `$$` escapes, a bare `$NAME` resolves strictly, anything else is itself. */
|
|
347
|
+
private resolveNestedRef;
|
|
348
|
+
/** The variable a bare `$NAME` reference names; throws, naming the key or path, when it is unset (#775). */
|
|
349
|
+
private readEnvRef;
|
|
335
350
|
/** Audit counters for the boot-time env-ref summary log (#775). */
|
|
336
351
|
private _envRefHitCount;
|
|
337
352
|
private _envRefMissCount;
|
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
import BaseManager from './BaseManager.js';
|
|
2
2
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
|
+
import { type SqliteHandle } from '../providers/SqliteDatabaseProvider.js';
|
|
3
4
|
import type { Migration } from '../providers/sqliteMigrations.js';
|
|
4
5
|
import { type ActorContext } from '../context/ActorContext.js';
|
|
5
6
|
import type { BackupData, ManagerBackupOptions, ManagerRestoreOptions } from './BaseManager.js';
|
|
6
7
|
import { type AddonDatabaseDeclaration } from '../utils/addonDatabaseRules.js';
|
|
7
8
|
import { type StagedRestoreManifest } from '../providers/sqliteBackup.js';
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
* The environment variable holding the key every database backup is encrypted
|
|
12
|
-
* under (#1723). Its own secret, not the database key: the copy that travels
|
|
13
|
-
* and the copy that stays should not fall to the same compromise. Never a
|
|
14
|
-
* config key. Unset, no database is backed up, and admins are told.
|
|
15
|
-
*/
|
|
16
|
-
export declare const BACKUP_KEY_ENV = "NGDPBASE_BACKUP_KEY";
|
|
9
|
+
import { DATABASE_KEY_ENV } from '../utils/databaseKey.js';
|
|
10
|
+
export { DATABASE_KEY_ENV };
|
|
11
|
+
export { BACKUP_KEY_ENV, backupKeyFingerprint } from '../utils/backupKey.js';
|
|
17
12
|
/** How long a database backup or restore staging may run on its worker before it is stopped, when not configured. */
|
|
18
13
|
export declare const DEFAULT_BACKUP_WORKER_TIMEOUT_MS = 3600000;
|
|
19
|
-
/**
|
|
20
|
-
* A short fingerprint of a backup key (#1723, D3): the first 16 hex digits of
|
|
21
|
-
* an HMAC of a fixed label under the key. Recorded in each backup so a restore
|
|
22
|
-
* under another key says so plainly; it never reveals the key.
|
|
23
|
-
*/
|
|
24
|
-
export declare function backupKeyFingerprint(key: string): string;
|
|
25
14
|
/** One database in a backup, as DatabaseManager's entry in the backup document lists it. */
|
|
26
15
|
export interface DatabaseBackupEntry {
|
|
27
16
|
/** `application`, or `addon-<owner>`. */
|
|
@@ -46,6 +35,15 @@ export interface DatabaseBackupData {
|
|
|
46
35
|
name: string;
|
|
47
36
|
error: string;
|
|
48
37
|
}>;
|
|
38
|
+
/**
|
|
39
|
+
* #1892 D2: in a restore's safety backup taken while the application
|
|
40
|
+
* database can't be opened, the databases left out and why. The restore
|
|
41
|
+
* keeps their files beside the restored ones, as `*.pre-restore`.
|
|
42
|
+
*/
|
|
43
|
+
leftOut?: {
|
|
44
|
+
databases: string[];
|
|
45
|
+
reason: string;
|
|
46
|
+
};
|
|
49
47
|
}
|
|
50
48
|
/** What an add-on hands over to have its database opened and migrated. */
|
|
51
49
|
export interface AddonDatabaseOptions {
|
|
@@ -82,12 +80,37 @@ declare class DatabaseManager extends BaseManager {
|
|
|
82
80
|
private readonly addonDatabases;
|
|
83
81
|
/** What the start did with a staged restore (#1723), reported once the notifications exist. */
|
|
84
82
|
private restoreAtStart;
|
|
83
|
+
/** The databases a staged restore put back at this start (#1876): kept after the report, for the managers whose data is in them. */
|
|
84
|
+
private restoredNow;
|
|
85
|
+
/** When the restored backup was taken, and where the replaced application database is kept, when a restore was applied at this start (#1876). */
|
|
86
|
+
private replaced;
|
|
85
87
|
/** Why this manager is degraded, one entry per cause, so one cause clearing leaves the others. */
|
|
86
88
|
private readonly faults;
|
|
89
|
+
/** Why the configured application database could not be opened safely (#1892); null otherwise. */
|
|
90
|
+
private unavailable;
|
|
87
91
|
constructor(engine: WikiEngine);
|
|
88
92
|
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
89
93
|
/** Whether an application database is open. */
|
|
90
94
|
isEnabled(): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Why the configured application database is not open (#1892): it could not
|
|
97
|
+
* be opened safely, and the site is in maintenance mode. Null when it is
|
|
98
|
+
* open, or when none is configured (`ngdpbase.database.provider` `none`).
|
|
99
|
+
* A manager that needs the database asks this and holds its work.
|
|
100
|
+
*/
|
|
101
|
+
getUnavailableReason(): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* #1892 (operator D3 rule, 2026-10-18): the application database can't be
|
|
104
|
+
* opened safely. The process stays up in maintenance mode through the one
|
|
105
|
+
* door (`engine.maintenance`, #1906): visitors see only the site's maintenance message,
|
|
106
|
+
* administrators can sign in and reach /admin, and `reportAtBoot()` sends
|
|
107
|
+
* them the reason as a critical notification once notifications exist.
|
|
108
|
+
*/
|
|
109
|
+
private markUnavailable;
|
|
110
|
+
/** The reason for a database that did not open, with the fix that fits the key (#1892). */
|
|
111
|
+
private static openFailure;
|
|
112
|
+
/** The refusal for anything that needs the application database while it is unavailable (#1892). */
|
|
113
|
+
private unavailableRefusal;
|
|
91
114
|
/**
|
|
92
115
|
* The open connection, for a row-storing provider's constructor. Throws when
|
|
93
116
|
* no database is configured, so a provider that needs one fails at boot with
|
|
@@ -151,6 +174,20 @@ declare class DatabaseManager extends BaseManager {
|
|
|
151
174
|
* staged, and admins are told.
|
|
152
175
|
*/
|
|
153
176
|
restore(backupData: BackupData, options?: ManagerRestoreOptions): Promise<void>;
|
|
177
|
+
/** Whether a staged restore put the databases back at this start (#1876): the job state in them is then the backup's. */
|
|
178
|
+
restoredAtStart(): boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Read the application database as it was just before this start's restore
|
|
181
|
+
* replaced it (#1876): the kept `*.pre-restore` file, read-only, under the
|
|
182
|
+
* site's database key, which it was encrypted under. For the owner of data
|
|
183
|
+
* in it (BackgroundJobManager's job runs since the backup) to say what the
|
|
184
|
+
* restore took away. Null when no restore replaced it at this start.
|
|
185
|
+
*/
|
|
186
|
+
readReplacedApplicationDatabase<T>(read: (db: SqliteHandle, backupTakenAt: string) => T): T | null;
|
|
187
|
+
/** The databases a staged restore put back at this start, by name (#1876). */
|
|
188
|
+
getRestoredAtStart(): string[];
|
|
189
|
+
/** Its restore is staged for the next start (#1723), and joins a site restore (#1876). */
|
|
190
|
+
restoreIsStaged(): boolean;
|
|
154
191
|
/** The restore staged and waiting for the next start, or null. */
|
|
155
192
|
getStagedRestore(): StagedRestoreManifest | null;
|
|
156
193
|
/**
|
|
@@ -180,7 +217,7 @@ declare class DatabaseManager extends BaseManager {
|
|
|
180
217
|
private fault;
|
|
181
218
|
private clearFault;
|
|
182
219
|
private notify;
|
|
183
|
-
/** Whether every open database passes SQLite's quick check; true when none is configured. */
|
|
220
|
+
/** Whether every open database passes SQLite's quick check; true when none is configured, false when the configured one could not be opened (#1892). */
|
|
184
221
|
integrityOk(): boolean;
|
|
185
222
|
/** Closes the add-on databases, then the application database. */
|
|
186
223
|
shutdown(): Promise<void>;
|