diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8e67f98..853dd9773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Added the subscription type to the users table of the admin control panel + ## 1.41.0 - 21.08.2021 ### Added diff --git a/apps/api/src/app/admin/admin.module.ts b/apps/api/src/app/admin/admin.module.ts index fa2118e17..4257f3fc5 100644 --- a/apps/api/src/app/admin/admin.module.ts +++ b/apps/api/src/app/admin/admin.module.ts @@ -1,3 +1,4 @@ +import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration.module'; import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; @@ -14,9 +15,11 @@ import { AdminService } from './admin.service'; DataGatheringModule, DataProviderModule, ExchangeRateDataModule, - PrismaModule + PrismaModule, + SubscriptionModule ], controllers: [AdminController], - providers: [AdminService] + providers: [AdminService], + exports: [AdminService] }) export class AdminModule {} diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index fab4f5d13..2d8d09fac 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -1,3 +1,5 @@ +import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; @@ -9,9 +11,11 @@ import { differenceInDays } from 'date-fns'; @Injectable() export class AdminService { public constructor( + private readonly configurationService: ConfigurationService, private readonly dataGatheringService: DataGatheringService, private readonly exchangeRateDataService: ExchangeRateDataService, - private readonly prismaService: PrismaService + private readonly prismaService: PrismaService, + private readonly subscriptionService: SubscriptionService ) {} public async get(): Promise { @@ -107,7 +111,8 @@ export class AdminService { } }, createdAt: true, - id: true + id: true, + Subscription: true }, take: 30, where: { @@ -118,16 +123,23 @@ export class AdminService { }); return usersWithAnalytics.map( - ({ _count, alias, Analytics, createdAt, id }) => { + ({ _count, alias, Analytics, createdAt, id, Subscription }) => { const daysSinceRegistration = differenceInDays(new Date(), createdAt) + 1; const engagement = Analytics.activityCount / daysSinceRegistration; + const subscription = this.configurationService.get( + 'ENABLE_FEATURE_SUBSCRIPTION' + ) + ? this.subscriptionService.getSubscription(Subscription) + : undefined; + return { alias, createdAt, engagement, id, + subscription, accountCount: _count.Account || 0, lastActivity: Analytics.updatedAt, transactionCount: _count.Order || 0 diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts index 673169307..d573f91fe 100644 --- a/apps/api/src/app/auth/auth.module.ts +++ b/apps/api/src/app/auth/auth.module.ts @@ -1,5 +1,6 @@ import { AuthDeviceService } from '@ghostfolio/api/app/auth-device/auth-device.service'; import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; +import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; @@ -17,7 +18,8 @@ import { JwtStrategy } from './jwt.strategy'; JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '180 days' } - }) + }), + SubscriptionModule ], providers: [ AuthDeviceService, diff --git a/apps/api/src/app/subscription/subscription.module.ts b/apps/api/src/app/subscription/subscription.module.ts index 34591d55a..48671550c 100644 --- a/apps/api/src/app/subscription/subscription.module.ts +++ b/apps/api/src/app/subscription/subscription.module.ts @@ -8,6 +8,7 @@ import { SubscriptionService } from './subscription.service'; @Module({ imports: [], controllers: [SubscriptionController], - providers: [ConfigurationService, PrismaService, SubscriptionService] + providers: [ConfigurationService, PrismaService, SubscriptionService], + exports: [SubscriptionService] }) export class SubscriptionModule {} diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index a5f5476ca..f735e04f4 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -1,7 +1,9 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; +import { SubscriptionType } from '@ghostfolio/common/types/subscription.type'; import { Injectable } from '@nestjs/common'; -import { addDays } from 'date-fns'; +import { Subscription } from '@prisma/client'; +import { addDays, isBefore } from 'date-fns'; import Stripe from 'stripe'; @Injectable() @@ -86,4 +88,23 @@ export class SubscriptionService { console.error(error); } } + + public getSubscription(aSubscriptions: Subscription[]) { + if (aSubscriptions.length > 0) { + const latestSubscription = aSubscriptions.reduce((a, b) => { + return new Date(a.expiresAt) > new Date(b.expiresAt) ? a : b; + }); + + return { + expiresAt: latestSubscription.expiresAt, + type: isBefore(new Date(), latestSubscription.expiresAt) + ? SubscriptionType.Premium + : SubscriptionType.Basic + }; + } else { + return { + type: SubscriptionType.Basic + }; + } + } } diff --git a/apps/api/src/app/user/user.module.ts b/apps/api/src/app/user/user.module.ts index 81f083fcf..7d2fc3d8e 100644 --- a/apps/api/src/app/user/user.module.ts +++ b/apps/api/src/app/user/user.module.ts @@ -1,3 +1,4 @@ +import { SubscriptionModule } from '@ghostfolio/api/app/subscription/subscription.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { Module } from '@nestjs/common'; @@ -11,7 +12,8 @@ import { UserService } from './user.service'; JwtModule.register({ secret: process.env.JWT_SECRET_KEY, signOptions: { expiresIn: '30 days' } - }) + }), + SubscriptionModule ], controllers: [UserController], providers: [ConfigurationService, PrismaService, UserService], diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index af40fe375..325f93284 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -1,3 +1,4 @@ +import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma.service'; import { locale } from '@ghostfolio/common/config'; @@ -6,7 +7,6 @@ import { getPermissions, permissions } from '@ghostfolio/common/permissions'; import { SubscriptionType } from '@ghostfolio/common/types/subscription.type'; import { Injectable } from '@nestjs/common'; import { Currency, Prisma, Provider, User, ViewMode } from '@prisma/client'; -import { isBefore } from 'date-fns'; import { UserSettingsParams } from './interfaces/user-settings-params.interface'; import { UserSettings } from './interfaces/user-settings.interface'; @@ -19,7 +19,8 @@ export class UserService { public constructor( private readonly configurationService: ConfigurationService, - private readonly prismaService: PrismaService + private readonly prismaService: PrismaService, + private readonly subscriptionService: SubscriptionService ) {} public async getUser({ @@ -98,24 +99,9 @@ export class UserService { } if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { - if (userFromDatabase?.Subscription?.length > 0) { - const latestSubscription = userFromDatabase.Subscription.reduce( - (a, b) => { - return new Date(a.expiresAt) > new Date(b.expiresAt) ? a : b; - } - ); - - user.subscription = { - expiresAt: latestSubscription.expiresAt, - type: isBefore(new Date(), latestSubscription.expiresAt) - ? SubscriptionType.Premium - : SubscriptionType.Basic - }; - } else { - user.subscription = { - type: SubscriptionType.Basic - }; - } + user.subscription = this.subscriptionService.getSubscription( + userFromDatabase?.Subscription + ); if (user.subscription.type === SubscriptionType.Basic) { user.permissions = user.permissions.filter((permission) => { diff --git a/apps/client/src/app/pages/admin/admin-page.html b/apps/client/src/app/pages/admin/admin-page.html index 1ba10e8c0..a5b1c2e79 100644 --- a/apps/client/src/app/pages/admin/admin-page.html +++ b/apps/client/src/app/pages/admin/admin-page.html @@ -116,7 +116,20 @@ {{ i + 1 }} - {{ userItem.alias || userItem.id }} +
+ {{ userItem.alias || userItem.id }} + {{ userItem.alias || (userItem.id | slice:0:5) + + '...' }} + +
{{ formatDistanceToNow(userItem.createdAt) }}