Browse Source

Setup read only mode

pull/520/head
Thomas 4 years ago
parent
commit
dad6944717
  1. 1
      apps/api/src/app/admin/admin.controller.ts
  2. 9
      apps/api/src/app/info/info.service.ts
  3. 15
      apps/api/src/app/user/user.service.ts
  4. 1
      apps/api/src/services/configuration.service.ts
  5. 1
      apps/api/src/services/interfaces/environment.interface.ts
  6. 2
      apps/api/src/services/property/property.service.ts
  7. 45
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  8. 10
      apps/client/src/app/components/admin-overview/admin-overview.html
  9. 9
      apps/client/src/app/components/admin-overview/admin-overview.module.ts
  10. 1
      libs/common/src/lib/config.ts
  11. 2
      libs/common/src/lib/interfaces/admin-data.interface.ts
  12. 3
      libs/common/src/lib/interfaces/info-item.interface.ts
  13. 6
      libs/common/src/lib/permissions.ts

1
apps/api/src/app/admin/admin.controller.ts

@ -1,7 +1,6 @@
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import { PROPERTY_CURRENCIES } from '@ghostfolio/common/config';
import {
AdminData,
AdminMarketData,

9
apps/api/src/app/info/info.service.ts

@ -6,6 +6,7 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
import {
PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_STRIPE_CONFIG,
PROPERTY_SYSTEM_MESSAGE
} from '@ghostfolio/common/config';
@ -36,6 +37,7 @@ export class InfoService {
public async get(): Promise<InfoItem> {
const info: Partial<InfoItem> = {};
let isReadOnlyMode: boolean;
const platforms = await this.prismaService.platform.findMany({
orderBy: { name: 'asc' },
select: { id: true, name: true }
@ -52,6 +54,12 @@ export class InfoService {
globalPermissions.push(permissions.enableImport);
}
if (this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE')) {
isReadOnlyMode = (await this.propertyService.getByKey(
PROPERTY_IS_READ_ONLY_MODE
)) as boolean;
}
if (this.configurationService.get('ENABLE_FEATURE_SOCIAL_LOGIN')) {
globalPermissions.push(permissions.enableSocialLogin);
}
@ -77,6 +85,7 @@ export class InfoService {
return {
...info,
globalPermissions,
isReadOnlyMode,
platforms,
systemMessage,
currencies: this.exchangeRateDataService.getCurrencies(),

15
apps/api/src/app/user/user.service.ts

@ -3,10 +3,14 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration.ser
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
import { baseCurrency, locale } from '@ghostfolio/common/config';
import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces';
import { getPermissions, permissions } from '@ghostfolio/common/permissions';
import {
getPermissions,
hasRole,
permissions
} from '@ghostfolio/common/permissions';
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
import { Injectable } from '@nestjs/common';
import { Prisma, Provider, User, ViewMode } from '@prisma/client';
import { Prisma, Provider, Role, User, ViewMode } from '@prisma/client';
import { UserSettingsParams } from './interfaces/user-settings-params.interface';
import { UserSettings } from './interfaces/user-settings.interface';
@ -80,6 +84,13 @@ export class UserService {
currentPermissions.push(permissions.accessFearAndGreedIndex);
}
if (
this.configurationService.get('ENABLE_FEATURE_READ_ONLY_MODE') &&
hasRole(user, Role.ADMIN)
) {
currentPermissions.push(permissions.toggleReadOnlyMode);
}
user.permissions = currentPermissions;
if (userFromDatabase?.Settings) {

1
apps/api/src/services/configuration.service.ts

@ -18,6 +18,7 @@ export class ConfigurationService {
ENABLE_FEATURE_CUSTOM_SYMBOLS: bool({ default: false }),
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: bool({ default: false }),
ENABLE_FEATURE_IMPORT: bool({ default: true }),
ENABLE_FEATURE_READ_ONLY_MODE: bool({ default: false }),
ENABLE_FEATURE_SOCIAL_LOGIN: bool({ default: false }),
ENABLE_FEATURE_STATISTICS: bool({ default: false }),
ENABLE_FEATURE_SUBSCRIPTION: bool({ default: false }),

1
apps/api/src/services/interfaces/environment.interface.ts

@ -9,6 +9,7 @@ export interface Environment extends CleanedEnvAccessors {
ENABLE_FEATURE_CUSTOM_SYMBOLS: boolean;
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: boolean;
ENABLE_FEATURE_IMPORT: boolean;
ENABLE_FEATURE_READ_ONLY_MODE: boolean;
ENABLE_FEATURE_SOCIAL_LOGIN: boolean;
ENABLE_FEATURE_STATISTICS: boolean;
ENABLE_FEATURE_SUBSCRIPTION: boolean;

2
apps/api/src/services/property/property.service.ts

@ -14,7 +14,7 @@ export class PropertyService {
public async get() {
const response: {
[key: string]: object | string | string[];
[key: string]: boolean | object | string | string[];
} = {
[PROPERTY_CURRENCIES]: []
};

45
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -1,4 +1,5 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { CacheService } from '@ghostfolio/client/services/cache.service';
import { DataService } from '@ghostfolio/client/services/data.service';
@ -6,6 +7,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
DEFAULT_DATE_FORMAT,
PROPERTY_CURRENCIES,
PROPERTY_IS_READ_ONLY_MODE,
PROPERTY_SYSTEM_MESSAGE
} from '@ghostfolio/common/config';
import { InfoItem, User } from '@ghostfolio/common/interfaces';
@ -32,6 +34,7 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
public defaultDateFormat = DEFAULT_DATE_FORMAT;
public exchangeRates: { label1: string; label2: string; value: number }[];
public hasPermissionForSystemMessage: boolean;
public hasPermissionToToggleReadOnlyMode: boolean;
public info: InfoItem;
public lastDataGathering: string;
public transactionCount: number;
@ -52,27 +55,32 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
) {
this.info = this.dataService.fetchInfo();
this.hasPermissionForSystemMessage = hasPermission(
this.info.globalPermissions,
permissions.enableSystemMessage
);
}
/**
* Initializes the controller
*/
public ngOnInit() {
this.fetchAdminData();
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.hasPermissionForSystemMessage = hasPermission(
this.info.globalPermissions,
permissions.enableSystemMessage
);
this.hasPermissionToToggleReadOnlyMode = hasPermission(
this.user.permissions,
permissions.toggleReadOnlyMode
);
}
});
}
/**
* Initializes the controller
*/
public ngOnInit() {
this.fetchAdminData();
}
public formatDistanceToNow(aDateString: string) {
if (aDateString) {
const distanceString = formatDistanceToNowStrict(parseISO(aDateString), {
@ -147,6 +155,10 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
.subscribe(() => {});
}
public onReadOnlyModeChange(aEvent: MatSlideToggleChange) {
this.setReadOnlyMode(aEvent.checked);
}
public onSetSystemMessage() {
const systemMessage = prompt('Please set your system message:');
@ -223,4 +235,13 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
}, 300);
});
}
private setReadOnlyMode(aValue: boolean) {
this.dataService
.putAdminSetting(PROPERTY_IS_READ_ONLY_MODE, {
value: aValue ? 'true' : ''
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {});
}
}

10
apps/client/src/app/components/admin-overview/admin-overview.html

@ -146,6 +146,16 @@
</button>
</div>
</div>
<div *ngIf="hasPermissionToToggleReadOnlyMode" class="d-flex my-3">
<div class="w-50" i18n>Read-only Mode</div>
<div class="w-50">
<mat-slide-toggle
color="primary"
[checked]="info?.isReadOnlyMode"
(change)="onReadOnlyModeChange($event)"
></mat-slide-toggle>
</div>
</div>
</mat-card-content>
</mat-card>
</div>

9
apps/client/src/app/components/admin-overview/admin-overview.module.ts

@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { CacheService } from '@ghostfolio/client/services/cache.service';
import { GfValueModule } from '@ghostfolio/ui/value';
@ -10,7 +11,13 @@ import { AdminOverviewComponent } from './admin-overview.component';
@NgModule({
declarations: [AdminOverviewComponent],
exports: [],
imports: [CommonModule, GfValueModule, MatButtonModule, MatCardModule],
imports: [
CommonModule,
GfValueModule,
MatButtonModule,
MatCardModule,
MatSlideToggleModule
],
providers: [CacheService],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

1
libs/common/src/lib/config.ts

@ -31,6 +31,7 @@ export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
export const DEFAULT_DATE_FORMAT_MONTH_YEAR = 'MMM yyyy';
export const PROPERTY_CURRENCIES = 'CURRENCIES';
export const PROPERTY_IS_READ_ONLY_MODE = 'IS_READ_ONLY_MODE';
export const PROPERTY_LAST_DATA_GATHERING = 'LAST_DATA_GATHERING';
export const PROPERTY_LOCKED_DATA_GATHERING = 'LOCKED_DATA_GATHERING';
export const PROPERTY_STRIPE_CONFIG = 'STRIPE_CONFIG';

2
libs/common/src/lib/interfaces/admin-data.interface.ts

@ -4,7 +4,7 @@ export interface AdminData {
dataGatheringProgress?: number;
exchangeRates: { label1: string; label2: string; value: number }[];
lastDataGathering?: Date | 'IN_PROGRESS';
settings: { [key: string]: object | string | string[] };
settings: { [key: string]: boolean | object | string | string[] };
transactionCount: number;
userCount: number;
users: {

3
libs/common/src/lib/interfaces/info-item.interface.ts

@ -7,11 +7,12 @@ export interface InfoItem {
currencies: string[];
demoAuthToken: string;
globalPermissions: string[];
isReadOnlyMode?: boolean;
lastDataGathering?: Date;
systemMessage?: string;
platforms: { id: string; name: string }[];
primaryDataSource: DataSource;
statistics: Statistics;
stripePublicKey?: string;
subscriptions: Subscription[];
systemMessage?: string;
}

6
libs/common/src/lib/permissions.ts

@ -1,4 +1,5 @@
import { Role } from '@prisma/client';
import { UserWithSettings } from './interfaces';
export const permissions = {
accessAdminControl: 'accessAdminControl',
@ -18,6 +19,7 @@ export const permissions = {
enableStatistics: 'enableStatistics',
enableSubscription: 'enableSubscription',
enableSystemMessage: 'enableSystemMessage',
toggleReadOnlyMode: 'toggleReadOnlyMode',
updateAccount: 'updateAccount',
updateAuthDevice: 'updateAuthDevice',
updateOrder: 'updateOrder',
@ -75,3 +77,7 @@ export function getPermissions(aRole: Role): string[] {
return [];
}
}
export function hasRole(aUser: UserWithSettings, aRole: Role): boolean {
return aUser?.role === aRole;
}

Loading…
Cancel
Save