This commit is contained in:
w-mj 2023-02-20 10:47:41 +08:00
parent fe433600ae
commit 70a97be750
No known key found for this signature in database
GPG Key ID: 3A2CB5BE2F835897
3 changed files with 138 additions and 148 deletions

24
main.go
View File

@ -4,7 +4,6 @@ import (
"embed" "embed"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"html/template"
"io/fs" "io/fs"
m "k8s-manager/src" m "k8s-manager/src"
"net/http" "net/http"
@ -18,17 +17,17 @@ func main() {
r := gin.Default() r := gin.Default()
r.SetFuncMap(template.FuncMap{ //r.SetFuncMap(template.FuncMap{
"inc": m.Inc, // "inc": m.Inc,
}) //})
//
tmp := template.Must(template.New(""). //tmp := template.Must(template.New("").
Funcs(template.FuncMap{ // Funcs(template.FuncMap{
"inc": m.Inc, // "inc": m.Inc,
}). // }).
ParseFS(f, "template/*")) // ParseFS(f, "template/*"))
//
r.SetHTMLTemplate(tmp) //r.SetHTMLTemplate(tmp)
fp, _ := fs.Sub(f, "static") fp, _ := fs.Sub(f, "static")
r.StaticFS("/static", http.FS(fp)) r.StaticFS("/static", http.FS(fp))
@ -42,6 +41,7 @@ func main() {
r.DELETE("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment) r.DELETE("/dep", m.JWTAuthMiddleware, m.CreateDeleteDeployment)
r.GET("/login/:username", Test) r.GET("/login/:username", Test)
r.GET("/username", m.JWTAuthMiddleware, m.GetUsername)
err := r.Run(":8000") err := r.Run(":8000")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/thoas/go-funk"
"net/http" "net/http"
"strings" "strings"
) )
@ -49,18 +48,20 @@ func statusString(a *AppViewModel) string {
} }
func AppPage(c *gin.Context) { func AppPage(c *gin.Context) {
username := c.MustGet("username").(string) //username := c.MustGet("username").(string)
appsvm := funk.Map(GetApps(username), func(a AppModel) AppViewModel { return AppViewModel{AppModel: &a} }).([]AppViewModel) //appsvm := funk.Map(GetApps(username), func(a AppModel) AppViewModel { return AppViewModel{AppModel: &a} }).([]AppViewModel)
for i := range appsvm { //for i := range appsvm {
a := &appsvm[i] // a := &appsvm[i]
a.Status = statusString(a) // a.Status = statusString(a)
a.ServiceDisplayName = a.ServiceName() // a.ServiceDisplayName = a.ServiceName()
} //}
vm := &AppsViewModel{ //vm := &AppsViewModel{
Username: username, // Username: username,
Apps: appsvm, // Apps: appsvm,
} //}
c.HTML(http.StatusOK, "app_view.html", vm)
c.File("template/app_view.html")
// c.HTML(http.StatusOK, "app_view.html", vm)
} }
func AppMiddleware(c *gin.Context) { func AppMiddleware(c *gin.Context) {
@ -109,3 +110,8 @@ func GetAppsController(c *gin.Context) {
apps := GetApps(username) apps := GetApps(username)
c.JSON(http.StatusOK, apps) c.JSON(http.StatusOK, apps)
} }
func GetUsername(c *gin.Context) {
username := c.MustGet("username").(string)
c.JSON(http.StatusOK, gin.H{"username": username})
}

View File

@ -4,21 +4,22 @@
<link href="static/css/bootstrap.min.css" rel="stylesheet"> <link href="static/css/bootstrap.min.css" rel="stylesheet">
<script src="static/js/bootstrap.bundle.min.js"></script> <script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/axios.min.js"></script> <script src="static/js/axios.min.js"></script>
<script src="static/js/vue.global.js"></script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>应用管理</title> <title>应用管理</title>
</head> </head>
<body> <body>
<div class="container"> <div class="container" id="app">
<h1>应用管理页面</h1> <h1>应用管理页面</h1>
<br/> <br/>
<div class="d-flex"> <div class="d-flex">
<div class="align-self-start">用户名:{{ .Username }}</div> <!-- <div class="align-self-start">用户名:{{ .Username }}</div>-->
<div class="ms-auto align-self-end"> <div class="ms-auto align-self-end">
<button class="btn btn-success" onclick="showModal('')">添加应用</button> <button class="btn btn-success" @click="showModal(null)">添加应用</button>
</div> </div>
</div> </div>
@ -40,32 +41,29 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{ range $i, $v := .Apps }} <tr v-for="(item, index) in apps">
<tr id="app-list-{{ $v.Id }}"> <td> {{ index + 1 }}</td>
<td> {{ inc $i }}</td> <td> {{ item.AppName }}</td>
<td class="app-field-id" style="display: none;"> {{ $v.Id }}</td> <td> {{ item.Comment }}</td>
<td class="app-field-name"> {{ $v.AppName }}</td> <td> {{ item.Image }}</td>
<td class="app-field-comment"> {{ $v.Comment }}</td> <td> {{ item.Command }}</td>
<td class="app-field-image"> {{ $v.Image }}</td> <td> {{ item.Env }}</td>
<td class="app-field-cmd"> {{ $v.Command }}</td> <td> {{ item.Status }}</td>
<td class="app-field-env"> {{ $v.Env }}</td> <td> {{ item.ServiceDisplayName }}</td>
<td class="app-field-status" id="app-status-{{ $v.Id }}"> {{ $v.Status }}</td> <td> {{ item.Ports }}</td>
<td class="app-field-svc"> {{ $v.ServiceDisplayName }}</td>
<td class="app-field-ports"> {{ $v.Ports }}</td>
<td> <td>
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-success" onclick="startApp('{{$v.Id}}')">启动</button> <button class="btn btn-success" @click="startApp(item)">启动</button>
<button class="btn btn-dark" onclick="stopApp('{{$v.Id}}')">停止</button> <button class="btn btn-dark" @click="stopApp(item)">停止</button>
<button class="btn btn-primary" onclick="showModal('app-list-{{ $v.Id }}')">修改</button> <button class="btn btn-primary" @click="showModal(item)">修改</button>
<button class="btn btn-danger" onclick="deleteApp('{{ $v.Id }}')">删除</button> <button class="btn btn-danger" @click="deleteApp(item)">删除</button>
</div> </div>
</td> </td>
</tr> </tr>
{{ else }}
<tr> <tr v-if="apps.length == 0">
<td colspan="7" style="text-align: center;">点击+按钮添加应用</td> <td colspan="7" style="text-align: center;">点击+按钮添加应用</td>
</tr> </tr>
{{ end }}
</tbody> </tbody>
</table> </table>
@ -80,156 +78,142 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<input type="text" id="app-id" class="form-control" style="display: none;" disabled/>
<input type="text" id="app-username" class="form-control" style="display: none;" disabled
value="{{ .Username }}"/>
<div class="row"> <div class="row">
<label for="app-name" class="form-label">应用名</label> <label for="app-name" class="form-label">应用名</label>
<input type="text" id="app-name" class="form-control" <input type="text" id="app-name" class="form-control" v-model="selectApp.AppName"
placeholder="只能为字母、数字和下划线"/> placeholder="只能为字母、数字和下划线"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-image" class="form-label">镜像</label> <label for="app-image" class="form-label">镜像</label>
<input type="text" id="app-image" class="form-control" placeholder="Docker镜像名"/> <input type="text" id="app-image" class="form-control" v-model="selectApp.Image"
placeholder="Docker镜像名"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-cmd" class="form-label">启动命令</label> <label for="app-cmd" class="form-label">启动命令</label>
<input type="text" id="app-cmd" class="form-control" placeholder="启动命令,可选"/> <input type="text" id="app-cmd" class="form-control" v-model="selectApp.Command"
placeholder="启动命令,可选"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-env" class="form-label">环境变量</label> <label for="app-env" class="form-label">环境变量</label>
<input type="text" id="app-env" class="form-control" placeholder="环境变量,可选"/> <input type="text" id="app-env" class="form-control" v-model="selectApp.Env"
placeholder="环境变量,可选"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-svc" class="form-label">服务名</label> <label for="app-svc" class="form-label">服务名</label>
<input type="text" id="app-svc" class="form-control" placeholder="服务名,可选"/> <input type="text" id="app-svc" class="form-control" v-model="selectApp.SvcName"
placeholder="服务名,可选"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-ports" class="form-label">端口号</label> <label for="app-ports" class="form-label">端口号</label>
<input type="text" id="app-ports" class="form-control" placeholder="端口号,多个端口以逗号分隔"/> <input type="text" id="app-ports" class="form-control" v-model="selectApp.Ports"
placeholder="端口号,多个端口以逗号分隔"/>
</div> </div>
<div class="row"> <div class="row">
<label for="app-comment" class="form-label">描述</label> <label for="app-comment" class="form-label">描述</label>
<textarea type="text" id="app-comment" class="form-control" placeholder="应用描述"></textarea> <textarea type="text" id="app-comment" class="form-control" v-model="selectApp.Comment"
placeholder="应用描述"></textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="updateApp()">保存 <button type="button" class="btn btn-primary" data-bs-dismiss="modal" @click="updateApp()">保存
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="toast d-flex align-items-center top-0 end-0" id="success-toast">--> <!-- <div class="toast d-flex align-items-center top-0 end-0" id="success-toast">-->
<!-- <div class="toast-body">--> <!-- <div class="toast-body">-->
<!-- <p id="toast-content"></p>--> <!-- <p id="toast-content"></p>-->
<!-- </div>--> <!-- </div>-->
<!-- <button type="button" class="btn-close ms-auto me-2" data-bs-dismiss="toast" aria-label="Close"></button>--> <!-- <button type="button" class="btn-close ms-auto me-2" data-bs-dismiss="toast" aria-label="Close"></button>-->
<!-- </div>--> <!-- </div>-->
</div> </div>
</body> </body>
<script> <script>
function updateModalField(classString, value) {
const classArray = classString.split(' ')
for (let c of classArray) {
if (c.startsWith("app-field-")) {
const k = c.replace("app-field-", "app-")
const ele = document.getElementById(k)
if (!ele) {
console.log("Cannot fine element ", k)
} else {
ele.value = value
}
return
}
}
}
function clearModalField() { const {createApp} = Vue
const id_list = ["app-id", "app-name", "app-image", "app-cmd", "app-env", "app-comment", "app-svc", "app-ports"] createApp({
for (const k of id_list) { data() {
document.getElementById(k).value = "" return {
username: "",
apps: [],
blankApp: {},
selectApp: {
Id: "",
AppName: "",
UserName: "",
Image: "",
Command: "",
Comment: "",
Env: "",
SvcName: "",
Ports: "",
} }
} }
},
function showModal(id) { methods: {
if (id) { showModal(a) {
const list_tr = document.getElementById(id) if (!a) {
const values = list_tr.children a = JSON.parse(JSON.stringify(this.blankApp))
for (let a of values) {
updateModalField(a.className, a.innerHTML.trim())
} }
} else { this.selectApp = a;
clearModalField()
}
(new bootstrap.Modal("#edit-modal")).show() (new bootstrap.Modal("#edit-modal")).show()
} },
startApp(item) {
function showToast(e) { axios.post("dep?id=" + item.Id).then(e => {
// document.getElementById("toast-content").innerHTML = e.msg console.log(e.data)
// t = new bootstrap.Toast(document.getElementById("success-toast")) })
// console.log(t) },
// t.show() deleteApp(app) {
} axios.delete("app", {
data: app
function updateApp() { }).then(e => {
const id_list = ["app-id", "app-name", "app-username", "app-image", "app-cmd", "app-env", "app-comment", "app-svc", "app-ports"] console.log(e.data)
const json_key_list = ["Id", "AppName", "UserName", "Image", "Command", "Env", "Comment", "SvcName", "Ports"] })
let appData = {} },
for (let k in id_list) { stopApp(app) {
appData[json_key_list[k]] = document.getElementById(id_list[k]).value axios.delete("dep?id=" + app.Id).then(e => {
} console.log(e.data)
if (appData['Id']) { })
appData['Id'] = parseInt(appData['Id']) },
axios.put("app", appData).then(e => { updateApp() {
showToast(e.data) let app = this.selectApp
if (app.Id) {
axios.put("app", app).then(e => {
console.log(e)
}) })
} else { } else {
appData['Id'] = null app.Id = null
axios.post("app", appData).then(e => { app.UserName = this.username
axios.post("app", app).then(e => {
console.log(e) console.log(e)
showToast(e.data)
}); });
} }
} }
},
function deleteApp(id) { mounted() {
axios.delete("app", { this.blankApp = JSON.parse(JSON.stringify(this.selectApp))
"data": { axios.get("username").then(r => {
Id: parseInt(id), this.username = r.data.username
Username: document.getElementById("app-username").value
}
}).then(e => {
console.log(e.data)
showToast(e.data)
}) })
} axios.get("apps").then(response => {
this.apps = response.data
function startApp(id) {
axios.post("dep?id=" + id).then(e => {
console.log(e.data)
}) })
}
function stopApp(id) {
axios.delete("dep?id=" + id).then(e => {
console.log(e.data)
})
} }
}).mount("#app")
</script> </script>
</html> </html>