Files
jh-monitor/src/actions/stats.ts
2025-10-14 12:34:12 +08:00

310 lines
7.2 KiB
TypeScript

'use server'
import { Page, Res } from '@/lib/api'
import drizzle, { and, change, cityhash, count, desc, edge, eq, gateway, is, sql, token } from '@/lib/drizzle'
export type AllocationStatus = {
city: string
count: number
assigned: number
}
// 城市分配状态
export async function getAllocationStatus(hours: number = 24) {
try {
const c1 = drizzle
.select({
cityId: edge.cityId,
count: count().as('count'),
})
.from(edge)
.where(eq(edge.active, 1))
.groupBy(edge.cityId)
.as('c1')
const c2 = drizzle
.select({
cityId: change.city,
assigned: count().as('assigned'),
})
.from(change)
.where(sql`time > NOW() - INTERVAL ${hours} HOUR`)
.groupBy(change.city)
.as('c2')
const result = await drizzle
.select({
city: cityhash.city,
count: sql<number>`c1.count`,
assigned: sql<number>`ifnull(c2.assigned, 0)`,
})
.from(cityhash)
.leftJoin(c1, eq(c1.cityId, cityhash.id))
.leftJoin(c2, eq(c2.cityId, cityhash.id))
.where(sql`cityhash.macaddr is not null`)
return {
success: true,
data: result,
}
}
catch (error) {
console.error('Allocation status query error:', error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
return {
success: false,
data: [],
error: '查询分配状态失败: ' + errorMessage,
}
}
}
export type GatewayInfo = {
macaddr: string
inner_ip: string
setid: number
enable: number
}
// 获取网关基本信息
export async function getGatewayInfo() {
try {
const result = await drizzle
.select({
macaddr: token.macaddr,
inner_ip: token.innerIp,
setid: token.setid,
enable: token.enable,
})
.from(token)
.orderBy(sql`cast(regexp_replace(token.inner_ip, '192.168.50.', '') as unsigned)`)
return {
success: true,
data: result,
}
}
catch (error) {
console.error('Gateway info query error:', error)
return {
success: false,
data: [],
error: '查询网关信息失败',
}
}
}
export type GatewayConfig = {
city: string | null
edge: string
user: string
public: string | null
inner_ip: string
ischange: number
isonline: number
}
// 网关配置
export async function getGatewayConfig(page?: number, filters?: {
mac?: string
public?: string
city?: string
user?: string
inner_ip?: string
}): Promise<Res<Page<GatewayConfig>>> {
try {
if (!page && !filters?.mac) {
throw new Error('页码和MAC地址不能同时为空')
}
page = filters?.mac ? 1 : Math.max(1, page || 1)
const [total, result] = await Promise.all([
drizzle.$count(gateway, filters?.mac ? eq(gateway.macaddr, filters?.mac) : undefined),
drizzle
.select({
city: cityhash.city,
edge: gateway.edge,
user: gateway.user,
public: edge.public,
inner_ip: gateway.network,
ischange: gateway.ischange,
isonline: gateway.isonline,
})
.from(gateway)
.leftJoin(cityhash, eq(cityhash.hash, gateway.cityhash))
.leftJoin(edge, eq(edge.macaddr, gateway.edge))
.where(filters ? and(
filters.mac ? eq(gateway.macaddr, filters.mac) : undefined,
filters.public ? eq(edge.public, filters.public) : undefined,
filters.city ? eq(cityhash.city, filters.city) : undefined,
filters.user ? eq(gateway.user, filters.user) : undefined,
filters.inner_ip ? eq(gateway.network, filters.inner_ip) : undefined,
) : undefined)
.orderBy(sql`inet_aton(gateway.inner_ip)`)
.offset((page - 1) * 250)
.limit(250),
])
return {
success: true,
data: {
total,
page,
size: 250,
items: result,
},
}
}
catch (error) {
console.error('Gateway config query error:', error)
return {
success: false,
error: '查询网关配置失败',
}
}
}
export type CityNode = {
city: string
count: number
hash: string
label: string | null
offset: number
}
// 城市节点数量分布
export async function getCityNodeCount() {
try {
const e = drizzle
.select({
cityId: edge.cityId,
count: count().as('count'),
})
.from(edge)
.where(eq(edge.active, 1))
.groupBy(edge.cityId)
.as('e')
const result = await drizzle
.select({
city: cityhash.city,
hash: cityhash.hash,
label: cityhash.label,
count: sql<number>`ifnull(e.count, 0)`,
offset: cityhash.offset,
})
.from(cityhash)
.leftJoin(e, eq(e.cityId, cityhash.id))
.groupBy(cityhash.hash)
.orderBy(desc(sql`e.count`))
return {
success: true,
data: result,
}
}
catch (error) {
console.error('City node count query error:', error)
return {
success: false,
data: [],
error: '查询城市节点失败',
}
}
}
export type Edge = {
id: number
macaddr: string
city: string | null
public: string
isp: string
single: number | boolean
sole: number | boolean
arch: number
online: number
}
// 获取节点信息
export async function getEdgeNodes(page: number, size: number, filters?: {
macaddr?: string
public?: string
city?: string
isp?: string
}): Promise<Res<Page<Edge>>> {
try {
page = Math.max(1, page)
size = Math.min(100, Math.max(10, size))
const condition = and(
eq(edge.active, 1),
filters?.macaddr ? eq(edge.macaddr, filters.macaddr) : undefined,
filters?.public ? eq(edge.public, filters.public) : undefined,
filters?.city ? eq(cityhash.city, filters.city) : undefined,
filters?.isp ? eq(edge.isp, filters.isp) : undefined,
)
console.log(drizzle
.select({
id: edge.id,
macaddr: edge.macaddr,
city: cityhash.city,
public: edge.public,
isp: edge.isp,
single: edge.single,
sole: edge.sole,
arch: edge.arch,
online: edge.online,
})
.from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(condition)
.orderBy(edge.id)
.offset(page * size - size)
.limit(size).toSQL().sql)
const [total, items] = await Promise.all([
drizzle
.select({ value: count() })
.from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(condition),
drizzle
.select({
id: edge.id,
macaddr: edge.macaddr,
city: cityhash.city,
public: edge.public,
isp: edge.isp,
single: edge.single,
sole: edge.sole,
arch: edge.arch,
online: edge.online,
})
.from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(condition)
.orderBy(edge.id)
.offset(page * size - size)
.limit(size),
])
return {
success: true,
data: {
total: total[0].value,
page,
size,
items,
},
}
}
catch (error) {
console.error('Edge nodes query error:', error)
return {
success: false,
error: '查询边缘节点失败',
}
}
}