Quick hack: Redirect to asset by URL - Endpoint extension for directus.io

October 21, 2021 ยท View on GitHub

// Quick hack: Redirect to asset by URL - Endpoint extension for directus.io // ========================================================================= // // 2021 - ToJans - Public Domain // // *** Don't hold me liable if it breaks anything! Quick hack, might expose all your data! // // You can use this endpoint extension to access assets via the folder and file structure // you used in the file module, so you don't need to reference assets by guid anymore. // // Usage: // ------ // // Browse to "/filestorage/[folder/subfolder/filename.ext]" according to the [folder]s // and [filename_download] you used in the file module. It will redirect to "/assets/some-huge-guid-xxxxx" // // Installation: // ------------- // // Save the contents of this file in "$DIRECTUS_ROOT/extensions/endpoints/filestorage/index.js" and (re)start directus. // // Note: // ----- // This is just a quick hack to see how extensible directus.io is, and I have to say I'm not disappointed. // // However, I only discovered directus.io a couple of hours ago, so I'm by no means an expert on this stuff. // So authorization & async might need to be validated TBH.

module.exports = function registerEndpoint(router, context) { const { services, exceptions, } = context;

const {
    FilesService,
    FoldersService,
} = services;
const {
    ServiceUnavailableException
} = exceptions;


router.get('*', (req, res, next) => {

    const foldersService = new FoldersService({
        schema: req.schema,
        accountability: req.accountability
    });

    const paths = req.params[0].split("/").filter(x => !!x);

    const file = paths.pop();

    let parentKey = null;
    let currentPath = "/";
    let hasError = false;

    foldersService.readByQuery({
            filter: {
                name: {
                    "_in": paths
                }
            },
            fields: ["id", "name", "parent"]
        }).then(folders => {

            while (paths.length && !hasError) {
                head = paths.shift();
                currentPath += head;
                let headFolder = folders.find(x => x.parent == parentKey && x.name == head);
                if (!headFolder) {
                    res.status(404).send({
                        status: 404,
                        error: "path not found: " + currentPath
                    });
                    hasError = true;
                    break;
                }
                parentKey = headFolder.id;
            }

            // TODO: I should handle this properly
            if (!hasError) {
                const filesService = new FilesService({
                    schema: req.schema,
                    accountability: req.accountability
                });

                return filesService.readByQuery({
                    filter: {
                        "filename_download": {
                            "_eq": file
                        },
                        // TODO: 
                        // This does not seem to work, as it fails on null values, so for now I just check it manually
                        // This might cause perf issues if we have a lot of files with the same name....
                        // "folder": {
                        //      "_eq": parentKey 
                        // }
                    },
                    fields: ["id", "type", "folder"]
                });
            }
        })
        .then((results) => {
            // TODO: workaround I mentioned a few lines earlier
            results = results.filter(x => x.folder === parentKey);
            if (!results.length) {
                res.status(404).send({
                    status: 404,
                    error: "file not found"
                });
            } else {
                res.redirect("/assets/" + results[0].id);
            }
        })
        .catch((error) => {
            return next(new ServiceUnavailableException(error.message));
        });
});

};