This commit is contained in:
		
							
								
								
									
										18
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | .kpt-pipeline/ | ||||||
|  | k8s/ | ||||||
|  | skaffold.yaml | ||||||
|  | README.md | ||||||
|  | .git/ | ||||||
|  | node_modules/ | ||||||
|  | .drone.yml | ||||||
|  |  | ||||||
|  | # Editor directories and files | ||||||
|  | .vscode/* | ||||||
|  | !.vscode/extensions.json | ||||||
|  | .idea | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.sw? | ||||||
|  | *.kpt-pipeline | ||||||
							
								
								
									
										2
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | kind: template | ||||||
|  | load: docker.yaml | ||||||
							
								
								
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | # Logs | ||||||
|  | logs | ||||||
|  | *.log | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  | pnpm-debug.log* | ||||||
|  | lerna-debug.log* | ||||||
|  |  | ||||||
|  | node_modules | ||||||
|  | .DS_Store | ||||||
|  | dist | ||||||
|  | dist-ssr | ||||||
|  | coverage | ||||||
|  | *.local | ||||||
|  |  | ||||||
|  | /cypress/videos/ | ||||||
|  | /cypress/screenshots/ | ||||||
|  |  | ||||||
|  | # Editor directories and files | ||||||
|  | .vscode/* | ||||||
|  | !.vscode/extensions.json | ||||||
|  | .idea | ||||||
|  | *.suo | ||||||
|  | *.ntvs* | ||||||
|  | *.njsproj | ||||||
|  | *.sln | ||||||
|  | *.sw? | ||||||
|  | *.kpt-pipeline | ||||||
							
								
								
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | FROM node AS dev | ||||||
|  |  | ||||||
|  | WORKDIR /app | ||||||
|  | EXPOSE 8080 | ||||||
|  |  | ||||||
|  | COPY package* ./ | ||||||
|  | RUN npm install | ||||||
|  | COPY . . | ||||||
|  |  | ||||||
|  | ENTRYPOINT ["npm", "run", "dev"] | ||||||
|  |  | ||||||
|  | # builder | ||||||
|  | FROM dev AS builder | ||||||
|  | RUN npm run build | ||||||
|  |  | ||||||
|  | # serve | ||||||
|  | FROM nginx AS prod | ||||||
|  | WORKDIR /usr/share/nginx/html | ||||||
|  | RUN rm -rf ./* | ||||||
|  | COPY ./nginx.conf /etc/nginx/conf.d/default.conf | ||||||
|  | COPY --from=builder /app/dist . | ||||||
|  | ENTRYPOINT ["nginx", "-g", "daemon off;"] | ||||||
							
								
								
									
										13
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8" /> | ||||||
|  |     <link rel="icon" href="/favicon.ico" /> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||||
|  |     <title>Log Viewer</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div id="app"></div> | ||||||
|  |     <script type="module" src="/src/main.js"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										58
									
								
								k8s/dev/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								k8s/dev/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | apiVersion: networking.k8s.io/v1 | ||||||
|  | kind: Ingress | ||||||
|  | metadata: | ||||||
|  |   name: playground | ||||||
|  |   annotations: | ||||||
|  |     kubernetes.io/ingress.class: traefik | ||||||
|  |     cert-manager.io/cluster-issuer: default | ||||||
|  |     traefik.ingress.kubernetes.io/router.entrypoints: websecure | ||||||
|  |     traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd | ||||||
|  |     traefik.ingress.kubernetes.io/router.tls: "true" | ||||||
|  |     external-dns.alpha.kubernetes.io/target: traefik.k-space.ee | ||||||
|  | spec: | ||||||
|  |   rules: | ||||||
|  |   - host: playground.k-space.ee | ||||||
|  |     http: | ||||||
|  |       paths: | ||||||
|  |       - pathType: Prefix | ||||||
|  |         path: "/" | ||||||
|  |         backend: | ||||||
|  |           service: | ||||||
|  |             name: log-viewer-frontend | ||||||
|  |             port: | ||||||
|  |               number: 8080 | ||||||
|  |   tls: | ||||||
|  |   - hosts: | ||||||
|  |     - playground.k-space.ee | ||||||
|  |     secretName: playground-tls | ||||||
|  | --- | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: Service | ||||||
|  | metadata: | ||||||
|  |   name: log-viewer-frontend | ||||||
|  | spec: | ||||||
|  |   type: ClusterIP | ||||||
|  |   selector: | ||||||
|  |     app: log-viewer-frontend | ||||||
|  |   ports: | ||||||
|  |     - protocol: TCP | ||||||
|  |       port: 8080 | ||||||
|  | --- | ||||||
|  | apiVersion: apps/v1 | ||||||
|  | kind: Deployment | ||||||
|  | metadata: | ||||||
|  |   name: log-viewer-frontend | ||||||
|  | spec: | ||||||
|  |   selector: | ||||||
|  |     matchLabels: | ||||||
|  |       app: log-viewer-frontend | ||||||
|  |   template: | ||||||
|  |     metadata: | ||||||
|  |       labels: | ||||||
|  |         app: log-viewer-frontend | ||||||
|  |     spec: | ||||||
|  |       containers: | ||||||
|  |       - name: log-viewer-frontend | ||||||
|  |         image: harbor.k-space.ee/playground/log-viewer-frontend | ||||||
|  |         ports: | ||||||
|  |         - containerPort: 8080 | ||||||
							
								
								
									
										9
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | server { | ||||||
|  |     listen       8080; | ||||||
|  |     server_name  _; | ||||||
|  |  | ||||||
|  |     location / { | ||||||
|  |         root   /usr/share/nginx/html; | ||||||
|  |         index  index.html index.htm; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										13612
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13612
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | { | ||||||
|  |   "name": "@k-space/log-viewer", | ||||||
|  |   "version": "0.0.0", | ||||||
|  |   "scripts": { | ||||||
|  |     "serve": "vue-cli-service serve", | ||||||
|  |     "build": "vite build", | ||||||
|  |     "dev": "vite --port 8080 --host", | ||||||
|  |     "preview": "vite preview --port 3003" | ||||||
|  |   }, | ||||||
|  |   "dependencies": { | ||||||
|  |     "@ag-grid-community/core": "^28.2.0", | ||||||
|  |     "@mdi/font": "5.9.55", | ||||||
|  |     "@vue/cli-service": "^5.0.8", | ||||||
|  |     "ag-grid-vue3": "^28.2.0", | ||||||
|  |     "core-js": "^3.25.1", | ||||||
|  |     "event-hooks-webpack-plugin": "^2.2.0", | ||||||
|  |     "pinia": "^2.0.21", | ||||||
|  |     "roboto-fontface": "*", | ||||||
|  |     "single-spa-vue": "^2.5.1", | ||||||
|  |     "systemjs-webpack-interop": "^2.3.7", | ||||||
|  |     "vue": "^3.2.39", | ||||||
|  |     "vuetify": "^3.0.0-beta.0", | ||||||
|  |     "webfontloader": "^1.0.0" | ||||||
|  |   }, | ||||||
|  |   "devDependencies": { | ||||||
|  |     "@vitejs/plugin-vue": "^3.0.3", | ||||||
|  |     "vite": "^3.0.9", | ||||||
|  |     "vue-cli-plugin-vuetify": "~2.5.8", | ||||||
|  |     "webpack-plugin-vuetify": "^2.0.0-alpha.0" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										0
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										42
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | apiVersion: skaffold/v3alpha1 | ||||||
|  | kind: Config | ||||||
|  | metadata: | ||||||
|  |   name: log-viewer-backend | ||||||
|  |  | ||||||
|  | build: | ||||||
|  |   artifacts: | ||||||
|  |   - image: harbor.k-space.ee/playground/log-viewer-frontend | ||||||
|  |     docker: | ||||||
|  |       dockerfile: Dockerfile | ||||||
|  |  | ||||||
|  | deploy: | ||||||
|  |   kubectl: {} | ||||||
|  |  | ||||||
|  | manifests: | ||||||
|  |   rawYaml: | ||||||
|  |      - k8s/staging/deployment.yaml | ||||||
|  |  | ||||||
|  | profiles: | ||||||
|  |   - name: dev | ||||||
|  |     activation: | ||||||
|  |       - command: dev | ||||||
|  |     build: | ||||||
|  |       artifacts: | ||||||
|  |         - image: harbor.k-space.ee/playground/log-viewer-frontend | ||||||
|  |           docker: | ||||||
|  |             target: dev | ||||||
|  |           sync: | ||||||
|  |             manual: | ||||||
|  |               - src: 'src/**/*.vue' | ||||||
|  |                 dest: . | ||||||
|  |               - src: 'src/**/*.js' | ||||||
|  |                 dest: . | ||||||
|  |               - src: 'src/**/*.css' | ||||||
|  |                 dest: . | ||||||
|  |               - src: 'src/**/*.svg' | ||||||
|  |                 dest: . | ||||||
|  |               - src: 'index.html' | ||||||
|  |                 dest: . | ||||||
|  |     manifests: | ||||||
|  |       rawYaml: | ||||||
|  |         - k8s/dev/deployment.yaml | ||||||
							
								
								
									
										15
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | <script setup> | ||||||
|  | import LogViewer from './components/LogViewer.vue' | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <template> | ||||||
|  |     <LogViewer /> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'app1', | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <style scoped> | ||||||
|  | </style> | ||||||
							
								
								
									
										1
									
								
								src/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"  xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg> | ||||||
