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