mirror of https://github.com/alibaba/arthas.git
				
				
				
			chore(web ui):ref tunnel modules (#2331)
This commit is contained in:
		
							parent
							
								
									aaeb2d2fd4
								
							
						
					
					
						commit
						b252ca2b1b
					
				| 
						 | 
				
			
			@ -1,2 +1,3 @@
 | 
			
		|||
# NODE_ENV=production
 | 
			
		||||
VITE_AGENT=true
 | 
			
		||||
VITE_ARTHAS_PORT=7777
 | 
			
		||||
VITE_ARTHAS_PROXY_PORT=8080
 | 
			
		||||
| 
						 | 
				
			
			@ -1,2 +1,3 @@
 | 
			
		|||
# NODE_ENV=production
 | 
			
		||||
VITE_AGENT=false
 | 
			
		||||
VITE_ARTHAS_PORT=8563
 | 
			
		||||
VITE_ARTHAS_PROXY_PORT=8563
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ declare module '*.vue' {
 | 
			
		|||
  export default component
 | 
			
		||||
}
 | 
			
		||||
interface ImportMetaEnv {
 | 
			
		||||
  readonly VITE_AGENT:string
 | 
			
		||||
  readonly VITE_ARTHAS_PORT:string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface ImportMeta {
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 542 B After Width: | Height: | Size: 542 B  | 
| 
						 | 
				
			
			@ -1,21 +1,31 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { onMounted, ref } from "vue";
 | 
			
		||||
import { onMounted, ref, computed  } from "vue";
 | 
			
		||||
import { Terminal } from "xterm"
 | 
			
		||||
import { FitAddon } from 'xterm-addon-fit';
 | 
			
		||||
import { WebglAddon } from "xterm-addon-webgl"
 | 
			
		||||
import { MenuAlt2Icon } from "@heroicons/vue/outline"
 | 
			
		||||
import fullPic from "~/assert/fullsc.png"
 | 
			
		||||
import arthasLogo from "~/assert/arthas.png"
 | 
			
		||||
const { isTunnel = false } = defineProps<{
 | 
			
		||||
  isTunnel?: boolean
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
let ws: WebSocket | undefined;
 | 
			
		||||
 | 
			
		||||
let intervalReadKey = -1
 | 
			
		||||
const DEFAULT_SCROLL_BACK = 1000
 | 
			
		||||
const MAX_SCROLL_BACK = 9999999
 | 
			
		||||
const MIN_SCROLL_BACK = 1
 | 
			
		||||
const ARTHAS_PORT = '8563'
 | 
			
		||||
const ARTHAS_PORT = isTunnel ? "7777" : "8563"
 | 
			
		||||
const ip = ref("")
 | 
			
		||||
const port = ref('')
 | 
			
		||||
const iframe = ref(true)
 | 
			
		||||
const fullSc = ref(true)
 | 
			
		||||
const agentID = ref('')
 | 
			
		||||
const isTunnel = import.meta.env.VITE_AGENT === 'true'
 | 
			
		||||
const outputHerf = computed(() => {
 | 
			
		||||
  console.log(agentID.value)
 | 
			
		||||
  return isTunnel?`proxy/${agentID.value}/arthas-output/`:`/arthas-output/`
 | 
			
		||||
})
 | 
			
		||||
// const isTunnel = import.meta.env.MODE === 'tunnel'
 | 
			
		||||
const fitAddon = new FitAddon();
 | 
			
		||||
const webglAddon = new WebglAddon();
 | 
			
		||||
let xterm = new Terminal({ allowProposedApi: true })
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +33,7 @@ let xterm = new Terminal({ allowProposedApi: true })
 | 
			
		|||
onMounted(() => {
 | 
			
		||||
  ip.value = getUrlParam('ip') ?? window.location.hostname;
 | 
			
		||||
  port.value = getUrlParam('port') ?? ARTHAS_PORT;
 | 
			
		||||
  if (isTunnel) agentID.value = getUrlParam("agentId") ?? ""
 | 
			
		||||
  let _iframe = getUrlParam('iframe')
 | 
			
		||||
  if (_iframe && _iframe.trim() !== 'false') iframe.value = false
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,12 +53,22 @@ function getUrlParam(name: string) {
 | 
			
		|||
  return urlparam.get(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function getWsUri() {
 | 
			
		||||
  const host = `${ip.value}:${port.value}`
 | 
			
		||||
  if (!isTunnel) return `ws://${host}/ws`;
 | 
			
		||||
  const path = getUrlParam("path") ?? 'ws'
 | 
			
		||||
  const _targetServer = getUrlParam("targetServer")
 | 
			
		||||
  let protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
 | 
			
		||||
  const uri = `${protocol}${host}/${encodeURIComponent(path)}?method=connectArthas&id=${agentID.value}`
 | 
			
		||||
  if (_targetServer != null) {
 | 
			
		||||
    return uri + '&targetServer=' + encodeURIComponent(_targetServer);
 | 
			
		||||
  }
 | 
			
		||||
  return uri
 | 
			
		||||
}
 | 
			
		||||
/** init websocket **/
 | 
			
		||||
function initWs(silent: boolean) {
 | 
			
		||||
  let path = 'ws://' + ip.value + ':' + port.value + '/ws';
 | 
			
		||||
  console.log(path)
 | 
			
		||||
  ws = new WebSocket(path);
 | 
			
		||||
  let uri = getWsUri()
 | 
			
		||||
  ws = new WebSocket(uri);
 | 
			
		||||
  ws.onerror = function () {
 | 
			
		||||
    ws ?? ws!.close();
 | 
			
		||||
    ws = undefined;
 | 
			
		||||
| 
						 | 
				
			
			@ -69,12 +90,21 @@ function initWs(silent: boolean) {
 | 
			
		|||
      }
 | 
			
		||||
    };
 | 
			
		||||
    ws?.send(JSON.stringify({ action: 'resize', cols, rows }));
 | 
			
		||||
    window.setInterval(function () {
 | 
			
		||||
    intervalReadKey = window.setInterval(function () {
 | 
			
		||||
      if (ws != null && ws.readyState === 1) {
 | 
			
		||||
        ws.send(JSON.stringify({ action: 'read', data: "" }));
 | 
			
		||||
      }
 | 
			
		||||
    }, 30000);
 | 
			
		||||
  }
 | 
			
		||||
  ws.onclose = function (message) {
 | 
			
		||||
    if (intervalReadKey != -1) {
 | 
			
		||||
      window.clearInterval(intervalReadKey)
 | 
			
		||||
      intervalReadKey = -1
 | 
			
		||||
    }
 | 
			
		||||
    if (message.code === 2000) {
 | 
			
		||||
      alert(message.reason);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** init xterm **/
 | 
			
		||||
| 
						 | 
				
			
			@ -103,20 +133,33 @@ function isValidNumber(scrollNumber: number) {
 | 
			
		|||
    scrollNumber <= MAX_SCROLL_BACK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** begin connect **/
 | 
			
		||||
function startConnect(silent: boolean = false) {
 | 
			
		||||
const connectGuard = (silent: boolean): boolean => {
 | 
			
		||||
  if (ip.value.trim() === '' || port.value.trim() === '') {
 | 
			
		||||
    alert('Ip or port can not be empty');
 | 
			
		||||
    return;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (isTunnel && agentID.value == '') {
 | 
			
		||||
    if (silent) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    alert('AgentId can not be empty');
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (ws) {
 | 
			
		||||
    alert('Already connected');
 | 
			
		||||
    return;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true
 | 
			
		||||
}
 | 
			
		||||
/** begin connect **/
 | 
			
		||||
function startConnect(silent: boolean = false) {
 | 
			
		||||
  if (connectGuard(silent)) {
 | 
			
		||||
    // init webSocket
 | 
			
		||||
    initWs(silent);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function disconnect() {
 | 
			
		||||
  try {
 | 
			
		||||
    ws!.close();
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +169,6 @@ function disconnect() {
 | 
			
		|||
    xterm.dispose();
 | 
			
		||||
    fitAddon.dispose()
 | 
			
		||||
    webglAddon.dispose()
 | 
			
		||||
    // $('#fullSc').hide();
 | 
			
		||||
    fullSc.value = false
 | 
			
		||||
    alert('Connection was closed successfully!');
 | 
			
		||||
  } catch {
 | 
			
		||||
| 
						 | 
				
			
			@ -157,16 +199,17 @@ function requestFullScreen(element: HTMLElement) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex flex-col h-[100vh] resize-none">
 | 
			
		||||
    <nav v-if="iframe" class="navbar bg-base-100 flex-row">
 | 
			
		||||
      <div class="flex-1">
 | 
			
		||||
        <a href="https://github.com/alibaba/arthas" target="_blank" title="" class="mr-2 w-20"><img src="/arthas.png"
 | 
			
		||||
            alt="Arthas" title="Welcome to Arthas web console" class=""></a>
 | 
			
		||||
        <ul class="menu menu-horizontal p-0">
 | 
			
		||||
  <div class="flex flex-col h-[100vh] w-[100vw] resize-none">
 | 
			
		||||
    <nav v-if="iframe" class="navbar bg-base-100 md:flex-row flex-col w-[100vw]">
 | 
			
		||||
      <div class="navbar-start">
 | 
			
		||||
        <div class="dropdown dropdown-start 2xl:hidden">
 | 
			
		||||
          <label tabindex="0" class="btn btn-ghost btn-sm">
 | 
			
		||||
            <MenuAlt2Icon class="w-6 h-6"></MenuAlt2Icon>
 | 
			
		||||
          </label>
 | 
			
		||||
          <ul tabindex="0" class="dropdown-content menu shadow bg-base-100">
 | 
			
		||||
            <li>
 | 
			
		||||
              <a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://arthas.aliyun.com/doc"
 | 
			
		||||
                target="_blank">Documentation
 | 
			
		||||
| 
						 | 
				
			
			@ -183,8 +226,33 @@ function requestFullScreen(element: HTMLElement) {
 | 
			
		|||
            </li>
 | 
			
		||||
          </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
      <form class="navbar-end">
 | 
			
		||||
        <div class="flex">
 | 
			
		||||
        <a href="https://github.com/alibaba/arthas" target="_blank" title="" class="mr-2 w-20"><img
 | 
			
		||||
            :src="arthasLogo" alt="Arthas" title="Welcome to Arthas web console"></a>
 | 
			
		||||
 | 
			
		||||
        <ul class="menu menu-vertical 2xl:menu-horizontal hidden">
 | 
			
		||||
          <li>
 | 
			
		||||
            <a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://arthas.aliyun.com/doc"
 | 
			
		||||
              target="_blank">Documentation
 | 
			
		||||
              <span class="sr-only">(current)</span></a>
 | 
			
		||||
          </li>
 | 
			
		||||
          <li>
 | 
			
		||||
            <a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm"
 | 
			
		||||
              href="https://arthas.aliyun.com/doc/arthas-tutorials.html" target="_blank">Online
 | 
			
		||||
              Tutorials</a>
 | 
			
		||||
          </li>
 | 
			
		||||
          <li>
 | 
			
		||||
            <a class="hover:text-sky-500 dark:hover:text-sky-400 text-sm" href="https://github.com/alibaba/arthas"
 | 
			
		||||
              target="_blank">Github</a>
 | 
			
		||||
          </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="navbar-center ">
 | 
			
		||||
        <div class=" xl:flex-row form-control"        
 | 
			
		||||
        :class="{
 | 
			
		||||
          'xl:flex-row':isTunnel,
 | 
			
		||||
          'lg:flex-row':!isTunnel
 | 
			
		||||
        }">
 | 
			
		||||
          <label class="input-group input-group-sm mr-2">
 | 
			
		||||
            <span>IP</span>
 | 
			
		||||
            <input type="text" placeholder="please enter ip address" class="input input-bordered input-sm "
 | 
			
		||||
| 
						 | 
				
			
			@ -194,28 +262,35 @@ function requestFullScreen(element: HTMLElement) {
 | 
			
		|||
            <span>Port</span>
 | 
			
		||||
            <input type="text" placeholder="please enter port" class="input input-sm input-bordered" v-model="port" />
 | 
			
		||||
          </label>
 | 
			
		||||
          <label v-if="isTunnel" class="input-group input-group-sm mr-2">
 | 
			
		||||
            <span>AgentId</span>
 | 
			
		||||
            <input type="text" placeholder="please enter AgentId" class="input input-sm input-bordered"
 | 
			
		||||
              v-model="agentID" />
 | 
			
		||||
          </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="btn-group btn-group-horizontal">
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="navbar-end">
 | 
			
		||||
        <div class="btn-group   2xl:btn-group-horizontal btn-group-horizontal"
 | 
			
		||||
        :class="{
 | 
			
		||||
          'md:btn-group-vertical':isTunnel
 | 
			
		||||
        }">
 | 
			
		||||
          <button
 | 
			
		||||
            class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
 | 
			
		||||
            @click.prevent="startConnect(true)">Connect</button>
 | 
			
		||||
          <button
 | 
			
		||||
            class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
 | 
			
		||||
            @click.prevent="disconnect">Disconnect</button>
 | 
			
		||||
          <a v-if="!isTunnel" class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
 | 
			
		||||
            href="arthas-output/" target="_blank">Arthas Output</a>
 | 
			
		||||
          <a class="btn btn-sm bg-secondary hover:bg-secondary-focus border-none text-secondary-content focus:bg-secondary-focus normal-case"
 | 
			
		||||
            :href="outputHerf" target="_blank">Arthas Output</a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      </form>
 | 
			
		||||
 | 
			
		||||
    </nav>
 | 
			
		||||
    <div class="w-full h-0 flex-auto bg-black overscroll-auto" id="terminal-card">
 | 
			
		||||
      <!-- <div class="h-full overflow-visible" id="terminal-card"> -->
 | 
			
		||||
      <div id="terminal" class="w-full h-full"></div>
 | 
			
		||||
      <!-- </div> -->
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div title="fullscreen" id="fullSc" class="fullSc" v-if="fullSc">
 | 
			
		||||
      <button id="fullScBtn" @click="xtermFullScreen"><img src="/fullsc.png"></button>
 | 
			
		||||
      <button id="fullScBtn" @click="xtermFullScreen"><img :src="fullPic"></button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
@tailwind base;
 | 
			
		||||
@tailwind components;
 | 
			
		||||
@tailwind utilities;
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 | 
			
		||||
    <title>Arthas Tutorials</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
 | 
			
		||||
    <div id="app">
 | 
			
		||||
    </div>
 | 
			
		||||
    <script src="./src/agents.ts" type="module"></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 | 
			
		||||
    <title>Arthas Tutorials</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="app">
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="./src/apps.ts" type="module"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { onMounted, reactive } from 'vue';
 | 
			
		||||
type AgentId = string
 | 
			
		||||
const agentInfos: ([AgentId, AgentInfo])[] = reactive([])
 | 
			
		||||
function getUrlParam(name: string) {
 | 
			
		||||
  const urlparam = new URLSearchParams(window.location.search)
 | 
			
		||||
  return urlparam.get(name)
 | 
			
		||||
}
 | 
			
		||||
function tunnelWebConsoleLink(agentId: string, targetServer: string) {
 | 
			
		||||
  return `/?targetServer=${targetServer}&agentId=${agentId}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const fetchMyApps = () => {
 | 
			
		||||
  const appName = getUrlParam("app") ?? ""
 | 
			
		||||
  let url = `/api/tunnelAgentInfo?app=${appName}`
 | 
			
		||||
  fetch(url)
 | 
			
		||||
    .then((response) => response.json())
 | 
			
		||||
    .then((data: Record<AgentId, AgentInfo>) => {
 | 
			
		||||
      for (const key in data) {
 | 
			
		||||
        agentInfos.push(
 | 
			
		||||
          [key, data[key] as AgentInfo]
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    })
 | 
			
		||||
    .catch((error) => console.error('api error ' + error))
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  fetchMyApps()
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <table class="table table-normal w-[100vw]">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <tr >
 | 
			
		||||
        <th class="normal-case">IP</th>
 | 
			
		||||
        <th class="normal-case">AgentId</th>
 | 
			
		||||
        <th class="normal-case">Version</th>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <tr v-for="(agentInfoRecord) in agentInfos" :key="agentInfoRecord[0]" class="hover">
 | 
			
		||||
        <td>
 | 
			
		||||
          <a class="btn btn-primary btn-sm"
 | 
			
		||||
            :href="tunnelWebConsoleLink(agentInfoRecord[0], agentInfoRecord[1].clientConnectHost)">{{
 | 
			
		||||
                agentInfoRecord[1].host
 | 
			
		||||
            }}</a>
 | 
			
		||||
        </td>
 | 
			
		||||
        <td>
 | 
			
		||||
          {{ agentInfoRecord[0] }}
 | 
			
		||||
        </td>
 | 
			
		||||
        <td>
 | 
			
		||||
          {{ agentInfoRecord[1].arthasVersion }}
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { onMounted, reactive } from 'vue';
 | 
			
		||||
 | 
			
		||||
const apps: string[] = reactive([])
 | 
			
		||||
function fetchMyApps() {
 | 
			
		||||
  fetch('/api/tunnelApps')
 | 
			
		||||
    .then((response) => response.json())
 | 
			
		||||
    .then((data: string[]) => {
 | 
			
		||||
      data.length > 0 && data.forEach(app => apps.push(app))
 | 
			
		||||
    })
 | 
			
		||||
    .catch((error) => {
 | 
			
		||||
      console.error('api error ' + error)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
function detailLink(appName: string) {
 | 
			
		||||
  return "agents.html?app=" + appName;
 | 
			
		||||
}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  fetchMyApps()
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <table class="table w-[100vw] table-normal">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <tr>
 | 
			
		||||
        <th class="normal-case">name of application</th>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <tr v-for="app in apps" :key="app" class="hover">
 | 
			
		||||
        <td>
 | 
			
		||||
          <a class="btn btn-primary btn-sm normal-case" :href="detailLink(app)">{{ app }}</a>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
import { createApp } from "vue";
 | 
			
		||||
import App from "./Agent.vue";
 | 
			
		||||
const app = createApp(App);
 | 
			
		||||
import "~/main.css";
 | 
			
		||||
app
 | 
			
		||||
  .mount("#app");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
import App from "./Apps.vue"
 | 
			
		||||
import { createApp } from "vue"
 | 
			
		||||
import "~/main.css";
 | 
			
		||||
createApp(App)
 | 
			
		||||
  .mount("#app");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
@tailwind base;
 | 
			
		||||
@tailwind components;
 | 
			
		||||
@tailwind utilities;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
import { createApp, h } from "vue";
 | 
			
		||||
import App from "~/component/Console.vue";
 | 
			
		||||
const app = createApp(h(App, { isTunnel: true }));
 | 
			
		||||
import "xterm/css/xterm.css";
 | 
			
		||||
import "~/main.css";
 | 
			
		||||
app
 | 
			
		||||
  .mount("#app");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
type AgentInfo = {
 | 
			
		||||
  clientConnectHost:string,
 | 
			
		||||
  host:string,
 | 
			
		||||
  port:number,
 | 
			
		||||
  arthasVersion:string
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html lang="en" data-theme="corporate">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8">
 | 
			
		||||
    <link rel="icon" href="/favicon.ico" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 | 
			
		||||
    <title>Arthas Console</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
    <script src="./src/main.ts" type="module"></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { createApp } from "vue";
 | 
			
		||||
import App from "./App.vue";
 | 
			
		||||
import App from "~/component/Console.vue";
 | 
			
		||||
const app = createApp(App);
 | 
			
		||||
import "xterm/css/xterm.css"
 | 
			
		||||
import "./main.css"
 | 
			
		||||
import "~/main.css"
 | 
			
		||||
app
 | 
			
		||||
  .mount("#app");
 | 
			
		||||
| 
						 | 
				
			
			@ -18,15 +18,10 @@ onBeforeUnmount(() => {
 | 
			
		|||
<template>
 | 
			
		||||
  <div class=" h-screen flex flex-col">
 | 
			
		||||
    <nav-header class="h-[10vh]"></nav-header>
 | 
			
		||||
    <div class=" flex-auto h-[90vh] overflow-auto">
 | 
			
		||||
      <div class="flex flex-row h-full">
 | 
			
		||||
        <!-- <nav-aside ></nav-aside> -->
 | 
			
		||||
        <div class="flex-auto overflow-auto h-[90vh] w-[90vw]">
 | 
			
		||||
    <div class=" flex-auto h-[90vh] overflow-auto w-[100vw]">
 | 
			
		||||
          <router-view>
 | 
			
		||||
          </router-view>
 | 
			
		||||
    </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  <err-dialog v-if="publicS.isErr" />
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useMachine } from '@xstate/vue';
 | 
			
		||||
import { computed, onBeforeMount, Ref, ref, watchEffect } from 'vue';
 | 
			
		||||
import { RefreshIcon, LogoutIcon, LoginIcon, MenuIcon, XCircleIcon } from '@heroicons/vue/outline';
 | 
			
		||||
import { RefreshIcon, LogoutIcon, LoginIcon, MenuIcon, MenuAlt2Icon } from '@heroicons/vue/outline';
 | 
			
		||||
import { fetchStore } from '@/stores/fetch';
 | 
			
		||||
import machine from "@/machines/consoleMachine"
 | 
			
		||||
import { publicStore } from '@/stores/public';
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ import { PuzzleIcon, TerminalIcon, ViewGridIcon } from "@heroicons/vue/outline"
 | 
			
		|||
import { DesktopComputerIcon } from "@heroicons/vue/solid"
 | 
			
		||||
import { useRoute, useRouter } from 'vue-router';
 | 
			
		||||
import permachine from '@/machines/perRequestMachine';
 | 
			
		||||
import pic from "~/assert/arthas.png"
 | 
			
		||||
const fetchM = useMachine(machine)
 | 
			
		||||
const publicS = publicStore()
 | 
			
		||||
const { send } = fetchM
 | 
			
		||||
| 
						 | 
				
			
			@ -148,23 +149,38 @@ const toNext = (url: string) => {
 | 
			
		|||
    window.open("/", "_blank")
 | 
			
		||||
  } else router.push(url)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <nav class=" h-[10vh] border-b-2 navbar">
 | 
			
		||||
  <nav class=" h-[10vh] border-b-2 navbar bg-base-100">
 | 
			
		||||
    <div class=" navbar-start flex items-stretch">
 | 
			
		||||
      <!-- <div class=" indicator mx-3"> -->
 | 
			
		||||
      <div class="dropdown dropdown-start hover xl:hidden">
 | 
			
		||||
        <label tabindex="0" class="btn btn-ghost m-1">
 | 
			
		||||
          <MenuAlt2Icon class="w-6 h-6"></MenuAlt2Icon>
 | 
			
		||||
        </label>
 | 
			
		||||
        <ul tabindex="0" class="menu menu-vertical dropdown-content bg-base-100 shadow rounded-box">
 | 
			
		||||
          <li v-for="(tab, idx) in tabs" :key="idx" @click="toNext(tab.url)">
 | 
			
		||||
            <a :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
 | 
			
		||||
              <component :is="tab.icon" class="w-4 h-4" />
 | 
			
		||||
              {{
 | 
			
		||||
                  tab.name
 | 
			
		||||
              }}
 | 
			
		||||
            </a>
 | 
			
		||||
          </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <a class="flex items-center justify-center mx-2" href="https://arthas.aliyun.com/doc/commands.html"
 | 
			
		||||
        target="_blank">
 | 
			
		||||
        <img src="/arthas.png" alt="logo" class="w-32" />
 | 
			
		||||
        <img :src="pic" alt="logo" class="w-32" />
 | 
			
		||||
      </a>
 | 
			
		||||
      <span class="badge badge-ghost self-end text-sm">v{{ version }}</span>
 | 
			
		||||
      <!-- </div> -->
 | 
			
		||||
      <span class="badge badge-ghost self-end badge-sm">v{{ version }}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="navbar-center">
 | 
			
		||||
      <ul class="menu menu-horizontal p-0">
 | 
			
		||||
 | 
			
		||||
      <ul class="menu menu-horizontal hidden xl:flex">
 | 
			
		||||
        <li v-for="(tab, idx) in tabs" :key="idx" @click="toNext(tab.url)">
 | 
			
		||||
          <a class="break-all" :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
 | 
			
		||||
          <a :class="{ 'bg-primary text-primary-content': routePath.includes(tab.url), }">
 | 
			
		||||
            <component :is="tab.icon" class="w-4 h-4" />
 | 
			
		||||
            {{
 | 
			
		||||
                tab.name
 | 
			
		||||
| 
						 | 
				
			
			@ -186,13 +202,11 @@ const toNext = (url: string) => {
 | 
			
		|||
          </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <button class=" btn btn-ghost"
 | 
			
		||||
        :class="{ 'btn-primary': !fetchS.online, 'btn-error': fetchS.online }">
 | 
			
		||||
      <button class=" btn btn-ghost" :class="{ 'btn-primary': !fetchS.online, 'btn-error': fetchS.online }">
 | 
			
		||||
        <LogoutIcon class="h-6 w-6" @click="logout" v-if="fetchS.online" />
 | 
			
		||||
        <login-icon class="h-6 w-6" @click="login" v-else />
 | 
			
		||||
      </button>
 | 
			
		||||
      <button class="btn-ghost btn"
 | 
			
		||||
        @click="reset">
 | 
			
		||||
      <button class="btn-ghost btn" @click="reset">
 | 
			
		||||
        <refresh-icon class="h-6 w-6" :class="restBtnclass" />
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { createApp } from "vue";
 | 
			
		||||
import "./index.css";
 | 
			
		||||
import "~/main.css"
 | 
			
		||||
import router from "./router/index";
 | 
			
		||||
import { createPinia } from "pinia";
 | 
			
		||||
import App from "./App.vue";
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export default function transformStackTrace(trace: StackTrace) {
 | 
			
		||||
  return `${trace.className}.${trace.methodName} (${trace.fileName}: ${trace.lineNumber})`;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -441,27 +441,37 @@ onBeforeUnmount(async () => {
 | 
			
		|||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="p-2 pointer-events-auto flex flex-col h-full">
 | 
			
		||||
    <div class="input-btn-style mb-4 h-32 flex flex-wrap flex-col items-start overflow-auto min-h-[6rem]">
 | 
			
		||||
      <div v-for="(cv, ci) in runtimeInfo" :key="ci" class="flex mb-1 pr-2">
 | 
			
		||||
        <span class="bg-primary-focus text-primary-content border border-primary-focus w-44 px-2 rounded-l">
 | 
			
		||||
          {{ cv[0] }}
 | 
			
		||||
        </span>
 | 
			
		||||
        <span class="bg-base-200 border border-primary-focus rounded-r px-2 flex-1">
 | 
			
		||||
  <div class="p-2 pointer-events-auto h-full overflow-auto">
 | 
			
		||||
    <div class="card bg-base-100 border mb-4 compact h-auto">
 | 
			
		||||
      <div class="card-body flex-wrap flex-row">
 | 
			
		||||
        <span v-for="(cv, ci) in runtimeInfo" :key="ci" class="badge badge-outline badge-primary">
 | 
			
		||||
 | 
			
		||||
          {{ cv[0] }}:
 | 
			
		||||
          {{ cv[1] }}
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- <CmdResMenu title="threads" :map="threads" class="w-full flex justify-center" /> -->
 | 
			
		||||
    <div class="flex justify-evenly mb-4 flex-1 h-80">
 | 
			
		||||
      <div id="heapMemory" class="w-80 h-80 flex-1 input-btn-style mr-4"></div>
 | 
			
		||||
      <div id="nonheapMemory" class="w-80 h-80 flex-1 input-btn-style mr-4"></div>
 | 
			
		||||
      <div id="bufferPoolMemory" class="w-80 h-80 flex-1 input-btn-style"></div>
 | 
			
		||||
      <div class="card border mr-4 flex-1 bg-base-100">
 | 
			
		||||
        <div id="heapMemory" class="w-80 h-80 card-body "></div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card border mr-4 flex-1 bg-base-100">
 | 
			
		||||
        <div id="nonheapMemory" class="w-80 h-80 card-body"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card border flex-1 bg-base-100">
 | 
			
		||||
        <div id="bufferPoolMemory" class="w-80 h-80 card-body"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="w-full flex justify-start items-start flex-1">
 | 
			
		||||
      <div id="gc-info" class="w-[40rem] h-80 input-btn-style p-2 mr-4"></div>
 | 
			
		||||
      <div class="input-btn-style flex-1 h-80 overflow-auto w-0">
 | 
			
		||||
      <div class="card bg-base-100 border mr-4">
 | 
			
		||||
        <div id="gc-info" class="w-[40rem] h-80 card-body p-2 "></div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card flex-1 h-80 overflow-auto w-0 border bg-base-100">
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
          <div class="flex justify-end mb-2">
 | 
			
		||||
            <div class="btn-group">
 | 
			
		||||
              <button class="btn btn-sm btn-outline" @click="decrease">-</button>
 | 
			
		||||
| 
						 | 
				
			
			@ -491,6 +501,7 @@ onBeforeUnmount(async () => {
 | 
			
		|||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
| 
						 | 
				
			
			@ -7,9 +7,6 @@ const routes: { cmd: string, url: string }[] = [
 | 
			
		|||
  }, {
 | 
			
		||||
    cmd: "classInfo",
 | 
			
		||||
    url: "classInfo"
 | 
			
		||||
  // }, {
 | 
			
		||||
  //   cmd: "perfcounter",
 | 
			
		||||
  //   url: "perfcounter"
 | 
			
		||||
  }, {
 | 
			
		||||
    cmd: "classLoader",
 | 
			
		||||
    url: "classLoader"
 | 
			
		||||
| 
						 | 
				
			
			@ -6,14 +6,11 @@ import { fetchStore } from '@/stores/fetch';
 | 
			
		|||
import { useMachine, useInterpret } from '@xstate/vue';
 | 
			
		||||
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
 | 
			
		||||
import Enhancer from '@/components/show/Enhancer.vue';
 | 
			
		||||
import {transfromStore} from "@/stores/resTransform"
 | 
			
		||||
import transformStackTrace from '@/utils/transform';
 | 
			
		||||
const fetchM = useInterpret(permachine)
 | 
			
		||||
const pollingM = useMachine(machine)
 | 
			
		||||
const fetchS = fetchStore()
 | 
			
		||||
// const publicS = publicStore()
 | 
			
		||||
const transS = transfromStore()
 | 
			
		||||
const { getCommonResEffect } = fetchS
 | 
			
		||||
// const {getCommonResEffect} = publicStore()
 | 
			
		||||
 | 
			
		||||
const loop = fetchS.pullResultsLoop(pollingM)
 | 
			
		||||
const tableResults = reactive([] as Map<string, string>[])
 | 
			
		||||
| 
						 | 
				
			
			@ -26,8 +23,8 @@ const keyList = [
 | 
			
		|||
  "classloader",
 | 
			
		||||
  "threadId",
 | 
			
		||||
  "threadName",]
 | 
			
		||||
// const enhancer = reactive(new Map())
 | 
			
		||||
const enhancer = ref(undefined as EnchanceResult | undefined)
 | 
			
		||||
 | 
			
		||||
getCommonResEffect(pollingM, body => {
 | 
			
		||||
  if (body.results.length > 0) {
 | 
			
		||||
    body.results.forEach(result => {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +37,7 @@ getCommonResEffect(pollingM, body => {
 | 
			
		|||
            let val: string | string[] = ""
 | 
			
		||||
            if (k === "stackTrace") {
 | 
			
		||||
              let stackTrace = result[k]
 | 
			
		||||
              val = stackTrace.map((trace) => transS.transformStackTrace(trace))
 | 
			
		||||
              val = stackTrace.map((trace) => transformStackTrace(trace))
 | 
			
		||||
            } else {
 | 
			
		||||
              val = result[k as Exclude<keyof typeof result, "jobId" | "type" | "stackTrace">].toString()
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -58,10 +55,8 @@ getCommonResEffect(pollingM, body => {
 | 
			
		|||
})
 | 
			
		||||
 | 
			
		||||
onBeforeMount(() => {
 | 
			
		||||
  // fetchM.send("INIT")
 | 
			
		||||
  pollingM.send("INIT")
 | 
			
		||||
  fetchS.asyncInit()
 | 
			
		||||
  // loop.open()
 | 
			
		||||
})
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
  loop.close()
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ import CmdResMenu from '@/components/show/CmdResMenu.vue';
 | 
			
		|||
import { fetchStore } from '@/stores/fetch';
 | 
			
		||||
import { interpret } from 'xstate';
 | 
			
		||||
import permachine from '@/machines/perRequestMachine';
 | 
			
		||||
const classInfoM = useMachine(machine)
 | 
			
		||||
const classMethodInfoM = useMachine(machine)
 | 
			
		||||
const dumpM = useMachine(machine)
 | 
			
		||||
const classDetailMap = reactive(new Map<string, string[]>())
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,10 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { computed, onBeforeMount, onUnmounted, reactive, ref } from 'vue';
 | 
			
		||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue';
 | 
			
		||||
import { computed, onBeforeMount, reactive, ref } from 'vue';
 | 
			
		||||
import { publicStore } from "@/stores/public"
 | 
			
		||||
import { fetchStore } from '@/stores/fetch';
 | 
			
		||||
import { interpret } from 'xstate';
 | 
			
		||||
import CmdResMenu from '@/components/show/CmdResMenu.vue';
 | 
			
		||||
import transformMachine from '@/machines/transformConfigMachine';
 | 
			
		||||
// import ClassInput from '@/components/input/ClassInput.vue';
 | 
			
		||||
import permachine from '@/machines/perRequestMachine';
 | 
			
		||||
import Tree from '@/components/show/Tree.vue';
 | 
			
		||||
const fetchS = fetchStore()
 | 
			
		||||
| 
						 | 
				
			
			@ -208,10 +206,11 @@ const resetClassloader = () => {
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="flex flex-col h-full justify-between">
 | 
			
		||||
    <div class="flex h-[40vh]">
 | 
			
		||||
      <div class="input-btn-style h-full p-4 mb-2 flex flex-col transition-all duration-500" :class='{
 | 
			
		||||
      <div class="card rounded-box border transition-all duration-500 bg-base-100 mr-2 " :class='{
 | 
			
		||||
        "w-full": loaderCache.hash === "",
 | 
			
		||||
        "w-2/3": loaderCache.hash !== ""
 | 
			
		||||
      }'>
 | 
			
		||||
        <div class="card-body h-full p-4 mb-2 ">
 | 
			
		||||
          <!-- 后置为了让用户能注意到右上角的refreshicon -->
 | 
			
		||||
          <div class="h-[5vh] mb-4 justify-end flex">
 | 
			
		||||
            <button @click="resetClassloader" class="btn btn-primary btn-sm mr-1">reset</button>
 | 
			
		||||
| 
						 | 
				
			
			@ -261,11 +260,12 @@ const resetClassloader = () => {
 | 
			
		|||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
      <div class=" ml-2 overflow-y-scroll transition-all duration-500" :class='{
 | 
			
		||||
        "w-0":loaderCache.hash === "",
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card rounded-box border bg-base-100" :class='{
 | 
			
		||||
        "w-0 border-none": loaderCache.hash === "",
 | 
			
		||||
        "input-btn-style w-1/3": loaderCache.hash !== ""
 | 
			
		||||
      }'>
 | 
			
		||||
        <div class="card-body ml-2 overflow-y-scroll transition-all duration-500">
 | 
			
		||||
 | 
			
		||||
          <div class="mb-2">
 | 
			
		||||
            <div class="overflow-auto">
 | 
			
		||||
| 
						 | 
				
			
			@ -322,17 +322,19 @@ const resetClassloader = () => {
 | 
			
		|||
                @click="getUrlStats">refresh</button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <ul class="mt-2 w-full flex flex-col">
 | 
			
		||||
            <li v-for="(url,i) in selectedClassLoadersUrlStats" :key="i" class="bg-blue-200 mb-2 p-2 break-all w-full">
 | 
			
		||||
              <li v-for="(url, i) in selectedClassLoadersUrlStats" :key="i"
 | 
			
		||||
                class="bg-blue-200 mb-2 p-2 break-all w-full">
 | 
			
		||||
                {{ url }}</li>
 | 
			
		||||
            </ul>
 | 
			
		||||
          </template>
 | 
			
		||||
          <!-- </div> -->
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- 下面的3格 -->
 | 
			
		||||
    <div class="w-full flex-auto flex h-[40vh] mt-2">
 | 
			
		||||
      <div class="input-btn-style w-1/3 mr-2 h-full flex">
 | 
			
		||||
        <div class="overflow-y-scroll h-ful w-full">
 | 
			
		||||
      <div class="card w-1/3 mr-2 bg-base-100 border">
 | 
			
		||||
        <div class="card-body overflow-y-scroll">
 | 
			
		||||
          <div class=" mb-2 flex items-center justify-end">
 | 
			
		||||
            <h3 class="text-xl flex-1 flex justify-center">urlStats</h3>
 | 
			
		||||
            <button class="btn btn-primary btn-sm" @click="getAllUrlStats">refresh</button>
 | 
			
		||||
| 
						 | 
				
			
			@ -341,12 +343,13 @@ const resetClassloader = () => {
 | 
			
		|||
            <CmdResMenu :title="v[0]" :map="v[1]" button-width="w-full" :button-accent="v[2] === loaderCache.hash">
 | 
			
		||||
            </CmdResMenu>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="flex flex-col h-full w-2/3">
 | 
			
		||||
        <div class="input-btn-style w-full mr-2 h-full">
 | 
			
		||||
          <div class="overflow-auto flex-1 h-full">
 | 
			
		||||
            <div class="flex justify-end mb-2">
 | 
			
		||||
      <div class="card h-full w-2/3 border bg-base-100">
 | 
			
		||||
        <div class="card-body overflow-auto">
 | 
			
		||||
          <div class="flex justify-around mb-2">
 | 
			
		||||
            <h3 class="text-xl">statistics categorized by class type</h3>
 | 
			
		||||
            <button class="btn btn-primary btn-sm" @click="getCategorizedByClassType">refresh</button>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="overflow-auto">
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +357,8 @@ const resetClassloader = () => {
 | 
			
		|||
              <thead>
 | 
			
		||||
                <tr>
 | 
			
		||||
                  <th></th>
 | 
			
		||||
                    <th class=" normal-case" v-for="(v,i) in keyList" :key="i" :class="{'group-first:z-0':i==0}">{{v}}
 | 
			
		||||
                  <th class=" normal-case" v-for="(v, i) in keyList" :key="i" :class="{ 'group-first:z-0': i == 0 }">
 | 
			
		||||
                    {{ v }}
 | 
			
		||||
                  </th>
 | 
			
		||||
                </tr>
 | 
			
		||||
              </thead>
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +376,7 @@ const resetClassloader = () => {
 | 
			
		|||
            </table>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -8,14 +8,12 @@ import TodoList from '@/components/input/TodoList.vue';
 | 
			
		|||
import {
 | 
			
		||||
  Listbox, ListboxButton, ListboxOptions, ListboxOption
 | 
			
		||||
} from '@headlessui/vue';
 | 
			
		||||
import { transfromStore } from "@/stores/resTransform"
 | 
			
		||||
import permachine from '@/machines/perRequestMachine';
 | 
			
		||||
import { watch } from 'fs';
 | 
			
		||||
import transformStackTrace from '@/utils/transform';
 | 
			
		||||
 | 
			
		||||
const fetchS = fetchStore()
 | 
			
		||||
const FetchService = useInterpret(permachine)
 | 
			
		||||
const stackTrace = reactive([] as string[])
 | 
			
		||||
const transformS = transfromStore()
 | 
			
		||||
const count = ref(0)
 | 
			
		||||
const leastTime = ref(200)
 | 
			
		||||
const isBlock = ref(false)
 | 
			
		||||
| 
						 | 
				
			
			@ -63,12 +61,7 @@ const infoCount = ref({
 | 
			
		|||
  TERMINATED: 0
 | 
			
		||||
} as ThreadStateCount)
 | 
			
		||||
const tableResults = reactive([] as Map<string, string>[])
 | 
			
		||||
const limitResults = tableResults
 | 
			
		||||
// watchEffect(()=>{
 | 
			
		||||
//   if(count.value > 0) {
 | 
			
		||||
//     tableResults.length = count.value
 | 
			
		||||
//   }
 | 
			
		||||
// })
 | 
			
		||||
 | 
			
		||||
const tableFilter = computed(() => {
 | 
			
		||||
  // 原本的数组
 | 
			
		||||
  let res = count.value > 0 ? tableResults.filter((v, i) => i < count.value) : tableResults
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +184,7 @@ const getSpecialThreads = (threadid: number = -1) => {
 | 
			
		|||
    const result = (res as CommonRes).body.results[0]
 | 
			
		||||
    if (result.type === "thread") {
 | 
			
		||||
      stackTrace.length = 0
 | 
			
		||||
      result.threadInfo.stackTrace.forEach(stack => stackTrace.unshift(transformS.transformStackTrace(stack)))
 | 
			
		||||
      result.threadInfo.stackTrace.forEach(stack => stackTrace.unshift(transformStackTrace(stack)))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -231,8 +224,9 @@ const getSpecialThreads = (threadid: number = -1) => {
 | 
			
		|||
      </label>
 | 
			
		||||
      <button class="btn btn-primary btn-sm btn-outline" @click="getThreads"> get threads </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="w-full h-[50vh] input-btn-style my-2 p-4 flex flex-col">
 | 
			
		||||
      <div class="flex h-[8vh] flex-wrap flex-auto">
 | 
			
		||||
    <div class="w-full h-[50vh] my-2 card rounded-box compact border">
 | 
			
		||||
      <div class="card-body overflow-auto">
 | 
			
		||||
        <div class="h-[8vh] flex-wrap flex-auto flex-row flex">
 | 
			
		||||
          <div v-for="(v, i) in Object.entries(infoCount)" :key="i" class="mr-2">
 | 
			
		||||
            <span class="text-primary-content border border-primary-focus bg-primary-focus w-44 px-2 rounded-l">
 | 
			
		||||
              {{ v[0] }}
 | 
			
		||||
| 
						 | 
				
			
			@ -276,10 +270,12 @@ const getSpecialThreads = (threadid: number = -1) => {
 | 
			
		|||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    <div class="input-btn-style flex-auto overflow-auto">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class=" flex-auto card rounded-box compact border">
 | 
			
		||||
      <div class="card-body overflow-auto ">
 | 
			
		||||
        <h2 class="text-lg">stackTrace</h2>
 | 
			
		||||
        <div v-for="(stack, i) in stackTrace" class="mb-2" :key="i">{{ stack }} </div>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -3,11 +3,13 @@
 | 
			
		|||
  "private": false,
 | 
			
		||||
  "version": "0.1.4",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "vite --port 8000 --mode ui",
 | 
			
		||||
    "dev:tunnel": "vite --port 8000 --mode tunnel",
 | 
			
		||||
    "build": "vue-tsc --noEmit && vite build --mode ui",
 | 
			
		||||
    "dev:ui": "vite --port 8000 --mode ui",
 | 
			
		||||
    "build": "vue-tsc --noEmit && vite build --mode tunnel && vite build --mode ui",
 | 
			
		||||
    "build:tunnel": "vue-tsc --noEmit && vite build --mode tunnel",
 | 
			
		||||
    "preview": "vite preview"
 | 
			
		||||
    "build:ui": "vue-tsc --noEmit && vite build --mode ui",
 | 
			
		||||
    "preview:ui": "vite preview --mode ui",
 | 
			
		||||
    "preview:tunnel": "vite preview --mode tunnel"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@headlessui/vue": "^1.6.6",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,7 @@
 | 
			
		|||
/** @type {import('tailwindcss').Config} */
 | 
			
		||||
module.exports = {
 | 
			
		||||
  content: [
 | 
			
		||||
    "./ui/index.html",
 | 
			
		||||
    "./index.html",
 | 
			
		||||
    "./src/*.{vue,js,ts,jsx,tsx}",
 | 
			
		||||
    "./ui/src/**/*.{vue,js,ts,jsx,tsx}",
 | 
			
		||||
    "all/**/*.{vue,ts,css}",
 | 
			
		||||
  ],
 | 
			
		||||
  theme: {
 | 
			
		||||
    extend: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,13 +14,18 @@
 | 
			
		|||
    "skipLibCheck": true,
 | 
			
		||||
    "baseUrl": ".",
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@/*":["ui/src/*"]
 | 
			
		||||
      "@/*": ["all/ui/ui/src/*"],
 | 
			
		||||
      "~/*": ["all/share/*"]
 | 
			
		||||
    },
 | 
			
		||||
    "experimentalDecorators": true
 | 
			
		||||
  },
 | 
			
		||||
  "include": [
 | 
			
		||||
    "ui/src/**/*.ts", "ui/src/**/*.d.ts", "ui/src/**/*.tsx", "ui/src/**/*.vue","ui/tests/**/*.ts",
 | 
			
		||||
    "src/**/*.vue", "src/**/*.ts","src/**/*.d.ts"
 | 
			
		||||
    "all/**/*.vue",
 | 
			
		||||
    "all/**/*.ts",
 | 
			
		||||
    "all/**/*.d.ts",
 | 
			
		||||
    "all/env.d.ts"
 | 
			
		||||
  ],
 | 
			
		||||
  "references": [{ "path": "./tsconfig.node.json" }]
 | 
			
		||||
  "references": [
 | 
			
		||||
    { "path": "./tsconfig.node.json" }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,5 +4,5 @@
 | 
			
		|||
    "module": "esnext",
 | 
			
		||||
    "moduleResolution": "node"
 | 
			
		||||
  },
 | 
			
		||||
  "include": ["vite.config.ts"]
 | 
			
		||||
  "include": ["./vite.config.ts"]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
/// <reference types="vite/client" />
 | 
			
		||||
 | 
			
		||||
declare module '*.vue' {
 | 
			
		||||
  import type { DefineComponent } from 'vue'
 | 
			
		||||
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
 | 
			
		||||
  const component: DefineComponent<{}, {}, any>
 | 
			
		||||
  export default component
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
@tailwind base;
 | 
			
		||||
@tailwind components;
 | 
			
		||||
@tailwind utilities;
 | 
			
		||||
 | 
			
		||||
@layer components {
 | 
			
		||||
  .button-style {
 | 
			
		||||
    @apply bg-blue-400 hover:opacity-50 transition rounded-md p-2
 | 
			
		||||
  }
 | 
			
		||||
  .input-btn-style {
 | 
			
		||||
    @apply border p-2 rounded-xl hover:shadow-md transition border-gray-300
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
import { defineStore } from "pinia";
 | 
			
		||||
 | 
			
		||||
export const transfromStore = defineStore("transformStore", {
 | 
			
		||||
  actions: {
 | 
			
		||||
    transformStackTrace(trace: StackTrace) {
 | 
			
		||||
      return `${trace.className}.${trace.methodName} (${trace.fileName}: ${trace.lineNumber})`;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -9,28 +9,45 @@ export default defineConfig(({ mode }) => {
 | 
			
		|||
    `${env.VITE_ARTHAS_PROXY_IP}:${env.VITE_ARTHAS_PROXY_PORT}`;
 | 
			
		||||
 | 
			
		||||
  console.log("Arthas proxy :", proxyTarget);
 | 
			
		||||
  let outDir, input
 | 
			
		||||
  console.log(env.VITE_AGENT)
 | 
			
		||||
  if (env.VITE_AGENT === "true") {
 | 
			
		||||
    outDir = `./dist/tunnel`
 | 
			
		||||
  let outDir, input, root, proxy;
 | 
			
		||||
 | 
			
		||||
  if (mode === "tunnel") {
 | 
			
		||||
    outDir = path.resolve(__dirname, `dist/tunnel`);
 | 
			
		||||
    root = "./all/tunnel";
 | 
			
		||||
    input = {
 | 
			
		||||
      tunnel: path.resolve(__dirname, "index.html")
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    outDir = `./dist`
 | 
			
		||||
      tunnel: path.resolve(__dirname, "all/tunnel/index.html"),
 | 
			
		||||
      apps: path.resolve(__dirname, "all/tunnel/apps.html"),
 | 
			
		||||
      agents: path.resolve(__dirname, "all/tunnel/agents.html"),
 | 
			
		||||
    };
 | 
			
		||||
    proxy = {
 | 
			
		||||
      "/api": {
 | 
			
		||||
        target: `http://${proxyTarget}`,
 | 
			
		||||
        changeOrigin: true,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  } else if (mode === "ui") {
 | 
			
		||||
    outDir = path.resolve(__dirname, `dist/ui`);
 | 
			
		||||
    root = "./all/ui";
 | 
			
		||||
    input = {
 | 
			
		||||
      main: path.resolve(__dirname, "index.html"),
 | 
			
		||||
      ui: path.resolve(__dirname, "ui/index.html"),
 | 
			
		||||
    }
 | 
			
		||||
      main: path.resolve(__dirname, "all/ui/index.html"),
 | 
			
		||||
      ui: path.resolve(__dirname, "all/ui/ui/index.html"),
 | 
			
		||||
    };
 | 
			
		||||
    proxy = {
 | 
			
		||||
      "/api": {
 | 
			
		||||
        target: `http://${proxyTarget}`,
 | 
			
		||||
        changeOrigin: true,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    plugins: [vue({
 | 
			
		||||
      reactivityTransform: true,
 | 
			
		||||
      reactivityTransform: path.resolve(__dirname, "all/ui"),
 | 
			
		||||
    })],
 | 
			
		||||
    resolve: {
 | 
			
		||||
      alias: {
 | 
			
		||||
        "@": path.resolve(__dirname, "./ui/src/"),
 | 
			
		||||
        "@": path.resolve(__dirname, "all/ui/ui/src"),
 | 
			
		||||
        "~": path.resolve(__dirname, "all/share"),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    build: {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,13 +70,10 @@ export default defineConfig(({ mode }) => {
 | 
			
		|||
      "__VUE_OPTIONS_API__": false,
 | 
			
		||||
    },
 | 
			
		||||
    base: "/",
 | 
			
		||||
    publicDir: path.resolve(__dirname, "all/share/public"),
 | 
			
		||||
    root,
 | 
			
		||||
    server: {
 | 
			
		||||
      proxy: {
 | 
			
		||||
        "/api": {
 | 
			
		||||
          target: `http://${proxyTarget}`,
 | 
			
		||||
          changeOrigin: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      proxy,
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,12 +71,21 @@
 | 
			
		|||
                        </configuration>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                    <execution>
 | 
			
		||||
                        <id>vite build</id>
 | 
			
		||||
                        <id>vite build:ui</id>
 | 
			
		||||
                        <goals>
 | 
			
		||||
                            <goal>yarn</goal>
 | 
			
		||||
                        </goals>
 | 
			
		||||
                        <configuration>
 | 
			
		||||
                            <arguments>vite build</arguments>
 | 
			
		||||
                            <arguments>vite build --mode ui</arguments>
 | 
			
		||||
                        </configuration>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                    <execution>
 | 
			
		||||
                        <id>vite build:tunnel</id>
 | 
			
		||||
                        <goals>
 | 
			
		||||
                            <goal>yarn</goal>
 | 
			
		||||
                        </goals>
 | 
			
		||||
                        <configuration>
 | 
			
		||||
                            <arguments>vite build --mode tunnel</arguments>
 | 
			
		||||
                        </configuration>
 | 
			
		||||
                    </execution>
 | 
			
		||||
                </executions>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue