app.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. const express = require('express');
  2. const path = require('path');
  3. const app = express();
  4. const config = require('./config/app')
  5. const session = require('express-session');
  6. const RedisStore = require('connect-redis')(session);
  7. const cookieParser = require('cookie-parser');
  8. const bodyParser = require('body-parser');
  9. const compression = require('compression');
  10. const authChecker = require('./libs/auth/checker');
  11. // const helmet = require('helmet'); // 安全中间件,主要添加一些安全头
  12. const rateLimit = require('express-rate-limit'); // 速率限制,预防ddos攻击
  13. // const cors = require('cors');
  14. // 使用 Helmet 中间件
  15. // app.use(helmet());
  16. // 创建速率限制器
  17. const apiLimiter = rateLimit({
  18. windowMs: 15 * 60 * 1000, // 15分钟
  19. max: 100, // 每个IP在15分钟内最多100个请求
  20. standardHeaders: 'draft-7', // 使用draft-7版本的RateLimit头
  21. legacyHeaders: false // 禁用X-RateLimit-*头
  22. });
  23. app.use('/api/', apiLimiter); // api 接口启用限制
  24. // app.use(cors()); // 启用CORS
  25. app.set('trust proxy', 1) //trust first proxy, get ip
  26. // 设置视图引擎为EJS
  27. app.set('view engine', 'ejs');
  28. // 设置视图目录
  29. app.set('views', [
  30. path.join(__dirname, 'views'),
  31. path.join(__dirname, 'views/v2'),
  32. ]);
  33. /**
  34. * Check should compress.
  35. */
  36. function shouldCompress(req, res) {
  37. if (req.headers['x-no-compression']) {
  38. // don't compress responses with this request header
  39. return false
  40. }
  41. // fallback to standard filter function
  42. return compression.filter(req, res)
  43. }
  44. app.use(compression({
  45. filter: shouldCompress
  46. }));
  47. app.use(cookieParser());
  48. app.use(express.static(config.STATIC_DIR));
  49. app.use(express.static(path.join(__dirname, 'dist')));
  50. app.use(session({
  51. store: new RedisStore({
  52. prefix: 'artsite_sess:'
  53. }),
  54. cookie: config.cookie,
  55. saveUninitialized: false,
  56. secret: 'MhxzKhl123.',
  57. resave: false,
  58. name: config.sessionName || sid,
  59. }));
  60. app.use(bodyParser.json());
  61. app.use(bodyParser.urlencoded({
  62. extended: false
  63. }));
  64. // 自定义日志中间件
  65. app.use((req, res, next) => {
  66. const now = new Date().toISOString();
  67. const ip = req.headers['x-forwarded-for'] || req.ip;
  68. console.log(`[${now}] ${ip} ${req.method} ${req.url}`);
  69. next(); // 调用 next() 函数,将请求传递给下一个中间件或路由处理程序
  70. });
  71. app.use('/napi/web/auth', require('./routes/napi/web/auth'));
  72. app.use('/napi/web/menu', authChecker.checkLogin, require('./routes/napi/web/menu'));
  73. app.use('/napi/web/art', authChecker.checkLogin, require('./routes/napi/web/art'));
  74. app.use('/napi/web/user', authChecker.checkLogin, require('./routes/napi/web/user'));
  75. app.use('/napi/web/role', authChecker.checkLogin, require('./routes/napi/web/role'));
  76. app.use('/thumbs/v1', require('./routes/res/thumbs'));
  77. app.use('/proxy', require('./routes/proxy'));
  78. // 旧多语言前缀 URL 统一 301 到 v2 URL,保留 query 参数以迁移 SEO 信号。
  79. app.use((req, res, next) => {
  80. const match = req.path.match(/^\/(en|zh|es|pt|ja)(\/.*)?$/);
  81. if (!match) return next();
  82. const tail = match[2] || '/';
  83. const query = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
  84. // 将 "slug-id" 形式提取出真实 id(兼容旧详情 URL)。
  85. const extractRealId = (str) => {
  86. const last = (str || '').split('-').pop();
  87. return /^[a-f0-9]{24}$/i.test(last) ? last : str;
  88. };
  89. let target = tail;
  90. if (tail === '/' || tail === '') {
  91. target = '/';
  92. } else if (/^\/coloring-page\//.test(tail)) {
  93. const str = tail.replace(/^\/coloring-page\//, '');
  94. target = `/coloring-page/${extractRealId(str)}`;
  95. } else if (/^\/(album|coloring-page-album)\//.test(tail)) {
  96. const id = tail.replace(/^\/(album|coloring-page-album)\//, '');
  97. target = `/coloring-page-album/${id}`;
  98. } else if (tail === '/gallery') {
  99. target = '/coloring-page-gallery';
  100. } else if (tail === '/videos') {
  101. target = '/video-coloring-pages';
  102. } else if (/^\/coloring-page-video\//.test(tail)) {
  103. const id = tail.replace(/^\/coloring-page-video\//, '');
  104. target = `/video-coloring-page/${id}`;
  105. } else if (tail === '/albums') {
  106. target = '/coloring-page-albums';
  107. } else if (/^\/(tag|category)\//.test(tail)) {
  108. const tag = tail.replace(/^\/(tag|category)\//, '') || 'latest';
  109. target = `/coloring-pages?tag=${encodeURIComponent(tag)}`;
  110. } else if (tail === '/tag' || tail === '/category') {
  111. target = '/coloring-pages?tag=latest';
  112. }
  113. return res.redirect(301, `${target}${query}`);
  114. });
  115. //v2
  116. app.use('/', require('./routes/v2/index')); // 首页和具体分类合集页
  117. app.use('/coloring-page', require('./routes/v2/detail')) // 详情页
  118. app.use('/coloring-pages', require('./routes/v2/coloring-pages')) // 所有精选合集页
  119. app.use('/coloring-page-gallery', require('./routes/v2/gallery')) // 图库页
  120. app.use('/share', require('./routes/v2/share')) // deeplink share专属页面
  121. ////////////////////////// 合集 //////////////////////////////
  122. app.use('/summer-coloring-pages', require('./routes/v2/coloring-page-collection')) // flower coloring pages 合集
  123. app.use('/flower-coloring-pages', require('./routes/v2/coloring-page-collection')) // flower coloring pages 合集
  124. app.use('/mandala-coloring-pages', require('./routes/v2/coloring-page-collection')) // mandala coloring pages 合集
  125. app.use('/zentangle-coloring-pages', require('./routes/v2/coloring-page-collection')) // zentangle coloring pages 合集
  126. app.use('/zen-coloring-pages', require('./routes/v2/coloring-page-collection')) // zen coloring pages 合集
  127. app.use('/cat-coloring-pages', require('./routes/v2/coloring-page-collection')) // cat coloring pages 合集
  128. app.use('/butterfly-coloring-pages', require('./routes/v2/coloring-page-collection'))
  129. app.use('/architecture-coloring-pages', require('./routes/v2/coloring-page-collection'))
  130. app.use('/simple-coloring-pages', require('./routes/v2/coloring-page-collection'))
  131. app.use('/girl-coloring-pages', require('./routes/v2/coloring-page-collection'))
  132. app.use('/fantasy-coloring-pages', require('./routes/v2/coloring-page-collection'))
  133. app.use('/christmas-coloring-pages', require('./routes/v2/coloring-page-collection'))
  134. app.use('/patterns-coloring-pages', require('./routes/v2/coloring-page-collection'))
  135. app.use('/peacock-coloring-pages', require('./routes/v2/coloring-page-collection'))
  136. app.use('/dragon-coloring-pages', require('./routes/v2/coloring-page-collection'))
  137. app.use('/unicorn-coloring-pages', require('./routes/v2/coloring-page-collection'))
  138. app.use('/food-coloring-pages', require('./routes/v2/coloring-page-collection'))
  139. app.use('/video-coloring-pages', require('./routes/v2/video-coloring-page'))
  140. app.use('/video-coloring-page', require('./routes/v2/video-coloring-page'))
  141. app.use('/coloring-page-albums', require('./routes/v2/album'))
  142. app.use('/coloring-page-album', require('./routes/v2/album'))
  143. app.use('/tips-tricks', require('./routes/v2/tips-tricks'));
  144. app.use('/download', require('./routes/res/download'));
  145. app.use('/api/comment', require('./routes/v2/comment')); // 评论
  146. app.use('/api/contact', require('./routes/v2/contact')); // 联系信息
  147. app.use('/api/subscribe', require('./routes/v2/subscribe')); // 用户提交邮箱订阅
  148. app.use('/api/tasks', require('./routes/v2/tasks')); // 用户分享前提交task列表
  149. //v1
  150. app.use('/', require('./routes/index'));
  151. // catch 404 and forward to error handler
  152. app.use(function (req, res) {
  153. // 设置状态码为404
  154. res.status(404);
  155. // res.sendFile(path.join(__dirname, '404.html'));
  156. res.render('404', { title: '404 Error', description: 'PAGE NOT FOUND' });
  157. });
  158. // error handler
  159. app.use(function (err, req, res, next) {
  160. // set locals, only providing error in development
  161. res.locals.message = err.message;
  162. res.locals.error = req.app.get('env') === 'development' ? err : {};
  163. console.log("error:" + err + " status: ");
  164. // render the error page
  165. res.status(err.status || 404);
  166. res.render('404', { title: '404 Error', description: 'PAGE NOT FOUND' });
  167. });
  168. // 启动服务器,监听6889端口
  169. const PORT = process.env.PORT || 6889;
  170. app.listen(PORT, () => {
  171. console.log(`Server is running on http://localhost:${PORT}`);
  172. });