Feature/set public stripe key dynamically (#216)

* Set public Stripe key dynamically

* Update changelog
pull/217/head
Thomas 3 years ago committed by GitHub
parent 39a76f7f40
commit 51fbc538ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the styling of the current pricing plan
- Improved the styling of the transaction type badge
- Set the public _Stripe_ key dynamically
- Upgraded `angular-material-css-vars` from version `2.0.0` to `2.1.0`
### Fixed

@ -20,6 +20,7 @@ export class InfoService {
) {}
public async get(): Promise<InfoItem> {
const info: Partial<InfoItem> = {};
const platforms = await this.prisma.platform.findMany({
orderBy: { name: 'asc' },
select: { id: true, name: true }
@ -41,9 +42,12 @@ export class InfoService {
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
globalPermissions.push(permissions.enableSubscription);
info.stripePublicKey = this.configurationService.get('STRIPE_PUBLIC_KEY');
}
return {
...info,
globalPermissions,
platforms,
currencies: Object.values(Currency),

@ -30,6 +30,7 @@ export class ConfigurationService {
REDIS_HOST: str({ default: 'localhost' }),
REDIS_PORT: port({ default: 6379 }),
ROOT_URL: str({ default: 'http://localhost:4200' }),
STRIPE_PUBLIC_KEY: str({ default: '' }),
STRIPE_SECRET_KEY: str({ default: '' }),
WEB_AUTH_RP_ID: host({ default: 'localhost' })
});

@ -20,6 +20,7 @@ export interface Environment extends CleanedEnvAccessors {
REDIS_HOST: string;
REDIS_PORT: number;
ROOT_URL: string;
STRIPE_PUBLIC_KEY: string;
STRIPE_SECRET_KEY: string;
WEB_AUTH_RP_ID: string;
}

@ -56,13 +56,6 @@ export class AppComponent implements OnDestroy, OnInit {
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((info) => {
this.info = info;
});
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe(() => {
@ -70,6 +63,8 @@ export class AppComponent implements OnDestroy, OnInit {
const urlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
const urlSegments = urlSegmentGroup.segments;
this.currentRoute = urlSegments[0].path;
this.info = this.dataService.fetchInfo();
});
this.userService.stateChanged

@ -15,7 +15,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialCssVarsModule } from 'angular-material-css-vars';
import { MarkdownModule } from 'ngx-markdown';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { NgxStripeModule } from 'ngx-stripe';
import { NgxStripeModule, STRIPE_PUBLISHABLE_KEY } from 'ngx-stripe';
import { environment } from '../environments/environment';
import { CustomDateAdapter } from './adapter/custom-date-adapter';
@ -27,6 +27,10 @@ import { authInterceptorProviders } from './core/auth.interceptor';
import { httpResponseInterceptorProviders } from './core/http-response.interceptor';
import { LanguageService } from './core/language.service';
export function NgxStripeFactory(): string {
return environment.stripePublicKey;
}
@NgModule({
declarations: [AppComponent],
imports: [
@ -57,7 +61,11 @@ import { LanguageService } from './core/language.service';
useClass: CustomDateAdapter,
deps: [LanguageService, MAT_DATE_LOCALE, Platform]
},
{ provide: MAT_DATE_FORMATS, useValue: DateFormats }
{ provide: MAT_DATE_FORMATS, useValue: DateFormats },
{
provide: STRIPE_PUBLISHABLE_KEY,
useFactory: NgxStripeFactory
}
],
bootstrap: [AppComponent]
})

@ -39,19 +39,13 @@ export class AboutPageComponent implements OnDestroy, OnInit {
* Initializes the controller
*/
public ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ globalPermissions, statistics }) => {
this.hasPermissionForStatistics = hasPermission(
globalPermissions,
permissions.enableStatistics
);
this.statistics = statistics;
const { globalPermissions, statistics } = this.dataService.fetchInfo();
this.hasPermissionForStatistics = hasPermission(
globalPermissions,
permissions.enableStatistics
);
this.changeDetectorRef.markForCheck();
});
this.statistics = statistics;
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))

