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,7 @@
<first-menu>
<menu-item href="/clusters/grants">认证列表</menu-item>
<span class="item">|</span>
<menu-item :href="'/clusters/grants/grant?grantId=' + grant.id" code="index">{{grant.name}}详情</menu-item>
<menu-item :href="'/clusters/grants/test?grantId=' + grant.id" code="test">测试</menu-item>
<menu-item :href="'/clusters/grants/update?grantId=' + grant.id" code="update">修改</menu-item>
</first-menu>

View File

@@ -0,0 +1,5 @@
<first-menu>
<menu-item href="/clusters/grants" code="index">认证列表</menu-item>
<span class="item disabled">|</span>
<menu-item href="/clusters/grants/create" code="create">[创建认证]</menu-item>
</first-menu>

View File

@@ -0,0 +1,86 @@
{$layout}
{$template "menu"}
<div class="margin"></div>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<table class="ui table selectable definition">
<tr>
<td>名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus"/>
<p class="comment">为当前认证信息起一个容易识别的名称。</p>
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
<select class="ui dropdown" style="width:10em" name="method" v-model="method">
<option v-for="method in methods" :value="method.value">{{method.name}}</option>
</select>
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="method == 'user'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" value="root" v-model="username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>SSH密码</td>
<td><input type="password" name="password" maxlength="100"/>
<p class="comment">SSH登录用户密码。<mask-warning></mask-warning></p> </td>
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="method == 'privateKey'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" value="root" v-model="username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>RSA私钥 *</td>
<td>
<file-textarea name="privateKey" spellcheck="false" placeholder="填入RSA私钥内容或者拖动私钥文件到当前框中"></file-textarea>
<p class="comment">用来生成登录SSH公钥的私钥。<mask-warning></mask-warning></p>
</td>
</tr>
<tr>
<td>私钥密码<br/><em>Passphrase</em></td>
<td>
<input type="password" name="passphrase" maxlength="200"/>
<p class="comment">为私钥设置了密码Passphrase后需要填写。</p>
</td>
</tr>
</tbody>
<tr v-if="username != 'root'">
<td>执行sudo</td>
<td>
<checkbox name="su" checked="checked"></checkbox>
<p class="comment">非root的用户可以使用<code-label>sudo</code-label>获得更高权限来执行命令请确保当前用户已经加入到sudo分组中。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>备注</td>
<td>
<textarea name="description" rows="3"></textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,6 @@
Tea.context(function () {
this.method = "user";
this.username = "root"
this.success = NotifySuccess("保存成功", "/clusters/grants");
});

View File

@@ -0,0 +1,86 @@
{$layout "layout_popup"}
<h3>添加新认证</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<table class="ui table selectable definition">
<tr>
<td>名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" value=""/>
<p class="comment">为当前认证信息起一个容易识别的名称。</p>
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
<select class="ui dropdown" style="width:10em" name="method" v-model="method">
<option v-for="method in methods" :value="method.value">{{method.name}}</option>
</select>
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="method == 'user'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" value="root" v-model="username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>SSH密码</td>
<td><input type="password" name="password" maxlength="100"/>
<p class="comment">SSH登录用户密码。</p> </td>
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="method == 'privateKey'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" value="root" v-model="username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>RSA私钥 *</td>
<td>
<file-textarea name="privateKey" spellcheck="false" placeholder="填入RSA私钥内容或者拖动私钥文件到当前框中"></file-textarea>
<p class="comment">用来生成登录SSH公钥的私钥</p>
</td>
</tr>
<tr>
<td>私钥密码<br/><em>Passphrase</em></td>
<td>
<input type="password" name="passphrase" maxlength="200"/>
<p class="comment">为私钥设置了密码Passphrase后需要填写。</p>
</td>
</tr>
</tbody>
<tr v-if="username != 'root'">
<td>执行sudo</td>
<td>
<checkbox name="su" checked="checked"></checkbox>
<p class="comment">非root的用户可以使用<code-label>sudo</code-label>获得更高权限来执行命令请确保当前用户已经加入到sudo分组中。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>备注</td>
<td>
<textarea name="description" rows="3"></textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,6 @@
Tea.context(function () {
this.method = "user";
this.username = "root"
this.success = NotifyPopup;
});

View File

