mirror of https://github.com/ghostfolio/ghostfolio
				
				
			
							committed by
							
								 GitHub
								GitHub
							
						
					
				
				 10 changed files with 151 additions and 335 deletions
			
			
		| @ -1,232 +0,0 @@ | |||||
| import * as fs from 'fs'; |  | ||||
| import * as path from 'path'; |  | ||||
| 
 |  | ||||
| import { environment } from '@ghostfolio/api/environments/environment'; |  | ||||
| import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |  | ||||
| import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; |  | ||||
| import { DATE_FORMAT, interpolate } from '@ghostfolio/common/helper'; |  | ||||
| import { Injectable, NestMiddleware } from '@nestjs/common'; |  | ||||
| import { format } from 'date-fns'; |  | ||||
| import { NextFunction, Request, Response } from 'express'; |  | ||||
| 
 |  | ||||
| @Injectable() |  | ||||
| export class FrontendMiddleware implements NestMiddleware { |  | ||||
|   public indexHtmlDe = ''; |  | ||||
|   public indexHtmlEn = ''; |  | ||||
|   public indexHtmlEs = ''; |  | ||||
|   public indexHtmlFr = ''; |  | ||||
|   public indexHtmlIt = ''; |  | ||||
|   public indexHtmlNl = ''; |  | ||||
|   public indexHtmlPt = ''; |  | ||||
| 
 |  | ||||
|   private static readonly DEFAULT_DESCRIPTION = |  | ||||
|     'Ghostfolio is a personal finance dashboard to keep track of your assets like stocks, ETFs or cryptocurrencies across multiple platforms.'; |  | ||||
| 
 |  | ||||
|   public constructor( |  | ||||
|     private readonly configurationService: ConfigurationService |  | ||||
|   ) { |  | ||||
|     try { |  | ||||
|       this.indexHtmlDe = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('de'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlEn = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile(DEFAULT_LANGUAGE_CODE), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlEs = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('es'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlFr = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('fr'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlIt = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('it'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlNl = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('nl'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|       this.indexHtmlPt = fs.readFileSync( |  | ||||
|         this.getPathOfIndexHtmlFile('pt'), |  | ||||
|         'utf8' |  | ||||
|       ); |  | ||||
|     } catch {} |  | ||||
|   } |  | ||||
| 
 |  | ||||
|   public use(request: Request, response: Response, next: NextFunction) { |  | ||||
|     const currentDate = format(new Date(), DATE_FORMAT); |  | ||||
|     let featureGraphicPath = 'assets/cover.png'; |  | ||||
|     let title = 'Ghostfolio – Open Source Wealth Management Software'; |  | ||||
| 
 |  | ||||
|     if (request.path.startsWith('/en/blog/2022/08/500-stars-on-github')) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/500-stars-on-github.jpg'; |  | ||||
|       title = `500 Stars - ${title}`; |  | ||||
|     } else if (request.path.startsWith('/en/blog/2022/10/hacktoberfest-2022')) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/hacktoberfest-2022.png'; |  | ||||
|       title = `Hacktoberfest 2022 - ${title}`; |  | ||||
|     } else if (request.path.startsWith('/en/blog/2022/11/black-friday-2022')) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/black-friday-2022.jpg'; |  | ||||
|       title = `Black Friday 2022 - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith( |  | ||||
|         '/en/blog/2022/12/the-importance-of-tracking-your-personal-finances' |  | ||||
|       ) |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/20221226.jpg'; |  | ||||
|       title = `The importance of tracking your personal finances - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith( |  | ||||
|         '/de/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt' |  | ||||
|       ) |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/ghostfolio-x-sackgeld.png'; |  | ||||
|       title = `Ghostfolio auf Sackgeld.com vorgestellt - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith('/en/blog/2023/02/ghostfolio-meets-umbrel') |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/ghostfolio-x-umbrel.png'; |  | ||||
|       title = `Ghostfolio meets Umbrel - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith( |  | ||||
|         '/en/blog/2023/03/ghostfolio-reaches-1000-stars-on-github' |  | ||||
|       ) |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/1000-stars-on-github.jpg'; |  | ||||
|       title = `Ghostfolio reaches 1’000 Stars on GitHub - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith( |  | ||||
|         '/en/blog/2023/05/unlock-your-financial-potential-with-ghostfolio' |  | ||||
|       ) |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/20230520.jpg'; |  | ||||
|       title = `Unlock your Financial Potential with Ghostfolio - ${title}`; |  | ||||
|     } else if ( |  | ||||
|       request.path.startsWith('/en/blog/2023/07/exploring-the-path-to-fire') |  | ||||
|     ) { |  | ||||
|       featureGraphicPath = 'assets/images/blog/20230701.jpg'; |  | ||||
|       title = `Exploring the Path to FIRE - ${title}`; |  | ||||
|     } |  | ||||
| 
 |  | ||||
|     if ( |  | ||||
|       request.path.startsWith('/api/') || |  | ||||
|       this.isFileRequest(request.url) || |  | ||||
|       !environment.production |  | ||||
|     ) { |  | ||||
|       // Skip
 |  | ||||
|       next(); |  | ||||
|     } else if (request.path === '/de' || request.path.startsWith('/de/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlDe, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Mit dem Finanz-Dashboard Ghostfolio können Sie Ihr Vermögen in Form von Aktien, ETFs oder Kryptowährungen verteilt über mehrere Finanzinstitute überwachen.', |  | ||||
|           languageCode: 'de', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else if (request.path === '/es' || request.path.startsWith('/es/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlEs, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Ghostfolio es un dashboard de finanzas personales para hacer un seguimiento de tus activos como acciones, ETFs o criptodivisas a través de múltiples plataformas.', |  | ||||
|           languageCode: 'es', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else if (request.path === '/fr' || request.path.startsWith('/fr/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlFr, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Ghostfolio est un dashboard de finances personnelles qui permet de suivre vos actifs comme les actions, les ETF ou les crypto-monnaies sur plusieurs plateformes.', |  | ||||
|           languageCode: 'fr', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else if (request.path === '/it' || request.path.startsWith('/it/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlIt, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Ghostfolio è un dashboard di finanza personale per tenere traccia delle vostre attività come azioni, ETF o criptovalute su più piattaforme.', |  | ||||
|           languageCode: 'it', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else if (request.path === '/nl' || request.path.startsWith('/nl/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlNl, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Ghostfolio is een persoonlijk financieel dashboard om uw activa zoals aandelen, ETF’s of cryptocurrencies over meerdere platforms bij te houden.', |  | ||||
|           languageCode: 'nl', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else if (request.path === '/pt' || request.path.startsWith('/pt/')) { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlPt, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: |  | ||||
|             'Ghostfolio é um dashboard de finanças pessoais para acompanhar os seus activos como acções, ETFs ou criptomoedas em múltiplas plataformas.', |  | ||||
|           languageCode: 'pt', |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } else { |  | ||||
|       response.send( |  | ||||
|         interpolate(this.indexHtmlEn, { |  | ||||
|           currentDate, |  | ||||
|           featureGraphicPath, |  | ||||
|           title, |  | ||||
|           description: FrontendMiddleware.DEFAULT_DESCRIPTION, |  | ||||
|           languageCode: DEFAULT_LANGUAGE_CODE, |  | ||||
|           path: request.path, |  | ||||
|           rootUrl: this.configurationService.get('ROOT_URL') |  | ||||
|         }) |  | ||||
|       ); |  | ||||
|     } |  | ||||
|   } |  | ||||
| 
 |  | ||||
|   private getPathOfIndexHtmlFile(aLocale: string) { |  | ||||
|     return path.join(__dirname, '..', 'client', aLocale, 'index.html'); |  | ||||
|   } |  | ||||
| 
 |  | ||||
|   private isFileRequest(filename: string) { |  | ||||
|     if (filename === '/assets/LICENSE') { |  | ||||
|       return true; |  | ||||
|     } else if ( |  | ||||
|       filename.includes('auth/ey') || |  | ||||
|       filename.includes( |  | ||||
|         'personal-finance-tools/open-source-alternative-to-markets.sh' |  | ||||
|       ) |  | ||||
|     ) { |  | ||||
|       return false; |  | ||||
|     } |  | ||||
| 
 |  | ||||
|     return filename.split('.').pop() !== filename; |  | ||||
|   } |  | ||||
| } |  | ||||
| @ -0,0 +1,128 @@ | |||||
|  | import * as fs from 'fs'; | ||||
|  | import { join } from 'path'; | ||||
|  | 
 | ||||
|  | import { environment } from '@ghostfolio/api/environments/environment'; | ||||
|  | import { | ||||
|  |   DEFAULT_LANGUAGE_CODE, | ||||
|  |   DEFAULT_ROOT_URL, | ||||
|  |   SUPPORTED_LANGUAGE_CODES | ||||
|  | } from '@ghostfolio/common/config'; | ||||
|  | import { DATE_FORMAT, interpolate } from '@ghostfolio/common/helper'; | ||||
|  | import { format } from 'date-fns'; | ||||
|  | import { NextFunction, Request, Response } from 'express'; | ||||
|  | 
 | ||||
|  | const descriptions = { | ||||
|  |   de: 'Mit dem Finanz-Dashboard Ghostfolio können Sie Ihr Vermögen in Form von Aktien, ETFs oder Kryptowährungen verteilt über mehrere Finanzinstitute überwachen.', | ||||
|  |   en: 'Ghostfolio is a personal finance dashboard to keep track of your assets like stocks, ETFs or cryptocurrencies across multiple platforms.', | ||||
|  |   es: 'Ghostfolio es un dashboard de finanzas personales para hacer un seguimiento de tus activos como acciones, ETFs o criptodivisas a través de múltiples plataformas.', | ||||
|  |   fr: 'Ghostfolio est un dashboard de finances personnelles qui permet de suivre vos actifs comme les actions, les ETF ou les crypto-monnaies sur plusieurs plateformes.', | ||||
|  |   it: 'Ghostfolio è un dashboard di finanza personale per tenere traccia delle vostre attività come azioni, ETF o criptovalute su più piattaforme.', | ||||
|  |   nl: 'Ghostfolio is een persoonlijk financieel dashboard om uw activa zoals aandelen, ETF’s of cryptocurrencies over meerdere platforms bij te houden.', | ||||
|  |   pt: 'Ghostfolio é um dashboard de finanças pessoais para acompanhar os seus activos como acções, ETFs ou criptomoedas em múltiplas plataformas.' | ||||
|  | }; | ||||
|  | 
 | ||||
|  | const title = 'Ghostfolio – Open Source Wealth Management Software'; | ||||
|  | const titleShort = 'Ghostfolio'; | ||||
|  | 
 | ||||
|  | let indexHtmlMap: { [languageCode: string]: string } = {}; | ||||
|  | 
 | ||||
|  | try { | ||||
|  |   indexHtmlMap = SUPPORTED_LANGUAGE_CODES.reduce( | ||||
|  |     (map, languageCode) => ({ | ||||
|  |       ...map, | ||||
|  |       [languageCode]: fs.readFileSync( | ||||
|  |         join(__dirname, '..', 'client', languageCode, 'index.html'), | ||||
|  |         'utf8' | ||||
|  |       ) | ||||
|  |     }), | ||||
|  |     {} | ||||
|  |   ); | ||||
|  | } catch {} | ||||
|  | 
 | ||||
|  | const locales = { | ||||
|  |   '/de/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/ghostfolio-x-sackgeld.png', | ||||
|  |     title: `Ghostfolio auf Sackgeld.com vorgestellt - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2022/08/500-stars-on-github': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/500-stars-on-github.jpg', | ||||
|  |     title: `500 Stars - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2022/10/hacktoberfest-2022': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/hacktoberfest-2022.png', | ||||
|  |     title: `Hacktoberfest 2022 - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2022/12/the-importance-of-tracking-your-personal-finances': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/20221226.jpg', | ||||
|  |     title: `The importance of tracking your personal finances - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2023/02/ghostfolio-meets-umbrel': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/ghostfolio-x-umbrel.png', | ||||
|  |     title: `Ghostfolio meets Umbrel - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2023/03/ghostfolio-reaches-1000-stars-on-github': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/1000-stars-on-github.jpg', | ||||
|  |     title: `Ghostfolio reaches 1’000 Stars on GitHub - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2023/05/unlock-your-financial-potential-with-ghostfolio': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/20230520.jpg', | ||||
|  |     title: `Unlock your Financial Potential with Ghostfolio - ${titleShort}` | ||||
|  |   }, | ||||
|  |   '/en/blog/2023/07/exploring-the-path-to-fire': { | ||||
|  |     featureGraphicPath: 'assets/images/blog/20230701.jpg', | ||||
|  |     title: `Exploring the Path to FIRE - ${titleShort}` | ||||
|  |   } | ||||
|  | }; | ||||
|  | 
 | ||||
|  | const isFileRequest = (filename: string) => { | ||||
|  |   if (filename === '/assets/LICENSE') { | ||||
|  |     return true; | ||||
|  |   } else if ( | ||||
|  |     filename.includes('auth/ey') || | ||||
|  |     filename.includes( | ||||
|  |       'personal-finance-tools/open-source-alternative-to-markets.sh' | ||||
|  |     ) | ||||
|  |   ) { | ||||
|  |     return false; | ||||
|  |   } | ||||
|  | 
 | ||||
|  |   return filename.split('.').pop() !== filename; | ||||
|  | }; | ||||
|  | 
 | ||||
|  | export const HtmlTemplateMiddleware = async ( | ||||
|  |   request: Request, | ||||
|  |   response: Response, | ||||
|  |   next: NextFunction | ||||
|  | ) => { | ||||
|  |   const path = request.originalUrl.replace(/\/$/, ''); | ||||
|  |   let languageCode = path.substr(1, 2); | ||||
|  | 
 | ||||
|  |   if (!SUPPORTED_LANGUAGE_CODES.includes(languageCode)) { | ||||
|  |     languageCode = DEFAULT_LANGUAGE_CODE; | ||||
|  |   } | ||||
|  | 
 | ||||
|  |   const currentDate = format(new Date(), DATE_FORMAT); | ||||
|  |   const rootUrl = process.env.ROOT_URL || DEFAULT_ROOT_URL; | ||||
|  | 
 | ||||
|  |   if ( | ||||
|  |     path.startsWith('/api/') || | ||||
|  |     isFileRequest(path) || | ||||
|  |     !environment.production | ||||
|  |   ) { | ||||
|  |     // Skip
 | ||||
|  |     next(); | ||||
|  |   } else { | ||||
|  |     const indexHtml = interpolate(indexHtmlMap[languageCode], { | ||||
|  |       currentDate, | ||||
|  |       languageCode, | ||||
|  |       path, | ||||
|  |       rootUrl, | ||||
|  |       description: descriptions[languageCode], | ||||
|  |       featureGraphicPath: | ||||
|  |         locales[path]?.featureGraphicPath ?? 'assets/cover.png', | ||||
|  |       title: locales[path]?.title ?? title | ||||
|  |     }); | ||||
|  | 
 | ||||
|  |     return response.send(indexHtml); | ||||
|  |   } | ||||
|  | }; | ||||
| @ -1,63 +0,0 @@ | |||||
| <!DOCTYPE html> |  | ||||
| <html class="h-100 position-relative" lang="${languageCode}"> |  | ||||
|   <head> |  | ||||
|     <title>${title}</title> |  | ||||
|     <base href="/" /> |  | ||||
|     <meta charset="utf-8" /> |  | ||||
|     <meta content="yes" name="apple-mobile-web-app-capable" /> |  | ||||
|     <meta content="${description}" name="description" /> |  | ||||
|     <meta |  | ||||
|       content="app, asset, cryptocurrency, dashboard, etf, finance, management, performance, portfolio, software, stock, trading, wealth, web3" |  | ||||
|       name="keywords" |  | ||||
|     /> |  | ||||
|     <meta content="yes" name="mobile-web-app-capable" /> |  | ||||
|     <meta content="summary_large_image" name="twitter:card" /> |  | ||||
|     <meta |  | ||||
|       content="Ghostfolio is a personal finance dashboard to keep track of your assets like stocks, ETFs or cryptocurrencies" |  | ||||
|       name="twitter:description" |  | ||||
|     /> |  | ||||
|     <meta content="${rootUrl}/${featureGraphicPath}" name="twitter:image" /> |  | ||||
|     <meta content="${title}" name="twitter:title" /> |  | ||||
|     <meta |  | ||||
|       content="initial-scale=1, viewport-fit=cover, width=device-width" |  | ||||
|       name="viewport" |  | ||||
|     /> |  | ||||
|     <meta content="#FFFFFF" name="theme-color" /> |  | ||||
|     <meta content="" property="og:description" /> |  | ||||
|     <meta content="${title}" property="og:title" /> |  | ||||
|     <meta content="website" property="og:type" /> |  | ||||
|     <meta content="${rootUrl}${path}" property="og:url" /> |  | ||||
|     <meta content="${rootUrl}/${featureGraphicPath}" property="og:image" /> |  | ||||
|     <meta content="${currentDate}T00:00:00+00:00" property="og:updated_time" /> |  | ||||
|     <meta content="${title}" property="og:site_name" /> |  | ||||
| 
 |  | ||||
|     <link |  | ||||
|       href="../assets/apple-touch-icon.png" |  | ||||
|       rel="apple-touch-icon" |  | ||||
|       sizes="180x180" |  | ||||
|     /> |  | ||||
|     <link |  | ||||
|       href="../assets/favicon-32x32.png" |  | ||||
|       rel="icon" |  | ||||
|       sizes="32x32" |  | ||||
|       type="image/png" |  | ||||
|     /> |  | ||||
|     <link |  | ||||
|       href="../assets/favicon-16x16.png" |  | ||||
|       rel="icon" |  | ||||
|       sizes="16x16" |  | ||||
|       type="image/png" |  | ||||
|     /> |  | ||||
|     <link href="../assets/site.webmanifest" rel="manifest" /> |  | ||||
|   </head> |  | ||||
|   <body> |  | ||||
|     <gf-root></gf-root> |  | ||||
| 
 |  | ||||
|     <script src="../ionicons/ionicons.esm.js" type="module"></script> |  | ||||
|     <script nomodule="" src="ionicons.js"></script> |  | ||||
| 
 |  | ||||
|     <noscript |  | ||||
|       >Please enable JavaScript to continue using this application.</noscript |  | ||||
|     > |  | ||||
|   </body> |  | ||||
| </html> |  | ||||
					Loading…
					
					
				
		Reference in new issue