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