fix: resolved all vue-tsc errors

This commit is contained in:
0xJacky 2023-11-29 22:04:30 +08:00
parent d325dd7493
commit ab1adcfa3d
No known key found for this signature in database
GPG key ID: B6E4A6E4A561BAF0
64 changed files with 675 additions and 451 deletions

View file

@ -2,7 +2,7 @@ package analytic
import (
"fmt"
analytic2 "github.com/0xJacky/Nginx-UI/internal/analytic"
"github.com/0xJacky/Nginx-UI/internal/analytic"
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/host"
@ -17,22 +17,6 @@ import (
"github.com/gorilla/websocket"
)
type CPUStat struct {
User float64 `json:"user"`
System float64 `json:"system"`
Idle float64 `json:"idle"`
Total float64 `json:"total"`
}
type Stat struct {
Uptime uint64 `json:"uptime"`
LoadAvg *load.AvgStat `json:"loadavg"`
CPU CPUStat `json:"cpu"`
Memory analytic2.MemStat `json:"memory"`
Disk analytic2.DiskStat `json:"disk"`
Network net.IOCountersStat `json:"network"`
}
func Analytic(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
@ -51,7 +35,7 @@ func Analytic(c *gin.Context) {
var stat Stat
for {
stat.Memory, err = analytic2.GetMemoryStat()
stat.Memory, err = analytic.GetMemoryStat()
if err != nil {
logger.Error(err)
@ -76,7 +60,7 @@ func Analytic(c *gin.Context) {
stat.LoadAvg, _ = load.Avg()
stat.Disk, err = analytic2.GetDiskStat()
stat.Disk, err = analytic.GetDiskStat()
if err != nil {
logger.Error(err)
@ -103,20 +87,24 @@ func Analytic(c *gin.Context) {
}
func GetAnalyticInit(c *gin.Context) {
cpuInfo, _ := cpu.Info()
network, _ := net.IOCounters(false)
memory, err := analytic2.GetMemoryStat()
cpuInfo, err := cpu.Info()
if err != nil {
logger.Error(err)
return
}
diskStat, err := analytic2.GetDiskStat()
network, err := net.IOCounters(false)
if err != nil {
logger.Error(err)
}
memory, err := analytic.GetMemoryStat()
if err != nil {
logger.Error(err)
}
diskStat, err := analytic.GetDiskStat()
if err != nil {
logger.Error(err)
return
}
var _net net.IOCountersStat
@ -132,86 +120,30 @@ func GetAnalyticInit(c *gin.Context) {
hostInfo.Platform = "CentOS"
}
loadAvg, _ := load.Avg()
loadAvg, err := load.Avg()
c.JSON(http.StatusOK, gin.H{
"host": hostInfo,
"cpu": gin.H{
"info": cpuInfo,
"user": analytic2.CpuUserRecord,
"total": analytic2.CpuTotalRecord,
if err != nil {
logger.Error(err)
}
c.JSON(http.StatusOK, InitResp{
Host: hostInfo,
CPU: CPURecords{
Info: cpuInfo,
User: analytic.CpuUserRecord,
Total: analytic.CpuTotalRecord,
},
"network": gin.H{
"init": _net,
"bytesRecv": analytic2.NetRecvRecord,
"bytesSent": analytic2.NetSentRecord,
Network: NetworkRecords{
Init: _net,
BytesRecv: analytic.NetRecvRecord,
BytesSent: analytic.NetSentRecord,
},
"disk_io": gin.H{
"writes": analytic2.DiskWriteRecord,
"reads": analytic2.DiskReadRecord,
DiskIO: DiskIORecords{
Writes: analytic.DiskWriteRecord,
Reads: analytic.DiskReadRecord,
},
"memory": memory,
"disk": diskStat,
"loadavg": loadAvg,
Memory: memory,
Disk: diskStat,
LoadAvg: loadAvg,
})
}
func GetNodeStat(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error(err)
return
}
defer ws.Close()
for {
// write
err = ws.WriteJSON(analytic2.GetNodeStat())
if err != nil || websocket.IsUnexpectedCloseError(err,
websocket.CloseGoingAway,
websocket.CloseNoStatusReceived,
websocket.CloseNormalClosure) {
logger.Error(err)
break
}
time.Sleep(10 * time.Second)
}
}
func GetNodesAnalytic(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error(err)
return
}
defer ws.Close()
for {
// write
err = ws.WriteJSON(analytic2.NodeMap)
if err != nil || websocket.IsUnexpectedCloseError(err,
websocket.CloseGoingAway,
websocket.CloseNoStatusReceived,
websocket.CloseNormalClosure) {
logger.Error(err)
break
}
time.Sleep(10 * time.Second)
}
}

70
api/analytic/nodes.go Normal file
View file

@ -0,0 +1,70 @@
package analytic
import (
"github.com/0xJacky/Nginx-UI/internal/analytic"
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"net/http"
"time"
)
func GetNodeStat(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error(err)
return
}
defer ws.Close()
for {
// write
err = ws.WriteJSON(analytic.GetNodeStat())
if err != nil || websocket.IsUnexpectedCloseError(err,
websocket.CloseGoingAway,
websocket.CloseNoStatusReceived,
websocket.CloseNormalClosure) {
logger.Error(err)
break
}
time.Sleep(10 * time.Second)
}
}
func GetNodesAnalytic(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// upgrade http to websocket
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
logger.Error(err)
return
}
defer ws.Close()
for {
// write
err = ws.WriteJSON(analytic.NodeMap)
if err != nil || websocket.IsUnexpectedCloseError(err,
websocket.CloseGoingAway,
websocket.CloseNoStatusReceived,
websocket.CloseNormalClosure) {
logger.Error(err)
break
}
time.Sleep(10 * time.Second)
}
}

52
api/analytic/type.go Normal file
View file

@ -0,0 +1,52 @@
package analytic
import (
"github.com/0xJacky/Nginx-UI/internal/analytic"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/net"
)
type CPUStat struct {
User float64 `json:"user"`
System float64 `json:"system"`
Idle float64 `json:"idle"`
Total float64 `json:"total"`
}
type Stat struct {
Uptime uint64 `json:"uptime"`
LoadAvg *load.AvgStat `json:"loadavg"`
CPU CPUStat `json:"cpu"`
Memory analytic.MemStat `json:"memory"`
Disk analytic.DiskStat `json:"disk"`
Network net.IOCountersStat `json:"network"`
}
type CPURecords struct {
Info []cpu.InfoStat `json:"info"`
User []analytic.Usage[float64] `json:"user"`
Total []analytic.Usage[float64] `json:"total"`
}
type NetworkRecords struct {
Init net.IOCountersStat `json:"init"`
BytesRecv []analytic.Usage[uint64] `json:"bytesRecv"`
BytesSent []analytic.Usage[uint64] `json:"bytesSent"`
}
type DiskIORecords struct {
Writes []analytic.Usage[uint64] `json:"writes"`
Reads []analytic.Usage[uint64] `json:"reads"`
}
type InitResp struct {
Host *host.InfoStat `json:"host"`
CPU CPURecords `json:"cpu"`
Network NetworkRecords `json:"network"`
DiskIO DiskIORecords `json:"disk_io"`
Memory analytic.MemStat `json:"memory"`
Disk analytic.DiskStat `json:"disk"`
LoadAvg *load.AvgStat `json:"loadavg"`
}

View file

@ -3,7 +3,7 @@ package certificate
import (
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/cert/dns"
model2 "github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/gin-gonic/gin"
"github.com/spf13/cast"
@ -21,7 +21,7 @@ func GetDnsCredential(c *gin.Context) {
return
}
type apiDnsCredential struct {
model2.Model
model.Model
Name string `json:"name"`
dns.Config
}
@ -35,7 +35,7 @@ func GetDnsCredential(c *gin.Context) {
func GetDnsCredentialList(c *gin.Context) {
d := query.DnsCredential
provider := c.Query("provider")
var data []*model2.DnsCredential
var data []*model.DnsCredential
var err error
if provider != "" {
data, err = d.Where(d.Provider.Eq(provider)).Find()
@ -65,7 +65,7 @@ func AddDnsCredential(c *gin.Context) {
}
json.Config.Name = json.Provider
dnsCredential := model2.DnsCredential{
dnsCredential := model.DnsCredential{
Name: json.Name,
Config: &json.Config,
Provider: json.Provider,
@ -99,7 +99,7 @@ func EditDnsCredential(c *gin.Context) {
}
json.Config.Name = json.Provider
_, err = d.Where(d.ID.Eq(dnsCredential.ID)).Updates(&model2.DnsCredential{
_, err = d.Where(d.ID.Eq(dnsCredential.ID)).Updates(&model.DnsCredential{
Name: json.Name,
Config: &json.Config,
Provider: json.Provider,

View file

@ -1,9 +1,120 @@
import http from '@/lib/http'
import ws from '@/lib/websocket'
export interface CPUInfoStat {
cpu: number
vendorId: string
family: string
model: string
stepping: number
physicalId: string
coreId: string
cores: number
modelName: string
mhz: number
cacheSize: number
flags: string[]
microcode: string
}
export interface IOCountersStat {
name: string
bytesSent: number
bytesRecv: number
packetsSent: number
packetsRecv: number
errin: number
errout: number
dropin: number
dropout: number
fifoin: number
fifoout: number
}
export interface HostInfoStat {
hostname: string
uptime: number
bootTime: number
procs: number
os: string
platform: string
platformFamily: string
platformVersion: string
kernelVersion: string
kernelArch: string
virtualizationSystem: string
virtualizationRole: string
hostId: string
}
export interface MemStat {
total: string
used: string
cached: string
free: string
swap_used: string
swap_total: string
swap_cached: string
swap_percent: number
pressure: number
}
export interface DiskStat {
total: string
used: string
percentage: number
writes: Usage
reads: Usage
}
export interface LoadStat {
load1: number
load5: number
load15: number
}
export interface Usage {
x: string
y: number
}
export interface CPURecords {
info: CPUInfoStat[]
user: Usage[]
total: Usage[]
}
export interface NetworkRecords {
init: IOCountersStat
bytesRecv: Usage[]
bytesSent: Usage[]
}
export interface DiskIORecords {
writes: Usage[]
reads: Usage[]
}
export interface AnalyticInit {
host: HostInfoStat
cpu: CPURecords
network: NetworkRecords
disk_io: DiskIORecords
disk: DiskStat
memory: MemStat
loadavg: LoadStat
}
const analytic = {
init() {
init(): Promise<AnalyticInit> {
return http.get('/analytic/init')
},
server() {
return ws('/api/analytic')
},
nodes() {
return ws('/api/analytic/nodes')
},
}
export default analytic

View file

@ -16,7 +16,7 @@ const auth = {
login(r.token)
})
},
async casdoor_login(code: string, state: string) {
async casdoor_login(code?: string, state?: string) {
await http.post('/casdoor_callback', {
code,
state,

View file

@ -1,8 +1,9 @@
import http from '@/lib/http'
export interface DNSProvider {
name: string
name?: string
code: string
provider?: string
configuration: {
credentials: {
[key: string]: string
@ -11,11 +12,15 @@ export interface DNSProvider {
[key: string]: string
}
}
links: {
links?: {
api: string
go_client: string
}
}
export interface DnsChallenge extends DNSProvider {
dns_credential_id: number
challenge_method: string
}
const auto_cert = {
get_dns_providers(): Promise<DNSProvider[]> {

View file

@ -5,8 +5,15 @@ export interface Environment extends ModelBase {
name: string
url: string
token: string
status?: boolean
}
export interface Node {
id: number
name: string
token: string
response_at?: Date
}
const environment: Curd<Environment> = new Curd('/environment')
export default environment

View file

@ -3,10 +3,10 @@ import http from '@/lib/http'
import type { NgxServer } from '@/api/ngx'
export interface Variable {
type: string
name: { [key: string]: string }
type?: string
name?: { [key: string]: string }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any
value?: any
}
export interface Template extends NgxServer {

View file

@ -1,5 +1,14 @@
import http from '@/lib/http'
export interface RuntimeInfo {
name: string
os: string
arch: string
ex_path: string
body: string
published_at: string
}
const upgrade = {
get_latest_release(channel: string) {
return http.get('/upgrade/release', {

View file

@ -7,7 +7,7 @@ import type { Series } from '@/components/Chart/types'
const { series, centerText, colors, name, bottomText }
= defineProps<{
series: Series[]
series: Series[] | number[]
centerText?: string
colors?: string
name?: string

View file

@ -1,4 +1,6 @@
import type {Usage} from '@/api/analytic'
export interface Series {
name: string
data: []
data: Usage[]
}

View file

@ -1,5 +1,7 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import type { Ref } from 'vue'
import type { Environment } from '@/api/environment'
import environment from '@/api/environment'
const props = defineProps<{
@ -12,13 +14,13 @@ const emit = defineEmits(['update:target', 'update:map'])
const { $gettext } = useGettext()
const data = ref([])
const data_map = ref({})
const data = ref([]) as Ref<Environment[]>
const data_map = ref({}) as Ref<Record<number, Environment>>
environment.get_list().then(r => {
data.value = r.data
r.data.forEach(node => {
data_map[node.id] = node
data_map.value[node.id] = node
})
})
@ -30,7 +32,7 @@ const value = computed({
if (typeof props.map === 'object') {
v.forEach(id => {
if (id !== 0)
emit('update:map', { ...props.map, [id]: data_map[id].name })
emit('update:map', { ...props.map, [id]: data_map.value[id].name })
})
}
emit('update:target', v)

View file

@ -1,6 +1,5 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import type { Ref } from 'vue'
import Breadcrumb from '@/components/Breadcrumb/Breadcrumb.vue'
const route = useRoute()
@ -9,10 +8,8 @@ const display = computed(() => {
return !route.meta.hiddenHeaderContent
})
const name = ref(route.name) as Ref<() => string>
watch(() => route.name, () => {
name.value = route.name as () => string
const name = computed(() => {
return (route.name as never as () => string)()
})
</script>
@ -27,7 +24,7 @@ watch(() => route.name, () => {
<div class="main">
<div class="row">
<h1 class="title">
{{ name() }}
{{ name }}
</h1>
<div class="action">
<slot name="action" />

View file

@ -28,7 +28,7 @@ watch(current, v => {
settings.set_language(v)
gettext.current = v
const name = route.name as () => string
const name = route.name as never as () => string
document.title = `${name()} | Nginx UI`
})

View file

@ -15,7 +15,7 @@ export interface StdCurdProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onClickEdit?: (id: number | string, record: any, index: number) => void
// eslint-disable-next-line @typescript-eslint/no-explicit-any
beforeSave?: (data: any) => void
beforeSave?: (data: any) => Promise<void>
}
const props = defineProps<StdTableProps & StdCurdProps>()
@ -24,14 +24,16 @@ const { $gettext } = gettext
const visible = ref(false)
const update = ref(0)
const data = reactive({ id: null })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = reactive({ id: null })
provide('data', data)
const error = reactive({})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error: any = reactive({})
const selected = ref([])
function onSelect(keys) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function onSelect(keys: any) {
selected.value = keys
}
@ -86,7 +88,7 @@ function cancel() {
clear_error()
}
function edit(id) {
function edit(id: number | string) {
props.api!.get(id).then(async r => {
Object.keys(data).forEach(k => {
delete data[k]

View file

@ -2,7 +2,7 @@
import { message } from 'ant-design-vue'
import { HolderOutlined } from '@ant-design/icons-vue'
import { useGettext } from 'vue3-gettext'
import type { ComputedRef } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import type { SorterResult } from 'ant-design-vue/lib/table/interface'
import StdPagination from './StdPagination.vue'
import StdDataEntry from '@/components/StdDesign/StdDataEntry'
@ -46,9 +46,11 @@ const props = withDefaults(defineProps<StdTableProps>(), {
const emit = defineEmits(['onSelected', 'onSelectedRecord', 'clickEdit', 'update:selectedRowKeys', 'clickBatchModify'])
const { $gettext } = useGettext()
const route = useRoute()
const dataSource = ref([])
const expandKeysList = ref([])
const rowsKeyIndexMap = ref({})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dataSource: Ref<any[]> = ref([])
const expandKeysList: Ref<number[]> = ref([])
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const rowsKeyIndexMap: Ref<Record<number, any>> = ref({})
const loading = ref(true)
// This can be useful if there are more than one StdTable in the same page.
@ -78,7 +80,7 @@ const selectedRowKeysBuffer = computed({
})
const searchColumns = computed(() => {
const _searchColumns = []
const _searchColumns: Column[] = []
props.columns?.forEach(column => {
if (column.search)
@ -101,7 +103,7 @@ const pithyColumns = computed(() => {
}) as ComputedRef<Column[]>
const batchColumns = computed(() => {
const batch = []
const batch: Column[] = []
props.columns?.forEach(column => {
if (column.batch)
@ -125,7 +127,7 @@ defineExpose({
get_list,
})
function destroy(id) {
function destroy(id: number | string) {
props.api!.destroy(id).then(() => {
get_list()
message.success($gettext('Deleted successfully'))
@ -155,9 +157,11 @@ function get_list(page_num = null, page_size = 20) {
message.error(e?.message ?? $gettext('Server error'))
})
}
function buildIndexMap(data, level: number = 0, index: number = 0, total: number[] = []) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function buildIndexMap(data: any, level: number = 0, index: number = 0, total: number[] = []) {
if (data && data.length > 0) {
data.forEach(v => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data.forEach((v: any) => {
v.level = level
const current_indexes = [...total, index++]
@ -168,7 +172,7 @@ function buildIndexMap(data, level: number = 0, index: number = 0, total: number
})
}
}
function orderPaginationChange(_pagination: Pagination, filters, sorter: SorterResult) {
function orderPaginationChange(_pagination: Pagination, filters: never, sorter: SorterResult) {
if (sorter) {
selectedRowKeysBuffer.value = []
params.order_by = sorter.field
@ -189,19 +193,19 @@ function orderPaginationChange(_pagination: Pagination, filters, sorter: SorterR
selectedRowKeysBuffer.value = []
}
function expandedTable(keys) {
function expandedTable(keys: number[]) {
expandKeysList.value = keys
}
const crossPageSelect = {}
const crossPageSelect: Record<string, number[]> = {}
async function onSelectChange(_selectedRowKeys) {
async function onSelectChange(_selectedRowKeys: number[]) {
const page = params.page || 1
crossPageSelect[page] = await _selectedRowKeys
crossPageSelect[page] = _selectedRowKeys
let t = []
Object.keys(crossPageSelect).forEach(v => {
let t: number[] = []
Object.keys(crossPageSelect).forEach((v: string) => {
t.push(...crossPageSelect[v])
})
@ -215,8 +219,8 @@ async function onSelectChange(_selectedRowKeys) {
selectedRowKeysBuffer.value = Array.from(set)
emit('onSelected', selectedRowKeysBuffer.value)
}
function onSelect(record) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function onSelect(record: any) {
emit('onSelectedRecord', record)
}

View file

@ -9,7 +9,7 @@ import type { StdTableProps } from '@/components/StdDesign/StdDataDisplay/StdTab
const { $gettext } = gettext
async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[]>) {
const header: { title?: string; key: string }[] = []
const header: { title?: string; key: string | string[] }[] = []
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const headerKeys: any[] = []
const showColumnsMap: Record<string, Column> = {}
@ -24,8 +24,8 @@ async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[
title: t,
key: column.dataIndex,
})
headerKeys.push(column.dataIndex)
showColumnsMap[column.dataIndex] = column
headerKeys.push(column.dataIndex.toString())
showColumnsMap[column.dataIndex.toString()] = column
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -59,7 +59,7 @@ async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[
const c = showColumnsMap[key]
_data = c?.customRender?.({ text: _data }) ?? _data
obj[c.dataIndex] = _data
_.set(obj, c.dataIndex, _data)
})
data.push(obj)
})

View file

@ -8,12 +8,12 @@ const props = defineProps<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
dataSource: Record<string, any>
errors?: Record<string, string>
layout?: 'horizontal' | 'vertical'
layout?: 'horizontal' | 'vertical' | 'inline'
}>()
const emit = defineEmits<{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
'update:dataSource': (v: any[]) => void
'update:dataSource': [data: Record<string, any>]
}>()
const dataSource = computed({

View file

@ -7,7 +7,7 @@ const props = defineProps<Props>()
const { $gettext } = useGettext()
export interface Props {
dataIndex?: string
dataIndex?: string | string[]
label?: string
extra?: string
error?: {
@ -16,7 +16,7 @@ export interface Props {
}
const tag = computed(() => {
return props.error?.[props.dataIndex] ?? ''
return props.error?.[props.dataIndex!.toString()] ?? ''
})
const valid_status = computed(() => {

View file

@ -4,8 +4,8 @@ import { useGettext } from 'vue3-gettext'
const props = defineProps<{
value: string
generate: boolean
placeholder: string
generate?: boolean
placeholder?: string
}>()
const emit = defineEmits(['update:value'])

View file

@ -6,17 +6,16 @@ import type { Column } from '@/components/StdDesign/types'
const props = defineProps<{
selectedKey: string | number
value: string | number
value?: string | number
recordValueIndex: string
selectionType: 'radio' | 'checkbox'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
api: Curd<any>
columns: Column[]
dataKey: string
disableSearch: boolean
disableSearch?: boolean
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getParams: any
description: string
description?: string
}>()
const emit = defineEmits(['update:selectedKey', 'changeSelect'])
@ -29,8 +28,8 @@ onMounted(() => {
})
const selected = ref([])
const record = reactive({})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const record: any = reactive({})
function init() {
if (props.selectedKey && !props.value && props.selectionType === 'radio') {
@ -113,12 +112,11 @@ const _selectedKey = computed({
<StdTable
:api="api"
:columns="columns"
:data_key="dataKey"
:disable_search="disableSearch"
:disable-search="disableSearch"
pithy
:get_params="getParams"
:get-params="getParams"
:selection-type="selectionType"
disable_query_params
disable-query-params
@on-selected="onSelect"
@on-selected-record="onSelectedRecord"
/>

View file

@ -52,6 +52,7 @@ function textarea(edit: StdDesignEdit, dataSource: any, dataIndex: any) {
function password(edit: StdDesignEdit, dataSource: any, dataIndex: any) {
return <StdPassword
v-model:value={dataSource[dataIndex]}
value={dataSource[dataIndex]}
generate={edit.config?.generate}
placeholder={placeholder_helper(edit)}
/>
@ -60,19 +61,21 @@ function password(edit: StdDesignEdit, dataSource: any, dataIndex: any) {
function select(edit: StdDesignEdit, dataSource: any, dataIndex: any) {
return <StdSelect
v-model:value={dataSource[dataIndex]}
mask={edit.mask}
value={dataSource[dataIndex]}
mask={edit.mask as Record<string, () => string>}
/>
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function selector(edit: StdDesignEdit, dataSource: any, dataIndex: any) {
return <StdSelector
v-model:selectedKey={dataSource[dataIndex]}
selectedKey={dataSource[dataIndex]}
recordValueIndex={edit.selector?.recordValueIndex}
selectionType={edit.selector?.selectionType}
api={edit.selector?.api}
columns={edit.selector?.columns}
disableSearch={edit.selector?.disable_search}
getParams={edit.selector?.get_params}
disableSearch={edit.selector?.disableSearch}
getParams={edit.selector?.getParams}
description={edit.selector?.description}
/>
}

View file

@ -8,20 +8,18 @@ export interface StdDesignEdit {
batch?: boolean // batch edit
mask?: {
[key: string]: () => string
} // use for select-option
mask?: Record<string, () => string> // use for select-option
rules?: [] // validator rules
selector?: {
get_params?: {}
getParams?: {}
recordValueIndex: any // relative to api return
selectionType: any
api: Curd,
valueApi?: Curd,
columns: any
disable_search?: boolean
disableSearch?: boolean
description?: string
bind?: any
itemKey?: any // default is id
@ -52,7 +50,7 @@ export interface Flex {
export interface Column {
title?: string | (() => string);
dataIndex: string;
dataIndex: string | string[];
edit?: StdDesignEdit;
customRender?: function;
extra?: string | (() => string);

View file

@ -9,7 +9,7 @@ import NginxControl from '@/components/NginxControl/NginxControl.vue'
import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue'
const emit = defineEmits<{
clickUnFold: () => void
clickUnFold: [void]
}>()
const { $gettext } = gettext

View file

@ -3,6 +3,7 @@ import { useRoute } from 'vue-router'
import type { ComputedRef } from 'vue'
import { computed, ref, watch } from 'vue'
import type { AntdIconType } from '@ant-design/icons-vue/lib/components/AntdIcon'
import type { IconComponentProps } from '@ant-design/icons-vue/es/components/Icon'
import Logo from '@/components/Logo/Logo.vue'
import { routes } from '@/routes'
import EnvIndicator from '@/components/EnvIndicator/EnvIndicator.vue'
@ -56,7 +57,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
const t: sidebar = {
path: s.path,
name: s.name,
meta: s.meta as meta,
meta: s.meta as unknown as meta,
children: [],
};
@ -64,7 +65,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
if (c.meta && c.meta.hiddenInSidebar)
return
t.children.push((c as sidebar))
t.children.push((c as unknown as sidebar))
})
res.push(t)
})
@ -80,7 +81,6 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
<AMenu
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKey"
:open-keys="openKeys"
mode="inline"
>
<EnvIndicator />
@ -91,7 +91,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
:key="s.name"
@click="$router.push(`/${s.path}`).catch(() => {})"
>
<component :is="s.meta.icon" />
<Component :is="s.meta.icon as IconComponentProps" />
<span>{{ s.name() }}</span>
</AMenuItem>
@ -100,7 +100,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
:key="s.path"
>
<template #title>
<component :is="s.meta.icon" />
<Component :is="s.meta.icon as IconComponentProps" />
<span>{{ s.name() }}</span>
</template>
<AMenuItem

View file

@ -1,4 +1,5 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import type { AntDesignOutlinedIconType } from '@ant-design/icons-vue/lib/icons/AntDesignOutlined'
import {
CloudOutlined,
@ -13,7 +14,6 @@ import {
UserOutlined,
} from '@ant-design/icons-vue'
import NProgress from 'nprogress'
import type { AntDesignOutlinedIconType } from '@ant-design/icons-vue/lib/icons/AntDesignOutlined'
import gettext from '../gettext'
import { useUserStore } from '@/pinia'

View file

@ -1 +1 @@
{"version":"2.0.0-beta.4","build_id":56,"total_build":260}
{"version":"2.0.0-beta.4","build_id":62,"total_build":266}

View file

@ -9,13 +9,14 @@ import cert from '@/api/cert'
import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
import CertInfo from '@/views/domain/cert/CertInfo.vue'
import type { Column } from '@/components/StdDesign/types'
const { $gettext, interpolate } = useGettext()
const { $gettext } = useGettext()
const columns = [{
const columns: Column[] = [{
title: () => $gettext('Name'),
dataIndex: 'name',
sorter: true,
sortable: true,
pithy: true,
customRender: (args: customRender) => {
const { text, record } = args
@ -31,7 +32,7 @@ const columns = [{
}, {
title: () => $gettext('Config Name'),
dataIndex: 'filename',
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Auto Cert'),
@ -50,7 +51,7 @@ const columns = [{
return h('div', template)
},
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('SSL Certificate Path'),
@ -58,19 +59,19 @@ const columns = [{
edit: {
type: input,
},
display: false,
hidden: true,
}, {
title: () => $gettext('SSL Certificate Key Path'),
dataIndex: 'ssl_certificate_key_path',
edit: {
type: input,
},
display: false,
hidden: true,
}, {
title: () => $gettext('Updated at'),
dataIndex: 'updated_at',
customRender: datetime,
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Action'),
@ -83,7 +84,6 @@ const columns = [{
:title="$gettext('Certification')"
:api="cert"
:columns="columns"
row-key="name"
>
<template #beforeEdit="{ data }">
<template v-if="data.auto_cert === 1">
@ -109,7 +109,7 @@ const columns = [{
style="margin-bottom: 15px"
>
<AAlert
:message="interpolate($gettext('Domains list is empty, try to reopen auto-cert for %{config}'), { config: data.filename })"
:message="$gettext('Domains list is empty, try to reopen auto-cert for %{config}', { config: data.filename })"
type="error"
show-icon
/>

View file

@ -1,12 +1,16 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import type { SelectProps } from 'ant-design-vue'
import type { Ref } from 'vue'
import type { DNSProvider } from '@/api/auto_cert'
import auto_cert from '@/api/auto_cert'
const { $gettext } = useGettext()
const providers = ref([])
const providers = ref([]) as Ref<DNSProvider[]>
const data = inject('data')
// This data is provided by the Top StdCurd component,
// is the object that you are trying to modify it
const data = inject('data') as DNSProvider
const code = computed(() => {
return data.code
@ -14,9 +18,11 @@ const code = computed(() => {
const provider_idx = ref()
function init() {
data.configuration = {
credentials: {},
additional: {},
if (!data.configuration) {
data.configuration = {
credentials: {},
additional: {},
}
}
providers.value?.forEach((v: { code: string }, k: number) => {
if (v.code === code.value)
@ -39,6 +45,7 @@ watch(code, init)
watch(current, () => {
data.code = current.value.code
data.provider = current.value.name
auto_cert.get_dns_provider(current.value.code).then(r => {
Object.assign(current.value, r)
})
@ -47,7 +54,7 @@ watch(current, () => {
const options = computed<SelectProps['options']>(() => {
const list: SelectProps['options'] = []
providers.value.forEach((v: { name: string }, k: number) => {
providers.value.forEach((v: DNSProvider, k: number) => {
list!.push({
value: k,
label: v.name,

View file

@ -5,13 +5,14 @@ import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransfor
import dns_credential from '@/api/dns_credential'
import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
import { input } from '@/components/StdDesign/StdDataEntry'
import type { Column } from '@/components/StdDesign/types'
const { $gettext } = useGettext()
const columns = [{
const columns: Column[] = [{
title: () => $gettext('Name'),
dataIndex: 'name',
sorter: true,
sortable: true,
pithy: true,
edit: {
type: input,
@ -19,13 +20,13 @@ const columns = [{
}, {
title: () => $gettext('Provider'),
dataIndex: ['config', 'name'],
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Updated at'),
dataIndex: 'updated_at',
customRender: datetime,
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Action'),
@ -38,7 +39,6 @@ const columns = [{
:title="$gettext('DNS Credentials')"
:api="dns_credential"
:columns="columns"
row-key="name"
>
<template #beforeEdit>
<AAlert

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { computed, ref } from 'vue'
import { message } from 'ant-design-vue'
import type { Ref } from 'vue'
import { formatDateTime } from '@/lib/helper'
import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
import gettext from '@/gettext'
@ -10,6 +10,7 @@ import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
import ngx from '@/api/ngx'
import InspectConfig from '@/views/config/InspectConfig.vue'
import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
import type { ChatComplicationMessage } from '@/api/openai'
const { $gettext, interpolate } = gettext
const route = useRoute()
@ -25,7 +26,7 @@ const name = computed(() => {
})
const configText = ref('')
const history_chatgpt_record = ref([])
const history_chatgpt_record = ref([]) as Ref<ChatComplicationMessage[]>
const file_path = ref('')
const active_key = ref(['1', '2'])
const modified_at = ref('')
@ -52,7 +53,7 @@ init()
function save() {
config.save(name.value, { content: configText.value }).then(r => {
configText.value = r.config
configText.value = r.content
message.success($gettext('Saved successfully'))
}).catch(r => {
message.error(interpolate($gettext('Save error %{msg}'), { msg: r.message ?? '' }))

View file

@ -1,23 +1,23 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import Icon, { LinkOutlined, SendOutlined, ThunderboltOutlined } from '@ant-design/icons-vue'
import type ReconnectingWebSocket from 'reconnecting-websocket'
import type { Ref } from 'vue'
import { useSettingsStore } from '@/pinia'
import type { Node } from '@/api/environment'
import environment from '@/api/environment'
import logo from '@/assets/img/logo.png'
import pulse from '@/assets/svg/pulse.svg'
import { formatDateTime } from '@/lib/helper'
import ws from '@/lib/websocket'
import NodeAnalyticItem from '@/views/dashboard/components/NodeAnalyticItem.vue'
import analytic from '@/api/analytic'
const settingsStore = useSettingsStore()
const { $gettext } = useGettext()
const data = ref([])
const data = ref([]) as Ref<Node[]>
const node_map = computed(() => {
const o = {}
const o = {} as Record<number, Node>
data.value.forEach(v => {
o[v.id] = v
@ -32,16 +32,19 @@ onMounted(() => {
environment.get_list().then(r => {
data.value = r.data
})
websocket = ws('/api/analytic/nodes')
websocket.onmessage = m => {
websocket = analytic.nodes()
websocket.onmessage = async m => {
const nodes = JSON.parse(m.data)
for (const key in nodes) {
Object.keys(nodes).forEach((v: string) => {
const key = Number.parseInt(v)
// update node online status
if (node_map.value[key]) {
Object.assign(node_map.value[key], nodes[key])
node_map.value[key].response_at = new Date()
}
}
})
}
})
@ -49,13 +52,7 @@ onUnmounted(() => {
websocket.close()
})
export interface Node {
id: number
name: string
token: string
}
const { environment: env } = settingsStore
const { environment: env } = useSettingsStore()
function link_start(node: Node) {
env.id = node.id

View file

@ -3,9 +3,10 @@ import { useGettext } from 'vue3-gettext'
import type ReconnectingWebSocket from 'reconnecting-websocket'
import AreaChart from '@/components/Chart/AreaChart.vue'
import RadialBarChart from '@/components/Chart/RadialBarChart.vue'
import type { CPUInfoStat, DiskStat, HostInfoStat, LoadStat, MemStat } from '@/api/analytic'
import analytic from '@/api/analytic'
import ws from '@/lib/websocket'
import { bytesToSize } from '@/lib/helper'
import type { Series } from '@/components/Chart/types'
const { $gettext } = useGettext()
@ -17,32 +18,42 @@ const host = reactive({
os: '',
kernelVersion: '',
kernelArch: '',
})
}) as HostInfoStat
const cpu = ref('0.0')
const cpu_info = reactive([])
const cpu_analytic_series = reactive([{ name: 'User', data: [] }, { name: 'Total', data: [] }])
const cpu_info = reactive([]) as CPUInfoStat[]
const cpu_analytic_series = reactive([{ name: 'User', data: [] }, { name: 'Total', data: [] }]) as Series[]
const net_analytic = reactive([{ name: $gettext('Receive'), data: [] },
{ name: $gettext('Send'), data: [] }])
{ name: $gettext('Send'), data: [] }]) as Series[]
const disk_io_analytic = reactive([{ name: $gettext('Writes'), data: [] },
{ name: $gettext('Reads'), data: [] }])
{ name: $gettext('Reads'), data: [] }]) as Series[]
const memory = reactive({ swap_used: '', swap_percent: '', swap_total: '' })
const disk = reactive({ percentage: '', used: '' })
const memory = reactive({
total: '',
used: '',
cached: '',
free: '',
swap_used: '',
swap_cached: '',
swap_percent: 0,
swap_total: '',
pressure: 0,
}) as MemStat
const disk = reactive({ percentage: 0, used: '', total: '', writes: { x: '', y: 0 }, reads: { x: '', y: 0 } }) as DiskStat
const disk_io = reactive({ writes: 0, reads: 0 })
const uptime = ref('')
const loadavg = reactive({ load1: 0, load5: 0, load15: 0 })
const loadavg = reactive({ load1: 0, load5: 0, load15: 0 }) as LoadStat
const net = reactive({ recv: 0, sent: 0, last_recv: 0, last_sent: 0 })
const net_formatter = (bytes: number) => {
return `${bytesToSize(bytes)}/s`
}
interface Usage {
x: number
y: number
const cpu_formatter = (usage: number) => {
return usage.toFixed(2)
}
onMounted(() => {
@ -60,22 +71,15 @@ onMounted(() => {
net.last_recv = r.network.init.bytesRecv
net.last_sent = r.network.init.bytesSent
r.cpu.user.forEach((u: Usage) => {
cpu_analytic_series[0].data.push([u.x, u.y.toFixed(2)])
})
r.cpu.total.forEach((u: Usage) => {
cpu_analytic_series[1].data.push([u.x, u.y.toFixed(2)])
})
r.network.bytesRecv.forEach((u: Usage) => {
net_analytic[0].data.push([u.x, u.y.toFixed(2)])
})
r.network.bytesSent.forEach((u: Usage) => {
net_analytic[1].data.push([u.x, u.y.toFixed(2)])
})
cpu_analytic_series[0].data = cpu_analytic_series[0].data.concat(r.cpu.user)
cpu_analytic_series[1].data = cpu_analytic_series[1].data.concat(r.cpu.total)
net_analytic[0].data = net_analytic[0].data.concat(r.network.bytesRecv)
net_analytic[1].data = net_analytic[1].data.concat(r.network.bytesSent)
disk_io_analytic[0].data = disk_io_analytic[0].data.concat(r.disk_io.writes)
disk_io_analytic[1].data = disk_io_analytic[1].data.concat(r.disk_io.reads)
websocket = ws('/api/analytic')
websocket = analytic.server()
websocket.onmessage = wsOnMessage
})
})
@ -97,17 +101,17 @@ function handle_uptime(t: number) {
uptime.value = `${uptime_days}d ${uptime_hours}h ${Math.floor(_uptime / 60)}m`
}
function wsOnMessage(m) {
function wsOnMessage(m: MessageEvent) {
const r = JSON.parse(m.data)
const cpu_usage = r.cpu.system + r.cpu.user
const cpu_usage = Math.min(r.cpu.system + r.cpu.user, 100)
cpu.value = cpu_usage.toFixed(2)
const time = new Date().getTime()
const time = new Date().toLocaleString()
cpu_analytic_series[0].data.push([time, r.cpu.user.toFixed(2)])
cpu_analytic_series[1].data.push([time, cpu.value])
cpu_analytic_series[0].data.push({ x: time, y: r.cpu.user.toFixed(2) })
cpu_analytic_series[1].data.push({ x: time, y: cpu_usage })
if (cpu_analytic_series[0].data.length > 100) {
cpu_analytic_series[0].data.shift()
@ -135,8 +139,8 @@ function wsOnMessage(m) {
net.last_recv = r.network.bytesRecv
net.last_sent = r.network.bytesSent
net_analytic[0].data.push([time, net.recv])
net_analytic[1].data.push([time, net.sent])
net_analytic[0].data.push({ x: time, y: net.recv })
net_analytic[1].data.push({ x: time, y: net.sent })
if (net_analytic[0].data.length > 100) {
net_analytic[0].data.shift()
@ -302,6 +306,7 @@ function wsOnMessage(m) {
</AStatistic>
<AreaChart
:series="cpu_analytic_series"
:y-formatter="cpu_formatter"
:max="100"
/>
</ACard>

View file

@ -67,10 +67,10 @@ function create_another() {
const has_server_name = computed(() => {
const servers = ngx_config.servers
for (const server_key in servers) {
for (const k in servers[server_key].directives) {
const v = servers[server_key].directives[k]
if (v.directive === 'server_name' && v.params.trim() !== '')
for (const server of Object.values(servers)) {
for (const directive of Object.values(server.directives!)) {
if (directive.directive === 'server_name' && directive.params.trim() !== '')
return true
}
}

View file

@ -2,15 +2,19 @@
import { useGettext } from 'vue3-gettext'
import { useRoute, useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import type { Ref } from 'vue'
import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
import NgxConfigEditor from '@/views/domain/ngx_conf/NgxConfigEditor.vue'
import type { Site } from '@/api/domain'
import domain from '@/api/domain'
import type { NgxConfig } from '@/api/ngx'
import ngx from '@/api/ngx'
import config from '@/api/config'
import RightSettings from '@/views/domain/components/RightSettings.vue'
import type { CertificateInfo } from '@/api/cert'
import type { ChatComplicationMessage } from '@/api/openai'
const { $gettext, interpolate } = useGettext()
@ -29,7 +33,7 @@ const ngx_config: NgxConfig = reactive({
servers: [],
})
const cert_info_map = reactive({})
const cert_info_map: Record<string, CertificateInfo> = reactive({})
const auto_cert = ref(false)
const enabled = ref(false)
@ -52,16 +56,16 @@ const advance_mode = computed({
},
})
const history_chatgpt_record = ref([])
const history_chatgpt_record = ref([]) as Ref<ChatComplicationMessage[]>
function handle_response(r) {
function handle_response(r: Site) {
if (r.advanced)
advance_mode.value = true
if (r.advanced)
advance_mode.value = true
Object.keys(cert_info_map).forEach(v => {
Object.keys(cert_info_map).forEach((v: string) => {
delete cert_info_map[v]
})
parse_error_status.value = false
@ -87,13 +91,13 @@ function init() {
}
}
function handle_parse_error(e) {
function handle_parse_error(e: { error?: string; message: string }) {
console.error(e)
if (e?.error === 'nginx_config_syntax_error') {
parse_error_status.value = true
parse_error_message.value = e.message
config.get(`sites-available/${name.value}`).then(r => {
configText.value = r.config
configText.value = r.content
})
}
else {
@ -117,7 +121,7 @@ function on_mode_change(advanced: boolean) {
})
}
function build_config() {
async function build_config() {
return ngx.build_config(ngx_config).then(r => {
configText.value = r.content
})
@ -316,10 +320,6 @@ provide('data', data)
opacity: 0;
}
.location-block {
}
.directive-params-wrapper {
margin: 10px 0;
}

View file

@ -8,13 +8,14 @@ import domain from '@/api/domain'
import { input } from '@/components/StdDesign/StdDataEntry'
import SiteDuplicate from '@/views/domain/components/SiteDuplicate.vue'
import InspectConfig from '@/views/config/InspectConfig.vue'
import type { Column } from '@/components/StdDesign/types'
const { $gettext } = useGettext()
const columns = [{
const columns: Column[] = [{
title: () => $gettext('Name'),
dataIndex: 'name',
sorter: true,
sortable: true,
pithy: true,
edit: {
type: input,
@ -37,13 +38,13 @@ const columns = [{
return h('div', template)
},
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Updated at'),
dataIndex: 'modified_at',
customRender: datetime,
sorter: true,
sortable: true,
pithy: true,
}, {
title: () => $gettext('Action'),
@ -52,7 +53,7 @@ const columns = [{
const table = ref()
function enable(name) {
function enable(name: string) {
domain.enable(name).then(() => {
message.success($gettext('Enabled successfully'))
table.value?.get_list()
@ -61,7 +62,7 @@ function enable(name) {
})
}
function disable(name) {
function disable(name: string) {
domain.disable(name).then(() => {
message.success($gettext('Disabled successfully'))
table.value?.get_list()
@ -70,7 +71,7 @@ function disable(name) {
})
}
function destroy(site_name) {
function destroy(site_name: string) {
domain.destroy(site_name).then(() => {
table.value.get_list()
message.success($gettext('Delete site: %{site_name}', { site_name }))

View file

@ -37,7 +37,7 @@ const now = computed(() => new Date().toISOString())
</template>
<template v-else>
<CheckCircleOutlined class="text-green-500" />
<span class="ml-2">{{ $gettext('Certificate is valid<') }}</span>
<span class="ml-2">{{ $gettext('Certificate is valid') }}</span>
</template>
</div>
</div>

View file

@ -1,23 +1,26 @@
<script setup lang="tsx">
import { useGettext } from 'vue3-gettext'
import { Badge } from 'ant-design-vue'
import type { ComputedRef, Ref } from 'vue'
import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
import type { Cert } from '@/api/cert'
import cert from '@/api/cert'
import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
import { input } from '@/components/StdDesign/StdDataEntry'
import type { NgxDirective } from '@/api/ngx'
import type { Column } from '@/components/StdDesign/types'
const { $gettext } = useGettext()
const current_server_directives = inject('current_server_directives')
const directivesMap = inject('directivesMap') as Record<string, NgxDirective[]>
const current_server_directives = inject('current_server_directives') as ComputedRef<NgxDirective[]>
const directivesMap = inject('directivesMap') as Ref<Record<string, NgxDirective[]>>
const visible = ref(false)
const record = ref({})
const record = ref({}) as Ref<Cert>
const columns = [{
const columns: Column[] = [{
title: () => $gettext('Name'),
dataIndex: 'name',
sorter: true,
sortable: true,
pithy: true,
customRender: (args: customRender) => {
const { text, record: r } = args
@ -47,7 +50,7 @@ const columns = [{
return h('div', template)
},
sorter: true,
sortable: true,
pithy: true,
}]
@ -55,13 +58,13 @@ function open() {
visible.value = true
}
function onSelectedRecord(r) {
function onSelectedRecord(r: Cert) {
record.value = r
}
function ok() {
if (directivesMap.ssl_certificate?.[0]) {
directivesMap.ssl_certificate[0].params = record.value.ssl_certificate_path
if (directivesMap.value.ssl_certificate?.[0]) {
directivesMap.value.ssl_certificate[0].params = record.value.ssl_certificate_path
}
else {
current_server_directives?.value.push({
@ -69,8 +72,8 @@ function ok() {
params: record.value.ssl_certificate_path,
})
}
if (directivesMap.ssl_certificate_key?.[0]) {
directivesMap.ssl_certificate_key[0].params = record.value.ssl_certificate_key_path
if (directivesMap.value.ssl_certificate_key?.[0]) {
directivesMap.value.ssl_certificate_key[0].params = record.value.ssl_certificate_key_path
}
else {
current_server_directives?.value.push({

View file

@ -3,16 +3,19 @@ import { useGettext } from 'vue3-gettext'
import ObtainCert from '@/views/domain/cert/components/ObtainCert.vue'
import type { NgxDirective } from '@/api/ngx'
const props = defineProps<{
export interface Props {
enabled: boolean
}>()
configName: string
}
const props = defineProps<Props>()
const emit = defineEmits(['callback', 'update:enabled'])
const { $gettext } = useGettext()
const issuing_cert = ref(false)
const obtain_cert = ref()
const directivesMap = inject('directivesMap') as Record<string, NgxDirective[]>
const directivesMap = inject('directivesMap') as Ref<Record<string, NgxDirective[]>>
const enabled = computed({
get() {
@ -24,10 +27,10 @@ const enabled = computed({
})
const no_server_name = computed(() => {
if (!directivesMap.server_name)
if (!directivesMap.value.server_name)
return true
return directivesMap.server_name.length === 0
return directivesMap.value.server_name.length === 0
})
provide('no_server_name', no_server_name)

View file

@ -1,11 +1,13 @@
<script setup lang="ts">
import { inject } from 'vue'
import { useGettext } from 'vue3-gettext'
import type { DnsChallenge } from '@/api/auto_cert'
import DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
const { $gettext } = useGettext()
const no_server_name = inject('no_server_name')
const data = inject('data')
// Provide by ObtainCert.vue
const data = inject('data') as DnsChallenge
</script>
<template>
@ -30,28 +32,23 @@ const data = inject('data')
:message="$gettext('Note')"
>
<template #description>
<p v-translate>
The server_name
in the current configuration must be the domain name you need to get the certificate, support
multiple domains.
<p>
{{ $gettext('The server_name'
+ 'in the current configuration must be the domain name you need to get the certificate, support'
+ 'multiple domains.') }}
</p>
<p v-translate>
The certificate for the domain will be checked every hour,
and will be renewed if it has been more than 1 week since it was last issued.
<p>
{{ $gettext('The certificate for the domain will be checked 5 minutes,'
+ 'and will be renewed if it has been more than 1 week since it was last issued.') }}
</p>
<p
v-if="data.challenge_method === 'http01'"
v-translate
>
Make sure you have configured a reverse proxy for .well-known
directory to HTTPChallengePort before obtaining the certificate.
<p v-if="data.challenge_method === 'http01'">
{{ $gettext('Make sure you have configured a reverse proxy for .well-known '
+ 'directory to HTTPChallengePort before obtaining the certificate.') }}
</p>
<p
v-else-if="data.challenge_method === 'dns01'"
v-translate
>
Please first add credentials in Certification > DNS Credentials, and then select one of the credentials
below to request the API of the DNS provider.
<p v-else-if="data.challenge_method === 'dns01'">
{{ $gettext('Please first add credentials in Certification > DNS Credentials, '
+ 'and then select one of the credentials'
+ 'below to request the API of the DNS provider.') }}
</p>
</template>
</AAlert>

View file

@ -1,14 +1,20 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import type { SelectProps } from 'ant-design-vue'
import type { Ref } from 'vue'
import type { DNSProvider } from '@/api/auto_cert'
import auto_cert from '@/api/auto_cert'
import dns_credential from '@/api/dns_credential'
const { $gettext } = useGettext()
const providers = ref([])
const credentials = ref([])
const providers = ref([]) as Ref<DNSProvider[]>
const credentials = ref<SelectProps['options']>([])
const data = inject('data')
// This data is provided by the Top StdCurd component,
// is the object that you are trying to modify it
// we externalize the dns_credential_id to the parent component,
// this is used to tell the backend which dns_credential to use
const data = inject('data') as DNSProvider & { dns_credential_id: number | null }
const code = computed(() => {
return data.code
@ -16,7 +22,7 @@ const code = computed(() => {
const provider_idx = ref()
function init() {
providers.value?.forEach((v, k: number) => {
providers.value?.forEach((v: DNSProvider, k: number) => {
if (v.code === code.value)
provider_idx.value = k
})
@ -42,7 +48,7 @@ watch(current, () => {
dns_credential.get_list({ provider: data.provider }).then(r => {
r.data.forEach(v => {
credentials.value.push({
credentials.value?.push({
value: v.id,
label: v.name,
})

View file

@ -2,12 +2,14 @@
import { useGettext } from 'vue3-gettext'
import Modal from 'ant-design-vue/lib/modal'
import { message } from 'ant-design-vue'
import type { Ref } from 'vue'
import websocket from '@/lib/websocket'
import template from '@/api/template'
import domain from '@/api/domain'
import AutoCertStepOne from '@/views/domain/cert/components/AutoCertStepOne.vue'
import type DNSChallenge from '@/views/domain/cert/components/DNSChallenge.vue'
import type { NgxDirective } from '@/api/ngx'
import type { NgxConfig, NgxDirective } from '@/api/ngx'
import type { Props } from '@/views/domain/cert/IssueCert.vue'
import type { DnsChallenge } from '@/api/auto_cert'
const emit = defineEmits(['update:auto_cert'])
@ -15,14 +17,14 @@ const { $gettext, interpolate } = useGettext()
const modalVisible = ref(false)
const step = ref(1)
const directivesMap = inject('directivesMap') as Record<string, NgxDirective[]>
const directivesMap = inject('directivesMap') as Ref<Record<string, NgxDirective[]>>
const progressStrokeColor = {
from: '#108ee9',
to: '#87d068',
}
const data: DNSChallenge = reactive({
const data: DnsChallenge = reactive({
dns_credential_id: 0,
challenge_method: 'http01',
code: '',
@ -40,15 +42,15 @@ provide('data', data)
const logContainer = ref()
const save_site_config = inject('save_site_config')
const no_server_name = inject('no_server_name')
const props = inject('props')
const issuing_cert = inject('issuing_cert')
const ngx_config = inject('ngx_config')
const current_server_directives = inject('current_server_directives')
const save_site_config = inject('save_site_config') as () => Promise<void>
const no_server_name = inject('no_server_name') as Ref<boolean>
const props = inject('props') as Props
const issuing_cert = inject('issuing_cert') as Ref<boolean>
const ngx_config = inject('ngx_config') as NgxConfig
const current_server_directives = inject('current_server_directives') as NgxDirective[]
const name = computed(() => {
return directivesMap.server_name[0].params.trim()
return directivesMap.value.server_name[0].params.trim()
})
const issue_cert = async (config_name: string, server_name: string) => {
@ -101,9 +103,9 @@ const issue_cert = async (config_name: string, server_name: string) => {
}
async function callback(ssl_certificate: string, ssl_certificate_key: string) {
directivesMap.ssl_certificate[0].params = ssl_certificate
directivesMap.ssl_certificate_key[0].params = ssl_certificate_key
save_site_config()
directivesMap.value.ssl_certificate[0].params = ssl_certificate
directivesMap.value.ssl_certificate_key[0].params = ssl_certificate_key
await save_site_config()
}
function change_auto_cert(status: boolean) {
@ -119,7 +121,7 @@ function change_auto_cert(status: boolean) {
})
}
else {
domain.remove_auto_cert(props.config_name).then(() => {
domain.remove_auto_cert(props.configName).then(() => {
message.success(interpolate($gettext('Auto-renewal disabled for %{name}'), { name: name.value }))
}).catch(e => {
message.error(e.message ?? interpolate($gettext('Disable auto-renewal failed for %{name}'), { name: name.value }))
@ -131,23 +133,23 @@ async function onchange(status: boolean) {
if (status) {
await template.get_block('letsencrypt.conf').then(r => {
ngx_config.servers.forEach(async v => {
v.locations = v.locations.filter(l => l.path !== '/.well-known/acme-challenge')
v.locations = v?.locations?.filter(l => l.path !== '/.well-known/acme-challenge')
v.locations.push(...r.locations)
v.locations?.push(...r.locations)
})
}).then(async () => {
// if ssl_certificate is empty, do not save, just use the config from last step.
if (directivesMap.ssl_certificate?.[0])
if (directivesMap.value.ssl_certificate?.[0])
await save_site_config()
job()
})
}
else {
await ngx_config.servers.forEach(v => {
v.locations = v.locations.filter(l => l.path !== '/.well-known/acme-challenge')
ngx_config.servers.forEach(v => {
v.locations = v?.locations?.filter(l => l.path !== '/.well-known/acme-challenge')
})
save_site_config()
await save_site_config()
change_auto_cert(status)
}
@ -165,26 +167,26 @@ function job() {
return
}
const server_name = directivesMap.server_name[0]
const server_name_idx = directivesMap.value.server_name[0]?.idx ?? 0
if (!directivesMap.ssl_certificate) {
current_server_directives.splice(server_name.idx + 1, 0, {
if (!directivesMap.value.ssl_certificate) {
current_server_directives.splice(server_name_idx + 1, 0, {
directive: 'ssl_certificate',
params: '',
})
}
nextTick(() => {
if (!directivesMap.ssl_certificate_key) {
const ssl_certificate = directivesMap.ssl_certificate[0]
if (!directivesMap.value.ssl_certificate_key) {
const ssl_certificate_idx = directivesMap.value.ssl_certificate[0]?.idx ?? 0
current_server_directives.splice(ssl_certificate.idx + 1, 0, {
current_server_directives.splice(ssl_certificate_idx + 1, 0, {
directive: 'ssl_certificate_key',
params: '',
})
}
}).then(() => {
issue_cert(props.config_name, name.value)
issue_cert(props.configName, name.value)
})
}

View file

@ -13,7 +13,7 @@ const node_map = reactive({})
const target = ref([])
const overwrite = ref(false)
const enabled = ref(false)
const name = inject('name')
const name = inject('name') as Ref<string>
function deploy() {
Modal.confirm({

View file

@ -9,14 +9,16 @@ import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
import { formatDateTime } from '@/lib/helper'
import Deploy from '@/views/domain/components/Deploy.vue'
import { useSettingsStore } from '@/pinia'
import type { ChatComplicationMessage } from '@/api/openai'
import type { NgxConfig } from '@/api/ngx'
const settings = useSettingsStore()
const { $gettext } = useGettext()
const configText = inject('configText')
const ngx_config = inject('ngx_config')
const enabled = inject('enabled')
const name = inject('name')
const history_chatgpt_record = inject('history_chatgpt_record')
const configText = inject('configText') as Ref<string>
const ngx_config = inject('ngx_config') as Ref<NgxConfig>
const enabled = inject('enabled') as Ref<boolean>
const name = inject('name') as Ref<string>
const history_chatgpt_record = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
const filename = inject('filename')
const data: Ref<Site> = inject('data') as Ref<Site>

View file

@ -1,5 +1,4 @@
<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { Form, message, notification } from 'ant-design-vue'
import gettext from '@/gettext'
@ -27,7 +26,12 @@ const show = computed({
},
})
const modelRef = reactive({ name: '', target: [] })
interface Model {
name: string // site name
target: number[] // ids of deploy targets
}
const modelRef: Model = reactive({ name: '', target: [] })
const rulesRef = reactive({
name: [
@ -49,7 +53,7 @@ const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesR
const loading = ref(false)
const node_map = reactive({})
const node_map: Record<number, string> = reactive({})
function onSubmit() {
validate().then(async () => {

View file

@ -33,13 +33,13 @@ function add() {
function save() {
adding.value = false
ngx_config.servers[props.currentServerIndex].locations?.push({
ngx_config.servers[props.currentServerIndex!].locations?.push({
...location,
})
}
function remove(index: number) {
ngx_config.servers[props.currentServerIndex].locations?.splice(index, 1)
ngx_config.servers[props.currentServerIndex!].locations?.splice(index, 1)
}
</script>

View file

@ -17,7 +17,7 @@ const errorIdx = ref()
const hasAccessLog = computed(() => {
let flag = false
props.ngxConfig.servers[props.currentServerIdx].directives.forEach((v, k) => {
props.ngxConfig?.servers[props.currentServerIdx].directives?.forEach((v, k) => {
if (v.directive === 'access_log') {
flag = true
accessIdx.value = k
@ -29,7 +29,7 @@ const hasAccessLog = computed(() => {
const hasErrorLog = computed(() => {
let flag = false
props.ngxConfig.servers[props.currentServerIdx].directives.forEach((v, k) => {
props.ngxConfig?.servers[props.currentServerIdx].directives?.forEach((v, k) => {
if (v.directive === 'error_log') {
flag = true
errorIdx.value = k

View file

@ -27,7 +27,7 @@ const emit = defineEmits(['callback', 'update:auto_cert'])
const { $gettext } = useGettext()
const save_site_config = inject('save_site_config')!
const save_site_config = inject('save_site_config') as () => Promise<void>
const route = useRoute()
@ -68,7 +68,7 @@ const current_server_directives = computed(() => {
})
const directivesMap: ComputedRef<Record<string, NgxDirective[]>> = computed(() => {
const map = {}
const map: Record<string, NgxDirective[]> = {}
current_server_directives.value?.forEach((v, k) => {
v.idx = k
@ -81,6 +81,7 @@ const directivesMap: ComputedRef<Record<string, NgxDirective[]>> = computed(() =
return map
})
// eslint-disable-next-line sonarjs/cognitive-complexity
function change_tls(status: boolean) {
if (status) {
// deep copy servers[0] to servers[1]
@ -93,15 +94,15 @@ function change_tls(status: boolean) {
const servers = ngx_config.servers
let i = 0
while (i < servers[1].directives.length) {
const v = servers[1].directives[i]
if (v.directive === 'listen')
servers[1].directives.splice(i, 1)
while (i < (servers?.[1].directives?.length ?? 0)) {
const v = servers?.[1]?.directives?.[i]
if (v?.directive === 'listen')
servers[1]?.directives?.splice(i, 1)
else
i++
}
servers[1].directives.splice(0, 0, {
servers?.[1]?.directives?.splice(0, 0, {
directive: 'listen',
params: '443 ssl',
}, {
@ -112,10 +113,10 @@ function change_tls(status: boolean) {
params: 'on',
})
const server_name = directivesMap.value.server_name[0]
const server_name_idx = directivesMap.value?.server_name?.[0].idx ?? 0
if (!directivesMap.value.ssl_certificate) {
servers[1].directives.splice(server_name.idx + 1, 0, {
servers?.[1]?.directives?.splice(server_name_idx + 1, 0, {
directive: 'ssl_certificate',
params: '',
})
@ -123,7 +124,7 @@ function change_tls(status: boolean) {
setTimeout(() => {
if (!directivesMap.value.ssl_certificate_key) {
servers[1].directives.splice(server_name.idx + 2, 0, {
servers?.[1]?.directives?.splice(server_name_idx + 2, 0, {
directive: 'ssl_certificate_key',
params: '',
})
@ -144,8 +145,8 @@ const support_ssl = computed(() => {
const servers = ngx_config.servers
for (const server_key in servers) {
for (const k in servers[server_key].directives) {
const v = servers[server_key].directives[k]
if (v.directive === 'listen' && v.params.indexOf('ssl') > 0)
const v = servers?.[server_key]?.directives?.[Number.parseInt(k)]
if (v?.directive === 'listen' && v?.params?.indexOf('ssl') > 0)
return true
}
}

View file

@ -52,10 +52,10 @@ async function add() {
ngx_config.custom = ngx_config.custom?.trim()
if (data.value.locations)
ngx_config.servers[props.currentServerIndex].locations.push(...data.value.locations)
ngx_config?.servers?.[props.currentServerIndex]?.locations?.push(...data.value.locations)
if (data.value.directives)
ngx_config.servers[props.currentServerIndex].directives.push(...data.value.directives)
ngx_config?.servers?.[props.currentServerIndex]?.directives?.push(...data.value.directives)
visible.value = false
}

View file

@ -9,9 +9,9 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
'update:data': (data: {
'update:data': [data: {
[key: string]: Variable
}) => void
}]
}>()
const data = computed({
@ -30,7 +30,7 @@ const data = computed({
v-for="(_, k) in data"
:key="k"
v-model:data="data[k]"
:name="k"
:name="k.toString()"
/>
</AForm>
</template>

View file

@ -10,7 +10,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
'update:data': (data: Variable) => void
'update:data': [data: Variable]
}>()
const data = computed({

View file

@ -3,6 +3,7 @@ import { reactive, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { DeleteOutlined } from '@ant-design/icons-vue'
import CodeEditor from '@/components/CodeEditor'
import type { NgxDirective } from '@/api/ngx'
const props = defineProps<{
idx?: number
@ -12,7 +13,7 @@ const emit = defineEmits(['save'])
const { $gettext } = useGettext()
const ngx_directives = inject('ngx_directives')
const ngx_directives = inject('ngx_directives') as NgxDirective[]
const directive = reactive({ directive: '', params: '' })
const adding = ref(false)
const mode = ref('default')

View file

@ -26,7 +26,7 @@ const content = ref('')
function init() {
if (ngx_directives[props.index].directive === 'include') {
config.get(ngx_directives[props.index].params).then(r => {
content.value = r.config
content.value = r.content
})
}
}
@ -35,7 +35,7 @@ watch(props, init)
function save() {
config.save(ngx_directives[props.index].params, { content: content.value }).then(r => {
content.value = r.config
content.value = r.content
message.success($gettext('Saved successfully'))
}).catch(r => {
message.error(interpolate($gettext('Save error %{msg}'), { msg: r.message ?? '' }))

View file

@ -7,13 +7,14 @@ import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransfor
import environment from '@/api/environment'
import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
import { input } from '@/components/StdDesign/StdDataEntry'
import type { Column } from '@/components/StdDesign/types'
const { $gettext } = useGettext()
const columns = [{
const columns: Column[] = [{
title: () => $gettext('Name'),
dataIndex: 'name',
sorter: true,
sortable: true,
pithy: true,
edit: {
type: input,
@ -22,18 +23,20 @@ const columns = [{
{
title: () => $gettext('URL'),
dataIndex: 'url',
sorter: true,
sortable: true,
pithy: true,
edit: {
type: input,
placeholder: () => 'https://10.0.0.1:9000',
config: {
placeholder: () => 'https://10.0.0.1:9000',
},
},
},
{
title: () => 'NodeSecret',
dataIndex: 'token',
sorter: true,
display: false,
sortable: true,
hidden: true,
edit: {
type: input,
},
@ -88,14 +91,14 @@ const columns = [{
return h('div', template)
},
sorter: true,
sortable: true,
pithy: true,
},
{
title: () => $gettext('Updated at'),
dataIndex: 'updated_at',
customRender: datetime,
sorter: true,
sortable: true,
pithy: true,
},
{

View file

@ -1,7 +1,5 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import type { Ref, UnwrapNestedRefs } from 'vue'
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import type ReconnectingWebSocket from 'reconnecting-websocket'
import { useRoute, useRouter } from 'vue-router'
import { debounce } from 'lodash'
@ -11,7 +9,7 @@ import nginx_log from '@/api/nginx_log'
import ws from '@/lib/websocket'
const { $gettext } = useGettext()
const logContainer: Ref<Element> = ref()!
const logContainer = ref()
let websocket: ReconnectingWebSocket | WebSocket
const route = useRoute()
const buffer = ref('')
@ -21,7 +19,7 @@ const router = useRouter()
const loading = ref(false)
const filter = ref('')
const control: UnwrapNestedRefs<INginxLogData> = reactive({
const control: INginxLogData = reactive({
type: logType(),
conf_name: route.query.conf_name as string,
server_idx: Number.parseInt(route.query.server_idx as string),
@ -53,10 +51,8 @@ function addLog(data: string, prepend: boolean = false) {
buffer.value += data
nextTick(() => {
const elem = (logContainer.value as Element)
elem?.scroll({
top: elem.scrollHeight,
logContainer.value?.scroll({
top: logContainer.value.scrollHeight,
left: 0,
})
})

View file

@ -2,6 +2,17 @@
import { useGettext } from 'vue3-gettext'
const { $gettext } = useGettext()
const route = useRoute()
const info = computed(() => {
if (typeof route.meta.error === 'function')
return route.meta.error()
else if (typeof route.meta.error === 'string')
return route.meta.error
else
return $gettext('File Not Found')
})
</script>
<template>
@ -9,7 +20,7 @@ const { $gettext } = useGettext()
<h1 class="title">
{{ $route.meta.status_code || 404 }}
</h1>
<p>{{ $route.meta.error?.() ?? $gettext('File Not Found') }}</p>
<p>{{ info }}</p>
<AButton
type="primary"
@click="$router.push('/')"

View file

@ -95,7 +95,7 @@ const loginWithCasdoor = () => {
if (route.query?.code !== undefined && route.query?.state !== undefined) {
loading.value = true
auth.casdoor_login(route.query.code.toString(), route.query.state.toString()).then(async () => {
auth.casdoor_login(route.query?.code?.toString(), route.query?.state?.toString()).then(async () => {
message.success($gettext('Login successful'), 1)
const next = (route.query?.next || '').toString() || '/'

View file

@ -10,7 +10,7 @@ import ws from '@/lib/websocket'
const { $gettext } = useGettext()
let term: Terminal | null
let ping: NodeJS.Timer | null
let ping: number
const websocket = ws('/api/pty')
@ -83,9 +83,9 @@ function wsOnOpen() {
onUnmounted(() => {
window.removeEventListener('resize', fit)
clearInterval(ping as number)
clearInterval(ping)
term?.dispose()
ping = null
ping = 0
websocket.close()
})

View file

@ -8,22 +8,13 @@ import { message } from 'ant-design-vue'
import { useRoute } from 'vue-router'
import websocket from '@/lib/websocket'
import version from '@/version.json'
import type { RuntimeInfo } from '@/api/upgrade'
import upgrade from '@/api/upgrade'
const { $gettext } = useGettext()
const route = useRoute()
interface APIData {
name: string
os: string
arch: string
ex_path: string
body: string
published_at: string
}
const data: Ref<APIData> = ref({})
const data = ref({}) as Ref<RuntimeInfo>
const last_check = ref('')
const loading = ref(false)
const channel = ref('stable')
@ -65,16 +56,16 @@ const is_latest_ver = computed(() => {
return data.value.name === `v${version.version}`
})
const logContainer = ref(null)
const logContainer = ref()
function log(msg: string) {
const para = document.createElement('p')
para.appendChild(document.createTextNode($gettext(msg)));
para.appendChild(document.createTextNode($gettext(msg)))
(logContainer.value as Element).appendChild(para);
logContainer.value.appendChild(para)
(logContainer.value as Element).scroll({ top: 320, left: 0, behavior: 'smooth' })
logContainer.value.scroll({ top: 320, left: 0, behavior: 'smooth' })
}
const dry_run = computed(() => {
@ -85,8 +76,8 @@ async function perform_upgrade() {
progressStatus.value = 'active'
modalClosable.value = false
modalVisible.value = true
progressPercent.value = 0;
(logContainer.value as Element).innerHTML = ''
progressPercent.value = 0
logContainer.value.innerHTML = ''
log($gettext('Upgrading Nginx UI, please wait...'))

View file

@ -1 +1 @@
{"version":"2.0.0-beta.4","build_id":56,"total_build":260}
{"version":"2.0.0-beta.4","build_id":62,"total_build":266}

View file

@ -6,18 +6,18 @@ import (
"time"
)
type Usage struct {
Time time.Time `json:"x"`
Usage interface{} `json:"y"`
type Usage[T uint64 | float64] struct {
Time time.Time `json:"x"`
Usage T `json:"y"`
}
var (
CpuUserRecord []Usage
CpuTotalRecord []Usage
NetRecvRecord []Usage
NetSentRecord []Usage
DiskWriteRecord []Usage
DiskReadRecord []Usage
CpuUserRecord []Usage[float64]
CpuTotalRecord []Usage[float64]
NetRecvRecord []Usage[uint64]
NetSentRecord []Usage[uint64]
DiskWriteRecord []Usage[uint64]
DiskReadRecord []Usage[uint64]
LastDiskWrites uint64
LastDiskReads uint64
LastNetSent uint64
@ -37,9 +37,10 @@ func init() {
now := time.Now()
// init record slices
for i := 100; i > 0; i-- {
u := Usage{Time: now.Add(time.Duration(-i) * time.Second), Usage: 0}
CpuUserRecord = append(CpuUserRecord, u)
CpuTotalRecord = append(CpuTotalRecord, u)
uf := Usage[float64]{Time: now.Add(time.Duration(-i) * time.Second), Usage: 0}
CpuUserRecord = append(CpuUserRecord, uf)
CpuTotalRecord = append(CpuTotalRecord, uf)
u := Usage[uint64]{Time: now.Add(time.Duration(-i) * time.Second), Usage: 0}
NetRecvRecord = append(NetRecvRecord, u)
NetSentRecord = append(NetSentRecord, u)
DiskWriteRecord = append(DiskWriteRecord, u)

View file

@ -41,14 +41,14 @@ func recordCpu(now time.Time) {
cpuSystemUsage := (cpuTimesAfter[0].System - cpuTimesBefore[0].System) / (float64(1000*threadNum) / 1000)
cpuSystemUsage *= 100
u := Usage{
u := Usage[float64]{
Time: now,
Usage: cpuUserUsage,
}
CpuUserRecord = append(CpuUserRecord, u)
s := Usage{
s := Usage[float64]{
Time: now,
Usage: cpuUserUsage + cpuSystemUsage,
}
@ -75,11 +75,11 @@ func recordNetwork(now time.Time) {
if len(network) == 0 {
return
}
NetRecvRecord = append(NetRecvRecord, Usage{
NetRecvRecord = append(NetRecvRecord, Usage[uint64]{
Time: now,
Usage: network[0].BytesRecv - LastNetRecv,
})
NetSentRecord = append(NetSentRecord, Usage{
NetSentRecord = append(NetSentRecord, Usage[uint64]{
Time: now,
Usage: network[0].BytesSent - LastNetSent,
})
@ -96,11 +96,11 @@ func recordNetwork(now time.Time) {
func recordDiskIO(now time.Time) {
readCount, writeCount := getTotalDiskIO()
DiskReadRecord = append(DiskReadRecord, Usage{
DiskReadRecord = append(DiskReadRecord, Usage[uint64]{
Time: now,
Usage: readCount - LastDiskReads,
})
DiskWriteRecord = append(DiskWriteRecord, Usage{
DiskWriteRecord = append(DiskWriteRecord, Usage[uint64]{
Time: now,
Usage: writeCount - LastDiskWrites,
})

View file

@ -23,11 +23,11 @@ type MemStat struct {
}
type DiskStat struct {
Total string `json:"total"`
Used string `json:"used"`
Percentage float64 `json:"percentage"`
Writes Usage `json:"writes"`
Reads Usage `json:"reads"`
Total string `json:"total"`
Used string `json:"used"`
Percentage float64 `json:"percentage"`
Writes Usage[uint64] `json:"writes"`
Reads Usage[uint64] `json:"reads"`
}
func GetMemoryStat() (MemStat, error) {