mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: Node.js 运行环境增加端口校验 (#2407)
This commit is contained in:
parent
ce6069da37
commit
aa588205e9
@ -715,7 +715,7 @@ func (b *BaseApi) ComposeUpdate(c *gin.Context) {
|
||||
// @Param follow query string false "是否追踪"
|
||||
// @Param tail query string false "显示行号"
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/compose/search/log [post]
|
||||
// @Router /containers/compose/search/log [get]
|
||||
func (b *BaseApi) ComposeLogs(c *gin.Context) {
|
||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
|
@ -25,6 +25,7 @@ type RuntimeCreate struct {
|
||||
type NodeConfig struct {
|
||||
Install bool `json:"install"`
|
||||
Clean bool `json:"clean"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type RuntimeDelete struct {
|
||||
|
@ -21,6 +21,8 @@ type RuntimeDTO struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CodeDir string `json:"codeDir"`
|
||||
AppParams []AppParam `json:"appParams"`
|
||||
Port int `json:"port"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type PackageScripts struct {
|
||||
@ -41,5 +43,7 @@ func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {
|
||||
CreatedAt: runtime.CreatedAt,
|
||||
CodeDir: runtime.CodeDir,
|
||||
Version: runtime.Version,
|
||||
Port: runtime.Port,
|
||||
Path: runtime.GetPath(),
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ type Runtime struct {
|
||||
Type string `gorm:"type:varchar;not null" json:"type"`
|
||||
Status string `gorm:"type:varchar;not null" json:"status"`
|
||||
Resource string `gorm:"type:varchar;not null" json:"resource"`
|
||||
Port int `gorm:"type:integer;" json:"port"`
|
||||
Message string `gorm:"type:longtext;" json:"message"`
|
||||
CodeDir string `gorm:"type:varchar;" json:"codeDir"`
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ type IRuntimeRepo interface {
|
||||
WithNotId(id uint) DBOption
|
||||
WithStatus(status string) DBOption
|
||||
WithDetailId(id uint) DBOption
|
||||
WithPort(port int) DBOption
|
||||
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
|
||||
Create(ctx context.Context, runtime *model.Runtime) error
|
||||
Save(runtime *model.Runtime) error
|
||||
@ -59,6 +60,12 @@ func (r *RuntimeRepo) WithNotId(id uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) WithPort(port int) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("port = ?", port)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) {
|
||||
var runtimes []model.Runtime
|
||||
db := getDb(opts...).Model(&model.Runtime{})
|
||||
|
@ -82,6 +82,33 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func checkPortExist(port int) error {
|
||||
errMap := make(map[string]interface{})
|
||||
errMap["port"] = port
|
||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))
|
||||
if appInstall.ID > 0 {
|
||||
errMap["type"] = i18n.GetMsgByKey("TYPE_APP")
|
||||
errMap["name"] = appInstall.Name
|
||||
return buserr.WithMap("ErrPortExist", errMap, nil)
|
||||
}
|
||||
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithPort(port))
|
||||
if runtime != nil {
|
||||
errMap["type"] = i18n.GetMsgByKey("TYPE_RUNTIME")
|
||||
errMap["name"] = runtime.Name
|
||||
return buserr.WithMap("ErrPortExist", errMap, nil)
|
||||
}
|
||||
domain, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithPort(port))
|
||||
if domain.ID > 0 {
|
||||
errMap["type"] = i18n.GetMsgByKey("TYPE_DOMAIN")
|
||||
errMap["name"] = domain.Domain
|
||||
return buserr.WithMap("ErrPortExist", errMap, nil)
|
||||
}
|
||||
if common.ScanPort(port) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var DatabaseKeys = map[string]uint{
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
@ -559,6 +586,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
|
||||
envParams[k] = strconv.FormatFloat(t, 'f', -1, 32)
|
||||
case uint:
|
||||
envParams[k] = strconv.Itoa(int(t))
|
||||
case int:
|
||||
envParams[k] = strconv.Itoa(t)
|
||||
default:
|
||||
envParams[k] = t.(string)
|
||||
}
|
||||
|
@ -39,7 +39,16 @@ func NewRuntimeService() IRuntimeService {
|
||||
}
|
||||
|
||||
func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
exist, _ := runtimeRepo.GetFirst(runtimeRepo.WithName(create.Name), commonRepo.WithByType(create.Type))
|
||||
var (
|
||||
opts []repo.DBOption
|
||||
)
|
||||
if create.Name != "" {
|
||||
opts = append(opts, commonRepo.WithLikeName(create.Name))
|
||||
}
|
||||
if create.Type != "" {
|
||||
opts = append(opts, commonRepo.WithByType(create.Type))
|
||||
}
|
||||
exist, _ := runtimeRepo.GetFirst(opts...)
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
@ -66,6 +75,9 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
return buserr.New(constant.ErrPathNotFound)
|
||||
}
|
||||
create.Install = true
|
||||
if err = checkPortExist(create.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
appDetail, err := appDetailRepo.GetFirst(commonRepo.WithByID(create.AppDetailID))
|
||||
@ -98,6 +110,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
|
||||
return
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
runtime.Port = create.Port
|
||||
if err = handleNode(create, runtime, fileOp, appVersionDir); err != nil {
|
||||
return
|
||||
}
|
||||
@ -286,6 +299,13 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
if exist != nil {
|
||||
return buserr.New(constant.ErrImageExist)
|
||||
}
|
||||
case constant.RuntimeNode:
|
||||
if runtime.Port != req.Port {
|
||||
if err = checkPortExist(req.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.Port = req.Port
|
||||
}
|
||||
}
|
||||
|
||||
projectDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
|
||||
@ -296,6 +316,9 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
Params: req.Params,
|
||||
CodeDir: req.CodeDir,
|
||||
Version: req.Version,
|
||||
NodeConfig: request.NodeConfig{
|
||||
Port: req.Port,
|
||||
},
|
||||
}
|
||||
composeContent, envContent, _, err := handleParams(create, projectDir)
|
||||
if err != nil {
|
||||
@ -321,6 +344,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
|
||||
case constant.RuntimeNode:
|
||||
runtime.Version = req.Version
|
||||
runtime.CodeDir = req.CodeDir
|
||||
runtime.Port = req.Port
|
||||
runtime.Status = constant.RuntimeReCreating
|
||||
_ = runtimeRepo.Save(runtime)
|
||||
go reCreateRuntime(runtime)
|
||||
|
@ -304,6 +304,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
|
||||
case constant.RuntimeNode:
|
||||
create.Params["CODE_DIR"] = create.CodeDir
|
||||
create.Params["NODE_VERSION"] = create.Version
|
||||
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
|
||||
if create.NodeConfig.Install {
|
||||
create.Params["RUN_INSTALL"] = "1"
|
||||
} else {
|
||||
|
@ -14,6 +14,10 @@ ErrNameIsExist: "Name is already exist"
|
||||
ErrDemoEnvironment: "Demo server, prohibit this operation!"
|
||||
ErrCmdTimeout: "Command execution timed out!"
|
||||
ErrCmdIllegal: "The command contains illegal characters. Please modify and try again!"
|
||||
ErrPortExist: '{{ .port }} port is already occupied by {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: "Application"
|
||||
TYPE_RUNTIME: "Runtime environment"
|
||||
TYPE_DOMAIN: "Domain name"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} port already in use"
|
||||
|
@ -14,6 +14,10 @@ ErrNameIsExist: "名稱已存在"
|
||||
ErrDemoEnvironment: "演示伺服器,禁止此操作!"
|
||||
ErrCmdTimeout: "指令執行超時!"
|
||||
ErrCmdIllegal: "執行命令中存在不合法字符,請修改後重試!"
|
||||
ErrPortExist: '{{ .port }} 埠已被 {{ .type }} [{{ .name }}] 佔用'
|
||||
TYPE_APP: "應用"
|
||||
TYPE_RUNTIME: "運作環境"
|
||||
TYPE_DOMAIN: "網域名稱"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
|
||||
|
@ -14,6 +14,10 @@ ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrCmdIllegal: "执行命令中存在不合法字符,请修改后重试!"
|
||||
ErrPortExist: '{{ .port }} 端口已被 {{ .type }} [{{ .name }}] 占用'
|
||||
TYPE_APP: "应用"
|
||||
TYPE_RUNTIME: "运行环境"
|
||||
TYPE_DOMAIN: "域名"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
|
@ -17,7 +17,7 @@ var AddDefaultNetwork = &gormigrate.Migration{
|
||||
}
|
||||
|
||||
var UpdateRuntime = &gormigrate.Migration{
|
||||
ID: "20230920-update-runtime",
|
||||
ID: "20230927-update-runtime",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Runtime{}); err != nil {
|
||||
return err
|
||||
|
@ -14,6 +14,7 @@ export namespace Runtime {
|
||||
version: string;
|
||||
status: string;
|
||||
codeDir: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export interface RuntimeReq extends ReqPage {
|
||||
@ -35,6 +36,7 @@ export namespace Runtime {
|
||||
appParams: App.InstallParams[];
|
||||
appID: number;
|
||||
source?: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface RuntimeCreate {
|
||||
@ -50,6 +52,7 @@ export namespace Runtime {
|
||||
rebuild?: boolean;
|
||||
source?: string;
|
||||
codeDir?: string;
|
||||
port?: number;
|
||||
}
|
||||
|
||||
export interface RuntimeUpdate {
|
||||
|
@ -13,7 +13,7 @@
|
||||
>
|
||||
{{ $t('app.all') }}
|
||||
</el-button>
|
||||
<div v-for="item in tags" :key="item.key" style="display: inline">
|
||||
<div v-for="item in tags" :key="item.key" class="inline">
|
||||
<el-button
|
||||
class="tag-button"
|
||||
:class="activeTag === item.key ? '' : 'no-active'"
|
||||
@ -56,12 +56,7 @@
|
||||
<template #default>
|
||||
<span>
|
||||
<span>{{ $t('app.installHelper') }}</span>
|
||||
<el-link
|
||||
style="font-size: 12px; margin-left: 5px"
|
||||
icon="Position"
|
||||
@click="quickJump()"
|
||||
type="primary"
|
||||
>
|
||||
<el-link class="text-xs scroll-ml-1" icon="Position" @click="quickJump()" type="primary">
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</el-link>
|
||||
</span>
|
||||
|
@ -31,10 +31,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('runtime.version')" prop="version"></el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('runtime.externalPort')"
|
||||
prop="params.PANEL_APP_PORT_HTTP"
|
||||
></el-table-column>
|
||||
<el-table-column :label="$t('runtime.externalPort')" prop="port">
|
||||
<template #default="{ row }">
|
||||
{{ row.port }}
|
||||
<el-button link :icon="Promotion" @click="goDashboard(row.port, 'http')"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||
<template #default="{ row }">
|
||||
<el-popover
|
||||
@ -53,6 +55,11 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.button.log')" prop="path">
|
||||
<template #default="{ row }">
|
||||
<el-button @click="openLog(row)" link type="primary">{{ $t('website.check') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
:label="$t('commons.table.date')"
|
||||
@ -74,6 +81,8 @@
|
||||
</LayoutContent>
|
||||
<OperateNode ref="operateRef" @close="search" />
|
||||
<Delete ref="deleteRef" @close="search" />
|
||||
<ComposeLogs ref="composeLogRef" />
|
||||
<PortJumpDialog ref="dialogPortJumpRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -88,6 +97,17 @@ import Delete from '@/views/website/runtime/delete/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import RouterMenu from '../index.vue';
|
||||
import router from '@/routers/router';
|
||||
import ComposeLogs from '@/components/compose-log/index.vue';
|
||||
import { Promotion } from '@element-plus/icons-vue';
|
||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
const loading = ref(false);
|
||||
const items = ref<Runtime.RuntimeDTO[]>([]);
|
||||
const operateRef = ref();
|
||||
const deleteRef = ref();
|
||||
const dialogPortJumpRef = ref();
|
||||
const composeLogRef = ref();
|
||||
|
||||
const paginationConfig = reactive({
|
||||
cacheSizeKey: 'runtime-page-size',
|
||||
@ -101,8 +121,6 @@ const req = reactive<Runtime.RuntimeReq>({
|
||||
pageSize: 40,
|
||||
type: 'node',
|
||||
});
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('container.stop'),
|
||||
@ -147,10 +165,6 @@ const buttons = [
|
||||
},
|
||||
},
|
||||
];
|
||||
const loading = ref(false);
|
||||
const items = ref<Runtime.RuntimeDTO[]>([]);
|
||||
const operateRef = ref();
|
||||
const deleteRef = ref();
|
||||
|
||||
const disabledRuntime = (row: Runtime.Runtime) => {
|
||||
return row.status === 'starting' || row.status === 'recreating';
|
||||
@ -182,6 +196,14 @@ const openDelete = async (row: Runtime.Runtime) => {
|
||||
deleteRef.value.acceptParams(row.id, row.name);
|
||||
};
|
||||
|
||||
const openLog = (row: any) => {
|
||||
composeLogRef.value.acceptParams({ compose: row.path + '/docker-compose.yml', resource: row.name });
|
||||
};
|
||||
|
||||
const goDashboard = async (port: any, protocol: string) => {
|
||||
dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });
|
||||
};
|
||||
|
||||
const operateRuntime = async (operate: string, ID: number) => {
|
||||
try {
|
||||
const action = await ElMessageBox.confirm(
|
||||
|
@ -89,8 +89,8 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item :label="$t('runtime.externalPort')" prop="params.PANEL_APP_PORT_HTTP">
|
||||
<el-input v-model.number="runtime.params['PANEL_APP_PORT_HTTP']" />
|
||||
<el-form-item :label="$t('runtime.externalPort')" prop="port">
|
||||
<el-input v-model.number="runtime.port" />
|
||||
<span class="input-help">{{ $t('runtime.externalPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -170,15 +170,16 @@ const initData = (type: string) => ({
|
||||
resource: 'appstore',
|
||||
rebuild: false,
|
||||
codeDir: '/',
|
||||
port: 3000,
|
||||
});
|
||||
let runtime = reactive<Runtime.RuntimeCreate>(initData('node'));
|
||||
const rules = ref<any>({
|
||||
name: [Rules.appName],
|
||||
appID: [Rules.requiredSelect],
|
||||
codeDir: [Rules.requiredInput],
|
||||
port: [Rules.requiredInput, Rules.port],
|
||||
params: {
|
||||
NODE_APP_PORT: [Rules.requiredInput, Rules.port],
|
||||
PANEL_APP_PORT_HTTP: [Rules.requiredInput, Rules.port],
|
||||
PACKAGE_MANAGER: [Rules.requiredSelect],
|
||||
HOST_IP: [Rules.requiredSelect],
|
||||
EXEC_SCRIPT: [Rules.requiredSelect],
|
||||
@ -192,7 +193,7 @@ watch(
|
||||
() => runtime.params['NODE_APP_PORT'],
|
||||
(newVal) => {
|
||||
if (newVal && mode.value == 'create') {
|
||||
runtime.params['PANEL_APP_PORT_HTTP'] = newVal;
|
||||
runtime.port = newVal;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
@ -325,6 +326,7 @@ const getRuntime = async (id: number) => {
|
||||
source: data.source,
|
||||
params: data.params,
|
||||
codeDir: data.codeDir,
|
||||
port: data.port,
|
||||
});
|
||||
editParams.value = data.appParams;
|
||||
if (mode.value == 'edit') {
|
||||
|
Loading…
Reference in New Issue
Block a user