Introduce @ghostfolio/common lib (#102)
This commit is contained in:
34
libs/common/src/lib/config.ts
Normal file
34
libs/common/src/lib/config.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Currency } from '.prisma/client';
|
||||
|
||||
export const baseCurrency = Currency.CHF;
|
||||
|
||||
export const benchmarks = ['VOO'];
|
||||
|
||||
export const currencyPairs = [
|
||||
`${Currency.USD}${Currency.EUR}`,
|
||||
`${Currency.USD}${Currency.GBP}`,
|
||||
`${Currency.USD}${Currency.CHF}`
|
||||
];
|
||||
|
||||
export const ghostfolioScraperApiSymbolPrefix = '_GF_';
|
||||
|
||||
export const locale = 'de-CH';
|
||||
|
||||
export const primaryColorHex = '#36cfcc';
|
||||
export const primaryColorRgb = {
|
||||
r: 54,
|
||||
g: 207,
|
||||
b: 204
|
||||
};
|
||||
|
||||
export const secondaryColorHex = '#3686cf';
|
||||
export const secondaryColorRgb = {
|
||||
r: 54,
|
||||
g: 134,
|
||||
b: 207
|
||||
};
|
||||
|
||||
export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
|
||||
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
|
||||
|
||||
export const UNKNOWN_KEY = 'UNKNOWN';
|
135
libs/common/src/lib/helper.ts
Normal file
135
libs/common/src/lib/helper.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
import { getDate, getMonth, getYear, subDays } from 'date-fns';
|
||||
|
||||
import { ghostfolioScraperApiSymbolPrefix } from './config';
|
||||
|
||||
const cryptocurrencies = require('cryptocurrencies');
|
||||
|
||||
export const DEMO_USER_ID = '9b112b4d-3b7d-4bad-9bdd-3b0f7b4dac2f';
|
||||
|
||||
export function capitalize(aString: string) {
|
||||
return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
export function getBackgroundColor() {
|
||||
return getCssVariable(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? '--dark-background'
|
||||
: '--light-background'
|
||||
);
|
||||
}
|
||||
|
||||
export function getCssVariable(aCssVariable: string) {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
aCssVariable
|
||||
);
|
||||
}
|
||||
|
||||
export function getTextColor() {
|
||||
return getCssVariable(
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? '--light-primary-text'
|
||||
: '--dark-primary-text'
|
||||
);
|
||||
}
|
||||
|
||||
export function getToday() {
|
||||
const year = getYear(new Date());
|
||||
const month = getMonth(new Date());
|
||||
const day = getDate(new Date());
|
||||
|
||||
return new Date(Date.UTC(year, month, day));
|
||||
}
|
||||
|
||||
export function getUtc(aDateString: string) {
|
||||
const [yearString, monthString, dayString] = aDateString.split('-');
|
||||
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
parseInt(yearString),
|
||||
parseInt(monthString) - 1,
|
||||
parseInt(dayString)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function getYesterday() {
|
||||
const year = getYear(new Date());
|
||||
const month = getMonth(new Date());
|
||||
const day = getDate(new Date());
|
||||
|
||||
return subDays(new Date(Date.UTC(year, month, day)), 1);
|
||||
}
|
||||
|
||||
export function groupBy<T, K extends keyof T>(
|
||||
key: K,
|
||||
arr: T[]
|
||||
): Map<T[K], T[]> {
|
||||
const map = new Map<T[K], T[]>();
|
||||
arr.forEach((t) => {
|
||||
if (!map.has(t[key])) {
|
||||
map.set(t[key], []);
|
||||
}
|
||||
map.get(t[key])!.push(t);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
export function isCrypto(aSymbol = '') {
|
||||
const cryptocurrencySymbol = aSymbol.substring(0, aSymbol.length - 3);
|
||||
|
||||
return cryptocurrencies.symbols().includes(cryptocurrencySymbol);
|
||||
}
|
||||
|
||||
export function isCurrency(aSymbol = '') {
|
||||
return (
|
||||
(aSymbol.includes(Currency.CHF) ||
|
||||
aSymbol.includes(Currency.EUR) ||
|
||||
aSymbol.includes(Currency.USD)) &&
|
||||
aSymbol.length >= 6
|
||||
);
|
||||
}
|
||||
|
||||
export function isGhostfolioScraperApiSymbol(aSymbol = '') {
|
||||
return aSymbol.startsWith(ghostfolioScraperApiSymbolPrefix);
|
||||
}
|
||||
|
||||
export function isRakutenRapidApiSymbol(aSymbol = '') {
|
||||
return aSymbol === 'GF.FEAR_AND_GREED_INDEX';
|
||||
}
|
||||
|
||||
export function parseCurrency(aString: string): Currency {
|
||||
if (aString?.toLowerCase() === 'chf') {
|
||||
return Currency.CHF;
|
||||
} else if (aString?.toLowerCase() === 'eur') {
|
||||
return Currency.EUR;
|
||||
} else if (aString?.toLowerCase() === 'gbp') {
|
||||
return Currency.GBP;
|
||||
} else if (aString?.toLowerCase() === 'usd') {
|
||||
return Currency.USD;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function resetHours(aDate: Date) {
|
||||
const year = getYear(aDate);
|
||||
const month = getMonth(aDate);
|
||||
const day = getDate(aDate);
|
||||
|
||||
return new Date(Date.UTC(year, month, day));
|
||||
}
|
||||
|
||||
export function resolveFearAndGreedIndex(aValue: number) {
|
||||
if (aValue <= 25) {
|
||||
return { emoji: '🥵', text: 'Extreme Fear' };
|
||||
} else if (aValue <= 45) {
|
||||
return { emoji: '😨', text: 'Fear' };
|
||||
} else if (aValue <= 55) {
|
||||
return { emoji: '😐', text: 'Neutral' };
|
||||
} else if (aValue < 75) {
|
||||
return { emoji: '😜', text: 'Greed' };
|
||||
} else if (aValue >= 75) {
|
||||
return { emoji: '🤪', text: 'Extreme Greed' };
|
||||
}
|
||||
}
|
3
libs/common/src/lib/interfaces/access.interface.ts
Normal file
3
libs/common/src/lib/interfaces/access.interface.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface Access {
|
||||
granteeAlias: string;
|
||||
}
|
15
libs/common/src/lib/interfaces/admin-data.interface.ts
Normal file
15
libs/common/src/lib/interfaces/admin-data.interface.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export interface AdminData {
|
||||
exchangeRates: { label1: string; label2: string; value: number }[];
|
||||
lastDataGathering: Date | 'IN_PROGRESS';
|
||||
transactionCount: number;
|
||||
userCount: number;
|
||||
users: {
|
||||
alias: string;
|
||||
createdAt: Date;
|
||||
Analytics: {
|
||||
activityCount: number;
|
||||
updatedAt: Date;
|
||||
};
|
||||
id: string;
|
||||
}[];
|
||||
}
|
29
libs/common/src/lib/interfaces/index.ts
Normal file
29
libs/common/src/lib/interfaces/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Access } from './access.interface';
|
||||
import { AdminData } from './admin-data.interface';
|
||||
import { InfoItem } from './info-item.interface';
|
||||
import { PortfolioItem } from './portfolio-item.interface';
|
||||
import { PortfolioOverview } from './portfolio-overview.interface';
|
||||
import { PortfolioPerformance } from './portfolio-performance.interface';
|
||||
import { PortfolioPosition } from './portfolio-position.interface';
|
||||
import { PortfolioReportRule } from './portfolio-report-rule.interface';
|
||||
import { PortfolioReport } from './portfolio-report.interface';
|
||||
import { Position } from './position.interface';
|
||||
import { UserSettings } from './user-settings.interface';
|
||||
import { UserWithSettings } from './user-with-settings';
|
||||
import { User } from './user.interface';
|
||||
|
||||
export {
|
||||
Access,
|
||||
AdminData,
|
||||
InfoItem,
|
||||
PortfolioItem,
|
||||
PortfolioOverview,
|
||||
PortfolioPerformance,
|
||||
PortfolioPosition,
|
||||
PortfolioReport,
|
||||
PortfolioReportRule,
|
||||
Position,
|
||||
User,
|
||||
UserSettings,
|
||||
UserWithSettings
|
||||
};
|
13
libs/common/src/lib/interfaces/info-item.interface.ts
Normal file
13
libs/common/src/lib/interfaces/info-item.interface.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
export interface InfoItem {
|
||||
currencies: Currency[];
|
||||
demoAuthToken: string;
|
||||
globalPermissions: string[];
|
||||
lastDataGathering?: Date;
|
||||
message?: {
|
||||
text: string;
|
||||
type: string;
|
||||
};
|
||||
platforms: { id: string; name: string }[];
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
import { Position } from '@ghostfolio/common/interfaces';
|
||||
|
||||
export interface PortfolioItem {
|
||||
date: string;
|
||||
grossPerformancePercent: number;
|
||||
investment: number;
|
||||
positions: { [symbol: string]: Position };
|
||||
value: number;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
export interface PortfolioOverview {
|
||||
committedFunds: number;
|
||||
fees: number;
|
||||
ordersCount: number;
|
||||
totalBuy: number;
|
||||
totalSell: number;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
export interface PortfolioPerformance {
|
||||
currentGrossPerformance: number;
|
||||
currentGrossPerformancePercent: number;
|
||||
currentNetPerformance: number;
|
||||
currentNetPerformancePercent: number;
|
||||
currentValue: number;
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
import { MarketState } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
export interface PortfolioPosition {
|
||||
accounts: {
|
||||
[name: string]: { current: number; original: number };
|
||||
};
|
||||
allocationCurrent: number;
|
||||
allocationInvestment: number;
|
||||
currency: Currency;
|
||||
exchange?: string;
|
||||
grossPerformance: number;
|
||||
grossPerformancePercent: number;
|
||||
industry?: string;
|
||||
investment: number;
|
||||
marketChange?: number;
|
||||
marketChangePercent?: number;
|
||||
marketPrice: number;
|
||||
marketState: MarketState;
|
||||
name: string;
|
||||
quantity: number;
|
||||
sector?: string;
|
||||
transactionCount: number;
|
||||
symbol: string;
|
||||
type?: string;
|
||||
url?: string;
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
export interface PortfolioReportRule {
|
||||
evaluation: string;
|
||||
name: string;
|
||||
value: boolean;
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
import { PortfolioReportRule } from './portfolio-report-rule.interface';
|
||||
|
||||
export interface PortfolioReport {
|
||||
rules: { [group: string]: PortfolioReportRule[] };
|
||||
}
|
12
libs/common/src/lib/interfaces/position.interface.ts
Normal file
12
libs/common/src/lib/interfaces/position.interface.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
export interface Position {
|
||||
averagePrice: number;
|
||||
currency: Currency;
|
||||
firstBuyDate: string;
|
||||
investment: number;
|
||||
investmentInOriginalCurrency?: number;
|
||||
marketPrice?: number;
|
||||
quantity: number;
|
||||
transactionCount: number;
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
export interface UserSettings {
|
||||
baseCurrency: Currency;
|
||||
locale: string;
|
||||
}
|
6
libs/common/src/lib/interfaces/user-with-settings.ts
Normal file
6
libs/common/src/lib/interfaces/user-with-settings.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Account, Settings, User } from '@prisma/client';
|
||||
|
||||
export type UserWithSettings = User & {
|
||||
Account: Account[];
|
||||
Settings: Settings;
|
||||
};
|
17
libs/common/src/lib/interfaces/user.interface.ts
Normal file
17
libs/common/src/lib/interfaces/user.interface.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Access } from '@ghostfolio/api/app/user/interfaces/access.interface';
|
||||
import { Account } from '@prisma/client';
|
||||
|
||||
import { UserSettings } from './user-settings.interface';
|
||||
|
||||
export interface User {
|
||||
access: Access[];
|
||||
accounts: Account[];
|
||||
alias?: string;
|
||||
id: string;
|
||||
permissions: string[];
|
||||
settings: UserSettings;
|
||||
subscription: {
|
||||
expiresAt: Date;
|
||||
type: 'Trial';
|
||||
};
|
||||
}
|
64
libs/common/src/lib/permissions.ts
Normal file
64
libs/common/src/lib/permissions.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Role } from '@prisma/client';
|
||||
|
||||
export function isApiTokenAuthorized(aApiToken: string) {
|
||||
return aApiToken === 'Bearer fc804dead6ff45b98da4e5530f6aa3cb';
|
||||
}
|
||||
|
||||
export const permissions = {
|
||||
accessAdminControl: 'accessAdminControl',
|
||||
accessFearAndGreedIndex: 'accessFearAndGreedIndex',
|
||||
createAccount: 'createAccount',
|
||||
createOrder: 'createOrder',
|
||||
createUserAccount: 'createUserAccount',
|
||||
deleteAccount: 'deleteAcccount',
|
||||
deleteOrder: 'deleteOrder',
|
||||
deleteUser: 'deleteUser',
|
||||
enableSocialLogin: 'enableSocialLogin',
|
||||
enableSubscription: 'enableSubscription',
|
||||
readForeignPortfolio: 'readForeignPortfolio',
|
||||
updateAccount: 'updateAccount',
|
||||
updateOrder: 'updateOrder',
|
||||
updateUserSettings: 'updateUserSettings'
|
||||
};
|
||||
|
||||
export function hasPermission(
|
||||
aPermissions: string[] = [],
|
||||
aPermission: string
|
||||
) {
|
||||
return aPermissions.includes(aPermission);
|
||||
}
|
||||
|
||||
export function getPermissions(aRole: Role): string[] {
|
||||
switch (aRole) {
|
||||
case 'ADMIN':
|
||||
return [
|
||||
permissions.accessAdminControl,
|
||||
permissions.createAccount,
|
||||
permissions.createOrder,
|
||||
permissions.deleteAccount,
|
||||
permissions.deleteOrder,
|
||||
permissions.deleteUser,
|
||||
permissions.readForeignPortfolio,
|
||||
permissions.updateAccount,
|
||||
permissions.updateOrder,
|
||||
permissions.updateUserSettings
|
||||
];
|
||||
|
||||
case 'DEMO':
|
||||
return [permissions.createUserAccount];
|
||||
|
||||
case 'USER':
|
||||
return [
|
||||
permissions.createAccount,
|
||||
permissions.createOrder,
|
||||
permissions.deleteAccount,
|
||||
permissions.deleteOrder,
|
||||
permissions.updateAccount,
|
||||
permissions.updateOrder,
|
||||
permissions.updateUserSettings
|
||||
];
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
import { Access, User } from '@prisma/client';
|
||||
|
||||
export type AccessWithGranteeUser = Access & { GranteeUser?: User };
|
1
libs/common/src/lib/types/date-range.type.ts
Normal file
1
libs/common/src/lib/types/date-range.type.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type DateRange = '1d' | '1y' | '5y' | 'max' | 'ytd';
|
1
libs/common/src/lib/types/granularity.type.ts
Normal file
1
libs/common/src/lib/types/granularity.type.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type Granularity = 'day' | 'month';
|
13
libs/common/src/lib/types/index.ts
Normal file
13
libs/common/src/lib/types/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { AccessWithGranteeUser } from './access-with-grantee-user.type';
|
||||
import { DateRange } from './date-range.type';
|
||||
import { Granularity } from './granularity.type';
|
||||
import { OrderWithAccount } from './order-with-account.type';
|
||||
import { RequestWithUser } from './request-with-user.type';
|
||||
|
||||
export {
|
||||
AccessWithGranteeUser,
|
||||
DateRange,
|
||||
Granularity,
|
||||
OrderWithAccount,
|
||||
RequestWithUser
|
||||
};
|
5
libs/common/src/lib/types/order-with-account.type.ts
Normal file
5
libs/common/src/lib/types/order-with-account.type.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Account, Order, Platform } from '@prisma/client';
|
||||
|
||||
type AccountWithPlatform = Account & { Platform?: Platform };
|
||||
|
||||
export type OrderWithAccount = Order & { Account?: AccountWithPlatform };
|
3
libs/common/src/lib/types/request-with-user.type.ts
Normal file
3
libs/common/src/lib/types/request-with-user.type.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { UserWithSettings } from '@ghostfolio/common/interfaces';
|
||||
|
||||
export type RequestWithUser = Request & { user: UserWithSettings };
|
Reference in New Issue
Block a user