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,41 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="node.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">缓存硬盘容量</td>
<td>
<size-capacity-box :v-value="node.maxCacheDiskCapacity" :v-name="'maxCacheDiskCapacityJSON'"></size-capacity-box>
<p class="comment">缓存所在硬盘的最大用量超出此用量或硬盘接近用尽时将会自动尝试清理旧数据0表示不限制。</p>
</td>
</tr>
<tr>
<td>硬盘缓存主目录</td>
<td>
<input type="text" name="cacheDiskDir" maxlength="500" value="" v-model="node.cacheDiskDir"/>
<p class="comment">存放文件缓存的主目录,通常填写绝对路径。不填则表示使用集群缓存策略中定义的目录。</p>
</td>
</tr>
<tr>
<td>其他硬盘缓存目录</td>
<td>
<node-cache-disk-dirs-box name="cacheDiskSubDirsJSON" v-model="node.cacheDiskSubDirs"></node-cache-disk-dirs-box>
<p class="comment">除了主目录外,可以在这里添加别的可用于缓存的目录;缓存目录有变更时(添加、删除或修改)需要手动将这些缓存目录清空。</p>
</td>
</tr>
<tr>
<td>缓存内存容量</td>
<td>
<size-capacity-box :v-value="node.maxCacheMemoryCapacity" :v-name="'maxCacheMemoryCapacityJSON'"></size-capacity-box>
<p class="comment">缓存能使用的内存的最大容量0表示不限制。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,17 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="nodeId"/>
<node-ddos-protection-config-box :v-ddos-protection-config="config" :v-default-configs="defaultConfigs" :v-is-node="true" :v-cluster-is-on="clusterDDoSProtectionIsOn"></node-ddos-protection-config-box>
<p class="ui message green" v-if="checkResult != null && checkResult.isOk">节点检查结果:{{checkResult.message}}</p>
<p class="ui message red" v-if="checkResult != null && !checkResult.isOk">节点检查结果:有错误发生:{{checkResult.message}}</p>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,15 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.checkResult = null
this.$post(".status")
.params({
nodeId: this.nodeId
})
.success(function (resp) {
let results = resp.data.results
if (results.length > 0) {
this.checkResult = results[0]
}
})
})

View File

@@ -0,0 +1,26 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="node.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">DNS线路</td>
<td>
<div v-if="allDNSRoutes.length > 0">
<dns-route-selector :v-all-routes="allDNSRoutes" :v-routes="dnsRoutes"></dns-route-selector>
<p class="comment">当前节点对应的DNS线路可用线路是根据集群设置的域名获取的注意DNS服务商可能对这些线路有其他限制。</p>
</div>
<div v-else>
<span class="disabled">暂时没有可选的线路。</span>
</div>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,35 @@
{$layout "layout_popup"}
<h3>添加调度动作</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="nodeId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">执行条件 *</td>
<td>
<node-schedule-conds-box :v-params="params" :v-operators="operators" @changeparam="changeParam"></node-schedule-conds-box>
</td>
</tr>
<tr>
<td>执行动作 *</td>
<td>
<node-schedule-action-box :v-actions="actions"></node-schedule-action-box>
</td>
</tr>
<tr v-show="param != null && param.hasDuration">
<td>持续时间 *</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="3" style="width: 4em" value="60" name="durationMinutes"/>
<span class="ui label">分钟</span>
</div>
<p class="comment">表示当前动作执行后多久自动还原为原始状态。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,9 @@
Tea.context(function () {
this.param = null
this.changeParam = function (param) {
if (param != null) {
this.param = param
}
}
})

View File

@@ -0,0 +1,39 @@
{$layout "layout_popup"}
<h3>修改调度动作</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="actionId" :value="action.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">执行条件 *</td>
<td>
<node-schedule-conds-box :v-params="params" :v-operators="operators" v-model="action.conds" @changeparam="changeParam"></node-schedule-conds-box>
</td>
</tr>
<tr>
<td>执行动作 *</td>
<td>
<node-schedule-action-box :v-actions="actions" v-model="action.action"></node-schedule-action-box>
</td>
</tr>
<tr v-show="param != null && param.hasDuration">
<td>持续时间 *</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="3" style="width: 4em" value="60" name="durationMinutes" v-model="action.durationMinutes"/>
<span class="ui label">分钟</span>
</div>
<p class="comment">表示当前动作执行后多久自动还原为原始状态。</p>
</td>
</tr>
<tr>
<td>启用当前动作</td>
<td><checkbox name="isOn" v-model="action.isOn"></checkbox></td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,18 @@
Tea.context(function () {
this.param = null
if (this.action.conds.conds != null) {
let that = this
this.action.conds.conds.forEach(function (cond) {
that.param = that.params.$find(function (k, v) {
return v.code == cond.param
})
})
}
this.changeParam = function (param) {
if (param != null) {
this.param = param
}
}
})

