|
@@ -136,6 +136,39 @@ router.get(/^\/(en|zh|es|pt|ja)$/, function (req, res, next) { // 限制严格
|
|
|
doc.size = doc.contents.length;
|
|
doc.size = doc.contents.length;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 视频故事
|
|
|
|
|
+ let videos = await models.ArtVideoStory
|
|
|
|
|
+ .find({ enabled: true, seoTitle: { $exists: true } })
|
|
|
|
|
+ .sort({ order: 'asc' })
|
|
|
|
|
+ .limit(6)
|
|
|
|
|
+ .lean()
|
|
|
|
|
+ .exec();
|
|
|
|
|
+
|
|
|
|
|
+ for (let doc of videos) {
|
|
|
|
|
+ doc.poster = `${host}/thumbs/coloring-page/vs-poster/320/${doc._id}.${imageType}`;
|
|
|
|
|
+ doc.size = doc.contents.length;
|
|
|
|
|
+ if (doc.seoTitle) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let titleJson = JSON.parse(doc.seoTitle);
|
|
|
|
|
+ doc.seoTitle = titleJson && titleJson[lang] ? titleJson[lang] : doc.name;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoTitle = doc.name;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (doc.seoDescription) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let descJson = JSON.parse(doc.seoDescription);
|
|
|
|
|
+ doc.seoDescription = descJson && descJson[lang] ? descJson[lang] : doc.seoTitle;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoDescription = doc.seoTitle;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 设计师
|
|
// 设计师
|
|
|
let designers = await models.Art.aggregate([
|
|
let designers = await models.Art.aggregate([
|
|
|
// 首先,过滤出 status = 9000 的文档
|
|
// 首先,过滤出 status = 9000 的文档
|
|
@@ -181,6 +214,7 @@ router.get(/^\/(en|zh|es|pt|ja)$/, function (req, res, next) { // 限制严格
|
|
|
recommend,
|
|
recommend,
|
|
|
special,
|
|
special,
|
|
|
albums,
|
|
albums,
|
|
|
|
|
+ videos,
|
|
|
designers,
|
|
designers,
|
|
|
translate,
|
|
translate,
|
|
|
categories,
|
|
categories,
|
|
@@ -537,7 +571,7 @@ router.get('/:lang/albums', function (req, res, next) {
|
|
|
.sort({ order: 'asc' })
|
|
.sort({ order: 'asc' })
|
|
|
.populate('title')
|
|
.populate('title')
|
|
|
.populate('slogon')
|
|
.populate('slogon')
|
|
|
- .select('tag title slogon contents')
|
|
|
|
|
|
|
+ .select('tag title slogon seoTitle seoDescription contents')
|
|
|
.lean()
|
|
.lean()
|
|
|
.exec();
|
|
.exec();
|
|
|
|
|
|
|
@@ -548,6 +582,26 @@ router.get('/:lang/albums', function (req, res, next) {
|
|
|
doc.title = doc.title ? doc.title[lang] : '';
|
|
doc.title = doc.title ? doc.title[lang] : '';
|
|
|
doc.slogon = doc.slogon ? doc.slogon[lang] : '';
|
|
doc.slogon = doc.slogon ? doc.slogon[lang] : '';
|
|
|
doc.size = doc.contents.length;
|
|
doc.size = doc.contents.length;
|
|
|
|
|
+ if (doc.seoTitle) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let titleJson = JSON.parse(doc.seoTitle);
|
|
|
|
|
+ doc.seoTitle = titleJson && titleJson[lang] ? titleJson[lang] : doc.title;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoTitle = doc.title;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (doc.seoDescription) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let descJson = JSON.parse(doc.seoDescription);
|
|
|
|
|
+ doc.seoDescription = descJson && descJson[lang] ? descJson[lang] : doc.slogon;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoDescription = doc.slogon;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let data = {
|
|
let data = {
|
|
@@ -618,7 +672,7 @@ router.get('/:lang/coloring-page-album/:id', function (req, res, next) {
|
|
|
.populate('title')
|
|
.populate('title')
|
|
|
.populate('slogon')
|
|
.populate('slogon')
|
|
|
.populate({ path: 'contents', select: artSelect })
|
|
.populate({ path: 'contents', select: artSelect })
|
|
|
- .select('tag title slogon contents')
|
|
|
|
|
|
|
+ .select('tag title slogon seoTitle seoDescription contents')
|
|
|
.lean()
|
|
.lean()
|
|
|
.exec();
|
|
.exec();
|
|
|
|
|
|
|
@@ -630,13 +684,33 @@ router.get('/:lang/coloring-page-album/:id', function (req, res, next) {
|
|
|
doc.title = doc.title ? doc.title[lang] : '';
|
|
doc.title = doc.title ? doc.title[lang] : '';
|
|
|
doc.slogon = doc.slogon ? doc.slogon[lang] : '';
|
|
doc.slogon = doc.slogon ? doc.slogon[lang] : '';
|
|
|
doc.size = doc.contents.length;
|
|
doc.size = doc.contents.length;
|
|
|
|
|
+ if (doc.seoTitle) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let titleJson = JSON.parse(doc.seoTitle);
|
|
|
|
|
+ doc.seoTitle = titleJson && titleJson[lang] ? titleJson[lang] : doc.title;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoTitle = doc.title;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (doc.seoDescription) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let descJson = JSON.parse(doc.seoDescription);
|
|
|
|
|
+ doc.seoDescription = descJson && descJson[lang] ? descJson[lang] : doc.slogon;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoDescription = doc.slogon;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
organizeData(doc.contents, lang, imageType);
|
|
organizeData(doc.contents, lang, imageType);
|
|
|
|
|
|
|
|
|
|
|
|
|
let data = {
|
|
let data = {
|
|
|
- title: `${translate.coloringPageAlbum[lang]}: ${doc.title}`,
|
|
|
|
|
- description: `${doc.slogon}`,
|
|
|
|
|
|
|
+ title: doc.seoTitle,
|
|
|
|
|
+ description: doc.seoDescription,
|
|
|
data: doc,
|
|
data: doc,
|
|
|
translate,
|
|
translate,
|
|
|
languages,
|
|
languages,
|
|
@@ -672,6 +746,180 @@ router.get('/:lang/coloring-page-album/:id', function (req, res, next) {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+// 视频故事页路由
|
|
|
|
|
+router.get('/:lang/videos', function (req, res, next) {
|
|
|
|
|
+ (async function () {
|
|
|
|
|
+ let lang = utils.lang.ensureLanguage(req.params.lang);
|
|
|
|
|
+ if (!req.cookies.lang || req.cookies.lang != lang) {
|
|
|
|
|
+ res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let imageType = req.headers.accept?.includes('image/webp') ? 'webp' : 'jpeg'; // 浏览器支持webp就用webp
|
|
|
|
|
+
|
|
|
|
|
+ let cacheKey = `${CACHE_PREFIX}_${imageType}_${req.originalUrl}`;
|
|
|
|
|
+ let htmlData = await redis.getAsync(cacheKey);
|
|
|
|
|
+ if (!htmlData) {
|
|
|
|
|
+ // 视频故事
|
|
|
|
|
+ let vidoes = await models.ArtVideoStory
|
|
|
|
|
+ .find({ enabled: true, seoTitle: { $exists: true } })
|
|
|
|
|
+ .sort({ order: 'asc' })
|
|
|
|
|
+ .lean()
|
|
|
|
|
+ .exec();
|
|
|
|
|
+
|
|
|
|
|
+ let host = config.cdnHost ?? config.resHost;
|
|
|
|
|
+ for (let doc of videos) {
|
|
|
|
|
+ doc.poster = `${host}/thumbs/coloring-page/vs-poster/320/${doc._id}.${imageType}`;
|
|
|
|
|
+ doc.size = doc.contents.length;
|
|
|
|
|
+ if (doc.seoTitle) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let titleJson = JSON.parse(doc.seoTitle);
|
|
|
|
|
+ doc.seoTitle = titleJson && titleJson[lang] ? titleJson[lang] : doc.name;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoTitle = doc.name;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (doc.seoDescription) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let descJson = JSON.parse(doc.seoDescription);
|
|
|
|
|
+ doc.seoDescription = descJson && descJson[lang] ? descJson[lang] : doc.seoTitle;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoDescription = doc.seoTitle;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let data = {
|
|
|
|
|
+ title: meta.videosTitle[lang],
|
|
|
|
|
+ description: meta.videosDescription[lang],
|
|
|
|
|
+ data: videos,
|
|
|
|
|
+ length: videos.length,
|
|
|
|
|
+ translate,
|
|
|
|
|
+ languages,
|
|
|
|
|
+ lang,
|
|
|
|
|
+ uri: req.originalUrl,
|
|
|
|
|
+ pageUri: replaceUriParams,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 渲染EJS模板到内存中
|
|
|
|
|
+ res.render('videos', data, async (err, html) => {
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ // 如果渲染出错,调用next()传递错误
|
|
|
|
|
+ return next(err);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 渲染成功,存redis, 发送数据到客户端
|
|
|
|
|
+ htmlData = html;
|
|
|
|
|
+ try {
|
|
|
|
|
+ await redis.set(cacheKey, htmlData, 'EX', CACHE_EXPIRES);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ res.send(htmlData);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 缓存命中, 直接发送缓存数据
|
|
|
|
|
+ res.set({ 'X-From-Cache': 'true' });
|
|
|
|
|
+ res.send(htmlData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ })().catch(next);
|
|
|
|
|
+
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 视频故事详情页路由
|
|
|
|
|
+router.get('/:lang/coloring-page-video/:id', function (req, res, next) {
|
|
|
|
|
+ (async function () {
|
|
|
|
|
+ let lang = utils.lang.ensureLanguage(req.params.lang);
|
|
|
|
|
+ if (!req.cookies.lang || req.cookies.lang != lang) {
|
|
|
|
|
+ res.cookie('lang', lang, config.cookie);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let id = req.params.id;
|
|
|
|
|
+ utils.validators.validateId(id);
|
|
|
|
|
+
|
|
|
|
|
+ let imageType = req.headers.accept?.includes('image/webp') ? 'webp' : 'jpeg'; // 浏览器支持webp就用webp
|
|
|
|
|
+
|
|
|
|
|
+ let cacheKey = `${CACHE_PREFIX}_${imageType}_${req.originalUrl}`;
|
|
|
|
|
+ let htmlData = await redis.getAsync(cacheKey);
|
|
|
|
|
+ if (!htmlData) {
|
|
|
|
|
+ // 专辑
|
|
|
|
|
+ let doc = await models.ArtVideoStory
|
|
|
|
|
+ .findById(id)
|
|
|
|
|
+ .lean()
|
|
|
|
|
+ .exec();
|
|
|
|
|
+
|
|
|
|
|
+ if (!doc) throw createError(404, 'Album Not Found!');
|
|
|
|
|
+
|
|
|
|
|
+ let host = config.cdnHost ?? config.resHost;
|
|
|
|
|
+ doc.poster = `${host}/thumbs/coloring-page/vs-poster/480/${doc._id}.${imageType}`;
|
|
|
|
|
+ doc.size = doc.contents.length;
|
|
|
|
|
+ if (doc.seoTitle) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let titleJson = JSON.parse(doc.seoTitle);
|
|
|
|
|
+ doc.seoTitle = titleJson && titleJson[lang] ? titleJson[lang] : doc.name;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoTitle = doc.title;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (doc.seoDescription) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ let descJson = JSON.parse(doc.seoDescription);
|
|
|
|
|
+ doc.seoDescription = descJson && descJson[lang] ? descJson[lang] : doc.seoTitle;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc.seoDescription = doc.seoTitle;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ organizeData(doc.contents, lang, imageType);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ let data = {
|
|
|
|
|
+ title: doc.seoTitle,
|
|
|
|
|
+ description: doc.seoDescription,
|
|
|
|
|
+ data: doc,
|
|
|
|
|
+ translate,
|
|
|
|
|
+ languages,
|
|
|
|
|
+ lang,
|
|
|
|
|
+ uri: req.originalUrl,
|
|
|
|
|
+ pageUri: replaceUriParams,
|
|
|
|
|
+ };
|
|
|
|
|
+ // 渲染EJS模板到内存中
|
|
|
|
|
+ res.render('video', data, async (err, html) => {
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ // 如果渲染出错,调用next()传递错误
|
|
|
|
|
+ return next(err);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 渲染成功,存redis, 发送数据到客户端
|
|
|
|
|
+ htmlData = html;
|
|
|
|
|
+ try {
|
|
|
|
|
+ await redis.set(cacheKey, htmlData, 'EX', CACHE_EXPIRES);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ res.send(htmlData);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 缓存命中, 直接发送缓存数据
|
|
|
|
|
+ res.set({ 'X-From-Cache': 'true' });
|
|
|
|
|
+ res.send(htmlData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ })().catch(next);
|
|
|
|
|
+
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
// 设计师专栏路由, 重定向到新的页面
|
|
// 设计师专栏路由, 重定向到新的页面
|
|
|
router.get('/:lang/designers', function (req, res, next) {
|
|
router.get('/:lang/designers', function (req, res, next) {
|
|
|
const uri = req.originalUrl;
|
|
const uri = req.originalUrl;
|