@ -54,24 +54,19 @@ export class AccountPageComponent implements OnDestroy, OnInit {
private userService: UserService,
public webAuthnService: WebAuthnService
) {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, globalPermissions, subscriptions }) => {
this.coupon = subscriptions?.[0]?.coupon;
this.couponId = subscriptions?.[0]?.couponId;
this.currencies = currencies;
this.hasPermissionForSubscription = hasPermission(
globalPermissions,
permissions.enableSubscription
);
this.price = subscriptions?.[0]?.price;
this.priceId = subscriptions?.[0]?.priceId;
this.changeDetectorRef.markForCheck();
});
const { currencies, globalPermissions, subscriptions } =
this.dataService.fetchInfo();
this.coupon = subscriptions?.[0]?.coupon;
this.couponId = subscriptions?.[0]?.couponId;
this.currencies = currencies;
this.hasPermissionForSubscription = hasPermission(
globalPermissions,
permissions.enableSubscription
);
this.price = subscriptions?.[0]?.price;
this.priceId = subscriptions?.[0]?.priceId;
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))

@ -8,7 +8,6 @@ import {
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Currency } from '@prisma/client';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DataService } from '../../../services/data.service';
import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
@ -34,15 +33,10 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
) {}
ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, platforms }) => {
this.currencies = currencies;
this.platforms = platforms;
this.changeDetectorRef.markForCheck();
});
const { currencies, platforms } = this.dataService.fetchInfo();
this.currencies = currencies;
this.platforms = platforms;
}
public onCancel(): void {

@ -3,10 +3,8 @@ import { Router } from '@angular/router';
import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface';
import { DataService } from '@ghostfolio/client/services/data.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { format } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'gf-landing-page',
@ -34,16 +32,11 @@ export class LandingPageComponent implements OnDestroy, OnInit {
* Initializes the controller
*/
public ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ demoAuthToken }) => {
this.demoAuthToken = demoAuthToken;
const { demoAuthToken } = this.dataService.fetchInfo();
this.initializeLineChart();
this.demoAuthToken = demoAuthToken;
this.changeDetectorRef.markForCheck();
});
this.initializeLineChart();
}
public initializeLineChart() {

@ -28,15 +28,10 @@ export class PricingPageComponent implements OnDestroy, OnInit {
private dataService: DataService,
private userService: UserService
) {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ subscriptions }) => {
this.coupon = this.price = subscriptions?.[0]?.coupon;
this.price = subscriptions?.[0]?.price;
const { subscriptions } = this.dataService.fetchInfo();
this.changeDetectorRef.markForCheck();
});
this.coupon = this.price = subscriptions?.[0]?.coupon;
this.price = subscriptions?.[0]?.price;
}
/**

@ -41,18 +41,13 @@ export class RegisterPageComponent implements OnDestroy, OnInit {
* Initializes the controller
*/
public ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ demoAuthToken, globalPermissions }) => {
this.demoAuthToken = demoAuthToken;
this.hasPermissionForSocialLogin = hasPermission(
globalPermissions,
permissions.enableSocialLogin
);
const { demoAuthToken, globalPermissions } = this.dataService.fetchInfo();
this.changeDetectorRef.markForCheck();
});
this.demoAuthToken = demoAuthToken;
this.hasPermissionForSocialLogin = hasPermission(
globalPermissions,
permissions.enableSocialLogin
);
}
public async createAccount() {

@ -50,15 +50,10 @@ export class CreateOrUpdateTransactionDialog implements OnDestroy {
) {}
ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, platforms }) => {
this.currencies = currencies;
this.platforms = platforms;
const { currencies, platforms } = this.dataService.fetchInfo();
this.changeDetectorRef.markForCheck();
});
this.currencies = currencies;
this.platforms = platforms;
this.filteredLookupItems = this.searchSymbolCtrl.valueChanges.pipe(
startWith(''),

@ -10,7 +10,6 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { Order as OrderModel } from '@prisma/client';
import { environment } from 'apps/client/src/environments/environment';
import { format, parseISO } from 'date-fns';
import { DeviceDetectorService } from 'ngx-device-detector';
import { EMPTY, Subject, Subscription } from 'rxjs';
@ -72,17 +71,12 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
* Initializes the controller
*/
public ngOnInit() {
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ globalPermissions }) => {
this.hasPermissionToImportOrders = hasPermission(
globalPermissions,
permissions.enableImport
);
const { globalPermissions } = this.dataService.fetchInfo();
this.changeDetectorRef.markForCheck();
});
this.hasPermissionToImportOrders = hasPermission(
globalPermissions,
permissions.enableImport
);
this.deviceType = this.deviceService.getDeviceInfo().deviceType;

