-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,118 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: Prettier | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
prettier: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 18 | ||
|
||
- name: Run npm ci | ||
run: npm ci | ||
|
||
- name: Run Prettier | ||
run: npx prettier --write . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
npx lint-staged |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 The Sailscasts Company | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Sails Stash | ||
|
||
With Sails Stash, you can easily implement efficient caching for your Sails applications, enhancing performance and scalability without the need for complex setup. | ||
|
||
Sails Stash integrates seamlessly with your Sails project, providing a straightforward way to cache data using Redis. By leveraging Redis as a caching layer, you can optimize the retrieval of frequently accessed data, reducing database load and improving overall application performance. | ||
|
||
## Features | ||
|
||
- Seamless integration with Sails projects | ||
- Efficient caching using Redis | ||
- Improved performance and scalability | ||
- Simple setup and usage | ||
|
||
## Installation | ||
|
||
You can install Sails Stash via npm: | ||
|
||
```sh | ||
npm i sails-stash | ||
``` | ||
|
||
## Using Redis as store | ||
|
||
To use Redis as a cache store, install the `sails-redis` adapter | ||
|
||
```sh | ||
npm i sails-redis | ||
``` | ||
|
||
### Setup the datastore | ||
|
||
```js | ||
// config/datastores.js | ||
... | ||
cache: { | ||
adapter: 'sails-redis' | ||
url: '<REDIS_URL>' | ||
} | ||
``` | ||
|
||
## Usage | ||
|
||
You can now cache values in your Sails actions. | ||
|
||
```js | ||
await sails.cache.fetch( | ||
'posts', | ||
async function () { | ||
return await Post.find() | ||
}, | ||
6000, | ||
) | ||
``` | ||
|
||
Check out the [documentation](https://docs.sailscasts.com/stash) for more ways to setup and use Sails Stash. | ||
|
||
## Contributing | ||
|
||
If you're interested in contributing to Sails Content, please read our [contributing guide](https://github.com/sailscastshq/sails-stash/blob/develop/.github/CONTRIBUTING.md). | ||
|
||
## Sponsors | ||
|
||
If you'd like to become a sponsor, check out [DominusKelvin](https://github.com/sponsors/DominusKelvin) sponsor page and tiers. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
const RedisStore = require('./lib/stores/redis-store') | ||
|
||
module.exports = function defineSailsCacheHook(sails) { | ||
return { | ||
defaults: { | ||
stash: { | ||
store: process.env.CACHE_STORE || 'redis', | ||
stores: { | ||
redis: { | ||
store: 'redis', | ||
datastore: 'cache', | ||
}, | ||
memcached: { | ||
store: 'memcached', | ||
datastore: 'cache', | ||
}, | ||
}, | ||
}, | ||
}, | ||
initialize: async function () { | ||
function getCacheStore(store) { | ||
switch (sails.config.stash.stores[store].store) { | ||
case 'redis': | ||
return new RedisStore(sails) | ||
default: | ||
throw new Error('Invalid cache store provided') | ||
} | ||
} | ||
|
||
let cacheStore = getCacheStore( | ||
sails.config.stash.stores[sails.config.stash.store].store, | ||
) | ||
|
||
sails.cache = { | ||
get: cacheStore.get.bind(cacheStore), | ||
set: cacheStore.set.bind(cacheStore), | ||
has: cacheStore.has.bind(cacheStore), | ||
delete: cacheStore.delete.bind(cacheStore), | ||
fetch: cacheStore.fetch.bind(cacheStore), | ||
add: cacheStore.add.bind(cacheStore), | ||
pull: cacheStore.pull.bind(cacheStore), | ||
forever: cacheStore.forever.bind(cacheStore), | ||
destroy: cacheStore.destroy.bind(cacheStore), | ||
store: function (store) { | ||
return getCacheStore(store) | ||
}, | ||
} | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
function CacheStore(sails) { | ||
if (new.target === CacheStore) { | ||
throw new Error( | ||
'CacheStore is an abstract class and cannot be instantiated directly', | ||
) | ||
} | ||
|
||
this.sails = sails | ||
this.datastore = sails.config.stash.stores[sails.config.stash.store].datastore | ||
this.store = null | ||
} | ||
/** | ||
* Retrieves the cache store instance. | ||
* @returns {Promise<any>} A promise that resolves to the cache store instance. | ||
*/ | ||
CacheStore.prototype.getStore = async function () { | ||
if (!this.store) { | ||
this.store = await this.sails.getDatastore(this.datastore) | ||
} | ||
return this.store | ||
} | ||
|
||
module.exports = CacheStore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
const util = require('util') | ||
const CacheStore = require('./cache-store') | ||
|
||
function RedisStore(sails) { | ||
CacheStore.call(this, sails) | ||
} | ||
|
||
RedisStore.prototype = Object.create(CacheStore.prototype) | ||
RedisStore.prototype.constructor = RedisStore | ||
RedisStore.prototype.getStore = CacheStore.prototype.getStore | ||
|
||
/** | ||
* Retrieves the value associated with the specified key from the cache store. | ||
* @param {string} key - The key to retrieve the value for. | ||
* @param {any | Function} [defaultValueOrCallback] - Optional. Default value or callback function. | ||
* @returns {Promise<any>} Promise resolving to the value associated with the key or default value. | ||
*/ | ||
RedisStore.prototype.get = async function (key, defaultValueOrCallback) { | ||
const store = await this.getStore() | ||
const value = await store.leaseConnection(async function (db) { | ||
const cacheHit = await util.promisify(db.get).bind(db)(key) | ||
|
||
if (cacheHit === null) { | ||
if (typeof defaultValueOrCallback === 'function') { | ||
const defaultValue = await defaultValueOrCallback() | ||
return defaultValue | ||
} else { | ||
return defaultValueOrCallback | ||
} | ||
} else { | ||
return JSON.parse(cacheHit) | ||
} | ||
}) | ||
return value | ||
} | ||
/** | ||
* Sets the value associated with the specified key in the cache store. | ||
* @param {string} key - The key for the value. | ||
* @param {any} value - The value to store. | ||
* @param {number} [ttlInSeconds] - Optional. Time to live for the key-value pair in seconds. | ||
* @returns {Promise<void>} Promise indicating completion of the set operation. | ||
*/ | ||
RedisStore.prototype.set = async function (key, value, ttlInSeconds) { | ||
const store = await this.getStore() | ||
await store.leaseConnection(async function (db) { | ||
if (ttlInSeconds) { | ||
await util.promisify(db.setex).bind(db)( | ||
key, | ||
ttlInSeconds, | ||
JSON.stringify(value), | ||
) | ||
} else { | ||
await util.promisify(db.set).bind(db)(key, JSON.stringify(value)) | ||
} | ||
}) | ||
} | ||
|
||
/** | ||
* Checks if the cache store contains the specified key. | ||
* @param {string} key - The key to check. | ||
* @returns {Promise<boolean>} Promise resolving to true if the key exists, false otherwise. | ||
*/ | ||
RedisStore.prototype.has = async function (key) { | ||
const cacheHit = await this.get(key) | ||
if (cacheHit) return true | ||
return false | ||
} | ||
|
||
/** | ||
* Deletes the key-value pair associated with the specified key from the cache store. | ||
* @param {string | string[]} key - The key to delete. | ||
* @returns {Promise<void>} Promise indicating completion of the delete operation. | ||
*/ | ||
RedisStore.prototype.delete = async function (key) { | ||
const store = await this.getStore() | ||
const deletedCount = await store.leaseConnection(async function (db) { | ||
return await util.promisify(db.del).bind(db)(key) | ||
}) | ||
return deletedCount | ||
} | ||
/** | ||
* Retrieves the value associated with the specified key from the cache store. | ||
* If the key exists in the cache, returns the corresponding value. | ||
* If the key does not exist in the cache, the provided default value or the result of the callback function will be stored in the cache and returned. | ||
* @param {string} key - The key to retrieve the value for. | ||
* @param {any | Function} [defaultValueOrCallback] - Optional. Default value or callback function to compute the default value. | ||
* @param {number} [ttlInSeconds] - Optional. Time to live for the key-value pair in seconds. | ||
* @returns {Promise<any>} A promise that resolves to the value associated with the key, or the default value if the key does not exist in the cache. | ||
*/ | ||
RedisStore.prototype.fetch = async function ( | ||
key, | ||
defaultValueOrCallback, | ||
ttlInSeconds, | ||
) { | ||
const cacheHit = await this.get(key) | ||
if (!cacheHit) { | ||
let defaultValue | ||
if (typeof defaultValueOrCallback === 'function') { | ||
defaultValue = await defaultValueOrCallback() | ||
} else { | ||
defaultValue = defaultValueOrCallback | ||
} | ||
await this.set(key, defaultValue, ttlInSeconds) | ||
return defaultValue | ||
} else { | ||
return cacheHit | ||
} | ||
} | ||
/** | ||
* Adds a key-value pair to the cache store if the key does not already exist. | ||
* If the key already exists, the value is not updated. | ||
* @param {string} key - The key for the value. | ||
* @param {any} value - The value to store. | ||
* @param {number} [ttlInSeconds] - Optional. Time to live for the key-value pair in seconds. | ||
* @returns {Promise<boolean>} A promise that resolves to true if the key was added successfully, false if the key already exists. | ||
*/ | ||
RedisStore.prototype.add = async function (key, value, ttlInSeconds) { | ||
const cacheHit = await this.get(key) | ||
if (!cacheHit) { | ||
await this.set(key, value, ttlInSeconds) | ||
return true | ||
} else { | ||
return false | ||
} | ||
} | ||
/** | ||
* Retrieves and removes the value associated with the specified key from the cache store. | ||
* If the key exists in the cache, returns the corresponding value and removes the key-value pair. | ||
* If the key does not exist, returns null. | ||
* @param {string} key - The key to retrieve and remove the value for. | ||
* @returns {Promise<any>} A promise that resolves to the value associated with the key, or null if the key does not exist in the cache. | ||
*/ | ||
RedisStore.prototype.pull = async function (key) { | ||
const value = await this.get(key) | ||
await this.delete(key) | ||
return value | ||
} | ||
/** | ||
* Stores the specified key-value pair in the cache store indefinitely. | ||
* The key-value pair will not have an expiry time and will remain in the cache until explicitly removed. | ||
* @param {string} key - The key for the value. | ||
* @param {any} value - The value to store. | ||
* @returns {Promise<void>} A promise indicating completion of the operation. | ||
*/ | ||
RedisStore.prototype.forever = async function (key, value) { | ||
await this.set(key, value) | ||
} | ||
|
||
/** | ||
* Destroys the cache store, removing all stored key-value pairs. | ||
* @returns {Promise<void>} A promise indicating completion of the operation. | ||
*/ | ||
RedisStore.prototype.destroy = async function () { | ||
const store = await this.getStore() | ||
await store.leaseConnection(async function (db) { | ||
return await util.promisify(db.flushall).bind(db)('ASYNC') | ||
}) | ||
} | ||
|
||
module.exports = RedisStore |
Oops, something went wrong.