feat: SSH 服务支持开机自启设置 (#2578)

Refs #2225
This commit is contained in:
ssongliu 2023-10-17 17:52:34 +08:00 committed by GitHub
parent a33094d169
commit e9eea9c795
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 36 deletions

View file

@ -3,6 +3,7 @@ package dto
import "time"
type SSHInfo struct {
AutoStart bool `json:"authStart"`
Status string `json:"status"`
Message string `json:"message"`
Port string `json:"port"`

View file

@ -45,6 +45,7 @@ func NewISSHService() ISSHService {
func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data := dto.SSHInfo{
AutoStart: true,
Status: constant.StatusEnable,
Message: "",
Port: "22",
@ -67,7 +68,7 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data.Status = constant.StatusEnable
}
}
data.AutoStart, _ = systemctl.IsEnable(serviceName)
sshConf, err := os.ReadFile(sshPath)
if err != nil {
data.Message = err.Error()
@ -98,19 +99,19 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
}
func (u *SSHService) OperateSSH(operation string) error {
if operation == "start" || operation == "stop" || operation == "restart" {
serviceName, err := loadServiceName()
if err != nil {
return err
}
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
if err != nil {
return fmt.Errorf("%s %s failed, stdout: %s, err: %v", operation, serviceName, stdout, err)
}
return nil
serviceName, err := loadServiceName()
if err != nil {
return err
}
return fmt.Errorf("not support such operation: %s", operation)
sudo := cmd.SudoHandleCmd()
if operation == "enable" || operation == "disable" {
serviceName += ".service"
}
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
if err != nil {
return fmt.Errorf("%s %s failed, stdout: %s, err: %v", operation, serviceName, stdout, err)
}
return nil
}
func (u *SSHService) Update(key, value string) error {

View file

@ -2,9 +2,10 @@ package systemctl
import (
"fmt"
"github.com/pkg/errors"
"os/exec"
"strings"
"github.com/pkg/errors"
)
func RunSystemCtl(args ...string) (string, error) {
@ -24,6 +25,14 @@ func IsActive(serviceName string) (bool, error) {
return out == "active\n", nil
}
func IsEnable(serviceName string) (bool, error) {
out, err := RunSystemCtl("is-enabled", serviceName)
if err != nil {
return false, err
}
return out == "enabled\n", nil
}
func IsExist(serviceName string) (bool, error) {
out, err := RunSystemCtl("list-unit-files")
if err != nil {

View file

@ -14818,6 +14818,9 @@ const docTemplate = `{
"dto.SSHInfo": {
"type": "object",
"properties": {
"authStart": {
"type": "boolean"
},
"listenAddress": {
"type": "string"
},
@ -16277,6 +16280,21 @@ const docTemplate = `{
},
"showHidden": {
"type": "boolean"
},
"sortBy": {
"type": "string",
"enum": [
"name",
"size",
"modTime"
]
},
"sortOrder": {
"type": "string",
"enum": [
"ascending",
"descending"
]
}
}
},

View file

@ -14811,6 +14811,9 @@
"dto.SSHInfo": {
"type": "object",
"properties": {
"authStart": {
"type": "boolean"
},
"listenAddress": {
"type": "string"
},
@ -16270,6 +16273,21 @@
},
"showHidden": {
"type": "boolean"
},
"sortBy": {
"type": "string",
"enum": [
"name",
"size",
"modTime"
]
},
"sortOrder": {
"type": "string",
"enum": [
"ascending",
"descending"
]
}
}
},

View file

@ -1861,6 +1861,8 @@ definitions:
type: object
dto.SSHInfo:
properties:
authStart:
type: boolean
listenAddress:
type: string
message:
@ -2832,6 +2834,17 @@ definitions:
type: string
showHidden:
type: boolean
sortBy:
enum:
- name
- size
- modTime
type: string
sortOrder:
enum:
- ascending
- descending
type: string
type: object
request.FilePathCheck:
properties:

View file

@ -955,6 +955,9 @@ const message = {
fileHeper: 'Note: 1. Sorting is not supported after searching 2. Folders are not supported by size sorting',
},
ssh: {
autoStart: 'Auto Start',
enable: 'Enable Autostart',
disable: 'Disable Autostart',
sshAlert:
'The list data is sorted based on login time, but please note that changing time zones or other operations may cause deviations in the time of login logs.',
sshOperate: 'Operation [{0}] on the SSH service is performed. Do you want to continue?',

View file

@ -919,6 +919,9 @@ const message = {
fileHeper: '注意1.搜尋之後不支援排序 2.依大小排序不支援資料夾',
},
ssh: {
autoStart: '開機自啟',
enable: '設置開機自啟',
disable: '關閉開機自啟',
sshAlert: '列表數據根據登錄時間排序但請註意切換時區或其他操作可能導致登錄日誌的時間出現偏差',
sshOperate: ' SSH 服務進行 [{0}] 操作是否繼續',
sshChange: 'SSH 配置修改',

View file

@ -919,6 +919,9 @@ const message = {
fileHeper: '注意1.搜索之后不支持排序 2.按大小排序不支持文件夹',
},
ssh: {
autoStart: '开机自启',
enable: '设置开机自启',
disable: '关闭开机自启',
sshAlert: '列表数据根据登录时间排序但请注意切换时区或其他操作可能导致登录日志的时间出现偏差',
sshOperate: ' SSH 服务进行 [{0}] 操作是否继续',
sshChange: 'SSH 配置修改',

View file

@ -22,23 +22,28 @@
</el-tag>
</template>
</el-popover>
<span v-if="form.status === 'Enable'" class="buttons">
<el-button type="primary" @click="onOperate('stop')" link>
<span class="buttons">
<el-button v-if="form.status === 'Enable'" type="primary" @click="onOperate('stop')" link>
{{ $t('commons.button.stop') }}
</el-button>
<el-divider direction="vertical" />
<el-button type="primary" @click="onOperate('restart')" link>
{{ $t('container.restart') }}
</el-button>
</span>
<span v-if="form.status === 'Disable'" class="buttons">
<el-button type="primary" @click="onOperate('start')" link>
<el-button v-if="form.status === 'Disable'" type="primary" @click="onOperate('start')" link>
{{ $t('commons.button.start') }}
</el-button>
<el-divider direction="vertical" />
<el-button type="primary" @click="onOperate('restart')" link>
{{ $t('container.restart') }}
</el-button>
<el-divider direction="vertical" />
<el-button type="primary" link>
{{ $t('ssh.autoStart') }}
</el-button>
<el-switch
style="margin-left: 10px"
inactive-value="disable"
active-value="enable"
@change="onOperate(autoStart)"
v-model="autoStart"
/>
</span>
</div>
</el-card>
@ -170,6 +175,8 @@ const portRef = ref();
const addressRef = ref();
const rootsRef = ref();
const autoStart = ref('enable');
const sshConf = ref();
const form = reactive({
status: 'enable',
@ -218,22 +225,28 @@ const onChangeAddress = () => {
};
const onOperate = async (operation: string) => {
ElMessageBox.confirm(i18n.global.t('ssh.sshOperate', [i18n.global.t('commons.button.' + operation)]), 'SSH', {
let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';
ElMessageBox.confirm(i18n.global.t('ssh.sshOperate', [i18n.global.t(msg + operation)]), 'SSH', {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
loading.value = true;
await operateSSH(operation)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
});
})
.then(async () => {
loading.value = true;
await operateSSH(operation)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
autoStart.value = operation === 'enable' ? 'disable' : 'enable';
loading.value = false;
});
})
.catch(() => {
autoStart.value = operation === 'enable' ? 'disable' : 'enable';
});
};
const onSave = async (formEl: FormInstance | undefined, key: string, value: string) => {