View File

@@ -0,0 +1,160 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<h4>基础设置 &nbsp; <a href="" @click.prevent="editSchedule" style="font-size: 0.8em"><span v-if="!scheduleIsEditing">[修改]</span><span v-else>[取消修改]</span></a> </h4>
<form class="ui form">
<table class="ui table definition selectable" v-show="!scheduleIsEditing">
<tr>
<td class="title">当前节点租期结束日期</td>
<td>
<span v-if="schedule.offlineDay.length > 0">{{schedule.offlineDay}}</span>
<span v-else class="disabled">没有设置</span>
<p class="comment" v-if="schedule.isOffline"><span class="red">已到期</span></p>
<p class="comment" v-else-if="schedule.isNearlyOffline"><span class="red">即将到期</span></p>
</td>
</tr>
<tr>
<td>当前节点为集群备用节点</td>
<td>
<span v-if="schedule.isBackupForCluster" class="green">Y</span>
<span v-else class="disabled">N</span>
</td>
</tr>
<tr>
<td>当前节点为分组备用节点</td>
<td>
<span v-if="schedule.isBackupForGroup" class="green">Y</span>
<span v-else class="disabled">N</span>
</td>
</tr>
<tr>
<td>当前节点备用IP</td>
<td>
<div v-if="schedule.backupIPs != null && schedule.backupIPs.length > 0">
<span v-for="backupIP in schedule.backupIPs" class="ui basic tiny label">{{backupIP}}</span>
</div>
</td>
</tr>
<tr>
<td>当前触发动作</td>
<td>
<div v-if="schedule.actionStatus != null && schedule.actionStatus.actionId > 0">
<div class="ui fields inline">
<div class="ui field">
<node-schedule-conds-viewer :v-params="params" :v-operators="operators" v-model="schedule.actionStatus.conds"></node-schedule-conds-viewer>
</div>
<div class="ui field"><span class="red">-&gt;</span></div>
<div class="ui field">
<span class="red">{{actionMap[schedule.actionStatus.action.code].name}}</span>
</div>
<div class="ui field" v-if="schedule.actionStatusExpiresTime.length > 0">
<span class="red">| &nbsp; &nbsp; 有效期至{{schedule.actionStatusExpiresTime}}</span>
</div>
<div class="ui field">
<a href="" @click.prevent="resetActionStatus">[重置]</a>
</div>
</div>
</div>
<div v-else>
<span class="disabled"></span>
</div>
</td>
</tr>
</table>
</form>
<form class="ui form" data-tea-success="success" data-tea-action="$" v-show="scheduleIsEditing">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="schedule.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">当前节点租期结束日期</td>
<td>
<datepicker name="offlineDay" v-model="schedule.offlineDay"></datepicker>
<p class="comment">当前节点租期结束日期,到此日期后当前节点将会被自动下线;如果为空,则表示永远不下线。</p>
</td>
</tr>
<tr>
<td>当前节点为集群备用节点</td>
<td>
<checkbox name="isBackupForCluster" v-model="schedule.isBackupForCluster"></checkbox>
<p class="comment">选中后,标识当前节点为所在集群<span v-if="schedule.nodeClusterName.length > 0"><code-label>{{schedule.nodeClusterName}}</code-label></span>的备用节点同集群的其他节点可以通过调度动作切换到此节点。备用节点未被调度触发时不会加入DNS域名解析。</p>
</td>
</tr>
<tr>
<td>当前节点为分组备用节点</td>
<td>
<checkbox name="isBackupForGroup" v-model="schedule.isBackupForGroup"></checkbox>
<p class="comment">选中后,标识当前节点为所在分组<span v-if="schedule.nodeGroupName.length > 0"><code-label>{{schedule.nodeGroupName}}</code-label></span>的备用节点,同分组的其他节点可以通过调度动作切换到此节点<span v-if="schedule.nodeGroupName.length == 0">;如果暂时还没有分组,则当前设置不会起作用</span>。备用节点未被调度触发时不会加入DNS域名解析。</p>
</td>
</tr>
<tr>
<td>当前节点备用IP</td>
<td>
<values-box name="backupIPs" :v-values="schedule.backupIPs" placeholder="IP"></values-box>
<p class="comment">可以通过调度动作切换到这些备用IP。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
<div v-show="!scheduleIsEditing">
<div class="ui margin"></div>
<h4>调度动作&nbsp;
<a href="" style="font-size: 0.8em" @click.prevent="createAction">[添加]</a> &nbsp;
<a href="" v-if="!showActionsCopyOps" @click.prevent="showActionsCopyOps = !showActionsCopyOps" title="更多操作" style="font-size: 0.8em">[更多操作]</a>
<span v-show="showActionsCopyOps">
<a href="" style="font-size: 0.8em" v-if="schedule.nodeGroupName.length > 0" @click.prevent="copyActionsToGroup">[复制到分组"{{schedule.nodeGroupName}}"]</a> &nbsp;
<a href="" style="font-size: 0.8em" @click.prevent="copyActionsToCluster">[复制到集群"{{schedule.nodeClusterName}}"]</a>
</span>
</h4>
<p class="comment" v-if="actions.length == 0">暂时还没有任何调度动作。</p>
<div v-if="actions.length > 0">
<p class="ui message warning" v-if="schedule.isBackupForCluster || schedule.isBackupForGroup">当前节点为备用节点,这里设置的调度动作将不会被触发。</p>
<table class="ui table celled selectable" id="sortable-table">
<thead>
<tr>
<th style="width:3em"></th>
<th>条件</th>
<th class="three wide">动作</th>
<th class="two wide">持续时间</th>
<th class="width5">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tbody v-for="action in actions" :v-id="action.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
<td>
<node-schedule-conds-viewer :v-params="params" :v-operators="operators" v-model="action.conds"></node-schedule-conds-viewer>
</td>
<td>
<span :class="{red: schedule.actionStatus != null && schedule.actionStatus.actionId > 0 && schedule.actionStatus.actionId == action.id}">{{action.actionName}}</span>
</td>
<td>{{action.durationDescription}}</td>
<td>
<div v-if="schedule.actionStatus != null && schedule.actionStatus.actionId > 0 && schedule.actionStatus.actionId == action.id">
<span class="red">已触发</span>
</div>
<div v-else>
<label-on :v-is-on="action.isOn"></label-on>
</div>
</td>
<td>
<a href="" @click.prevent="updateAction(action.id)">修改</a> &nbsp; <a href="" @click.prevent="deleteAction(action.id)">删除</a>
</td>
</tr>
</tbody>
</table>
<p v-if="actions.length > 0" class="comment">可以拖动左侧的<i class="icon bars"></i>排序。</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,96 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.scheduleIsEditing = false
this.editSchedule = function () {
this.scheduleIsEditing = !this.scheduleIsEditing
}
this.createAction = function () {
teaweb.popup(".actions.createPopup?nodeId=" + this.schedule.id, {
height: "26em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.deleteAction = function (actionId) {
teaweb.confirm("确定要删除此调度动作吗?", function () {
this.$post(".actions.delete")
.params({
actionId: actionId
})
.success(function () {
teaweb.reload()
})
})
}
this.updateAction = function (actionId) {
teaweb.popup(".actions.updatePopup?actionId=" + actionId, {
height: "26em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.resetActionStatus = function () {
teaweb.confirm("确定要重置当前节点的状态吗?", function () {
this.$post(".resetActionStatus")
.params({
nodeId: this.schedule.id
})
.success(function () {
teaweb.successRefresh("重置成功")
})
})
}
this.showActionsCopyOps = false
this.copyActionsToGroup = function () {
teaweb.confirm("确定要复制当前节点的调度动作到所在分组吗?", function () {
this.$post(".actions.copyToGroup")
.params({
nodeId: this.schedule.id
})
.success(function () {
teaweb.successRefresh("同步成功")
})
})
}
this.copyActionsToCluster = function () {
teaweb.confirm("确定要复制当前节点的调度动作到所在分组吗?", function () {
this.$post(".actions.copyToCluster")
.params({
nodeId: this.schedule.id
})
.success(function () {
teaweb.successRefresh("同步成功")
})
})
}
// 排序
this.$delay(function () {
let that = this
sortTable(function (actionIds) {
that.$post(".actions.updateOrders")
.params({
nodeId: that.schedule.id,
actionIds: actionIds
})
.success(function () {
teaweb.success("保存成功")
})
})
})
})

View File

@@ -0,0 +1,40 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="nodeId" :value="node.id"/>
<input type="hidden" name="loginId" :value="loginId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">SSH主机地址</td>
<td>
<input type="text" name="sshHost" maxlength="64" v-model="sshHost"/>
<p class="comment"><span v-if="hostIsAutoFilled" class="red"><strong>已自动填充</strong>,需要点击"保存"按钮后生效。</span>比如192.168.1.100。</p>
</td>
</tr>
<tr>
<td>SSH主机端口</td>
<td>
<input type="text" name="sshPort" maxlength="5" v-model="sshPort"/>
<p class="comment">比如22。</p>
</td>
</tr>
<tr>
<td>SSH登录认证</td>
<td>
<grant-selector :v-grant="grant" :v-node-cluster-id="clusterId"></grant-selector>
</td>
</tr>
</table>
<div class="ui message" v-if="isTesting">正在测试连接 ...</div>
<div class="ui message green" v-if="resp != null && resp.isOk">连接成功!</div>
<div class="ui message red" v-if="resp != null && !resp.isOk">连接失败:{{resp.error}}</div>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,53 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
// 认证相关
this.grant = null
this.sshHost = ""
this.sshPort = ""
this.loginId = 0
if (this.node.login != null) {
this.loginId = this.node.login.id
if (this.node.login.params != null) {
this.sshHost = this.node.login.params.host
if (this.node.login.params.port > 0) {
this.sshPort = this.node.login.params.port
}
}
if (this.node.login.grant != null && typeof this.node.login.grant.id != "undefined") {
this.grant = {
id: this.node.login.grant.id,
name: this.node.login.grant.name,
method: this.node.login.grant.method,
methodName: this.node.login.grant.methodName,
username: this.node.login.grant.username
}
}
}
// 测试相关
this.resp = null
this.isTesting = false
if (this.grant != null && this.grant.id > 0 && this.sshHost.length > 0 && this.sshPort.toString().length > 0) {
this.isTesting = true
this.$delay(function () {
this.$post(".test")
.params({
grantId: this.grant.id,
host: this.sshHost,
port: this.sshPort
})
.success(function (resp) {
this.resp = resp.data
})
.done(function () {
this.isTesting = false
})
}, 1000)
}
})

