@dargmuesli/nuxt-vio 23.0.0-beta.2 → 23.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ import { getSharedSentryConfig } from '~~/node/static'
2
+
3
+ export const useSharedSentryConfig = () => {
4
+ const isTesting = useIsTesting()
5
+ const runtimeConfig = useRuntimeConfig()
6
+
7
+ return getSharedSentryConfig({
8
+ enableLogs: runtimeConfig.public.sentry.logs.enable,
9
+ environment: runtimeConfig.public.sentry.environment,
10
+ host: runtimeConfig.public.sentry.host,
11
+ isInProduction: runtimeConfig.public.vio.isInProduction,
12
+ isTesting,
13
+ projectId: runtimeConfig.public.sentry.project.id,
14
+ projectPublicKey: runtimeConfig.public.sentry.project.publicKey,
15
+ release: runtimeConfig.public.sentry.release,
16
+ })
17
+ }
@@ -1,9 +1,11 @@
1
+ import * as Sentry from '@sentry/nuxt'
1
2
  import { decodeJwt } from 'jose'
2
3
  import type { JWTPayload } from 'jose'
3
4
  import { defineStore } from 'pinia'
4
5
  import { ref } from 'vue'
5
6
 
6
7
  export const useVioAuthStore = defineStore('vio-auth', () => {
8
+ const runtimeConfig = useRuntimeConfig()
7
9
  const jwt = ref<string>()
8
10
  const jwtDecoded = ref<JWTPayload>()
9
11
  const signedInUsername = ref<string>()
@@ -21,6 +23,17 @@ export const useVioAuthStore = defineStore('vio-auth', () => {
21
23
  jwtDecodedNew.exp > Math.floor(Date.now() / 1000)
22
24
  ? (jwtDecodedNew.username as string | undefined)
23
25
  : undefined
26
+
27
+ if (runtimeConfig.public.sentry.features.userTracking) {
28
+ Sentry.setUser(
29
+ signedInUsername.value
30
+ ? {
31
+ id: jwtDecodedNew?.sub as string | undefined,
32
+ username: signedInUsername.value,
33
+ }
34
+ : null,
35
+ )
36
+ }
24
37
  }
25
38
 
26
39
  return {
@@ -1,2 +1,3 @@
1
1
  export * from './environment'
2
2
  export * from './nuxt'
3
+ export * from './sentry'
@@ -0,0 +1,42 @@
1
+ import type * as Sentry from '@sentry/nuxt'
2
+
3
+ export const NUXT_PUBLIC_SENTRY_ENVIRONMENT =
4
+ process.env.NUXT_PUBLIC_SENTRY_ENVIRONMENT
5
+ export const NUXT_PUBLIC_SENTRY_HOST = process.env.NUXT_PUBLIC_SENTRY_HOST || ''
6
+ export const NUXT_PUBLIC_SENTRY_LOGS_ENABLE =
7
+ process.env.NUXT_PUBLIC_SENTRY_LOGS_ENABLE !== 'false'
8
+ export const NUXT_PUBLIC_SENTRY_PROJECT_ID =
9
+ process.env.NUXT_PUBLIC_SENTRY_PROJECT_ID || ''
10
+ export const NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY =
11
+ process.env.NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY || ''
12
+ export const NUXT_PUBLIC_SENTRY_RELEASE = process.env.RELEASE_NAME
13
+
14
+ export const getSharedSentryConfig = ({
15
+ enableLogs,
16
+ environment,
17
+ host,
18
+ isInProduction,
19
+ isTesting,
20
+ projectId,
21
+ projectPublicKey,
22
+ release,
23
+ }: {
24
+ enableLogs?: boolean
25
+ environment?: string
26
+ host?: string
27
+ isInProduction: boolean
28
+ isTesting?: boolean
29
+ projectId?: string
30
+ projectPublicKey?: string
31
+ release?: string
32
+ }): Parameters<typeof Sentry.init>[0] => ({
33
+ dsn:
34
+ projectPublicKey && host && projectId
35
+ ? `https://${projectPublicKey}@${host}/${projectId}`
36
+ : undefined,
37
+ enabled: isInProduction && !isTesting,
38
+ enableLogs,
39
+ environment,
40
+ release,
41
+ tracesSampleRate: 1.0,
42
+ })
package/nuxt.config.ts CHANGED
@@ -2,7 +2,17 @@ import tailwindcss from '@tailwindcss/vite'
2
2
  import { defu } from 'defu'
3
3
  import { createResolver } from 'nuxt/kit'
4
4
 
5
- import { IS_IN_STACK, SITE_URL, VIO_NUXT_BASE_CONFIG } from './node/static'
5
+ import {
6
+ IS_IN_STACK,
7
+ NUXT_PUBLIC_SENTRY_ENVIRONMENT,
8
+ NUXT_PUBLIC_SENTRY_HOST,
9
+ NUXT_PUBLIC_SENTRY_LOGS_ENABLE,
10
+ NUXT_PUBLIC_SENTRY_PROJECT_ID,
11
+ NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY,
12
+ NUXT_PUBLIC_SENTRY_RELEASE,
13
+ SITE_URL,
14
+ VIO_NUXT_BASE_CONFIG,
15
+ } from './node/static'
6
16
  import {
7
17
  GTAG_COOKIE_ID,
8
18
  TIMEZONE_COOKIE_NAME,
@@ -53,6 +63,7 @@ export default defineNuxtConfig(
53
63
  '@nuxtjs/seo',
54
64
  '@nuxtjs/turnstile',
55
65
  '@pinia/nuxt',
66
+ '@sentry/nuxt/module',
56
67
  'nuxt-gtag',
57
68
  'shadcn-nuxt',
58
69
  (_options, nuxt) => {
@@ -72,7 +83,16 @@ export default defineNuxtConfig(
72
83
  ) {
73
84
  if (nuxt.options.nitro.static) {
74
85
  nuxtConfigSecurityHeaders.contentSecurityPolicy = defu(
75
- VIO_GET_CSP({ siteUrl: new URL(SITE_URL) }),
86
+ VIO_GET_CSP({
87
+ sentry:
88
+ NUXT_PUBLIC_SENTRY_HOST && NUXT_PUBLIC_SENTRY_PROJECT_ID
89
+ ? {
90
+ host: NUXT_PUBLIC_SENTRY_HOST,
91
+ projectId: NUXT_PUBLIC_SENTRY_PROJECT_ID,
92
+ }
93
+ : undefined,
94
+ siteUrl: new URL(SITE_URL),
95
+ }),
76
96
  nuxtConfigSecurityHeaders.contentSecurityPolicy,
77
97
  )
78
98
  }
@@ -106,6 +126,37 @@ export default defineNuxtConfig(
106
126
  i18n: {
107
127
  baseUrl: SITE_URL,
108
128
  },
129
+ sentry: {
130
+ environment: NUXT_PUBLIC_SENTRY_ENVIRONMENT,
131
+ features: {
132
+ consoleCapture: true,
133
+ pinia: true,
134
+ profiling: true,
135
+ replay: true,
136
+ securityHeaders: true,
137
+ userTracking: true,
138
+ },
139
+ host: NUXT_PUBLIC_SENTRY_HOST,
140
+ logs: {
141
+ enable: NUXT_PUBLIC_SENTRY_LOGS_ENABLE,
142
+ },
143
+ profiles: {
144
+ sampleRate: 1.0,
145
+ },
146
+ project: {
147
+ id: NUXT_PUBLIC_SENTRY_PROJECT_ID,
148
+ publicKey: NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY,
149
+ },
150
+ release: NUXT_PUBLIC_SENTRY_RELEASE,
151
+ replays: {
152
+ onError: {
153
+ sampleRate: 1.0,
154
+ },
155
+ session: {
156
+ sampleRate: 0.0,
157
+ },
158
+ },
159
+ },
109
160
  site: {
110
161
  url: SITE_URL, // TODO: evaluate removal for static builds in favor of i18n.baseUrl
111
162
  },
@@ -127,11 +178,7 @@ export default defineNuxtConfig(
127
178
  },
128
179
  tsConfig: {
129
180
  nodeTsConfig: {
130
- include: [
131
- resolve('../.config'),
132
- resolve('../node'),
133
- // resolve('../sentry.server.config.ts'),
134
- ],
181
+ include: [resolve('../.config'), resolve('../node')],
135
182
  },
136
183
  vueCompilerOptions: {
137
184
  htmlAttributes: [], // https://github.com/johnsoncodehk/volar/issues/1970#issuecomment-1276994634
@@ -290,6 +337,16 @@ export default defineNuxtConfig(
290
337
  },
291
338
  strict: true,
292
339
  },
340
+ sentry: {
341
+ release: {
342
+ name: process.env.RELEASE_NAME || undefined,
343
+ },
344
+ sourcemaps: {
345
+ disable: !process.env.SENTRY_AUTH_TOKEN,
346
+ },
347
+ telemetry: false,
348
+ // `org`, `project` and `authToken` are read from the `SENTRY_ORG`, `SENTRY_PROJECT` and `SENTRY_AUTH_TOKEN` environment variables by the module itself.
349
+ },
293
350
  seo: {
294
351
  minify: false, // TODO: enable (https://github.com/harlan-zw/nuxt-seo-utils/issues/103)
295
352
  },
@@ -326,6 +383,9 @@ export default defineNuxtConfig(
326
383
  strictTransportSecurity: false, // prevent endless reload in Chrome
327
384
  },
328
385
  },
386
+ sentry: {
387
+ enabled: false,
388
+ },
329
389
  site: {
330
390
  debug: true,
331
391
  },
package/package.json CHANGED
@@ -15,6 +15,7 @@
15
15
  "@nuxtjs/seo": "5.3.14",
16
16
  "@nuxtjs/turnstile": "1.1.3",
17
17
  "@pinia/nuxt": "1.0.2",
18
+ "@sentry/nuxt": "10.71.0",
18
19
  "@tailwindcss/forms": "0.5.11",
19
20
  "@tailwindcss/typography": "0.5.20",
20
21
  "@tailwindcss/vite": "4.3.3",
@@ -84,7 +85,9 @@
84
85
  "server",
85
86
  "shared",
86
87
  "nuxt.config.ts",
87
- "package.json"
88
+ "package.json",
89
+ "sentry.client.config.ts",
90
+ "sentry.server.config.ts"
88
91
  ],
89
92
  "main": "nuxt.config.ts",
90
93
  "name": "@dargmuesli/nuxt-vio",
@@ -101,7 +104,7 @@
101
104
  "url": "git+https://github.com/dargmuesli/vio.git"
102
105
  },
103
106
  "type": "module",
104
- "version": "23.0.0-beta.2",
107
+ "version": "23.0.0-beta.3",
105
108
  "scripts": {
106
109
  "build": "pnpm run build:node",
107
110
  "build:node": "NUXT_PUBLIC_I18N_BASE_URL=https://app.localhost:3001 nuxt build playground",
@@ -0,0 +1,38 @@
1
+ import * as Sentry from '@sentry/nuxt'
2
+
3
+ const runtimeConfig = useRuntimeConfig()
4
+ const sharedSentryConfig = useSharedSentryConfig()
5
+
6
+ if (sharedSentryConfig.dsn) {
7
+ const features = runtimeConfig.public.sentry.features
8
+
9
+ Sentry.init({
10
+ ...sharedSentryConfig,
11
+ integrations: [
12
+ ...(features.profiling ? [Sentry.browserProfilingIntegration()] : []),
13
+ ...(features.consoleCapture
14
+ ? [
15
+ Sentry.captureConsoleIntegration(),
16
+ Sentry.consoleLoggingIntegration(),
17
+ ]
18
+ : []),
19
+ Sentry.httpClientIntegration(),
20
+ ...(features.pinia ? [Sentry.piniaIntegration(usePinia())] : []),
21
+ ...(features.replay ? [Sentry.replayIntegration()] : []),
22
+ Sentry.zodErrorsIntegration(),
23
+ ],
24
+ profilesSampleRate: features.profiling
25
+ ? runtimeConfig.public.sentry.profiles.sampleRate
26
+ : undefined,
27
+ replaysOnErrorSampleRate: features.replay
28
+ ? runtimeConfig.public.sentry.replays.onError.sampleRate
29
+ : undefined,
30
+ replaysSessionSampleRate: features.replay
31
+ ? runtimeConfig.public.sentry.replays.session.sampleRate
32
+ : undefined,
33
+ })
34
+ } else {
35
+ console.warn(
36
+ 'Sentry configuration is incomplete, skipping Sentry initialization.',
37
+ )
38
+ }
@@ -0,0 +1,36 @@
1
+ import * as Sentry from '@sentry/nuxt'
2
+
3
+ import {
4
+ getSharedSentryConfig,
5
+ IS_IN_PRODUCTION,
6
+ NUXT_PUBLIC_SENTRY_ENVIRONMENT,
7
+ NUXT_PUBLIC_SENTRY_HOST,
8
+ NUXT_PUBLIC_SENTRY_LOGS_ENABLE,
9
+ NUXT_PUBLIC_SENTRY_PROJECT_ID,
10
+ NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY,
11
+ NUXT_PUBLIC_SENTRY_RELEASE,
12
+ } from './node/static'
13
+
14
+ const sharedSentryConfig = getSharedSentryConfig({
15
+ enableLogs: NUXT_PUBLIC_SENTRY_LOGS_ENABLE,
16
+ environment: NUXT_PUBLIC_SENTRY_ENVIRONMENT,
17
+ host: NUXT_PUBLIC_SENTRY_HOST,
18
+ isInProduction: IS_IN_PRODUCTION,
19
+ isTesting: !!process.env.NUXT_PUBLIC_VIO_IS_TESTING,
20
+ projectId: NUXT_PUBLIC_SENTRY_PROJECT_ID,
21
+ projectPublicKey: NUXT_PUBLIC_SENTRY_PROJECT_PUBLIC_KEY,
22
+ release: NUXT_PUBLIC_SENTRY_RELEASE,
23
+ })
24
+
25
+ if (sharedSentryConfig.dsn) {
26
+ Sentry.init({
27
+ ...sharedSentryConfig,
28
+ integrations: NUXT_PUBLIC_SENTRY_LOGS_ENABLE
29
+ ? [Sentry.consoleLoggingIntegration()]
30
+ : [],
31
+ })
32
+ } else {
33
+ console.warn(
34
+ 'Sentry configuration is incomplete, skipping Sentry initialization.',
35
+ )
36
+ }
@@ -0,0 +1,33 @@
1
+ export default defineEventHandler((event) => {
2
+ const runtimeConfig = useRuntimeConfig(event)
3
+ const sentryConfig = runtimeConfig.public.sentry
4
+
5
+ if (
6
+ !sentryConfig.features.securityHeaders ||
7
+ !sentryConfig.host ||
8
+ !sentryConfig.project.id ||
9
+ !sentryConfig.project.publicKey
10
+ ) {
11
+ return
12
+ }
13
+
14
+ const securityEndpointParams = [
15
+ `sentry_key=${sentryConfig.project.publicKey}`,
16
+ sentryConfig.environment &&
17
+ `sentry_environment=${sentryConfig.environment}`,
18
+ sentryConfig.release && `sentry_release=${sentryConfig.release}`,
19
+ ]
20
+ .filter(Boolean)
21
+ .join('&')
22
+
23
+ appendHeader(
24
+ event,
25
+ 'NEL',
26
+ '\'{"report_to":"sentry","max_age":31536000,"include_subdomains":true}\'',
27
+ )
28
+ appendHeader(
29
+ event,
30
+ 'Report-To',
31
+ `'{"group":"sentry","max_age":31536000,"endpoints":[{"url":"https://${sentryConfig.host}/api/${sentryConfig.project.id}/security/?${securityEndpointParams}"}],"include_subdomains":true}'`,
32
+ )
33
+ })
@@ -12,7 +12,13 @@ export const COOKIE_CONTROL_CONSENT_COOKIE_NAME =
12
12
  export const COOKIE_PREFIX = VIO_SITE_NAME.toLocaleLowerCase()
13
13
  export const COOKIE_SEPARATOR = '_'
14
14
  export const FETCH_RETRY_AMOUNT = 3
15
- export const VIO_GET_CSP = ({ siteUrl }: { siteUrl: URL }) =>
15
+ export const VIO_GET_CSP = ({
16
+ sentry,
17
+ siteUrl,
18
+ }: {
19
+ sentry?: { host: string; projectId: string }
20
+ siteUrl: URL
21
+ }) =>
16
22
  defu(
17
23
  {
18
24
  // Cloudflare
@@ -26,6 +32,17 @@ export const VIO_GET_CSP = ({ siteUrl }: { siteUrl: URL }) =>
26
32
  }
27
33
  : {}),
28
34
  },
35
+ {
36
+ // Sentry
37
+ ...(sentry
38
+ ? {
39
+ 'connect-src': [
40
+ `https://${sentry.host}/api/${sentry.projectId}/envelope/`,
41
+ ],
42
+ 'worker-src': ['blob:'],
43
+ }
44
+ : {}),
45
+ },
29
46
  {
30
47
  // Google Analytics 4 (https://developers.google.com/tag-platform/tag-manager/web/csp)
31
48
  'connect-src': [