feat(server): Add Rustdesk Command
And add build full s6 image for rustdesk command
This commit is contained in:
52
.github/workflows/build.yml
vendored
52
.github/workflows/build.yml
vendored
@@ -292,6 +292,21 @@ jobs:
|
|||||||
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker Full S6 image to Docker Hub ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }} # Only run this step if SKIP_DOCKER_HUB is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile_full_s6
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
- name: Build and push Docker image to GHCR ${{ matrix.job.platform }}
|
||||||
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@@ -308,6 +323,21 @@ jobs:
|
|||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:${{ env.TAG }}-${{ matrix.job.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
- name: Build and push Docker Full S6 image to GHCR ${{ matrix.job.platform }}
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }} # Only run this step if SKIP_GHCR is false
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: "."
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: ${{ matrix.job.docker_platform }}
|
||||||
|
push: true
|
||||||
|
provenance: false
|
||||||
|
build-args: |
|
||||||
|
BUILDARCH=${{ matrix.job.platform }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-${{ matrix.job.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
#
|
#
|
||||||
docker-manifest:
|
docker-manifest:
|
||||||
name: Push Docker Manifest
|
name: Push Docker Manifest
|
||||||
@@ -378,4 +408,26 @@ jobs:
|
|||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-armv7l,
|
||||||
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:latest-arm64
|
||||||
push: true
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push Full S6 manifest Docker Hub (:version)
|
||||||
|
if: ${{ env.SKIP_DOCKER_HUB == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
|
extra-images: ${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
${{ env.DOCKERHUB_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
|
amend: true
|
||||||
|
|
||||||
|
- name: Create and push Full S6 manifest GHCR (:latest)
|
||||||
|
if: ${{ env.SKIP_GHCR == 'false' }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
base-image: ghcr.io/${{ env.BASE_IMAGE_NAMESPACE }}/rustdesk-api:full-s6
|
||||||
|
extra-images: ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-amd64,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-armv7l,
|
||||||
|
ghcr.io/${{ env.GHCR_IMAGE_NAMESPACE }}/rustdesk-api:full-s6-arm64
|
||||||
|
push: true
|
||||||
amend: true
|
amend: true
|
||||||
@@ -2,9 +2,8 @@ FROM alpine
|
|||||||
|
|
||||||
ARG BUILDARCH
|
ARG BUILDARCH
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache tzdata file
|
RUN apk add --no-cache tzdata
|
||||||
COPY ./${BUILDARCH}/release /app/
|
COPY ./${BUILDARCH}/release /app/
|
||||||
RUN file /app/apimain
|
|
||||||
VOLUME /app/data
|
VOLUME /app/data
|
||||||
|
|
||||||
EXPOSE 21114
|
EXPOSE 21114
|
||||||
|
|||||||
38
Dockerfile_full_s6
Normal file
38
Dockerfile_full_s6
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
FROM rustdesk/rustdesk-server-s6:latest as server
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
ARG BUILDARCH
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
COPY ./${BUILDARCH}/release /app/
|
||||||
|
|
||||||
|
COPY --from=server /init /init
|
||||||
|
COPY --from=server /etc/s6-overlay /etc/s6-overlay
|
||||||
|
COPY --from=server /package /package
|
||||||
|
COPY --from=server /usr/bin/healthcheck.sh /usr/bin/healthcheck.sh
|
||||||
|
COPY --from=server /usr/bin/hbbr /usr/bin/hbbr
|
||||||
|
COPY --from=server /usr/bin/hbbs /usr/bin/hbbs
|
||||||
|
COPY --from=server /usr/bin/rustdesk-utils /usr/bin/rustdesk-utils
|
||||||
|
COPY --from=server /command /command
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
mkdir -p /etc/s6-overlay/s6-rc.d/api && \
|
||||||
|
echo -e "key-secret\nhbbs" > /etc/s6-overlay/s6-rc.d/api/dependencies && \
|
||||||
|
echo "longrun" > /etc/s6-overlay/s6-rc.d/api/type && \
|
||||||
|
echo "#!/command/with-contenv sh" > /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "cd /app" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
echo "./apimain" >> /etc/s6-overlay/s6-rc.d/api/run && \
|
||||||
|
touch /etc/s6-overlay/s6-rc.d/user/contents.d/api && \
|
||||||
|
echo "/package/admin/s6/command/s6-svstat /run/s6-rc/servicedirs/api || exit 1" >> /usr/bin/healthcheck.sh && \
|
||||||
|
ln -s /run /var/run
|
||||||
|
|
||||||
|
ENV RELAY=relay.example.com
|
||||||
|
ENV ENCRYPTED_ONLY=0
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
|
EXPOSE 21114 21115 21116 21116/udp 21117 21118 21119
|
||||||
|
|
||||||
|
ENTRYPOINT ["/init"]
|
||||||
@@ -169,7 +169,7 @@ func InitGlobal() {
|
|||||||
global.Lock = lock.NewLocal()
|
global.Lock = lock.NewLocal()
|
||||||
}
|
}
|
||||||
func DatabaseAutoUpdate() {
|
func DatabaseAutoUpdate() {
|
||||||
version := 247
|
version := 251
|
||||||
|
|
||||||
db := global.DB
|
db := global.DB
|
||||||
|
|
||||||
@@ -253,6 +253,7 @@ func Migrate(version uint) {
|
|||||||
&model.AuditFile{},
|
&model.AuditFile{},
|
||||||
&model.AddressBookCollection{},
|
&model.AddressBookCollection{},
|
||||||
&model.AddressBookCollectionRule{},
|
&model.AddressBookCollectionRule{},
|
||||||
|
&model.ServerCmd{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("migrate err :=>", err)
|
fmt.Println("migrate err :=>", err)
|
||||||
|
|||||||
@@ -2,45 +2,114 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"Gwen/global"
|
"Gwen/global"
|
||||||
|
"Gwen/http/request/admin"
|
||||||
"Gwen/http/response"
|
"Gwen/http/response"
|
||||||
|
"Gwen/model"
|
||||||
|
"Gwen/service"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rustdesk struct {
|
type Rustdesk struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfig RUSTDESK服务配置
|
type RustdeskCmd struct {
|
||||||
// @Tags ADMIN
|
Cmd string `json:"cmd"`
|
||||||
// @Summary RUSTDESK服务配置
|
Option string `json:"option"`
|
||||||
// @Description 服务配置,给webclient提供api-server
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 500 {object} response.Response
|
|
||||||
// @Router /admin/server-config [get]
|
|
||||||
// @Security token
|
|
||||||
func (r *Rustdesk) ServerConfig(c *gin.Context) {
|
|
||||||
cf := &response.ServerConfigResponse{
|
|
||||||
IdServer: global.Config.Rustdesk.IdServer,
|
|
||||||
Key: global.Config.Rustdesk.Key,
|
|
||||||
RelayServer: global.Config.Rustdesk.RelayServer,
|
|
||||||
ApiServer: global.Config.Rustdesk.ApiServer,
|
|
||||||
}
|
|
||||||
response.Success(c, cf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppConfig APP服务配置
|
func (r *Rustdesk) CmdList(c *gin.Context) {
|
||||||
// @Tags ADMIN
|
q := &admin.PageQuery{}
|
||||||
// @Summary APP服务配置
|
if err := c.ShouldBindQuery(q); err != nil {
|
||||||
// @Description APP服务配置
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
// @Accept json
|
return
|
||||||
// @Produce json
|
}
|
||||||
// @Success 200 {object} response.Response
|
res := service.AllService.ServerCmdService.List(q.Page, 9999)
|
||||||
// @Failure 500 {object} response.Response
|
//在列表前添加系统命令
|
||||||
// @Router /admin/app-config [get]
|
list := make([]*model.ServerCmd, 0)
|
||||||
// @Security token
|
list = append(list, model.SysServerCmds...)
|
||||||
func (r *Rustdesk) AppConfig(c *gin.Context) {
|
list = append(list, res.ServerCmds...)
|
||||||
response.Success(c, &gin.H{
|
res.ServerCmds = list
|
||||||
"web_client": global.Config.App.WebClient,
|
response.Success(c, res)
|
||||||
})
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdDelete(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.AllService.ServerCmdService.Delete(ex)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
func (r *Rustdesk) CmdCreate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Create(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) CmdUpdate(c *gin.Context) {
|
||||||
|
f := &model.ServerCmd{}
|
||||||
|
if err := c.ShouldBindJSON(f); err != nil {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ParamsError")+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errList := global.Validator.ValidStruct(c, f)
|
||||||
|
if len(errList) > 0 {
|
||||||
|
response.Fail(c, 101, errList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ex := service.AllService.ServerCmdService.Info(f.Id)
|
||||||
|
if ex.Id == 0 {
|
||||||
|
response.Fail(c, 101, response.TranslateMsg(c, "ItemNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.AllService.ServerCmdService.Update(f)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rustdesk) SendCmd(c *gin.Context) {
|
||||||
|
rc := &RustdeskCmd{}
|
||||||
|
c.ShouldBindJSON(rc)
|
||||||
|
if rc.Cmd == "" {
|
||||||
|
response.Fail(c, 101, "cmd is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := service.AllService.ServerCmdService.SendCmd(rc.Cmd, rc.Option)
|
||||||
|
if err != nil {
|
||||||
|
response.Fail(c, 101, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,9 +46,20 @@ func Init(g *gin.Engine) {
|
|||||||
ShareRecordBind(adg)
|
ShareRecordBind(adg)
|
||||||
MyBind(adg)
|
MyBind(adg)
|
||||||
|
|
||||||
|
RustdeskCmdBind(adg)
|
||||||
|
|
||||||
//访问静态文件
|
//访问静态文件
|
||||||
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
//g.StaticFS("/upload", http.Dir(global.Config.Gin.ResourcesPath+"/upload"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RustdeskCmdBind(adg *gin.RouterGroup) {
|
||||||
|
cont := &admin.Rustdesk{}
|
||||||
|
rg := adg.Group("/rustdesk")
|
||||||
|
rg.POST("/sendCmd", cont.SendCmd)
|
||||||
|
rg.GET("/cmdList", cont.CmdList)
|
||||||
|
rg.POST("/cmdDelete", cont.CmdDelete)
|
||||||
|
rg.POST("/cmdCreate", cont.CmdCreate)
|
||||||
|
}
|
||||||
func LoginBind(rg *gin.RouterGroup) {
|
func LoginBind(rg *gin.RouterGroup) {
|
||||||
cont := &admin.Login{}
|
cont := &admin.Login{}
|
||||||
rg.POST("/login", cont.Login)
|
rg.POST("/login", cont.Login)
|
||||||
|
|||||||
24
model/serverCmd.go
Normal file
24
model/serverCmd.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ServerCmd struct {
|
||||||
|
IdModel
|
||||||
|
Cmd string `json:"cmd" gorm:"default:'';not null;"`
|
||||||
|
Alias string `json:"alias" gorm:"default:'';not null;"`
|
||||||
|
Option string `json:"option" gorm:"default:'';not null;"`
|
||||||
|
Explain string `json:"explain" gorm:"default:'';not null;"`
|
||||||
|
TimeModel
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerCmdList struct {
|
||||||
|
ServerCmds []*ServerCmd `json:"list"`
|
||||||
|
Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
var SysServerCmds = []*ServerCmd{
|
||||||
|
{Cmd: "h", Option: "", Explain: "show help"},
|
||||||
|
{Cmd: "relay-servers", Alias: "rs", Option: "<separated by ,>", Explain: "set or show relay servers"},
|
||||||
|
{Cmd: "ip-blocker", Alias: "ib", Option: "[<ip>|<number>] [-]", Explain: "block or unblock ip or show blocked ip"},
|
||||||
|
{Cmd: "ip-changes", Alias: "ic", Option: "[<id>|<number>] [-]", Explain: "ip-changes(ic) [<id>|<number>] [-]"},
|
||||||
|
{Cmd: "always-use-relay(aur)", Alias: "aur", Option: "[y|n]", Explain: "always use relay"},
|
||||||
|
{Cmd: "test-geo", Alias: "tg", Option: "<ip1> <ip2>", Explain: "test geo"},
|
||||||
|
}
|
||||||
92
service/serverCmd.go
Normal file
92
service/serverCmd.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"Gwen/global"
|
||||||
|
"Gwen/model"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerCmdService struct{}
|
||||||
|
|
||||||
|
// List
|
||||||
|
func (is *ServerCmdService) List(page, pageSize uint) (res *model.ServerCmdList) {
|
||||||
|
res = &model.ServerCmdList{}
|
||||||
|
res.Page = int64(page)
|
||||||
|
res.PageSize = int64(pageSize)
|
||||||
|
tx := global.DB.Model(&model.ServerCmd{})
|
||||||
|
tx.Count(&res.Total)
|
||||||
|
tx.Scopes(Paginate(page, pageSize))
|
||||||
|
tx.Find(&res.ServerCmds)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info
|
||||||
|
func (is *ServerCmdService) Info(id uint) *model.ServerCmd {
|
||||||
|
u := &model.ServerCmd{}
|
||||||
|
global.DB.Where("id = ?", id).First(u)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
func (is *ServerCmdService) Delete(u *model.ServerCmd) error {
|
||||||
|
return global.DB.Delete(u).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create
|
||||||
|
func (is *ServerCmdService) Create(u *model.ServerCmd) error {
|
||||||
|
res := global.DB.Create(u).Error
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCmd 发送命令
|
||||||
|
func (is *ServerCmdService) SendCmd(cmd string, arg string) (string, error) {
|
||||||
|
//组装命令
|
||||||
|
cmd = cmd + " " + arg
|
||||||
|
res, err := is.SendSocketCmd("v6", cmd)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
//v6连接失败,尝试v4
|
||||||
|
res, err = is.SendSocketCmd("v4", cmd)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendSocketCmd
|
||||||
|
func (is *ServerCmdService) SendSocketCmd(ty string, cmd string) (string, error) {
|
||||||
|
addr := "[::1]"
|
||||||
|
tcp := "tcp6"
|
||||||
|
if ty == "v4" {
|
||||||
|
tcp = "tcp"
|
||||||
|
addr = "127.0.0.1"
|
||||||
|
}
|
||||||
|
conn, err := net.Dial(tcp, addr+":21115")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("connect to id %s server failed: %v\n", ty, err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
//发送命令
|
||||||
|
_, err = conn.Write([]byte(cmd))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("send cmd failed: %v\n", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
//读取返回
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil && err.Error() != "EOF" {
|
||||||
|
fmt.Printf("read response failed: %v\n", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf[:n]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *ServerCmdService) Update(f *model.ServerCmd) error {
|
||||||
|
return global.DB.Model(f).Updates(f).Error
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ type Service struct {
|
|||||||
*LoginLogService
|
*LoginLogService
|
||||||
*AuditService
|
*AuditService
|
||||||
*ShareRecordService
|
*ShareRecordService
|
||||||
|
*ServerCmdService
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Service {
|
func New() *Service {
|
||||||
|
|||||||
Reference in New Issue
Block a user