View File

@@ -0,0 +1,37 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-success="success" data-tea-action="$">
<input type="hidden" name="nodeId" :value="node.id"/>
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">CPU线程数</td>
<td>
<input type="text" name="maxCPU" v-model="node.maxCPU" style="width:5em" maxlength="4"/>
<p class="comment">当前节点可以使用的最多的CPU线程数如果为0则默认为总的CPU线程数。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
</table>
<h4>DNS解析</h4>
<dns-resolver-config-box :v-dns-resolver-config="dnsResolverConfig"></dns-resolver-config-box>
<h4>API相关</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">API节点地址</td>
<td>
<div style="margin-bottom: 0.5em">
<api-node-addresses-box :v-name="'apiNodeAddrsJSON'" :v-addrs="apiNodeAddrs"></api-node-addresses-box>
</div>
<p class="comment">当前节点单独使用的API节点设置。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,41 @@
{$layout}
{$template "/clusters/cluster/node/node_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div>
<second-menu>
<menu-item @click.prevent="createThreshold">[添加阈值]</menu-item>
</second-menu>
</div>
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
<table class="ui table selectable celled" v-if="thresholds.length > 0">
<thead>
<tr>
<th>监控项</th>
<th>参数</th>
<th>操作符</th>
<th>对比值</th>
<th>统计时间段</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="threshold in thresholds">
<td>{{threshold.itemName}}</td>
<td>{{threshold.paramName}}</td>
<td>{{threshold.operatorName}}</td>
<td>{{threshold.value}}<span v-if="threshold.paramIsPercent">%</span></td>
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
<td>
<label-on :v-is-on="threshold.isOn"></label-on>
</td>
<td>
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a> &nbsp;
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,43 @@
Tea.context(function () {
this.createThreshold = function () {
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/createPopup", {
clusterId: this.clusterId,
nodeId: this.nodeId
}), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.updateThreshold = function (thresholdId) {
teaweb.popup(Tea.url("/clusters/cluster/settings/thresholds/updatePopup", {
thresholdId: thresholdId
}), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.deleteThreshold = function (thresholdId) {
let that = this
teaweb.confirm("确定要删除这个阈值吗?", function () {
that.$post("/clusters/cluster/settings/thresholds/delete")
.params({
thresholdId: thresholdId
})
.success(function () {
teaweb.success("删除成功", function () {
teaweb.reload()
})
})
})
}
})