-
Notifications
You must be signed in to change notification settings - Fork 0
Using a custom storage module
In 0.10.0 we have added a small storage validation. This validation ensures that a minimum of required functions are implemented (save, exists, serve, delete). If you install 0.10.0 and your custom storage adapter has no implementation of e.g. exists, node won't start.
Besides to that, your custom storage adapter needs to call the constructor of the Base class, see https://github.com/TryGhost/Ghost/blob/master/core/server/storage/local-file-store.js#L16.
As of Ghost 0.6.0, it is possible to easily replace the storage layer which handles images with custom code. This means you can send your images to a 3rd party image service, CDN, or database without changing Ghost core.
The storage layer is used to store images both from an upload via the admin UI or API, and also when images are included in a zip file uploaded to the importer. The methods needed and how they are used by Ghost is described in the Storage API section below.
Note: this really is a beta-level developer feature! Please do use it, but keep an eye on the release notes for each version, as there may be significant changes to how it works in a patch release of Ghost. See the Planned changes section for more info.
In order to replace the storage module, the basic requirements are:
- Create a new folder inside
/contentcalled/storage - Inside of
/storageyou need to create your new module - Your module must export a constructor function that accepts a config option
- Your module must also provide 2 required methods:
saveandserve - Depending on expected usage, your module may also need to require
existsorgetUniqueFileName - Your
config.jsfile will need to be updated to provide config for your new storage module and set it as active.
In your config.js file, you'll need to add a new storage block to whichever environment you want to change:
storage: {
active: 'my-module',
'my-module': {
key: 'abcdef'
}
},
The storage block should have 2 items:
- an
activekey, which contains the name* of your module - a key which reflects the name* of your module, containing any config your module needs
* The name of your module is whatever it is called in the filesystem as per usual with node e.g. you may have content/storage/my-module.js or content/storage/my-module/index.js.
Internally, Ghost expects there to be four methods available on the storage class: save and serve are strictly required. exists and getUniqueFileName are also used by the image importer. Please be aware that there are currently no checks to ensure that the methods are present and correct.
- Usage: Called both in uploads and the importer to store user-provided images.
-
Takes:
-
image- the image object provided by the uploader/importer, has the propertiesnameandpath. - [
targetDir] - optionally provided by the importer, to override the relative directory path for the image.
-
- Returns: A promise which resolves to the full URI of the image, either relative to the blog or absolute.
On upload, .save() is passed an image object which has both a name and path. The .save() method is expected to store the image and return a promise which resolves to the path from which the image should be requested in future.
- Usage: Called to get a function which can be used as express middleware for serving images.
- Takes: no arguments
- Returns: a function which can be used as express middleware
Ghost calls storage.serve() as part of its middleware stack, and mounts the returned function as the middleware for serving images. If your module's .save() method returns absolute URLs, .serve() can be a no-op passthrough middleware function:
serve = function() {
return function (req, res, next) {
next();
};
};
If your module needs to serve the images itself, it should use req.path to resolve the file to be served, and use res to send the image to the browser. See the express documentation for more details.
Note: .getUniqueFileName() is implemented in storage/base.js and can be optionally overridden. It is called by the image importer to determine the new path for an image, but assumes that path will be relative to the blog. More work is required to make it possible for custom storage modules that resolve absolute image URIs to work with the image importer (see Planned changes below).
- Usage: Used in the image importer to determine the unique version of the filename (e.g. my-image-2.jpg if my-image.jpg already exists).
-
Takes:
-
storean instance of the storage module -
imagean image object withnameandpathproperties
-
- Returns: A promise which resolves to a unique target filename.
-
Usage: Called by
.getUniqueFileName()in storage/base.js to determine if an image with this name exists already or not. -
Takes:
filenamethe name of the file which is being uploaded. -
Returns: A promise which resolves to
trueorfalsedepending on whether or not the given image has already been stored.
As exists is currently only used by the .getUniqueFileName() function, it only needs to be implemented if .getUniqueFileName() is not overridden. If you create your own version of .getUniqueFileName() then you may or may not need it.
Your module can depend on any npm module that Ghost already has installed. This includes useful modules like bluebird, fs-extra and request. You can also include a package.json inside of your module to add extra dependencies, but you would need to run npm install locally to your module for this to work.
The easiest way to implement a storage module is to require and extend the base storage module from Ghost. You can create a storage module without doing this, but it means implementing more of the methods yourself.
More documentation on this coming soon, in the meantime, check out these examples:
- local-file-store (Ghost's default) saves images to the local filesystem
- http-store passes image requests through to an HTTP endpoint
- s3-store saves to Amazon S3 and proxies requests to S3
- s3-store saves to Amazon S3 and works with 0.10+
- qn-store saves to Qiniu storage
- ghost-cloudinary-store saves to Cloudinary
- upyun-ghost-store saves to Upyun
- ghost-google-drive saves to Google drive
- ghost-azure-storage saves to azure storage
- ghost-imgur saves to Imgur
- google-cloud-storage
As it says at the top of this page, this is a beta developer feature and if you're using it you should expect that it may fundamentally change in any version of Ghost.
In the near future, core/server/storage/base.js will likely be released as an independent npm module which can be required, with useful helper functions to do with file handling.
At the moment, the image handler has a little bit too much logic in it, which means it only works with custom storage modules in a limited capacity. This will likely be reworked so that the importer will work reliably with custom storage modules that resolve image URIs to absolute URLs.
It is also desirable to add checks to the storage handling which check that a storage module has all of the necessary methods and provides feedback if there are problems.
Additionally, we may add some magic to Ghost to help with npm installing storage modules.