sitemap.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /**
  2. * 每天运行一次,生成站点地图
  3. */
  4. const fs = require('fs');
  5. const datefns = require('date-fns');
  6. const models = require('../../models');
  7. const categories = require('../../config/category');
  8. const { tags } = require('../../config/tag');
  9. const date = datefns.format(Date.now(), 'yyyy-MM-dd');
  10. const sitemapPath = __dirname + '/../../dist/sitemap.xml';
  11. const sitemapTempPath = __dirname + '/../../dist/sitemap.xml.temp';
  12. // 生成站点地图
  13. async function generateSitemap() {
  14. let commonXml = [
  15. '<?xml version="1.0" encoding="UTF-8"?>',
  16. // '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
  17. '<urlset',
  18. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
  19. ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd"',
  20. ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"',
  21. ' xmlns:xhtml="http://www.w3.org/1999/xhtml"',
  22. '>',
  23. ' <url>',
  24. ' <loc>https://art.pcoloring.com/en</loc>',
  25. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en" />',
  26. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh" />',
  27. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es" />',
  28. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt" />',
  29. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja" />',
  30. ` <lastmod>${date}</lastmod>`,
  31. ' <changefreq>daily</changefreq>',
  32. ' <priority>1.0</priority>',
  33. ' </url>',
  34. ' <url>',
  35. ' <loc>https://art.pcoloring.com/en/gallery</loc>',
  36. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/gallery" />',
  37. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/gallery" />',
  38. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/gallery" />',
  39. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/gallery" />',
  40. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/gallery" />',
  41. ` <lastmod>${date}</lastmod>`,
  42. ' <changefreq>weekly</changefreq>',
  43. ' <priority>0.8</priority>',
  44. ' </url>',
  45. ' <url>',
  46. ' <loc>https://art.pcoloring.com/en/videos</loc>',
  47. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/videos" />',
  48. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/videos" />',
  49. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/videos" />',
  50. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/videos" />',
  51. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/videos" />',
  52. ` <lastmod>${date}</lastmod>`,
  53. ' <changefreq>weekly</changefreq>',
  54. ' <priority>0.8</priority>',
  55. ' </url>',
  56. // ' <url>',
  57. // ' <loc>https://art.pcoloring.com/en/category</loc>',
  58. // ` <lastmod>${date}</lastmod>`,
  59. // ' <changefreq>daily</changefreq>',
  60. // ' <priority>0.8</priority>',
  61. // ' </url>',
  62. ' <url>',
  63. ' <loc>https://art.pcoloring.com/en/tag</loc>',
  64. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/tag" />',
  65. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/tag" />',
  66. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/tag" />',
  67. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/tag" />',
  68. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/tag" />',
  69. ` <lastmod>${date}</lastmod>`,
  70. ' <changefreq>daily</changefreq>',
  71. ' <priority>0.8</priority>',
  72. ' </url>',
  73. ' <url>',
  74. ' <loc>https://art.pcoloring.com/en/albums</loc>',
  75. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/albums" />',
  76. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/albums" />',
  77. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/albums" />',
  78. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/albums" />',
  79. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/albums" />',
  80. ` <lastmod>${date}</lastmod>`,
  81. ' <changefreq>weekly</changefreq>',
  82. ' <priority>0.8</priority>',
  83. ' </url>',
  84. ' <url>',
  85. ' <loc>https://art.pcoloring.com/en/special</loc>',
  86. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/special" />',
  87. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/special" />',
  88. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/special" />',
  89. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/special" />',
  90. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/special" />',
  91. ` <lastmod>${date}</lastmod>`,
  92. ' <changefreq>daily</changefreq>',
  93. ' <priority>0.8</priority>',
  94. ' </url>',
  95. ' <url>',
  96. ' <loc>https://art.pcoloring.com/en/artists</loc>',
  97. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/artists" />',
  98. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/artists" />',
  99. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/artists" />',
  100. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/artists" />',
  101. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/artists" />',
  102. ` <lastmod>${date}</lastmod>`,
  103. ' <changefreq>monthly</changefreq>',
  104. ' <priority>0.8</priority>',
  105. ' </url>',
  106. ' <url>',
  107. ' <loc>https://art.pcoloring.com/en/info</loc>',
  108. ' <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/info" />',
  109. ' <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/info" />',
  110. ' <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/info" />',
  111. ' <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/info" />',
  112. ' <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/info" />',
  113. ` <lastmod>${date}</lastmod>`,
  114. ' </url>',
  115. ];
  116. // categories
  117. // let categoriesXml = [];
  118. // categories.forEach(e => {
  119. // categoriesXml = categoriesXml.concat([
  120. // ' <url>',
  121. // ` <loc>https://art.pcoloring.com/en/category/${e.id}</loc>`,
  122. // ` <lastmod>${date}</lastmod>`,
  123. // ' </url>',
  124. // ]
  125. // );
  126. // });
  127. // console.log(categoriesXml);
  128. // tags
  129. let tagsXml = [];
  130. let newtags = tags.filter(e => e.count && e.count > 0);
  131. newtags.forEach(e => {
  132. tagsXml = tagsXml.concat([
  133. ' <url>',
  134. ` <loc>https://art.pcoloring.com/en/tag/${e.tag}</loc>`,
  135. ` <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/tag/${e.tag}" />`,
  136. ` <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/tag/${e.tag}" />`,
  137. ` <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/tag/${e.tag}" />`,
  138. ` <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/tag/${e.tag}" />`,
  139. ` <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/tag/${e.tag}" />`,
  140. ` <lastmod>${date}</lastmod>`,
  141. ' </url>',
  142. ]);
  143. });
  144. console.log(tagsXml);
  145. // albums
  146. let albums = await models.ArtAlbum
  147. .find({ pid: 'art', enabled: true })
  148. .sort({ order: 'asc' })
  149. .select('_id')
  150. .lean()
  151. .exec();
  152. let albumsXml = [];
  153. albums.forEach(e => {
  154. albumsXml = albumsXml.concat([
  155. ' <url>',
  156. ` <loc>https://art.pcoloring.com/en/coloring-page-album/${e._id}</loc>`,
  157. ` <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/coloring-page-album/${e._id}" />`,
  158. ` <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/coloring-page-album/${e._id}" />`,
  159. ` <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/coloring-page-album/${e._id}" />`,
  160. ` <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/coloring-page-album/${e._id}" />`,
  161. ` <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/coloring-page-album/${e._id}" />`,
  162. ` <lastmod>${date}</lastmod>`,
  163. ' </url>',
  164. ])
  165. });
  166. console.log(albumsXml);
  167. // designers
  168. let designers = await models.Art.aggregate([
  169. // 首先,过滤出 status = 9000 的文档
  170. { $match: { open: true, status: 9000 } },
  171. // 首先,根据 user 字段进行分组,并计算每个 user 出现的次数
  172. { $group: { _id: '$user', count: { $sum: 1 } } },
  173. // 然后,按照 count 字段进行降序排列
  174. { $sort: { count: -1 } },
  175. // 接着,与 users 集合进行连接,以获取用户的详细信息
  176. {
  177. $lookup: {
  178. from: 'users', // 要连接的集合名称
  179. localField: '_id', // 本地字段(即上一步分组后的 _id 字段)
  180. foreignField: '_id', // 要连接的集合中的字段
  181. as: 'userDetails' // 连接后结果存储在新字段 userDetails 中
  182. }
  183. },
  184. // 展开 userDetails 数组,以便将用户信息提升到顶层
  185. { $unwind: '$userDetails' },
  186. // 调整输出格式,只保留需要的字段
  187. { $project: { _id: 1, user: '$_id', count: 1, username: '$userDetails.username', name: '$userDetails.name' } },
  188. // 限制返回的记录数量
  189. { $limit: 40 }
  190. ]);
  191. let designersXml = [];
  192. designers.forEach(e => {
  193. designersXml = designersXml.concat([
  194. ' <url>',
  195. ` <loc>https://art.pcoloring.com/en/artist/${e._id}</loc>`,
  196. ` <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/artist/${e._id}" />`,
  197. ` <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/artist/${e._id}" />`,
  198. ` <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/artist/${e._id}" />`,
  199. ` <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/artist/${e._id}" />`,
  200. ` <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/artist/${e._id}" />`,
  201. ` <lastmod>${date}</lastmod>`,
  202. ' </url>',
  203. ])
  204. });
  205. console.log(designersXml);
  206. // details
  207. let detailsXml = [];
  208. const cursor = await models.Art
  209. .find({ open: true, status: 9000 })
  210. .select('name title')
  211. .sort({ publishTime: -1 }).cursor(); // 使用cursor,避免一次查询太多记录内存受不了
  212. // 遍历游标中的每个文档
  213. for await (const doc of cursor) {
  214. let uriTitle = doc.name;
  215. if (doc.title) {
  216. try {
  217. let titleJson = JSON.parse(doc.title);
  218. if (titleJson.en) uriTitle = titleJson.en; // URL 里还是尽量用英文title
  219. } catch (e) {
  220. console.error(e.message);
  221. }
  222. }
  223. let utf8name = encodeURIComponent(uriTitle.replace(/[\s_]+/g, '-')).toLowerCase();
  224. detailsXml = detailsXml.concat([
  225. ' <url>',
  226. ` <loc>https://art.pcoloring.com/en/coloring-page/${utf8name}-${doc._id}</loc>`,
  227. ` <xhtml:link rel="alternate" hreflang="en" href="https://art.pcoloring.com/en/coloring-page/${utf8name}-${doc._id}" />`,
  228. ` <xhtml:link rel="alternate" hreflang="zh" href="https://art.pcoloring.com/zh/coloring-page/${utf8name}-${doc._id}" />`,
  229. ` <xhtml:link rel="alternate" hreflang="es" href="https://art.pcoloring.com/es/coloring-page/${utf8name}-${doc._id}" />`,
  230. ` <xhtml:link rel="alternate" hreflang="pt" href="https://art.pcoloring.com/pt/coloring-page/${utf8name}-${doc._id}" />`,
  231. ` <xhtml:link rel="alternate" hreflang="ja" href="https://art.pcoloring.com/ja/coloring-page/${utf8name}-${doc._id}" />`,
  232. ` <lastmod>${date}</lastmod>`,
  233. ' </url>',
  234. ]);
  235. console.log(`process detail ${doc._id} url done`);
  236. }
  237. let endXml = ['</urlset>'];
  238. let sitemapXml = [...commonXml, ...tagsXml, ...albumsXml, ...designersXml, ...detailsXml, ...endXml];
  239. let sitemapXmlStr = sitemapXml.join('\n');
  240. fs.writeFileSync(sitemapTempPath, sitemapXmlStr); // 先写入临时文件
  241. fs.renameSync(sitemapTempPath, sitemapPath); // 再重命名
  242. console.log('生成sitemap成功');
  243. }
  244. function run() {
  245. generateSitemap();
  246. }
  247. module.exports = { run }
  248. if (require.main == module) {
  249. run();
  250. }