Skip to content

Commit 77b5d3f

Browse files
authored
Merge pull request #1732 from pkarw/develop
Develop
2 parents f8461be + 1e4239c commit 77b5d3f

File tree

12 files changed

+147
-8
lines changed

12 files changed

+147
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ You can find some tutorials and explainations on our [YouTube channel](https://w
140140
* [Feature list](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/Feature%20list.md)
141141
* [Vue Storefront modules](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/api-modules/about-modules.md)
142142
* [TypeScript Action Plan](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/TypeScript%20Action%20Plan.md)
143+
* [GraphQL Action Plan](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/GraphQL%20Action%20Plan.md)
144+
* [SSR Cache](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/SSR%20Cache.md)
143145

144146
### Vue Storefront core and themes
145147
* [Working with themes](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/themes/Working%20with%20themes.md)

config/default.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"api": "api",
77
"useOutputCacheTagging": false,
88
"useOutputCache": false,
9-
"outputCacheDefaultTtl": 86400
9+
"outputCacheDefaultTtl": 86400,
10+
"availableCacheTags": ["product", "category", "home", "checkout", "page-not-found", "compare", "my-account", "P", "C"],
11+
"invalidateCacheKey": "aeSu7aip"
1012
},
1113
"redis": {
1214
"host": "localhost",

core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"@vue-storefront/store": "^1.3.0",
1212
"app-root-path": "^2.0.1",
1313
"bodybuilder": "2.2.13",
14+
"commander": "^2.18.0",
1415
"config": "^1.30.0",
1516
"d3-dsv": "^1.0.8",
1617
"detect-installed": "^2.0.4",

core/pages/Checkout.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,5 +358,11 @@ export default {
358358
title: this.$route.meta.title || i18n.t('Checkout'),
359359
meta: this.$route.meta.description ? [{ vmid: 'description', description: this.$route.meta.description }] : []
360360
}
361+
},
362+
asyncData ({ store, route }) { // this is for SSR purposes to prefetch data
363+
return new Promise((resolve, reject) => {
364+
store.state.requestContext.outputCacheTags.add(`checkout`)
365+
resolve()
366+
})
361367
}
362368
}

core/pages/Compare.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,11 @@ export default {
4141
title: this.$route.meta.title || this.$props.title || i18n.t('Compare Products'),
4242
meta: this.$route.meta.description ? [{ vmid: 'description', description: this.$route.meta.description }] : []
4343
}
44+
},
45+
asyncData ({ store, route }) { // this is for SSR purposes to prefetch data
46+
return new Promise((resolve, reject) => {
47+
store.state.requestContext.outputCacheTags.add(`compare`)
48+
resolve()
49+
})
4450
}
4551
}

core/pages/MyAccount.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,11 @@ export default {
6666
title: this.$route.meta.title || i18n.t('My Account'),
6767
meta: this.$route.meta.description ? [{ vmid: 'description', description: this.$route.meta.description }] : []
6868
}
69+
},
70+
asyncData ({ store, route }) { // this is for SSR purposes to prefetch data
71+
return new Promise((resolve, reject) => {
72+
store.state.requestContext.outputCacheTags.add(`my-account`)
73+
resolve()
74+
})
6975
}
7076
}

