From 041bde1cfc5a05ba87c85af846959b9b839eaa4e Mon Sep 17 00:00:00 2001 From: Ray <11846445+A77AY@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:20:47 +0800 Subject: [PATCH 1/4] feat: integrate Sentry error tracking and session replay with user identification --- .github/workflows/main.yaml | 1 - package.json | 2 +- src/app/app.component.ts | 20 ++++++++++++++++++-- src/app/app.config.ts | 6 ++++++ src/main.ts | 11 ++++++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 660b36cc6..aa9501910 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -29,7 +29,6 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} with: sourcemaps: ./dist/control-center/browser - environment: ${{ startsWith(github.ref_name, 'epic/') && 'staging' || 'production' }} set_commits: skip - name: Remove source maps run: find dist/control-center/browser -type f -name '*.map' -delete diff --git a/package.json b/package.json index 59e10d99b..72527def3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "start": "ng serve", + "start": ". ./.env && ng serve --define \"SENTRY_DSN='${SENTRY_DSN}'\"", "prod": "ng serve --configuration=production", "build": "ng build", "watch": "ng build --watch --configuration development", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 37968da61..ee51e9bbd 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -2,7 +2,14 @@ import Keycloak from 'keycloak-js'; import { debounceTime, map, of, shareReplay, switchMap, tap } from 'rxjs'; import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component, DestroyRef, inject, signal } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + OnInit, + inject, + signal, +} from '@angular/core'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -11,6 +18,7 @@ import { MatSidenavModule } from '@angular/material/sidenav'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Router, RouterOutlet } from '@angular/router'; +import * as Sentry from '@sentry/angular'; import { AppModeService, @@ -221,7 +229,7 @@ const createNavLinks = (): Link[] => [ MatTooltipModule, ], }) -export class AppComponent { +export class AppComponent implements OnInit { private keycloakService = inject(Keycloak); private keycloakUserService = inject(KeycloakUserService); private repositoryService = inject(ThriftRepositoryService); @@ -270,6 +278,14 @@ export class AppComponent { this.registerConsoleUtils(); } + ngOnInit() { + if (SENTRY_DSN) { + this.keycloakUserService.user.getFirstValue().subscribe((user) => { + Sentry.setUser({ id: user.id, username: user.username, email: user.email }); + }); + } + } + logout() { this.keycloakService.logout(); } diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 198117db1..a69e0e1fb 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -7,6 +7,7 @@ import { provideHttpClient, withInterceptorsFromDi, withXhr } from '@angular/com import localeRu from '@angular/common/locales/ru'; import { ApplicationConfig, + ErrorHandler, LOCALE_ID, inject, isDevMode, @@ -18,6 +19,7 @@ import { MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { MatIconRegistry } from '@angular/material/icon'; import { provideRouter, withRouterConfig } from '@angular/router'; +import * as Sentry from '@sentry/angular'; import { ERROR_PARSER, LogError, QUERY_PARAMS_SERIALIZERS } from '@vality/matez'; @@ -43,6 +45,10 @@ registerLocaleData(localeRu); export const appConfig: ApplicationConfig = { providers: [ + { + provide: ErrorHandler, + useValue: Sentry.createErrorHandler(), + }, provideBrowserGlobalErrorListeners(), provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes, withRouterConfig({ paramsInheritanceStrategy: 'always' })), diff --git a/src/main.ts b/src/main.ts index e7a9e0e01..1d64dcc63 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import { isDevMode } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import * as Sentry from '@sentry/angular'; @@ -7,7 +8,15 @@ import { appConfig } from './app/app.config'; if (SENTRY_DSN) { Sentry.init({ dsn: SENTRY_DSN, - integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + environment: isDevMode() ? 'development' : 'production', + integrations: [ + Sentry.browserTracingIntegration(), + Sentry.replayIntegration({ + maskAllText: true, + maskAllInputs: true, + blockAllMedia: true, + }), + ], tracesSampleRate: 1, replaysSessionSampleRate: 1, replaysOnErrorSampleRate: 1, From 4a08c0e086a88ddd891925ab453ab24b4c83293b Mon Sep 17 00:00:00 2001 From: Ray <11846445+A77AY@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:33:34 +0800 Subject: [PATCH 2/4] feat: add environment configuration and expose it as a Sentry tag in production --- public/assets/_appConfig.json | 3 ++- src/app/app.component.ts | 11 ++++++++++- src/services/config/types/app-config.ts | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/public/assets/_appConfig.json b/public/assets/_appConfig.json index 795953363..252ef7ccd 100644 --- a/public/assets/_appConfig.json +++ b/public/assets/_appConfig.json @@ -18,5 +18,6 @@ "category": 2, "termset": 2 } - } + }, + "environment": "production" } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ee51e9bbd..09cde6b5e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,6 +8,7 @@ import { DestroyRef, OnInit, inject, + isDevMode, signal, } from '@angular/core'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; @@ -35,7 +36,7 @@ import { ThriftRepositoryService } from '~/api/services'; import { SidenavInfoModule, SidenavInfoService } from '~/components/sidenav-info'; import { getLimitedDomainObjectDetails } from '~/components/thrift-api-crud'; import { DomainObjectCardComponent } from '~/components/thrift-api-crud/domain'; -import { KeycloakUserService, Service } from '~/services'; +import { ConfigService, KeycloakUserService, Service } from '~/services'; import { LOGGING } from '~/utils'; import { APP_ROUTES } from './app-routes'; @@ -232,6 +233,7 @@ const createNavLinks = (): Link[] => [ export class AppComponent implements OnInit { private keycloakService = inject(Keycloak); private keycloakUserService = inject(KeycloakUserService); + private configService = inject(ConfigService); private repositoryService = inject(ThriftRepositoryService); private router = inject(Router); private dr = inject(DestroyRef); @@ -280,6 +282,13 @@ export class AppComponent implements OnInit { ngOnInit() { if (SENTRY_DSN) { + if (!isDevMode()) { + this.configService.config.getFirstValue().subscribe((config) => { + if (config?.environment) { + Sentry.setTag('environment', config.environment); + } + }); + } this.keycloakUserService.user.getFirstValue().subscribe((user) => { Sentry.setUser({ id: user.id, username: user.username, email: user.email }); }); diff --git a/src/services/config/types/app-config.ts b/src/services/config/types/app-config.ts index 34e5a7971..cc648fc81 100644 --- a/src/services/config/types/app-config.ts +++ b/src/services/config/types/app-config.ts @@ -30,4 +30,5 @@ export interface AppConfig { }; checkout: Endpoint; default: Record; + environment?: 'production' | 'staging'; } From 0a7ce708d002ee3a98a56da90a29a99acba276f1 Mon Sep 17 00:00:00 2001 From: Ray <11846445+A77AY@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:04:10 +0800 Subject: [PATCH 3/4] refactor: rename environment configuration property to tier across application and ignore .env files --- .gitignore | 5 ++++- public/assets/_appConfig.json | 2 +- src/app/app.component.ts | 4 ++-- src/services/config/types/app-config.ts | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 50edd2db7..e1bb39e32 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,7 @@ Thumbs.db # Config appConfig*.json -authConfig*.json \ No newline at end of file +authConfig*.json + +# Env +.env diff --git a/public/assets/_appConfig.json b/public/assets/_appConfig.json index 252ef7ccd..cedb0da85 100644 --- a/public/assets/_appConfig.json +++ b/public/assets/_appConfig.json @@ -19,5 +19,5 @@ "termset": 2 } }, - "environment": "production" + "tier": "production" } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 09cde6b5e..19918167d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -284,8 +284,8 @@ export class AppComponent implements OnInit { if (SENTRY_DSN) { if (!isDevMode()) { this.configService.config.getFirstValue().subscribe((config) => { - if (config?.environment) { - Sentry.setTag('environment', config.environment); + if (config?.tier) { + Sentry.setTag('tier', config.tier); } }); } diff --git a/src/services/config/types/app-config.ts b/src/services/config/types/app-config.ts index cc648fc81..a88551791 100644 --- a/src/services/config/types/app-config.ts +++ b/src/services/config/types/app-config.ts @@ -30,5 +30,5 @@ export interface AppConfig { }; checkout: Endpoint; default: Record; - environment?: 'production' | 'staging'; + tier?: 'production' | 'staging'; } From 8e3188bbac8c557e5dec450feb967da26758265a Mon Sep 17 00:00:00 2001 From: Ray <11846445+A77AY@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:07:30 +0800 Subject: [PATCH 4/4] refactor: remove isDevMode check to enable Sentry tier tagging in development environment --- src/app/app.component.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 19918167d..a96a144ed 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -282,13 +282,11 @@ export class AppComponent implements OnInit { ngOnInit() { if (SENTRY_DSN) { - if (!isDevMode()) { - this.configService.config.getFirstValue().subscribe((config) => { - if (config?.tier) { - Sentry.setTag('tier', config.tier); - } - }); - } + this.configService.config.getFirstValue().subscribe((config) => { + if (config?.tier) { + Sentry.setTag('tier', config.tier); + } + }); this.keycloakUserService.user.getFirstValue().subscribe((user) => { Sentry.setUser({ id: user.id, username: user.username, email: user.email }); });