Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
Vue.component("message-media-instance-selector", {
props: ["v-instance-id"],
mounted: function () {
let that = this
Tea.action("/admins/recipients/instances/options")
.post()
.success(function (resp) {
that.instances = resp.data.instances
// 初始化简介
if (that.instanceId > 0) {
let instance = that.instances.$find(function (_, instance) {
return instance.id == that.instanceId
})
if (instance != null) {
that.description = instance.description
that.update(instance.id)
}
}
})
},
data: function () {
let instanceId = this.vInstanceId
if (instanceId == null) {
instanceId = 0
}
return {
instances: [],
description: "",
instanceId: instanceId
}
},
watch: {
instanceId: function (v) {
this.update(v)
}
},
methods: {
update: function (v) {
let instance = this.instances.$find(function (_, instance) {
return instance.id == v
})
if (instance == null) {
this.description = ""
} else {
this.description = instance.description
}
this.$emit("change", instance)
}
},
template: `<div>
<select class="ui dropdown auto-width" name="instanceId" v-model="instanceId">
<option value="0">[选择媒介]</option>
<option v-for="instance in instances" :value="instance.id">{{instance.name}} ({{instance.media.name}})</option>
</select>
<p class="comment" v-html="description"></p>
</div>`
})

View File

@@ -0,0 +1,52 @@
Vue.component("message-media-selector", {
props: ["v-media-type"],
mounted: function () {
let that = this
Tea.action("/admins/recipients/mediaOptions")
.post()
.success(function (resp) {
that.medias = resp.data.medias
// 初始化简介
if (that.mediaType.length > 0) {
let media = that.medias.$find(function (_, media) {
return media.type == that.mediaType
})
if (media != null) {
that.description = media.description
}
}
})
},
data: function () {
let mediaType = this.vMediaType
if (mediaType == null) {
mediaType = ""
}
return {
medias: [],
description: "",
mediaType: mediaType
}
},
watch: {
mediaType: function (v) {
let media = this.medias.$find(function (_, media) {
return media.type == v
})
if (media == null) {
this.description = ""
} else {
this.description = media.description
}
this.$emit("change", media)
},
},
template: `<div>
<select class="ui dropdown auto-width" name="mediaType" v-model="mediaType">
<option value="">[选择媒介类型]</option>
<option v-for="media in medias" :value="media.type">{{media.name}}</option>
</select>
<p class="comment" v-html="description"></p>
</div>`
})

View File

@@ -0,0 +1,58 @@
// 消息接收人设置
Vue.component("message-receivers-box", {
props: ["v-node-cluster-id"],
mounted: function () {
let that = this
Tea.action("/clusters/cluster/settings/message/selectedReceivers")
.params({
clusterId: this.clusterId
})
.post()
.success(function (resp) {
that.receivers = resp.data.receivers
})
},
data: function () {
let clusterId = this.vNodeClusterId
if (clusterId == null) {
clusterId = 0
}
return {
clusterId: clusterId,
receivers: []
}
},
methods: {
addReceiver: function () {
let that = this
let recipientIdStrings = []
let groupIdStrings = []
this.receivers.forEach(function (v) {
if (v.type == "recipient") {
recipientIdStrings.push(v.id.toString())
} else if (v.type == "group") {
groupIdStrings.push(v.id.toString())
}
})
teaweb.popup("/clusters/cluster/settings/message/selectReceiverPopup?recipientIds=" + recipientIdStrings.join(",") + "&groupIds=" + groupIdStrings.join(","), {
callback: function (resp) {
that.receivers.push(resp.data)
}
})
},
removeReceiver: function (index) {
this.receivers.$remove(index)
}
},
template: `<div>
<input type="hidden" name="receiversJSON" :value="JSON.stringify(receivers)"/>
<div v-if="receivers.length > 0">
<div v-for="(receiver, index) in receivers" class="ui label basic small">
<span v-if="receiver.type == 'group'">分组:</span>{{receiver.name}} <span class="grey small" v-if="receiver.subName != null && receiver.subName.length > 0">({{receiver.subName}})</span> &nbsp; <a href="" title="删除" @click.prevent="removeReceiver(index)"><i class="icon remove"></i></a>
</div>
<div class="ui divider"></div>
</div>
<button type="button" class="ui button tiny" @click.prevent="addReceiver">+</button>
</div>`
})

View File

@@ -0,0 +1,56 @@
Vue.component("message-recipient-group-selector", {
props: ["v-groups"],
data: function () {
let groups = this.vGroups
if (groups == null) {
groups = []
}
let groupIds = []
if (groups.length > 0) {
groupIds = groups.map(function (v) {
return v.id.toString()
}).join(",")
}
return {
groups: groups,
groupIds: groupIds
}
},
methods: {
addGroup: function () {
let that = this
teaweb.popup("/admins/recipients/groups/selectPopup?groupIds=" + this.groupIds, {
callback: function (resp) {
that.groups.push(resp.data.group)
that.update()
}
})
},
removeGroup: function (index) {
this.groups.$remove(index)
this.update()
},
update: function () {
let groupIds = []
if (this.groups.length > 0) {
this.groups.forEach(function (v) {
groupIds.push(v.id)
})
}
this.groupIds = groupIds.join(",")
}
},
template: `<div>
<input type="hidden" name="groupIds" :value="groupIds"/>
<div v-if="groups.length > 0">
<div>
<div v-for="(group, index) in groups" class="ui label small basic">
{{group.name}} &nbsp; <a href="" title="删除" @click.prevent="removeGroup(index)"><i class="icon remove"></i></a>
</div>
</div>
<div class="ui divider"></div>
</div>
<button class="ui button tiny" type="button" @click.prevent="addGroup()">+</button>
</div>`
})

