From 66521fea1809e3a93ea7db442e1bf5b4c7e7de67 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 30 Sep 2025 22:26:59 +0700 Subject: [PATCH 1/4] Remove Sentry support Signed-off-by: Andrey Sobolev --- dev/prod/package.json | 1 - dev/prod/src/platform.ts | 1 - packages/analytics-providers/package.json | 1 - packages/analytics-providers/src/index.ts | 1 - packages/analytics-providers/src/sentry.ts | 75 ------------------- packages/analytics-providers/src/types.ts | 1 - pods/front/src/__start.ts | 1 - .../pod-analytics-collector/src/config.ts | 2 - .../pod-calendar-mailer/src/config.ts | 2 - services/github/pod-github/src/config.ts | 3 - services/mail/pod-mail/README.md | 14 +++- .../pod-telegram-bot/src/config.ts | 2 - 12 files changed, 10 insertions(+), 94 deletions(-) delete mode 100644 packages/analytics-providers/src/sentry.ts diff --git a/dev/prod/package.json b/dev/prod/package.json index 094e4fd85dc..4069ad94333 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -282,7 +282,6 @@ "@hcengineering/ai-assistant": "^0.6.0", "@hcengineering/ai-assistant-assets": "^0.6.0", "@hcengineering/ai-assistant-resources": "^0.6.0", - "@sentry/svelte": "^9.22.0", "posthog-js": "^1.246.0", "readable-stream": "^4.7.0", "svelte": "^4.2.20" diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index c8a9a099b89..a9c7c0d80ba 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -168,7 +168,6 @@ export interface Config { GITHUB_APP?: string GITHUB_CLIENTID?: string GITHUB_URL: string - SENTRY_DSN?: string LOVE_ENDPOINT?: string LIVEKIT_WS?: string SIGN_URL?: string diff --git a/packages/analytics-providers/package.json b/packages/analytics-providers/package.json index b8e3ff3fb98..fb379496217 100644 --- a/packages/analytics-providers/package.json +++ b/packages/analytics-providers/package.json @@ -46,7 +46,6 @@ "@hcengineering/presentation": "^0.6.3", "@hcengineering/core": "^0.6.32", "posthog-js": "^1.246.0", - "@sentry/svelte": "^9.22.0", "ua-parser-js": "^2.0.4", "@hcengineering/ui": "^0.6.15" }, diff --git a/packages/analytics-providers/src/index.ts b/packages/analytics-providers/src/index.ts index 31bc7c2f0dd..011e7ff1b43 100644 --- a/packages/analytics-providers/src/index.ts +++ b/packages/analytics-providers/src/index.ts @@ -15,7 +15,6 @@ export * from './analyticsCollector' export * from './posthog' -export * from './sentry' export * from './utils' export * from './types' export * from './configure' diff --git a/packages/analytics-providers/src/sentry.ts b/packages/analytics-providers/src/sentry.ts deleted file mode 100644 index 22b7ba33216..00000000000 --- a/packages/analytics-providers/src/sentry.ts +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright © 2025 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { type AnalyticProvider } from '@hcengineering/analytics' -import * as Sentry from '@sentry/svelte' - -export class SentryAnalyticProvider implements AnalyticProvider { - navigate (path: string): void {} - init (config: Record): boolean { - if (config.SENTRY_DSN !== undefined && config.SENTRY_DSN !== '') { - Sentry.init({ - dsn: config.SENTRY_DSN, - integrations: [ - Sentry.browserTracingIntegration(), - Sentry.replayIntegration({ - maskAllText: false, - blockAllMedia: false - }) - ], - - // Set tracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production - tracesSampleRate: 1.0, - - tracePropagationTargets: [/^https:\/\/huly\.app/, /^https:\/\/app\.huly\.io/, /^https:\/\/account\.huly\.io/], - - replaysSessionSampleRate: 0.0, - replaysOnErrorSampleRate: 1.0 - }) - return true - } - return false - } - - setUser (email: string): void { - Sentry.setUser({ email }) - } - - setAlias (distinctId: string, alias: string): void {} - - logout (): void { - Sentry.setUser(null) - } - - setTag (key: string, value: string | number): void { - Sentry.setTag(key, value) - } - - setWorkspace (ws: string, guest: boolean): void { - const prop: string = guest ? 'visited-workspace' : 'workspace' - this.setTag(prop, ws) - } - - handleEvent (event: string): void { - // currently we don't need it, but maybe in future - // Sentry.captureMessage(event, 'log') - } - - handleError (error: Error): void { - Sentry.captureException(error) - } -} diff --git a/packages/analytics-providers/src/types.ts b/packages/analytics-providers/src/types.ts index e995b530681..7ca93ae0e31 100644 --- a/packages/analytics-providers/src/types.ts +++ b/packages/analytics-providers/src/types.ts @@ -17,7 +17,6 @@ import { type AnalyticEvent } from '@hcengineering/analytics-collector' export interface AnalyticsConfig { ANALYTICS_COLLECTOR_URL?: string - SENTRY_DSN?: string POSTHOG_API?: string POSTHOG_HOST?: string [key: string]: any diff --git a/pods/front/src/__start.ts b/pods/front/src/__start.ts index a359eb92066..8aa0e80c8f0 100644 --- a/pods/front/src/__start.ts +++ b/pods/front/src/__start.ts @@ -32,7 +32,6 @@ startFront(metricsContext, { INTERCOM_APP_ID: process.env.INTERCOM_APP_ID ?? '', INTERCOM_API_URL: process.env.INTERCOM_API_URL ?? '', GITHUB_URL: process.env.GITHUB_URL ?? '', - SENTRY_DSN: process.env.SENTRY_DSN ?? '', LIVEKIT_WS: process.env.LIVEKIT_WS ?? '', LOVE_ENDPOINT: process.env.LOVE_ENDPOINT ?? '', SIGN_URL: process.env.SIGN_URL ?? '', diff --git a/services/analytics-collector/pod-analytics-collector/src/config.ts b/services/analytics-collector/pod-analytics-collector/src/config.ts index 9b44d03e67c..3868a98ba70 100644 --- a/services/analytics-collector/pod-analytics-collector/src/config.ts +++ b/services/analytics-collector/pod-analytics-collector/src/config.ts @@ -20,7 +20,6 @@ export interface Config { AccountsUrl: string PostHogHost: string PostHogAPI: string - SentryDSN?: string MaxPayloadSize?: string } @@ -34,7 +33,6 @@ const config: Config = (() => { AccountsUrl: process.env.ACCOUNTS_URL, PostHogHost: process.env.POSTHOG_HOST, PostHogAPI: process.env.POSTHOG_API_KEY, - SentryDSN: process.env.SENTRY_DSN ?? '', MaxPayloadSize: process.env.MAX_PAYLOAD_SIZE ?? '10mb' } diff --git a/services/calendar/pod-calendar-mailer/src/config.ts b/services/calendar/pod-calendar-mailer/src/config.ts index 14e75994f71..e86b5da16b7 100644 --- a/services/calendar/pod-calendar-mailer/src/config.ts +++ b/services/calendar/pod-calendar-mailer/src/config.ts @@ -18,7 +18,6 @@ dotenvConfig() interface Config { secret: string - sentryDSN: string accountsUrl: string smtpUrl: string queueRegion: string @@ -26,7 +25,6 @@ interface Config { const config: Config = { secret: process.env.SECRET ?? 'secret', - sentryDSN: process.env.SENTRY_DSN ?? '', accountsUrl: process.env.ACCOUNTS_URL ?? 'http://localhost:3000', smtpUrl: process.env.SMTP_URL ?? 'http://localhost:8097', queueRegion: process.env.QUEUE_REGION ?? 'localhost' diff --git a/services/github/pod-github/src/config.ts b/services/github/pod-github/src/config.ts index 118eca0fc27..ad0bfa39703 100644 --- a/services/github/pod-github/src/config.ts +++ b/services/github/pod-github/src/config.ts @@ -22,7 +22,6 @@ interface Config { BotName: string - SentryDSN: string BrandingPath: string WorkspaceInactivityInterval: number // Interval in days to stop workspace synchronization if not visited @@ -49,7 +48,6 @@ const envMap: { [key in keyof Config]: string } = { CollaboratorURL: 'COLLABORATOR_URL', - SentryDSN: 'SENTRY_DSN', BrandingPath: 'BRANDING_PATH', WorkspaceInactivityInterval: 'WORKSPACE_INACTIVITY_INTERVAL', @@ -94,7 +92,6 @@ const config: Config = (() => { CollaboratorURL: process.env[envMap.CollaboratorURL], - SentryDSN: process.env[envMap.SentryDSN], BrandingPath: process.env[envMap.BrandingPath] ?? '', WorkspaceInactivityInterval: parseInt(process.env[envMap.WorkspaceInactivityInterval] ?? '3'), // In days RateLimit: parseInt(process.env[envMap.RateLimit] ?? '25') diff --git a/services/mail/pod-mail/README.md b/services/mail/pod-mail/README.md index 13ee9fa584d..bb4905a1e92 100644 --- a/services/mail/pod-mail/README.md +++ b/services/mail/pod-mail/README.md @@ -2,21 +2,22 @@ ## Overview -The Mail Service is responsible for sending emails using SMTP or SES transfer. +The Mail Service is responsible for sending emails using SMTP or SES transfer. It supports sending emails with multiple recipients, along with optional CC, BCC, and HTML content. ### Configuration Environment variables should be set to configure the Mail Service: + - `PORT`: The port on which the mail service listens for incoming HTTP requests. - `API_KEY`: An API key that clients must pass. The parameter is optional, should be provided when external access to the service is allowed. -- `SENTRY_DSN`: (optional) The Data Source Name for Sentry error tracking. This is optional and should be set if you wish to enable error tracking with Sentry. - `SOURCE`: The sender source (fallback for when emails emit from the system). - `REPLY_TO`: (optional) Email to use for replies (useful for uni-directional STMP setups where emails are only emitted, but not received). Settings for SMTP or SES email service should be specified, simultaneous use of both protocols is not supported SMTP settings: + - `SMTP_HOST`: Hostname of the SMTP server used for sending emails. - `SMTP_PORT`: Port number of the SMTP server. - `SMTP_USERNAME`: Username for authenticating with the SMTP server. Refer to your SMTP server documentation for the appropriate format. @@ -29,6 +30,7 @@ SMTP settings: - `SMTP_ALLOW_SELF_SIGNED` (Optional): Allow self-signed certificates for TLS connections. Set to 'true' to enable (not recommended for production use). Default: false SES settings: + - `SES_ACCESS_KEY`: AWS SES access key for authentication. - `SES_SECRET_KEY`: AWS SES secret key for authentication. - `SES_REGION`: AWS SES region where your SES service is hosted. @@ -36,14 +38,17 @@ SES settings: ### Running the Service Add .env file to the root of the project with the following content to add integration with fake SMTP server: + ``` PORT=8097 SMTP_HOST="mail.smtpbucket.com" -SMTP_PORT=8025 +SMTP_PORT=8025 ``` + To use the real SMTP server it is required to register an account in some email service provider and specify settings and credentials for it. Start the service locally using: + ```bash rushx run-local ``` @@ -76,6 +81,7 @@ Send an email message. - `raw`: An optional special value that overrides the entire contents of the current MIME node, including MIME headers. Useful if you want to prepare node contents yourself. Request body example: + ``` { "subject": "Test SMTP", @@ -87,7 +93,7 @@ Request body example: "filename": "test.txt", "content": "Hello world", "contentType": "text/plain" - } + } ] } ``` diff --git a/services/telegram-bot/pod-telegram-bot/src/config.ts b/services/telegram-bot/pod-telegram-bot/src/config.ts index 74d089e9236..73aab11af8a 100644 --- a/services/telegram-bot/pod-telegram-bot/src/config.ts +++ b/services/telegram-bot/pod-telegram-bot/src/config.ts @@ -27,7 +27,6 @@ export interface Config { QueueConfig: string QueueRegion: string Secret: string - SentryDSN: string ServiceId: string } @@ -46,7 +45,6 @@ const config: Config = (() => { App: process.env.APP ?? 'Huly', OtpTimeToLiveSec: parseNumber(process.env.OTP_TIME_TO_LIVE_SEC) ?? 5 * 60, OtpRetryDelaySec: parseNumber(process.env.OTP_RETRY_DELAY_SEC) ?? 60, - SentryDSN: process.env.SENTRY_DSN ?? '', AccountsURL: process.env.ACCOUNTS_URL, DbUrl: process.env.DB_URL, QueueRegion: process.env.QUEUE_REGION, From 41b2e28e29e9136b6efb0e958332c0ea6e0388d1 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 30 Sep 2025 22:34:44 +0700 Subject: [PATCH 2/4] Remove Posthog from UI Signed-off-by: Andrey Sobolev --- desktop/src/ui/types.ts | 3 - dev/prod/package.json | 1 - dev/prod/src/platform.ts | 2 - packages/analytics-providers/package.json | 1 - packages/analytics-providers/src/configure.ts | 8 +-- packages/analytics-providers/src/index.ts | 1 - packages/analytics-providers/src/posthog.ts | 68 ------------------- pods/front/src/__start.ts | 2 - 8 files changed, 1 insertion(+), 85 deletions(-) delete mode 100644 packages/analytics-providers/src/posthog.ts diff --git a/desktop/src/ui/types.ts b/desktop/src/ui/types.ts index 0888fdb1a0b..52388b81e09 100644 --- a/desktop/src/ui/types.ts +++ b/desktop/src/ui/types.ts @@ -8,9 +8,6 @@ export interface Config { ACCOUNTS_URL: string AI_URL?: string ANALYTICS_COLLECTOR_URL?: string - POSTHOG_API_KEY?: string - POSTHOG_HOST?: string - SENTRY_DSN?: string BRANDING_URL?: string CALENDAR_URL: string COLLABORATOR?: string diff --git a/dev/prod/package.json b/dev/prod/package.json index 4069ad94333..68b09abe548 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -282,7 +282,6 @@ "@hcengineering/ai-assistant": "^0.6.0", "@hcengineering/ai-assistant-assets": "^0.6.0", "@hcengineering/ai-assistant-resources": "^0.6.0", - "posthog-js": "^1.246.0", "readable-stream": "^4.7.0", "svelte": "^4.2.20" } diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index a9c7c0d80ba..75540ba2364 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -172,8 +172,6 @@ export interface Config { LIVEKIT_WS?: string SIGN_URL?: string PRINT_URL?: string - POSTHOG_API_KEY?: string - POSTHOG_HOST?: string ANALYTICS_COLLECTOR_URL?: string BRANDING_URL?: string TELEGRAM_BOT_URL?: string diff --git a/packages/analytics-providers/package.json b/packages/analytics-providers/package.json index fb379496217..030455a073e 100644 --- a/packages/analytics-providers/package.json +++ b/packages/analytics-providers/package.json @@ -45,7 +45,6 @@ "@hcengineering/analytics-collector": "^0.6.0", "@hcengineering/presentation": "^0.6.3", "@hcengineering/core": "^0.6.32", - "posthog-js": "^1.246.0", "ua-parser-js": "^2.0.4", "@hcengineering/ui": "^0.6.15" }, diff --git a/packages/analytics-providers/src/configure.ts b/packages/analytics-providers/src/configure.ts index b248e8887cd..b827bf8d7ce 100644 --- a/packages/analytics-providers/src/configure.ts +++ b/packages/analytics-providers/src/configure.ts @@ -15,21 +15,15 @@ import { type AnalyticProvider, Analytics } from '@hcengineering/analytics' import { AnalyticsCollectorProvider } from './analyticsCollector' -import { PosthogAnalyticProvider } from './posthog' -import { SentryAnalyticProvider } from './sentry' import { type AnalyticsConfig } from './types' export * from './analyticsCollector' -export * from './posthog' -export * from './sentry' export * from './utils' export * from './types' export function configureAnalyticsProviders (config: AnalyticsConfig): void { const providers: AnalyticProvider[] = [ - new AnalyticsCollectorProvider(), - new SentryAnalyticProvider(), - new PosthogAnalyticProvider() + new AnalyticsCollectorProvider() ] for (const provider of providers) { diff --git a/packages/analytics-providers/src/index.ts b/packages/analytics-providers/src/index.ts index 011e7ff1b43..913a4af25f2 100644 --- a/packages/analytics-providers/src/index.ts +++ b/packages/analytics-providers/src/index.ts @@ -14,7 +14,6 @@ // export * from './analyticsCollector' -export * from './posthog' export * from './utils' export * from './types' export * from './configure' diff --git a/packages/analytics-providers/src/posthog.ts b/packages/analytics-providers/src/posthog.ts deleted file mode 100644 index a15614f216e..00000000000 --- a/packages/analytics-providers/src/posthog.ts +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright © 2025 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import { type AnalyticProvider } from '@hcengineering/analytics' -import posthog from 'posthog-js' - -export class PosthogAnalyticProvider implements AnalyticProvider { - init (config: Record): boolean { - if (config.POSTHOG_API_KEY !== undefined && config.POSTHOG_API_KEY !== '' && config.POSTHOG_HOST !== null) { - posthog.init(config.POSTHOG_API_KEY, { - api_host: config.POSTHOG_HOST, - autocapture: false, - capture_pageview: false, - capture_pageleave: false - }) - return true - } - return false - } - - setUser (email: string): void { - if (!posthog._isIdentified()) { - posthog.identify(email, { email }) - } - } - - setAlias (distinctId: string, alias: string): void { - posthog.alias(alias, distinctId) - } - - setTag (key: string, value: string | number): void { - posthog.setPersonProperties({ [key]: value }) - } - - setWorkspace (ws: string, guest: boolean): void { - const prop: string = guest ? 'visited-workspace' : 'workspace' - this.setTag(prop, ws) - if (!guest) posthog.group(prop, ws, { name: `${ws}` }) - } - - logout (): void { - posthog.reset(true) - } - - handleEvent (event: string, params: Record): void { - posthog.capture(event, params) - } - - handleError (error: Error): void { - posthog.capture(error.message) - } - - navigate (path: string): void { - posthog.capture('$pageview') - } -} diff --git a/pods/front/src/__start.ts b/pods/front/src/__start.ts index 8aa0e80c8f0..a7864f3ce6a 100644 --- a/pods/front/src/__start.ts +++ b/pods/front/src/__start.ts @@ -38,8 +38,6 @@ startFront(metricsContext, { PRINT_URL: process.env.PRINT_URL ?? '', PRESENCE_URL: process.env.PRESENCE_URL ?? '', PUSH_PUBLIC_KEY: process.env.PUSH_PUBLIC_KEY ?? '', - POSTHOG_API_KEY: process.env.POSTHOG_API_KEY, - POSTHOG_HOST: process.env.POSTHOG_HOST, DESKTOP_UPDATES_URL: process.env.DESKTOP_UPDATES_URL, DESKTOP_UPDATES_CHANNEL: process.env.DESKTOP_UPDATES_CHANNEL, DESKTOP_UPDATES_CHANNELS: process.env.DESKTOP_UPDATES_CHANNELS, From ed66cc07a3560eff21f7f97c796b303317003fcd Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 30 Sep 2025 23:36:53 +0700 Subject: [PATCH 3/4] Fix formatting Signed-off-by: Andrey Sobolev --- packages/analytics-providers/src/configure.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/analytics-providers/src/configure.ts b/packages/analytics-providers/src/configure.ts index b827bf8d7ce..0603c268dc3 100644 --- a/packages/analytics-providers/src/configure.ts +++ b/packages/analytics-providers/src/configure.ts @@ -22,9 +22,7 @@ export * from './utils' export * from './types' export function configureAnalyticsProviders (config: AnalyticsConfig): void { - const providers: AnalyticProvider[] = [ - new AnalyticsCollectorProvider() - ] + const providers: AnalyticProvider[] = [new AnalyticsCollectorProvider()] for (const provider of providers) { Analytics.init(provider, config) From 8c760d683c9778a829f2685eeb8d82d51ccd823f Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 30 Sep 2025 23:57:08 +0700 Subject: [PATCH 4/4] Update analytics collector to report errors to OTLP collector Signed-off-by: Andrey Sobolev --- dev/docker-compose.yaml | 33 +++---- dev/prod/public/config.json | 3 +- .../src/analyticsCollector.ts | 43 +++++++-- .../src/components/DocPopup.svelte | 3 +- .../src/components/DrawingBoard.svelte | 2 - .../src/components/DrawingBoardToolbar.svelte | 5 +- .../src/components/FilePreviewPopup.svelte | 1 - .../src/components/markup/Mark.svelte | 5 +- plugins/activity-resources/package.json | 1 + .../src/components/Activity.svelte | 15 +-- .../src/components/Configure.svelte | 4 +- .../components/LinkPreviewPresenter.svelte | 3 +- .../components/FieldMappingPresenter.svelte | 2 +- plugins/calendar-resources/package.json | 1 + .../src/components/CalDavAccess.svelte | 5 +- .../src/components/ScheduleEditor.svelte | 17 ++-- .../src/components/NewCardForm.svelte | 3 +- .../settings/ManageMasterTagsContent.svelte | 13 +-- .../settings/RelationsSection.svelte | 4 +- .../src/components/inbox/InboxHeader.svelte | 5 +- .../src/components/ChannelInput.svelte | 5 +- .../chat-message/ChatMessageInput.svelte | 2 - plugins/communication-resources/package.json | 1 + .../components/message/MessageInput.svelte | 69 +++++++------- .../src/components/AccountArrayEditor.svelte | 3 +- .../src/components/AccountBox.svelte | 2 + .../src/components/AddMembersPopup.svelte | 2 + .../src/components/PersonIdArrayEditor.svelte | 3 + .../src/components/Configure.svelte | 5 +- .../src/components/ConfigureV2.svelte | 4 +- .../src/components/Connect.svelte | 1 - .../src/components/IntegrationState.svelte | 5 +- .../src/components/Guest.svelte | 1 - .../src/components/Configure.svelte | 5 +- .../src/components/AutoJoin.svelte | 4 + .../src/components/RoomSelector.svelte | 13 +-- .../components/BrowserNotificatator.svelte | 1 - .../src/components/DOCXViewer.svelte | 7 +- .../src/components/PrintToPDF.svelte | 2 - plugins/process-resources/package.json | 1 + .../src/components/ErrorTooltip.svelte | 5 +- .../src/components/settings/TimeEditor.svelte | 9 +- .../transformEditors/AppendEditor.svelte | 9 +- .../transformEditors/NumberEditor.svelte | 9 +- .../src/components/CreateCandidate.svelte | 8 +- .../src/components/MailboxEditorModal.svelte | 2 + .../src/components/MailboxItem.svelte | 4 +- .../src/components/Mailboxes.svelte | 2 + .../src/components/Owners.svelte | 5 +- .../src/components/Password.svelte | 1 - .../integrations/IntegrationCard.svelte | 4 +- .../integrations/Integrations.svelte | 11 ++- .../socialIds/AddEmailSocialId.svelte | 9 +- .../components/socialIds/SocialIdRow.svelte | 3 +- plugins/survey-resources/package.json | 1 + .../src/components/PollCollection.svelte | 3 +- .../components/AssociationPresenter.svelte | 5 +- .../src/components/SelectWorkspaceMenu.svelte | 15 +-- .../src/components/Workbench.svelte | 1 - .../pod-analytics-collector/src/config.ts | 12 ++- .../pod-analytics-collector/src/geoip.ts | 4 +- .../pod-analytics-collector/src/index.ts | 4 +- .../pod-analytics-collector/src/main.ts | 2 +- .../pod-analytics-collector/src/platform.ts | 22 ----- .../pod-analytics-collector/src/server.ts | 91 +++++++++++-------- 65 files changed, 307 insertions(+), 238 deletions(-) delete mode 100644 services/analytics-collector/pod-analytics-collector/src/platform.ts diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 42ba98f2dc1..330dc472f24 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -426,22 +426,23 @@ services: # - STATS_URL=http://huly.local:4900 # # - LOVE_ENDPOINT=http://huly.local:8096 # # - OPENAI_API_KEY=token - # analytics: - # image: hardcoreeng/analytics-collector - # extra_hosts: - # - 'huly.local:host-gateway' - # restart: unless-stopped - # ports: - # - 4017:4017 - # environment: - # - SECRET=secret - # - PORT=4017 - # - SERVICE_ID=analytics-collector-service - # - ACCOUNTS_URL=http://huly.local:3000 - # - STATS_URL=http://huly.local:4900 - # - POSTHOG_HOST=${POSTHOG_HOST} - # - POSTHOG_API_KEY=${POSTHOG_API_KEY} - # - MAX_PAYLOAD_SIZE=10mb + analytics: + image: hardcoreeng/analytics-collector + extra_hosts: + - 'huly.local:host-gateway' + restart: unless-stopped + ports: + - 4017:4017 + environment: + - SECRET=secret + - PORT=4017 + - SERVICE_ID=analytics-collector-service + - ACCOUNTS_URL=http://huly.local:3000 + - STATS_URL=http://huly.local:4900 + - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces + # - POSTHOG_HOST=${POSTHOG_HOST} + # - POSTHOG_API_KEY=${POSTHOG_API_KEY} + # - MAX_PAYLOAD_SIZE=10mb export: image: hardcoreeng/export extra_hosts: diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 2c17577b866..ac85cae2d8d 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -29,5 +29,6 @@ "BACKUP_URL": "http://huly.local:4039/api/backup", "PULSE_URL": "ws://huly.local:8099/ws", "HULYLAKE_URL": "http://huly.local:8096", - "EXCLUDED_APPLICATIONS_FOR_ANONYMOUS": "[\"chunter\", \"notification\"]" + "EXCLUDED_APPLICATIONS_FOR_ANONYMOUS": "[\"chunter\", \"notification\"]", + "ANALYTICS_COLLECTOR_URL": "http://huly.local:4017" } diff --git a/packages/analytics-providers/src/analyticsCollector.ts b/packages/analytics-providers/src/analyticsCollector.ts index 13f5061816e..1fb59e29848 100644 --- a/packages/analytics-providers/src/analyticsCollector.ts +++ b/packages/analytics-providers/src/analyticsCollector.ts @@ -14,11 +14,11 @@ // import { type AnalyticProvider } from '@hcengineering/analytics' -import presentation from '@hcengineering/presentation' -import { getMetadata } from '@hcengineering/platform' import { AnalyticEventType } from '@hcengineering/analytics-collector' -import { collectEventMetadata, triggerUrlChange } from './utils' +import { getMetadata } from '@hcengineering/platform' +import presentation from '@hcengineering/presentation' import { type QueuedEvent } from './types' +import { collectEventMetadata, triggerUrlChange } from './utils' export class AnalyticsCollectorProvider implements AnalyticProvider { private readonly collectIntervalMs = 5000 @@ -39,6 +39,7 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { if (this.url !== undefined && this.url !== '' && this.url !== null) { this.initializeAnonymousId() this.startCollectionTimer() + this.registerExceptionHandlers() return true } return false @@ -48,6 +49,18 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { this.anonymousId = this.generateAnonymousId() } + private registerExceptionHandlers (): void { + // Capture unhandled errors + window.addEventListener('error', (event) => { + this.handleError(event.error ?? new Error(event.message)) + }) + + // Capture unhandled promise rejections + window.addEventListener('unhandledrejection', (event) => { + this.handleError(event.reason instanceof Error ? event.reason : new Error(String(event.reason))) + }) + } + private generateAnonymousId (): string { return 'anon_' + Date.now() + '_' + Math.random().toString(36).substring(2, 15) } @@ -151,7 +164,13 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { $is_identified: this.isAuthenticated } - const eventMetadata: Record = collectEventMetadata(baseProperties) + let eventMetadata: Record = {} + + try { + eventMetadata = collectEventMetadata(baseProperties) + } catch (err: any) { + // Ignore metadata collection errors + } if (eventType === AnalyticEventType.CustomEvent && (eventName !== '' || eventName != null)) { eventMetadata.event = eventName } @@ -271,16 +290,20 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { handleError (error: Error): void { const currentId = this.isAuthenticated && (this.email != null || this.email !== '') ? this.email : this.anonymousId - this.addEvent( - AnalyticEventType.Error, - { + + const event: QueuedEvent = { + event: AnalyticEventType.Error, + properties: { error_message: error.message ?? 'Unknown error', error_type: error.name ?? 'Error', error_stack: error.stack ?? '' }, - '$exception', - currentId - ) + timestamp: Date.now(), + distinct_id: currentId ?? '' + } + this.events.push(event) + // We need to trigger sending immediately for errors + void this.sendEvents() } navigate (path: string): void { diff --git a/packages/presentation/src/components/DocPopup.svelte b/packages/presentation/src/components/DocPopup.svelte index fe5519bc9c6..ec9a99d9824 100644 --- a/packages/presentation/src/components/DocPopup.svelte +++ b/packages/presentation/src/components/DocPopup.svelte @@ -37,6 +37,7 @@ import presentation from '..' import { ObjectCreate } from '../types' import { getClient } from '../utils' + import { Analytics } from '@hcengineering/analytics' export let _class: Ref> export let objects: Doc[] = [] @@ -184,7 +185,7 @@ presenter = result }) .catch((err) => { - console.error('Failed to find presenter for class ' + _class, err) + Analytics.handleError(err) }) } } diff --git a/packages/presentation/src/components/DrawingBoard.svelte b/packages/presentation/src/components/DrawingBoard.svelte index 2f426d2fc3c..fe64e017048 100644 --- a/packages/presentation/src/components/DrawingBoard.svelte +++ b/packages/presentation/src/components/DrawingBoard.svelte @@ -136,7 +136,6 @@ } catch (error: any) { commandProcessor.set([]) Analytics.handleError(error) - console.error('Failed to parse drawing content', error) } } else { commandProcessor.set([]) @@ -151,7 +150,6 @@ } createDrawing(data).catch((error) => { Analytics.handleError(error) - console.error('Failed to save drawing', error) }) } } diff --git a/packages/presentation/src/components/DrawingBoardToolbar.svelte b/packages/presentation/src/components/DrawingBoardToolbar.svelte index b4532cee655..93ce265c0a5 100644 --- a/packages/presentation/src/components/DrawingBoardToolbar.svelte +++ b/packages/presentation/src/components/DrawingBoardToolbar.svelte @@ -36,6 +36,7 @@ import DrawingBoardToolbarColorIcon from './DrawingBoardToolbarColorIcon.svelte' import DrawingBoardColorSelectorIcon from './DrawingBoardColorSelectorIcon.svelte' import { ColorsList, DrawingBoardColoringSetup } from '../drawingColors' + import { Analytics } from '@hcengineering/analytics' interface DrawingBoardToolbarEvents { undo: undefined @@ -130,9 +131,7 @@ break } default: { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _exhaustive: never = id - console.error('Unknown command id', id) + Analytics.handleError(new Error(`Unknown command id '${id as any}'`)) } } }) diff --git a/packages/presentation/src/components/FilePreviewPopup.svelte b/packages/presentation/src/components/FilePreviewPopup.svelte index 478d3e7f2c5..4f8f96c05c9 100644 --- a/packages/presentation/src/components/FilePreviewPopup.svelte +++ b/packages/presentation/src/components/FilePreviewPopup.svelte @@ -73,7 +73,6 @@ .catch((error) => { drawingLoading = false Analytics.handleError(error) - console.error('Failed to load drawings for file', file, error) }) } } diff --git a/packages/presentation/src/components/markup/Mark.svelte b/packages/presentation/src/components/markup/Mark.svelte index 617be8e4dd2..ebb36448f95 100644 --- a/packages/presentation/src/components/markup/Mark.svelte +++ b/packages/presentation/src/components/markup/Mark.svelte @@ -18,6 +18,7 @@ import uiPlugin, { navigate, parseLocation } from '@hcengineering/ui' import presentation from '../../plugin' + import { Analytics } from '@hcengineering/analytics' export let mark: MarkupMark @@ -40,8 +41,8 @@ } } } - } catch (err) { - console.error('Failed to handle link', mark, err) + } catch (err: any) { + Analytics.handleError(err) } } diff --git a/plugins/activity-resources/package.json b/plugins/activity-resources/package.json index 2091b6f0702..a2364c385ee 100644 --- a/plugins/activity-resources/package.json +++ b/plugins/activity-resources/package.json @@ -37,6 +37,7 @@ "typescript": "^5.8.3" }, "dependencies": { + "@hcengineering/analytics": "^0.6.0", "@hcengineering/activity": "^0.6.0", "@hcengineering/card": "^0.6.0", "@hcengineering/contact": "^0.6.24", diff --git a/plugins/activity-resources/src/components/Activity.svelte b/plugins/activity-resources/src/components/Activity.svelte index 9ba9f3d299a..ae113b41e98 100644 --- a/plugins/activity-resources/src/components/Activity.svelte +++ b/plugins/activity-resources/src/components/Activity.svelte @@ -22,15 +22,16 @@ } from '@hcengineering/activity' import { Class, Doc, getCurrentAccount, Ref, SortingOrder } from '@hcengineering/core' import { createQuery, getClient } from '@hcengineering/presentation' - import { Grid, Section, Spinner, location, Lazy } from '@hcengineering/ui' + import { Grid, Lazy, location, Section, Spinner } from '@hcengineering/ui' import { onDestroy, onMount } from 'svelte' - import ActivityExtensionComponent from './ActivityExtension.svelte' - import ActivityFilter from './ActivityFilter.svelte' + import { editingMessageStore, messageInFocus } from '../activity' import { combineActivityMessages, sortActivityMessages } from '../activityMessagesUtils' - import { canGroupMessages, getMessageFromLoc, getSpace, getActivityNewestFirst } from '../utils' + import { canGroupMessages, getActivityNewestFirst, getMessageFromLoc, getSpace } from '../utils' import ActivityMessagePresenter from './activity-message/ActivityMessagePresenter.svelte' - import { editingMessageStore, messageInFocus } from '../activity' + import ActivityExtensionComponent from './ActivityExtension.svelte' + import ActivityFilter from './ActivityFilter.svelte' + import { Analytics } from '@hcengineering/analytics' export let object: WithReferences export let showCommenInput: boolean = true @@ -185,8 +186,8 @@ } clazz = hierarchy.getClass(clazz).extends } - } catch (e) { - console.error(e) + } catch (e: any) { + Analytics.handleError(e) return [] } return [] diff --git a/plugins/ai-assistant-resources/src/components/Configure.svelte b/plugins/ai-assistant-resources/src/components/Configure.svelte index db3075465c7..2c2ab3eb78f 100644 --- a/plugins/ai-assistant-resources/src/components/Configure.svelte +++ b/plugins/ai-assistant-resources/src/components/Configure.svelte @@ -25,6 +25,7 @@ import aiAssistant from '../plugin' import { buildSocialIdString, getCurrentAccount, SocialIdType, type PersonId } from '@hcengineering/core' import HulyAssistant from './icons/HulyAssistant.svelte' + import { Analytics } from '@hcengineering/analytics' export let integration: Integration | undefined = undefined @@ -51,9 +52,10 @@ isLoading = false dispatch('close') - } catch (err) { + } catch (err: any) { isLoading = false console.error('Failed to find/create huly assistant social id/integration:', err) + Analytics.handleError(err) } }) diff --git a/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte b/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte index 7f11cab575a..a3347653f5c 100644 --- a/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte +++ b/plugins/attachment-resources/src/components/LinkPreviewPresenter.svelte @@ -27,6 +27,7 @@ import { getImageDimensions } from '../utils' import LinkPreviewIcon from './LinkPreviewIcon.svelte' import LinkPreviewImage from './LinkPreviewImage.svelte' + import { Analytics } from '@hcengineering/analytics' export let attachment: WithLookup export let isOwn = false @@ -56,7 +57,7 @@ }) .catch((err) => { viewModel = undefined - console.error(err) + Analytics.handleError(err) }) }) diff --git a/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte index 3240822f756..0a0e48ae1bf 100644 --- a/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte +++ b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte @@ -18,7 +18,7 @@ try { attr = getClient().getHierarchy().getAttribute(value.ofClass, value.attributeName) } catch (err: any) { - console.error(err) + Analytics.handleError(err) } diff --git a/plugins/calendar-resources/package.json b/plugins/calendar-resources/package.json index c46cc0c290c..01d142c3798 100644 --- a/plugins/calendar-resources/package.json +++ b/plugins/calendar-resources/package.json @@ -38,6 +38,7 @@ "svelte-eslint-parser": "^0.33.1" }, "dependencies": { + "@hcengineering/analytics": "^0.6.0", "@hcengineering/account-client": "^0.6.0", "@hcengineering/login": "^0.6.12", "@hcengineering/core": "^0.6.32", diff --git a/plugins/calendar-resources/src/components/CalDavAccess.svelte b/plugins/calendar-resources/src/components/CalDavAccess.svelte index 55b1b40856d..25db31a9fa1 100644 --- a/plugins/calendar-resources/src/components/CalDavAccess.svelte +++ b/plugins/calendar-resources/src/components/CalDavAccess.svelte @@ -13,6 +13,7 @@ import { getMetadata } from '@hcengineering/platform' import { getCurrentAccount, pickPrimarySocialId, SocialId, SocialIdType } from '@hcengineering/core' import { getAccountClient } from '../utils' + import { Analytics } from '@hcengineering/analytics' const workspaceUuid = getCurrentWorkspaceUuid() @@ -89,7 +90,7 @@ selectedSocialId = socialId accessEnabled = integrationWs != null && integrationGlobal != null } catch (err: any) { - console.error('Failed to load CalDAV intergrations', err) + Analytics.handleError(err) error = `${err.message ?? 'Unable to load CalDAV intergrations'}` } finally { loading = false @@ -151,7 +152,7 @@ dispatch('close', true) } catch (err: any) { error = `${err.message ?? 'Unable to save CalDAV intergration'}` - console.error('Failed to save CalDAV integration', err) + Analytics.handleError(err) } finally { loading = false } diff --git a/plugins/calendar-resources/src/components/ScheduleEditor.svelte b/plugins/calendar-resources/src/components/ScheduleEditor.svelte index d06644bb26e..44e43ac6125 100644 --- a/plugins/calendar-resources/src/components/ScheduleEditor.svelte +++ b/plugins/calendar-resources/src/components/ScheduleEditor.svelte @@ -55,6 +55,7 @@ import calendar from '../plugin' import CalendarSelector from './CalendarSelector.svelte' import TimeZoneSelector from './TimeZoneSelector.svelte' + import { Analytics } from '@hcengineering/analytics' export let schedule: Schedule | undefined @@ -99,8 +100,8 @@ .then((text) => { durationVariants.push({ msec, text }) }) - .catch((err) => { - console.error(err) + .catch((err: any) => { + Analytics.handleError(err) }) }) } @@ -112,8 +113,8 @@ .then((text) => { intervalVariants.push({ msec, text }) }) - .catch((err) => { - console.error(err) + .catch((err: any) => { + Analytics.handleError(err) }) }) } @@ -170,8 +171,8 @@ .then((res) => { formattedMeetingDuration = res }) - .catch((err) => { - console.error(err) + .catch((err: any) => { + Analytics.handleError(err) }) } @@ -180,8 +181,8 @@ .then((res) => { formattedMeetingInterval = res }) - .catch((err) => { - console.error(err) + .catch((err: any) => { + Analytics.handleError(err) }) } diff --git a/plugins/card-resources/src/components/NewCardForm.svelte b/plugins/card-resources/src/components/NewCardForm.svelte index 51efcd5940e..9cc1b893f11 100644 --- a/plugins/card-resources/src/components/NewCardForm.svelte +++ b/plugins/card-resources/src/components/NewCardForm.svelte @@ -33,6 +33,7 @@ import chat from '@hcengineering/chat' import EditorActions from './EditorActions.svelte' + import { Analytics } from '@hcengineering/analytics' const dispatch = createEventDispatcher() const communicationClient = getCommunicationClient() @@ -90,7 +91,7 @@ const createdCard = await createCard(type, space, data, cardDescription, _id) if (isThread) { if (createdCard == null) { - console.error('Failed to create thread card') + Analytics.handleError(new Error('Failed to create thread card')) return } const blobs: (BlobParams & { mimeType: string })[] = descriptionBox.getAttachments().map((attachment) => ({ diff --git a/plugins/card-resources/src/components/settings/ManageMasterTagsContent.svelte b/plugins/card-resources/src/components/settings/ManageMasterTagsContent.svelte index 188fb656fe1..6385afa55e4 100644 --- a/plugins/card-resources/src/components/settings/ManageMasterTagsContent.svelte +++ b/plugins/card-resources/src/components/settings/ManageMasterTagsContent.svelte @@ -13,26 +13,27 @@ // limitations under the License. -->