mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-02-12 11:30:07 +08:00
fix: 解决 Node 运行环境状态异常问题 (#4197)
This commit is contained in:
parent
a33eabb4f0
commit
6ce35b723b
@ -203,3 +203,19 @@ func (b *BaseApi) OperateNodeModules(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithOutData(c)
|
helper.SuccessWithOutData(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tags Runtime
|
||||||
|
// @Summary Sync runtime status
|
||||||
|
// @Description 同步运行环境状态
|
||||||
|
// @Accept json
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /runtimes/sync [post]
|
||||||
|
func (b *BaseApi) SyncStatus(c *gin.Context) {
|
||||||
|
err := runtimeService.SyncRuntimeStatus()
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
@ -42,6 +42,7 @@ type IRuntimeService interface {
|
|||||||
GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error)
|
GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error)
|
||||||
OperateNodeModules(req request.NodeModuleOperateReq) error
|
OperateNodeModules(req request.NodeModuleOperateReq) error
|
||||||
SyncForRestart() error
|
SyncForRestart() error
|
||||||
|
SyncRuntimeStatus() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuntimeService() IRuntimeService {
|
func NewRuntimeService() IRuntimeService {
|
||||||
@ -586,3 +587,16 @@ func (r *RuntimeService) SyncForRestart() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RuntimeService) SyncRuntimeStatus() error {
|
||||||
|
runtimes, err := runtimeRepo.List()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, runtime := range runtimes {
|
||||||
|
if runtime.Type == constant.RuntimeNode {
|
||||||
|
_ = SyncRuntimeContainerStatus(&runtime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -181,33 +181,20 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
|
|||||||
}
|
}
|
||||||
container := containers[0]
|
container := containers[0]
|
||||||
|
|
||||||
interval := 10 * time.Second
|
switch container.State {
|
||||||
retries := 60
|
case "exited":
|
||||||
for i := 0; i < retries; i++ {
|
runtime.Status = constant.RuntimeError
|
||||||
resp, err := cli.InspectContainer(container.ID)
|
case "running":
|
||||||
if err != nil {
|
|
||||||
time.Sleep(interval)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if resp.State.Health != nil {
|
|
||||||
status := strings.ToLower(resp.State.Health.Status)
|
|
||||||
switch status {
|
|
||||||
case "starting":
|
|
||||||
runtime.Status = constant.RuntimeStarting
|
|
||||||
_ = runtimeRepo.Save(runtime)
|
|
||||||
case "healthy":
|
|
||||||
runtime.Status = constant.RuntimeRunning
|
runtime.Status = constant.RuntimeRunning
|
||||||
_ = runtimeRepo.Save(runtime)
|
case "paused":
|
||||||
return nil
|
runtime.Status = constant.RuntimeStopped
|
||||||
case "unhealthy":
|
default:
|
||||||
runtime.Status = constant.RuntimeUnhealthy
|
if runtime.Status != constant.RuntimeBuildIng {
|
||||||
_ = runtimeRepo.Save(runtime)
|
runtime.Status = constant.RuntimeStopped
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(interval)
|
|
||||||
}
|
return runtimeRepo.Save(runtime)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
|
||||||
|
@ -20,6 +20,7 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
groupRouter.POST("/del", baseApi.DeleteRuntime)
|
||||||
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
groupRouter.POST("/update", baseApi.UpdateRuntime)
|
||||||
groupRouter.GET("/:id", baseApi.GetRuntime)
|
groupRouter.GET("/:id", baseApi.GetRuntime)
|
||||||
|
groupRouter.POST("/sync", baseApi.SyncStatus)
|
||||||
|
|
||||||
groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript)
|
groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript)
|
||||||
groupRouter.POST("/operate", baseApi.OperateRuntime)
|
groupRouter.POST("/operate", baseApi.OperateRuntime)
|
||||||
|
@ -58,3 +58,7 @@ export const UpdatePHPExtensions = (req: Runtime.PHPExtensionsUpdate) => {
|
|||||||
export const DeletePHPExtensions = (req: Runtime.PHPExtensionsDelete) => {
|
export const DeletePHPExtensions = (req: Runtime.PHPExtensionsDelete) => {
|
||||||
return http.post<any>(`/runtimes/php/extensions/del`, req);
|
return http.post<any>(`/runtimes/php/extensions/del`, req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SyncRuntime = () => {
|
||||||
|
return http.post(`/runtimes/sync`, {});
|
||||||
|
};
|
||||||
|
@ -2240,6 +2240,35 @@ const message = {
|
|||||||
javaFileter: 'Java Dangerous File Filtering',
|
javaFileter: 'Java Dangerous File Filtering',
|
||||||
scannerFilter: 'Scanner filter',
|
scannerFilter: 'Scanner filter',
|
||||||
escapeFilter: 'escape filter',
|
escapeFilter: 'escape filter',
|
||||||
|
customRule: 'custom rule',
|
||||||
|
httpMethod: 'HTTP method filter',
|
||||||
|
fileExt: 'File upload limit',
|
||||||
|
fileExtHelper: 'File extensions that are prohibited from uploading',
|
||||||
|
deny: 'forbidden',
|
||||||
|
allow: 'only allow',
|
||||||
|
field: 'match object',
|
||||||
|
pattern: 'matching condition',
|
||||||
|
ruleContent: 'match content',
|
||||||
|
contain: 'include',
|
||||||
|
equal: 'equal',
|
||||||
|
regex: 'regular expression',
|
||||||
|
notEqual: 'Not equal to',
|
||||||
|
customRuleHelper: 'Perform corresponding actions based on condition matching',
|
||||||
|
actionAllow: 'Allow',
|
||||||
|
blockIP: 'Block IP',
|
||||||
|
code: 'return status code',
|
||||||
|
noRes: 'Disconnect 444',
|
||||||
|
badReq: 'Parameter error 400',
|
||||||
|
forbidden: 'Access Forbidden 403',
|
||||||
|
serverErr: 'Server error 500',
|
||||||
|
resHtml: 'Response page',
|
||||||
|
allowHelper: 'Allowing access will skip subsequent WAF rules, please use with caution',
|
||||||
|
captcha: 'human-machine verification',
|
||||||
|
fiveSeconds: '5 seconds verification',
|
||||||
|
location: 'Region',
|
||||||
|
redisConfig: 'Redis configuration',
|
||||||
|
redisHelper: 'Enable Redis to persist temporarily blocked IPs',
|
||||||
|
wafHelper: 'All websites will lose protection after closing',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
name: 'Website Monitor',
|
name: 'Website Monitor',
|
||||||
|
@ -2094,6 +2094,35 @@ const message = {
|
|||||||
javaFileter: 'Java 危險檔案過濾',
|
javaFileter: 'Java 危險檔案過濾',
|
||||||
scannerFilter: '掃描器過濾',
|
scannerFilter: '掃描器過濾',
|
||||||
escapeFilter: '轉義過濾',
|
escapeFilter: '轉義過濾',
|
||||||
|
customRule: '自訂規則',
|
||||||
|
httpMethod: 'HTTP 方法過濾',
|
||||||
|
fileExt: '檔案上傳限制',
|
||||||
|
fileExtHelper: '禁止上傳的檔案副檔名',
|
||||||
|
deny: '禁止',
|
||||||
|
allow: '只允許',
|
||||||
|
field: '匹配對象',
|
||||||
|
pattern: '符合條件',
|
||||||
|
ruleContent: '符合內容',
|
||||||
|
contain: '包含',
|
||||||
|
equal: '等於',
|
||||||
|
regex: '正規表示式',
|
||||||
|
notEqual: '不等於',
|
||||||
|
customRuleHelper: '根據條件匹配執行對應動作',
|
||||||
|
actionAllow: '允許',
|
||||||
|
blockIP: '封鎖 IP',
|
||||||
|
code: '返回狀態碼',
|
||||||
|
noRes: '斷開連線 444',
|
||||||
|
badReq: '參數錯誤 400',
|
||||||
|
forbidden: '禁止訪問 403',
|
||||||
|
serverErr: '伺服器錯誤 500',
|
||||||
|
resHtml: '回應頁面',
|
||||||
|
allowHelper: '允許訪問會跳過後續的 WAF 規則,請謹慎使用',
|
||||||
|
captcha: '人機驗證',
|
||||||
|
fiveSeconds: '5 秒驗證',
|
||||||
|
location: '地區',
|
||||||
|
redisConfig: 'Redis 配置',
|
||||||
|
redisHelper: '開啟 Redis 可以將暫時拉黑的 IP 持久化',
|
||||||
|
wafHelper: '關閉之後所有網站將失去防護',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
name: '網站監控',
|
name: '網站監控',
|
||||||
|
@ -2011,6 +2011,7 @@ const message = {
|
|||||||
},
|
},
|
||||||
xpack: {
|
xpack: {
|
||||||
name: '专业版',
|
name: '专业版',
|
||||||
|
menu: '高级功能',
|
||||||
waf: {
|
waf: {
|
||||||
name: 'WAF',
|
name: 'WAF',
|
||||||
blackWhite: '黑白名单',
|
blackWhite: '黑白名单',
|
||||||
@ -2065,7 +2066,7 @@ const message = {
|
|||||||
cookieHelper: '禁止请求中携带恶意 Cookie',
|
cookieHelper: '禁止请求中携带恶意 Cookie',
|
||||||
headerDefense: 'Header 规则',
|
headerDefense: 'Header 规则',
|
||||||
headerHelper: '禁止请求中携带恶意 Header',
|
headerHelper: '禁止请求中携带恶意 Header',
|
||||||
httpRule: 'HTTP 请求方法规则',
|
httpRule: 'HTTP 规则',
|
||||||
httpHelper: '限制网站的请求方法类型',
|
httpHelper: '限制网站的请求方法类型',
|
||||||
geoRule: '地区访问限制',
|
geoRule: '地区访问限制',
|
||||||
geoHelper: '限制某些地区访问你的网站',
|
geoHelper: '限制某些地区访问你的网站',
|
||||||
@ -2094,6 +2095,35 @@ const message = {
|
|||||||
javaFileter: 'Java 危险文件过滤',
|
javaFileter: 'Java 危险文件过滤',
|
||||||
scannerFilter: '扫描器过滤',
|
scannerFilter: '扫描器过滤',
|
||||||
escapeFilter: '转义过滤',
|
escapeFilter: '转义过滤',
|
||||||
|
customRule: '自定义规则',
|
||||||
|
httpMethod: 'HTTP 方法过滤',
|
||||||
|
fileExt: '文件上传限制',
|
||||||
|
fileExtHelper: '禁止上传的文件扩展名',
|
||||||
|
deny: '禁止',
|
||||||
|
allow: '仅允许',
|
||||||
|
field: '匹配对象',
|
||||||
|
pattern: '匹配条件',
|
||||||
|
ruleContent: '匹配内容',
|
||||||
|
contain: '包含',
|
||||||
|
equal: '等于',
|
||||||
|
regex: '正则表达式',
|
||||||
|
notEqual: '不等于',
|
||||||
|
customRuleHelper: '根据条件匹配执行相应动作',
|
||||||
|
actionAllow: '允许',
|
||||||
|
blockIP: '封禁 IP',
|
||||||
|
code: '返回状态码',
|
||||||
|
noRes: '断开连接 444',
|
||||||
|
badReq: '参数错误 400',
|
||||||
|
forbidden: '禁止访问 403',
|
||||||
|
serverErr: '服务器错误 500',
|
||||||
|
resHtml: '响应页面',
|
||||||
|
allowHelper: '允许访问会跳过后续的 WAF 规则,请谨慎使用',
|
||||||
|
captcha: '人机验证',
|
||||||
|
fiveSeconds: '5 秒验证',
|
||||||
|
location: '地区',
|
||||||
|
redisConfig: 'Redis 配置',
|
||||||
|
redisHelper: '开启 Redis 可以将临时拉黑的 IP 持久化',
|
||||||
|
wafHelper: '关闭之后所有网站将失去防护',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
name: '网站监控',
|
name: '网站监控',
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import { Runtime } from '@/api/interface/runtime';
|
import { Runtime } from '@/api/interface/runtime';
|
||||||
import { OperateRuntime, SearchRuntimes } from '@/api/modules/runtime';
|
import { OperateRuntime, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';
|
||||||
import { dateFormat } from '@/utils/util';
|
import { dateFormat } from '@/utils/util';
|
||||||
import OperateNode from '@/views/website/runtime/node/operate/index.vue';
|
import OperateNode from '@/views/website/runtime/node/operate/index.vue';
|
||||||
import Status from '@/components/status/index.vue';
|
import Status from '@/components/status/index.vue';
|
||||||
@ -192,6 +192,10 @@ const search = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sync = () => {
|
||||||
|
SyncRuntime();
|
||||||
|
};
|
||||||
|
|
||||||
const openModules = (row: Runtime.Runtime) => {
|
const openModules = (row: Runtime.Runtime) => {
|
||||||
moduleRef.value.acceptParams({ id: row.id, packageManager: row.params['PACKAGE_MANAGER'] });
|
moduleRef.value.acceptParams({ id: row.id, packageManager: row.params['PACKAGE_MANAGER'] });
|
||||||
};
|
};
|
||||||
@ -243,10 +247,12 @@ const toFolder = (folder: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
sync();
|
||||||
search();
|
search();
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
search();
|
search();
|
||||||
}, 10000 * 3);
|
sync();
|
||||||
|
}, 1000 * 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user