|
18 | 18 | */ |
19 | 19 | // TODO: database access can be a service |
20 | 20 | const { RESOURCE_STATUS_PENDING_CREATE } = require('../modules/puterfs/ResourceService.js'); |
21 | | -const { NodePathSelector, NodeUIDSelector, NodeInternalIDSelector, NodeSelector } = require('./node/selectors.js'); |
| 21 | +const { NodePathSelector, NodeUIDSelector, NodeInternalIDSelector, NodeSelector, NodeChildSelector } = require('./node/selectors.js'); |
22 | 22 | const FSNodeContext = require('./FSNodeContext.js'); |
23 | 23 | const { Context } = require('../util/context.js'); |
24 | 24 | const APIError = require('../api/APIError.js'); |
25 | 25 | const { PermissionUtil, PermissionRewriter, PermissionImplicator, PermissionExploder } = require('../services/auth/permissionUtils.mjs'); |
26 | 26 | const { DB_WRITE } = require('../services/database/consts'); |
27 | 27 | const { UserActorType } = require('../services/auth/Actor'); |
28 | | -const { get_user } = require('../helpers'); |
| 28 | +const { get_user, is_valid_uuid4 } = require('../helpers'); |
29 | 29 | const BaseService = require('../services/BaseService'); |
30 | 30 | const { MANAGE_PERM_PREFIX } = require('../services/auth/permissionConts.mjs'); |
31 | 31 | const { quot } = require('@heyputer/putility/src/libs/string.js'); |
@@ -334,6 +334,68 @@ class FilesystemService extends BaseService { |
334 | 334 | return fsNode; |
335 | 335 | } |
336 | 336 |
|
| 337 | + // #region Simplified API |
| 338 | + async read (selector, options = {}) { |
| 339 | + const node = this.#coerceToNode(selector); |
| 340 | + const ll_read = new LLRead(); |
| 341 | + const stream = await ll_read.run({ |
| 342 | + ...options, |
| 343 | + fsNode: node, |
| 344 | + }); |
| 345 | + return stream; |
| 346 | + } |
| 347 | + |
| 348 | + #coerceToNode (stringOrSelectorOrNode, { creatable } = {}) { |
| 349 | + if ( stringOrSelectorOrNode instanceof FSNodeContext ) { |
| 350 | + if ( creatable ) { |
| 351 | + throw new Error('cannot specify a file/directory to create with an FSNodeContext'); |
| 352 | + } |
| 353 | + return stringOrSelectorOrNode; |
| 354 | + } |
| 355 | + |
| 356 | + if ( stringOrSelectorOrNode instanceof NodeSelector ) { |
| 357 | + if ( creatable && (stringOrSelectorOrNode instanceof NodeUIDSelector) ) { |
| 358 | + throw new Error('cannot specify a file/directory to create by UUID'); |
| 359 | + } |
| 360 | + return this.node(stringOrSelectorOrNode); |
| 361 | + } |
| 362 | + |
| 363 | + if ( typeof stringOrSelectorOrNode !== 'string' ) { |
| 364 | + throw new Error('expected string, NodeSelector, or FSNodeContext'); |
| 365 | + } |
| 366 | + const string = stringOrSelectorOrNode; |
| 367 | + |
| 368 | + if ( string.startsWith('./') ) { |
| 369 | + throw new Error('relative paths are not supported here'); |
| 370 | + } |
| 371 | + |
| 372 | + // This will be coerced by `this.node` to a NodePathSelector |
| 373 | + if ( string.startsWith('/') ) { |
| 374 | + return this.node(string); |
| 375 | + } |
| 376 | + |
| 377 | + // UUID followed by path component |
| 378 | + if ( string.includes('/') ) { |
| 379 | + const uuidPart = string.slice(0, string.indexOf('/')); |
| 380 | + if ( ! is_valid_uuidv4(uuidPart) ) { |
| 381 | + throw new Error('expected file/directory identifier to begin with UUID or /'); |
| 382 | + } |
| 383 | + |
| 384 | + throw new Error('"UUID/then/path" form is not yet supported'); |
| 385 | + } |
| 386 | + |
| 387 | + if ( ! is_valid_uuid4(string) ) { |
| 388 | + throw new Error('string is not a valid file/directory specifier'); |
| 389 | + } |
| 390 | + |
| 391 | + if ( creatable ) { |
| 392 | + throw new Error('cannot specify a file/directory to create by UID'); |
| 393 | + } |
| 394 | + |
| 395 | + return this.node(new NodeUIDSelector(string)); |
| 396 | + } |
| 397 | + // #endregion |
| 398 | + |
337 | 399 | /** |
338 | 400 | * get_entry() returns a filesystem entry using |
339 | 401 | * path, uid, or id associated with a filesystem |
|
0 commit comments