1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: 网站增加选择 node.js 运行环境 (#2444)

This commit is contained in:
zhengkunwang 2023-10-07 07:30:45 -05:00 committed by GitHub
parent f258d4e443
commit d7bdf5813d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 279 additions and 82 deletions

View File

@ -0,0 +1,129 @@
package service
import (
"encoding/json"
"errors"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"io/fs"
"os"
"path"
"strings"
"time"
)
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
}
}
defer func() {
_ = os.RemoveAll(tmpDir)
}()
remarkInfo, _ := json.Marshal(runtime)
remarkInfoPath := fmt.Sprintf("%s/runtime.json", tmpDir)
if err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {
return err
}
appPath := runtime.GetPath()
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", ""); err != nil {
return err
}
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
return err
}
return nil
}
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool) error {
isOk := false
fileOp := files.NewFileOp()
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
return err
}
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
defer func() {
_, _ = compose.Up(runtime.GetComposePath())
_ = os.RemoveAll(strings.ReplaceAll(recoverFile, ".tar.gz", ""))
}()
if !fileOp.Stat(tmpPath+"/runtime.json") || !fileOp.Stat(tmpPath+"/runtime.tar.gz") {
return errors.New("the wrong recovery package does not have runtime.json or runtime.tar.gz files")
}
var oldRuntime model.Runtime
runtimeJson, err := os.ReadFile(tmpPath + "/runtime.json")
if err != nil {
return err
}
if err := json.Unmarshal(runtimeJson, &oldRuntime); err != nil {
return fmt.Errorf("unmarshal runtime.json failed, err: %v", err)
}
if oldRuntime.Type != runtime.Type || oldRuntime.Name != runtime.Name {
return errors.New("the current backup file does not match the application")
}
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format("20060102150405")))
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
return fmt.Errorf("backup runtime %s for rollback before recover failed, err: %v", runtime.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRuntimeRecover(runtime, rollbackFile, true); err != nil {
global.LOG.Errorf("rollback runtime %s from %s failed, err: %v", runtime.Name, rollbackFile, err)
return
}
global.LOG.Infof("rollback runtime %s from %s successful", runtime.Name, rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
newEnvFile, err := coverEnvJsonToStr(runtime.Env)
if err != nil {
return err
}
runtimeDir := runtime.GetPath()
backPath := fmt.Sprintf("%s_bak", runtimeDir)
_ = fileOp.Rename(runtimeDir, backPath)
_ = fileOp.CreateDir(runtimeDir, 0755)
if err := handleUnTar(tmpPath+"/runtime.tar.gz", fmt.Sprintf("%s/%s", constant.RuntimeDir, runtime.Type)); err != nil {
global.LOG.Errorf("handle recover from runtime.tar.gz failed, err: %v", err)
_ = fileOp.DeleteDir(runtimeDir)
_ = fileOp.Rename(backPath, runtimeDir)
return err
}
_ = fileOp.DeleteDir(backPath)
if len(newEnvFile) != 0 {
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.RuntimeDir, runtime.Type, runtime.Name)
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
_, _ = file.WriteString(newEnvFile)
}
oldRuntime.ID = runtime.ID
oldRuntime.Status = constant.RuntimeStarting
if err := runtimeRepo.Save(&oldRuntime); err != nil {
global.LOG.Errorf("save db app install failed, err: %v", err)
return err
}
isOk = true
return nil
}

View File

@ -132,7 +132,8 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
if website.Type == constant.Deployment {
switch website.Type {
case constant.Deployment:
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return err
@ -145,7 +146,19 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
global.LOG.Errorf("docker-compose restart failed, err: %v", err)
return err
}
case constant.Runtime:
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
if err != nil {
return err
}
if runtime.Type == constant.RuntimeNode {
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true); err != nil {
return err
}
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
}
}
siteDir := fmt.Sprintf("%s/openresty/%s/www/sites", constant.AppInstallDir, nginxInfo.Name)
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir); err != nil {
global.LOG.Errorf("handle recover from web.tar.gz failed, err: %v", err)
@ -194,7 +207,8 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
}
global.LOG.Info("put openresty conf into tmp dir successful")
if website.Type == constant.Deployment {
switch website.Type {
case constant.Deployment:
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return err
@ -203,7 +217,19 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
return err
}
global.LOG.Info("put app.tar.gz into tmp dir successful")
case constant.Runtime:
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID))
if err != nil {
return err
}
if runtime.Type == constant.RuntimeNode {
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias)); err != nil {
return err
}
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
}
}
websiteDir := fmt.Sprintf("%s/openresty/%s/www/sites/%s", constant.AppInstallDir, nginxInfo.Name, website.Alias)
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), ""); err != nil {
return err

View File

@ -263,6 +263,8 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
return err
}
website.RuntimeID = runtime.ID
switch runtime.Type {
case constant.RuntimePHP:
if runtime.Resource == constant.ResourceAppstore {
var (
req request.AppInstallCreate
@ -295,6 +297,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
}
website.Proxy = proxy
}
case constant.RuntimeNode:
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
}
}
var domains []model.WebsiteDomain

