mfe version

This commit is contained in:
Булат Хайруллин 2024-10-24 18:22:18 +03:00
parent 88917d409e
commit e42c97d285
298 changed files with 171893 additions and 8861 deletions

1
frontend/.env Normal file
View file

@ -0,0 +1 @@
MFE_BASE_URL=/mfe/dashboard

View file

@ -0,0 +1,32 @@
"use strict";
exports.__esModule = true;
var fs = require('fs');
var mfeBaseUrlKey = 'MFE_BASE_URL';
var srcUrlRegex = /url\((\\?["'])?(?!data:)\S+(\\?["'])?\)/g;
function normalizeCssPaths(params) {
params.paths = params.paths ? params.paths : [];
params.paths.forEach(function (path) { return normalizeCssPath(path, params.outDir); });
}
exports.normalizeCssPaths = normalizeCssPaths;
function normalizeCssPath(path, outputDirectory) {
console.log("Start processing " + path);
var css = fs.readFileSync(path, 'utf8');
var counter = 0;
var processedCss = css.replace(srcUrlRegex, function (srcUrl) {
if (srcUrl.search(outputDirectory) != -1)
return srcUrl;
var fileName = getFileName(srcUrl);
var processedUrl = "url('" + outputDirectory + "/" + fileName + "')";
counter++;
console.log("Replaced " + srcUrl + " -> " + processedUrl);
return processedUrl;
});
console.log("Replaced " + counter + " urls");
fs.writeFileSync(path, processedCss);
}
function getFileName(srcUrl) {
var url = srcUrl.substring(4, srcUrl.length - 1); // unbox 'url(...)'
url = url.replace(/(\\?["'])/g, '');
var urlPaths = url.split('/');
return urlPaths[urlPaths.length - 1].split('?')[0];
}

View file

@ -0,0 +1,33 @@
const fs = require('fs');
const srcUrlRegex = /url\((\\?["'])?(?!data:)\S+(\\?["'])?\)/g;
export function normalizeCssPaths(params: {paths: string[], outDir: string}) {
params.paths = params.paths ? params.paths : [];
params.paths.forEach(path => normalizeCssPath(path, params.outDir));
}
function normalizeCssPath(path: string, outputDirectory: string) {
console.log(`Start processing ${path}`);
const css: string = fs.readFileSync(path, 'utf8');
let counter = 0;
const processedCss = css.replace(srcUrlRegex, (srcUrl: string) => {
if (srcUrl.search(outputDirectory) != -1) return srcUrl;
let fileName = getFileName(srcUrl);
let processedUrl = `url('${outputDirectory}/${fileName}')`;
counter++;
console.log(`Replaced ${srcUrl} -> ${processedUrl}`);
return processedUrl;
});
console.log(`Replaced ${counter} urls`);
fs.writeFileSync(path, processedCss);
}
function getFileName(srcUrl: string): string {
let url = srcUrl.substring(4, srcUrl.length - 1); // unbox 'url(...)'
url = url.replace(/(\\?["'])/g, '');
let urlPaths = url.split('/');
return urlPaths[urlPaths.length - 1].split('?')[0];
}

File diff suppressed because it is too large Load diff

View file

@ -26,8 +26,8 @@
"@angular/platform-browser": "7.2.15",
"@angular/platform-browser-dynamic": "7.2.15",
"@angular/router": "7.2.15",
"@ng-bootstrap/ng-bootstrap": "4.1.1",
"@webbpm/base-package": "3.178.2",
"@ng-bootstrap/ng-bootstrap": "4.2.2-micord.1",
"ag-grid-angular": "29.0.0-micord.4",
"ag-grid-community": "29.0.0-micord.4",
"angular-calendar": "0.28.28",
@ -61,7 +61,7 @@
"systemjs": "0.21.4",
"systemjs-plugin-babel": "0.0.25",
"tslib": "1.9.3",
"zone.js": "0.8.29"
"zone.js": "0.11.8"
},
"devDependencies": {
"@angular-devkit/build-optimizer": "0.13.9",
@ -69,33 +69,36 @@
"@angular/cli": "7.3.9",
"@angular/compiler-cli": "7.2.15",
"@angular/platform-server": "7.2.15",
"@babel/core": "7.9.6",
"@babel/preset-env": "7.9.6",
"@babel/core": "7.18.10",
"@babel/preset-env": "7.18.10",
"@types/bootstrap": "3.3.39",
"@types/eslint": "7.2.5",
"@types/jquery": "2.0.49",
"@types/node": "7.0.5",
"@types/selectize": "0.12.33",
"angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2",
"babel-loader": "8.1.0",
"babel-loader": "9.1.2",
"codelyzer": "5.2.1",
"copy-webpack-plugin": "5.0.3",
"cross-env": "5.2.1",
"css-loader": "2.1.0",
"css-loader": "6.11.0",
"del": "2.2.2",
"file-loader": "3.0.1",
"html-webpack-plugin": "4.5.2",
"dotenv": "^16.4.5",
"dotenv-webpack": "^8.1.0",
"file-loader": "6.2.0",
"html-webpack-plugin": "5.6.0",
"lite-server": "2.3.0",
"mini-css-extract-plugin": "0.6.0",
"mini-css-extract-plugin": "2.9.1",
"mkdirp": "0.5.1",
"raw-loader": "1.0.0",
"style-loader": "0.23.1",
"terser-webpack-plugin": "1.2.4",
"raw-loader": "4.0.2",
"style-loader": "3.3.4",
"terser-webpack-plugin": "5.3.10",
"tslint": "5.13.1",
"typescript": "3.2.4",
"typescript-parser": "2.6.1-cg-fork",
"webpack": "4.32.2",
"webpack-bundle-analyzer": "3.3.2",
"webpack-cli": "3.3.2"
"typescript-parser": "2.6.1-cg.2",
"webpack": "5.90.1",
"webpack-bundle-analyzer": "4.10.1",
"webpack-cli": "5.0.2"
}
}

View file

@ -4,7 +4,7 @@
<parent>
<groupId>ru.micord.ervu</groupId>
<artifactId>dashboard</artifactId>
<version>1.8.0-SNAPSHOT</version>
<version>1.8.0</version>
</parent>
<groupId>ru.micord.ervu.dashboard</groupId>

View file

@ -0,0 +1,45 @@
/*
* @font-face from bootstrap-icons
*/
@font-face {
font-display: block;
font-family: "bootstrap-icons";
src: url("../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff2"),
url("../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff");
}
/*
* @font-face from font-awesome
*/
@font-face {
font-family: 'FontAwesome';
src: url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.eot?v=4.7.0');
src: url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),
url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),
url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),
url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),
url('../../../node_modules/font-awesome/fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'GilroyL';
src: url('../fonts/gilroy-light.otf');
}
@font-face {
font-family: 'Gilroy';
src: url('../fonts/gilroy-regular.ttf');
}
@font-face {
font-family: 'GilroyM';
src: url('../fonts/gilroy-medium.ttf');
}
@font-face {
font-family: 'GilroySB';
src: url('../fonts/gilroy-semibold.ttf');
}
@font-face {
font-family: 'GilroyEB';
src: url('../fonts/gilroy-extrabold.otf');
}

View file

@ -1,24 +1,3 @@
@font-face {
font-family: 'GilroyL';
src: url('../fonts/gilroy-light.otf');
}
@font-face {
font-family: 'Gilroy';
src: url('../fonts/gilroy-regular.ttf');
}
@font-face {
font-family: 'GilroyM';
src: url('../fonts/gilroy-medium.ttf');
}
@font-face {
font-family: 'GilroySB';
src: url('../fonts/gilroy-semibold.ttf');
}
@font-face {
font-family: 'GilroyEB';
src: url('../fonts/gilroy-extrabold.otf');
}
.webbpm.ervu_dashboard {
--black: #070e1a;
--color-text-primary: #f4fcff;
@ -82,7 +61,7 @@
--w-screen: min(2vw, 2.5rem); /*40*/
}
body.webbpm.ervu_dashboard {
.webbpm.ervu_dashboard {
display: flex;
flex-direction: column;
color: var(--color-text-primary);
@ -141,15 +120,61 @@ body.webbpm.ervu_dashboard {
.webbpm.ervu_dashboard .header-menu process,
.webbpm.ervu_dashboard .header-menu process + div,
.webbpm.ervu_dashboard .header-menu admin-menu {
display: none !important;
}
.webbpm.ervu_dashboard .header-menu .update-data {
display: none;
color: var(--color-text-primary);
opacity: 0.4;
margin-right: 16px;
}
.webbpm.ervu_dashboard .header-menu .user-data .user-data-name,
.webbpm.ervu_dashboard .header-menu .user-data .user-data-name:is(:focus, :active) {
color: var(--color-text-primary);
font-size: var(--size-text-primary);
padding: var(--indent-mini) var(--indent-huge) var(--indent-mini) var(--indent-xbase);
margin: 0;
border-radius: 100px;
border: 1px solid var(--color-success);
background: var(--color-dark-20);
box-shadow: none;
outline: transparent;
}
.webbpm.ervu_dashboard .header-menu .user-data.show .user-data-name {
background-color: var(--color-success);
}
.webbpm.ervu_dashboard .header-menu .user-data .user-data-name::after {
position: absolute;
content: "\f282";
color: var(--color-text-primary);
font-family: bootstrap-icons;
font-weight: 800;
font-size: var(--size-text-secondary);
top: calc(50% - var(--size-text-secondary) / 2);
right: var(--indent-base);
margin: 0;
border: 0;
}
.webbpm.ervu_dashboard .header-menu .user-data.show .user-data-name::after {
top: calc(50% - (var(--size-text-secondary) + var(--indent-xmini)) / 2);
transform: rotate(180deg);
}
.webbpm.ervu_dashboard .header-menu .user-data .dropdown-menu {
width: max-content;
max-width: 600px;
padding: var(--indent-mini) 0;
border-radius: 24px;
border: 1px solid var(--color-text-secondary);
background: var(--color-dark-20);
backdrop-filter: var(--bg-blur-40);
box-shadow: var(--bg-shadow);
}
.webbpm.ervu_dashboard .header-menu .user-data .dropdown-item {
color: var(--color-text-primary);
white-space: normal;
background-color: transparent;
}
.webbpm.ervu_dashboard .header-menu .logout {
max-width: max-content;
}
@ -186,45 +211,6 @@ body.webbpm.ervu_dashboard {
align-items: center;
}
.webbpm.ervu_dashboard .header .dropdown-menu.show {
top: var(--h-header) !important;
right: 0px !important;
left: auto !important;
transform: none !important;
margin: 0;
border: 0;
border-radius: 0 0 10px 10px;
background-color: var(--white);
box-shadow: 0 8px 12px rgb(77 72 91 / 5%), 0 6px 10px rgb(77 72 91 / 0%);
}
.webbpm.ervu_dashboard .header .dropdown-menu.show .dropdown-menu-inner {
max-height: calc(100vh - 140px);
overflow-y: auto;
}
.webbpm.ervu_dashboard .header :is(process, admin-menu) .dropdown-menu.show {
top: 49px !important;
}
.webbpm.ervu_dashboard .header .logout .dropdown-menu.show {
width: 300px;
}
.webbpm.ervu_dashboard .dropdown-menu-inner:hover {
background-color: transparent;
}
.webbpm.ervu_dashboard .dropdown-item {
padding: 4px 20px;
}
.webbpm.ervu_dashboard .dropdown-item:hover,
.webbpm.ervu_dashboard .dropdown-item:focus,
.webbpm.ervu_dashboard .dropdown-item:active {
color: var(--color-link);
background-color: transparent;
outline: transparent;
}
/*----------------- Login ---------------- */
.webbpm.ervu_dashboard :is(.form-signin, .form-signup, .confirm) {
color: var(--color-text-primary);
@ -329,6 +315,10 @@ body.webbpm.ervu_dashboard {
--size-num-addtitle: 2rem; /*32px*/
--size-num-primary: 1.75rem; /*28px*/
}
.webbpm.ervu_dashboard .header-menu .user-data .dropdown-menu {
max-width: calc(100vw - var(--w-screen) * 2);
}
}
.webbpm.ervu_dashboard .progress {

View file

@ -23,7 +23,7 @@
box-sizing: border-box;
}
body.webbpm .form-signin label {
.webbpm .form-signin label {
width: 160px;
margin-right: 0;
}
@ -99,13 +99,13 @@ body.webbpm .form-signin label {
}
/*-- layout --*/
html, body.webbpm {
html, .webbpm {
width: 100%;
height: 100%;
display: block;
}
body.webbpm {
.webbpm {
background-color: #f9f9fa;
font-family: Arial;
font-size: var(--size-text-secondary);

View file

@ -5,6 +5,7 @@
@import "../../../node_modules/bootstrap-icons/font/bootstrap-icons.css";
@import "../../../node_modules/font-awesome/css/font-awesome.css";
@import "../../../node_modules/@webbpm/base-package/css/style.css";
@import "font-faces.css";
@import "structure.css";
@import "inbox-dashboard.css";
@import "components-dashboard.css";

View file

@ -4,7 +4,12 @@
<div class="logo-title">ЕДИНЫЙ РЕЕСТР<br />ВОИНСКОГО УЧЕТА</div>
</div>
<div class="header-menu">
<div class="update-data">Данные на 14 августа 2024 г.</div>
<div ngbDropdown class="logout" log-out></div>
<!--<div class="update-data">Данные на 14 августа 2024 г.</div>-->
<div ngbDropdown class="user-data" *ngIf="name && realm">
<button ngbDropdownToggle class="user-data-name">{{name | emptyIfNull}}</button>
<div ngbDropdownMenu>
<div ngbDropdownItem class="user-data-realm">{{realm | emptyIfNull}}</div>
</div>
</div>
</div>
</nav>

View file

@ -0,0 +1,39 @@
<div class="form-signin">
<form #formComponent="ngForm">
<div class="row">
<label>Имя</label>
<div class="input-group">
<input type="text" name="name" class="form-control" placeholder="Имя" required autofocus
[(ngModel)]="name">
</div>
</div>
<div class="row">
<label>Название подразделения</label>
<div class="input-group">
<input type="text" name="realm" class="form-control" placeholder="Название подразделения" required
[(ngModel)]="realm">
</div>
</div>
<div class="row">
<label>Роль</label>
<div class="input-group">
<input type="text" name="role" class="form-control" placeholder="Роль" required
[(ngModel)]="role">
</div>
</div>
<div class="row">
<label>Domain id</label>
<div class="input-group">
<input type="text" name="domain_id" class="form-control" placeholder="Id подразделения" required
[(ngModel)]="recruitmentId">
</div>
</div>
<div class="login-btn-box">
<button type="submit" class="btn btn-primary" (click)="enter()">Войти</button>
</div>
</form>
</div>

View file

@ -0,0 +1,14 @@
<div webbpm class="webbpm ervu_dashboard">
<div class="wrapper">
<app-header *ngIf="headerVisible">
</app-header>
<div class="container">
<div class="container-inside" id="webbpm-angular-application-container">
<router-outlet></router-outlet>
</div>
</div>
<app-footer *ngIf="footerVisible">
</app-footer>
</div>
</div>

View file

@ -0,0 +1 @@
<mfe-webbpm style="all: initial; display: block; height: 100vh"></mfe-webbpm>

View file

@ -1,71 +0,0 @@
import {
AnalyticalScope,
Behavior,
DateTimePicker,
NotNull,
ObjectRef,
ScriptLocationError,
TextField
} from "@webbpm/base-package";
@AnalyticalScope(TextField)
export class BirthDateRageCalculator extends Behavior {
@ObjectRef()
@NotNull()
public startDate: DateTimePicker;
@ObjectRef()
@NotNull()
public endDate: DateTimePicker;
private ageField: TextField;
private onUpdateFunction: Function;
public initialize() {
super.initialize();
this.ageField = this.getScript(TextField);
if (!this.ageField) {
throw new ScriptLocationError(TextField.name, this);
}
this.onUpdateFunction = () => {
const age = this.ageField.getTextValue();
this.startDate.setValueAsDate(this.getStartDate(age));
this.endDate.setValueAsDate(this.getEndDate(age));
}
}
public bindEvents() {
super.bindEvents();
this.ageField.addUserChangeValueListener(this.onUpdateFunction);
}
public unbindEvents() {
super.unbindEvents();
this.ageField.removeUserChangeValueListener(this.onUpdateFunction);
}
protected getStartDate(age: string) : Date {
if (age !== null && age !== "") {
const date = new Date(
new Date().getFullYear() - Number(age) - 1,
new Date().getMonth(),
new Date().getDay()
);
date.setDate(date.getDate() + 1);
return date;
}
return null;
}
protected getEndDate(age: string) : Date {
if (age !== null && age !== "") {
return new Date(
new Date().getFullYear() - Number(age),
new Date().getMonth(),
new Date().getDay(),
);
}
return null;
}
}

View file

@ -1,74 +0,0 @@
import {
AnalyticalScope,
Behavior,
ControlWithValue,
NotNull,
ObjectRef,
Text
} from "@webbpm/base-package";
@AnalyticalScope(Text)
export class UnionAgeAndBirthDateTextScript extends Behavior {
@ObjectRef()
@NotNull()
public inputBirthDate: ControlWithValue;
private text: Text;
private readonly onChangeFunction = () => {
if (this.text) {
const birthDate = this.parseToIsoFormat(this.inputBirthDate.getTextValue());
this.text.setValue(this.format(new Date(birthDate)));
}
}
initialize() {
super.initialize();
this.text = this.getScript(Text);
}
bindEvents() {
super.bindEvents();
this.inputBirthDate.addChangeListener(this.onChangeFunction)
}
unbindEvents() {
super.unbindEvents();
this.inputBirthDate.removeChangeListener(this.onChangeFunction);
}
private format(birthdate: Date): string {
const age = this.calculateAge(birthdate)
const ageString = this.getTextAge(age);
const strBirthDate = birthdate.getFullYear();
return `${age} ${ageString}, ${strBirthDate}`;
}
private getTextAge(age: number) {
if (age % 10 === 1 && age % 100 !== 11) {
return 'год';
} else if (age % 10 >= 2 && age % 10 <= 4 &&
(age % 100 < 10 || age % 100 >= 20)) {
return 'года';
} else {
return 'лет';
}
}
private calculateAge(birthDate: Date) {
const currentDate = new Date();
let age = currentDate.getFullYear() - birthDate.getFullYear();
const difMonthValue = currentDate.getMonth() - birthDate.getMonth();
if (difMonthValue < 0 || (difMonthValue === 0 && currentDate.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
private parseToIsoFormat(date: string): string {
let strings = date.split('.');
return `${strings[2]}-${1}-${strings[0]}`;
}
}

View file

@ -1,21 +0,0 @@
import {AnalyticalScope, Text} from "@webbpm/base-package";
import {ReplaceValueTextFormatter} from "./ReplaceValueTextFormatter";
@AnalyticalScope(Text)
export class PhoneNumberTextFormatter extends ReplaceValueTextFormatter {
format(value: string): string {
const newValue = super.format(value);
if (newValue !== value) {
return newValue;
}
return this.formatNumber(value);
}
private formatNumber(number: string): string {
let cleanedNumber = number.replace(/\D/g, '');
if (cleanedNumber.length === 11 && (cleanedNumber.startsWith('7') || cleanedNumber.startsWith('8'))) {
cleanedNumber = cleanedNumber.slice(1)
return cleanedNumber.replace(/^(\d{3})(\d{3})(\d{2})(\d{2})$/, '+7 ($1) $2-$3-$4');
}
return number;
}
}

View file

@ -0,0 +1,74 @@
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {MfeConfigurationProvider} from "./modules/mfe/provider/mfe-configuration.provider";
import {NgModuleRef} from "@angular/core";
let childEventHandlerFromContainer = null;
export type ChildEventType = 'navigate' | 'token-request'
export type ParentEventType = 'navigate';
export function fireMfeEventToContainer(eventType: ChildEventType, eventData: any): Promise<any> {
if (typeof childEventHandlerFromContainer === 'function') {
return childEventHandlerFromContainer(eventType, eventData);
}
else {
throw new Error(
'Event fired from child MFE to container before being bootstrapped as MFE App',
);
}
}
export function bootstrapMfeApp(createApp: () => Promise<NgModuleRef<unknown> | void>) {
function mount(
element: HTMLElement,
settings: {
standalone: boolean,
useShadowDom?: boolean,
containerBaseUrl?: string,
componentBaseUrl?: string,
startUrl: string,
childEventHandler?: any,
params?: any
} = {
standalone: false,
startUrl: '/mfe/dashboard'
},
) {
let containerBaseUrl = settings.containerBaseUrl || ''; // префикс ресурса
let startUrl = settings.startUrl || ''; // ресурс хост-приложения
MfeConfigurationProvider.setPageBaseUrl(joinPath(containerBaseUrl, startUrl));
element.appendChild(createContainerForBootstrap())
childEventHandlerFromContainer = settings.childEventHandler;
createApp();
return {
parentEventHandler(eventType: ParentEventType, url: string) {
},
unmount() {
console.log("Unmounting dashboard application");
platformBrowserDynamic().destroy();
},
}
}
return {
mount,
};
}
function createContainerForBootstrap(): HTMLElement {
let mfeBootstrapContainer = document.createElement('div');
mfeBootstrapContainer.setAttribute(MfeConfigurationProvider.BASE_COMPONENT_ATTRIBUTE, '');
return mfeBootstrapContainer;
}
export function joinPath(...paths: string[]): string {
return '/' + paths
.filter(path => path)
.map(path => path.endsWith('/') ? path.substring(0, path.length - 1) : path)
.map(path => path.startsWith('/') ? path.substring(1, path.length) : path)
.join('/')
}

View file

@ -0,0 +1,18 @@
import 'zone.js/dist/zone';
import {enableProdMode} from "@angular/core";
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {bootstrapMfeApp} from './mfe-app-tools';
// @ts-ignore
import {MfeWebbpmModuleNgFactory} from "./modules/mfe/mfe-webbpm.module.ngfactory";
window['dev_mode'] = false;
enableProdMode();
const mount = bootstrapMfeApp(() => {
return platformBrowserDynamic()
.bootstrapModuleFactory(MfeWebbpmModuleNgFactory)
.catch((err) => console.error(err));
}).mount;
export {mount};

View file

@ -1,56 +1,53 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {ConfirmExitGuard} from "@webbpm/base-package";
import {RolesGuard} from "./guard/RolesGuard";
const appRoutes: Routes = [
{
path: 'access-denied',
component: AccessDeniedComponent,
canActivate: [ConfirmExitGuard]
},
{
path: 'milregistration',
loadChildren: 'generated-sources/page-milregistration.module#PagemilregistrationModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'appeals',
loadChildren: 'generated-sources/page-appeals.module#PageappealsModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'ratings',
loadChildren: 'generated-sources/page-ratings.module#PageratingsModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'recruitment',
loadChildren: 'generated-sources/page-recruitment.module#PagerecruitmentModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'notregistered',
loadChildren: 'generated-sources/page-notregistered.module#PagenotregisteredModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'citizen',
loadChildren: 'generated-sources/page-citizen.module#PagecitizenModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'citizencard/:id',
loadChildren: 'generated-sources/page-citizencard.module#PagecitizencardModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
},
{
path: 'main',
loadChildren: 'generated-sources/page-main.module#PagemainModule',
canActivate: [ConfirmExitGuard, RolesGuard]
},
{
path: 'total_registered_second',
loadChildren: 'generated-sources/page-total_registered_second.module#Pagetotal_registered_secondModule',
canActivate: [ConfirmExitGuard, RolesGuard]
}
];

View file

@ -13,7 +13,6 @@ import {
} from "@webbpm/base-package";
import {AppHeaderComponent} from "./component/app-header.component";
import {AppFooterComponent} from "./component/app-footer.component";
import {AccessDeniedComponent} from "./component/access-denied.component";
import {ApplicationVersionComponent} from "./component/application-version.component";
import {RouterModule} from "@angular/router";
import {InternationalPhoneNumberModule} from "ngx-international-phone-number";
@ -21,6 +20,7 @@ import {AppProgressIndicationComponent} from "./component/app-progress-indicatio
import {AppProgressIndicationService} from "./service/app-progress-indication.service";
import {ErvuChartV2} from "../../ervu-dashboard/component/chart/ErvuChartV2";
import {FilterContainer} from "../../ervu-dashboard/component/filter/FilterContainer";
import {TokenInterceptor} from "./interceptor/token-interceptor.service";
import {DropdownTreeViewComponent} from "../../component/field/DropdownTreeViewComponent";
import {DropdownTreeviewSelectComponent} from "../../component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component";
import {DropdownTreeviewComponent} from "../../component/external/ngx-treeview/lib/dropdown-treeview.component";
@ -33,7 +33,6 @@ export const DIRECTIVES = [
forwardRef(() => AppHeaderComponent),
forwardRef(() => AppFooterComponent),
forwardRef(() => ApplicationVersionComponent),
forwardRef(() => AccessDeniedComponent),
forwardRef(() => AppProgressIndicationComponent),
forwardRef(() => ErvuChartV2),
forwardRef(() => FilterContainer),
@ -65,7 +64,8 @@ export const DIRECTIVES = [
DIRECTIVES
],
providers: [
{ provide: ProgressIndicationService, useClass: AppProgressIndicationService }
TokenInterceptor,
{provide: ProgressIndicationService, useClass: AppProgressIndicationService}
],
bootstrap: [],
entryComponents: [AppProgressIndicationComponent]

View file

@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component} from "@angular/core";
import {Router} from "@angular/router";
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from "@angular/core";
import {AuthorizationService} from "../service/authorization.service";
@Component({
moduleId: module.id,
@ -9,7 +9,16 @@ import {Router} from "@angular/router";
})
export class AppHeaderComponent {
name: string;
realm: string;
constructor(protected router: Router) {
constructor(protected authService: AuthorizationService,
protected cd: ChangeDetectorRef) {
authService.onSessionUpdate
.subscribe(session => {
this.name = session.name;
this.realm = session.realm;
cd.markForCheck()
})
}
}

View file

@ -0,0 +1,48 @@
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree
} from "@angular/router";
import {Observable} from "rxjs";
import {Injectable} from "@angular/core";
import {AuthorizationService} from "../service/authorization.service";
import {TokenProvider} from "../provider/token.provider";
@Injectable({providedIn: 'root'})
export class RolesGuard implements CanActivate {
protected readonly allowedRoles: string[] = [];
constructor(protected authService: AuthorizationService,
protected tokenProvider: TokenProvider,
protected router: Router) {
}
async canActivate(
route: ActivatedRouteSnapshot, state: RouterStateSnapshot
): Promise<boolean | UrlTree> {
if (!await this.tokenProvider.getToken()) {
return this.getUrlOnFailure()
}
if (!this.authService.isAuthorized()) {
return this.authService.getCurrentSession()
.then(() => this.checkRoles() ? true : this.getUrlOnFailure())
.catch(() => this.getUrlOnFailure());
}
else {
return this.checkRoles();
}
}
protected getUrlOnFailure(): UrlTree {
return null;
}
protected checkRoles(): boolean {
return this.allowedRoles.length === 0
|| this.authService.hasAnyRole(this.allowedRoles);
}
}

View file

@ -0,0 +1,21 @@
import {HttpRequest, HttpHandler, HttpEvent, HttpInterceptor} from "@angular/common/http";
import {from, Observable} from "rxjs";
import {Injectable} from "@angular/core";
import {TokenProvider} from "../provider/token.provider";
@Injectable({providedIn: 'root'})
export class TokenInterceptor implements HttpInterceptor {
constructor(protected tokenProvider: TokenProvider) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.handle(request, next))
}
private async handle(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
const token = await this.tokenProvider.getToken();
request = request.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next.handle(request).toPromise();
}
}

View file

@ -0,0 +1,3 @@
export class TokenProvider {
public getToken(): Promise<string> { return null }
}

View file

@ -10,16 +10,16 @@ export class AppProgressIndicationService {
event.stopPropagation();
};
private counter: number = 0;
private focused: any;
private ngbModalRef: NgbModalRef;
private options: NgbModalOptions = {
protected counter: number = 0;
protected focused: any;
protected ngbModalRef: NgbModalRef;
protected options: NgbModalOptions = {
backdrop: 'static',
keyboard: false,
windowClass: 'modal-center loader'
};
constructor(private ngbModal: NgbModal) {
constructor(protected ngbModal: NgbModal) {
}
public showProgressBar(): boolean {
@ -58,11 +58,11 @@ export class AppProgressIndicationService {
}
}
private showProgressIndicator() {
protected showProgressIndicator() {
this.ngbModalRef = this.ngbModal.open(AppProgressIndicationComponent, this.options);
}
private hideProgressIndicator() {
protected hideProgressIndicator() {
this.ngbModalRef.dismiss('cancel');
this.ngbModalRef = null;
}

View file

@ -0,0 +1,55 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Subject} from "rxjs";
export interface UserSession {
name: string,
realm: string,
recruitmentId: string,
roles: string[]
}
@Injectable({providedIn: 'root'})
export class AuthorizationService {
private session: UserSession;
public onSessionUpdate: Subject<UserSession> = new Subject<UserSession>();
constructor(protected httpClient: HttpClient) {}
public getCurrentSession(): Promise<any> {
if (this.session) return new Promise(resolve => resolve(this.session))
return this.httpClient.get('session')
.toPromise()
.then((session: UserSession) => {
this.session = session;
this.onSessionUpdate.next(session);
return session;
})
}
isAuthorized(): boolean {
return !!this.session;
}
hasAnyRole(roles: string[]): boolean {
return this.isAuthorized() && roles.some(role => this.getRoles().includes(role));
}
getName(): string {
return this.isAuthorized() ? this.session.name : null;
}
getRealm(): string {
return this.isAuthorized() ? this.session.realm : null;
}
getRecruitmentId(): string {
return this.isAuthorized() ? this.session.recruitmentId : null;
}
getRoles(): string[] {
return this.isAuthorized() ? this.session.roles : null;
}
}

View file

@ -0,0 +1,36 @@
import {Component, ViewEncapsulation} from "@angular/core";
import {
Event,
NavigationCancel,
NavigationEnd,
NavigationError,
NavigationStart,
Router
} from "@angular/router";
import {ProgressIndicationService} from "@webbpm/base-package";
@Component({
moduleId: module.id,
encapsulation: ViewEncapsulation.ShadowDom,
selector: 'mfe-webbpm',
templateUrl: './../../../../../src/resources/template/webbpm/mfe-webbpm.html',
styleUrls: ['./../../../../../src/resources/css/style.css'],
})
export class MfeWebbpmComponent {
public headerVisible: boolean = true;
public footerVisible: boolean = true;
constructor(private router: Router,
private progressIndicationService: ProgressIndicationService) {
router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
progressIndicationService.showProgressBar();
}
else if (event instanceof NavigationEnd
|| event instanceof NavigationError
|| event instanceof NavigationCancel) {
progressIndicationService.hideProgressBar();
}
})
}
}

View file

@ -0,0 +1,12 @@
import {Component} from "@angular/core";
import {MfeConfigurationProvider} from "../provider/mfe-configuration.provider";
@Component({
moduleId: module.id,
selector: `[${MfeConfigurationProvider.BASE_COMPONENT_ATTRIBUTE}]`,
templateUrl: './../../../../../src/resources/template/webbpm/mfe-wrapper.html',
styleUrls: ['./../../../../../src/resources/css/font-faces.css']
})
export class MfeWrapperComponent {
}

View file

@ -0,0 +1,10 @@
import {RolesGuard} from "../../app/guard/RolesGuard";
import {UrlTree} from "@angular/router";
import {Injectable} from "@angular/core";
@Injectable({providedIn: 'root'})
export class MfeRolesGuard extends RolesGuard {
protected getUrlOnFailure(): UrlTree {
return this.router.createUrlTree(['access-denied']);
}
}

View file

@ -0,0 +1,14 @@
import {HTTP_INTERCEPTORS} from "@angular/common/http";
import {
FormDirtyInterceptor,
HttpSecurityErrorInterceptor
} from "@webbpm/base-package";
import {TokenInterceptor} from "../../app/interceptor/token-interceptor.service";
import {MfeHttpBackendInterceptor} from "./mfe-http-backend-interceptor";
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
{provide: HTTP_INTERCEPTORS, useClass: MfeHttpBackendInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityErrorInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true}
];

View file

@ -0,0 +1,66 @@
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from "@angular/common/http";
import {Observable} from "rxjs";
import {
AppConfigService,
ApplicationSettingsProvider,
AppVersionService,
TokenHeaderUtil
} from "@webbpm/base-package";
import {MfeConfigurationProvider} from "../provider/mfe-configuration.provider";
import {joinPath} from "../../../mfe-app-tools";
export class MfeHttpBackendInterceptor implements HttpInterceptor {
private static readonly CONTENT_TYPE_HEADER = 'Content-Type';
private static readonly ENABLE_VERSION_IN_URL: string = "enable.version.in.url";
private static readonly BACKEND_URL: string = "backend.url";
private static readonly BACKEND_CONTEXT: string = "backend.context";
constructor(private appConfigService: AppConfigService,
private appVersionService: AppVersionService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = TokenHeaderUtil.addHeader(
req, MfeHttpBackendInterceptor.CONTENT_TYPE_HEADER, 'application/json;charset=UTF-8'
);
let overrideObj
if (!req.url.startsWith(ApplicationSettingsProvider.RESOURCES_PATH)
&& !req.url.startsWith('http:') && !req.url.startsWith('https:')) {
let appVersionInUrl = this.getVersion();
let backendUrl = this.appConfigService.getParamValue(MfeHttpBackendInterceptor.BACKEND_URL);
let backendContext = this.appConfigService.getParamValue(
MfeHttpBackendInterceptor.BACKEND_CONTEXT);
let url;
if (backendUrl) {
url = backendUrl;
}
else if (backendContext) {
url = joinPath(
MfeConfigurationProvider.MFE_BASE_URL,
backendContext
);
}
else {
url = ApplicationSettingsProvider.BACKEND_URL;
}
overrideObj = {url: `${url}${appVersionInUrl}/${req.url}`};
}
return next.handle(req.clone(overrideObj));
}
private getVersion(): string {
return this.appConfigService.getParamValue(MfeHttpBackendInterceptor.ENABLE_VERSION_IN_URL) ==
"true" ? "-" + this.appVersionService.getAppVersion() : "";
}
}

View file

@ -0,0 +1,41 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {
ConfirmExitGuard
} from "@webbpm/base-package";
import {APP_BASE_HREF} from "@angular/common";
import {MfeConfigurationProvider} from "./provider/mfe-configuration.provider";
import {RolesGuard} from "../app/guard/RolesGuard";
import {AccessDeniedComponent} from "../app/component/access-denied.component";
const webbpmRoutes: Routes = [
{
path: 'access-denied',
component: AccessDeniedComponent,
},
{
path: '',
loadChildren: 'generated-sources/page-main.module#PagemainModule',
canActivate: [ConfirmExitGuard, RolesGuard],
pathMatch: 'full',
},
{
path: '**',
redirectTo: '',
}
];
@NgModule({
providers: [
{provide: APP_BASE_HREF, useFactory: () => MfeConfigurationProvider.getPageBaseUrl()}
],
imports: [RouterModule.forRoot(webbpmRoutes, {
useHash: false,
onSameUrlNavigation: "reload"
})],
exports: [RouterModule]
})
export class MfeWebbpmRoutingModule {
}

View file

@ -0,0 +1,68 @@
import {APP_INITIALIZER, ErrorHandler, forwardRef, NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {FormsModule} from "@angular/forms";
import {AgGridModule} from "ag-grid-angular";
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
import {ToastNoAnimationModule} from "ngx-toastr";
import {AppModule} from "../app/app.module";
import {
AppConfigService,
AppVersionService,
BpmnModule,
ComponentsModule,
CoreModule, ProgressIndicationService,
} from "@webbpm/base-package";
import {AppRoutingModule} from "../app/app-routing.module";
import {GlobalErrorHandler} from "../webbpm/handler/global-error.handler.prod";
import {DEFAULT_HTTP_INTERCEPTOR_PROVIDERS} from "./interceptor/mfe-default-interceptors.prod";
import {MfeAppVersionService} from "./service/mfe-app-version.service";
import {MfeAppConfigService} from "./service/mfe-app-config.service";
import {MfeWebbpmComponent} from "./component/mfe-webbpm.component";
import {MfeWebbpmRoutingModule} from "./mfe-webbpm-routing.module";
import {MfeWrapperComponent} from "./component/mfe-wrapper.component";
import {MfeProgressIndicationService} from "./service/mfe-progress-indication.service";
import {RolesGuard} from "../app/guard/RolesGuard";
import {MfeRolesGuard} from "./guard/MfeRolesGuard";
import {TokenProvider} from "../app/provider/token.provider";
import {MfeTokenProvider} from "./provider/mfe-token.provider";
import {AccessDeniedComponent} from "../app/component/access-denied.component";
let IMPORTS = [
BrowserAnimationsModule,
BrowserModule,
FormsModule,
NgbModule,
ToastNoAnimationModule.forRoot(),
AgGridModule,
AppRoutingModule,
BpmnModule,
CoreModule,
ComponentsModule,
AppModule,
MfeWebbpmRoutingModule
];
@NgModule({
imports: IMPORTS,
declarations: [
MfeWrapperComponent,
MfeWebbpmComponent,
AccessDeniedComponent
],
exports: [],
providers: [
{provide: AppVersionService, useClass: MfeAppVersionService},
{provide: AppConfigService, useClass: MfeAppConfigService},
{provide: ErrorHandler, useClass: GlobalErrorHandler},
{provide: ProgressIndicationService, useClass: MfeProgressIndicationService},
{provide: RolesGuard, useClass: MfeRolesGuard},
{provide: TokenProvider, useClass: MfeTokenProvider},
DEFAULT_HTTP_INTERCEPTOR_PROVIDERS
],
bootstrap: [
MfeWrapperComponent
]
})
export class MfeWebbpmModule {
}

View file

@ -0,0 +1,14 @@
export class MfeConfigurationProvider {
private static config; // way to avoid webpack optimization
public static MFE_BASE_URL = process.env.MFE_BASE_URL || '';
public static BASE_COMPONENT_ATTRIBUTE = 'mfe-webbpm-dashboard';
public static getPageBaseUrl(): string {
return this.config.PAGE_BASE_URL
}
public static setPageBaseUrl(pageBaseUrl: string) {
if (!this.config) this.config = {};
this.config.PAGE_BASE_URL = pageBaseUrl;
}
}

View file

@ -0,0 +1,8 @@
import {TokenProvider} from "../../app/provider/token.provider";
import {fireMfeEventToContainer} from "../../../mfe-app-tools";
export class MfeTokenProvider extends TokenProvider {
getToken(): Promise<string> {
return fireMfeEventToContainer('token-request', {});
}
}

View file

@ -0,0 +1,28 @@
import {Injectable} from "@angular/core";
import {AppConfigService, ApplicationSettingsProvider} from "@webbpm/base-package";
import {HttpClient} from "@angular/common/http";
import {MfeConfigurationProvider} from "../provider/mfe-configuration.provider";
import {joinPath} from "../../../mfe-app-tools";
@Injectable({providedIn:'root'})
export class MfeAppConfigService extends AppConfigService {
load(): Promise<any> {
let http: HttpClient = this['http'];
let url = joinPath(
MfeConfigurationProvider.MFE_BASE_URL,
ApplicationSettingsProvider.RESOURCES_PATH,
'app-config.json'
)
return http
.get(url)
.toPromise()
.then(configData => {
this['mapParam'] = configData;
})
.catch((error: any): any => {
console.error('Dashboard configuration file "app-config.json" could not be read');
return Promise.reject(error);
});
}
}

View file

@ -0,0 +1,29 @@
import {ApplicationSettingsProvider, AppVersionService} from "@webbpm/base-package";
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {MfeConfigurationProvider} from "../provider/mfe-configuration.provider";
import {joinPath} from "../../../mfe-app-tools";
@Injectable({providedIn:'root'})
export class MfeAppVersionService extends AppVersionService {
load(): Promise<any> {
let http: HttpClient = this['http'];
let url = joinPath(
MfeConfigurationProvider.MFE_BASE_URL,
ApplicationSettingsProvider.RESOURCES_PATH,
'app.version'
)
return http
.get(url, {responseType: 'text'})
.toPromise()
.then(version => {
this['appVersion'] = version;
console.log(`Dashboard application version = ${version}`);
})
.catch((error: any): any => {
console.error('Dashboard file "app.version" could not be read');
return Promise.reject(error);
});
}
}

View file

@ -0,0 +1,20 @@
import {AppProgressIndicationService} from "../../app/service/app-progress-indication.service";
import {Injectable} from "@angular/core";
import {
AppProgressIndicationComponent
} from "../../app/component/app-progress-indication.component";
@Injectable({providedIn: 'root'})
export class MfeProgressIndicationService extends AppProgressIndicationService {
protected showProgressIndicator() {
const options = {...this.options};
options.container = this.getContainerForProgressIndication();
this.ngbModalRef = this.ngbModal.open(AppProgressIndicationComponent, options);
}
getContainerForProgressIndication(): HTMLElement {
return document.querySelector('mfe-webbpm')
.shadowRoot.querySelector('[webbpm]');
}
}

View file

@ -0,0 +1,49 @@
import {Component} from "@angular/core";
import {Router} from "@angular/router";
import {WebbpmTokenProvider} from "../provider/webbpm-token-provider";
@Component({
moduleId: module.id,
selector: 'token-form',
templateUrl: './../../../../../src/resources/template/webbpm/jwt-form.html'
})
export class TokenFormComponent {
name: string = '';
realm: string = '';
recruitmentId: string = '';
role: string = '';
private readonly encodedHeader = this.encodeBase64Url(JSON.stringify({
kid: "69d4a060-4053-4056-ae69-5e9bcf41125f",
typ: "JWT",
alg: "RS512"
}));
constructor(private router: Router) {
}
public enter() {
localStorage.setItem(WebbpmTokenProvider.ACCESS_TOKEN_STORAGE_KEY, this.generateToken());
this.router.navigateByUrl('');
}
private generateToken(): string {
let claims = {
name: this.name,
realm: this.realm,
recruitmentId: this.recruitmentId,
roles: [this.role]
};
return `${this.encodedHeader}.${this.encodeBase64Url(JSON.stringify(claims))}.`
}
private encodeBase64Url(input: string): string {
let bytes = new TextEncoder().encode(input);
const byteArray = Array.from(bytes);
const binString = String.fromCharCode(...byteArray);
return btoa(binString).replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
}

View file

@ -0,0 +1,10 @@
import {RolesGuard} from "../../app/guard/RolesGuard";
import {Injectable} from "@angular/core";
import {UrlTree} from "@angular/router";
@Injectable({providedIn: 'root'})
export class WebbpmRolesGuard extends RolesGuard {
protected getUrlOnFailure(): UrlTree {
return this.router.createUrlTree(['token-form']);
}
}

View file

@ -4,9 +4,11 @@ import {
HttpSecurityErrorInterceptor,
HttpSecurityInterceptor
} from "@webbpm/base-package";
import {TokenInterceptor} from "../../app/interceptor/token-interceptor.service";
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityErrorInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true}
];

View file

@ -1,9 +1,11 @@
import {HTTP_INTERCEPTORS} from "@angular/common/http";
import {FormDirtyInterceptor, HttpSecurityInterceptor} from "@webbpm/base-package";
import {DevHttpSecurityErrorInterceptor} from "./http-security-error-interceptor.dev";
import {TokenInterceptor} from "../../app/interceptor/token-interceptor.service";
export const DEFAULT_HTTP_INTERCEPTOR_PROVIDERS = [
{provide: HTTP_INTERCEPTORS, useClass: HttpSecurityInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: DevHttpSecurityErrorInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true}
{provide: HTTP_INTERCEPTORS, useClass: FormDirtyInterceptor, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true}
];

View file

@ -0,0 +1,12 @@
import {TokenProvider} from "../../app/provider/token.provider";
export class WebbpmTokenProvider implements TokenProvider {
public static readonly ACCESS_TOKEN_STORAGE_KEY = 'accessToken'
getToken(): Promise<string> {
return new Promise(resolve => {
resolve(localStorage.getItem(WebbpmTokenProvider.ACCESS_TOKEN_STORAGE_KEY));
});
}
}

View file

@ -3,12 +3,18 @@ import {RouterModule, Routes} from "@angular/router";
import {
ConfirmExitGuard
} from "@webbpm/base-package";
import {TokenFormComponent} from "./component/token-form.component";
import {RolesGuard} from "../app/guard/RolesGuard";
const webbpmRoutes: Routes = [
{
path: 'token-form',
component: TokenFormComponent
},
{
path: '',
loadChildren: 'generated-sources/page-main.module#PagemainModule',
canActivate: [ConfirmExitGuard],
canActivate: [ConfirmExitGuard, RolesGuard],
pathMatch: 'full',
},
{

View file

@ -1,4 +1,4 @@
import {ErrorHandler, NgModule} from "@angular/core";
import {ErrorHandler, forwardRef, NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {FormsModule} from "@angular/forms";
@ -17,6 +17,11 @@ import {
import {AppRoutingModule} from "../app/app-routing.module";
import {GlobalErrorHandler} from "./handler/global-error.handler.prod";
import {DEFAULT_HTTP_INTERCEPTOR_PROVIDERS} from "./interceptor/default-interceptors.prod";
import {WebbpmTokenProvider} from "./provider/webbpm-token-provider";
import {TokenFormComponent} from "./component/token-form.component";
import {TokenProvider} from "../app/provider/token.provider";
import {RolesGuard} from "../app/guard/RolesGuard";
import {WebbpmRolesGuard} from "./guard/WebbpmRolesGuard";
let IMPORTS = [
BrowserAnimationsModule,
@ -37,11 +42,14 @@ let IMPORTS = [
imports: IMPORTS,
declarations: [
WebbpmComponent,
HomeComponent
HomeComponent,
TokenFormComponent
],
exports: [],
providers: [
{provide: ErrorHandler, useClass: GlobalErrorHandler},
{provide: TokenProvider, useClass: WebbpmTokenProvider},
{provide: RolesGuard, useClass: WebbpmRolesGuard},
DEFAULT_HTTP_INTERCEPTOR_PROVIDERS
],
bootstrap: [

View file

@ -6,17 +6,25 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require('terser-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const {config} = require('dotenv')
const {
container: {ModuleFederationPlugin}
} = webpack;
const {normalizeCssPaths} = require("./normalize-css-path");
function _path(p) {
return path.join(__dirname, p);
}
config();
module.exports = {
mode: 'production',
entry: {
'polyfills': './build/scripts/polyfills.js',
'vendor': './build/scripts/vendor.js',
'main': './build/scripts/main.aot.js'
polyfills: './build/scripts/polyfills.js',
vendor: './build/scripts/vendor.js',
main: './build/scripts/main.aot.js',
},
context: process.cwd(),
@ -24,26 +32,33 @@ module.exports = {
output: {
path: path.join(process.cwd(), './dist'),
filename: '[name].[chunkhash].bundle.js',
chunkFilename: '[id].[chunkhash].chunk.js'
chunkFilename: '[id].[chunkhash].chunk.js',
assetModuleFilename: 'src/resources/[base]',
publicPath: 'auto'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, "node_modules")],
options: {
presets: ['@babel/preset-env']
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.js$/,
loader: 'angular-router-loader?aot=true'
use: {
loader: 'angular-router-loader?aot=true'
}
},
{
test: /\.html$/,
loader: 'raw-loader'
use: {
loader: 'raw-loader'
}
},
{
test: /\.css$/,
@ -56,12 +71,14 @@ module.exports = {
// publicPath: '../'
}
},
"css-loader"
{
loader: 'css-loader'
}
]
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico|otf)$/,
loader: 'file-loader?name=src/resources/[name].[hash].[ext]'
type: 'asset/resource'
}
]
},
@ -69,7 +86,6 @@ module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
@ -83,11 +99,19 @@ module.exports = {
},
plugins: [
new Dotenv(),
() => normalizeCssPaths({
paths: [
'./build/resources/css/font-faces.css.shim.ngstyle.js',
'./build/resources/css/style.css.ngstyle.js'
],
outDir: `${process.env.MFE_BASE_URL || ''}/src/resources`,
}),
// new BundleAnalyzerPlugin(),
new HtmlWebpackPlugin({
template: 'index.webpack.html',
filename: 'index.html',
chunksSortMode : 'none'
chunksSortMode: 'none'
}),
new CopyWebpackPlugin([
{from: 'index.webpack.html', to: 'index.html'},
@ -97,14 +121,21 @@ module.exports = {
{from: 'src/resources/app.version', to: 'src/resources/app.version'}
]),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
filename: '[name].[fullhash].css',
chunkFilename: '[id].[fullhash].css'
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
Popper: ['popper.js', 'default']
}),
new ModuleFederationPlugin({
name: 'dashboard',
filename: 'remoteEntry.js',
exposes: {
"./dashboard": "./build/scripts/mfe-main.aot",
},
})
],