var express = require('express'); var router = express.Router(); const models = require('../models'); const config = require('../config/app'); const redis = require('../libs/redis'); const categories = require('../config/category'); const tags = require('../config/tag'); const languages = require('../config/language'); const translate = require('../config/translate'); const { getLocale, ensureLanguage } = require('../libs/utils'); const { format } = require('date-fns'); const { getListBuilder } = require('../libs/pager'); const CACHE_PREFIX = "art_v1"; const CACHE_EXPIRES = 60; // 60s刷新一次 router.get('/', (req, res, next) => { let locale = getLocale(req.acceptsLanguages()); let lang = ensureLanguage(locale); return res.redirect(`/${lang}`); }); router.get('/:lang/', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let baseSort = { publishTime: 'desc' }; // 最新上线 let latest = await models.Art .find({ status: 9000 }) .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId') .sort(baseSort) .limit(12) .lean() .exec(); organizeData(latest); // 热门精选 let recommend = await models.Art .find({ tags: 'data_good', status: 9000 }) .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId') .sort(baseSort) .limit(12) .lean() .exec(); organizeData(recommend); // special 专区 let special = await models.Art .find({ hasSpecial: true, status: 9000 }) .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId') .sort(baseSort) .limit(12) .lean() .exec(); organizeData(special); // 专辑 let albums = await models.ArtAlbum .find({ pid: 'art', enabled: true }) .sort({ order: 'asc' }) .populate('title') .populate('slogon') .select('tag title slogon contents') .limit(6) .lean() .exec(); for (let doc of albums) { doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`; doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`; doc.title = doc.title ? doc.title[lang] : ''; doc.slogon = doc.slogon ? doc.slogon[lang] : ''; doc.size = doc.contents.length; } let data = { title: 'Art Number Coloring', latest, recommend, special, albums, translate, categories, languages, lang, uri: `/${lang}`, }; res.render('index', data); }); router.get('/:lang/category/:tag?', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let tag = req.params.tag; if (!tag) tag = 'latest'; let query = { page: req.query.page, length: req.query.length, orderBy: 'publishTime', order: 'desc', base: { status: 9000 }, filters: tag == 'latest' ? {} : { tags: tag }, } let result = await getListBuilder(query, models.Art); organizeData(result.data); let data = { title: 'Coloring Page Categories', data: result.data, page: result.page, search: req.query.search, length: result.length, recordsFiltered: result.recordsFiltered, recordsTotal: result.recordsTotal, translate, categories, languages, lang, tag, uri: `/${lang}/category/${tag}`, }; res.render('category', data); }); router.get('/:lang/tag/:tag?', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let tag = req.params.tag; if (!tag) tag = 'latest'; let query = { page: req.query.page, length: req.query.length, search: req.query.search, orderBy: 'publishTime', order: 'desc', base: { status: 9000 }, filters: tag == 'latest' ? {} : { tags: tag }, } let result = await getListBuilder(query, models.Art); organizeData(result.data); let data = { title: 'Coloring Page Tags', data: result.data, page: result.page, length: result.length, recordsFiltered: result.recordsFiltered, recordsTotal: result.recordsTotal, translate, categories, languages, lang, tag, tags, uri: `/${lang}/tag/${tag}`, }; res.render('tag', data); }); router.get('/:lang/search', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let search = req.query.search; let query = { page: req.query.page, length: req.query.length, search: req.query.search, orderBy: 'publishTime', order: 'desc', base: { status: 9000 }, filters: {}, } let result = await getListBuilder(query, models.Art); organizeData(result.data); let data = { title: 'Coloring Page Search', data: result.data, page: result.page, length: result.length, recordsFiltered: result.recordsFiltered, recordsTotal: result.recordsTotal, translate, categories, languages, lang, uri: `/${lang}/search?search=${search}`, }; res.render('search', data); }); router.get('/:lang/special', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let query = { page: req.query.page, length: req.query.length, orderBy: 'publishTime', order: 'desc', base: { status: 9000 }, filters: { hasSpecial: true }, } let result = await getListBuilder(query, models.Art); organizeData(result.data); let data = { title: 'Special Coloring Page', data: result.data, page: result.page, length: result.length, recordsFiltered: result.recordsFiltered, recordsTotal: result.recordsTotal, translate, languages, lang, uri: `/${lang}/special`, }; res.render('special', data); }); router.get('/:lang/albums', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); // 专辑 let albums = await models.ArtAlbum .find({ pid: 'art', enabled: true }) .sort({ order: 'asc' }) .populate('title') .populate('slogon') .select('tag title slogon contents') .lean() .exec(); for (let doc of albums) { doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`; doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`; doc.title = doc.title ? doc.title[lang] : ''; doc.slogon = doc.slogon ? doc.slogon[lang] : ''; doc.size = doc.contents.length; } let data = { title: 'Coloring Page Albums', data: albums, length: albums.length, translate, languages, lang, uri: `/${lang}/albums`, }; res.render('albums', data); }); router.get('/:lang/album/:id', async (req, res, next) => { let lang = ensureLanguage(req.params.lang); let id = req.params.id; // 专辑 let doc = await models.ArtAlbum .findById(id) .populate('title') .populate('slogon') .populate({ path: 'contents', select: "width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId" }) .select('tag title slogon contents') .lean() .exec(); if (!doc) throw createError(404, 'Album Not Found!'); doc.icon = `${config.resHost}/res/coloring/album_icon/320/${doc._id}.jpeg`; doc.cover = `${config.resHost}/res/coloring/album_cover/640/${doc._id}.jpeg`; doc.title = doc.title ? doc.title[lang] : ''; doc.slogon = doc.slogon ? doc.slogon[lang] : ''; doc.size = doc.contents.length; organizeData(doc.contents); let data = { title: `${doc.title}`, data: doc, translate, languages, lang, uri: `/${lang}/album/${id}`, }; res.render('album', data); }); router.get('/:lang/detail/:id', async (req, res) => { let lang = ensureLanguage(req.params.lang); let id = req.params.id; let doc = await models.Art .findById(id) .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId desc name') .populate('user', 'username name') .lean() .exec(); if (!doc) throw createError(404, 'Art Not Found!'); organizeDetail(doc, lang); // find relate // 算法: 排除掉主流的tag,用剩下的tag去检索,取最多12条记录 let tags = [...doc.tags]; let cates = categories.map(e => e.id); tags = tags.filter(e => !cates.includes(e)); let baseSort = { publishTime: 'desc' }; let relates = await models.Art .find({ tags: { $in: tags }, status: 9000 }) .select('width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId') .sort(baseSort) .limit(12) .lean() .exec(); organizeData(relates); let data = { title: `Coloring Page ${doc.name}`, data: doc, translate, lang, languages, relates, uri: `/${lang}/detail/${id}`, }; res.render('detail', data); }); router.get('/play/:id', async (req, res) => { let id = req.params.id; let data = { id }; let doc = await models.Art.findById(id); if (!doc) throw createError(404, 'Art Not Found!'); res.render('play', data); }); const organizeData = (data) => { data.forEach(doc => { let host = config.resHost; let publishVersion = doc.publishVersion || 0; let version = publishVersion + 1500; doc.thumb = `${host}/thumbs/v2/page/480/${doc._id}.jpeg`; if (doc.mystery) { // 神秘图固定用一张 doc.thumb = `${host}/thumbs/v2/mystery/480/${doc._id}.jpeg`; } else if (doc.hasSpecial) { // special图有切线图、渐变切线图、灰度图、上传图3中可选 let str = 'special_outline'; if (doc.useSpecialThumb == 1) { str = 'special_gray'; } else if (doc.useSpecialThumb == 2) { str = 'special_thumb'; } else if (doc.useSpecialThumb == 3) { str = 'special_gradient'; } doc.thumb = `${host}/thumbs/v2/${str}/480/${doc._id}.jpeg`; } doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip` delete doc.hasSpecial; delete doc.useSpecialThumb; delete doc.publishVersion; delete doc.pageId; }) } const organizeDetail = (doc, lang) => { let host = config.resHost; let publishVersion = doc.publishVersion || 0; let version = publishVersion + 1500; doc.thumb = `${host}/thumbs/v2/page/480/${doc._id}.jpeg`; if (doc.mystery) { // 神秘图固定用一张 doc.thumb = `${host}/thumbs/v2/mystery/480/${doc._id}.jpeg`; } else if (doc.hasSpecial) { // special图有切线图、渐变切线图、灰度图、上传图3中可选 let str = 'special_outline'; if (doc.useSpecialThumb == 1) { str = 'special_gray'; } else if (doc.useSpecialThumb == 2) { str = 'special_thumb'; } else if (doc.useSpecialThumb == 3) { str = 'special_gradient'; } doc.thumb = `${host}/thumbs/v2/${str}/480/${doc._id}.jpeg`; } doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip` doc.desc = translate.descTest[lang]; doc.title = translate.titleTest[lang]; doc.publishTime = format(new Date(doc.publishTime), 'yyyy/MM/dd'); delete doc.hasSpecial; delete doc.useSpecialThumb; delete doc.publishVersion; delete doc.pageId; } module.exports = router;