View File

@@ -0,0 +1,113 @@
Vue.component("message-row", {
props: ["v-message", "v-can-close"],
data: function () {
let paramsJSON = this.vMessage.params
let params = null
if (paramsJSON != null && paramsJSON.length > 0) {
params = JSON.parse(paramsJSON)
}
return {
message: this.vMessage,
params: params,
isClosing: false
}
},
methods: {
viewCert: function (certId) {
teaweb.popup("/servers/certs/certPopup?certId=" + certId, {
height: "28em",
width: "48em"
})
},
readMessage: function (messageId) {
let that = this
Tea.action("/messages/readPage")
.params({"messageIds": [messageId]})
.post()
.success(function () {
// 刷新父级页面Badge
if (window.parent.Tea != null && window.parent.Tea.Vue != null) {
window.parent.Tea.Vue.checkMessagesOnce()
}
// 刷新当前页面
if (that.vCanClose && typeof (NotifyPopup) != "undefined") {
that.isClosing = true
setTimeout(function () {
NotifyPopup({})
}, 1000)
} else {
teaweb.reload()
}
})
}
},
template: `<div>
<table class="ui table selectable" v-if="!isClosing">
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
<td style="position: relative">
<strong>{{message.datetime}}</strong>
<span v-if="message.cluster != null && message.cluster.id != null">
<span> | </span>
<a :href="'/clusters/cluster?clusterId=' + message.cluster.id" target="_top" v-if="message.role == 'node'">集群:{{message.cluster.name}}</a>
<a :href="'/ns/clusters/cluster?clusterId=' + message.cluster.id" target="_top" v-if="message.role == 'dns'">DNS集群{{message.cluster.name}}</a>
</span>
<span v-if="message.node != null && message.node.id != null">
<span> | </span>
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id" target="_top" v-if="message.role == 'node'">节点:{{message.node.name}}</a>
<a :href="'/ns/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id" target="_top" v-if="message.role == 'dns'">DNS节点{{message.node.name}}</a>
</span>
<a href="" style="position: absolute; right: 1em" @click.prevent="readMessage(message.id)" title="标为已读"><i class="icon check"></i></a>
</td>
</tr>
<tr :class="{error: message.level == 'error', positive: message.level == 'success', warning: message.level == 'warning'}">
<td>
<pre style="padding: 0; margin:0; word-break: break-all;">{{message.body}}</pre>
<!-- 健康检查 -->
<div v-if="message.type == 'HealthCheckFailed'" style="margin-top: 0.8em">
<a :href="'/clusters/cluster/node?clusterId=' + message.cluster.id + '&nodeId=' + param.node.id" v-for="param in params" class="ui label small basic" style="margin-bottom: 0.5em" target="_top">{{param.node.name}}: {{param.error}}</a>
</div>
<!-- 集群DNS设置 -->
<div v-if="message.type == 'ClusterDNSSyncFailed'" style="margin-top: 0.8em">
<a :href="'/dns/clusters/cluster?clusterId=' + message.cluster.id" target="_top">查看问题 &raquo;</a>
</div>
<!-- 证书即将过期 -->
<div v-if="message.type == 'SSLCertExpiring'" style="margin-top: 0.8em">
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a><span v-if="params != null && params.acmeTaskId > 0"> &nbsp;|&nbsp; <a :href="'/servers/certs/acme'" target="_top">查看任务&raquo;</a></span>
</div>
<!-- 证书续期成功 -->
<div v-if="message.type == 'SSLCertACMETaskSuccess'" style="margin-top: 0.8em">
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> &nbsp;|&nbsp; <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务&raquo;</a>
</div>
<!-- 证书续期失败 -->
<div v-if="message.type == 'SSLCertACMETaskFailed'" style="margin-top: 0.8em">
<a href="" @click.prevent="viewCert(params.certId)" target="_top">查看证书</a> &nbsp;|&nbsp; <a :href="'/servers/certs/acme'" v-if="params != null && params.acmeTaskId > 0" target="_top">查看任务&raquo;</a>
</div>
<!-- 网站域名审核 -->
<div v-if="message.type == 'serverNamesRequireAuditing'" style="margin-top: 0.8em">
<a :href="'/servers/server/settings/serverNames?serverId=' + params.serverId" target="_top">去审核</a></a>
</div>
<!-- 节点调度 -->
<div v-if="message.type == 'NodeSchedule'" style="margin-top: 0.8em">
<a :href="'/clusters/cluster/node/settings/schedule?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id" target="_top">查看调度状态 &raquo;</a>
</div>
<!-- 节点租期结束 -->
<div v-if="message.type == 'NodeOfflineDay'" style="margin-top: 0.8em">
<a :href="'/clusters/cluster/node/detail?clusterId=' + message.cluster.id + '&nodeId=' + message.node.id" target="_top">查看详情 &raquo;</a>
</div>
</td>
</tr>
</table>
<div class="margin"></div>
</div>`
})