diff --git a/assets/auto-imports.d.ts b/assets/auto-imports.d.ts index b4bf0999..af6c69dc 100644 --- a/assets/auto-imports.d.ts +++ b/assets/auto-imports.d.ts @@ -31,11 +31,9 @@ declare global { const controlledRef: typeof import('@vueuse/core')['controlledRef'] const createApp: typeof import('vue')['createApp'] const createEventHook: typeof import('@vueuse/core')['createEventHook'] - const createGenericProjection: typeof import('@vueuse/math')['createGenericProjection'] const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] const createPinia: typeof import('pinia')['createPinia'] - const createProjection: typeof import('@vueuse/math')['createProjection'] const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] @@ -70,9 +68,6 @@ declare global { const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] const lightTheme: typeof import('./stores/settings')['lightTheme'] - const logicAnd: typeof import('@vueuse/math')['logicAnd'] - const logicNot: typeof import('@vueuse/math')['logicNot'] - const logicOr: typeof import('@vueuse/math')['logicOr'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] const mapActions: typeof import('pinia')['mapActions'] const mapGetters: typeof import('pinia')['mapGetters'] @@ -120,7 +115,6 @@ declare global { const refDefault: typeof import('@vueuse/core')['refDefault'] const refThrottled: typeof import('@vueuse/core')['refThrottled'] const refWithControl: typeof import('@vueuse/core')['refWithControl'] - const releases: typeof import('./stores/releases')['default'] const resolveComponent: typeof import('vue')['resolveComponent'] const resolveRef: typeof import('@vueuse/core')['resolveRef'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] @@ -160,7 +154,6 @@ declare global { const unref: typeof import('vue')['unref'] const unrefElement: typeof import('@vueuse/core')['unrefElement'] const until: typeof import('@vueuse/core')['until'] - const useAbs: typeof import('@vueuse/math')['useAbs'] const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] const useAnimate: typeof import('@vueuse/core')['useAnimate'] const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] @@ -178,7 +171,6 @@ declare global { const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] const useAttrs: typeof import('vue')['useAttrs'] - const useAverage: typeof import('@vueuse/math')['useAverage'] const useBase64: typeof import('@vueuse/core')['useBase64'] const useBattery: typeof import('@vueuse/core')['useBattery'] const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] @@ -186,8 +178,6 @@ declare global { const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] const useCached: typeof import('@vueuse/core')['useCached'] - const useCeil: typeof import('@vueuse/math')['useCeil'] - const useClamp: typeof import('@vueuse/math')['useClamp'] const useClipboard: typeof import('@vueuse/core')['useClipboard'] const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems'] const useCloned: typeof import('@vueuse/core')['useCloned'] @@ -228,7 +218,6 @@ declare global { const useFetch: typeof import('@vueuse/core')['useFetch'] const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] - const useFloor: typeof import('@vueuse/math')['useFloor'] const useFocus: typeof import('@vueuse/core')['useFocus'] const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] const useFps: typeof import('@vueuse/core')['useFps'] @@ -250,13 +239,10 @@ declare global { const useLogStream: typeof import('./composable/eventsource')['useLogStream'] const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] - const useMath: typeof import('@vueuse/math')['useMath'] - const useMax: typeof import('@vueuse/math')['useMax'] const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] const useMemoize: typeof import('@vueuse/core')['useMemoize'] const useMemory: typeof import('@vueuse/core')['useMemory'] - const useMin: typeof import('@vueuse/math')['useMin'] const useMounted: typeof import('@vueuse/core')['useMounted'] const useMouse: typeof import('@vueuse/core')['useMouse'] const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] @@ -276,19 +262,16 @@ declare global { const usePointer: typeof import('@vueuse/core')['usePointer'] const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] - const usePrecision: typeof import('@vueuse/math')['usePrecision'] const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] const usePrevious: typeof import('@vueuse/core')['usePrevious'] - const useProjection: typeof import('@vueuse/math')['useProjection'] const useRafFn: typeof import('@vueuse/core')['useRafFn'] const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] const useReleases: typeof import('./stores/releases')['useReleases'] const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] - const useRound: typeof import('@vueuse/math')['useRound'] const useRoute: typeof import('vue-router')['useRoute'] const useRouter: typeof import('vue-router')['useRouter'] const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] @@ -308,7 +291,6 @@ declare global { const useStorage: typeof import('@vueuse/core')['useStorage'] const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] - const useSum: typeof import('@vueuse/math')['useSum'] const useSupported: typeof import('@vueuse/core')['useSupported'] const useSwipe: typeof import('@vueuse/core')['useSwipe'] const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] @@ -329,7 +311,6 @@ declare global { const useToast: typeof import('./composable/toast')['useToast'] const useToggle: typeof import('@vueuse/core')['useToggle'] const useTransition: typeof import('@vueuse/core')['useTransition'] - const useTrunc: typeof import('@vueuse/math')['useTrunc'] const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] const useVModel: typeof import('@vueuse/core')['useVModel'] diff --git a/internal/analytics/http_beacon.go b/internal/analytics/http_beacon.go new file mode 100644 index 00000000..825ed4f0 --- /dev/null +++ b/internal/analytics/http_beacon.go @@ -0,0 +1,40 @@ +package analytics + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httputil" + + log "github.com/sirupsen/logrus" +) + +func SendBeacon(e BeaconEvent) error { + jsonValue, err := json.Marshal(e) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", "https://b.dozzle.dev/event", bytes.NewBuffer(jsonValue)) + if err != nil { + return err + } + + response, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode/100 != 2 { + dump, err := httputil.DumpResponse(response, true) + if err != nil { + return err + } + log.Debugf("%v", string(dump)) + return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status) + } + + return nil +} diff --git a/internal/analytics/types.go b/internal/analytics/types.go index f84affea..3ec028fc 100644 --- a/internal/analytics/types.go +++ b/internal/analytics/types.go @@ -16,3 +16,16 @@ type RequestEvent struct { TotalContainers int `json:"totalContainers"` RunningContainers int `json:"runningContainers"` } + +type BeaconEvent struct { + Version string `json:"version"` + Browser string `json:"browser"` + AuthProvider string `json:"authProvider"` + FilterLength int `json:"filterLength"` + RemoteHostLength int `json:"remoteHostLength"` + HasDocumentation bool `json:"hasDocumentation"` + HasCustomAddress bool `json:"hasCustomAddress"` + HasCustomBase bool `json:"hasCustomBase"` + HasHostname bool `json:"hasHostname"` + RunningContainers int `json:"runningContainers"` +} diff --git a/internal/web/events.go b/internal/web/events.go index 9d414f31..370330df 100644 --- a/internal/web/events.go +++ b/internal/web/events.go @@ -8,6 +8,8 @@ import ( "net/http" "sync" + "github.com/amir20/dozzle/internal/analytics" + "github.com/amir20/dozzle/internal/content" "github.com/amir20/dozzle/internal/docker" log "github.com/sirupsen/logrus" @@ -31,6 +33,18 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { events := make(chan docker.ContainerEvent) stats := make(chan docker.ContainerStat) + pages, _ := content.ReadAll() + b := analytics.BeaconEvent{ + Version: h.config.Version, + Browser: r.Header.Get("User-Agent"), + AuthProvider: string(h.config.Authorization.Provider), + HasHostname: h.config.Hostname != "", + HasCustomBase: h.config.Base != "", + HasCustomAddress: h.config.Addr != "", + RemoteHostLength: len(h.clients), + HasDocumentation: len(pages) > 0, + } + { wg := sync.WaitGroup{} wg.Add(len(h.clients)) @@ -69,9 +83,18 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { log.Errorf("error writing containers to event stream: %v", err) } + b.RunningContainers = len(allContainers) f.Flush() } + if !h.config.NoAnalytics { + go func() { + if err := analytics.SendBeacon(b); err != nil { + log.Debugf("error sending beacon: %v", err) + } + }() + } + for { select { case stat := <-stats: