index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. var express = require('express');
  2. var router = express.Router();
  3. const models = require('../models');
  4. const config = require('../config/app');
  5. const redis = require('../libs/redis');
  6. const categories = require('../config/category');
  7. const tags = require('../config/tag');
  8. const languages = require('../config/language');
  9. const translate = require('../config/translate');
  10. const { getLocale, ensureLanguage } = require('../libs/utils');
  11. const { format } = require('date-fns');
  12. const { getListBuilder } = require('../libs/pager');
  13. const CACHE_PREFIX = "art_v1";
  14. const CACHE_EXPIRES = 60; // 60s刷新一次
  15. router.get('/', (req, res, next) => {
  16. let locale = getLocale(req.acceptsLanguages());
  17. let lang = ensureLanguage(locale);
  18. return res.redirect(`/${lang}`);
  19. });
  20. router.get('/:lang/', async (req, res, next) => {
  21. let lang = ensureLanguage(req.params.lang);
  22. let baseSort = { publishTime: 'desc' };
  23. // 最新上线
  24. let latest = await models.Art
  25. .find({ status: 9000 })
  26. .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
  27. .sort(baseSort)
  28. .limit(12)
  29. .lean()
  30. .exec();
  31. organizeData(latest);
  32. // 热门精选
  33. let recommend = await models.Art
  34. .find({ tags: 'data_good', status: 9000 })
  35. .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
  36. .sort(baseSort)
  37. .limit(12)
  38. .lean()
  39. .exec();
  40. organizeData(recommend);
  41. // special 专区
  42. let special = await models.Art
  43. .find({ hasSpecial: true, status: 9000 })
  44. .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
  45. .sort(baseSort)
  46. .limit(12)
  47. .lean()
  48. .exec();
  49. organizeData(special);
  50. // 专辑
  51. let albums = await models.ArtAlbum
  52. .find({ pid: 'art', enabled: true })
  53. .sort({ order: 'asc' })
  54. .populate('title')
  55. .populate('slogon')
  56. .select('tag title slogon contents')
  57. .limit(6)
  58. .lean()
  59. .exec();
  60. for (let doc of albums) {
  61. doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
  62. doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
  63. doc.title = doc.title ? doc.title[lang] : '';
  64. doc.slogon = doc.slogon ? doc.slogon[lang] : '';
  65. doc.size = doc.contents.length;
  66. }
  67. let data = {
  68. title: 'Art Number Coloring',
  69. latest,
  70. recommend,
  71. special,
  72. albums,
  73. translate,
  74. categories,
  75. languages,
  76. lang,
  77. uri: `/${lang}`,
  78. };
  79. res.render('index', data);
  80. });
  81. router.get('/:lang/category/:tag?', async (req, res, next) => {
  82. let lang = ensureLanguage(req.params.lang);
  83. let tag = req.params.tag;
  84. if (!tag) tag = 'latest';
  85. let query = {
  86. page: req.query.page,
  87. length: req.query.length,
  88. orderBy: 'publishTime',
  89. order: 'desc',
  90. base: { status: 9000 },
  91. filters: tag == 'latest' ? {} : { tags: tag },
  92. }
  93. let result = await getListBuilder(query, models.Art);
  94. organizeData(result.data);
  95. let data = {
  96. title: 'Coloring Page Categories',
  97. data: result.data,
  98. page: result.page,
  99. search: req.query.search,
  100. length: result.length,
  101. recordsFiltered: result.recordsFiltered,
  102. recordsTotal: result.recordsTotal,
  103. translate,
  104. categories,
  105. languages,
  106. lang,
  107. tag,
  108. uri: `/${lang}/category/${tag}`,
  109. };
  110. res.render('category', data);
  111. });
  112. router.get('/:lang/tag/:tag?', async (req, res, next) => {
  113. let lang = ensureLanguage(req.params.lang);
  114. let tag = req.params.tag;
  115. if (!tag) tag = 'latest';
  116. let query = {
  117. page: req.query.page,
  118. length: req.query.length,
  119. search: req.query.search,
  120. orderBy: 'publishTime',
  121. order: 'desc',
  122. base: { status: 9000 },
  123. filters: tag == 'latest' ? {} : { tags: tag },
  124. }
  125. let result = await getListBuilder(query, models.Art);
  126. organizeData(result.data);
  127. let data = {
  128. title: 'Coloring Page Tags',
  129. data: result.data,
  130. page: result.page,
  131. length: result.length,
  132. recordsFiltered: result.recordsFiltered,
  133. recordsTotal: result.recordsTotal,
  134. translate,
  135. categories,
  136. languages,
  137. lang,
  138. tag,
  139. tags,
  140. uri: `/${lang}/tag/${tag}`,
  141. };
  142. res.render('tag', data);
  143. });
  144. router.get('/:lang/search', async (req, res, next) => {
  145. let lang = ensureLanguage(req.params.lang);
  146. let search = req.query.search;
  147. let query = {
  148. page: req.query.page,
  149. length: req.query.length,
  150. search: req.query.search,
  151. orderBy: 'publishTime',
  152. order: 'desc',
  153. base: { status: 9000 },
  154. filters: {},
  155. }
  156. let result = await getListBuilder(query, models.Art);
  157. organizeData(result.data);
  158. let data = {
  159. title: 'Coloring Page Search',
  160. data: result.data,
  161. page: result.page,
  162. length: result.length,
  163. recordsFiltered: result.recordsFiltered,
  164. recordsTotal: result.recordsTotal,
  165. translate,
  166. categories,
  167. languages,
  168. lang,
  169. uri: `/${lang}/search?search=${search}`,
  170. };
  171. res.render('search', data);
  172. });
  173. router.get('/:lang/special', async (req, res, next) => {
  174. let lang = ensureLanguage(req.params.lang);
  175. let query = {
  176. page: req.query.page,
  177. length: req.query.length,
  178. orderBy: 'publishTime',
  179. order: 'desc',
  180. base: { status: 9000 },
  181. filters: { hasSpecial: true },
  182. }
  183. let result = await getListBuilder(query, models.Art);
  184. organizeData(result.data);
  185. let data = {
  186. title: 'Special Coloring Page',
  187. data: result.data,
  188. page: result.page,
  189. length: result.length,
  190. recordsFiltered: result.recordsFiltered,
  191. recordsTotal: result.recordsTotal,
  192. translate,
  193. languages,
  194. lang,
  195. uri: `/${lang}/special`,
  196. };
  197. res.render('special', data);
  198. });
  199. router.get('/:lang/albums', async (req, res, next) => {
  200. let lang = ensureLanguage(req.params.lang);
  201. // 专辑
  202. let albums = await models.ArtAlbum
  203. .find({ pid: 'art', enabled: true })
  204. .sort({ order: 'asc' })
  205. .populate('title')
  206. .populate('slogon')
  207. .select('tag title slogon contents')
  208. .lean()
  209. .exec();
  210. for (let doc of albums) {
  211. doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
  212. doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
  213. doc.title = doc.title ? doc.title[lang] : '';
  214. doc.slogon = doc.slogon ? doc.slogon[lang] : '';
  215. doc.size = doc.contents.length;
  216. }
  217. let data = {
  218. title: 'Coloring Page Albums',
  219. data: albums,
  220. length: albums.length,
  221. translate,
  222. languages,
  223. lang,
  224. uri: `/${lang}/albums`,
  225. };
  226. res.render('albums', data);
  227. });
  228. router.get('/:lang/album/:id', async (req, res, next) => {
  229. let lang = ensureLanguage(req.params.lang);
  230. let id = req.params.id;
  231. // 专辑
  232. let doc = await models.ArtAlbum
  233. .findById(id)
  234. .populate('title')
  235. .populate('slogon')
  236. .populate({ path: 'contents', select: "width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId" })
  237. .select('tag title slogon contents')
  238. .lean()
  239. .exec();
  240. if (!doc) throw createError(404, 'Album Not Found!');
  241. doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`;
  242. doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`;
  243. doc.title = doc.title ? doc.title[lang] : '';
  244. doc.slogon = doc.slogon ? doc.slogon[lang] : '';
  245. doc.size = doc.contents.length;
  246. organizeData(doc.contents);
  247. let data = {
  248. title: `${doc.title}`,
  249. data: doc,
  250. translate,
  251. languages,
  252. lang,
  253. uri: `/${lang}/album/${id}`,
  254. };
  255. res.render('album', data);
  256. });
  257. router.get('/:lang/detail/:id', async (req, res) => {
  258. let lang = ensureLanguage(req.params.lang);
  259. let id = req.params.id;
  260. let doc = await models.Art
  261. .findById(id)
  262. .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId desc name')
  263. .populate('user', 'username name')
  264. .lean()
  265. .exec();
  266. if (!doc) throw createError(404, 'Art Not Found!');
  267. organizeDetail(doc, lang);
  268. // find relate
  269. // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录
  270. let tags = [...doc.tags];
  271. let cates = categories.map(e => e.id);
  272. tags = tags.filter(e => !cates.includes(e));
  273. let baseSort = { publishTime: 'desc' };
  274. let relates = await models.Art
  275. .find({ tags: { $in: tags }, status: 9000 })
  276. .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId')
  277. .sort(baseSort)
  278. .limit(12)
  279. .lean()
  280. .exec();
  281. organizeData(relates);
  282. let data = {
  283. title: `Coloring Page ${doc.name}`,
  284. data: doc,
  285. translate,
  286. lang,
  287. languages,
  288. relates,
  289. uri: `/${lang}/detail/${id}`,
  290. };
  291. res.render('detail', data);
  292. });
  293. router.get('/play/:id', async (req, res) => {
  294. let id = req.params.id;
  295. let data = { id };
  296. let doc = await models.Art.findById(id);
  297. if (!doc) throw createError(404, 'Art Not Found!');
  298. res.render('play', data);
  299. });
  300. const organizeData = (data) => {
  301. data.forEach(doc => {
  302. let host = config.resHost;
  303. let publishVersion = doc.publishVersion || 0;
  304. let version = publishVersion + 1500;
  305. doc.thumb = `${host}/thumbs/v2/page/480/${doc._id}.jpeg`;
  306. if (doc.mystery) { // 神秘图固定用一张
  307. doc.thumb = `${host}/thumbs/v2/mystery/480/${doc._id}.jpeg`;
  308. } else if (doc.hasSpecial) { // special图有切线图、渐变切线图、灰度图、上传图3中可选
  309. let str = 'special_outline';
  310. if (doc.useSpecialThumb == 1) {
  311. str = 'special_gray';
  312. } else if (doc.useSpecialThumb == 2) {
  313. str = 'special_thumb';
  314. } else if (doc.useSpecialThumb == 3) {
  315. str = 'special_gradient';
  316. }
  317. doc.thumb = `${host}/thumbs/v2/${str}/480/${doc._id}.jpeg`;
  318. }
  319. doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip`
  320. delete doc.hasSpecial;
  321. delete doc.useSpecialThumb;
  322. delete doc.publishVersion;
  323. delete doc.pageId;
  324. })
  325. }
  326. const organizeDetail = (doc, lang) => {
  327. let host = config.resHost;
  328. let publishVersion = doc.publishVersion || 0;
  329. let version = publishVersion + 1500;
  330. doc.thumb = `${host}/thumbs/v2/page/480/${doc._id}.jpeg`;
  331. if (doc.mystery) { // 神秘图固定用一张
  332. doc.thumb = `${host}/thumbs/v2/mystery/480/${doc._id}.jpeg`;
  333. } else if (doc.hasSpecial) { // special图有切线图、渐变切线图、灰度图、上传图3中可选
  334. let str = 'special_outline';
  335. if (doc.useSpecialThumb == 1) {
  336. str = 'special_gray';
  337. } else if (doc.useSpecialThumb == 2) {
  338. str = 'special_thumb';
  339. } else if (doc.useSpecialThumb == 3) {
  340. str = 'special_gradient';
  341. }
  342. doc.thumb = `${host}/thumbs/v2/${str}/480/${doc._id}.jpeg`;
  343. }
  344. doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip`
  345. doc.desc = translate.descTest[lang];
  346. doc.title = translate.titleTest[lang];
  347. doc.publishTime = format(new Date(doc.publishTime), 'yyyy/MM/dd');
  348. delete doc.hasSpecial;
  349. delete doc.useSpecialThumb;
  350. delete doc.publishVersion;
  351. delete doc.pageId;
  352. }
  353. module.exports = router;