@@ -0,0 +1,4 @@
.CodeMirror-wrap pre {
word-break: break-all !important;
}
/*# sourceMappingURL=grant.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["grant.less"],"names":[],"mappings":"AAAA,gBAAiB;EAChB,qBAAA","file":"grant.css"}

View File

@@ -0,0 +1,87 @@
{$layout}
{$template "grant_menu"}
{$template "/code_editor"}
<table class="ui table selectable definition">
<tr>
<td>名称</td>
<td>
{{grant.name}}
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
{{grant.methodName}}
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="grant.method == 'user'">
<tr>
<td>SSH用户名</td>
<td>
{{grant.username}}
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>SSH密码</td>
<td>{{grant.password}}
<p class="comment">SSH登录用户密码。</p> </td>
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="grant.method == 'privateKey'">
<tr>
<td>SSH用户名</td>
<td>{{grant.username}}
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>RSA私钥</td>
<td>
<source-code-box type="text/plain">{{grant.privateKey}}</source-code-box>
<p class="comment">用来生成登录SSH公钥的私钥</p>
</td>
</tr>
<tr>
<td>私钥密码<br/><em>Passphrase</em></td>
<td>
<span v-if="grant.passphrase.length > 0">{{grant.passphrase}}</span>
<span v-else class="disabled">-</span>
</td>
</tr>
</tbody>
<tr>
<td>执行sudo</td>
<td>
<span v-if="grant.su" class="green">Y</span>
<span v-else class="disabled">N</span>
</td>
</tr>
<tr>
<td>备注</td>
<td>
<span v-if="grant.description.length > 0">{{grant.description}}</span>
<span v-if="grant.description.length == 0">-</span>
</td>
</tr>
</table>
<div class="ui divider"></div>
<h3>使用此认证的集群</h3>
<div>
<p v-if="clusters.length == 0" class="comment">暂时还没有集群使用此认证。</p>
<a :href="'/clusters/cluster?clusterId=' + cluster.id" class="ui label small basic" v-for="cluster in clusters">{{cluster.name}}</a>
</div>
<div class="ui divider"></div>
<h3>使用此认证的节点</h3>
<div>
<p v-if="nodes.length == 0" class="comment">暂时还没有节点使用此认证。</p>
<a :href="'/clusters/cluster/node?clusterId=' + node.cluster.id + '&nodeId=' + node.id" class="ui label small basic" :class="{red:!node.isOn}" v-for="node in nodes">{{node.name}}<span class="small">{{node.cluster.name}}</span></a>
</div>

View File

@@ -0,0 +1,3 @@
.CodeMirror-wrap pre {
word-break: break-all !important;
}

View File

@@ -0,0 +1,57 @@
{$layout}
{$template "menu"}
<!-- 搜索表单 -->
<form class="ui form" method="get" action="/clusters/grants">
<div class="margin"></div>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="keyword" placeholder="名称、用户名等..." v-model="keyword"/>
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>
<!-- SSH认证列表 -->
<div class="ui message" v-if="grants.length == 0">暂时还没有认证信息。</div>
<table class="ui table selectable celled" v-if="grants.length > 0">
<thead>
<tr>
<th>名称</th>
<th>类型</th>
<th>用户名</th>
<th class="center width5">集群数</th>
<th class="center width5">节点数</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="grant in grants">
<td><a :href="'/clusters/grants/grant?grantId=' + grant.id"><keyword :v-word="keyword">{{grant.name}}</keyword></a></td>
<td>
<span class="ui label tiny basic">{{grant.method.name}}</span>
</td>
<td>
<span v-if="grant.username.length > 0">{{grant.username}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="grant.countClusters > 0">{{grant.countClusters}}</span>
<span v-else class="disabled">0</span>
</td>
<td class="center">
<span v-if="grant.countNodes > 0">{{grant.countNodes}}</span>
<span v-else class="disabled">0</span>
</td>
<td>
<a :href="'/clusters/grants/grant?grantId=' + grant.id">详情</a> &nbsp; <a href="" @click.prevent="deleteGrant(grant.id)">删除</a>
</td>
</tr>
</table>
<div class="page" v-html="page"></div>

View File

@@ -0,0 +1,11 @@
Tea.context(function () {
this.deleteGrant = function (grantId) {
teaweb.confirm("确定要删除此认证吗?", function () {
this.$post(".delete")
.params({
"grantId": grantId
})
.refresh();
});
};
});

View File

@@ -0,0 +1,29 @@
.grants-box {
margin-top: 1em;
}
.grant-box {
float: left;
width: 12em;
height: 4.5em;
overflow-x: hidden;
overflow-y: auto;
margin-right: 0.5em;
border: 1px #ccc solid;
margin-bottom: 0.5em;
padding: 0.5em 0.3em;
text-align: left;
cursor: pointer;
}
.grant-box .small {
font-size: 0.8em;
}
.grant-box div.method {
margin-top: 0.3em;
}
.grant-box div.method .small {
font-size: 0.8em;
}
.grant-box::-webkit-scrollbar {
width: 4px;
}
/*# sourceMappingURL=selectPopup.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["selectPopup.less"],"names":[],"mappings":"AAAA;EACC,eAAA;;AAGD;EACC,WAAA;EACA,WAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,mBAAA;EACA,sBAAA;EACA,oBAAA;EACA,oBAAA;EACA,gBAAA;EACA,eAAA;;AAXD,UAaC;EACC,gBAAA;;AAdF,UAiBC,IAAG;EACF,iBAAA;;AAlBF,UAiBC,IAAG,OAGF;EACC,gBAAA;;AAKH,UAAU;EACT,UAAA","file":"selectPopup.css"}

View File

@@ -0,0 +1,40 @@
{$layout "layout_popup"}
<h3>选择SSH认证</h3>
<form class="ui form">
<div class="ui fields inline">
<div class="ui field">
<input type="text" placeholder="搜索名称、用户名等" v-model="keyword"/>
</div>
</div>
</form>
<div class="ui divider"></div>
<span v-if="grants.length == 0">暂时还没有可用的认证。</span>
<h4 v-if="suggestGrants.length > 0">可能的认证</h4>
<div class="grants-box" v-if="suggestGrants.length > 0">
<div class="grant-box" v-for="grant in suggestGrants">
<div :class="{blue:grantId == grant.id}" @click.prevent="selectGrant(grant)"><a href="">{{grant.name}}</a> <span v-if="grant.username.length > 0" class="small grey">{{grant.username}}</span>
<div class="method">
<span class="small grey">{{grant.methodName}}</span>
</div>
</div>
</div>
</div>
<div class="clear"></div>
<h4>全部认证</h4>
<div class="grants-box">
<div class="grant-box" v-for="grant in grants">
<div :class="{blue:grantId == grant.id}" @click.prevent="selectGrant(grant)"><a href="">{{grant.name}}</a> <span v-if="grant.username.length > 0" class="small grey">{{grant.username}}</span>
<div class="method">
<span class="small grey">{{grant.methodName}}</span>
</div>
</div>
</div>
</div>
<div class="clear"></div>
<p class="comment">请点击使用某个认证。</p>

View File

@@ -0,0 +1,29 @@
Tea.context(function () {
this.grantId = 0
this.keyword = ""
let allGrants = this.grants.$copy()
this.selectGrant = function (grant) {
NotifyPopup({
code: 200,
data: {
grant: grant
}
})
}
this.$delay(function () {
let that = this
this.$watch("keyword", function (keyword) {
if (keyword.length > 0) {
that.grants = allGrants.$findAll(function (k, grant) {
return teaweb.match(grant.name, keyword)
|| teaweb.match(grant.description, keyword)
|| teaweb.match(grant.username, keyword)
})
} else {
that.grants = allGrants
}
})
})
})

View File

@@ -0,0 +1,33 @@
.grants-box {
margin-top: 1em;
}
.grant-box {
float: left;
width: 12em;
height: 4.5em;
overflow-x: hidden;
overflow-y: auto;
margin-right: 0.5em;
border: 1px #ccc solid;
margin-bottom: 0.5em;
padding: 0.5em 0.3em;
text-align: left;
cursor: pointer;
.small {
font-size: 0.8em;
}
div.method {
margin-top: 0.3em;
.small {
font-size: 0.8em;
}
}
}
.grant-box::-webkit-scrollbar {
width: 4px;
}

View File

@@ -0,0 +1,63 @@
{$layout}
{$template "grant_menu"}
<div class="ui message">可以在这里测试SSH主机连接是否正常。</div>
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-before="requestBefore" data-tea-done="requestDone">
<csrf-token></csrf-token>
<input type="hidden" name="grantId" :value="grant.id"/>
<table class="ui table selectable definition">
<tr>
<td class="title">节点主机地址 *</td>
<td>
<input type="text" name="host" placeholder="x.x.x.x" style="width: 10em" ref="focus"/>
</td>
</tr>
<tr>
<td>节点主机端口 *</td>
<td>
<input type="text" name="port" style="width: 5em" size="5" maxlength="5"/>
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
{{grant.methodName}}
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="grant.method == 'user'">
<tr>
<td>SSH用户名</td>
<td>
{{grant.username}}
</td>
</tr>
<tr>
<td>SSH密码</td>
<td>{{grant.password}}
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="grant.method == 'privateKey'">
<tr>
<td>SSH用户名</td>
<td>{{grant.username}}</td>
</tr>
<tr>
<td>RSA私钥</td>
<td>
<pre class="pre-box" style="max-height: 10em; overflow-y: auto">{{grant.privateKey}}</pre>
</td>
</tr>
</tbody>
</table>
<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 v-if="!isRequesting">提交测试</submit-btn>
<button class="ui button disabled" v-if="isRequesting">连接中...</button>
</form>

View File

@@ -0,0 +1,17 @@
Tea.context(function () {
this.isRequesting = false
this.resp = null
this.success = function (resp) {
this.resp = resp.data
}
this.requestBefore = function () {
this.isRequesting = true
this.resp = null
}
this.requestDone = function () {
this.isRequesting = false
}
})

View File

@@ -0,0 +1,80 @@
{$layout}
{$template "grant_menu"}
<div class="margin"></div>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="grantId" :value="grant.id"/>
<table class="ui table selectable definition">
<tr>
<td>名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" v-model="grant.name"/>
<p class="comment">为当前认证信息起一个容易识别的名称。</p>
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
<select class="ui dropdown" style="width:10em" name="method" v-model="method">
<option v-for="method in methods" :value="method.value">{{method.name}}</option>
</select>
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="method == 'user'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" v-model="grant.username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>SSH密码</td>
<td><input type="password" name="password" maxlength="100" v-model="grant.password"/>
<p class="comment">SSH登录用户密码。<mask-warning></mask-warning></p> </td>
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="method == 'privateKey'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" v-model="grant.username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>RSA私钥 *</td>
<td>
<file-textarea name="privateKey" v-model="grant.privateKey" spellcheck="false" placeholder="填入RSA私钥内容或者拖动私钥文件到当前框中"></file-textarea>
<p class="comment">用来生成登录SSH公钥的私钥。<mask-warning></mask-warning></p>
</td>
</tr>
<tr>
<td>私钥密码<br/><em>Passphrase</em></td>
<td>
<input type="password" name="passphrase" maxlength="200" v-model="grant.passphrase"/>
<p class="comment">为私钥设置了密码Passphrase后需要填写。</p>
</td>
</tr>
</tbody>
<tr v-if="grant.username != 'root'">
<td>执行sudo</td>
<td>
<checkbox name="su" v-model="grant.su"></checkbox>
<p class="comment">非root的用户可以使用<code-label>sudo</code-label>获得更高权限来执行命令请确保当前用户已经加入到sudo分组中。</p>
</td>
</tr>
<tr>
<td>备注</td>
<td>
<textarea name="description" rows="3" v-model="grant.description"></textarea>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,5 @@
Tea.context(function () {
this.method = this.grant.method;
this.success = NotifySuccess("保存成功", "/clusters/grants/grant?grantId=" + this.grant.id);
});

View File

@@ -0,0 +1,87 @@
{$layout "layout_popup"}
<h3>修改认证</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="grantId" :value="grant.id"/>
<input type="hidden" name="nodeId" :value="grant.nodeId"/>
<table class="ui table selectable definition">
<tr>
<td>名称 *</td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" value="" v-model="grant.name"/>
<p class="comment">为当前认证信息起一个容易识别的名称。</p>
</td>
</tr>
<tr>
<td class="title">认证方式</td>
<td>
<select class="ui dropdown" style="width:10em" name="method" v-model="method">
<option v-for="method in methods" :value="method.value">{{method.name}}</option>
</select>
</td>
</tr>
<!-- 用户名/密码 -->
<tbody v-if="method == 'user'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" v-model="grant.username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>SSH密码</td>
<td><input type="password" name="password" maxlength="100" v-model="grant.password"/>
<p class="comment">SSH登录用户密码。</p> </td>
</tr>
</tbody>
<!-- 私钥 -->
<tbody v-if="method == 'privateKey'">
<tr>
<td>SSH用户名 *</td>
<td>
<input type="text" name="username" maxlength="100" v-model="grant.username"/>
<p class="comment">SSH登录用户名。</p>
</td>
</tr>
<tr>
<td>RSA私钥 *</td>
<td>
<file-textarea name="privateKey" v-model="grant.privateKey" spellcheck="false" placeholder="填入RSA私钥内容或者拖动私钥文件到当前框中"></file-textarea>
<p class="comment">用来生成登录SSH公钥的私钥。</p>
</td>
</tr>
<tr>
<td>私钥密码<br/><em>Passphrase</em></td>
<td>
<input type="password" name="passphrase" maxlength="200" v-model="grant.passphrase"/>
<p class="comment">为私钥设置了密码Passphrase后需要填写。</p>
</td>
</tr>
</tbody>
<tr v-if="grant.username != 'root'">
<td>执行sudo</td>
<td>
<checkbox name="su" v-model="grant.su"></checkbox>
<p class="comment">非root的用户可以使用<code-label>sudo</code-label>获得更高权限来执行命令请确保当前用户已经加入到sudo分组中。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>备注</td>
<td>
<textarea name="description" rows="3" v-model="grant.description"></textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,5 @@
Tea.context(function () {
this.method = this.grant.method;
this.success = NotifyPopup;
});