View File

@ -216,6 +216,8 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
server.UpdateDirective("include", []string{nginxInclude})
case constant.Runtime:
switch runtime.Type {
case constant.RuntimePHP:
if runtime.Resource == constant.ResourceLocal {
switch runtime.Type {
case constant.RuntimePHP:
@ -223,13 +225,13 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
localPath := path.Join(nginxInstall.GetPath(), rootIndex, "index.php")
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
}
}
if runtime.Resource == constant.ResourceAppstore {
switch runtime.Type {
case constant.RuntimePHP:
} else {
server.UpdateRoot(rootIndex)
server.UpdatePHPProxy([]string{website.Proxy}, "")
}
case constant.RuntimeNode:
proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port)
server.UpdateRootProxy([]string{proxy})
}
}

View File

@ -93,7 +93,7 @@ onMounted(() => {
website.value = res.data;
if (res.data.type === 'runtime') {
const runRes = await GetRuntime(res.data.runtimeID);
if (runRes.data.resource === 'appstore') {
if (runRes.data.type == 'php' && runRes.data.resource === 'appstore') {
configPHP.value = true;
}
}

View File

@ -39,7 +39,7 @@ const getWebsiteDetail = async () => {
if (res.data.type === 'runtime') {
installId.value = res.data.appInstallId;
const runRes = await GetRuntime(res.data.runtimeID);
if (runRes.data.resource === 'appstore') {
if (runRes.data.type == 'php' && runRes.data.resource === 'appstore') {
configPHP.value = true;
}
}

View File

@ -152,6 +152,16 @@
</div>
</div>
<div v-if="website.type === 'runtime'">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="$t('commons.table.type')" prop="runtimeType">
<el-select v-model="website.runtimeType" @change="changeRuntimeType()">
<el-option label="PHP" value="php"></el-option>
<el-option label="Node.js" value="node"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('runtime.runtime')" prop="runtimeID">
<el-select
v-model="website.runtimeID"
@ -161,11 +171,21 @@
<el-option
v-for="run in runtimes"
:key="run.name"
:label="run.name + '(' + $t('runtime.' + run.resource) + ')'"
:label="run.name + ' [' + $t('runtime.' + run.resource) + ']'"
:value="run.id"
></el-option>
>
<el-row>
<el-col :span="8">{{ run.name }}</el-col>
<el-col :span="12">
{{ ' [' + $t('runtime.' + run.resource) + ']' }}
</el-col>
</el-row>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<div v-if="website.runtimeType === 'php'">
<Params
v-if="runtimeResource === 'appstore'"
:key="paramKey"
@ -190,10 +210,12 @@
</el-form-item>
</div>
</div>
</div>
<el-form-item
prop="advanced"
v-if="
website.type === 'runtime' || (website.type === 'deployment' && website.appType === 'new')
(website.type === 'runtime' && website.runtimeType === 'php') ||
(website.type === 'deployment' && website.appType === 'new')
"
>
<el-checkbox v-model="website.appinstall.advanced" :label="$t('app.advanced')" size="large" />
@ -363,6 +385,7 @@ const website = ref({
port: 9000,
proxyProtocol: 'http://',
proxyAddress: '',
runtimeType: 'php',
});
const rules = ref<any>({
primaryDomain: [Rules.domainWithPort],
@ -383,6 +406,7 @@ const rules = ref<any>({
},
proxyType: [Rules.requiredSelect],
port: [Rules.port],
runtimeType: [Rules.requiredInput],
});
const open = ref(false);
@ -500,6 +524,16 @@ const getAppDetailByID = (id: number) => {
});
};
const changeRuntimeType = () => {
runtimeReq.value.type = website.value.runtimeType;
if (website.value.runtimeType == 'php') {
runtimeReq.value.status = 'normal';
} else {
runtimeReq.value.status = 'running';
}
getRuntimes();
};
const changeRuntime = (runID: number) => {
runtimes.value.forEach((item) => {
if (item.id === runID) {