move server error interceptor, update app config service

This commit is contained in:
Dan Percic 2021-09-19 23:44:11 +03:00
parent c8a63b9aa4
commit 6557bafd2f
24 changed files with 123 additions and 153 deletions

View File

@ -110,6 +110,9 @@
"apps/red-ui/src/manifest.webmanifest"
],
"styles": ["apps/red-ui/src/styles.scss", "libs/common-ui/src/assets/styles/common.scss"],
"stylePreprocessorOptions": {
"includePaths": ["./apps/red-ui/src/assets/styles"]
},
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js"],
"vendorChunk": true,
"extractLicenses": false,

View File

@ -36,10 +36,9 @@ import { SpotlightSearchComponent } from '@components/spotlight-search/spotlight
import { PruningTranslationLoader } from '@utils/pruning-translation-loader';
import { DatePipe } from '@shared/pipes/date.pipe';
import * as links from '../assets/help-mode/links.json';
import { HELP_DOCS, IqserHelpModeModule } from '@iqser/common-ui';
import { ServerErrorInterceptor } from '@utils/server-error-interceptor';
import { HELP_DOCS, IqserHelpModeModule, MAX_RETRIES_ON_SERVER_ERROR, ServerErrorInterceptor } from '@iqser/common-ui';
export function httpLoaderFactory(httpClient: HttpClient) {
export function httpLoaderFactory(httpClient: HttpClient): PruningTranslationLoader {
return new PruningTranslationLoader(httpClient, '/assets/i18n/', '.json');
}
@ -145,6 +144,11 @@ const components = [
provide: HELP_DOCS,
useValue: links
},
{
provide: MAX_RETRIES_ON_SERVER_ERROR,
useFactory: (appConfigService: AppConfigService) => appConfigService.config.MAX_RETRIES_ON_SERVER_ERROR,
deps: [AppConfigService]
},
DatePipe
],
bootstrap: [AppComponent]

View File

@ -1,19 +1,19 @@
<section>
<p *ngIf="!configuredAdminName && !configuredAdminUrl" class="heading-xl" translate="auth-error.heading"></p>
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
<p
*ngIf="configuredAdminName && configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }"
*ngIf="adminName && adminUrl"
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: adminName, adminUrl: adminUrl }"
class="heading-xl"
></p>
<p
*ngIf="configuredAdminName && !configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: configuredAdminName }"
*ngIf="adminName && !adminUrl"
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: adminName }"
class="heading-xl"
></p>
<p
*ngIf="!configuredAdminName && configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: configuredAdminName }"
*ngIf="!adminName && adminUrl"
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: adminName }"
class="heading-xl"
></p>
<a (click)="logout()" translate="auth-error.logout"></a>
<a (click)="userService.logout()" translate="auth-error.logout"></a>
</section>

View File

@ -1,24 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { UserService } from '@services/user.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
@Component({
selector: 'redaction-auth-error',
templateUrl: './auth-error.component.html',
styleUrls: ['./auth-error.component.scss']
})
export class AuthErrorComponent implements OnInit {
configuredAdminName: string;
configuredAdminUrl: string;
export class AuthErrorComponent {
adminName = this._appConfigService.config.ADMIN_CONTACT_NAME;
adminUrl = this._appConfigService.config.ADMIN_CONTACT_URL;
constructor(private readonly _userService: UserService, private readonly _appConfigService: AppConfigService) {}
ngOnInit(): void {
this.configuredAdminName = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_NAME);
this.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
}
logout() {
this._userService.logout();
}
constructor(readonly userService: UserService, private readonly _appConfigService: AppConfigService) {}
}

View File

@ -5,7 +5,7 @@ import { PermissionsService } from '@services/permissions.service';
import { LanguageService } from '@i18n/language.service';
import { TranslateService } from '@ngx-translate/core';
import { UserControllerService } from '@redaction/red-ui-http';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { DomSanitizer } from '@angular/platform-browser';
import { languagesTranslations } from '../../translations/languages-translations';
import { LoadingService } from '@iqser/common-ui';
@ -42,7 +42,7 @@ export class UserProfileScreenComponent implements OnInit {
});
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
this._appConfigService.getConfig(AppConfigKey.OAUTH_URL) + '/account/password'
this._appConfigService.config.OAUTH_URL + '/account/password'
);
}

