Release 1.297.4 (#2209)
This commit is contained in:
parent
8ba50f2729
commit
42274917e0
@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## 1.297.3 - 2023-08-05
|
## 1.297.4 - 2023-08-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -7,11 +7,16 @@ import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-
|
|||||||
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
|
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
|
||||||
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
|
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
|
||||||
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
|
import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module';
|
||||||
|
import {
|
||||||
|
DEFAULT_LANGUAGE_CODE,
|
||||||
|
SUPPORTED_LANGUAGE_CODES
|
||||||
|
} from '@ghostfolio/common/config';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bull';
|
||||||
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { ScheduleModule } from '@nestjs/schedule';
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
|
import { StatusCodes } from 'http-status-codes';
|
||||||
|
|
||||||
import { AccessModule } from './access/access.module';
|
import { AccessModule } from './access/access.module';
|
||||||
import { AccountModule } from './account/account.module';
|
import { AccountModule } from './account/account.module';
|
||||||
@ -32,14 +37,10 @@ import { OrderModule } from './order/order.module';
|
|||||||
import { PlatformModule } from './platform/platform.module';
|
import { PlatformModule } from './platform/platform.module';
|
||||||
import { PortfolioModule } from './portfolio/portfolio.module';
|
import { PortfolioModule } from './portfolio/portfolio.module';
|
||||||
import { RedisCacheModule } from './redis-cache/redis-cache.module';
|
import { RedisCacheModule } from './redis-cache/redis-cache.module';
|
||||||
|
import { SitemapModule } from './sitemap/sitemap.module';
|
||||||
import { SubscriptionModule } from './subscription/subscription.module';
|
import { SubscriptionModule } from './subscription/subscription.module';
|
||||||
import { SymbolModule } from './symbol/symbol.module';
|
import { SymbolModule } from './symbol/symbol.module';
|
||||||
import { UserModule } from './user/user.module';
|
import { UserModule } from './user/user.module';
|
||||||
import {
|
|
||||||
DEFAULT_LANGUAGE_CODE,
|
|
||||||
SUPPORTED_LANGUAGE_CODES
|
|
||||||
} from '@ghostfolio/common/config';
|
|
||||||
import { StatusCodes } from 'http-status-codes';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -81,7 +82,7 @@ import { StatusCodes } from 'http-status-codes';
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
ServeStaticModule.forRoot({
|
ServeStaticModule.forRoot({
|
||||||
exclude: ['/api*'],
|
exclude: ['/api*', '/sitemap.xml'],
|
||||||
rootPath: join(__dirname, '..', 'client'),
|
rootPath: join(__dirname, '..', 'client'),
|
||||||
serveStaticOptions: {
|
serveStaticOptions: {
|
||||||
setHeaders: (res) => {
|
setHeaders: (res) => {
|
||||||
@ -104,6 +105,7 @@ import { StatusCodes } from 'http-status-codes';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
SitemapModule,
|
||||||
SubscriptionModule,
|
SubscriptionModule,
|
||||||
SymbolModule,
|
SymbolModule,
|
||||||
TwitterBotModule,
|
TwitterBotModule,
|
||||||
|
@ -4,7 +4,7 @@ import * as path from 'path';
|
|||||||
import { environment } from '@ghostfolio/api/environments/environment';
|
import { environment } from '@ghostfolio/api/environments/environment';
|
||||||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
|
||||||
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, interpolate } from '@ghostfolio/common/helper';
|
||||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { NextFunction, Request, Response } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
@ -18,7 +18,6 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
public indexHtmlIt = '';
|
public indexHtmlIt = '';
|
||||||
public indexHtmlNl = '';
|
public indexHtmlNl = '';
|
||||||
public indexHtmlPt = '';
|
public indexHtmlPt = '';
|
||||||
public sitemapXml = '';
|
|
||||||
|
|
||||||
private static readonly DEFAULT_DESCRIPTION =
|
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.';
|
'Ghostfolio is a personal finance dashboard to keep track of your assets like stocks, ETFs or cryptocurrencies across multiple platforms.';
|
||||||
@ -55,10 +54,6 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
this.getPathOfIndexHtmlFile('pt'),
|
this.getPathOfIndexHtmlFile('pt'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
this.sitemapXml = fs.readFileSync(
|
|
||||||
path.join(__dirname, 'assets', 'sitemap.xml'),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,16 +118,9 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
) {
|
) {
|
||||||
// Skip
|
// Skip
|
||||||
next();
|
next();
|
||||||
} else if (request.path === '/sitemap.xml') {
|
|
||||||
response.setHeader('content-type', 'application/xml');
|
|
||||||
response.send(
|
|
||||||
this.interpolate(this.sitemapXml, {
|
|
||||||
currentDate: format(getYesterday(), DATE_FORMAT)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (request.path === '/de' || request.path.startsWith('/de/')) {
|
} else if (request.path === '/de' || request.path.startsWith('/de/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlDe, {
|
interpolate(this.indexHtmlDe, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -145,7 +133,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else if (request.path === '/es' || request.path.startsWith('/es/')) {
|
} else if (request.path === '/es' || request.path.startsWith('/es/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlEs, {
|
interpolate(this.indexHtmlEs, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -158,7 +146,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else if (request.path === '/fr' || request.path.startsWith('/fr/')) {
|
} else if (request.path === '/fr' || request.path.startsWith('/fr/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlFr, {
|
interpolate(this.indexHtmlFr, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -171,7 +159,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else if (request.path === '/it' || request.path.startsWith('/it/')) {
|
} else if (request.path === '/it' || request.path.startsWith('/it/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlIt, {
|
interpolate(this.indexHtmlIt, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -184,7 +172,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else if (request.path === '/nl' || request.path.startsWith('/nl/')) {
|
} else if (request.path === '/nl' || request.path.startsWith('/nl/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlNl, {
|
interpolate(this.indexHtmlNl, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -197,7 +185,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else if (request.path === '/pt' || request.path.startsWith('/pt/')) {
|
} else if (request.path === '/pt' || request.path.startsWith('/pt/')) {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlPt, {
|
interpolate(this.indexHtmlPt, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -210,7 +198,7 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
response.send(
|
response.send(
|
||||||
this.interpolate(this.indexHtmlEn, {
|
interpolate(this.indexHtmlEn, {
|
||||||
currentDate,
|
currentDate,
|
||||||
featureGraphicPath,
|
featureGraphicPath,
|
||||||
title,
|
title,
|
||||||
@ -227,21 +215,10 @@ export class FrontendMiddleware implements NestMiddleware {
|
|||||||
return path.join(__dirname, '..', 'client', aLocale, 'index.html');
|
return path.join(__dirname, '..', 'client', aLocale, 'index.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
private interpolate(template: string, context: any) {
|
|
||||||
return template.replace(/[$]{([^}]+)}/g, (_, objectPath) => {
|
|
||||||
const properties = objectPath.split('.');
|
|
||||||
return properties.reduce(
|
|
||||||
(previous, current) => previous?.[current],
|
|
||||||
context
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private isFileRequest(filename: string) {
|
private isFileRequest(filename: string) {
|
||||||
if (filename === '/assets/LICENSE') {
|
if (filename === '/assets/LICENSE') {
|
||||||
return true;
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
filename === '/sitemap.xml' ||
|
|
||||||
filename.includes('auth/ey') ||
|
filename.includes('auth/ey') ||
|
||||||
filename.includes(
|
filename.includes(
|
||||||
'personal-finance-tools/open-source-alternative-to-markets.sh'
|
'personal-finance-tools/open-source-alternative-to-markets.sh'
|
||||||
|
36
apps/api/src/app/sitemap/sitemap.controller.ts
Normal file
36
apps/api/src/app/sitemap/sitemap.controller.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DATE_FORMAT,
|
||||||
|
getYesterday,
|
||||||
|
interpolate
|
||||||
|
} from '@ghostfolio/common/helper';
|
||||||
|
import { Controller, Get, Res, VERSION_NEUTRAL, Version } from '@nestjs/common';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
@Controller('/sitemap.xml')
|
||||||
|
export class SitemapController {
|
||||||
|
public sitemapXml = '';
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
try {
|
||||||
|
this.sitemapXml = fs.readFileSync(
|
||||||
|
path.join(__dirname, 'assets', 'sitemap.xml'),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Version(VERSION_NEUTRAL)
|
||||||
|
public async flushCache(@Res() response: Response): Promise<void> {
|
||||||
|
response.setHeader('content-type', 'application/xml');
|
||||||
|
response.send(
|
||||||
|
interpolate(this.sitemapXml, {
|
||||||
|
currentDate: format(getYesterday(), DATE_FORMAT)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
apps/api/src/app/sitemap/sitemap.module.ts
Normal file
24
apps/api/src/app/sitemap/sitemap.module.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
|
||||||
|
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
|
||||||
|
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';
|
||||||
|
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
|
||||||
|
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
|
||||||
|
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
|
||||||
|
import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module';
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { SitemapController } from './sitemap.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [SitemapController],
|
||||||
|
imports: [
|
||||||
|
ConfigurationModule,
|
||||||
|
DataGatheringModule,
|
||||||
|
DataProviderModule,
|
||||||
|
ExchangeRateDataModule,
|
||||||
|
PrismaModule,
|
||||||
|
RedisCacheModule,
|
||||||
|
SymbolProfileModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SitemapModule {}
|
@ -23,7 +23,7 @@ async function bootstrap() {
|
|||||||
defaultVersion: '1',
|
defaultVersion: '1',
|
||||||
type: VersioningType.URI
|
type: VersioningType.URI
|
||||||
});
|
});
|
||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api', { exclude: ['sitemap.xml'] });
|
||||||
app.useGlobalPipes(
|
app.useGlobalPipes(
|
||||||
new ValidationPipe({
|
new ValidationPipe({
|
||||||
forbidNonWhitelisted: true,
|
forbidNonWhitelisted: true,
|
||||||
|
@ -234,6 +234,16 @@ export function isCurrency(aSymbol = '') {
|
|||||||
return currencies[aSymbol];
|
return currencies[aSymbol];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function interpolate(template: string, context: any) {
|
||||||
|
return template.replace(/[$]{([^}]+)}/g, (_, objectPath) => {
|
||||||
|
const properties = objectPath.split('.');
|
||||||
|
return properties.reduce(
|
||||||
|
(previous, current) => previous?.[current],
|
||||||
|
context
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function resetHours(aDate: Date) {
|
export function resetHours(aDate: Date) {
|
||||||
const year = getYear(aDate);
|
const year = getYear(aDate);
|
||||||
const month = getMonth(aDate);
|
const month = getMonth(aDate);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghostfolio",
|
"name": "ghostfolio",
|
||||||
"version": "1.297.3",
|
"version": "1.297.4",
|
||||||
"homepage": "https://ghostfol.io",
|
"homepage": "https://ghostfol.io",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user