Skip to content

Commit

Permalink
🚧 rework config and try user in the plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
acidjazz committed Jan 30, 2025
1 parent 0113a68 commit 1a0d22e
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 47 deletions.
13 changes: 8 additions & 5 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { careReportConfig, careConfigDefaults } from './runtime/care'

export interface ModuleOptions {
apiKey: string
env: string
apiDomain: string
verbose: boolean
log: boolean
authUtils: boolean
authUtilsFields: string[]
}
Expand All @@ -17,6 +18,11 @@ declare module 'nuxt/schema' {
*
*/
apiKey: string
/**
* fume.care environment
* @default development
*/
env?: string
/**
* Optional custom fume.care API domain
*
Expand All @@ -29,7 +35,7 @@ declare module 'nuxt/schema' {
*
* @default false
*/
verbose?: boolean
log?: boolean
/**
* Attempt to store the user from nuxt-auth-utils
* @see https://nuxt.com/modules/auth-utils
Expand Down Expand Up @@ -57,9 +63,6 @@ export default defineNuxtModule<ModuleOptions>({
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
const config = useRuntimeConfig().public.care || options

if (config.authUtils) await installModule('nuxt-auth-utils')

nuxt.hook('modules:done', () => careReportConfig(config))
addPlugin(resolver.resolve('./runtime/plugin'))
addServerPlugin(resolver.resolve('./runtime/nitro'))
Expand Down
82 changes: 46 additions & 36 deletions src/runtime/care.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import type { H3Event } from 'h3'
import type { ModuleOptions as Config } from '../module'

interface ErrorPayload {
hook: string
name: string
message: string
stack: string
hook: string
cause: string
client?: boolean
client: boolean
environment: string
os: {
platform: string
arch: string
version: string
}
process: {
pid: number
version: string
}
}

interface ErrorMeta {
Expand All @@ -27,8 +32,9 @@ interface ErrorMeta {
}

export const careConfigDefaults = {
env: 'development',
apiDomain: 'https://fume.care',
verbose: false,
log: false,
authUtils: false,
authUtilsFields: ['id', 'email', 'name', 'avatar'],
}
Expand All @@ -46,6 +52,16 @@ export enum CareHookType {
windowRejection = 'window:unhandledrejection',
}

const getMeta = (config: Config, user?: Record<string, string>) => {
const meta: ErrorMeta = { user: undefined, meta: undefined }
console.log('getMeta user', user)
if (config.authUtils && user) {
meta.user = userFromFields(user, config.authUtilsFields)
}
if (config.log) log.info('[fume.care] stored meta being sent:', JSON.stringify(meta))
return meta
}

const userFromFields = (user: Record<string, unknown>, fields: string[]) => {
if (!user || !fields?.length) return undefined
const filtered = Object.fromEntries(
Expand All @@ -57,52 +73,42 @@ const userFromFields = (user: Record<string, unknown>, fields: string[]) => {
return Object.keys(filtered).length ? filtered : undefined
}

const getMeta = async (config: Config, event?: H3Event) => {
const meta: ErrorMeta = { user: undefined, meta: undefined }

// if we are incorporating nuxt-auth-utils in app/
if (config.authUtils && !event) {
// @ts-expect-error auto-imported
const { user } = useUserSession()
meta.user = userFromFields(user.value, config.authUtilsFields)
}

// if we are incorporating nuxt-auth-utils in server/
if (config.authUtils && event) {
// @ts-expect-error auto-imported
const { user } = await getUserSession(event)
meta.user = userFromFields(user, config.authUtilsFields)
}
if (config.verbose) log.info('[fume.care] stored meta being sent:', JSON.stringify(meta))
return meta
const getEnv = (config: Config) => {
if (config.env) return config.env
if (process.env.NUXT_HUB_ENV) return process.env.NUXT_HUB_ENV
if (process.env.NUXT_APP_ENV) return process.env.NUXT_APP_ENV
return 'development'
}

const validApiKey = (config: Config): boolean => {
if (!config || !config.apiKey) return false
return /^[a-z0-9]{32}$/i.test(config.apiKey)
}

export const careCheckConfig = (config: Config): boolean => {
return validApiKey(config) && getEnv(config) !== 'development'
}

export const careReportConfig = (config: Config) => {
if (!config.apiKey) {
log.info('[fume.care] no API key detected - reporting muted')
log.info('[fume.care] no API key detected - reporting disabled')
}
else if (!validApiKey(config)) {
log.warn('[fume.care] API key is invalid - reporting muted')
log.warn('[fume.care] API key is invalid - reporting disabled')
}
else if (getEnv(config) === 'development') {
log.info('[fume.care] development or undetected environment - reporting disabled')
}
else {
log.success('[fume.care] Valid API key found - reporting activated')
log.success(`[fume.care] Valid API key found - reporting enabled for \`${getEnv(config)}\` environment`)
}

if (config.verbose) {
log.info('[fume.care] Verbose mode enabled - error details will be printed')
if (careCheckConfig(config) && config.log) {
log.info('[fume.care] logging enabled - error details will be printed')
}
}

export const careCheckConfig = (config: Config): boolean => {
return validApiKey(config)
}

export const careReport = async (type: CareHookType, err: unknown, unmerged: Config, event?: H3Event) => {
export const careReport = async (type: CareHookType, err: unknown, unmerged: Config, event?: H3Event, user?: Record<string, string>) => {
const config = mergeConfig(unmerged)
const error = err as ErrorPayload
const payload: ErrorPayload = {
Expand All @@ -112,33 +118,37 @@ export const careReport = async (type: CareHookType, err: unknown, unmerged: Con
hook: type,
cause: error.cause,
client: typeof window !== 'undefined',
environment: getEnv(config),
os: {
platform: process.platform,
arch: process.arch,
version: process.version,
},
process: {
pid: process.pid,
version: process.version,
},
}
const url = `${config.apiDomain}/api/issue`
const meta = getMeta(config, user)
try {
if (config.verbose) {
if (config.log) {
log.info(`[fume.care] Error in ${type} going to ${url}`, payload)
}
const meta = await getMeta(config, event)
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiKey: config.apiKey,
environment: 'production',
payload: JSON.stringify(payload),
meta: JSON.stringify(meta),
}),
})
const data = await response.json()
if (config.verbose) log.success('[fume.care] Error sent successfully:', data.meta)
if (config.log) log.success('[fume.care] Error sent successfully:', data.meta)
return data
}
catch (err) {
if (config.verbose) log.error(`[fume.care] Failed to send error:`, err)
if (config.log) log.error(`[fume.care] Failed to send error:`, err)
}
}
11 changes: 10 additions & 1 deletion src/runtime/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,14 @@ import { CareHookType, careCheckConfig, careReport } from './care'
export default defineNitroPlugin((nitroApp) => {
const config = useRuntimeConfig().public.care as Required<ModuleOptions>
if (careCheckConfig(config))
nitroApp.hooks.hook('error', async (error, { event }) => careReport(CareHookType.nitroError, error, config, event))
nitroApp.hooks.hook('error', async (error, { event }) => {
let user = undefined
if (config.authUtils) {
// @ts-expect-error auto-imported
const { user: userSession } = getUserSession(event)
user = userSession.value
}

careReport(CareHookType.nitroError, error, config, event, user)
})
})
20 changes: 15 additions & 5 deletions src/runtime/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@ import { defineNuxtPlugin, useRuntimeConfig } from '#app'

export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig().public.care as Required<ModuleOptions>
const user = () => {
if (config.authUtils) {
// @ts-expect-error auto-imported
const { user } = useUserSession()
console.log(user.value)
return user.value
}
return undefined
}
if (careCheckConfig(config)) {
if (import.meta.client || window) {
if (import.meta.client || window)
window.addEventListener('unhandledrejection', event =>
careReport(CareHookType.windowRejection, event.reason, config))
}
nuxtApp.hook('vue:error', (error: unknown, _instance, _info) => careReport(CareHookType.vueError, error, config))
nuxtApp.hook('app:error', (error: unknown) => careReport(CareHookType.appError, error, config))
careReport(CareHookType.windowRejection, event.reason, config, undefined, user()))
nuxtApp.hook('vue:error', (error: unknown, _instance, _info) =>
careReport(CareHookType.vueError, error, config, undefined, user()))
nuxtApp.hook('app:error', (error: unknown) =>
careReport(CareHookType.appError, error, config, undefined, user()))
}
})

0 comments on commit 1a0d22e

Please sign in to comment.