Feature/setup open startup page (#1967)
* Setup Open Startup page * Update changelog
This commit is contained in:
parent
302339e1cd
commit
edd690850c
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
|
||||
- Added a connection timeout to the environment variable `DATABASE_URL`
|
||||
- Introduced the _Open Startup_ (`/open`) page with aggregated key metrics including uptime
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
||||
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
|
||||
import {
|
||||
PROPERTY_BETTER_UPTIME_MONITOR_ID,
|
||||
PROPERTY_COUNTRIES_OF_SUBSCRIBERS,
|
||||
PROPERTY_DEMO_USER_ID,
|
||||
PROPERTY_IS_READ_ONLY_MODE,
|
||||
@ -115,19 +116,28 @@ export class InfoService {
|
||||
globalPermissions.push(permissions.createUserAccount);
|
||||
}
|
||||
|
||||
const [benchmarks, demoAuthToken, statistics, subscriptions, tags] =
|
||||
await Promise.all([
|
||||
this.benchmarkService.getBenchmarkAssetProfiles(),
|
||||
this.getDemoAuthToken(),
|
||||
this.getStatistics(),
|
||||
this.getSubscriptions(),
|
||||
this.tagService.get()
|
||||
]);
|
||||
|
||||
return {
|
||||
...info,
|
||||
benchmarks,
|
||||
demoAuthToken,
|
||||
globalPermissions,
|
||||
isReadOnlyMode,
|
||||
platforms,
|
||||
statistics,
|
||||
subscriptions,
|
||||
systemMessage,
|
||||
tags,
|
||||
baseCurrency: this.configurationService.get('BASE_CURRENCY'),
|
||||
benchmarks: await this.benchmarkService.getBenchmarkAssetProfiles(),
|
||||
currencies: this.exchangeRateDataService.getCurrencies(),
|
||||
demoAuthToken: await this.getDemoAuthToken(),
|
||||
statistics: await this.getStatistics(),
|
||||
subscriptions: await this.getSubscriptions(),
|
||||
tags: await this.tagService.get()
|
||||
currencies: this.exchangeRateDataService.getCurrencies()
|
||||
};
|
||||
}
|
||||
|
||||
@ -291,6 +301,7 @@ export class InfoService {
|
||||
const gitHubContributors = await this.countGitHubContributors();
|
||||
const gitHubStargazers = await this.countGitHubStargazers();
|
||||
const slackCommunityUsers = await this.countSlackCommunityUsers();
|
||||
const uptime = await this.getUptime();
|
||||
|
||||
statistics = {
|
||||
activeUsers1d,
|
||||
@ -299,7 +310,8 @@ export class InfoService {
|
||||
gitHubContributors,
|
||||
gitHubStargazers,
|
||||
newUsers30d,
|
||||
slackCommunityUsers
|
||||
slackCommunityUsers,
|
||||
uptime
|
||||
};
|
||||
|
||||
await this.redisCacheService.set(
|
||||
@ -323,4 +335,33 @@ export class InfoService {
|
||||
|
||||
return JSON.parse(stripeConfig.value);
|
||||
}
|
||||
|
||||
private async getUptime(): Promise<number> {
|
||||
{
|
||||
try {
|
||||
const monitorId = (await this.propertyService.getByKey(
|
||||
PROPERTY_BETTER_UPTIME_MONITOR_ID
|
||||
)) as string;
|
||||
|
||||
const get = bent(
|
||||
`https://betteruptime.com/api/v2/monitors/${monitorId}/sla`,
|
||||
'GET',
|
||||
'json',
|
||||
200,
|
||||
{
|
||||
Authorization: `Bearer ${this.configurationService.get(
|
||||
'BETTER_UPTIME_API_KEY'
|
||||
)}`
|
||||
}
|
||||
);
|
||||
|
||||
const { data } = await get();
|
||||
return data.attributes.availability / 100;
|
||||
} catch (error) {
|
||||
Logger.error(error, 'InfoService');
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ export class ConfigurationService {
|
||||
choices: ['AUD', 'CAD', 'CNY', 'EUR', 'GBP', 'JPY', 'RUB', 'USD'],
|
||||
default: 'USD'
|
||||
}),
|
||||
BETTER_UPTIME_API_KEY: str({ default: '' }),
|
||||
CACHE_TTL: num({ default: 1 }),
|
||||
DATA_SOURCE_EXCHANGE_RATES: str({ default: DataSource.YAHOO }),
|
||||
DATA_SOURCE_IMPORT: str({ default: DataSource.YAHOO }),
|
||||
|
@ -4,6 +4,7 @@ export interface Environment extends CleanedEnvAccessors {
|
||||
ACCESS_TOKEN_SALT: string;
|
||||
ALPHA_VANTAGE_API_KEY: string;
|
||||
BASE_CURRENCY: string;
|
||||
BETTER_UPTIME_API_KEY: string;
|
||||
CACHE_TTL: number;
|
||||
DATA_SOURCE_EXCHANGE_RATES: string;
|
||||
DATA_SOURCE_IMPORT: string;
|
||||
|
@ -166,6 +166,11 @@ const routes: Routes = [
|
||||
(m) => m.MarketsPageModule
|
||||
)
|
||||
},
|
||||
{
|
||||
path: 'open',
|
||||
loadChildren: () =>
|
||||
import('./pages/open/open-page.module').then((m) => m.OpenPageModule)
|
||||
},
|
||||
{
|
||||
path: 'p',
|
||||
loadChildren: () =>
|
||||
|
@ -24,6 +24,7 @@ export class AuthGuard implements CanActivate {
|
||||
'/faq',
|
||||
'/features',
|
||||
'/markets',
|
||||
'/open',
|
||||
'/p',
|
||||
'/pricing',
|
||||
'/register',
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { environment } from '@ghostfolio/api/environments/environment';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { Statistics } from '@ghostfolio/common/interfaces/statistics.interface';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { environment } from '../../../environments/environment';
|
||||
|
||||
@Component({
|
||||
host: { class: 'page' },
|
||||
selector: 'gf-about-page',
|
||||
@ -16,6 +16,7 @@ import { environment } from '../../../environments/environment';
|
||||
templateUrl: './about-page.html'
|
||||
})
|
||||
export class AboutPageComponent implements OnDestroy, OnInit {
|
||||
public defaultLanguageCode = DEFAULT_LANGUAGE_CODE;
|
||||
public hasPermissionForBlog: boolean;
|
||||
public hasPermissionForStatistics: boolean;
|
||||
public hasPermissionForSubscription: boolean;
|
||||
|
@ -6,9 +6,12 @@
|
||||
<p>
|
||||
Ghostfolio is a lightweight wealth management application for
|
||||
individuals to keep track of stocks, ETFs or cryptocurrencies and make
|
||||
solid, data-driven investment decisions. The source code is fully
|
||||
available as open source software (OSS). The project has been
|
||||
initiated by
|
||||
solid, data-driven investment decisions. We share aggregated
|
||||
<a href="https://ghostfol.io/{{ defaultLanguageCode }}/open"
|
||||
>key metrics</a
|
||||
>
|
||||
of our platform’s performance and the source code is fully available
|
||||
as open source software (OSS). The project has been initiated by
|
||||
<a href="https://dotsilver.ch" title="Website of Thomas Kaul"
|
||||
>Thomas Kaul</a
|
||||
>
|
||||
|
20
apps/client/src/app/pages/open/open-page-routing.module.ts
Normal file
20
apps/client/src/app/pages/open/open-page-routing.module.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
|
||||
|
||||
import { OpenPageComponent } from './open-page.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
canActivate: [AuthGuard],
|
||||
component: OpenPageComponent,
|
||||
path: '',
|
||||
title: $localize`Open Startup`
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class OpenPageRoutingModule {}
|
29
apps/client/src/app/pages/open/open-page.component.ts
Normal file
29
apps/client/src/app/pages/open/open-page.component.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { Statistics } from '@ghostfolio/common/interfaces/statistics.interface';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
host: { class: 'page' },
|
||||
selector: 'gf-open-page',
|
||||
styleUrls: ['./open-page.scss'],
|
||||
templateUrl: './open-page.html'
|
||||
})
|
||||
export class OpenPageComponent implements OnDestroy, OnInit {
|
||||
public statistics: Statistics;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
public constructor(private dataService: DataService) {
|
||||
const { statistics } = this.dataService.fetchInfo();
|
||||
|
||||
this.statistics = statistics;
|
||||
}
|
||||
|
||||
public ngOnInit() {}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.unsubscribeSubject.next();
|
||||
this.unsubscribeSubject.complete();
|
||||
}
|
||||
}
|
111
apps/client/src/app/pages/open/open-page.html
Normal file
111
apps/client/src/app/pages/open/open-page.html
Normal file
@ -0,0 +1,111 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3 class="d-none d-sm-block mb-3 text-center">Open Startup</h3>
|
||||
<div class="intro-container">
|
||||
<p>
|
||||
At Ghostfolio, transparency is at the core of our values. We openly
|
||||
share aggregated key metrics of our platform’s performance and publish
|
||||
the source code as
|
||||
<a
|
||||
href="https://github.com/ghostfolio/ghostfolio"
|
||||
title="Contributors to Ghostfolio"
|
||||
>open source software</a
|
||||
>
|
||||
(OSS).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-content>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<gf-value
|
||||
size="large"
|
||||
subLabel="(Last 24 hours)"
|
||||
[value]="statistics?.activeUsers1d ?? '-'"
|
||||
>Active Users</gf-value
|
||||
>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<gf-value
|
||||
size="large"
|
||||
subLabel="(Last 30 days)"
|
||||
[value]="statistics?.newUsers30d ?? '-'"
|
||||
>New Users</gf-value
|
||||
>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<gf-value
|
||||
size="large"
|
||||
subLabel="(Last 30 days)"
|
||||
[value]="statistics?.activeUsers30d ?? '-'"
|
||||
>Active Users</gf-value
|
||||
>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<a class="d-block" href="https://ghostfolio.slack.com">
|
||||
<gf-value
|
||||
size="large"
|
||||
[value]="statistics?.slackCommunityUsers ?? '-'"
|
||||
>Users in Slack community</gf-value
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<a
|
||||
class="d-block"
|
||||
href="https://github.com/ghostfolio/ghostfolio/graphs/contributors"
|
||||
>
|
||||
<gf-value
|
||||
size="large"
|
||||
[value]="statistics?.gitHubContributors ?? '-'"
|
||||
>Contributors on GitHub</gf-value
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<a
|
||||
class="d-block"
|
||||
href="https://github.com/ghostfolio/ghostfolio/stargazers"
|
||||
>
|
||||
<gf-value
|
||||
size="large"
|
||||
[value]="statistics?.gitHubStargazers ?? '-'"
|
||||
>Stars on GitHub</gf-value
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<a
|
||||
class="d-block"
|
||||
href="https://hub.docker.com/r/ghostfolio/ghostfolio"
|
||||
>
|
||||
<gf-value
|
||||
size="large"
|
||||
[value]="statistics?.dockerHubPulls ?? '-'"
|
||||
>Pulls on Docker Hub</gf-value
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4 my-2">
|
||||
<a class="d-block" href="https://status.ghostfol.io">
|
||||
<gf-value
|
||||
size="large"
|
||||
[isPercent]="true"
|
||||
[precision]="2"
|
||||
[value]="statistics?.uptime ?? '-'"
|
||||
>Uptime</gf-value
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
14
apps/client/src/app/pages/open/open-page.module.ts
Normal file
14
apps/client/src/app/pages/open/open-page.module.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
|
||||
import { OpenPageRoutingModule } from './open-page-routing.module';
|
||||
import { OpenPageComponent } from './open-page.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [OpenPageComponent],
|
||||
imports: [CommonModule, GfValueModule, MatCardModule, OpenPageRoutingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class OpenPageModule {}
|
19
apps/client/src/app/pages/open/open-page.scss
Normal file
19
apps/client/src/app/pages/open/open-page.scss
Normal file
@ -0,0 +1,19 @@
|
||||
:host {
|
||||
color: rgb(var(--dark-primary-text));
|
||||
display: block;
|
||||
|
||||
.intro-container {
|
||||
a {
|
||||
color: rgba(var(--palette-primary-500), 1);
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
color: rgba(var(--palette-primary-300), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.is-dark-theme) {
|
||||
color: rgb(var(--light-primary-text));
|
||||
}
|
@ -6,102 +6,106 @@
|
||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
<url>
|
||||
<loc>https://ghostfol.io</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/de/blog</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/de/blog/2021/07/hallo-ghostfolio</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/de/blog/2023/01/ghostfolio-auf-sackgeld-vorgestellt</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/de/pricing</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/about</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/about/changelog</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2021/07/hello-ghostfolio</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/01/ghostfolio-first-months-in-open-source</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/07/ghostfolio-meets-internet-identity</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/07/how-do-i-get-my-finances-in-order</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/08/500-stars-on-github</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/10/hacktoberfest-2022</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/11/black-friday-2022</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2022/12/the-importance-of-tracking-your-personal-finances</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2023/02/ghostfolio-meets-umbrel</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2023/03/ghostfolio-reaches-1000-stars-on-github</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/demo</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/faq</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/features</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/markets</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/open</loc>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/pricing</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/register</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/resources</loc>
|
||||
<lastmod>2023-03-25T00:00:00+00:00</lastmod>
|
||||
<lastmod>2023-05-15T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
</urlset>
|
||||
|
@ -70,6 +70,7 @@ export const HEADER_KEY_TOKEN = 'Authorization';
|
||||
export const MAX_CHART_ITEMS = 365;
|
||||
|
||||
export const PROPERTY_BENCHMARKS = 'BENCHMARKS';
|
||||
export const PROPERTY_BETTER_UPTIME_MONITOR_ID = 'BETTER_UPTIME_MONITOR_ID';
|
||||
export const PROPERTY_COUNTRIES_OF_SUBSCRIBERS = 'COUNTRIES_OF_SUBSCRIBERS';
|
||||
export const PROPERTY_COUPONS = 'COUPONS';
|
||||
export const PROPERTY_CURRENCIES = 'CURRENCIES';
|
||||
|
@ -6,4 +6,5 @@ export interface Statistics {
|
||||
gitHubStargazers: number;
|
||||
newUsers30d: number;
|
||||
slackCommunityUsers: string;
|
||||
uptime: number;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user