| After Width: | Height: | Size: 308 B | 
							
								
								
									
										15
									
								
								src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  |  | ||||||
|  | div#app { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100vh; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .screenshots-drawer { | ||||||
|  |   position: fixed; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .ag-theme-material { | ||||||
|  |   --ag-value-change-value-highlight-background-color: #f9ff99; | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								src/components/ComboboxFilter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/components/ComboboxFilter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | export default { | ||||||
|  |     template: ` | ||||||
|  |       <select v-model="filter" class="v-select"> | ||||||
|  |         <option value=""> </option> | ||||||
|  |         <option v-for="option in params.options" :value="option"> | ||||||
|  |            {{ option }} | ||||||
|  |         </option> | ||||||
|  |       </select> | ||||||
|  |     `, | ||||||
|  |     data: function () { | ||||||
|  |         return { | ||||||
|  |             filter: '', | ||||||
|  |         }; | ||||||
|  |     }, | ||||||
|  |     methods: { | ||||||
|  |         updateFilter() { | ||||||
|  |             this.params.filterChangedCallback(); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         doesFilterPass(params) { | ||||||
|  |             const value = this.params.field.split('.').reduce((a, b) => a[b], params.data); | ||||||
|  |             return value === this.filter; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         isFilterActive() { | ||||||
|  |             return this.filter !== '' | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
							
								
								
									
										83
									
								
								src/components/ExamineLogModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/components/ExamineLogModal.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | <template> | ||||||
|  |   <v-dialog | ||||||
|  |       v-model="examineLog" | ||||||
|  |       width="50wv" | ||||||
|  |   > | ||||||
|  |     <v-card> | ||||||
|  |       <v-card-text style="height: 70vh"> | ||||||
|  |         <ag-grid-vue | ||||||
|  |             style="width: 100%; height: 100%;" | ||||||
|  |             class="ag-theme-material" | ||||||
|  |             @grid-ready="onGridReady" | ||||||
|  |             :columnDefs="columnDefs" | ||||||
|  |             :row-data="examineLogContent" | ||||||
|  |             :supress-horisontal-scroll="true" | ||||||
|  |             :enable-scrolling="true" | ||||||
|  |             :enableCellTextSelection="true" | ||||||
|  |             :ensureDomOrder="true" | ||||||
|  |         ></ag-grid-vue> | ||||||
|  |       </v-card-text> | ||||||
|  |       <v-card-actions> | ||||||
|  |         <v-btn color="primary" block @click="closeModal">Close</v-btn> | ||||||
|  |       </v-card-actions> | ||||||
|  |     </v-card> | ||||||
|  |   </v-dialog> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import { AgGridVue } from "ag-grid-vue3"; | ||||||
|  | import "ag-grid-community/styles//ag-grid.css"; | ||||||
|  | import "ag-grid-community/styles//ag-theme-material.css"; | ||||||
|  | import ScreenshotCell from "./ScreenshotCell.js"; | ||||||
|  | import { VCard, VCardText, VCardActions } from 'vuetify/components/VCard' | ||||||
|  | import { VDialog } from 'vuetify/components/VDialog' | ||||||
|  | import { VBtn } from 'vuetify/components/VBtn' | ||||||
|  | import { VTable } from 'vuetify/components/VTable' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   components: { | ||||||
|  |     AgGridVue, | ||||||
|  |     VCard, | ||||||
|  |     VCardText, | ||||||
|  |     VCardActions, | ||||||
|  |     VBtn, | ||||||
|  |     VDialog, | ||||||
|  |     VTable, | ||||||
|  |     ScreenshotCell: ScreenshotCell | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       columnDefs:  [ | ||||||
|  |         { | ||||||
|  |           field: 'key', | ||||||
|  |           sortable: true, | ||||||
|  |           filter: 'agTextColumnFilter', | ||||||
|  |           resizable: true | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'value', | ||||||
|  |           sortable: true, | ||||||
|  |           filter: 'agTextColumnFilter', | ||||||
|  |           resizable: true | ||||||
|  |         }, | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   props: { | ||||||
|  |     examineLogContent: Array, | ||||||
|  |     closeModal: Function | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     examineLog() { | ||||||
|  |       return !!this.examineLogContent | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     onGridReady(params) { | ||||||
|  |       params.api.sizeColumnsToFit() | ||||||
|  |     }, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | </script> | ||||||
							
								
								
									
										173
									
								
								src/components/LogViewer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/components/LogViewer.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | |||||||
|  | <template> | ||||||
|  |   <div style="height: 100%; width: 100%"> | ||||||
|  |   <ag-grid-vue | ||||||
|  |       style="width: 100%; height: 100%;" | ||||||
|  |       class="ag-theme-material" | ||||||
|  |       @grid-ready="onGridReady" | ||||||
|  |       :defaultColDef="defaultColDef" | ||||||
|  |       :columnDefs="columnDefs" | ||||||
|  |       :pagination="true" | ||||||
|  |       :paginationAutoPageSize=true | ||||||
|  |       :row-data="null" | ||||||
|  |       row-selection="single" | ||||||
|  |       :onRowSelected="openExamineLog" | ||||||
|  |       :supress-horisontal-scroll="true" | ||||||
|  |       :enable-scrolling="true" | ||||||
|  |     ></ag-grid-vue> | ||||||
|  |     <ExamineLogModal :examine-log-content="examineLogContent" :close-modal="closeExamineLog" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import { AgGridVue } from "ag-grid-vue3"; | ||||||
|  | import "ag-grid-community/styles//ag-grid.css"; | ||||||
|  | import "ag-grid-community/styles//ag-theme-material.css"; | ||||||
|  | import ScreenshotCell from "./ScreenshotCell.js"; | ||||||
|  | import ExamineLogModal from "./ExamineLogModal.vue"; | ||||||
|  | import ComboboxFilter from "./ComboboxFilter.js"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   components: { | ||||||
|  |     ExamineLogModal, | ||||||
|  |     AgGridVue, | ||||||
|  |     ComboboxFilter, | ||||||
|  |     ScreenshotCell: ScreenshotCell | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       examineLogContent: null, | ||||||
|  |       gridApi: null, | ||||||
|  |       gridColumnApi: null, | ||||||
|  |       defaultColDef: { | ||||||
|  |         width: 50, | ||||||
|  |         initialPinned: true, | ||||||
|  |         resizable: true, | ||||||
|  |         enableCellChangeFlash: true | ||||||
|  |       }, | ||||||
|  |       currentRowCount: 0, | ||||||
|  |       comboBoxOptions: {}, | ||||||
|  |       viewRowCount: 20, | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     columnDefs() { | ||||||
|  |       return [ | ||||||
|  |         { | ||||||
|  |           field: '@timestamp', | ||||||
|  |           width: 70, | ||||||
|  |           sortable: true | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'kubernetes.namespace', | ||||||
|  |           headerName: 'namespace', | ||||||
|  |           tooltipValueGetter: (params) => params.value, | ||||||
|  |           filter: ComboboxFilter, | ||||||
|  |           filterParams: { | ||||||
|  |             options: this.comboBoxOptions['kubernetes.namespace'], | ||||||
|  |             field: 'kubernetes.namespace', | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'kubernetes.pod.name', | ||||||
|  |           headerName: 'pod', | ||||||
|  |           tooltipValueGetter: (params) => params.value, | ||||||
|  |           filter: ComboboxFilter, | ||||||
|  |           filterParams: { | ||||||
|  |             options: this.comboBoxOptions['kubernetes.pod.name'], | ||||||
|  |             field: 'kubernetes.pod.name', | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'kubernetes.container.name', | ||||||
|  |           headerName: 'container', | ||||||
|  |           tooltipValueGetter: (params) => params.value, | ||||||
|  |           filter: ComboboxFilter, | ||||||
|  |           filterParams: { | ||||||
|  |             options: this.comboBoxOptions['kubernetes.container.name'], | ||||||
|  |             field: 'kubernetes.container.name', | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'message', | ||||||
|  |           tooltipValueGetter: (params) => params.value, | ||||||
|  |           width: 500, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           field: 'stream', | ||||||
|  |         }, | ||||||
|  |       ]; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created() { | ||||||
|  |     this.setupStream() | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     setupStream() { | ||||||
|  |       let es = new EventSource('/events'); | ||||||
|  |       es.onmessage = (e) => this.handleReceiveMessage(e) | ||||||
|  |       es.addEventListener("filters", (e) => this.handleReceiveFilters(e)) | ||||||
|  |     }, | ||||||
|  |     onGridReady(params) { | ||||||
|  |       this.gridApi = params.api; | ||||||
|  |       this.gridColumnApi = params.columnApi; | ||||||
|  |     }, | ||||||
|  |     handleReceiveMessage (event) { | ||||||
|  |       const eventData = this.parseEventData(event.data); | ||||||
|  |       const res = this.gridApi.applyTransaction({ | ||||||
|  |         add: [eventData] | ||||||
|  |       }); | ||||||
|  |       const rowNode = res.add[0] | ||||||
|  |       this.gridApi.flashCells({ rowNodes: [rowNode]}); | ||||||
|  |       this.gridApi.sizeColumnsToFit() | ||||||
|  |     }, | ||||||
|  |     handleReceiveFilters (event) { | ||||||
|  |       this.comboBoxOptions = this.parseEventData(event.data); | ||||||
|  |     }, | ||||||
|  |     parseEventData (eventData) { | ||||||
|  |       try { | ||||||
|  |         let json = JSON.parse(eventData) | ||||||
|  |         if (!json.message) { | ||||||
|  |           json.message = JSON.stringify(json.json) | ||||||
|  |         } | ||||||
|  |         return json | ||||||
|  |       } catch (e) { | ||||||
|  |         console.error(e, eventData) | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     openExamineLog (row) { | ||||||
|  |       const selectedRow = row.data | ||||||
|  |       row.node.setSelected(false) | ||||||
|  |       this.examineLog = true | ||||||
|  |       const flattened = flattenObj(selectedRow) | ||||||
|  |       const pairs = []; | ||||||
|  |       Object.keys(flattened).map((key) => { | ||||||
|  |         pairs.push({ | ||||||
|  |           key: key, | ||||||
|  |           value: flattened[key] | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |       this.examineLogContent = pairs | ||||||
|  |     }, | ||||||
|  |     closeExamineLog () { | ||||||
|  |       this.examineLogContent = null | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const flattenObj = (ob) => { | ||||||
|  |   let result = {}; | ||||||
|  |   for (const i in ob) { | ||||||
|  |     if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) { | ||||||
|  |       const temp = flattenObj(ob[i]); | ||||||
|  |       for (const j in temp) { | ||||||
|  |         result[i + '.' + j] = temp[j]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       result[i] = ob[i]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return result; | ||||||
|  | }; | ||||||
|  | </script> | ||||||
							
								
								
									
										30
									
								
								src/components/ScreenshotCell.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/components/ScreenshotCell.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | export default { | ||||||
|  |     template: `<div> | ||||||
|  |       <a @click="openDrawer">View screenshots</a> | ||||||
|  |       <div v-if="drawerOpen" class="screenshots-drawer"> | ||||||
|  |         <img v-for="screenshot in screenshots" :src="screenshot.orig"/> | ||||||
|  |       </div> | ||||||
|  |     </div>`, | ||||||
|  |     data: function () { | ||||||
|  |         return { | ||||||
|  |             screenshots: [], | ||||||
|  |             drawerOpen: false, | ||||||
|  |         }; | ||||||
|  |     }, | ||||||
|  |     beforeMount() { | ||||||
|  |         this.updateImage(this.params); | ||||||
|  |         this.updateImage(this.params); | ||||||
|  |     }, | ||||||
|  |     methods: { | ||||||
|  |         updateImage(params) { | ||||||
|  |             this.screenshots = params.value | ||||||
|  |             this.value = params.value; | ||||||
|  |         }, | ||||||
|  |         refresh(params) { | ||||||
|  |             this.updateImage(params); | ||||||
|  |         }, | ||||||
|  |         openDrawer () { | ||||||
|  |             this.drawerOpen = true | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  | }; | ||||||
							
								
								
									
										10
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import { createApp } from 'vue' | ||||||
|  | import App from './App.vue' | ||||||
|  | import vuetify from './plugins/vuetify' | ||||||
|  | import { loadFonts } from './plugins/webfontloader' | ||||||
|  | import './assets/main.css' | ||||||
|  | loadFonts() | ||||||
|  |  | ||||||
|  | createApp(App) | ||||||
|  |   .use(vuetify) | ||||||
|  |   .mount('#app') | ||||||
							
								
								
									
										10
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | // Styles | ||||||
|  | import '@mdi/font/css/materialdesignicons.css' | ||||||
|  | import 'vuetify/styles' | ||||||
|  |  | ||||||
|  | // Vuetify | ||||||
|  | import { createVuetify } from 'vuetify' | ||||||
|  |  | ||||||
|  | export default createVuetify( | ||||||
|  |   // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides | ||||||
|  | ) | ||||||
							
								
								
									
										15
									
								
								src/plugins/webfontloader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/plugins/webfontloader.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | /** | ||||||
|  |  * plugins/webfontloader.js | ||||||
|  |  * | ||||||
|  |  * webfontloader documentation: https://github.com/typekit/webfontloader | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | export async function loadFonts () { | ||||||
|  |   const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') | ||||||
|  |  | ||||||
|  |   webFontLoader.load({ | ||||||
|  |     google: { | ||||||
|  |       families: ['Roboto:100,300,400,500,700,900&display=swap'], | ||||||
|  |     }, | ||||||
|  |   }) | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								src/set-public-path.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/set-public-path.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | import { setPublicPath } from "systemjs-webpack-interop"; | ||||||
|  |  | ||||||
|  | setPublicPath("app1"); | ||||||
							
								
								
									
										14
									
								
								src/stores/counter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/stores/counter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import { ref, computed } from 'vue' | ||||||
|  | import { defineStore } from 'pinia' | ||||||
|  |  | ||||||
|  | export const useCounterStore = defineStore('counter', () => { | ||||||
|  |   const count = ref(0) | ||||||
|  |   const doubleCount = computed(() => count.value * 2) | ||||||
|  |   function increment() { | ||||||
|  |     count.value++ | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   return { count, doubleCount, increment } | ||||||
|  | }) | ||||||
							
								
								
									
										26
									
								
								vite.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vite.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | import vue from '@vitejs/plugin-vue' | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   resolve: { | ||||||
|  |     alias: { | ||||||
|  |       vue: 'vue/dist/vue.esm-bundler.js' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   rollupOptions: { | ||||||
|  |     input: 'src/main.js', | ||||||
|  |     format: 'system', | ||||||
|  |     preserveEntrySignatures: true | ||||||
|  |   }, | ||||||
|  |   plugins: [vue({ | ||||||
|  |     template: { | ||||||
|  |       transformAssetUrls: { | ||||||
|  |         base: '/src' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   })], | ||||||
|  |   pluginOptions: { | ||||||
|  |     vuetify: { | ||||||
|  |       // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  |  | ||||||
|  | const path = require('path'); | ||||||
|  | const fs = require('fs'); | ||||||
|  | const EventHooksPlugin = require('event-hooks-webpack-plugin'); | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |     publicPath: '/logs', | ||||||
|  |     chainWebpack: (config) => { | ||||||
|  |         config.devServer.headers({ | ||||||
|  |             'Access-Control-Allow-Origin': '*', | ||||||
|  |         }); | ||||||
|  |         config.devServer.set('port', 8080); | ||||||
|  |         config.devServer.set('hot', true); | ||||||
|  |  | ||||||
|  |         config.output.filename('[name].js'); | ||||||
|  |         config.output.publicPath('/logs'); | ||||||
|  |  | ||||||
|  |         config.externals([ | ||||||
|  |             'vue', | ||||||
|  |             'vue-router' | ||||||
|  |         ]); | ||||||
|  |     }, | ||||||
|  |     lintOnSave: true, | ||||||
|  |     filenameHashing: false, | ||||||
|  |     configureWebpack: { | ||||||
|  |         plugins: [ | ||||||
|  |             new EventHooksPlugin({ | ||||||
|  |                 done: () => { | ||||||
|  |                     if (process.env.NODE_ENV !== 'development') { | ||||||
|  |                         const buildDir = path.join(__dirname, '/dist'); | ||||||
|  |                         fs.unlinkSync(`${buildDir}/index.html`); | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |             }), | ||||||
|  |         ], | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     pluginOptions: { | ||||||
|  |       vuetify: { | ||||||
|  | 			// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader | ||||||
|  | 		} | ||||||
|  |     } | ||||||
|  | }; | ||||||
		Reference in New Issue
	
	Block a user