@ -29,6 +29,7 @@ import { permissions } from '@ghostfolio/common/permissions';
import { Order as OrderModel } from '@prisma/client';
import { Account as AccountModel } from '@prisma/client';
import { parseISO } from 'date-fns';
import { cloneDeep } from 'lodash';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -92,21 +93,16 @@ export class DataService {
return this.http.get<Export>('/api/export');
}
public fetchInfo() {
return this.http.get<InfoItem>('/api/info').pipe(
map((data) => {
if (
this.settingsStorageService.getSetting('utm_source') ===
'trusted-web-activity'
) {
data.globalPermissions = data.globalPermissions.filter(
(permission) => permission !== permissions.enableSubscription
);
}
public fetchInfo(): InfoItem {
const info = cloneDeep((window as any).info);
return data;
})
);
if (window.localStorage.getItem('utm_source') === 'trusted-web-activity') {
info.globalPermissions = info.globalPermissions.filter(
(permission) => permission !== permissions.enableSubscription
);
}
return info;
}
public fetchSymbolItem(aSymbol: string) {

@ -1,6 +1,6 @@
export const environment = {
lastPublish: '{BUILD_TIMESTAMP}',
production: true,
stripePublicKey: '{STRIPE_PUBLIC_KEY}',
stripePublicKey: '',
version: `v${require('../../../../package.json').version}`
};

@ -1,16 +1,33 @@
import { enableProdMode } from '@angular/core';
import { LOCALE_ID } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { InfoItem } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
(async () => {
const response = await fetch('/api/info');
const info: InfoItem = await response.json();
platformBrowserDynamic()
.bootstrapModule(AppModule, {
providers: [{ provide: LOCALE_ID, useValue: 'de-CH' }]
})
.catch((err) => console.error(err));
if (window.localStorage.getItem('utm_source') === 'trusted-web-activity') {
info.globalPermissions = info.globalPermissions.filter(
(permission) => permission !== permissions.enableSubscription
);
}
(window as any).info = info;
environment.stripePublicKey = info.stripePublicKey;
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule, {
providers: [{ provide: LOCALE_ID, useValue: 'de-CH' }]
})
.catch((err) => console.error(err));
})();

@ -14,5 +14,6 @@ export interface InfoItem {
};
platforms: { id: string; name: string }[];
statistics: Statistics;
stripePublicKey?: string;
subscriptions: Subscription[];
}

@ -16,7 +16,7 @@ const buildTimestamp = `${formatWithTwoDigits(
)}:${formatWithTwoDigits(now.getMinutes())}`;
try {
let changedFiles = replace.sync({
const changedFiles = replace.sync({
files: './dist/apps/client/main.*.js',
from: /{BUILD_TIMESTAMP}/g,
to: buildTimestamp,
@ -24,14 +24,6 @@ try {
});
console.log('Build version set: ' + buildTimestamp);
console.log(changedFiles);
changedFiles = replace.sync({
files: './dist/apps/client/main.*.js',
from: /{STRIPE_PUBLIC_KEY}/g,
to: process.env.STRIPE_PUBLIC_KEY ?? '',
allowEmptyPaths: false
});
console.log(changedFiles);
} catch (error) {
console.error('Error occurred:', error);
}

Loading…
Cancel
Save