forked from jabzzy/keystone-express-sitemap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
197 lines (172 loc) · 6.1 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
var sitemap = require('express-sitemap'),
dateFormat = require('date-format-lite'),
_ = require('underscore'),
async = require('async');
var KeystoneSitemap = function(keystone, req, res) {
// store routes for express-sitemap function method map option
var map = {};
// store routes for express-sitemap function route declaration option
var route = {};
var dynamicRoutes = [];
var dateFormatString = 'YYYY-MM-DD';
/**
* Check existing keystone object lists to see if given string is a name of existing list
* Called by create function
* @param string {string} string to check against existing list names
*/
var findKeystoneList = function(string) {
//remove dynamic parameter marker from string
string = string.replace(':', '').toLowerCase();
var lists = Object.keys(keystone.lists).map(function(l) {
return l.toLowerCase();
});
var listIndex = lists.indexOf(string);
if (listIndex >= 0) {
return keystone.list(Object.keys(keystone.lists)[listIndex]);
}
else {
return null;
}
};
/**
* Loop through declared routes to determine which are static and which are tied to dynamic database values (routes with :_id or other similar parameters)
* Called by create function
*/
var parseRoutes = function() {
// get keystone route object list
// keystone projects using express 4.x.x store routes in keystone.app._router.stack
// keystone projects using express 3.x.x store routes in keystone.app.routes.get
var routes = keystone.app._router.stack || keystone.app.routes.get;
if (routes && routes.length > 0) {
routes.forEach(function(v, i) {
// express 4.x.x route objects have path property
// express 3.x.x route objects have route.path property
var path = v.path ? v.path : (v.route ? v.route.path : null);
// remove any kestyone admin paths (/keystone/)
if (path != null && path.match(/keystone\*{0,1}$|keystone\/|\/\*$|sitemap\.xml/) == null) {
var ignored = false;
//check routes against the ignored routes, if applicable
if (options && options.ignore && Object.prototype.toString.call(options.ignore) === '[object Array]') {
for (var ig in options.ignore) {
if (path === options.ignore[ig] || path.match(options.ignore[ig]) !== null) {
ignored = true;
break;
}
};
}
if (ignored) {
return false;
}
// check for dynamic routes (with parameters identified by :[parameter name])
if (path.indexOf(':') > 0) {
dynamicRoutes.push(path);
}
// route is a static route, add to routes that will be parsed into sitemap
else {
map[path] = ['get'];
route[path] = {};
}
}
});
}
// If there are dynamic routes, move to asynchronous function to query database for all routes that would follow that pattern. If not, finish up and generate sitemap.
if (dynamicRoutes.length > 0) {
asyncAddListRoutes();
}
else {
createXmlFile();
}
};
/**
* Applied to each item in dynamicRoutes array
* @param path {string} express route in the format /path/:param
* @param callback {function} callback function passed from asyncAddListRoutes
*/
var addListRoutes = function(path, callback) {
var paths = path.split('/');
var list = null;
var hasCustomRoute = false;
var dynamicParam = _.find(paths, function(p) {
return p.indexOf(':') >= 0;
});
// Loop through route paths to find keystone list name reference
// Reference will either be fixed in route, followed by dynamic parameter (/listname/:id), or will be set as the dynamic parameter for a custom route (/custom/:listname)
for (var p in paths) {
list = findKeystoneList(paths[p]);
if (list != null) {
//route is custom if the list name is the dynamic parameter in the URL
hasCustomRoute = paths[p] === dynamicParam;
break;
}
}
if (list != null) {
//check what property of the list object is being used as the URL identifier, based on keystone model settings
var idParam = list.options.autokey && list.options.autokey.path ? list.options.autokey.path : '_id';
list.model.find().exec(function(err, results) {
if (results && results.length > 0) {
results.forEach(function(v,i) {
var include = true;
if (options && options.filters && options.filters[list.key]) {
include = options.filters[list.key](v);
}
if (include) {
//only define lastModDate if the model has a property tracking when it was last updated
var lastModDate = v.updatedAt ? v.updatedAt.format(dateFormatString) : null;
//define page url that will get user access to list item v
var pageUrl = paths.join('/').replace(dynamicParam, v[idParam]);
map[pageUrl] = ['get'];
route[pageUrl] = {
lastmod: lastModDate
};
}
});
}
callback();
});
}
else {
callback();
}
};
/**
* Initialize asynchronous function to map all possible routes that can be generated by dynamic routes tied to lists
* Async to ensure DB queries for each list are all complete before map is created
*/
var asyncAddListRoutes = function() {
async.map(dynamicRoutes, addListRoutes, function(result) {
createXmlFile();
});
};
/**
* Send back the XML sitemap once all routes have been parsed
*/
var createXmlFile = function() {
//express 3.x.x does not define req.hostname, only req.host
//express 4.x.x has separate parameters for hostname and protocol
var host = req.hostname ? req.hostname : req.host;
sitemap({
map: map,
route: route,
url: host,
http: req.protocol
}).XMLtoWeb(res);
};
/**
* Initialize parsing of declared Express routes into sitemap.xml
* @param ks {object} the keystone object created by application initialization
* @param rq {object} express request object from sitemap.xml route handler function
* @param rs {object} express response object from sitemap.xml route handler function
*/
var create = function(ks, rq, rs, opt) {
// set variables to be used by all other KeystoneSitemap functions
keystone = ks;
req = rq;
res = rs;
options = opt;
parseRoutes();
};
return {
create: create
}
};
module.exports = KeystoneSitemap();