View File

@ -16,17 +16,17 @@
<div class="grid-container">
<div class="row">
<div translate="license-info-screen.backend-version"></div>
<div>{{ appConfigService.getConfig('BACKEND_APP_VERSION', '-') }}</div>
<div>{{ appConfigService.config.BACKEND_APP_VERSION || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.frontend-version"></div>
<div>{{ appConfigService.getConfig('FRONTEND_APP_VERSION', '-') }}</div>
<div>{{ appConfigService.config.FRONTEND_APP_VERSION || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.custom-app-title"></div>
<div>{{ appConfigService.getConfig('APP_NAME', '-') }}</div>
<div>{{ appConfigService.config.APP_NAME || '-' }}</div>
</div>
<div class="row">
@ -45,14 +45,14 @@
<div class="row">
<div translate="license-info-screen.licensed-to"></div>
<div>{{ appConfigService.getConfig('LICENSE_CUSTOMER', '-') }}</div>
<div>{{ appConfigService.config.LICENSE_CUSTOMER || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.licensing-period"></div>
<div>
{{ appConfigService.getConfig('LICENSE_START', '-') }} /
{{ appConfigService.getConfig('LICENSE_END', '-') }}
{{ appConfigService.config.LICENSE_START || '-' }} /
{{ appConfigService.config.LICENSE_END || '-' }}
</div>
</div>
@ -107,8 +107,7 @@
[yAxisLabelRight]="'license-info-screen.chart.total-pages' | translate"
[yAxisLabel]="'license-info-screen.chart.pages-per-month' | translate"
[yAxis]="true"
>
</combo-chart-component>
></combo-chart-component>
</div>
</div>
</div>

View File

@ -60,9 +60,9 @@ export class LicenseInformationScreenComponent implements OnInit {
}
async ngOnInit() {
this.totalLicensedNumberOfPages = this.appConfigService.getConfig('LICENSE_PAGE_COUNT', 0);
const startDate = moment(this.appConfigService.getConfig('LICENSE_START'), 'DD-MM-YYYY');
const endDate = moment(this.appConfigService.getConfig('LICENSE_END'), 'DD-MM-YYYY');
this.totalLicensedNumberOfPages = this.appConfigService.config.LICENSE_PAGE_COUNT || 0;
const startDate = moment(this.appConfigService.config.LICENSE_START, 'DD-MM-YYYY');
const endDate = moment(this.appConfigService.config.LICENSE_END, 'DD-MM-YYYY');
await this._setMonthlyStats(startDate, endDate);
@ -93,7 +93,7 @@ export class LicenseInformationScreenComponent implements OnInit {
}
sendMail(): void {
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
const licenseCustomer = this.appConfigService.config.LICENSE_CUSTOMER;
const subject = this._translateService.instant('license-info-screen.email.title', {
licenseCustomer
});
@ -106,7 +106,7 @@ export class LicenseInformationScreenComponent implements OnInit {
pages: this.totalLicensedNumberOfPages
})
].join(lineBreak);
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
window.location.href = `mailto:${this.appConfigService.config.LICENSE_EMAIL}?subject=${subject}&body=${body}`;
}
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {

View File

@ -9,7 +9,7 @@ import {
SortingOrders,
TableColumnConfig
} from '@iqser/common-ui';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
@ -45,7 +45,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
@ViewChild('deletedTimeTemplate', { static: true }) deletedTimeTemplate: TemplateRef<never>;
@ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>;
protected readonly _primaryKey = 'dossierName';
private readonly _deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
private readonly _deleteRetentionHours = this._appConfigService.config.DELETE_RETENTION_HOURS;
constructor(
protected readonly _injector: Injector,

View File

@ -1,14 +1,13 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { Title } from '@angular/platform-browser';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import packageInfo from '../../../../../../package.json';
import config from '../../../assets/config/config.json';
import { CacheApiService, wipeCaches } from '@redaction/red-cache';
export enum AppConfigKey {
const version = packageInfo.version;
export const enum AppConfigKey {
OAUTH_URL = 'OAUTH_URL',
OAUTH_CLIENT_ID = 'OAUTH_CLIENT_ID',
OAUTH_IDP_HINT = 'OAUTH_IDP_HINT',
@ -21,6 +20,7 @@ export enum AppConfigKey {
RECENT_PERIOD_IN_HOURS = 'RECENT_PERIOD_IN_HOURS',
DELETE_RETENTION_HOURS = 'DELETE_RETENTION_HOURS',
APP_NAME = 'APP_NAME',
MAX_RETRIES_ON_SERVER_ERROR = 'MAX_RETRIES_ON_SERVER_ERROR',
// TODO
BACKEND_APP_VERSION = 'BACKEND_APP_VERSION',
@ -37,47 +37,37 @@ export enum AppConfigKey {
providedIn: 'root'
})
export class AppConfigService {
private _config: { [key in AppConfigKey]?: any } = {};
private _config = { ...config, [AppConfigKey.FRONTEND_APP_VERSION]: version } as const;
constructor(
private readonly _httpClient: HttpClient,
private readonly _cacheApiService: CacheApiService,
private readonly _titleService: Title
) {}
get version(): string {
return packageInfo.version;
) {
this._checkFrontendVersion();
}
loadAppConfig(): Observable<any> {
private _checkFrontendVersion(): void {
this._cacheApiService.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION).then(async lastVersion => {
console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', this.version);
if (lastVersion !== this.version) {
console.log('[REDACTION] Version-missmatch - wiping caches!');
console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', version);
if (lastVersion !== version) {
console.warn('[REDACTION] Version-mismatch - wiping caches!');
await wipeCaches();
}
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, this.version);
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, version);
});
return this._httpClient.get<any>('/assets/config/config.json').pipe(
tap(config => {
console.log('[REDACTION] Started with config: ', config);
this._config = config;
this._config[AppConfigKey.FRONTEND_APP_VERSION] = this.version;
})
);
}
updateDisplayName(name: string) {
this.setConfig(AppConfigKey.APP_NAME, name);
this._titleService.setTitle(this.getConfig(AppConfigKey.APP_NAME, 'RedactManager'));
get config() {
return this._config;
}
setConfig(key: AppConfigKey, value: any) {
this._config[key] = value;
updateDisplayName(name: string): void {
this._config = { ...this._config, [AppConfigKey.APP_NAME]: name } as const;
this._titleService.setTitle(this._config.APP_NAME || 'RedactManager');
}
getConfig(key: AppConfigKey | string, defaultValue?: any) {
return this._config[key] ? this._config[key] : defaultValue;
getConfig(key: AppConfigKey, defaultValue?: string): string | number {
return this._config[key] ?? defaultValue;
}
}

View File

@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
import { UserService } from '@services/user.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { BASE_HREF } from '../../tokens';
@Injectable({
@ -19,10 +19,10 @@ export class AuthGuard extends KeycloakAuthGuard {
super(_router, _keycloak);
}
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
if (!this.authenticated) {
await this._keycloak.login({
idpHint: this._appConfigService.getConfig(AppConfigKey.OAUTH_IDP_HINT, null),
idpHint: this._appConfigService.config.OAUTH_IDP_HINT,
redirectUri: window.location.origin + this._baseHref + state.url
});
return false;

View File

@ -4,49 +4,47 @@ import { HttpClientModule } from '@angular/common/http';
import { AppConfigModule } from '@app-config/app-config.module';
import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { BASE_HREF } from '../../tokens';
import { APP_BOOTSTRAPPED } from '../bootstrap/app-bootstrap';
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) {
export function keycloakInitializer(
keycloakService: KeycloakService,
appConfigService: AppConfigService,
baseUrl: string
): () => Promise<void> {
let url = appConfigService.config.OAUTH_URL;
url = url.replace(/\/$/, ''); // remove trailing slash
const realm = url.substring(url.lastIndexOf('/') + 1, url.length);
url = url.substr(0, url.lastIndexOf('/realms'));
const options: KeycloakOptions = {
config: {
url: url,
realm: realm,
clientId: appConfigService.config.OAUTH_CLIENT_ID
},
initOptions: {
checkLoginIframe: false,
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
flow: 'standard'
},
enableBearerInterceptor: true
};
return () =>
appConfigService
.loadAppConfig()
.toPromise()
.then(() => {
let url = appConfigService.getConfig(AppConfigKey.OAUTH_URL);
url = url.replace(/\/$/, ''); // remove trailing slash
const realm = url.substring(url.lastIndexOf('/') + 1, url.length);
url = url.substr(0, url.lastIndexOf('/realms'));
const options: KeycloakOptions = {
config: {
url: url,
realm: realm,
clientId: appConfigService.getConfig(AppConfigKey.OAUTH_CLIENT_ID)
},
initOptions: {
checkLoginIframe: false,
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
flow: 'standard'
},
enableBearerInterceptor: true
};
return keycloak
.init(options)
.then(() => configureAutomaticRedirectToLoginScreen(keycloak))
.then(() => APP_BOOTSTRAPPED.next(true));
});
keycloakService
.init(options)
.then(() => configureAutomaticRedirectToLoginScreen(keycloakService))
.then(() => APP_BOOTSTRAPPED.next(true));
}
function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakService) {
keyCloakService.getKeycloakInstance().onAuthRefreshError = () => {
keyCloakService.logout();
keyCloakService.getKeycloakInstance().onAuthRefreshError = async () => {
await keyCloakService.logout();
};
}
@NgModule({
declarations: [],
imports: [CommonModule, HttpClientModule, KeycloakAngularModule, AppConfigModule],
providers: [
{

View File

@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, S
import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { Subscription } from 'rxjs';
@Component({
@ -67,11 +67,11 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
clearTimeout(this.pageReadTimeout);
}
if (this.active && !this.read) {
this.pageReadTimeout = setTimeout(() => {
this.pageReadTimeout = window.setTimeout(() => {
if (this.active && !this.read) {
this._markPageRead();
}
}, this._appConfigService.getConfig(AppConfigKey.AUTO_READ_TIME, 1.5) * 1000);
}, this._appConfigService.config.AUTO_READ_TIME * 1000);
}
}
}

View File

@ -23,7 +23,7 @@ import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationActionsService } from '../../services/annotation-actions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { BASE_HREF } from '../../../../tokens';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { LoadingService } from '@iqser/common-ui';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
@ -298,7 +298,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
private _setSelectionMode(): void {
const textTool = (<unknown>this.instance.Core.Tools.TextTool) as TextTool;
textTool.SELECTION_MODE = this._appConfigService.getConfig(AppConfigKey.SELECTION_MODE);
textTool.SELECTION_MODE = this._appConfigService.config.SELECTION_MODE;
}
private _toggleRectangleAnnotationAction(readonly: boolean) {

View File

@ -13,7 +13,7 @@ import {
import { FileManagementControllerService, FileStatus, StatusControllerService } from '@redaction/red-ui-http';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import * as moment from 'moment';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { getLeftDateTime } from '@utils/functions';
import { Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
@ -44,7 +44,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
tableColumnConfigs: TableColumnConfig<FileListItem>[];
readonly tableHeaderLabel = _('edit-dossier-dialog.deleted-documents.table-header.label');
readonly circleButtonTypes = CircleButtonTypes;
readonly deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
readonly deleteRetentionHours = this._appConfigService.config.DELETE_RETENTION_HOURS;
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<never>;
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
@ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef<never>;

View File

@ -27,7 +27,7 @@ import { StatusSorter } from '@utils/sorters/status-sorter';
import { convertFiles, handleFileDrop } from '@utils/file-drop-utils';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import {
CircleButtonTypes,
@ -256,7 +256,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
}
recentlyModifiedChecker = (file: FileStatusWrapper) =>
moment(file.lastUpdated).add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours').isAfter(moment());
moment(file.lastUpdated).add(this._appConfigService.config.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
private _configureTableColumns() {
const dynamicColumns: TableColumnConfig<FileStatusWrapper>[] = [];
@ -472,7 +472,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private _createQuickFilters() {
let quickFilters = [];
if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS);
const recentPeriod = this._appConfigService.config.RECENT_PERIOD_IN_HOURS;
quickFilters = [
{
key: 'recent',

View File

@ -1,7 +1,7 @@
import { ApplicationRef, Injectable } from '@angular/core';
import { DownloadControllerService, FileManagementControllerService } from '@redaction/red-ui-http';
import { interval, Observable } from 'rxjs';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { TranslateService } from '@ngx-translate/core';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
@ -56,7 +56,7 @@ export class FileDownloadService {
const token = await this._keycloakService.getToken();
const anchor = document.createElement('a');
anchor.href =
this._appConfigService.getConfig(AppConfigKey.API_URL) +
this._appConfigService.config.API_URL +
'/async/download?access_token=' +
encodeURIComponent(token) +
'&storageId=' +

View File

@ -3,7 +3,7 @@ import { FileUploadModel } from '../model/file-upload.model';
import { AppStateService } from '@state/app-state.service';
import { HttpErrorResponse, HttpEventType } from '@angular/common/http';
import { interval, Subscription } from 'rxjs';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { TranslateService } from '@ngx-translate/core';
import { UploadDownloadDialogService } from './upload-download-dialog.service';
import { toNumber } from '@utils/functions';
@ -55,7 +55,7 @@ export class FileUploadService {
}
async uploadFiles(files: FileUploadModel[]): Promise<number> {
const maxSizeMB = this._appConfigService.getConfig(AppConfigKey.MAX_FILE_SIZE_MB, 100);
const maxSizeMB = this._appConfigService.config.MAX_FILE_SIZE_MB;
const maxSizeBytes = toNumber(maxSizeMB) * 1024 * 1024;
const dossierFiles = this._appStateService.activeDossier.files;
let option: 'overwrite' | 'skip' | undefined;

View File

@ -22,7 +22,7 @@ export class UploadStatusOverlayComponent implements OnInit {
) {}
ngOnInit() {
this.uploadStatusInterval = setInterval(() => {
this.uploadStatusInterval = window.setInterval(() => {
// keep only errors
this.uploadService.filterFiles();
if (this.uploadService.files.length === 0) {

View File

@ -1,17 +1,17 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import { BASE_HREF } from '../tokens';
@Injectable()
export class ApiPathInterceptor implements HttpInterceptor {
constructor(@Inject(BASE_HREF) private readonly _baseHref: string, private readonly _appConfigService: AppConfigService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (!req.url.startsWith('/assets')) {
const updatedRequest = req.clone({
url: this._appConfigService.getConfig(AppConfigKey.API_URL) + req.url
url: this._appConfigService.config.API_URL + req.url
});
return next.handle(updatedRequest);
} else {

View File

@ -1,23 +0,0 @@
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ErrorService } from '@iqser/common-ui';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@Injectable()
export class ServerErrorInterceptor implements HttpInterceptor {
constructor(private readonly _errorService: ErrorService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
map((event: HttpEvent<any>) => event),
catchError((error: HttpErrorResponse) => {
if (error.status >= 500) {
// || error.status === 0
this._errorService.set(error);
}
return throwError(error);
})
);
}
}

View File

@ -1,4 +1,9 @@
{
"APP_NAME": "RedactManager",
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"AUTO_READ_TIME": 1.5,
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://demo.redactmanager.com/auth/realms/redaction",
"API_URL": "https://demo.redactmanager.com/redaction-gateway-v1",
"OAUTH_CLIENT_ID": "redaction",
@ -13,5 +18,6 @@
"SELECTION_MODE": "structural",
"RECENT_PERIOD_IN_HOURS": 24,
"MAX_FILE_SIZE_MB": 100,
"DELETE_RETENTION_HOURS": 96
"DELETE_RETENTION_HOURS": 96,
"MAX_RETRIES_ON_SERVER_ERROR": 3
}

View File

@ -865,7 +865,9 @@
"generic": "Action failed with code {status}"
},
"reload": "Reload",
"title": "Oops! Something went wrong..."
"title": "Oops! Something went wrong...",
"offline": "You're offline",
"close": "Close"
},
"exact-date": "{day} {month} {year} at {hour}:{minute}",
"file": "File",

View File

@ -2,7 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"allowSyntheticDefaultImports": true
},
"files": ["src/main.ts", "src/polyfills.ts"]
}

@ -1 +1 @@
Subproject commit c7546078ec884dd9a051a965111c4093f2a2ae94
Subproject commit 90287baf62e8a5cfbec742b6947352c56c255bdb