diff --git a/src/backend/src/filesystem/definitions/capabilities.js b/src/backend/src/filesystem/definitions/capabilities.js index 95e0424d0..ef9b289d0 100644 --- a/src/backend/src/filesystem/definitions/capabilities.js +++ b/src/backend/src/filesystem/definitions/capabilities.js @@ -23,6 +23,7 @@ const capabilityNames = [ 'uuid', 'operation-trace', 'readdir-uuid-mode', + 'update-thumbnail', // Standard Capabilities 'read', diff --git a/src/backend/src/modules/puterfs/lib/PuterFSProvider.js b/src/backend/src/modules/puterfs/lib/PuterFSProvider.js index 2e3f7c553..3da11f3e5 100644 --- a/src/backend/src/modules/puterfs/lib/PuterFSProvider.js +++ b/src/backend/src/modules/puterfs/lib/PuterFSProvider.js @@ -64,6 +64,7 @@ class PuterFSProvider extends putility.AdvancedBase { get_capabilities() { return new Set([ fsCapabilities.THUMBNAIL, + fsCapabilities.UPDATE_THUMBNAIL, fsCapabilities.UUID, fsCapabilities.OPERATION_TRACE, fsCapabilities.READDIR_UUID_MODE, @@ -564,6 +565,40 @@ class PuterFSProvider extends putility.AdvancedBase { await lock_handle.unlock(); } } + + async update_thumbnail({ context, node, thumbnail }) { + const { + actor: inputActor, + } = context.values; + const actor = inputActor ?? Context.get('actor'); + + context = context ?? Context.get(); + const services = context.get('services'); + + const svc_fsEntry = services.get('fsEntryService'); + const svc_event = services.get('event'); + + const svc_acl = services.get('acl'); + if ( ! await svc_acl.check(actor, node, 'write') ) { + throw await svc_acl.get_safe_acl_error(actor, node, 'write'); + } + + const uid = await node.get('uid'); + + const entryOp = await svc_fsEntry.update(uid, { + thumbnail + }); + + (async () => { + await entryOp.awaitDone(); + svc_event.emit('fs.write.file', { + node, + context, + }); + })(); + + return node; + } /** * Write a new file to the filesystem. Throws an error if the destination diff --git a/src/backend/src/routers/filesystem_api/update.js b/src/backend/src/routers/filesystem_api/update.js new file mode 100644 index 000000000..7eac77de5 --- /dev/null +++ b/src/backend/src/routers/filesystem_api/update.js @@ -0,0 +1,46 @@ +const APIError = require("../../api/APIError"); +const eggspress = require("../../api/eggspress"); +const FSNodeParam = require("../../api/filesystem/FSNodeParam"); +const StringParam = require("../../api/filesystem/StringParam"); +const { is_valid_url } = require("../../helpers"); +const { PuterFSProvider } = require("../../modules/puterfs/lib/PuterFSProvider"); +const { Context } = require("../../util/context"); + +module.exports = eggspress('/update-fsentry-thumbnail', { + subdomain: 'api', + verified: true, + auth2: true, + fs: true, + json: true, + allowedMethods: ['POST'], + parameters: { + fsNode: new FSNodeParam('path'), + thumbnail: new StringParam('thumbnail'), + }, +}, async (req, res, next) => { + if ( ! is_valid_url(req.values.thumbnail) ) { + throw new APIError.create('field_invalid', null, { + key: 'thumbnail', + expected: 'a valid URL', + got: typeof req.values.thumbnail, + }); + } + + if ( ! await req.values.fsNode.exists() ) { + throw new APIError.create('subject_does_not_exist'); + } + + const svc = Context.get('services'); + + const svc_mountpoint = svc.get('mountpoint'); + const provider = + await svc_mountpoint.get_provider(req.values.fsNode.selector); + + provider.update_thumbnail({ + context: Context.get(), + node: req.values.fsNode, + thumbnail: req.body.thumbnail, + }); + + res.json({}); +}); diff --git a/src/backend/src/services/FilesystemAPIService.js b/src/backend/src/services/FilesystemAPIService.js index 9e097b7ba..56cb3644b 100644 --- a/src/backend/src/services/FilesystemAPIService.js +++ b/src/backend/src/services/FilesystemAPIService.js @@ -62,6 +62,9 @@ class FilesystemAPIService extends BaseService { app.use(require('../routers/filesystem_api/search')) + // temporary or alpha + app.use(require('../routers/filesystem_api/update')) + // v1 app.use(require('../routers/writeFile')) app.use(require('../routers/file'))