mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.0 KiB
120 lines
3.0 KiB
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
|
|
import {
|
|
PROPERTY_CURRENCIES,
|
|
PROPERTY_IS_USER_SIGNUP_ENABLED
|
|
} from '@ghostfolio/common/config';
|
|
import { PropertyKey } from '@ghostfolio/common/types';
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
import { Property } from '@prisma/client';
|
|
import { addMilliseconds, isBefore } from 'date-fns';
|
|
import ms from 'ms';
|
|
|
|
import { PropertyValue } from './interfaces/interfaces';
|
|
|
|
@Injectable()
|
|
export class PropertyService {
|
|
private static readonly CACHE_TTL = ms('1 minute');
|
|
|
|
private cachedProperties: Promise<Property[]>;
|
|
private cachedPropertiesExpiresAt: Date;
|
|
|
|
public constructor(private readonly prismaService: PrismaService) {}
|
|
|
|
public async delete({ key }: { key: PropertyKey }) {
|
|
const property = await this.prismaService.property.delete({
|
|
where: { key }
|
|
});
|
|
|
|
this.invalidateCache();
|
|
|
|
return property;
|
|
}
|
|
|
|
public async get({ skipCache = false } = {}) {
|
|
const response: {
|
|
[key: string]: PropertyValue;
|
|
} = {
|
|
[PROPERTY_CURRENCIES]: []
|
|
};
|
|
|
|
const properties = await this.getProperties({ skipCache });
|
|
|
|
for (const property of properties) {
|
|
let value = property.value;
|
|
|
|
try {
|
|
value = JSON.parse(property.value);
|
|
} catch {}
|
|
|
|
response[property.key] = value;
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
public async getByKey<TValue extends PropertyValue>(
|
|
aKey: PropertyKey,
|
|
{ skipCache = false } = {}
|
|
) {
|
|
const properties = await this.get({ skipCache });
|
|
return properties[aKey] as TValue;
|
|
}
|
|
|
|
public async isUserSignupEnabled() {
|
|
return (
|
|
(await this.getByKey<boolean>(PROPERTY_IS_USER_SIGNUP_ENABLED)) ?? true
|
|
);
|
|
}
|
|
|
|
public async put({ key, value }: { key: PropertyKey; value: string }) {
|
|
const property = await this.prismaService.property.upsert({
|
|
create: { key, value },
|
|
update: { value },
|
|
where: { key }
|
|
});
|
|
|
|
this.invalidateCache();
|
|
|
|
return property;
|
|
}
|
|
|
|
/**
|
|
* Returns the properties from the in-memory cache, falling back to the
|
|
* database. Callers which write back a modified property must set
|
|
* skipCache to avoid basing the write on a stale read.
|
|
*/
|
|
private async getProperties({ skipCache = false } = {}) {
|
|
if (skipCache) {
|
|
return this.prismaService.property.findMany();
|
|
}
|
|
|
|
if (
|
|
this.cachedProperties &&
|
|
isBefore(new Date(), this.cachedPropertiesExpiresAt)
|
|
) {
|
|
return this.cachedProperties;
|
|
}
|
|
|
|
const properties = this.prismaService.property.findMany().catch((error) => {
|
|
if (this.cachedProperties === properties) {
|
|
this.invalidateCache();
|
|
}
|
|
|
|
throw error;
|
|
});
|
|
|
|
this.cachedProperties = properties;
|
|
this.cachedPropertiesExpiresAt = addMilliseconds(
|
|
new Date(),
|
|
PropertyService.CACHE_TTL
|
|
);
|
|
|
|
return this.cachedProperties;
|
|
}
|
|
|
|
private invalidateCache() {
|
|
this.cachedProperties = undefined;
|
|
this.cachedPropertiesExpiresAt = undefined;
|
|
}
|
|
}
|
|
|