import { NgModule, CUSTOM_ELEMENTS_SCHEMA, ErrorHandler } from '@angular/core'; import { BrowserModule, HammerModule } from '@angular/platform-browser'; import { RouteReuseStrategy } from '@angular/router'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; import { StatusBar } from '@ionic-native/status-bar/ngx'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { InAppBrowser } from '@ionic-native/in-app-browser/ngx'; import { WebView } from '@ionic-native/ionic-webview/ngx'; import { FilePath } from '@ionic-native/file-path/ngx'; import { Camera } from '@ionic-native/camera/ngx'; import { IonicStorageModule } from '@ionic/storage'; import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CalendarModule, DateAdapter } from 'angular-calendar'; import { adapterFactory } from 'angular-calendar/date-adapters/date-fns'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import {MatDatepickerModule} from '@angular/material/datepicker'; import {MAT_DATE_LOCALE} from '@angular/material/core'; import { Network } from '@ionic-native/network/ngx'; import { MultipleDocumentsPicker } from '@awesome-cordova-plugins/multiple-document-picker/ngx'; import { DocumentViewer } from '@awesome-cordova-plugins/document-viewer/ngx'; import { FFMpeg } from '@awesome-cordova-plugins/ffmpeg/ngx'; import { FFmpeg } from '@ffmpeg/ffmpeg'; import { NgxMatDatetimePickerModule, NgxMatNativeDateModule, NgxMatTimepickerModule } from '@angular-material-components/datetime-picker'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSelectModule } from '@angular/material/select'; import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { HttpClient } from '@angular/common/http'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { fas } from '@fortawesome/free-solid-svg-icons' import { far } from '@fortawesome/free-regular-svg-icons' import { fab } from '@fortawesome/free-brands-svg-icons' import { ScreenOrientation } from '@ionic-native/screen-orientation/ngx'; import { SQLite } from '@ionic-native/sqlite/ngx'; import { CookieService } from 'ngx-cookie-service'; import { ImagePicker } from '@ionic-native/image-picker/ngx'; import { MediaCapture } from '@awesome-cordova-plugins/media-capture/ngx'; import { Media } from '@ionic-native/media/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { StreamingMedia } from '@ionic-native/streaming-media/ngx'; import { PhotoViewer } from '@ionic-native/photo-viewer/ngx'; import {NgxImageCompressService} from 'ngx-image-compress'; import { CustomImageCachePageRoutingModule } from './services/file/custom-image-cache/custom-image-cache-routing.module'; import { IonicImageLoaderModule } from 'ionic-image-loader-v5'; import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer'; import { FileOpener } from '@awesome-cordova-plugins/file-opener/ngx'; import { MatInputModule } from '@angular/material/input'; import { MatNativeDateModule } from '@angular/material/core'; import * as SentrySibling from '@sentry/angular'; import { AngularFireModule } from '@angular/fire'; import { AngularFireMessagingModule } from '@angular/fire/messaging'; import { firebaseConfig } from '../firebase-config'; import { EditorModule } from '@tinymce/tinymce-angular';; import { CreateProcessPageModule } from './modals/create-process/create-process.module'; import { CreateProcessPage } from './modals/create-process/create-process.page'; import { LoggingInterceptorService } from './services/logging-interceptor.service'; import { PopupQuestionPipe } from './modals/popup-question.pipe'; import '@teamhive/capacitor-video-recorder'; import { tokenInterceptor } from './infra/monitoring/interceptors/token.interceptors'; import { InputFilterDirective } from './services/directives/input-filter.directive'; import { DeplomaOptionsPageModule } from './shared/popover/deploma-options/deploma-options.module'; import { DiplomaOptionsPage } from './shared/popover/deploma-options/deploma-options.page'; import { ImageCropperModule } from 'ngx-image-cropper'; import { metricsInterceptor } from './infra/monitoring/interceptors/metter.interceptor'; import {MatMenuModule} from '@angular/material/menu'; import {MatIconModule} from '@angular/material/icon'; import { ChatModule } from './module/chat/chat.module'; import { registerLocaleData } from '@angular/common'; import localePt from '@angular/common/locales/pt'; import { UserModule } from './module/user/user.module'; import { Logger } from './services/logger/main/service'; registerLocaleData(localePt, 'pt'); import * as Sentry from '@sentry/capacitor'; import { Integration } from '@sentry/types'; import { BrowserTracing } from '@sentry/tracing'; import { LogsDatabase } from './infra/database/dexie/instance/logs/service'; import { AppErrorHandler } from './infra/crash-analytics/app-error-handler'; // Sentry.init( // { // dsn: 'https://5b345a3ae70b4e4da463da65881b4aaa@o4504340905525248.ingest.sentry.io/4504345615794176', // // To set your release and dist versions // release: 'gabinetedigital@1.0.0', // dist: '1', // // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. // // We recommend adjusting this value in production. // tracesSampleRate: 1.0, // debug: true, // logs to console // beforeSend(event) { // console.log('event.exception.values[0].value', event.exception.values[0].value); // if (event.level === 'error') { // LogsDatabase.sentryError.add(event as any).then(() => { // console.log('event', event) // }) // // openTelemetryLogging.send({ // // level: 'info', // // message: event.exception.values[0].value, // // payload: { // // object: { // // sentry: true, // // error: event // // } // // }, // // }) // } // // Return event to send it to Sentry // return event; // }, // } // ); (function () { const httpLogs = []; // --- Patch fetch --- const originalFetch = window.fetch; window.fetch = async (input, config = {}) => { const url = typeof input === "string" ? input : (input as any).url; const method = config?.method || "GET"; // Capture tracer header let tracerHeader = null; if (config?.headers) { if (config.headers instanceof Headers) { tracerHeader = config.headers.get("x-tracer"); } else if (typeof config.headers === "object") { tracerHeader = config.headers["x-tracer"] || config.headers["X-Tracer"]; } } // Capture request body (payload) let requestPayload = null; if (config?.body) { try { if (typeof config.body === "string") { requestPayload = config.body; } else if (config.body !== null && typeof config.body === "object") { // Keep plain objects/arrays as-is requestPayload = config.body; } else { // For other cases (like FormData, Blob, etc.), attempt safe stringify requestPayload = JSON.stringify(config.body); } } catch { requestPayload = "[Unserializable body]"; } } const start = Date.now(); try { const response = await originalFetch(input, config); // Clone response so we don’t consume it const clone = response.clone(); let responsePayload = null; try { const contentType = clone.headers.get("content-type") || ""; if (contentType.includes("application/json")) { responsePayload = await clone.json(); } else if (contentType.includes("text")) { responsePayload = await clone.text(); } else { responsePayload = "[Non-textual body]"; } } catch { responsePayload = "[Unserializable response]"; } const log = { type: "fetch", url, method, status: response.status, xTracer: tracerHeader, requestPayload, responsePayload, duration: Date.now() - start, timestamp: new Date().toISOString(), }; if(response.status >= 400 || response.status === 0) { Logger.error('XHR', log) } httpLogs.push(log); return response; } catch (error) { const log = { type: "fetch", url, method, status: "NETWORK_ERROR", xTracer: tracerHeader, requestPayload, responsePayload: null, duration: Date.now() - start, timestamp: new Date().toISOString(), }; if(!url.includes('petermaquiran.xyz') && url != "") { Logger.error('XHR', log); } httpLogs.push(log); throw error; } }; // --- Patch XMLHttpRequest --- const OriginalXHR = window.XMLHttpRequest; function CustomXHR() { const xhr: any = new OriginalXHR(); const start = Date.now(); xhr._xTracer = null; xhr._requestPayload = null; const originalSetRequestHeader = xhr.setRequestHeader; xhr.setRequestHeader = function (key, value) { if (key.toLowerCase() === "x-tracer") { xhr._xTracer = value; } return originalSetRequestHeader.call(xhr, key, value); }; const originalSend = xhr.send; xhr.send = function (body) { if (body) { try { xhr._requestPayload = typeof body === "string" ? body : JSON.stringify(body); } catch { xhr._requestPayload = "[Unserializable body]"; } } return originalSend.call(xhr, body); }; xhr.addEventListener("loadend", function () { let responsePayload = null; try { const contentType = xhr.getResponseHeader("content-type") || ""; if (contentType.includes("application/json")) { responsePayload = JSON.parse(xhr.responseText); } else if (contentType.includes("text")) { responsePayload = xhr.responseText; } else { responsePayload = "[Non-textual body]"; } } catch { responsePayload = "[Unserializable response]"; } const log = { type: "xhr", url: xhr.responseURL, method: xhr._method || "GET", status: xhr.status, xTracer: xhr._xTracer, requestPayload: xhr._requestPayload, responsePayload, duration: Date.now() - start, timestamp: new Date().toISOString(), }; if(xhr.status >= 400 && !log.url.includes('petermaquiran.xyz') || xhr.status === 0 && !log.url.includes('petermaquiran.xyz')) { Logger.error('XHR', log) } httpLogs.push(log); }); const originalOpen = xhr.open; xhr.open = function (method, url, ...rest) { xhr._method = method; return originalOpen.call(xhr, method, url, ...rest); }; return xhr; } (window as any).XMLHttpRequest = CustomXHR; // Expose logs (window as any).getHttpLogs = () => httpLogs; })(); @NgModule({ declarations: [AppComponent, PopupQuestionPipe, InputFilterDirective], imports: [BrowserModule, CommonModule, FormsModule, CalendarModule.forRoot({ provide: DateAdapter, useFactory: adapterFactory }), //AngularFireModule.initializeApp(environment.firebase), //AngularFireMessagingModule, IonicImageLoaderModule, IonicModule.forRoot({animated: false}), IonicStorageModule.forRoot({ name: '__mydb', driverOrder: ['indexeddb', 'sqlite', 'websql'] }), AppRoutingModule, FontAwesomeModule, HttpClientModule, // NgbModule, NoopAnimationsModule, MatDatepickerModule, // NgxMatDatetimePickerModule, NgxMatTimepickerModule, NgxMatNativeDateModule, NgxMatMomentModule, MatButtonModule, ReactiveFormsModule, MatSelectModule, MatDialogModule, // HammerModule, CustomImageCachePageRoutingModule, // MatInputModule, MatNativeDateModule, NgxMatDatetimePickerModule, NgxMatTimepickerModule, NgxMatNativeDateModule, NgxMatMomentModule, MatSelectModule, MatButtonModule, AngularFireModule.initializeApp(firebaseConfig), AngularFireMessagingModule, EditorModule, // options DeplomaOptionsPageModule, CreateProcessPageModule, ImageCropperModule, MatMenuModule, MatIconModule, // module ChatModule, UserModule ], entryComponents: [ DiplomaOptionsPage, CreateProcessPage ], providers: [ { provide: MAT_DATE_LOCALE, useValue: 'pt' }, //{ provide: ErrorHandler, useClass: AppErrorHandler }, // { // provide: ErrorHandler, // // Attach the Sentry ErrorHandler // useValue: SentrySibling.createErrorHandler(), // }, StatusBar, //SplashScreen, HttpClient, HttpClientModule, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, InAppBrowser, Camera, File, WebView, FilePath, /* FCM, FirebaseX, */ ScreenOrientation, Network, SQLite, CookieService, ImagePicker, MediaCapture, Media, StreamingMedia, PhotoViewer, NgxImageCompressService, MultipleDocumentsPicker, NgxExtendedPdfViewerModule, FileOpener, DocumentViewer, FFMpeg, FFmpeg, { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptorService, multi: true }, tokenInterceptor, metricsInterceptor, ], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class AppModule { constructor(library: FaIconLibrary) { library.addIconPacks(fas, fab, far); } }