-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy patherror-handler.service.ts
207 lines (182 loc) · 8.27 KB
/
error-handler.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import { ErrorHandler, Injectable, isDevMode, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import * as Sentry from '@sentry/angular';
import { EnvService } from '~/app/services/environment.service';
import { GlobalMessageService } from '~/app/services/messaging.service';
// @ts-ignore
declare const __APP_VERSION__: string;
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
private printToConsole = printToConsole;
handleError(error: any): void {
this.printToConsole('Something unexpected happened', 'the core app', error);
Sentry.captureException(error);
}
}
/* Logs an error to the console (when in dev or debug mode) */
const printToConsole = (message?: string, location?: string, error?: any): void => {
console.groupCollapsed(
`%cError: ${message} in ${location || 'unknown location'}`,
'background:#f93939;color:#fff;padding:0.1rem 0.25rem;border-radius:4px'
);
console.error(error);
const d = new Date();
const date = `at ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} on ${d.toDateString()}`;
console.log(
`%cThis occurred ${date}, in ${location || 'an unknown location'}\n`
+ 'Please reference the debugging documentation for next steps: '
+ 'http://domain-locker.com/about/developing/debugging',
'font-size:10px; color:#f9a939;',
);
console.groupEnd();
};
interface ErrorParams {
error?: Error | any, // Should be error, but might be funny error type
message?: string; // Friendly message to show to user (if needed)
location?: string; // Location in code where error occurred
showToast?: boolean; // Whether to show a toast to the user
date?: Date; // Date of error (if not now)
}
/**
* Global error handler service.
* Called whenever an error is caught in the app.
* If running in dev or debug mode, logs to console.
* If server has GT configured, and user has enabled logging, sends to GlitchTip.
* If user-triggered, shows a toast message to the user.
*/
@Injectable({
providedIn: 'root'
})
export class ErrorHandlerService {
private glitchTipEnabled = false; // Don't log errors, unless enabled
private lsKey = 'PRIVACY_disable-error-tracking';
private printToConsole = printToConsole;
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
private globalMessageService: GlobalMessageService,
private envService: EnvService,
) {}
/* Shows a popup toast message to the user (if user-triggered) */
private showToast(title: string, message: string): void {
this.globalMessageService.showMessage({
severity: 'error',
summary: title,
detail: message,
})
}
/* Determines whether to enable GlitchTip (if enabled at server-level, and not disabled by user) */
private shouldEnableGlitchTip(): boolean {
if (!isPlatformBrowser(this.platformId)) return false; // Don't run on server-side
const glitchTipDsn = this.envService.getGlitchTipDsn(); // Get DSN from environment service
const disabledByUser = localStorage.getItem(this.lsKey) !== null; // Check if user disabled error tracking
const isLocal = isDevMode(); // Check if we are running in development mode
// Return false if no DSN, disabled by user, or running locally
return (!glitchTipDsn || disabledByUser || isLocal) ? false : true;
}
/* Initializes GlitchTip error tracking (if not disabled by user or admin) */
public initializeGlitchTip(): void {
this.glitchTipEnabled = this.shouldEnableGlitchTip();
if (!this.glitchTipEnabled) return;
const glitchTipDsn = this.envService.getGlitchTipDsn();
Sentry.init({
dsn: glitchTipDsn,
integrations: [ Sentry.browserTracingIntegration() ],
tracesSampleRate: 1.0,
release: __APP_VERSION__,
environment: this.envService.getEnvironmentType(),
denyUrls: ['localhost'],
});
}
/* Gets the user ID from local storage (if available) */
private getUserId(): string | null {
const projectName = this.envService.getProjectId() || 'domain-locker';
let userId: string | null = null;
if (projectName && typeof localStorage !== 'undefined') {
const authObject = localStorage.getItem(`sb-${projectName}-auth-token`);
if (authObject) {
const user = JSON.parse(authObject)?.user;
userId = user?.id || null;
}
}
return userId;
}
/* Logs an error to GlitchTip with user context */
private logToGlitchTip(message: string, location: string, error: any): void {
if (!this.glitchTipEnabled) return;
const userId = this.getUserId();
Sentry.setUser( userId ? { id: userId } : null);
Sentry.withScope((scope) => {
scope.setContext('context', { message, location });
Sentry.captureException(error);
});
}
private saveToLocalStorage(message: string, location: string, error: any): void {
if (!isPlatformBrowser(this.platformId) || !localStorage) return;
const key = 'DL_error_log';
const lsErrorLog = JSON.parse(localStorage.getItem(key) || '[]');
lsErrorLog.push({ message, location, error, date: new Date().toISOString() });
localStorage.setItem(key, JSON.stringify(lsErrorLog));
}
/* Entry point for error handler, takes appropriate logging action */
public handleError(params: ErrorParams): void {
const { error, message, location, showToast } = params;
if (!error && !message) return; // Not much I can do without an error or message!
// Log to console in development mode
this.printToConsole(message, location, error);
// Show error toast if showError is true
if (showToast && message && error) {
if (error.message) {
this.showToast(message, error.message);
} else {
this.showToast('Error', message);
}
}
// Log error to Glitchtip (if enabled) with user context
if (this.glitchTipEnabled && error) {
this.logToGlitchTip(message || 'mystery error', location || '-', error);
}
// Save to recent error log in localstorage
this.saveToLocalStorage(message || 'mystery error', location || '-', error);
}
public getRecentErrorLog(): any[] {
return JSON.parse(localStorage.getItem('DL_error_log') || '[]').reverse();
}
public printInitialDetails(): void {
if (!isPlatformBrowser(this.platformId)) return;
const environment = this.envService.getEnvironmentType();
const appVersion = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0';
const enabledDb = this.envService.isSupabaseEnabled() ? 'Supabase' : this.envService.isPostgresEnabled() ? 'Postgres' : 'None';
console.log(
`\n%c🔐 Domain Locker V${appVersion}`
+ '%c\nLicensed under MIT, © Alicia Sykes 2025.\nSource: github.com/lissy93/domain-locker\n',
'color:#a78bfa; background:#0b1021; font-size:1.5rem; padding:0.15rem 0.25rem; '
+ 'margin: 1rem auto 0.5rem auto; font-family: Helvetica; border: 2px solid #a78bfa; '
+ 'border-radius: 4px;font-weight: bold; text-shadow: 1px 1px 1px #a78bfabf;',
'color: #a78bfa; font-size:10px; font-family: Helvetica; margin: 0;'
);
const s = 'color:#1fe3f1; font-weight: bold;';
console.group('🐛 %cDebug Info', 'color:#a78bfa;');
console.groupCollapsed('🏗️ %cEnvironment', 'color:#dc8bfa;');
console.log('%cType', s, environment);
console.log('%cApp Version', s, appVersion);
console.log('%cDatabase', s, enabledDb);
console.groupEnd();
console.groupCollapsed('🌍 %cOrigin','color:#dc8bfa;');
console.log('%cProtocol', s, window.location.protocol);
console.log('%cHost', s, window.origin);
console.log('%cPath', s, window.location.pathname);
console.groupEnd();
console.groupCollapsed('📱 %cDevice','color:#dc8bfa;');
console.log('%cDate Fetched', s, new Date().getTime());
console.log('%cOS', s, navigator['platform']);
console.log('%cBrowser', s, navigator['appCodeName']);
console.log('%cLanguage', s, navigator['language']);
console.groupEnd();
console.groupCollapsed('🔗 %cHelp Links','color:#dc8bfa;');
console.log('%cSupport Docs', s, `${window.origin}/about/support`);
console.log('%cDebug Info', s, `${window.origin}/advanced/debug-info`);
console.log('%cGitHub', s, `https://github.com/lissy93/domain-locker`);
console.groupEnd();
console.groupEnd();
}
}