@jwilleke/ngdpbase-types 5.39.0 → 5.41.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 +1 -1
- package/lib/src/context/ApiContext.d.ts +3 -2
- package/lib/src/context/ApiContext.js +4 -2
- package/lib/src/ice/fileHandling.d.ts +6 -1
- package/lib/src/ice/givenName.d.ts +19 -0
- package/lib/src/ice/runExport.d.ts +41 -0
- package/lib/src/ice/runImport.d.ts +37 -8
- package/lib/src/ice/shims/csv.d.ts +24 -0
- package/lib/src/ice/shims/export.d.ts +23 -0
- package/lib/src/ice/shims/index.d.ts +3 -1
- package/lib/src/ice/shims/svg.d.ts +27 -0
- package/lib/src/ice/shims/takeout.d.ts +83 -0
- package/lib/src/ice/types.d.ts +301 -5
- package/lib/src/ice/vet.d.ts +19 -0
- package/lib/src/managers/AttachmentManager.d.ts +100 -36
- package/lib/src/managers/AuthManager.d.ts +152 -12
- package/lib/src/managers/AuthManager.js +3830 -0
- package/lib/src/managers/BackgroundJobManager.d.ts +25 -0
- package/lib/src/managers/BackupManager.d.ts +24 -0
- package/lib/src/managers/BackupManager.js +48 -1
- package/lib/src/managers/ExportManager.d.ts +16 -0
- package/lib/src/managers/ExportManager.js +25 -0
- package/lib/src/managers/FileManager.d.ts +182 -0
- package/lib/src/managers/ImportManager.d.ts +279 -16
- package/lib/src/managers/MediaManager.d.ts +2 -0
- package/lib/src/managers/PageManager.d.ts +53 -0
- package/lib/src/managers/SearchManager.d.ts +4 -0
- package/lib/src/managers/SharedFolderManager.d.ts +320 -0
- package/lib/src/managers/ValidationManager.d.ts +5 -2
- package/lib/src/middleware/csrf.js +99 -0
- package/lib/src/providers/AgentTokenAuthProvider.js +46 -0
- package/lib/src/providers/AuthentikBearerAuthProvider.js +176 -0
- package/lib/src/providers/BaseCredentialsProvider.js +21 -0
- package/lib/src/providers/BaseMediaProvider.d.ts +4 -0
- package/lib/src/providers/BasePageProvider.d.ts +8 -0
- package/lib/src/providers/CloudflareAccessAuthProvider.js +160 -0
- package/lib/src/providers/FileCredentialsProvider.js +188 -0
- package/lib/src/providers/FileSystemMediaProvider.d.ts +6 -0
- package/lib/src/providers/MagicLinkAuthProvider.js +333 -0
- package/lib/src/providers/OidcAuthProvider.js +350 -0
- package/lib/src/providers/OidcBearerAuthProvider.js +27 -0
- package/lib/src/providers/PasskeyAuthProvider.js +147 -0
- package/lib/src/providers/PasswordAuthProvider.js +44 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +3 -2
- package/lib/src/security/pageActions.js +43 -0
- package/lib/src/security/permissions.generated.d.ts +7 -3
- package/lib/src/types/File.d.ts +90 -0
- package/lib/src/types/Provider.d.ts +15 -0
- package/lib/src/types/express.d.ts +7 -1
- package/lib/src/types/guards.js +366 -0
- package/lib/src/types/linkedom.d.ts +2 -0
- package/lib/src/utils/atomicWrite.js +129 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +12 -0
- package/lib/src/utils/auditEventNames.generated.js +12 -0
- package/lib/src/utils/auditEvents.d.ts +15 -0
- package/lib/src/utils/auditEvents.js +17 -1
- package/lib/src/utils/bip39English.js +2 -0
- package/lib/src/utils/browserDescription.js +28 -0
- package/lib/src/utils/delegation.js +79 -0
- package/lib/src/utils/deriveUsername.js +38 -0
- package/lib/src/utils/emailForLog.js +28 -0
- package/lib/src/utils/fileMigrationPlan.d.ts +81 -0
- package/lib/src/utils/instanceEnvBackup.d.ts +6 -0
- package/lib/src/utils/instanceEnvBackup.js +13 -0
- package/lib/src/utils/ipLocation.js +106 -0
- package/lib/src/utils/magicLinkDeviceState.js +97 -0
- package/lib/src/utils/pageUrl.d.ts +1 -0
- package/lib/src/utils/passwordHash.js +150 -0
- package/lib/src/utils/permissionCatalog.js +102 -0
- package/lib/src/utils/privateStoreAccess.d.ts +25 -0
- package/lib/src/utils/privateStoreAccess.js +22 -1
- package/lib/src/utils/privateStoreCatalogs.js +57 -0
- package/lib/src/utils/privateStoreCrypto.js +266 -0
- package/lib/src/utils/privateStoreDoor.d.ts +10 -0
- package/lib/src/utils/privateStoreDoor.js +298 -0
- package/lib/src/utils/privateStoreImport.d.ts +0 -10
- package/lib/src/utils/privateStoreMeta.js +123 -0
- package/lib/src/utils/privateStorePath.d.ts +37 -1
- package/lib/src/utils/privateStorePath.js +60 -3
- package/lib/src/utils/privateStoreTakeout.js +149 -0
- package/lib/src/utils/privateStoreUnlock.d.ts +3 -0
- package/lib/src/utils/privateStoreUnlock.js +499 -0
- package/lib/src/utils/recoveryWordsFile.d.ts +54 -0
- package/lib/src/utils/safeRedirect.js +55 -0
- package/lib/src/utils/sessionAccount.js +55 -0
- package/lib/src/utils/sessionEnd.d.ts +1 -0
- package/lib/src/utils/sessionEnd.js +42 -0
- package/lib/src/utils/sessionGeneration.js +29 -0
- package/lib/src/utils/sessionIdle.d.ts +4 -0
- package/lib/src/utils/sessionIdle.js +72 -0
- package/lib/src/utils/sessionLimits.js +72 -0
- package/lib/src/utils/sessionSecurity.js +122 -0
- package/lib/src/utils/shareLockbox.js +101 -0
- package/lib/src/utils/sharedFolderAccess.d.ts +57 -0
- package/lib/src/utils/sharedFolderAccess.js +80 -0
- package/lib/src/utils/userCreateError.js +82 -0
- package/lib/src/utils/username.d.ts +2 -0
- package/lib/src/utils/username.js +105 -0
- package/package.json +2 -1
|
@@ -77,6 +77,23 @@ export interface ScheduledJobView {
|
|
|
77
77
|
/** History, newest first. A run with no slot was started by hand. */
|
|
78
78
|
runs: JobRunRecord[];
|
|
79
79
|
}
|
|
80
|
+
/** A run that ended, as the /admin dashboard shows it (#2138). */
|
|
81
|
+
export interface LastJobRun {
|
|
82
|
+
startedAt: string;
|
|
83
|
+
completedAt: string;
|
|
84
|
+
status: 'completed' | 'failed';
|
|
85
|
+
/** The username who asked; null for a run its schedule started. */
|
|
86
|
+
ranBy: string | null;
|
|
87
|
+
}
|
|
88
|
+
/** A job's newest ended run (#2138), from {@link BackgroundJobManager.lastFinishedRun}. */
|
|
89
|
+
export interface JobLastRun {
|
|
90
|
+
jobId: string;
|
|
91
|
+
displayName: string;
|
|
92
|
+
/** Null when it has never run — or, with `sinceStart`, not since this server started. */
|
|
93
|
+
lastRun: LastJobRun | null;
|
|
94
|
+
/** The job's history is kept in memory only (a maintenance tick, or no job state open): it starts empty at each start. */
|
|
95
|
+
sinceStart: boolean;
|
|
96
|
+
}
|
|
80
97
|
/**
|
|
81
98
|
* A job type that can be registered with the BackgroundJobManager.
|
|
82
99
|
*/
|
|
@@ -343,6 +360,14 @@ declare class BackgroundJobManager extends BaseManager {
|
|
|
343
360
|
* job's history.
|
|
344
361
|
*/
|
|
345
362
|
scheduledJobView(jobId: string): Promise<ScheduledJobView | null>;
|
|
363
|
+
/**
|
|
364
|
+
* The newest run of a job that ended, completed or failed, and who asked
|
|
365
|
+
* for it (#2138) — what the /admin dashboard shows under Reindex and
|
|
366
|
+
* Rebuild. Read from the history already kept: the job state, and this
|
|
367
|
+
* process's own runs for what the state does not hold (a maintenance tick,
|
|
368
|
+
* or no state open). Null when no job of that id is registered.
|
|
369
|
+
*/
|
|
370
|
+
lastFinishedRun(jobId: string): Promise<JobLastRun | null>;
|
|
346
371
|
private viewOf;
|
|
347
372
|
private scheduledOrThrow;
|
|
348
373
|
/** Refused while the job runs, waits for a retry, or the server is stopping: one run of a job at a time. */
|
|
@@ -322,6 +322,18 @@ export declare function longestGapMs(schedule: Schedule, from: Date): number;
|
|
|
322
322
|
* said nothing.
|
|
323
323
|
*/
|
|
324
324
|
export declare function autoBackupSchedule(time: string, days: string, timeZone: string): ScheduleInput;
|
|
325
|
+
/**
|
|
326
|
+
* The backup key file (#2134): what it is, for which site, the key's
|
|
327
|
+
* fingerprint, when it was saved (the server's local time), how to restore
|
|
328
|
+
* with it, and the one `NGDPBASE_BACKUP_KEY=` line, written so the boot's
|
|
329
|
+
* `.env` reader reads the key back exactly.
|
|
330
|
+
*/
|
|
331
|
+
export declare function backupKeyFileText(input: {
|
|
332
|
+
site: string;
|
|
333
|
+
fingerprint: string;
|
|
334
|
+
key: string;
|
|
335
|
+
saved: string;
|
|
336
|
+
}): string;
|
|
325
337
|
/**
|
|
326
338
|
* The text of the critical "not restored" notification (#1876): what the
|
|
327
339
|
* restore put back, what it did not, and per job the runs since the backup —
|
|
@@ -483,6 +495,18 @@ declare class BackupManager extends BaseManager {
|
|
|
483
495
|
* sign-in; nothing else shows, logs or returns it.
|
|
484
496
|
*/
|
|
485
497
|
revealBackupKey(): string | null;
|
|
498
|
+
/**
|
|
499
|
+
* The backup key as a small text file, for an admin to keep off the server
|
|
500
|
+
* without the rest of `.env` (#2134). The route asks `config-manage`, with
|
|
501
|
+
* its fresh sign-in. Recorded as `backup-key-download` (who and the
|
|
502
|
+
* fingerprint, never the key) before the file is made: on-failure refuse,
|
|
503
|
+
* so a record that cannot be written throws and nothing is sent. Null when
|
|
504
|
+
* the site has no backup key. Nothing is kept on the server.
|
|
505
|
+
*/
|
|
506
|
+
revealBackupKeyFile(ctx: ActorContext): Promise<{
|
|
507
|
+
fingerprint: string;
|
|
508
|
+
text: string;
|
|
509
|
+
} | null>;
|
|
486
510
|
/**
|
|
487
511
|
* The instance `.env` exactly as it is on disk, backup key line included,
|
|
488
512
|
* for an admin to keep a copy off the server (#1938). The route asks
|
|
@@ -15,7 +15,7 @@ import { BACKUP_KEY_ENV, backupKeyFingerprint, currentBackupKey, isLeftOutForNoK
|
|
|
15
15
|
import { currentSharedFoldersKey, SHARED_FOLDERS_KEY_ENV, sharedFoldersKeyFingerprint } from '../utils/sharedFoldersKey.js';
|
|
16
16
|
import { applyDeclaredPartsAtStart, archiveReader, archiveTarget, archiveWriter, backupDeclaredParts, placePartRecords, stageDeclaredParts, stageFilePart } from '../utils/directoryBackup.js';
|
|
17
17
|
import { writeTreeArchive } from '../utils/treeArchive.js';
|
|
18
|
-
import { envForBackup, envKeyNames, envKeyNamesFingerprint, INSTANCE_ENV_PART, instanceEnvPath } from '../utils/instanceEnvBackup.js';
|
|
18
|
+
import { envFileLine, envForBackup, envKeyNames, envKeyNamesFingerprint, INSTANCE_ENV_PART, instanceEnvPath } from '../utils/instanceEnvBackup.js';
|
|
19
19
|
import { readEnvLine } from '../utils/instanceEnvSecret.js';
|
|
20
20
|
import { systemContext } from '../context/bootActions.js';
|
|
21
21
|
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
@@ -315,6 +315,24 @@ function localTime(iso) {
|
|
|
315
315
|
const t = iso ? Date.parse(iso) : NaN;
|
|
316
316
|
return Number.isFinite(t) ? new Date(t).toLocaleString() : 'an unknown time';
|
|
317
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* The backup key file (#2134): what it is, for which site, the key's
|
|
320
|
+
* fingerprint, when it was saved (the server's local time), how to restore
|
|
321
|
+
* with it, and the one `NGDPBASE_BACKUP_KEY=` line, written so the boot's
|
|
322
|
+
* `.env` reader reads the key back exactly.
|
|
323
|
+
*/
|
|
324
|
+
export function backupKeyFileText(input) {
|
|
325
|
+
return [
|
|
326
|
+
'# ngdpbase backup key',
|
|
327
|
+
`# Site: ${input.site}`,
|
|
328
|
+
`# Fingerprint: ${input.fingerprint} (Admin → Backup on the restoring site must show the same)`,
|
|
329
|
+
`# Saved: ${input.saved}`,
|
|
330
|
+
'# Restore: put the line below in the new server\'s .env before it first starts (or replace its key, then restart).',
|
|
331
|
+
'# Keep this file off the server, somewhere as safe as a password manager. Anyone with this file and a backup can read that backup.',
|
|
332
|
+
envFileLine(BACKUP_KEY_ENV, input.key),
|
|
333
|
+
''
|
|
334
|
+
].join('\n');
|
|
335
|
+
}
|
|
318
336
|
/**
|
|
319
337
|
* The text of the critical "not restored" notification (#1876): what the
|
|
320
338
|
* restore put back, what it did not, and per job the runs since the backup —
|
|
@@ -925,6 +943,35 @@ class BackupManager extends BaseManager {
|
|
|
925
943
|
revealBackupKey() {
|
|
926
944
|
return currentBackupKey() || null;
|
|
927
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* The backup key as a small text file, for an admin to keep off the server
|
|
948
|
+
* without the rest of `.env` (#2134). The route asks `config-manage`, with
|
|
949
|
+
* its fresh sign-in. Recorded as `backup-key-download` (who and the
|
|
950
|
+
* fingerprint, never the key) before the file is made: on-failure refuse,
|
|
951
|
+
* so a record that cannot be written throws and nothing is sent. Null when
|
|
952
|
+
* the site has no backup key. Nothing is kept on the server.
|
|
953
|
+
*/
|
|
954
|
+
async revealBackupKeyFile(ctx) {
|
|
955
|
+
const key = currentBackupKey();
|
|
956
|
+
if (!key)
|
|
957
|
+
return null;
|
|
958
|
+
const fingerprint = backupKeyFingerprint(key);
|
|
959
|
+
const who = actorOf(ctx);
|
|
960
|
+
await recordAuditEvent(this.engine.getManager('AuditManager'), {
|
|
961
|
+
eventType: AUDIT_EVENT.BACKUP_KEY_DOWNLOAD,
|
|
962
|
+
...who,
|
|
963
|
+
action: 'backup-key-download',
|
|
964
|
+
result: 'success',
|
|
965
|
+
severity: 'high',
|
|
966
|
+
resource: BACKUP_KEY_ENV,
|
|
967
|
+
resourceType: 'backup-key',
|
|
968
|
+
metadata: { ...who.metadata, fingerprint }
|
|
969
|
+
});
|
|
970
|
+
const name = this.setting('ngdpbase.application-name', '');
|
|
971
|
+
const baseUrl = this.setting('ngdpbase.application.base-url', '');
|
|
972
|
+
const site = (name && baseUrl ? `${name} (${baseUrl})` : name || baseUrl) || 'this site';
|
|
973
|
+
return { fingerprint, text: backupKeyFileText({ site, fingerprint, key, saved: localTime(new Date().toISOString()) }) };
|
|
974
|
+
}
|
|
928
975
|
/**
|
|
929
976
|
* The instance `.env` exactly as it is on disk, backup key line included,
|
|
930
977
|
* for an admin to keep a copy off the server (#1938). The route asks
|
|
@@ -128,6 +128,11 @@ declare class ExportManager extends BaseManager {
|
|
|
128
128
|
* with the reason, as `none` is.
|
|
129
129
|
*/
|
|
130
130
|
private pdfProvider;
|
|
131
|
+
/**
|
|
132
|
+
* Is a PDF provider chosen (#2120)? The Export action offers PDF only then.
|
|
133
|
+
* Whether the chosen one works is known only by rendering, as before.
|
|
134
|
+
*/
|
|
135
|
+
isPdfConfigured(): boolean;
|
|
131
136
|
/** Log why, flag the manager (unless something worse is already shown), and refuse. */
|
|
132
137
|
private pdfUnavailable;
|
|
133
138
|
private acquirePdfSlot;
|
|
@@ -148,6 +153,17 @@ declare class ExportManager extends BaseManager {
|
|
|
148
153
|
* @returns {Promise<string>} HTML content
|
|
149
154
|
*/
|
|
150
155
|
exportPageToHtml(pageName: string, ctx: ActorContext, user?: ExportUser | null): Promise<string>;
|
|
156
|
+
/**
|
|
157
|
+
* One page as a complete HTML document: its content rendered, with the
|
|
158
|
+
* export's styling and metadata footer. It reads nothing: the caller has
|
|
159
|
+
* read the page through its door (the page export source, #2120).
|
|
160
|
+
*
|
|
161
|
+
* @param pageName - The page's name, its heading
|
|
162
|
+
* @param page - Its content and metadata
|
|
163
|
+
* @param user - User object for locale-aware formatting
|
|
164
|
+
* @returns The HTML document
|
|
165
|
+
*/
|
|
166
|
+
renderPageDocument(pageName: string, page: PageForExport, user?: ExportUser | null): Promise<string>;
|
|
151
167
|
/**
|
|
152
168
|
* Export multiple pages to a single HTML file
|
|
153
169
|
* @param {string[]} pageNames - Array of page names
|
|
@@ -182,6 +182,14 @@ class ExportManager extends BaseManager {
|
|
|
182
182
|
return new NonePdfProvider(`ngdpbase.export.pdf.provider '${name}' is not a PDF provider: use ${PDF_PROVIDERS.join(', ')}`);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Is a PDF provider chosen (#2120)? The Export action offers PDF only then.
|
|
187
|
+
* Whether the chosen one works is known only by rendering, as before.
|
|
188
|
+
*/
|
|
189
|
+
isPdfConfigured() {
|
|
190
|
+
const name = this.engine.getManager('ConfigurationManager')?.getProperty('ngdpbase.export.pdf.provider', PDF_DEFAULTS.provider);
|
|
191
|
+
return (name ?? PDF_DEFAULTS.provider) !== 'none';
|
|
192
|
+
}
|
|
185
193
|
/** Log why, flag the manager (unless something worse is already shown), and refuse. */
|
|
186
194
|
pdfUnavailable(reason) {
|
|
187
195
|
logger.warn(`[ExportManager] PDF export unavailable: ${reason}`);
|
|
@@ -248,6 +256,23 @@ class ExportManager extends BaseManager {
|
|
|
248
256
|
if (!page) {
|
|
249
257
|
throw new Error(`Page '${pageName}' not found`);
|
|
250
258
|
}
|
|
259
|
+
return this.renderPageDocument(pageName, page, user);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* One page as a complete HTML document: its content rendered, with the
|
|
263
|
+
* export's styling and metadata footer. It reads nothing: the caller has
|
|
264
|
+
* read the page through its door (the page export source, #2120).
|
|
265
|
+
*
|
|
266
|
+
* @param pageName - The page's name, its heading
|
|
267
|
+
* @param page - Its content and metadata
|
|
268
|
+
* @param user - User object for locale-aware formatting
|
|
269
|
+
* @returns The HTML document
|
|
270
|
+
*/
|
|
271
|
+
async renderPageDocument(pageName, page, user = null) {
|
|
272
|
+
const renderingManager = this.engine.getManager('RenderingManager');
|
|
273
|
+
if (!renderingManager) {
|
|
274
|
+
throw new Error('Required managers not available');
|
|
275
|
+
}
|
|
251
276
|
// Render the page content (without user context for exports)
|
|
252
277
|
const renderedContent = await renderingManager.renderMarkdown(page.content, pageName, null);
|
|
253
278
|
// Create full HTML document
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileManager — the one door for files (#1852, epic #1851).
|
|
3
|
+
*
|
|
4
|
+
* It reads today's attachments as file records (src/types/File.ts): the
|
|
5
|
+
* shared pool as the public place, and every vault the requester may read.
|
|
6
|
+
* It writes through one door, `put` (#2148): a file goes into the place the
|
|
7
|
+
* caller chose, after the permission for that place, with AttachmentManager
|
|
8
|
+
* as the storage underneath; `placesWritableBy` lists the places a user may
|
|
9
|
+
* write to. Rename, move, delete and per-place indexes are later steps
|
|
10
|
+
* (docs/guides/storage-developer-guide.md).
|
|
11
|
+
*
|
|
12
|
+
* Read access is the existing rules, asked at this door:
|
|
13
|
+
* - the public place needs `asset-read`, from the PDP;
|
|
14
|
+
* - a vault is read through AttachmentManager's own vault reads, so the
|
|
15
|
+
* container rule (owner or delegate, never a role, admin included) decides
|
|
16
|
+
* exactly as it does for a vault page's files.
|
|
17
|
+
*
|
|
18
|
+
* The operator inventory (the migration dry run, #1859) is not on the
|
|
19
|
+
* engine's door: only an instance opened by {@link FileManager.openForOperator},
|
|
20
|
+
* from the command line, answers it.
|
|
21
|
+
*/
|
|
22
|
+
import BaseManager from './BaseManager.js';
|
|
23
|
+
import { type AttachmentMetadata, type PutFile, type PutOptions, type UploadOptions } from './AttachmentManager.js';
|
|
24
|
+
import { type ActorContext } from '../context/ActorContext.js';
|
|
25
|
+
import { type AttachmentDetail } from '../utils/auditEvents.js';
|
|
26
|
+
import { type AssetRecord } from '../types/Asset.js';
|
|
27
|
+
import type { FileInventory, FilePlace, FileRecord } from '../types/File.js';
|
|
28
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
29
|
+
import { type FileMigrationReport } from '../utils/fileMigrationPlan.js';
|
|
30
|
+
/**
|
|
31
|
+
* What `put` hands back (#2149): the stored file's metadata, and, when
|
|
32
|
+
* vetting cleaned it, what was removed (one note per kind), so the uploader
|
|
33
|
+
* can be told.
|
|
34
|
+
*/
|
|
35
|
+
export type PutResult = AttachmentMetadata & {
|
|
36
|
+
cleaned?: string[];
|
|
37
|
+
};
|
|
38
|
+
/** A place the Upload file dialog offers (#2149): its key in the form, the words the user reads, and the place. */
|
|
39
|
+
export interface UploadPlace {
|
|
40
|
+
key: string;
|
|
41
|
+
label: string;
|
|
42
|
+
place: FilePlace;
|
|
43
|
+
}
|
|
44
|
+
/** Where the Upload file dialog was opened (#2149): the page's path, and the page it shows, when there is one. */
|
|
45
|
+
export interface UploadWhere {
|
|
46
|
+
path?: string;
|
|
47
|
+
pageName?: string;
|
|
48
|
+
}
|
|
49
|
+
declare class FileManager extends BaseManager {
|
|
50
|
+
/** Set only by {@link openForOperator}: this instance answers the inventory. */
|
|
51
|
+
private operatorInstance;
|
|
52
|
+
constructor(engine: WikiEngine);
|
|
53
|
+
/**
|
|
54
|
+
* Open the door read-only for an operator command (#1859): the site's
|
|
55
|
+
* configuration and attachment index, with nothing else started and
|
|
56
|
+
* nothing written. The site may be running; this process changes no file.
|
|
57
|
+
*/
|
|
58
|
+
static openForOperator(): Promise<FileManager>;
|
|
59
|
+
/** Every file `ctx` may read, newest first; `place` narrows it to one place. */
|
|
60
|
+
list(ctx: ActorContext, filter?: {
|
|
61
|
+
place?: FilePlace;
|
|
62
|
+
}): Promise<FileRecord[]>;
|
|
63
|
+
/** The file with this id, when `ctx` may read it. */
|
|
64
|
+
get(id: string, ctx: ActorContext): Promise<FileRecord | null>;
|
|
65
|
+
/**
|
|
66
|
+
* The files named `filename` that `ctx` may read: one per place at most,
|
|
67
|
+
* since a name is unique within a place (today's pool can hold more than
|
|
68
|
+
* one, which is what the migration's suffixes are for). `place` narrows it.
|
|
69
|
+
*/
|
|
70
|
+
findByName(filename: string, ctx: ActorContext, place?: FilePlace): Promise<FileRecord[]>;
|
|
71
|
+
/**
|
|
72
|
+
* The username a public record's uploader names (#2097): the uploader is
|
|
73
|
+
* stored as an identity @id, resolved through IdentityManager; an orphan,
|
|
74
|
+
* an @id with no account, `Unknown` or an email names no owner.
|
|
75
|
+
*/
|
|
76
|
+
private ownerOf;
|
|
77
|
+
private readable;
|
|
78
|
+
/** `asset-read` for the public place, from the PDP. No PDP, no answer: closed. */
|
|
79
|
+
private mayReadPublic;
|
|
80
|
+
/**
|
|
81
|
+
* The places `ctx` may put a file in (#2148, operator 2026-10-30): the
|
|
82
|
+
* public place when the PDP grants `asset-upload`; each of the user's own
|
|
83
|
+
* vaults the vault rule lets them upload to (the default vault, and every
|
|
84
|
+
* vault they already have); each shared folder where they hold
|
|
85
|
+
* `shared-folder-file`. A place they may not write to is never listed.
|
|
86
|
+
*
|
|
87
|
+
* Each place is asked exactly as {@link put} asks it, so what is offered is
|
|
88
|
+
* what the door allows. An affordance: nothing is recorded.
|
|
89
|
+
*/
|
|
90
|
+
placesWritableBy(ctx: ActorContext): Promise<FilePlace[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Put a file in `place` (#2148): the one write door. The permission for
|
|
93
|
+
* that place is asked first, and a refusal stores nothing. Then ICE vets
|
|
94
|
+
* the file (#2149): its type from its bytes, and its format's shim cleans
|
|
95
|
+
* it; the cleaned copy is the one kept. AttachmentManager stores it as the
|
|
96
|
+
* place requires (sealed in a sealed vault or shared folder), and this door
|
|
97
|
+
* records the upload with what vetting removed (a sealed place's file by
|
|
98
|
+
* id only, #2048). No page decides the place: `options.pageName` is only
|
|
99
|
+
* the page that shows it.
|
|
100
|
+
*
|
|
101
|
+
* @throws Error 'Permission denied: …' when `ctx` may not write there, or
|
|
102
|
+
* is nobody (#1926); a route answers it 401 or 403.
|
|
103
|
+
* @throws IceVetRefused when the file could not be checked; nothing is stored
|
|
104
|
+
*/
|
|
105
|
+
put(ctx: ActorContext, place: FilePlace, file: PutFile, options?: PutOptions): Promise<PutResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Record a file stored or deleted (#2149: moved here from AttachmentManager,
|
|
108
|
+
* which stores and records nothing itself). An upload is `continue`: a
|
|
109
|
+
* failed record is logged, never fatal. A delete is critical: it rethrows,
|
|
110
|
+
* tagged `AUDIT_WRITE_FAILED`, so the delete does not happen unrecorded and
|
|
111
|
+
* a caller can tell an unwritable record from a failed delete.
|
|
112
|
+
*
|
|
113
|
+
* A sealed store's file is recorded by id and store only (#2048), its
|
|
114
|
+
* owner by identity @id with the username beside it (#2041).
|
|
115
|
+
* AttachmentManager's delete door calls this until deleting is FileManager's.
|
|
116
|
+
*/
|
|
117
|
+
recordFileEvent(op: 'upload' | 'delete', context: ActorContext, detail: AttachmentDetail, wikiContext?: {
|
|
118
|
+
request?: {
|
|
119
|
+
ip?: string;
|
|
120
|
+
} | null;
|
|
121
|
+
}): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* What the Upload file dialog offers `ctx` (#2149): every place
|
|
124
|
+
* {@link placesWritableBy} lists, and nothing else, each with the words the
|
|
125
|
+
* user reads; and the one selected at first: where they are working
|
|
126
|
+
* ({@link workingPlaceKey}), else the public place, else the first. Never
|
|
127
|
+
* inferred from whether a page exists. An affordance: nothing is recorded.
|
|
128
|
+
*/
|
|
129
|
+
uploadPlaces(ctx: ActorContext, where?: UploadWhere): Promise<{
|
|
130
|
+
places: UploadPlace[];
|
|
131
|
+
defaultKey: string | null;
|
|
132
|
+
}>;
|
|
133
|
+
/** The place a dialog key names, when it is one `ctx` may write to (#2149); null otherwise. Never a place it was not offered. */
|
|
134
|
+
placeForKey(ctx: ActorContext, key: string): Promise<FilePlace | null>;
|
|
135
|
+
/** A place's key in the dialog: `public`, `vault:<id>` (always the user's own) or `folder:<name>`. */
|
|
136
|
+
static placeKey(place: FilePlace): string;
|
|
137
|
+
/** The words the user reads for a place. */
|
|
138
|
+
private placeLabel;
|
|
139
|
+
/**
|
|
140
|
+
* Where the user is working, as a dialog key (#2149): a shared folder's
|
|
141
|
+
* list (`/shared/<folder>`) or one of its pages is that folder; a page in
|
|
142
|
+
* one of their own vaults is that vault. Read from the location, never
|
|
143
|
+
* from whether a page by that name exists; anywhere else is no place.
|
|
144
|
+
*/
|
|
145
|
+
private workingPlaceKey;
|
|
146
|
+
/**
|
|
147
|
+
* The place today's page-tied upload goes to (#1396, #1398, #2125), until
|
|
148
|
+
* #2149/#2150 move its callers to a place the user chose: an upload onto a
|
|
149
|
+
* shared folder's page goes into that folder, saved or not; onto a vault
|
|
150
|
+
* page into that page's vault, by someone who may edit the page; with the
|
|
151
|
+
* Private tick into the uploader's own vault (`store`, or their default);
|
|
152
|
+
* otherwise the public place.
|
|
153
|
+
*/
|
|
154
|
+
placeForPageUpload(ctx: ActorContext, options: Pick<UploadOptions, 'pageName' | 'private' | 'store'>): Promise<FilePlace>;
|
|
155
|
+
/** May `ctx` put a file in `place`? The existing rule for each kind, asked of its owner. */
|
|
156
|
+
private mayPut;
|
|
157
|
+
/** The user's own vaults a file may go in: their default vault, and every vault they already have. */
|
|
158
|
+
private ownVaultIds;
|
|
159
|
+
private storeLayout;
|
|
160
|
+
/** A well-formed place: a vault is a user's (never the shared folders' container), with valid ids. */
|
|
161
|
+
private static isPlace;
|
|
162
|
+
/** A place in a log line. Ids only, never a file's name. */
|
|
163
|
+
private static describePlace;
|
|
164
|
+
/**
|
|
165
|
+
* Everything kept as files, for the migration dry run: the public files in
|
|
166
|
+
* full and each vault as counts (files, bytes, clash renames), never its
|
|
167
|
+
* names. Only on an instance opened by {@link openForOperator}, for an
|
|
168
|
+
* operator's context.
|
|
169
|
+
*/
|
|
170
|
+
inventory(ctx: ActorContext): Promise<FileInventory>;
|
|
171
|
+
/** The dry run: the inventory, planned with the configured values. Writes nothing. */
|
|
172
|
+
migrationDryRun(ctx: ActorContext): Promise<FileMigrationReport>;
|
|
173
|
+
/** An asset record as a file record: the same fields, plus place, owner and system-category. */
|
|
174
|
+
static toFileRecord(asset: AssetRecord, place: FilePlace, owner: string | null, systemCategory: string | null): FileRecord;
|
|
175
|
+
private configManager;
|
|
176
|
+
/** The public place's category today: the default entry of `ngdpbase.system-category`. */
|
|
177
|
+
private defaultCategory;
|
|
178
|
+
/** The category that declares this vault, or null when none does. */
|
|
179
|
+
private vaultCategory;
|
|
180
|
+
private suffixDigits;
|
|
181
|
+
}
|
|
182
|
+
export default FileManager;
|