Really need help with Custom Storage Adapter

Can someone help me with instructions to set up a custom storage adapter in Ghost?

I am running a custom server created with ghost-cli with the latest version. I followed the instructions to a T from the ghost docs, but they are outdated and cause errors when booting the ghost server after updating the configuration. Furthermore, all of the custom adapters, including the recommended S3 one that can be installed via npm, are not working. I really think the best approach here is to just find a working solution and build from there, but even the most basic implementations fail satisfy javascript importing requirements.

Here is my storage adapter code:

const BaseAdapter = require("ghost-storage-base");
const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');
const path = require('path');
const mime = require('mime-types');

class S3CustomAdapter extends BaseAdapter {
  constructor(config) {
    super();
    this.s3 = new AWS.S3({
      accessKeyId: config.key,
      secretAccessKey: config.secret,
      region: config.region,
    });
    this.bucket = config.bucket;
  }

  save(image) {
    return new Promise((resolve, reject) => {
      const fileName = `${uuidv4()}${path.extname(image.name)}`;
      const params = {
        Bucket: this.bucket,
        Key: fileName,
        Body: image.stream,
        ContentType: mime.lookup(image.name),
        ACL: 'public-read',
      };

      this.s3.upload(params, (err, data) => {
        if (err) {
          return reject(err);
        }
        resolve(data.Location);
      });
    });
  }

  exists(fileName) {
    const params = {
      Bucket: this.bucket,
      Key: fileName,
    };

    return this.s3
      .headObject(params)
      .promise()
      .then(() => true)
      .catch((err) => {
        if (err.code === 'NotFound') {
          return false;
        }
        throw err;
      });
  }

  serve() {
    return (req, res, next) => {
      const fileName = req.path.replace(/^\//, '');
      const params = {
        Bucket: this.bucket,
        Key: fileName,
      };

      this.s3.getObject(params)
        .createReadStream()
        .on('error', (err) => {
          if (err.code === 'NoSuchKey') {
            return res.status(404).end();
          }
          next(err);
        })
        .pipe(res);
    };
  }

  delete(fileName) {
    const params = {
      Bucket: this.bucket,
      Key: fileName,
    };

    return this.s3
      .deleteObject(params)
      .promise()
      .then(() => true)
      .catch((err) => {
        if (err.code === 'NoSuchKey') {
          return false;
        }
        throw err;
      });
  }

  read(fileName) {
    const params = {
      Bucket: this.bucket,
      Key: fileName,
    };

    return this.s3
      .getObject(params)
      .promise()
      .then((data) => data.Body)
      .catch((err) => {
        if (err.code === 'NoSuchKey') {
          return null;
        }
        throw err;
      });
  }
}

module.exports = S3CustomAdapter;

If this is no longer supported I think it would be greatly appreciated it if the community were made aware.

Thanks!

You are right that both the adapters here appear outdated:

Repairing this may involve some coding skills to investigate what needs to be updated about these older adapters.

It’s likely that Ghost Pro is using a storage adapter in production so general support for storage adapters should still be working.

1 Like

Is there some code I can reference somewhere? Have you implemented it recently?

Be it that it may be supported in production, I can’t find any working examples or get it to work by myself. Can you help me figure out what I need to do?

Have you found this reference?

This storage adapter was updated last year:

@Kevin Both the recommended S3 adapters are rather outdated. It could be good to refresh the docs with some more up to date alternatives.

Looking at the Github “Network” of ghost-storage-adapter-s3, there are several forks that have been updated more recently.

Perhaps if the Ghost project hosted this project some of these people would collaborate in one place. Right now it looks like a lot of people are forking and modifying the project, but there isn’t one central place that has most of the fixes, which is unfortunate.