mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-25 14:59:26 +01:00
feat: list of releases can now be seen at the top (#2480)
This commit is contained in:
85
assets/auto-imports.d.ts
vendored
85
assets/auto-imports.d.ts
vendored
@@ -26,7 +26,7 @@ declare global {
|
||||
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||
const config: typeof import('./stores/config')['default']
|
||||
const containerContext: typeof import('./composables/containerContext')['containerContext']
|
||||
const containerContext: typeof import('./composable/containerContext')['containerContext']
|
||||
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
@@ -56,14 +56,14 @@ declare global {
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const getDeep: typeof import('./utils/index')['getDeep']
|
||||
const globalShowPopup: typeof import('./composables/popup')['globalShowPopup']
|
||||
const globalShowPopup: typeof import('./composable/popup')['globalShowPopup']
|
||||
const h: typeof import('vue')['h']
|
||||
const hourStyle: typeof import('./stores/settings')['hourStyle']
|
||||
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const injectLocal: typeof import('@vueuse/core')['injectLocal']
|
||||
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||
const isMobile: typeof import('./composables/media')['isMobile']
|
||||
const isMobile: typeof import('./composable/media')['isMobile']
|
||||
const isObject: typeof import('./utils/index')['isObject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
@@ -102,10 +102,10 @@ declare global {
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||
const persistentVisibleKeys: typeof import('./composables/storage')['persistentVisibleKeys']
|
||||
const pinnedContainers: typeof import('./composables/storage')['pinnedContainers']
|
||||
const persistentVisibleKeys: typeof import('./composable/storage')['persistentVisibleKeys']
|
||||
const pinnedContainers: typeof import('./composable/storage')['pinnedContainers']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const provideContainerContext: typeof import('./composables/containerContext')['provideContainerContext']
|
||||
const provideContainerContext: typeof import('./composable/containerContext')['provideContainerContext']
|
||||
const provideLocal: typeof import('@vueuse/core')['provideLocal']
|
||||
const reactify: typeof import('@vueuse/core')['reactify']
|
||||
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||
@@ -120,14 +120,15 @@ 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']
|
||||
const search: typeof import('./stores/settings')['search']
|
||||
const sessionHost: typeof import('./composables/storage')['sessionHost']
|
||||
const sessionHost: typeof import('./composable/storage')['sessionHost']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const setTitle: typeof import('./composables/title')['setTitle']
|
||||
const setTitle: typeof import('./composable/title')['setTitle']
|
||||
const settings: typeof import('./stores/settings')['settings']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
@@ -188,10 +189,11 @@ declare global {
|
||||
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']
|
||||
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||
const useContainerContext: typeof import('./composables/containerContext')['useContainerContext']
|
||||
const useContainerContext: typeof import('./composable/containerContext')['useContainerContext']
|
||||
const useContainerStore: typeof import('./stores/container')['useContainerStore']
|
||||
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
@@ -245,7 +247,7 @@ declare global {
|
||||
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||
const useLink: typeof import('vue-router')['useLink']
|
||||
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||
const useLogStream: typeof import('./composables/eventsource')['useLogStream']
|
||||
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']
|
||||
@@ -284,6 +286,7 @@ declare global {
|
||||
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']
|
||||
@@ -293,7 +296,7 @@ declare global {
|
||||
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||
const useSearchFilter: typeof import('./composables/search')['useSearchFilter']
|
||||
const useSearchFilter: typeof import('./composable/search')['useSearchFilter']
|
||||
const useSeoMeta: typeof import('@vueuse/head')['useSeoMeta']
|
||||
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||
const useShare: typeof import('@vueuse/core')['useShare']
|
||||
@@ -323,7 +326,7 @@ declare global {
|
||||
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||
const useToNumber: typeof import('@vueuse/core')['useToNumber']
|
||||
const useToString: typeof import('@vueuse/core')['useToString']
|
||||
const useToast: typeof import('./composables/toast')['useToast']
|
||||
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']
|
||||
@@ -333,7 +336,7 @@ declare global {
|
||||
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||
const useVisibleFilter: typeof import('./composables/visible')['useVisibleFilter']
|
||||
const useVisibleFilter: typeof import('./composable/visible')['useVisibleFilter']
|
||||
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||
@@ -390,7 +393,7 @@ declare module 'vue' {
|
||||
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
|
||||
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
|
||||
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composable/containerContext')['containerContext']>
|
||||
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
|
||||
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
@@ -418,14 +421,14 @@ declare module 'vue' {
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly getDeep: UnwrapRef<typeof import('./utils/index')['getDeep']>
|
||||
readonly globalShowPopup: UnwrapRef<typeof import('./composables/popup')['globalShowPopup']>
|
||||
readonly globalShowPopup: UnwrapRef<typeof import('./composable/popup')['globalShowPopup']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly hourStyle: UnwrapRef<typeof import('./stores/settings')['hourStyle']>
|
||||
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
|
||||
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
||||
readonly isMobile: UnwrapRef<typeof import('./composables/media')['isMobile']>
|
||||
readonly isMobile: UnwrapRef<typeof import('./composable/media')['isMobile']>
|
||||
readonly isObject: UnwrapRef<typeof import('./utils/index')['isObject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
@@ -461,10 +464,10 @@ declare module 'vue' {
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composable/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composable/storage')['pinnedContainers']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composable/containerContext')['provideContainerContext']>
|
||||
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
|
||||
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
|
||||
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
|
||||
@@ -483,10 +486,10 @@ declare module 'vue' {
|
||||
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
|
||||
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
|
||||
readonly search: UnwrapRef<typeof import('./stores/settings')['search']>
|
||||
readonly sessionHost: UnwrapRef<typeof import('./composables/storage')['sessionHost']>
|
||||
readonly sessionHost: UnwrapRef<typeof import('./composable/storage')['sessionHost']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly setTitle: UnwrapRef<typeof import('./composables/title')['setTitle']>
|
||||
readonly setTitle: UnwrapRef<typeof import('./composable/title')['setTitle']>
|
||||
readonly settings: UnwrapRef<typeof import('./stores/settings')['settings']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
@@ -543,10 +546,11 @@ declare module 'vue' {
|
||||
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
|
||||
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
|
||||
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
|
||||
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
|
||||
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
|
||||
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
|
||||
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
@@ -599,7 +603,7 @@ declare module 'vue' {
|
||||
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
|
||||
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
||||
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composables/eventsource')['useLogStream']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
|
||||
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
|
||||
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
|
||||
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
|
||||
@@ -633,6 +637,7 @@ declare module 'vue' {
|
||||
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
|
||||
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
|
||||
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
|
||||
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
|
||||
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
|
||||
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
||||
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
||||
@@ -641,7 +646,7 @@ declare module 'vue' {
|
||||
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
|
||||
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
|
||||
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
|
||||
readonly useSearchFilter: UnwrapRef<typeof import('./composables/search')['useSearchFilter']>
|
||||
readonly useSearchFilter: UnwrapRef<typeof import('./composable/search')['useSearchFilter']>
|
||||
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
|
||||
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
|
||||
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
|
||||
@@ -670,7 +675,7 @@ declare module 'vue' {
|
||||
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
|
||||
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
|
||||
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composable/toast')['useToast']>
|
||||
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
|
||||
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
|
||||
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
|
||||
@@ -679,7 +684,7 @@ declare module 'vue' {
|
||||
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
|
||||
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
|
||||
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
|
||||
readonly useVisibleFilter: UnwrapRef<typeof import('./composables/visible')['useVisibleFilter']>
|
||||
readonly useVisibleFilter: UnwrapRef<typeof import('./composable/visible')['useVisibleFilter']>
|
||||
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
|
||||
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
|
||||
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
|
||||
@@ -730,7 +735,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
|
||||
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
|
||||
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composable/containerContext')['containerContext']>
|
||||
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
|
||||
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
@@ -758,14 +763,14 @@ declare module '@vue/runtime-core' {
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly getDeep: UnwrapRef<typeof import('./utils/index')['getDeep']>
|
||||
readonly globalShowPopup: UnwrapRef<typeof import('./composables/popup')['globalShowPopup']>
|
||||
readonly globalShowPopup: UnwrapRef<typeof import('./composable/popup')['globalShowPopup']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly hourStyle: UnwrapRef<typeof import('./stores/settings')['hourStyle']>
|
||||
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
|
||||
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
||||
readonly isMobile: UnwrapRef<typeof import('./composables/media')['isMobile']>
|
||||
readonly isMobile: UnwrapRef<typeof import('./composable/media')['isMobile']>
|
||||
readonly isObject: UnwrapRef<typeof import('./utils/index')['isObject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
@@ -801,10 +806,10 @@ declare module '@vue/runtime-core' {
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composable/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composable/storage')['pinnedContainers']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composable/containerContext')['provideContainerContext']>
|
||||
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
|
||||
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
|
||||
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
|
||||
@@ -823,10 +828,10 @@ declare module '@vue/runtime-core' {
|
||||
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
|
||||
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
|
||||
readonly search: UnwrapRef<typeof import('./stores/settings')['search']>
|
||||
readonly sessionHost: UnwrapRef<typeof import('./composables/storage')['sessionHost']>
|
||||
readonly sessionHost: UnwrapRef<typeof import('./composable/storage')['sessionHost']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly setTitle: UnwrapRef<typeof import('./composables/title')['setTitle']>
|
||||
readonly setTitle: UnwrapRef<typeof import('./composable/title')['setTitle']>
|
||||
readonly settings: UnwrapRef<typeof import('./stores/settings')['settings']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
@@ -883,10 +888,11 @@ declare module '@vue/runtime-core' {
|
||||
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
|
||||
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
|
||||
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
|
||||
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
|
||||
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composable/containerContext')['useContainerContext']>
|
||||
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
|
||||
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
@@ -939,7 +945,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
|
||||
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
||||
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composables/eventsource')['useLogStream']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
|
||||
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
|
||||
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
|
||||
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
|
||||
@@ -973,6 +979,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
|
||||
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
|
||||
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
|
||||
readonly useReleases: UnwrapRef<typeof import('./stores/releases')['useReleases']>
|
||||
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
|
||||
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
||||
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
||||
@@ -981,7 +988,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
|
||||
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
|
||||
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
|
||||
readonly useSearchFilter: UnwrapRef<typeof import('./composables/search')['useSearchFilter']>
|
||||
readonly useSearchFilter: UnwrapRef<typeof import('./composable/search')['useSearchFilter']>
|
||||
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
|
||||
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
|
||||
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
|
||||
@@ -1010,7 +1017,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
|
||||
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
|
||||
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composable/toast')['useToast']>
|
||||
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
|
||||
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
|
||||
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
|
||||
@@ -1019,7 +1026,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
|
||||
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
|
||||
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
|
||||
readonly useVisibleFilter: UnwrapRef<typeof import('./composables/visible')['useVisibleFilter']>
|
||||
readonly useVisibleFilter: UnwrapRef<typeof import('./composable/visible')['useVisibleFilter']>
|
||||
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
|
||||
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
|
||||
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
|
||||
|
||||
5
assets/components.d.ts
vendored
5
assets/components.d.ts
vendored
@@ -29,13 +29,14 @@ declare module 'vue' {
|
||||
DistanceTime: typeof import('./components/common/DistanceTime.vue')['default']
|
||||
DockerEventLogItem: typeof import('./components/LogViewer/DockerEventLogItem.vue')['default']
|
||||
Dropdown: typeof import('./components/common/Dropdown.vue')['default']
|
||||
DropdownMenu: typeof import('./components/common/DropdownMenu.vue')['default']
|
||||
FieldList: typeof import('./components/LogViewer/FieldList.vue')['default']
|
||||
FuzzySearchModal: typeof import('./components/FuzzySearchModal.vue')['default']
|
||||
'Ic:sharpFindInPage': typeof import('~icons/ic/sharp-find-in-page')['default']
|
||||
'Ic:sharpKeyboardReturn': typeof import('~icons/ic/sharp-keyboard-return')['default']
|
||||
InfiniteLoader: typeof import('./components/InfiniteLoader.vue')['default']
|
||||
KeyShortcut: typeof import('./components/common/KeyShortcut.vue')['default']
|
||||
Links: typeof import('./components/common/Links.vue')['default']
|
||||
Links: typeof import('./components/Links.vue')['default']
|
||||
LogActionsToolbar: typeof import('./components/LogViewer/LogActionsToolbar.vue')['default']
|
||||
LogContainer: typeof import('./components/LogViewer/LogContainer.vue')['default']
|
||||
LogDate: typeof import('./components/LogViewer/LogDate.vue')['default']
|
||||
@@ -44,6 +45,7 @@ declare module 'vue' {
|
||||
LogStd: typeof import('./components/LogViewer/LogStd.vue')['default']
|
||||
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
|
||||
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
|
||||
'Mdi:announcement': typeof import('~icons/mdi/announcement')['default']
|
||||
'Mdi:arrowUp': typeof import('~icons/mdi/arrow-up')['default']
|
||||
'Mdi:check': typeof import('~icons/mdi/check')['default']
|
||||
'Mdi:chevronDoubleDown': typeof import('~icons/mdi/chevron-double-down')['default']
|
||||
@@ -64,6 +66,7 @@ declare module 'vue' {
|
||||
'Ph:computerTower': typeof import('~icons/ph/computer-tower')['default']
|
||||
'Ph:controlBold': typeof import('~icons/ph/control-bold')['default']
|
||||
Popup: typeof import('./components/Popup.vue')['default']
|
||||
Releases: typeof import('./components/Releases.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ScrollableView: typeof import('./components/ScrollableView.vue')['default']
|
||||
|
||||
63
assets/components/Links.vue
Normal file
63
assets/components/Links.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-end gap-4">
|
||||
<template v-if="config.pages">
|
||||
<router-link
|
||||
:to="{ name: 'content-id', params: { id: page.id } }"
|
||||
:title="page.title"
|
||||
v-for="page in config.pages"
|
||||
:key="page.id"
|
||||
class="link-primary"
|
||||
>
|
||||
{{ page.title }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<dropdown class="dropdown-end" @closed="latestTag = latest?.tag ?? config.version">
|
||||
<template #trigger>
|
||||
<mdi:announcement class="h-6 w-6 -rotate-12" />
|
||||
<span
|
||||
class="absolute right-px top-0 h-2 w-2 rounded-full bg-red"
|
||||
v-if="hasUpdate && latestTag != latest?.tag"
|
||||
></span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="w-72">
|
||||
<releases />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
|
||||
<dropdown class="dropdown-end" v-if="config.user">
|
||||
<template #trigger>
|
||||
<img class="h-8 w-8 max-w-none rounded-full p-1 ring-2 ring-base-content/50" :src="config.user.avatar" />
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="p-2">
|
||||
<div class="font-bold">
|
||||
{{ config.user.name }}
|
||||
</div>
|
||||
<div class="text-sm font-light">
|
||||
{{ config.user.email }}
|
||||
</div>
|
||||
</div>
|
||||
<ul class="menu mt-4 p-0">
|
||||
<li v-if="config.authProvider === 'simple'">
|
||||
<button @click.prevent="logout()" class="text-primary">{{ $t("button.logout") }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
async function logout() {
|
||||
await fetch(withBase("/api/token"), {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
location.reload();
|
||||
}
|
||||
|
||||
const { hasUpdate, latest } = useReleases();
|
||||
const latestTag = useStorage("DOZZLE_LATEST_TAG", config.version);
|
||||
</script>
|
||||
@@ -5,11 +5,11 @@ import EventSource, { sources } from "eventsourcemock";
|
||||
import LogEventSource from "./LogEventSource.vue";
|
||||
import LogViewer from "./LogViewer.vue";
|
||||
import { settings } from "@/stores/settings";
|
||||
import { useSearchFilter } from "@/composables/search";
|
||||
import { useSearchFilter } from "@/composable/search";
|
||||
import { vi, describe, expect, beforeEach, test, afterEach } from "vitest";
|
||||
import { computed, nextTick } from "vue";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { containerContext } from "@/composables/containerContext";
|
||||
import { containerContext } from "@/composable/containerContext";
|
||||
|
||||
vi.mock("@/stores/config", () => ({
|
||||
__esModule: true,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { globalShowPopup } from "@/composables/popup";
|
||||
import { globalShowPopup } from "@/composable/popup";
|
||||
|
||||
let glopbalShow = globalShowPopup();
|
||||
let show = ref(glopbalShow.value);
|
||||
|
||||
74
assets/components/Releases.vue
Normal file
74
assets/components/Releases.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<ul class="space-y-4 p-2">
|
||||
<li v-for="release in releases" v-if="releases?.length">
|
||||
<div class="flex items-baseline gap-1">
|
||||
<carbon:warning class="h-4.25 w-4.25 self-center stroke-orange" v-if="release.breaking > 0" />
|
||||
<a :href="release.htmlUrl" class="link-primary text-lg font-bold" target="_blank" rel="noreferrer noopener">
|
||||
{{ release.name }}
|
||||
</a>
|
||||
<span class="ml-1 text-xs"><distance-time :date="new Date(release.createdAt)" /></span>
|
||||
<tag class="ml-auto bg-red px-1 py-1 text-xs" v-if="release.tag === latest?.tag">
|
||||
{{ $t("releases.latest") }}
|
||||
</tag>
|
||||
</div>
|
||||
<div class="text-sm text-base-content/80">
|
||||
{{ summary(release) }}
|
||||
</div>
|
||||
</li>
|
||||
<li v-else>
|
||||
<div class="text-sm text-base-content/80">
|
||||
{{ $t("releases.no_releases") }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { releases, latest } = useReleases();
|
||||
const { t } = useI18n();
|
||||
|
||||
function summary(release: { features: number; bugFixes: number; breaking: number }) {
|
||||
if (release.features > 0 && release.bugFixes > 0 && release.breaking > 0) {
|
||||
return t("releases.three_parts", {
|
||||
first: t("releases.breaking", { count: release.breaking }),
|
||||
second: t("releases.features", { count: release.features }),
|
||||
third: t("releases.bugFixes", { count: release.bugFixes }),
|
||||
});
|
||||
}
|
||||
|
||||
if (release.features > 0 && release.bugFixes > 0) {
|
||||
return t("releases.two_parts", {
|
||||
first: t("releases.features", { count: release.features }),
|
||||
second: t("releases.bugFixes", { count: release.bugFixes }),
|
||||
});
|
||||
}
|
||||
|
||||
if (release.features > 0 && release.breaking > 0) {
|
||||
return t("releases.two_parts", {
|
||||
first: t("releases.features", { count: release.features }),
|
||||
second: t("releases.breaking", { count: release.breaking }),
|
||||
});
|
||||
}
|
||||
|
||||
if (release.bugFixes > 0 && release.breaking > 0) {
|
||||
return t("releases.two_parts", {
|
||||
first: t("releases.bugFixes", { count: release.bugFixes }),
|
||||
second: t("releases.breaking", { count: release.breaking }),
|
||||
});
|
||||
}
|
||||
|
||||
if (release.features > 0) {
|
||||
return t("releases.features", { count: release.features });
|
||||
}
|
||||
|
||||
if (release.bugFixes > 0) {
|
||||
return t("releases.bugFixes", { count: release.bugFixes });
|
||||
}
|
||||
|
||||
if (release.breaking > 0) {
|
||||
return t("releases.breaking", { count: release.breaking });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
import { sessionHost } from "@/composables/storage";
|
||||
import { sessionHost } from "@/composable/storage";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -1,44 +1,25 @@
|
||||
<template>
|
||||
<details class="dropdown" ref="details" v-on-click-outside="close">
|
||||
<summary class="btn btn-primary flex-nowrap" v-bind="$attrs">
|
||||
<slot name="trigger"> {{ values[modelValue] ?? defaultLabel }} <carbon:caret-down /></slot>
|
||||
</summary>
|
||||
<ul class="menu dropdown-content rounded-box z-50 mt-1 w-52 border border-base-content/20 bg-base p-2 shadow">
|
||||
<slot>
|
||||
<li v-for="item in options">
|
||||
<a @click="modelValue = item.value">
|
||||
<mdi:check class="w-4" v-if="modelValue == item.value" />
|
||||
<div v-else class="w-4"></div>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</slot>
|
||||
</ul>
|
||||
</details>
|
||||
<div class="dropdown">
|
||||
<label tabindex="0" class="btn btn-circle btn-sm" @mousedown="checkAndCloseDropDown($event)" @blur="closed()">
|
||||
<slot name="trigger"></slot>
|
||||
</label>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="min-w-52 dropdown-content rounded-box z-50 mt-1 border border-base-content/20 bg-base p-2 shadow"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { vOnClickOutside } from "@vueuse/components";
|
||||
type DropdownItem = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
const { options = [], defaultLabel = "" } = defineProps<{ options?: DropdownItem[]; defaultLabel?: string }>();
|
||||
const { modelValue } = defineModels<{
|
||||
modelValue: string;
|
||||
}>();
|
||||
|
||||
const values = computed(() =>
|
||||
options.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr.value] = curr.label;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
);
|
||||
|
||||
const details = ref<HTMLElement | null>(null);
|
||||
const close = () => details.value?.removeAttribute("open");
|
||||
watch(modelValue, () => close());
|
||||
<script setup lang="ts">
|
||||
const closed = defineEmit();
|
||||
function checkAndCloseDropDown(e: MouseEvent) {
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
if (target?.matches(":focus")) {
|
||||
setTimeout(() => target.blur(), 0);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
44
assets/components/common/DropdownMenu.vue
Normal file
44
assets/components/common/DropdownMenu.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<details class="dropdown" ref="details" v-on-click-outside="close">
|
||||
<summary class="btn btn-primary flex-nowrap" v-bind="$attrs">
|
||||
<slot name="trigger"> {{ values[modelValue] ?? defaultLabel }} <carbon:caret-down /></slot>
|
||||
</summary>
|
||||
<ul class="menu dropdown-content rounded-box z-50 mt-1 w-52 border border-base-content/20 bg-base p-2 shadow">
|
||||
<slot>
|
||||
<li v-for="item in options">
|
||||
<a @click="modelValue = item.value">
|
||||
<mdi:check class="w-4" v-if="modelValue == item.value" />
|
||||
<div v-else class="w-4"></div>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</slot>
|
||||
</ul>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { vOnClickOutside } from "@vueuse/components";
|
||||
type DropdownItem = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
const { options = [], defaultLabel = "" } = defineProps<{ options?: DropdownItem[]; defaultLabel?: string }>();
|
||||
const { modelValue } = defineModels<{
|
||||
modelValue: string;
|
||||
}>();
|
||||
|
||||
const values = computed(() =>
|
||||
options.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr.value] = curr.label;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
);
|
||||
|
||||
const details = ref<HTMLElement | null>(null);
|
||||
const close = () => details.value?.removeAttribute("open");
|
||||
watch(modelValue, () => close());
|
||||
</script>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-end gap-4">
|
||||
<template v-if="config.pages">
|
||||
<router-link
|
||||
:to="{ name: 'content-id', params: { id: page.id } }"
|
||||
:title="page.title"
|
||||
v-for="page in config.pages"
|
||||
:key="page.id"
|
||||
class="link-primary"
|
||||
>
|
||||
{{ page.title }}
|
||||
</router-link>
|
||||
</template>
|
||||
<template v-if="config.user">
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="btn btn-circle btn-sm">
|
||||
<img class="h-10 w-10 max-w-none rounded-full p-1 ring-2 ring-base-content/50" :src="config.user.avatar" />
|
||||
</label>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="dropdown-content rounded-box z-50 mt-1 w-52 border border-base-content/20 bg-base p-2 shadow"
|
||||
>
|
||||
<div class="p-2">
|
||||
<div class="font-bold">
|
||||
{{ config.user.name }}
|
||||
</div>
|
||||
<div class="text-sm font-light">
|
||||
{{ config.user.email }}
|
||||
</div>
|
||||
</div>
|
||||
<ul class="menu mt-4 p-0">
|
||||
<li v-if="config.authProvider === 'simple'">
|
||||
<button @click.prevent="logout()" class="text-primary">{{ $t("button.logout") }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
async function logout() {
|
||||
await fetch(withBase("/api/token"), {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
@@ -22,7 +22,7 @@
|
||||
<transition name="fade">
|
||||
<div v-show="show">
|
||||
<div class="mt-4 flex items-center justify-center gap-2">
|
||||
<dropdown
|
||||
<dropdown-menu
|
||||
v-model="sessionHost"
|
||||
:options="hosts"
|
||||
defaultLabel="Hosts"
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { base, secured } = config;
|
||||
import { sessionHost } from "@/composables/storage";
|
||||
import { sessionHost } from "@/composable/storage";
|
||||
const store = useContainerStore();
|
||||
const route = useRoute();
|
||||
const { visibleContainers } = storeToRefs(store);
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span v-html="$t('settings.using-version', { version: currentVersion })"></span>
|
||||
<span v-html="$t('settings.using-version', { version: config.version })"></span>
|
||||
<div
|
||||
v-if="hasUpdate"
|
||||
v-html="$t('settings.update-available', { nextVersion: nextRelease.name, href: nextRelease.html_url })"
|
||||
v-html="$t('settings.update-available', { nextVersion: latest?.name, href: latest?.htmlUrl })"
|
||||
></div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
<dropdown-menu
|
||||
v-model="hourStyle"
|
||||
:options="[
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
@@ -45,7 +45,7 @@
|
||||
{{ $t("settings.12-24-format") }}
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
<dropdown-menu
|
||||
v-model="size"
|
||||
:options="[
|
||||
{ label: 'Small', value: 'small' },
|
||||
@@ -56,7 +56,7 @@
|
||||
{{ $t("settings.font-size") }}
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
<dropdown-menu
|
||||
v-model="lightTheme"
|
||||
:options="[
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
@@ -90,45 +90,22 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
search,
|
||||
lightTheme,
|
||||
smallerScrollbars,
|
||||
showTimestamp,
|
||||
showStd,
|
||||
hourStyle,
|
||||
showAllContainers,
|
||||
size,
|
||||
softWrap,
|
||||
automaticRedirect,
|
||||
hourStyle,
|
||||
lightTheme,
|
||||
search,
|
||||
showAllContainers,
|
||||
showStd,
|
||||
showTimestamp,
|
||||
size,
|
||||
smallerScrollbars,
|
||||
softWrap,
|
||||
} from "@/stores/settings";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
setTitle(t("title.settings"));
|
||||
|
||||
const currentVersion = config.version;
|
||||
let nextRelease = $ref({ html_url: "", name: "" });
|
||||
let hasUpdate = $ref(false);
|
||||
|
||||
async function fetchNextRelease() {
|
||||
if (!["dev", "master"].includes(currentVersion)) {
|
||||
const response = await fetch("https://api.github.com/repos/amir20/dozzle/releases/latest");
|
||||
if (response.ok) {
|
||||
const release = await response.json();
|
||||
hasUpdate =
|
||||
release.tag_name.slice(1).localeCompare(currentVersion, undefined, { numeric: true, sensitivity: "base" }) > 0;
|
||||
nextRelease = release;
|
||||
}
|
||||
} else {
|
||||
hasUpdate = true;
|
||||
nextRelease = {
|
||||
html_url: "",
|
||||
name: "master",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fetchNextRelease();
|
||||
const { latest, hasUpdate } = useReleases();
|
||||
</script>
|
||||
<style lang="postcss" scoped>
|
||||
.has-underline {
|
||||
|
||||
29
assets/stores/releases.ts
Normal file
29
assets/stores/releases.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
const { data: releases } = useFetch(withBase("/api/releases")).get().json<
|
||||
{
|
||||
name: string;
|
||||
mentionsCount: number;
|
||||
createdAt: string;
|
||||
body: string;
|
||||
tag: string;
|
||||
htmlUrl: string;
|
||||
latest: boolean;
|
||||
features: number;
|
||||
bugFixes: number;
|
||||
breaking: number;
|
||||
}[]
|
||||
>();
|
||||
|
||||
const hasUpdate = computed(() => {
|
||||
if (!releases.value?.length) return false;
|
||||
return releases.value[0].tag !== config.version;
|
||||
});
|
||||
|
||||
const latest = computed(() => releases.value?.find((release) => release.latest));
|
||||
|
||||
export function useReleases() {
|
||||
return {
|
||||
hasUpdate,
|
||||
latest,
|
||||
releases,
|
||||
};
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -34,7 +34,9 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.1 // indirect
|
||||
github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -42,11 +42,15 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
|
||||
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
|
||||
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
|
||||
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
|
||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760/go.mod h1:6+8gCKsZnxzhGTmKRh4BSkLos9CbWRJNcrp55We4SqQ=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -317,9 +321,11 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
|
||||
@@ -10,13 +10,13 @@ import (
|
||||
)
|
||||
|
||||
type simpleAuthContext struct {
|
||||
UserDatabase *UserDatabase
|
||||
UserDatabase UserDatabase
|
||||
tokenAuth *jwtauth.JWTAuth
|
||||
}
|
||||
|
||||
var ErrInvalidCredentials = errors.New("invalid credentials")
|
||||
|
||||
func NewSimpleAuth(userDatabase *UserDatabase) *simpleAuthContext {
|
||||
func NewSimpleAuth(userDatabase UserDatabase) *simpleAuthContext {
|
||||
h := sha256.New()
|
||||
for _, user := range userDatabase.Users {
|
||||
h.Write([]byte(user.Password))
|
||||
|
||||
@@ -20,12 +20,12 @@ type User struct {
|
||||
Password string `json:"-" yaml:"password"`
|
||||
}
|
||||
|
||||
func newUser(username, email, name string) *User {
|
||||
func newUser(username, email, name string) User {
|
||||
avatar := ""
|
||||
if email != "" {
|
||||
avatar = fmt.Sprintf("https://gravatar.com/avatar/%s?d=https%%3A%%2F%%2Fui-avatars.com%%2Fapi%%2F/%s/128", hashEmail(email), name)
|
||||
}
|
||||
return &User{
|
||||
return User{
|
||||
Username: username,
|
||||
Email: email,
|
||||
Name: name,
|
||||
@@ -37,23 +37,23 @@ type UserDatabase struct {
|
||||
Users map[string]*User `yaml:"users"`
|
||||
}
|
||||
|
||||
func ReadUsersFromFile(path string) (*UserDatabase, error) {
|
||||
func ReadUsersFromFile(path string) (UserDatabase, error) {
|
||||
users := UserDatabase{}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return &users, err
|
||||
return users, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := yaml.NewDecoder(file).Decode(&users); err != nil {
|
||||
return &users, err
|
||||
return users, err
|
||||
}
|
||||
|
||||
for username, user := range users.Users {
|
||||
user.Username = username
|
||||
}
|
||||
|
||||
return &users, nil
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (u *UserDatabase) Find(username string) *User {
|
||||
@@ -96,7 +96,8 @@ func UserFromContext(ctx context.Context) *User {
|
||||
}
|
||||
email := claims["email"].(string)
|
||||
name := claims["name"].(string)
|
||||
return newUser(username, email, name)
|
||||
user := newUser(username, email, name)
|
||||
return &user
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ type Page struct {
|
||||
Content string `json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func ReadAll() ([]*Page, error) {
|
||||
var pages []*Page
|
||||
func ReadAll() ([]Page, error) {
|
||||
var pages []Page
|
||||
files, err := filepath.Glob("data/content/*.md")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading /data/content/*.md: %w", err)
|
||||
@@ -38,10 +38,10 @@ func ReadAll() ([]*Page, error) {
|
||||
return pages, nil
|
||||
}
|
||||
|
||||
func Read(id string) (*Page, error) {
|
||||
func Read(id string) (Page, error) {
|
||||
data, err := os.ReadFile("data/content/" + id + ".md")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading /data/content/%s.md: %w", id, err)
|
||||
return Page{}, fmt.Errorf("error reading /data/content/%s.md: %w", id, err)
|
||||
}
|
||||
|
||||
markdown := goldmark.New(
|
||||
@@ -50,11 +50,11 @@ func Read(id string) (*Page, error) {
|
||||
context := parser.NewContext()
|
||||
var buf bytes.Buffer
|
||||
if err := markdown.Convert(data, &buf, parser.WithContext(context)); err != nil {
|
||||
return nil, fmt.Errorf("error converting markdown: %w", err)
|
||||
return Page{}, fmt.Errorf("error converting markdown: %w", err)
|
||||
}
|
||||
|
||||
metaData := meta.Get(context)
|
||||
page := &Page{
|
||||
page := Page{
|
||||
Content: buf.String(),
|
||||
Id: id,
|
||||
Title: id,
|
||||
|
||||
@@ -43,7 +43,7 @@ func init() {
|
||||
data_path = path
|
||||
}
|
||||
|
||||
func SaveUserSettings(user *auth.User, settings *Settings) error {
|
||||
func SaveUserSettings(user auth.User, settings Settings) error {
|
||||
path := filepath.Join(data_path, user.Username)
|
||||
|
||||
// Create user directory if it doesn't exist
|
||||
@@ -76,24 +76,24 @@ func SaveUserSettings(user *auth.User, settings *Settings) error {
|
||||
return f.Sync()
|
||||
}
|
||||
|
||||
func LoadUserSettings(user *auth.User) (*Settings, error) {
|
||||
func LoadUserSettings(user auth.User) (Settings, error) {
|
||||
path := filepath.Join(data_path, user.Username)
|
||||
settings_path := filepath.Join(path, "settings.json")
|
||||
|
||||
if _, err := os.Stat(settings_path); os.IsNotExist(err) {
|
||||
return &Settings{}, errors.New("Settings file does not exist")
|
||||
return Settings{}, errors.New("Settings file does not exist")
|
||||
}
|
||||
|
||||
f, err := os.Open(settings_path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Settings{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var settings Settings
|
||||
if err := json.NewDecoder(f).Decode(&settings); err != nil {
|
||||
return nil, err
|
||||
return Settings{}, err
|
||||
}
|
||||
|
||||
return &settings, nil
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
90
internal/releases/github.go
Normal file
90
internal/releases/github.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package releases
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/yuin/goldmark"
|
||||
)
|
||||
|
||||
type githubRelease struct {
|
||||
Name string `json:"name"`
|
||||
MentionsCount int `json:"mentions_count"`
|
||||
TagName string `json:"tag_name"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
HtmlUrl string `json:"html_url"`
|
||||
}
|
||||
|
||||
type Release struct {
|
||||
Name string `json:"name"`
|
||||
MentionsCount int `json:"mentionsCount"`
|
||||
Tag string `json:"tag"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
HtmlUrl string `json:"htmlUrl"`
|
||||
Latest bool `json:"latest"`
|
||||
Features int `json:"features"`
|
||||
BugFixes int `json:"bugFixes"`
|
||||
Breaking int `json:"breaking"`
|
||||
}
|
||||
|
||||
func Fetch(currentVersion string) ([]Release, error) {
|
||||
response, err := http.Get("https://api.github.com/repos/amir20/dozzle/releases?per_page=9")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
var githubReleases []githubRelease
|
||||
if err := json.NewDecoder(response.Body).Decode(&githubReleases); err != nil {
|
||||
return []Release{}, err
|
||||
}
|
||||
|
||||
var releases []Release
|
||||
for _, githubRelease := range githubReleases {
|
||||
var buffer bytes.Buffer
|
||||
goldmark.Convert([]byte(githubRelease.Body), &buffer)
|
||||
html := buffer.String()
|
||||
|
||||
if githubRelease.TagName == currentVersion {
|
||||
break
|
||||
}
|
||||
|
||||
release := Release{
|
||||
Name: githubRelease.Name,
|
||||
MentionsCount: githubRelease.MentionsCount,
|
||||
Tag: githubRelease.TagName,
|
||||
Body: html,
|
||||
CreatedAt: githubRelease.CreatedAt,
|
||||
HtmlUrl: githubRelease.HtmlUrl,
|
||||
}
|
||||
|
||||
doc, _ := goquery.NewDocumentFromReader(&buffer)
|
||||
doc.Find("h3").Each(func(i int, s *goquery.Selection) {
|
||||
if strings.Contains(s.Text(), "Features") {
|
||||
release.Features = s.Next().Find("li").Length()
|
||||
}
|
||||
|
||||
if strings.Contains(s.Text(), "Bug Fixes") {
|
||||
release.BugFixes = s.Next().Find("li").Length()
|
||||
}
|
||||
|
||||
if strings.Contains(s.Text(), "Breaking Changes") {
|
||||
release.Breaking = s.Next().Find("li").Length()
|
||||
}
|
||||
})
|
||||
|
||||
releases = append(releases, release)
|
||||
}
|
||||
|
||||
if len(releases) > 0 {
|
||||
releases[0].Latest = true
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
user := auth.UserFromContext(req.Context())
|
||||
if user != nil {
|
||||
if settings, err := profile.LoadUserSettings(user); err == nil {
|
||||
if settings, err := profile.LoadUserSettings(*user); err == nil {
|
||||
config["serverSettings"] = settings
|
||||
} else {
|
||||
config["serverSettings"] = struct{}{}
|
||||
|
||||
@@ -22,7 +22,7 @@ func (h *handler) saveSettings(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := profile.SaveUserSettings(user, &settings); err != nil {
|
||||
if err := profile.SaveUserSettings(*user, settings); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Errorf("Unable to save user settings: %s", err)
|
||||
return
|
||||
|
||||
24
internal/web/releases.go
Normal file
24
internal/web/releases.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/amir20/dozzle/internal/releases"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (h *handler) releases(w http.ResponseWriter, r *http.Request) {
|
||||
releases, err := releases.Fetch(h.config.Version)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Warnf("error reading releases: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(releases); err != nil {
|
||||
log.Errorf("json encoding error while streaming %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,7 @@ func createRouter(h *handler) *chi.Mux {
|
||||
r.Get("/api/logs/download/{host}/{id}", h.downloadLogs)
|
||||
r.Get("/api/logs/{host}/{id}", h.fetchLogsBetweenDates)
|
||||
r.Get("/api/events/stream", h.streamEvents)
|
||||
r.Get("/api/releases", h.releases)
|
||||
r.Put("/api/profile/settings", h.saveSettings)
|
||||
r.Get("/api/content/{id}", h.staticContent)
|
||||
r.Get("/logout", h.clearSession) // TODO remove this
|
||||
|
||||
@@ -80,3 +80,11 @@ settings:
|
||||
rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Show stdout and stderr labels
|
||||
automatic-redirect: Automatically redirect to new containers with the same name
|
||||
releases:
|
||||
features: one new feature | {count} features
|
||||
bugFixes: one bug fix | {count} fixes
|
||||
breaking: one breaking change | {count} breaking changes
|
||||
three_parts: "{first}, {second} and {third}"
|
||||
two_parts: "{first} with {second}"
|
||||
latest: Latest
|
||||
no_releases: You have the latest version
|
||||
|
||||
@@ -54,7 +54,7 @@ export default defineConfig(() => ({
|
||||
AutoImport({
|
||||
imports: ["vue", "vue-router", "vue-i18n", "vue/macros", "pinia", "@vueuse/head", "@vueuse/core"],
|
||||
dts: "assets/auto-imports.d.ts",
|
||||
dirs: ["assets/composables", "assets/stores", "assets/utils"],
|
||||
dirs: ["assets/composable", "assets/stores", "assets/utils"],
|
||||
vueTemplate: true,
|
||||
}),
|
||||
VueI18nPlugin({
|
||||
|
||||
Reference in New Issue
Block a user