mirror of https://github.com/veypi/OneAuth.git
reform web menu and add nats url proxy
parent
cc32bd481f
commit
ff8525ad2b
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
//
|
||||
// proxy.rs
|
||||
// Copyright (C) 2023 veypi <i@veypi.com>
|
||||
// 2023-10-19 19:31
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
@ -0,0 +1,101 @@
|
||||
//
|
||||
// task.rs
|
||||
// Copyright (C) 2023 veypi <i@veypi.com>
|
||||
// 2023-10-19 01:59
|
||||
// Distributed under terms of the MIT license.
|
||||
//
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tracing::{info, warn};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
struct sysInfo {
|
||||
client: clientInfo,
|
||||
id: String,
|
||||
// server: String,
|
||||
}
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
struct clientInfo {
|
||||
id: i64,
|
||||
acc: String,
|
||||
name: String,
|
||||
host: String,
|
||||
}
|
||||
pub fn start_nats_online(client: async_nats::client::Client) {
|
||||
let db: Arc<Mutex<HashMap<i64, clientInfo>>> = Arc::new(Mutex::new(HashMap::new()));
|
||||
{
|
||||
let db = db.clone();
|
||||
let client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut sub = client
|
||||
.subscribe("$SYS.ACCOUNT.*.CONNECT".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
while let Some(msg) = sub.next().await {
|
||||
let s = String::from_utf8(msg.payload.to_vec()).unwrap();
|
||||
info!("{}", s);
|
||||
let inf: sysInfo = serde_json::from_slice(&msg.payload.to_vec()).unwrap();
|
||||
info!("add {} {}", inf.client.id, inf.client.name);
|
||||
let mut db = db.lock().unwrap();
|
||||
db.insert(inf.client.id, inf.client);
|
||||
}
|
||||
});
|
||||
}
|
||||
{
|
||||
let db = db.clone();
|
||||
let client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut sub = client
|
||||
.subscribe("$SYS.ACCOUNT.*.DISCONNECT".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
while let Some(msg) = sub.next().await {
|
||||
// let s = String::from_utf8(msg.payload.to_vec()).unwrap();
|
||||
let inf: sysInfo = serde_json::from_slice(&msg.payload.to_vec()).unwrap();
|
||||
info!("remove {} {}", inf.client.id, inf.client.name);
|
||||
let mut db = db.lock().unwrap();
|
||||
db.remove(&inf.client.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
tokio::spawn(async move {
|
||||
let mut sub = client.subscribe("sys.online".to_string()).await.unwrap();
|
||||
while let Some(msg) = sub.next().await {
|
||||
// // let s = String::from_utf8(msg.payload.to_vec()).unwrap();
|
||||
// let inf: sysInfo = serde_json::from_slice(&msg.payload.to_vec()).unwrap();
|
||||
// info!("remove {} {}", inf.client.id, inf.client.name);
|
||||
// let mut db = db.lock().unwrap();
|
||||
// db.remove(&inf.client.id);
|
||||
if let Some(t) = msg.reply {
|
||||
let d = {
|
||||
let tmp = db.lock().unwrap();
|
||||
let payload: Vec<clientInfo> = tmp.iter().map(|(_, c)| c.clone()).collect();
|
||||
serde_json::to_string(&payload).unwrap()
|
||||
};
|
||||
match client.publish(t, d.into()).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
warn!("{}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_demo() {
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
interval.tick().await;
|
||||
let start = Instant::now();
|
||||
println!("time:{:?}", start);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
println!("time:{:?}", start.elapsed());
|
||||
}
|
||||
});
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* nats.ts
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-19 21:36
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
import ajax from './axios'
|
||||
|
||||
export default {
|
||||
local: './nats/',
|
||||
general() {
|
||||
return ajax.get(this.local + 'varz')
|
||||
},
|
||||
conns() {
|
||||
return ajax.get(this.local + 'connz', { subs: true })
|
||||
},
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* cfg.ts
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-18 22:03
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
|
||||
const cfg = {
|
||||
host: 'http://' + window.location.host,
|
||||
id: 'FR9P5t8debxc11aFF',
|
||||
}
|
||||
|
||||
export default cfg
|
||||
|
@ -1,30 +0,0 @@
|
||||
<!--
|
||||
* main.vue
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-04 10:56
|
||||
* Distributed under terms of the MIT license.
|
||||
-->
|
||||
<template>
|
||||
<q-list>
|
||||
<!-- <q-item-label header> -->
|
||||
<!-- Essential Links -->
|
||||
<!-- </q-item-label> -->
|
||||
|
||||
<EssentialLink v-for="link in menu.list" :key="link.title" v-bind="link" />
|
||||
</q-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import EssentialLink from 'src/components/EssentialLink.vue';
|
||||
import { useMenuStore } from 'src/stores/menu';
|
||||
import { onMounted } from 'vue';
|
||||
let menu = useMenuStore()
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
console.log('loading main menu')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -0,0 +1,35 @@
|
||||
<!--
|
||||
* main.vue
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-04 10:56
|
||||
* Distributed under terms of the MIT license.
|
||||
-->
|
||||
<template>
|
||||
<q-list>
|
||||
<!-- <q-item-label header> -->
|
||||
<!-- Essential Links -->
|
||||
<!-- </q-item-label> -->
|
||||
|
||||
<template v-for="link in items" :key="link.title">
|
||||
|
||||
<q-item class="flex items-center" v-ripple clickable tag="a" :to="link.to">
|
||||
<q-icon size="1.5rem" class="mr-2" :name="link.icon" />
|
||||
<q-item-section>
|
||||
<q-item-label>{{ link.title }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import menus from './menus'
|
||||
|
||||
const items = computed(() => menus.items.value)
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* menu.ts
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-18 22:58
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
import { Dict } from 'src/models';
|
||||
import { ref } from 'vue';
|
||||
import { RouteLocationRaw } from 'vue-router';
|
||||
import cfg from 'src/cfg'
|
||||
|
||||
export interface MenuLink {
|
||||
title: string;
|
||||
caption?: string;
|
||||
to?: RouteLocationRaw;
|
||||
link?: string;
|
||||
icon?: string;
|
||||
router?: any;
|
||||
}
|
||||
|
||||
const tmp_router = (title: string, icon: string, path: string, com:
|
||||
string) => {
|
||||
let name = 'app.' + path
|
||||
return {
|
||||
title: title,
|
||||
icon: icon,
|
||||
name: name,
|
||||
to: { name: name, params: { id: '' } },
|
||||
router: {
|
||||
path: path,
|
||||
name: name,
|
||||
component: () => import('../pages/app/' + com + '.vue')
|
||||
},
|
||||
}
|
||||
}
|
||||
let uniqueLinks: { [key: string]: [MenuLink] } = {
|
||||
[cfg.id]: [tmp_router('系统信息', 'v-data-view', 'oasys', 'oasys')]
|
||||
}
|
||||
|
||||
const defaultLinks: MenuLink[] = [
|
||||
{
|
||||
title: '应用中心',
|
||||
caption: '',
|
||||
icon: 'v-apps',
|
||||
to: { name: 'home' }
|
||||
},
|
||||
{
|
||||
title: '文件管理',
|
||||
caption: '',
|
||||
icon: 'v-folder',
|
||||
to: { name: 'fs' }
|
||||
},
|
||||
{
|
||||
title: '账号设置',
|
||||
icon: 'v-user',
|
||||
to: { name: 'user' }
|
||||
},
|
||||
{
|
||||
title: '文档中心',
|
||||
icon: 'v-file-exception',
|
||||
to: { name: 'doc' }
|
||||
},
|
||||
{
|
||||
title: '设置',
|
||||
caption: '',
|
||||
icon: 'v-setting',
|
||||
to: { name: 'settings' }
|
||||
},
|
||||
]
|
||||
|
||||
const items = ref(defaultLinks)
|
||||
|
||||
const load_default = () => {
|
||||
items.value = defaultLinks
|
||||
}
|
||||
|
||||
|
||||
const appLinks = ref([
|
||||
tmp_router('', 'v-home', 'home', 'home'),
|
||||
tmp_router('用户管理', 'v-team', 'user', 'user'),
|
||||
tmp_router('权限管理', 'v-key', 'auth', 'auth'),
|
||||
tmp_router('应用设置', 'v-setting', 'cfg', 'cfg'),
|
||||
tmp_router('test', 'v-key', 'test', '../IndexPage'),
|
||||
] as MenuLink[])
|
||||
|
||||
|
||||
export default { items, load_default, appLinks, uniqueLinks }
|
||||
|
@ -0,0 +1,79 @@
|
||||
<!--
|
||||
* oasys.vue
|
||||
* Copyright (C) 2023 veypi <i@veypi.com>
|
||||
* 2023-10-18 23:45
|
||||
* Distributed under terms of the MIT license.
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="data.server?.id">
|
||||
<div class="text-2xl mb-4">
|
||||
消息服务
|
||||
<div class="float-right text-sm">{{ new Date(data.server.time).toLocaleString() }}</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="w-full">ID: {{ data.server?.id }}</div>
|
||||
<div class="flex gap-8">
|
||||
<div>CPU占用: {{ data.statsz.cpu }}%</div>
|
||||
<div>内存占用: {{ (data.statsz.mem / 1024 / 1024).toFixed(2) }}M</div>
|
||||
<div>连接数: {{ data.statsz.connections }}</div>
|
||||
</div>
|
||||
<div>发送: {{ (send_received[1] / 1024).toFixed(2) }} KB/s</div>
|
||||
<div>收到: {{ (send_received[0] / 1024).toFixed(2) }} KB/s</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch, onUnmounted } from 'vue';
|
||||
import { nats } from '@veypi/oaer'
|
||||
|
||||
|
||||
const data = ref({} as any)
|
||||
const id = computed(() => data.value.server?.id)
|
||||
const subs: any[] = []
|
||||
const timer = ref()
|
||||
|
||||
let old_data = [0, 0]
|
||||
const send_received = computed(() => {
|
||||
if (!id.value) {
|
||||
return [0, 0]
|
||||
}
|
||||
let os = data.value.statsz.sent.bytes
|
||||
let or = data.value.statsz.received.bytes
|
||||
let res = [os - old_data[0], or - old_data[1]]
|
||||
old_data = [os, or]
|
||||
return res
|
||||
})
|
||||
|
||||
watch(id, (_) => {
|
||||
timer.value = setInterval(() => {
|
||||
nats.request('$SYS.REQ.SERVER.PING').then((m) => {
|
||||
data.value = JSON.parse(m)
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
watch(computed(() => nats.ready.value), e => {
|
||||
if (e) {
|
||||
nats.request('$SYS.REQ.SERVER.PING').then((m) => {
|
||||
data.value = JSON.parse(m)
|
||||
let os = data.value.statsz.sent.bytes
|
||||
let or = data.value.statsz.received.bytes
|
||||
old_data = [os, or]
|
||||
})
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer.value)
|
||||
for (let i of subs) {
|
||||
i.unsubscribe()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
Loading…
Reference in New Issue