diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e46e0f29..ce4af8b18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,17 +10,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support to drop a file in the import activities dialog +- Added a timeout to all data source requests ### Changed +- Harmonized the style of the user interface for granting and revoking public access to share the portfolio - Removed the account type from the user interface as a preparation to remove it from the `Account` database schema - Improved the logger output of the info service - Harmonized the logger output: () - Improved the language localization for Italian (`it`) - Improved the language localization for Dutch (`nl`) +- Improved the read-only mode ### Fixed +- Fixed the timeout in _EOD Historical Data_ requests - Fixed an issue with the portfolio summary caused by the language localization for Dutch (`nl`) ## 2.0.0 - 2023-09-09 @@ -2950,7 +2954,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Supported the management of additional currencies in the admin control panel - Introduced the system message -- Introduced the read only mode +- Introduced the read-only mode ### Changed diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index b1b6b9418..a45fbe634 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -8,7 +8,9 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DEFAULT_CURRENCY, - PROPERTY_CURRENCIES + PROPERTY_CURRENCIES, + PROPERTY_IS_READ_ONLY_MODE, + PROPERTY_IS_USER_SIGNUP_ENABLED } from '@ghostfolio/common/config'; import { AdminData, @@ -305,7 +307,9 @@ export class AdminService { response = await this.propertyService.delete({ key }); } - if (key === PROPERTY_CURRENCIES) { + if (key === PROPERTY_IS_READ_ONLY_MODE && value === 'true') { + await this.putSetting(PROPERTY_IS_USER_SIGNUP_ENABLED, 'false'); + } else if (key === PROPERTY_CURRENCIES) { await this.exchangeRateDataService.initialize(); } diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 3ac1cfbc1..f2c45a72b 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -8,6 +8,7 @@ import { PropertyService } from '@ghostfolio/api/services/property/property.serv import { TagService } from '@ghostfolio/api/services/tag/tag.service'; import { DEFAULT_CURRENCY, + DEFAULT_REQUEST_TIMEOUT, PROPERTY_BETTER_UPTIME_MONITOR_ID, PROPERTY_COUNTRIES_OF_SUBSCRIBERS, PROPERTY_DEMO_USER_ID, @@ -168,10 +169,18 @@ export class InfoService { private async countDockerHubPulls(): Promise { try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { pull_count } = await got( `https://hub.docker.com/v2/repositories/ghostfolio/ghostfolio`, { - headers: { 'User-Agent': 'request' } + headers: { 'User-Agent': 'request' }, + // @ts-ignore + signal: abortController.signal } ).json(); @@ -185,7 +194,16 @@ export class InfoService { private async countGitHubContributors(): Promise { try { - const { body } = await got('https://github.com/ghostfolio/ghostfolio'); + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + + const { body } = await got('https://github.com/ghostfolio/ghostfolio', { + // @ts-ignore + signal: abortController.signal + }); const $ = cheerio.load(body); @@ -203,10 +221,18 @@ export class InfoService { private async countGitHubStargazers(): Promise { try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { stargazers_count } = await got( `https://api.github.com/repos/ghostfolio/ghostfolio`, { - headers: { 'User-Agent': 'request' } + headers: { 'User-Agent': 'request' }, + // @ts-ignore + signal: abortController.signal } ).json(); @@ -323,18 +349,25 @@ export class InfoService { PROPERTY_BETTER_UPTIME_MONITOR_ID )) as string; + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { data } = await got( `https://uptime.betterstack.com/api/v2/monitors/${monitorId}/sla?from=${format( subDays(new Date(), 90), DATE_FORMAT )}&to${format(new Date(), DATE_FORMAT)}`, - { headers: { Authorization: `Bearer ${this.configurationService.get( 'BETTER_UPTIME_API_KEY' )}` - } + }, + // @ts-ignore + signal: abortController.signal } ).json(); diff --git a/apps/api/src/app/logo/logo.service.ts b/apps/api/src/app/logo/logo.service.ts index 166143a75..80ae1d6a9 100644 --- a/apps/api/src/app/logo/logo.service.ts +++ b/apps/api/src/app/logo/logo.service.ts @@ -1,4 +1,5 @@ import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config'; import { UniqueAsset } from '@ghostfolio/common/interfaces'; import { HttpException, Injectable } from '@nestjs/common'; import { DataSource } from '@prisma/client'; @@ -41,10 +42,18 @@ export class LogoService { } private getBuffer(aUrl: string) { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + return got( `https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${aUrl}&size=64`, { - headers: { 'User-Agent': 'request' } + headers: { 'User-Agent': 'request' }, + // @ts-ignore + signal: abortController.signal } ).buffer(); } diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index c5cc3c8ce..a2710bfd5 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -19,7 +19,7 @@ import { UserWithSettings } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { Prisma, Role, User } from '@prisma/client'; import { differenceInDays } from 'date-fns'; -import { sortBy } from 'lodash'; +import { sortBy, without } from 'lodash'; const crypto = require('crypto'); @@ -188,6 +188,11 @@ export class UserService { currentPermissions.push(permissions.enableSubscriptionInterstitial); } + currentPermissions = without( + currentPermissions, + permissions.createAccess + ); + // Reset benchmark user.Settings.settings.benchmark = undefined; } diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index 56594ca40..4360822f0 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -4,7 +4,10 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; +import { + DEFAULT_CURRENCY, + DEFAULT_REQUEST_TIMEOUT +} from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Granularity } from '@ghostfolio/common/types'; @@ -40,7 +43,16 @@ export class CoinGeckoService implements DataProviderInterface { }; try { - const { name } = await got(`${this.URL}/coins/${aSymbol}`).json(); + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + + const { name } = await got(`${this.URL}/coins/${aSymbol}`, { + // @ts-ignore + signal: abortController.signal + }).json(); response.name = name; } catch (error) { @@ -73,12 +85,22 @@ export class CoinGeckoService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { prices } = await got( `${ this.URL }/coins/${aSymbol}/market_chart/range?vs_currency=${DEFAULT_CURRENCY.toLowerCase()}&from=${getUnixTime( from - )}&to=${getUnixTime(to)}` + )}&to=${getUnixTime(to)}`, + { + // @ts-ignore + signal: abortController.signal + } ).json(); const result: { @@ -122,10 +144,20 @@ export class CoinGeckoService implements DataProviderInterface { } try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const response = await got( `${this.URL}/simple/price?ids=${aSymbols.join( ',' - )}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}` + )}&vs_currencies=${DEFAULT_CURRENCY.toLowerCase()}`, + { + // @ts-ignore + signal: abortController.signal + } ).json(); for (const symbol in response) { @@ -160,9 +192,16 @@ export class CoinGeckoService implements DataProviderInterface { let items: LookupItem[] = []; try { - const { coins } = await got( - `${this.URL}/search?query=${query}` - ).json(); + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + + const { coins } = await got(`${this.URL}/search?query=${query}`, { + // @ts-ignore + signal: abortController.signal + }).json(); items = coins.map(({ id: symbol, name }) => { return { diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index 07c0234b6..36eb22dad 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -1,4 +1,5 @@ import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; +import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config'; import { Country } from '@ghostfolio/common/interfaces/country.interface'; import { Sector } from '@ghostfolio/common/interfaces/sector.interface'; import { Injectable } from '@nestjs/common'; @@ -32,15 +33,35 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { return response; } + let abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const profile = await got( - `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json` + `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`, + { + // @ts-ignore + signal: abortController.signal + } ) .json() .catch(() => { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + return got( `${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol.split( '.' - )?.[0]}.json` + )?.[0]}.json`, + { + // @ts-ignore + signal: abortController.signal + } ) .json() .catch(() => { @@ -54,15 +75,35 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface { response.isin = isin; } + abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const holdings = await got( - `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json` + `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol}.json`, + { + // @ts-ignore + signal: abortController.signal + } ) .json() .catch(() => { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + return got( `${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol.split( '.' - )?.[0]}.json` + )?.[0]}.json`, + { + // @ts-ignore + signal: abortController.signal + } ) .json() .catch(() => { diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index fd8114ad6..307f6127a 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -78,6 +78,12 @@ export class EodHistoricalDataService implements DataProviderInterface { const symbol = this.convertToEodSymbol(aSymbol); try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const response = await got( `${this.URL}/eod/${symbol}?api_token=${ this.apiKey @@ -86,9 +92,8 @@ export class EodHistoricalDataService implements DataProviderInterface { DATE_FORMAT )}&period={aGranularity}`, { - timeout: { - request: DEFAULT_REQUEST_TIMEOUT - } + // @ts-ignore + signal: abortController.signal } ).json(); @@ -138,14 +143,19 @@ export class EodHistoricalDataService implements DataProviderInterface { } try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const realTimeResponse = await got( `${this.URL}/real-time/${symbols[0]}?api_token=${ this.apiKey }&fmt=json&s=${symbols.join(',')}`, { - timeout: { - request: DEFAULT_REQUEST_TIMEOUT - } + // @ts-ignore + signal: abortController.signal } ).json(); @@ -331,12 +341,17 @@ export class EodHistoricalDataService implements DataProviderInterface { let searchResult = []; try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const response = await got( `${this.URL}/search/${aQuery}?api_token=${this.apiKey}`, { - timeout: { - request: DEFAULT_REQUEST_TIMEOUT - } + // @ts-ignore + signal: abortController.signal } ).json(); diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index c5d163456..4fd1d4ebd 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -5,7 +5,10 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; +import { + DEFAULT_CURRENCY, + DEFAULT_REQUEST_TIMEOUT +} from '@ghostfolio/common/config'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; import { DataProviderInfo } from '@ghostfolio/common/interfaces'; import { Granularity } from '@ghostfolio/common/types'; @@ -63,8 +66,18 @@ export class FinancialModelingPrepService implements DataProviderInterface { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; }> { try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { historical } = await got( - `${this.URL}/historical-price-full/${aSymbol}?apikey=${this.apiKey}` + `${this.URL}/historical-price-full/${aSymbol}?apikey=${this.apiKey}`, + { + // @ts-ignore + signal: abortController.signal + } ).json(); const result: { @@ -110,8 +123,18 @@ export class FinancialModelingPrepService implements DataProviderInterface { } try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const response = await got( - `${this.URL}/quote/${aSymbols.join(',')}?apikey=${this.apiKey}` + `${this.URL}/quote/${aSymbols.join(',')}?apikey=${this.apiKey}`, + { + // @ts-ignore + signal: abortController.signal + } ).json(); for (const { price, symbol } of response) { @@ -144,8 +167,18 @@ export class FinancialModelingPrepService implements DataProviderInterface { let items: LookupItem[] = []; try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const result = await got( - `${this.URL}/search?query=${query}&apikey=${this.apiKey}` + `${this.URL}/search?query=${query}&apikey=${this.apiKey}`, + { + // @ts-ignore + signal: abortController.signal + } ).json(); items = result.map(({ currency, name, symbol }) => { diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index adf14e43f..5c84a9c92 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -6,6 +6,7 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config'; import { DATE_FORMAT, extractNumberFromString, @@ -95,7 +96,17 @@ export class ManualService implements DataProviderInterface { return {}; } - const { body } = await got(url, { headers }); + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + + const { body } = await got(url, { + headers, + // @ts-ignore + signal: abortController.signal + }); const $ = cheerio.load(body); diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index 307855aaf..7743d7805 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -5,7 +5,10 @@ import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; +import { + DEFAULT_REQUEST_TIMEOUT, + ghostfolioFearAndGreedIndexSymbol +} from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { Granularity } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; @@ -135,6 +138,12 @@ export class RapidApiService implements DataProviderInterface { oneYearAgo: { value: number; valueText: string }; }> { try { + const abortController = new AbortController(); + + setTimeout(() => { + abortController.abort(); + }, DEFAULT_REQUEST_TIMEOUT); + const { fgi } = await got( `https://fear-and-greed-index.p.rapidapi.com/v1/fgi`, { @@ -142,7 +151,9 @@ export class RapidApiService implements DataProviderInterface { useQueryString: 'true', 'x-rapidapi-host': 'fear-and-greed-index.p.rapidapi.com', 'x-rapidapi-key': this.configurationService.get('RAPID_API_API_KEY') - } + }, + // @ts-ignore + signal: abortController.signal } ).json(); diff --git a/apps/client/src/app/components/access-table/access-table.component.html b/apps/client/src/app/components/access-table/access-table.component.html index 589c67e1f..498e73bf0 100644 --- a/apps/client/src/app/components/access-table/access-table.component.html +++ b/apps/client/src/app/components/access-table/access-table.component.html @@ -1,3 +1,15 @@ + + diff --git a/apps/client/src/app/components/access-table/access-table.component.ts b/apps/client/src/app/components/access-table/access-table.component.ts index c0db8fb2f..298a02069 100644 --- a/apps/client/src/app/components/access-table/access-table.component.ts +++ b/apps/client/src/app/components/access-table/access-table.component.ts @@ -19,6 +19,7 @@ import { Access } from '@ghostfolio/common/interfaces'; }) export class AccessTableComponent implements OnChanges, OnInit { @Input() accesses: Access[]; + @Input() hasPermissionToCreateAccess = false; @Input() showActions: boolean; @Output() accessDeleted = new EventEmitter(); diff --git a/apps/client/src/app/components/access-table/access-table.module.ts b/apps/client/src/app/components/access-table/access-table.module.ts index 111f0e2f4..2ace3cfc1 100644 --- a/apps/client/src/app/components/access-table/access-table.module.ts +++ b/apps/client/src/app/components/access-table/access-table.module.ts @@ -3,13 +3,20 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatMenuModule } from '@angular/material/menu'; import { MatTableModule } from '@angular/material/table'; +import { RouterModule } from '@angular/router'; import { AccessTableComponent } from './access-table.component'; @NgModule({ declarations: [AccessTableComponent], exports: [AccessTableComponent], - imports: [CommonModule, MatButtonModule, MatMenuModule, MatTableModule], + imports: [ + CommonModule, + MatButtonModule, + MatMenuModule, + MatTableModule, + RouterModule + ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class GfPortfolioAccessTableModule {} diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts index 6f19a9b46..f7776f871 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.component.ts +++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { DataService } from '@ghostfolio/client/services/data.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { translate } from '@ghostfolio/ui/i18n'; import { StripeService } from 'ngx-stripe'; import { Subject } from 'rxjs'; @@ -17,6 +18,7 @@ export class PricingPageComponent implements OnDestroy, OnInit { public baseCurrency: string; public coupon: number; public couponId: string; + public hasPermissionToUpdateUserSettings: boolean; public importAndExportTooltipBasic = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC' ); @@ -55,6 +57,11 @@ export class PricingPageComponent implements OnDestroy, OnInit { if (state?.user) { this.user = state.user; + this.hasPermissionToUpdateUserSettings = hasPermission( + this.user.permissions, + permissions.updateUserSettings + ); + this.coupon = subscriptions?.[this.user?.subscription?.offer]?.coupon; this.couponId = subscriptions?.[this.user.subscription.offer]?.couponId; diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html index abd169a5e..1c1bb6c41 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.html +++ b/apps/client/src/app/pages/pricing/pricing-page.html @@ -333,7 +333,7 @@ >

diff --git a/apps/client/src/app/pages/user-account/user-account-page.scss b/apps/client/src/app/pages/user-account/user-account-page.scss index 6a010a85d..6dddf0e35 100644 --- a/apps/client/src/app/pages/user-account/user-account-page.scss +++ b/apps/client/src/app/pages/user-account/user-account-page.scss @@ -6,13 +6,6 @@ overflow-x: auto; } - .fab-container { - position: fixed; - right: 2rem; - bottom: 2rem; - z-index: 999; - } - .hint-text { font-size: 90%; line-height: 1.2; diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 345641f97..467986fa1 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -39,7 +39,7 @@ export const DEFAULT_CURRENCY = 'USD'; export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy'; export const DEFAULT_LANGUAGE_CODE = 'en'; export const DEFAULT_PAGE_SIZE = 50; -export const DEFAULT_REQUEST_TIMEOUT = ms('3 seconds'); +export const DEFAULT_REQUEST_TIMEOUT = ms('2 seconds'); export const DEFAULT_ROOT_URL = 'http://localhost:4200'; export const EMERGENCY_FUND_TAG_ID = '4452656d-9fa4-4bd0-ba38-70492e31d180';
Alias