@@ -5,6 +5,9 @@ const errors = require('@tryghost/errors');
55const config = require ( '../../../shared/config' ) ;
66const urlUtils = require ( '../../../shared/url-utils' ) ;
77const tpl = require ( '@tryghost/tpl' ) ;
8+ const { cardAssets} = require ( '../../services/assets-minification' ) ;
9+ const themeEngine = require ( '../../services/theme-engine' ) ;
10+ const settingsCache = require ( '../../../shared/settings-cache' ) ;
811
912const messages = {
1013 imageNotFound : 'Image not found' ,
@@ -99,7 +102,6 @@ function createPublicFileMiddleware(location, file, mime, maxAge, options = {})
99102 } ;
100103}
101104
102- // ### servePublicFile Middleware
103105// Handles requests to robots.txt and favicon.ico (and caches them)
104106function servePublicFile ( location , file , type , maxAge , options = { } ) {
105107 const publicFileMiddleware = createPublicFileMiddleware ( location , file , type , maxAge , options ) ;
@@ -113,6 +115,72 @@ function servePublicFile(location, file, type, maxAge, options = {}) {
113115 } ;
114116}
115117
116- module . exports = servePublicFile ;
118+ // Handles requests to public static files served by Ghost
119+ function servePublicFiles ( siteApp ) {
120+ // Serve sitemap.xsl file if not found in theme
121+ const defaultSitemapXslMiddleware = createPublicFileMiddleware ( 'static' , 'sitemap.xsl' , 'text/xsl' , config . get ( 'caching:sitemapXSL:maxAge' ) ) ;
122+ siteApp . get ( '/sitemap.xsl' , function serveSitemapXsl ( req , res , next ) {
123+ const activeTheme = themeEngine . getActive ( ) ;
124+ if ( ! activeTheme ) {
125+ return defaultSitemapXslMiddleware ( req , res , next ) ;
126+ }
127+
128+ const themeSitemapXslPath = path . join ( activeTheme . path , 'sitemap.xsl' ) ;
129+ fs . access ( themeSitemapXslPath , fs . constants . R_OK , ( err ) => {
130+ if ( err ) {
131+ // Theme doesn't have sitemap.xsl, serve default
132+ return defaultSitemapXslMiddleware ( req , res , next ) ;
133+ }
134+ // Theme has sitemap.xsl, let staticTheme middleware handle it
135+ return next ( ) ;
136+ } ) ;
137+ } ) ;
138+
139+ // Serve stylesheets for default templates
140+ siteApp . get ( '/public/ghost.css' , createPublicFileMiddleware ( 'static' , 'public/ghost.css' , 'text/css' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
141+ siteApp . get ( '/public/ghost.min.css' , createPublicFileMiddleware ( 'static' , 'public/ghost.min.css' , 'text/css' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
142+
143+ // Traffic analytics tracking script
144+ siteApp . get ( '/public/ghost-stats.min.js' , createPublicFileMiddleware ( 'static' , 'public/ghost-stats.min.js' , 'application/javascript' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
145+
146+ // Card assets (built on the fly)
147+ siteApp . get ( '/public/cards.min.css' , cardAssets . serveMiddleware ( ) , createPublicFileMiddleware ( 'built' , 'public/cards.min.css' , 'text/css' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
148+ siteApp . get ( '/public/cards.min.js' , cardAssets . serveMiddleware ( ) , createPublicFileMiddleware ( 'built' , 'public/cards.min.js' , 'application/javascript' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
149+
150+ // Comment counts
151+ siteApp . get ( '/public/comment-counts.min.js' , createPublicFileMiddleware ( 'static' , 'public/comment-counts.min.js' , 'application/javascript' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
152+
153+ // Member attribution
154+ siteApp . get ( '/public/member-attribution.min.js' , createPublicFileMiddleware ( 'static' , 'public/member-attribution.min.js' , 'application/javascript' , config . get ( 'caching:publicAssets:maxAge' ) ) ) ;
155+
156+ // Recommendations well-known
157+ siteApp . get ( '/.well-known/recommendations.json' , createPublicFileMiddleware ( 'built' , '.well-known/recommendations.json' , 'application/json' , config . get ( 'caching:publicAssets:maxAge' ) , { disableServerCache : true } ) ) ;
158+
159+ // Serve robots.txt if not found in theme (and blog is not private)
160+ const defaultRobotsTxtMiddleware = createPublicFileMiddleware ( 'static' , 'robots.txt' , 'text/plain' , config . get ( 'caching:robotstxt:maxAge' ) ) ;
161+ siteApp . get ( '/robots.txt' , function serveRobotsTxt ( req , res , next ) {
162+ // If private blogging is enabled, let filterPrivateRoutes handle it
163+ if ( settingsCache . get ( 'is_private' ) ) {
164+ return next ( ) ;
165+ }
166+
167+ const activeTheme = themeEngine . getActive ( ) ;
168+ if ( ! activeTheme ) {
169+ return defaultRobotsTxtMiddleware ( req , res , next ) ;
170+ }
171+
172+ const themeRobotsTxtPath = path . join ( activeTheme . path , 'robots.txt' ) ;
173+ fs . access ( themeRobotsTxtPath , fs . constants . R_OK , ( err ) => {
174+ if ( err ) {
175+ // Theme doesn't have robots.txt, serve default
176+ return defaultRobotsTxtMiddleware ( req , res , next ) ;
177+ }
178+ // Theme has robots.txt, let staticTheme middleware handle it
179+ return next ( ) ;
180+ } ) ;
181+ } ) ;
182+ }
183+
184+ module . exports = servePublicFiles ;
117185module . exports . servePublicFile = servePublicFile ;
118186module . exports . createPublicFileMiddleware = createPublicFileMiddleware ;
0 commit comments