2021-04-21 20:27:39 +02:00
|
|
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration.service';
|
|
|
|
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
2021-05-16 22:11:14 +02:00
|
|
|
import { locale } from '@ghostfolio/common/config';
|
|
|
|
import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces';
|
|
|
|
import { getPermissions, permissions } from '@ghostfolio/common/permissions';
|
2021-06-02 20:15:53 +02:00
|
|
|
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
|
2021-04-13 21:53:58 +02:00
|
|
|
import { Injectable } from '@nestjs/common';
|
2021-05-22 10:04:56 +02:00
|
|
|
import { Currency, Prisma, Provider, User, ViewMode } from '@prisma/client';
|
2021-06-21 20:03:36 +02:00
|
|
|
import { isBefore } from 'date-fns';
|
|
|
|
|
|
|
|
import { UserSettingsParams } from './interfaces/user-settings-params.interface';
|
2021-04-13 21:53:58 +02:00
|
|
|
|
|
|
|
const crypto = require('crypto');
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class UserService {
|
|
|
|
public static DEFAULT_CURRENCY = Currency.USD;
|
|
|
|
|
2021-04-18 19:06:54 +02:00
|
|
|
public constructor(
|
|
|
|
private readonly configurationService: ConfigurationService,
|
2021-08-07 22:38:07 +02:00
|
|
|
private readonly prismaService: PrismaService
|
2021-04-18 19:06:54 +02:00
|
|
|
) {}
|
2021-04-13 21:53:58 +02:00
|
|
|
|
|
|
|
public async getUser({
|
2021-04-25 21:22:35 +02:00
|
|
|
Account,
|
2021-04-13 21:53:58 +02:00
|
|
|
alias,
|
|
|
|
id,
|
2021-06-21 20:03:36 +02:00
|
|
|
permissions,
|
2021-06-02 20:15:53 +02:00
|
|
|
Settings,
|
|
|
|
subscription
|
2021-04-13 21:53:58 +02:00
|
|
|
}: UserWithSettings): Promise<IUser> {
|
2021-08-07 22:38:07 +02:00
|
|
|
const access = await this.prismaService.access.findMany({
|
2021-04-13 21:53:58 +02:00
|
|
|
include: {
|
|
|
|
User: true
|
|
|
|
},
|
|
|
|
orderBy: { User: { alias: 'asc' } },
|
|
|
|
where: { GranteeUser: { id } }
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
alias,
|
|
|
|
id,
|
2021-06-21 20:03:36 +02:00
|
|
|
permissions,
|
2021-06-02 20:15:53 +02:00
|
|
|
subscription,
|
2021-04-13 21:53:58 +02:00
|
|
|
access: access.map((accessItem) => {
|
|
|
|
return {
|
|
|
|
alias: accessItem.User.alias,
|
|
|
|
id: accessItem.id
|
|
|
|
};
|
|
|
|
}),
|
2021-04-25 21:22:35 +02:00
|
|
|
accounts: Account,
|
2021-04-13 21:53:58 +02:00
|
|
|
settings: {
|
2021-05-22 10:04:56 +02:00
|
|
|
locale,
|
|
|
|
baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY,
|
2021-06-02 20:15:53 +02:00
|
|
|
viewMode: Settings?.viewMode ?? ViewMode.DEFAULT
|
2021-04-13 21:53:58 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public async user(
|
|
|
|
userWhereUniqueInput: Prisma.UserWhereUniqueInput
|
|
|
|
): Promise<UserWithSettings | null> {
|
2021-08-07 22:38:07 +02:00
|
|
|
const userFromDatabase = await this.prismaService.user.findUnique({
|
2021-06-02 20:15:53 +02:00
|
|
|
include: { Account: true, Settings: true, Subscription: true },
|
2021-04-13 21:53:58 +02:00
|
|
|
where: userWhereUniqueInput
|
|
|
|
});
|
|
|
|
|
2021-06-02 20:15:53 +02:00
|
|
|
const user: UserWithSettings = userFromDatabase;
|
|
|
|
|
2021-06-21 20:03:36 +02:00
|
|
|
const currentPermissions = getPermissions(userFromDatabase.role);
|
|
|
|
|
|
|
|
if (this.configurationService.get('ENABLE_FEATURE_FEAR_AND_GREED_INDEX')) {
|
|
|
|
currentPermissions.push(permissions.accessFearAndGreedIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
user.permissions = currentPermissions;
|
|
|
|
|
2021-06-02 20:15:53 +02:00
|
|
|
if (userFromDatabase?.Settings) {
|
|
|
|
if (!userFromDatabase.Settings.currency) {
|
2021-04-13 21:53:58 +02:00
|
|
|
// Set default currency if needed
|
2021-06-02 20:15:53 +02:00
|
|
|
userFromDatabase.Settings.currency = UserService.DEFAULT_CURRENCY;
|
2021-04-13 21:53:58 +02:00
|
|
|
}
|
2021-06-02 20:15:53 +02:00
|
|
|
} else if (userFromDatabase) {
|
2021-04-13 21:53:58 +02:00
|
|
|
// Set default settings if needed
|
2021-06-02 20:15:53 +02:00
|
|
|
userFromDatabase.Settings = {
|
2021-04-13 21:53:58 +02:00
|
|
|
currency: UserService.DEFAULT_CURRENCY,
|
|
|
|
updatedAt: new Date(),
|
2021-06-02 20:15:53 +02:00
|
|
|
userId: userFromDatabase?.id,
|
2021-05-22 10:04:56 +02:00
|
|
|
viewMode: ViewMode.DEFAULT
|
2021-04-13 21:53:58 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-06-02 20:15:53 +02:00
|
|
|
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
|
|
|
|
};
|
|
|
|
}
|
2021-06-21 20:03:36 +02:00
|
|
|
|
|
|
|
if (user.subscription.type === SubscriptionType.Basic) {
|
|
|
|
user.permissions = user.permissions.filter((permission) => {
|
|
|
|
return permission !== permissions.updateViewMode;
|
|
|
|
});
|
|
|
|
user.Settings.viewMode = ViewMode.ZEN;
|
|
|
|
}
|
2021-06-02 20:15:53 +02:00
|
|
|
}
|
|
|
|
|
2021-04-13 21:53:58 +02:00
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async users(params: {
|
|
|
|
skip?: number;
|
|
|
|
take?: number;
|
|
|
|
cursor?: Prisma.UserWhereUniqueInput;
|
|
|
|
where?: Prisma.UserWhereInput;
|
|
|
|
orderBy?: Prisma.UserOrderByInput;
|
|
|
|
}): Promise<User[]> {
|
|
|
|
const { skip, take, cursor, where, orderBy } = params;
|
2021-08-07 22:38:07 +02:00
|
|
|
return this.prismaService.user.findMany({
|
2021-04-13 21:53:58 +02:00
|
|
|
skip,
|
|
|
|
take,
|
|
|
|
cursor,
|
|
|
|
where,
|
|
|
|
orderBy
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public createAccessToken(password: string, salt: string): string {
|
|
|
|
const hash = crypto.createHmac('sha512', salt);
|
|
|
|
hash.update(password);
|
|
|
|
|
|
|
|
return hash.digest('hex');
|
|
|
|
}
|
|
|
|
|
|
|
|
public async createUser(data?: Prisma.UserCreateInput): Promise<User> {
|
2021-08-07 22:38:07 +02:00
|
|
|
let user = await this.prismaService.user.create({
|
2021-04-25 12:07:32 +02:00
|
|
|
data: {
|
|
|
|
...data,
|
|
|
|
Account: {
|
|
|
|
create: {
|
|
|
|
isDefault: true,
|
|
|
|
name: 'Default Account'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-13 21:53:58 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
if (data.provider === Provider.ANONYMOUS) {
|
|
|
|
const accessToken = this.createAccessToken(
|
|
|
|
user.id,
|
|
|
|
this.getRandomString(10)
|
|
|
|
);
|
|
|
|
|
|
|
|
const hashedAccessToken = this.createAccessToken(
|
|
|
|
accessToken,
|
|
|
|
process.env.ACCESS_TOKEN_SALT
|
|
|
|
);
|
|
|
|
|
2021-08-07 22:38:07 +02:00
|
|
|
user = await this.prismaService.user.update({
|
2021-04-13 21:53:58 +02:00
|
|
|
data: { accessToken: hashedAccessToken },
|
|
|
|
where: { id: user.id }
|
|
|
|
});
|
|
|
|
|
|
|
|
return { ...user, accessToken };
|
|
|
|
}
|
|
|
|
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async updateUser(params: {
|
|
|
|
where: Prisma.UserWhereUniqueInput;
|
|
|
|
data: Prisma.UserUpdateInput;
|
|
|
|
}): Promise<User> {
|
|
|
|
const { where, data } = params;
|
2021-08-07 22:38:07 +02:00
|
|
|
return this.prismaService.user.update({
|
2021-04-13 21:53:58 +02:00
|
|
|
data,
|
|
|
|
where
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async deleteUser(where: Prisma.UserWhereUniqueInput): Promise<User> {
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.access.deleteMany({
|
2021-05-03 21:23:00 +02:00
|
|
|
where: { OR: [{ granteeUserId: where.id }, { userId: where.id }] }
|
|
|
|
});
|
|
|
|
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.account.deleteMany({
|
2021-05-03 21:23:00 +02:00
|
|
|
where: { userId: where.id }
|
|
|
|
});
|
|
|
|
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.analytics.delete({
|
2021-05-03 21:23:00 +02:00
|
|
|
where: { userId: where.id }
|
|
|
|
});
|
|
|
|
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.order.deleteMany({
|
2021-05-03 21:23:00 +02:00
|
|
|
where: { userId: where.id }
|
|
|
|
});
|
|
|
|
|
|
|
|
try {
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.settings.delete({
|
2021-05-03 21:23:00 +02:00
|
|
|
where: { userId: where.id }
|
|
|
|
});
|
|
|
|
} catch {}
|
|
|
|
|
2021-08-07 22:38:07 +02:00
|
|
|
return this.prismaService.user.delete({
|
2021-04-13 21:53:58 +02:00
|
|
|
where
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async updateUserSettings({
|
|
|
|
currency,
|
2021-05-22 10:04:56 +02:00
|
|
|
userId,
|
|
|
|
viewMode
|
2021-06-21 20:03:36 +02:00
|
|
|
}: UserSettingsParams) {
|
2021-08-07 22:38:07 +02:00
|
|
|
await this.prismaService.settings.upsert({
|
2021-04-13 21:53:58 +02:00
|
|
|
create: {
|
|
|
|
currency,
|
|
|
|
User: {
|
|
|
|
connect: {
|
|
|
|
id: userId
|
|
|
|
}
|
2021-05-22 10:04:56 +02:00
|
|
|
},
|
|
|
|
viewMode
|
2021-04-13 21:53:58 +02:00
|
|
|
},
|
|
|
|
update: {
|
2021-05-22 10:04:56 +02:00
|
|
|
currency,
|
|
|
|
viewMode
|
2021-04-13 21:53:58 +02:00
|
|
|
},
|
|
|
|
where: {
|
|
|
|
userId: userId
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-04-20 21:52:01 +02:00
|
|
|
|
|
|
|
private getRandomString(length: number) {
|
|
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
|
|
result.push(
|
|
|
|
characters.charAt(Math.floor(Math.random() * characters.length))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return result.join('');
|
|
|
|
}
|
2021-04-13 21:53:58 +02:00
|
|
|
}
|