core/pages/PageNotFound.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default {
1111
asyncData ({ store, route }) { // this is for SSR purposes to prefetch data
1212
return new Promise((resolve, reject) => {
1313
console.log('Entering asyncData for PageNotFound ' + new Date())
14+
store.state.requestContext.outputCacheTags.add(`page-not-found`)
1415
let ourBestsellersQuery = prepareQuery({ queryConfig: 'bestSellers' })
1516
store.dispatch('category/list', {}).then(categories => {
1617
store.dispatch('product/list', {

core/scripts/cache.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const program = require('commander')
2+
const config = require('config')
3+
const TagCache = require('redis-tag-cache').default
4+
5+
let cache
6+
if (config.server.useOutputCache) {
7+
cache = new TagCache({
8+
redis: config.redis,
9+
defaultTimeout: config.server.outputCacheDefaultTtl // Expire records after a day (even if they weren't invalidated)
10+
})
11+
console.log('Redis cache set', config.redis)
12+
} else {
13+
console.error('Output cache is disabled in the config')
14+
}
15+
16+
program
17+
.command('clear')
18+
.option('-t|--tag <tag>', 'tag name, available tags: ' + config.server.availableCacheTags.join(', '), '*')
19+
.action((cmd) => { // TODO: add parallel processing
20+
if (!cmd.tag) {
21+
console.error('error: tag must be specified')
22+
process.exit(1)
23+
} else {
24+
console.log(`Clear cache request for [${cmd.tag}]`)
25+
let tags = []
26+
if (cmd.tag === '*') {
27+
tags = config.server.availableCacheTags
28+
} else {
29+
tags = cmd.tag.split(',')
30+
}
31+
const subPromises = []
32+
tags.forEach(tag => {
33+
if (config.server.availableCacheTags.indexOf(tag) >= 0 || config.server.availableCacheTags.find(t => {
34+
return tag.indexOf(t) === 0
35+
})) {
36+
subPromises.push(cache.invalidate(tag).then(() => {
37+
console.log(`Tags invalidated successfully for [${tag}]`)
38+
}))
39+
} else {
40+
console.error(`Invalid tag name ${tag}`)
41+
}
42+
})
43+
Promise.all(subPromises).then(r => {
44+
console.log(`All tags invalidated successfully [${cmd.tag}]`)
45+
process.exit(0)
46+
}).catch(error => {
47+
console.error(error)
48+
})
49+
}
50+
})
51+
52+
program.parse(process.argv)

core/scripts/server.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,40 @@ app.use('/service-worker.js', serve('dist/service-worker.js', {
6363
}))
6464

6565
app.get('/invalidate', (req, res) => {
66-
if (req.query.tag) { // clear cache pages for specific query tag
66+
if (req.query.tag && req.query.key) { // clear cache pages for specific query tag
67+
if (req.query.key !== config.server.invalidateCacheKey) {
68+
console.error('Invalid cache invalidation key')
69+
res.end('Invalid cache invalidation key')
70+
return
71+
}
6772
console.log(`Clear cache request for [${req.query.tag}]`)
6873
let tags = []
6974
if (req.query.tag === '*') {
70-
tags = ['product', 'category', 'home']
75+
tags = config.server.availableCacheTags
7176
} else {
7277
tags = req.query.tag.split(',')
7378
}
7479
const subPromises = []
7580
tags.forEach(tag => {
76-
subPromises.push(cache.invalidate(tag).then(() => {
77-
console.log(`Tags invalidated successfully for [${tag}]`)
78-
}))
81+
if (config.server.availableCacheTags.indexOf(tag) >= 0 || config.server.availableCacheTags.find(t => {
82+
return tag.indexOf(t) === 0
83+
})) {
84+
subPromises.push(cache.invalidate(tag).then(() => {
85+
console.log(`Tags invalidated successfully for [${tag}]`)
86+
}))
87+
} else {
88+
console.error(`Invalid tag name ${tag}`)
89+
}
7990
})
8091
Promise.all(subPromises).then(r => {
8192
res.end(`Tags invalidated successfully [${req.query.tag}]`)
8293
}).catch(error => {
8394
res.end(error)
8495
console.error(error)
8596
})
97+
} else {
98+
res.end('GET Parameters key and tag are required')
99+
console.error('Invalid parameters for Clear cache request')
86100
}
87101
})
88102

doc/SSR Cache.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,53 @@ Example call to clear all product, category and home pages:
4848
**WARNING:**
4949
We strongly recommend You to NOT USE Output cache in the development mode. By using it You won't be able to refresh the UI changes after modyfing the Vue components etc.
5050

51-
**Cache invalidation:** Recent version of [mage2vuestorefront](https://github.com/DivanteLtd/mage2vuestorefront) do support output cache invalidation. Output cache is being tagged with the product and categories id (products and categories used on specific page). Mage2vuestorefront can invalidate cache of product and category pages if You set the following ENV variables:
51+
## CLI cache clear
52+
53+
You can manualy clear Redis cache for specific tags by running the following command:
5254

5355
```bash
54-
export VS_INVALIDATE_CACHE_URL=http://localhost:3000/invalidate?tag=
56+
npm run cache clear
57+
npm run cache clear -- --tag=product,category
58+
npm run cache clear -- --tag=P198
59+
npm run cache clear -- --tag=*
60+
```
61+
62+
Available tag keys are set in the `config.server.availableCacheTags` (by default: `"product", "category", "home", "checkout", "page-not-found", "compare", "my-account", "P", "C"`)
63+
64+
**Dynamic cache invalidation:** Recent version of [mage2vuestorefront](https://github.com/DivanteLtd/mage2vuestorefront) do support output cache invalidation. Output cache is being tagged with the product and categories id (products and categories used on specific page). Mage2vuestorefront can invalidate cache of product and category pages if You set the following ENV variables:
65+
66+
```bash
67+
export VS_INVALIDATE_CACHE_URL=http://localhost:3000/invalidate?key=SECRETKEY&tag=
5568
export VS_INVALIDATE_CACHE=1
5669
```
70+
71+
**SECURITY NOTE:** Please note that `key=SECRETKEY` should be equal to `vue-storefront/config/local.json` value of `server.invalidateCacheKey`
72+
73+
## Adding new types / cache tags
74+
75+
If You're adding new type of page (`core/pages`) and `config.server.useOutputCache=true` - You should also extend the `config.server.availableCacheTags` of new general purpose tag that will be connected with the URLs connected with this new page.
76+
77+
After doing so, please add the `asyncData` method to Your page code assigning the right tag (please take a look at `core/pages/Home.js` for instance):
78+
79+
```js
80+
asyncData ({ store, route }) { // this is for SSR purposes to prefetch data
81+
return new Promise((resolve, reject) => {
82+
store.state.requestContext.outputCacheTags.add(`home`)
83+
console.log('Entering asyncData for Home root ' + new Date())
84+
EventBus.$emitFilter('home-after-load', { store: store, route: route }).then((results) => {
85+
return resolve()
86+
}).catch((err) => {
87+
console.error(err)
88+
reject(err)
89+
})
90+
})
91+
},
92+
```
93+
94+
This line:
95+
96+
```js
97+
store.state.requestContext.outputCacheTags.add(`home`)
98+
```
99+
100+
... is in charge of assigning the specific tag with current HTTP request output.

0 commit comments

Comments
 (0)