1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-13 17:24:44 +08:00

feat: 增加创建 WORDPRESS 之前创建数据库功能

This commit is contained in:
zhengkunwang223 2022-10-09 23:35:24 +08:00 committed by zhengkunwang223
parent dc70d7cfc1
commit ad50907158
25 changed files with 427 additions and 201 deletions

View File

@ -39,7 +39,7 @@
"author": "Nginx",
"type": "internal",
"required": [""],
"limit": 0,
"limit": 1,
"crossVersionUpdate": true,
"source": "http://nginx.org/"
},

View File

@ -7,14 +7,11 @@ services:
restart: always
environment:
TZ: ${TZ}
MYSQL_DATABASE: ${DATABASE}
MYSQL_USER: ${USER}
MYSQL_PASSWORD: ${PASSWORD}
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
MYSQL_ROOT_PASSWORD: ${PANEL_DB_ROOT_PASSWORD}
networks:
- 1panel
ports:
- ${PORT}:3306
- ${PANEL_APP_PORT}:3306
volumes:
- ./data/:/var/lib/mysql
- ./conf/my.cnf:/etc/mysql/my.cnf

View File

@ -1,44 +1,12 @@
{
"formFields": [
{
"type": "text",
"labelZh": "时区",
"labelEn": "TimeZone",
"required": true,
"default": "Asia/Shanghai",
"envKey": "TZ"
},
{
"type": "text",
"labelZh": "数据库",
"labelEn": "Database",
"required": true,
"default": "db",
"envKey": "DATABASE"
},
{
"type": "text",
"labelZh": "普通用户",
"labelEn": "User",
"required": true,
"default": "mysql",
"envKey": "USER"
},
{
"type": "text",
"labelZh": "普通用户密码",
"labelEn": "Password",
"required": true,
"default": "1qaz@WSX",
"envKey": "PASSWORD"
},
{
"type": "text",
"labelZh": "Root用户密码",
"labelEn": "RootPassword",
"required": true,
"default": "1panel@mysql",
"envKey": "ROOT_PASSWORD"
"envKey": "PANEL_DB_ROOT_PASSWORD"
},
{
"type": "number",
@ -46,7 +14,7 @@
"labelEn": "Port",
"required": true,
"default": 3306,
"envKey": "PORT"
"envKey": "PANEL_APP_PORT"
}
]
}

View File

@ -7,14 +7,11 @@ services:
restart: always
environment:
TZ: ${TZ}
MYSQL_DATABASE: ${DATABASE}
MYSQL_USER: ${USER}
MYSQL_PASSWORD: ${PASSWORD}
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
MYSQL_ROOT_PASSWORD: ${PANEL_DB_ROOT_PASSWORD}
networks:
- 1panel
ports:
- ${PORT}:3306
- ${PANEL_APP_PORT}:3306
volumes:
- ./data/:/var/lib/mysql
- ./conf/my.cnf:/etc/my.cnf

View File

@ -1,44 +1,12 @@
{
"formFields": [
{
"type": "text",
"labelZh": "时区",
"labelEn": "TimeZone",
"required": true,
"default": "Asia/Shanghai",
"envKey": "TZ"
},
{
"type": "text",
"labelZh": "数据库",
"labelEn": "Database",
"required": true,
"default": "db",
"envKey": "DATABASE"
},
{
"type": "text",
"labelZh": "普通用户",
"labelEn": "User",
"required": true,
"default": "mysql",
"envKey": "USER"
},
{
"type": "text",
"labelZh": "普通用户密码",
"labelEn": "Password",
"required": true,
"default": "1qaz@WSX",
"envKey": "PASSWORD"
},
{
"type": "text",
"labelZh": "Root用户密码",
"labelEn": "RootPassword",
"required": true,
"default": "1panel@mysql",
"envKey": "ROOT_PASSWORD"
"envKey": "PANEL_DB_ROOT_PASSWORD"
},
{
"type": "number",
@ -46,7 +14,7 @@
"labelEn": "Port",
"required": true,
"default": 3306,
"envKey": "PORT"
"envKey": "PANEL_APP_PORT"
}
]
}

View File

@ -4,10 +4,10 @@ services:
image: nginx:1.23.1
restart: always
ports:
- ${PORT}:80
- ${PANEL_APP_PORT}:80
volumes:
- ./conf/nginx.conf:/etc/nginx/nginx.conf
- ./www:/home/www
- ./log:/var/log/nginx
- ./conf/conf.d/default.conf:/etc/nginx/conf.d/default.conf
- ./conf/conf.d:/etc/nginx/conf.d/
- ./html:/usr/share/nginx/html

View File

@ -6,7 +6,7 @@
"labelEn": "Port",
"required": true,
"default": 80,
"envKey": "PORT"
"envKey": "PANEL_APP_PORT"
}
]
}

View File

@ -2,19 +2,19 @@ version: '3'
services:
1panel_wordpress:
image: wordpress:6.0.1
container_name: 1panel_wordpress
container_name: ${CONTAINER_NAME}
ports:
- ${PORT}:80
- ${PANEL_APP_PORT}:80
restart: always
networks:
- 1panel
volumes:
- ./data:/var/www/html
environment:
WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME}
WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
WORDPRESS_DB_HOST: ${PANEL_DB_HOST}
WORDPRESS_DB_NAME: ${PANEL_DB_NAME}
WORDPRESS_DB_USER: ${PANEL_DB_USER}
WORDPRESS_DB_PASSWORD: ${PANEL_DB_USER_PASSWORD}
WORDPRESS_DEBUG: 1
networks:

View File

@ -6,32 +6,32 @@
"labelZh": "数据库服务",
"labelEn": "Database Service",
"required": true,
"default": "1Panel-mysql",
"envKey": "WORDPRESS_DB_HOST"
"default": "",
"envKey": "PANEL_DB_HOST"
},
{
"type": "text",
"labelZh": "数据库名",
"labelEn": "Database",
"required": true,
"default": "db",
"envKey": "WORDPRESS_DB_NAME"
"default": "random",
"envKey": "PANEL_DB_NAME"
},
{
"type": "text",
"labelZh": "数据库用户",
"labelEn": "User",
"required": true,
"default": "wordpress_user",
"envKey": "WORDPRESS_DB_USER"
"default": "random",
"envKey": "PANEL_DB_USER"
},
{
"type": "text",
"labelZh": "数据库用户密码",
"labelEn": "Password",
"required": true,
"default": "1qaz@WSX",
"envKey": "WORDPRESS_DB_PASSWORD"
"default": "random",
"envKey": "PANEL_DB_USER_PASSWORD"
},
{
"type": "number",
@ -39,7 +39,7 @@
"labelEn": "Port",
"required": true,
"default": 8080,
"envKey": "PORT"
"envKey": "PANEL_APP_PORT"
}
]
}

View File

@ -111,3 +111,20 @@ type AppService struct {
Label string `json:"label"`
Value string `json:"value"`
}
type AppDatabase struct {
ServiceName string `json:"PANEL_DB_HOST"`
DbName string `json:"PANEL_DB_NAME"`
DbUser string `json:"PANEL_DB_USER"`
Password string `json:"PANEL_DB_USER_PASSWORD"`
}
type AuthParam struct {
RootPassword string `json:"PANEL_DB_ROOT_PASSWORD"`
}
type ContainerExec struct {
ContainerName string `json:"containerName"`
DbParam AppDatabase `json:"dbParam"`
Auth AuthParam `json:"auth"`
}

View File

@ -2,8 +2,10 @@ package model
type AppContainer struct {
BaseModel
ServiceName string `json:"serviceName" gorm:"type:varchar(64);not null"`
ContainerName string `json:"containerName" gorm:"type:varchar(64);not null"`
AppInstallId uint `json:"appInstallId" gorm:"type:integer;not null"`
Port int `json:"port" gorm:"type:integer;not null"`
ServiceName string `json:"serviceName" gorm:"type:varchar(64);not null"`
ContainerName string `json:"containerName" gorm:"type:varchar(64);not null"`
AppInstallID uint `json:"appInstallId" gorm:"type:integer;not null"`
Port int `json:"port" gorm:"type:integer;not null"`
Auth string `json:"auth" gorm:"type:longtext;not null"`
AppInstall AppInstall `gorm:"-"`
}

View File

@ -31,7 +31,7 @@ func (i *AppInstall) GetComposePath() string {
func (i *AppInstall) BeforeDelete(tx *gorm.DB) (err error) {
if err = tx.Where("app_install_id = ?", i.ID).Delete(&AppContainer{}).Error; err != nil {
if err = tx.Model(AppContainer{}).Debug().Where("app_install_id = ?", i.ID).Delete(AppContainer{}).Error; err != nil {
return err
}

View File

@ -0,0 +1,10 @@
package model
type Database struct {
BaseModel
AppContainerId uint `json:"appContainerId" gorm:"type:integer;not null"`
Key string `json:"key" gorm:"type:varchar(64);not null"`
Dbname string `json:"dbname" gorm:"type:varchar(256);not null"`
Username string `json:"username" gorm:"type:varchar(256);not null"`
Password string `json:"password" gorm:"type:varchar(256);not null"`
}

View File

@ -35,7 +35,7 @@ func (a AppRepo) GetFirst(opts ...DBOption) (model.App, error) {
for _, opt := range opts {
db = opt(db)
}
if err := db.First(&app).Error; err != nil {
if err := db.Preload("AppTags").First(&app).Error; err != nil {
return app, err
}
return app, nil

View File

@ -16,6 +16,12 @@ func (a AppContainerRepo) WithAppId(appId uint) DBOption {
}
}
func (a AppContainerRepo) WithServiceName(serviceName string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("service_name = ?", serviceName)
}
}
func (a AppContainerRepo) GetBy(opts ...DBOption) ([]model.AppContainer, error) {
db := global.DB.Model(&model.AppContainer{})
var appContainers []model.AppContainer
@ -26,6 +32,16 @@ func (a AppContainerRepo) GetBy(opts ...DBOption) ([]model.AppContainer, error)
return appContainers, err
}
func (a AppContainerRepo) GetFirst(opts ...DBOption) (model.AppContainer, error) {
db := global.DB.Model(&model.AppContainer{})
var appContainer model.AppContainer
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&appContainer).Error
return appContainer, err
}
func (a AppContainerRepo) Create(container *model.AppContainer) error {
db := global.DB.Model(&model.AppContainer{})
return db.Create(&container).Error

View File

@ -1,6 +1,7 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/global"
"gorm.io/gorm"
@ -44,8 +45,8 @@ func (a AppInstallRepo) GetFirst(opts ...DBOption) (model.AppInstall, error) {
return install, err
}
func (a AppInstallRepo) Create(install *model.AppInstall) error {
db := global.DB.Model(&model.AppInstall{})
func (a AppInstallRepo) Create(ctx context.Context, install *model.AppInstall) error {
db := ctx.Value("db").(*gorm.DB).Model(&model.AppInstall{})
return db.Create(&install).Error
}
@ -54,7 +55,7 @@ func (a AppInstallRepo) Save(install model.AppInstall) error {
return db.Save(&install).Error
}
func (a AppInstallRepo) Delete(opts ...DBOption) error {
func (a AppInstallRepo) DeleteBy(opts ...DBOption) error {
db := global.DB.Model(&model.AppInstall{})
for _, opt := range opts {
db = opt(db)
@ -62,6 +63,11 @@ func (a AppInstallRepo) Delete(opts ...DBOption) error {
return db.Delete(&model.AppInstall{}).Error
}
func (a AppInstallRepo) Delete(install model.AppInstall) error {
db := global.DB
return db.Delete(&install).Error
}
func (a AppInstallRepo) Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error) {
var apps []model.AppInstall
db := global.DB.Model(&model.AppInstall{})

View File

@ -0,0 +1,47 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/app/model"
"gorm.io/gorm"
)
type DatabaseRepo struct {
}
func (d DatabaseRepo) Create(ctx context.Context, database *model.Database) error {
db := ctx.Value("db").(*gorm.DB).Model(&model.Database{})
return db.Create(&database).Error
}
//func (a DatabaseRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
// db := ctx.Value("db").(*gorm.DB)
// return db.Create(&tags).Error
//}
//func (d DatabaseRepo) DeleteBy(ctx context.Context, appIds []uint) error {
// db := ctx.Value("db").(*gorm.DB)
// return db.Where("app_id in (?)", appIds).Delete(&model.AppTag{}).Error
//}
//
//func (a AppTagRepo) DeleteAll(ctx context.Context) error {
// db := ctx.Value("db").(*gorm.DB)
// return db.Where("1 = 1").Delete(&model.AppTag{}).Error
//}
//
//func (a AppTagRepo) GetByAppId(appId uint) ([]model.AppTag, error) {
// var appTags []model.AppTag
// if err := global.DB.Where("app_id = ?", appId).Find(&appTags).Error; err != nil {
// return nil, err
// }
// return appTags, nil
//}
//
//func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {
// var appTags []model.AppTag
// if err := global.DB.Where("tag_id in (?)", tagIds).Find(&appTags).Error; err != nil {
// return nil, err
// }
// return appTags, nil
//}

View File

@ -15,6 +15,7 @@ type RepoGroup struct {
AppDetailRepo
AppInstallRepo
AppContainerRepo
DatabaseRepo
}
var RepoGroupApp = new(RepoGroup)

View File

@ -43,3 +43,11 @@ func (t TagRepo) GetByKeys(keys []string) ([]model.Tag, error) {
}
return tags, nil
}
func (t TagRepo) GetByAppId(appId uint) ([]model.Tag, error) {
var tags []model.Tag
if err := global.DB.Where("id in (select tag_id from app_tags where app_id = ?)", appId).Find(&tags).Error; err != nil {
return nil, err
}
return tags, nil
}

View File

@ -10,6 +10,7 @@ import (
"github.com/1Panel-dev/1Panel/app/repo"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/cmd"
"github.com/1Panel-dev/1Panel/utils/common"
"github.com/1Panel-dev/1Panel/utils/compose"
"github.com/1Panel-dev/1Panel/utils/docker"
@ -19,6 +20,7 @@ import (
"gopkg.in/yaml.v3"
"os"
"path"
"reflect"
"strconv"
"strings"
)
@ -179,7 +181,7 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
appDir := install.GetPath()
dir, _ := os.Stat(appDir)
if dir == nil {
return appInstallRepo.Delete(commonRepo.WithByID(install.ID))
return appInstallRepo.Delete(install)
}
out, err := compose.Down(dockerComposePath)
if err != nil {
@ -188,7 +190,7 @@ func (a AppService) Operate(req dto.AppInstallOperate) error {
if err := op.DeleteDir(appDir); err != nil {
return err
}
return appInstallRepo.Delete(commonRepo.WithByID(install.ID))
return appInstallRepo.Delete(install)
case dto.Sync:
if err := a.SyncInstalled(install.ID); err != nil {
return err
@ -214,7 +216,7 @@ func handleErr(install model.AppInstall, err error, out string) error {
func (a AppService) Install(name string, appDetailId uint, params map[string]interface{}) error {
port, ok := params["PORT"]
port, ok := params["PANEL_APP_PORT"]
if ok {
portStr := strconv.FormatFloat(port.(float64), 'f', -1, 32)
if common.ScanPort(portStr) {
@ -230,6 +232,7 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
if err != nil {
return err
}
if app.Required != "" {
var requiredArray []string
if err := json.Unmarshal([]byte(app.Required), &requiredArray); err != nil {
@ -301,6 +304,41 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
return err
}
var (
auth string
dbConfig dto.AppDatabase
)
tags, err := tagRepo.GetByAppId(app.ID)
if err != nil {
return err
}
for _, tag := range tags {
if tag.Key == "Database" {
var authParam dto.AuthParam
paramByte, err := json.Marshal(params)
if err != nil {
return err
}
if err := json.Unmarshal(paramByte, &authParam); err != nil {
return err
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
auth = string(authByte)
}
if tag.Key == "WebSite" {
paramByte, err := json.Marshal(params)
if err != nil {
return err
}
if err := json.Unmarshal(paramByte, &dbConfig); err != nil {
return err
}
}
}
composeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {
return err
@ -335,6 +373,7 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
ServiceName: serviceName,
ContainerName: containerName,
Port: servicePort,
Auth: auth,
})
}
for k, v := range changeKeys {
@ -349,35 +388,64 @@ func (a AppService) Install(name string, appDetailId uint, params map[string]int
return err
}
if err := appInstallRepo.Create(&appInstall); err != nil {
tx := global.DB.Begin()
ctx := context.WithValue(context.Background(), "db", tx)
if err := appInstallRepo.Create(ctx, &appInstall); err != nil {
tx.Rollback()
return err
}
for _, c := range appContainers {
c.AppInstallId = appInstall.ID
c.AppInstallID = appInstall.ID
}
if err := appContainerRepo.BatchCreate(context.WithValue(context.Background(), "db", global.DB), appContainers); err != nil {
if err := appContainerRepo.BatchCreate(ctx, appContainers); err != nil {
tx.Rollback()
return err
}
if !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) {
container, err := appContainerRepo.GetFirst(appContainerRepo.WithServiceName(dbConfig.ServiceName))
if err != nil {
tx.Rollback()
return err
}
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(container.AppInstallID))
if err != nil {
tx.Rollback()
return err
}
app, err := appRepo.GetFirst(commonRepo.WithByID(install.ID))
if err != nil {
tx.Rollback()
return err
}
var database model.Database
database.AppContainerId = container.ID
database.Dbname = dbConfig.DbName
database.Username = dbConfig.DbUser
database.Password = dbConfig.Password
if err := dataBaseRepo.Create(ctx, &database); err != nil {
tx.Rollback()
return err
}
var auth dto.AuthParam
json.Unmarshal([]byte(container.Auth), &auth)
execConfig := dto.ContainerExec{
ContainerName: container.ContainerName,
Auth: auth,
DbParam: dbConfig,
}
_, err = cmd.Exec(getSqlStr(app.Key, execConfig))
if err != nil {
tx.Rollback()
return err
}
}
tx.Commit()
go upApp(composeFilePath, appInstall)
return nil
}
func upApp(composeFilePath string, appInstall model.AppInstall) {
out, err := compose.Up(composeFilePath)
if err != nil {
if out != "" {
appInstall.Message = out
} else {
appInstall.Message = err.Error()
}
appInstall.Status = constant.Error
_ = appInstallRepo.Save(appInstall)
} else {
appInstall.Status = constant.Running
_ = appInstallRepo.Save(appInstall)
}
}
func (a AppService) SyncAllInstalled() error {
allList, err := appInstallRepo.GetBy()
if err != nil {
@ -405,13 +473,9 @@ func (a AppService) GetServices(key string) ([]dto.AppService, error) {
var res []dto.AppService
for _, install := range installs {
for _, container := range install.Containers {
value := container.ServiceName
if container.Port > 0 {
value = value + ":" + string(rune(container.Port))
}
res = append(res, dto.AppService{
Label: install.Name,
Value: value,
Value: container.ServiceName,
})
}
}
@ -505,53 +569,6 @@ func (a AppService) SyncInstalled(installId uint) error {
return appInstallRepo.Save(appInstall)
}
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
apps := make(map[string]model.App, len(oldApps))
for _, old := range oldApps {
old.Status = constant.AppTakeDown
apps[old.Key] = old
}
for _, item := range items {
app, ok := apps[item.Key]
if !ok {
app = model.App{}
}
app.Name = item.Name
app.Key = item.Key
app.ShortDesc = item.ShortDesc
app.Author = item.Author
app.Source = item.Source
app.Type = item.Type
app.CrossVersionUpdate = item.CrossVersionUpdate
app.Required = item.GetRequired()
app.Status = constant.AppNormal
apps[item.Key] = app
}
return apps
}
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
appDetails := make(map[string]model.AppDetail, len(details))
for _, old := range details {
old.Status = constant.AppTakeDown
appDetails[old.Version] = old
}
for _, v := range versions {
detail, ok := appDetails[v]
if ok {
detail.Status = constant.AppNormal
appDetails[v] = detail
} else {
appDetails[v] = model.AppDetail{
Version: v,
Status: constant.AppNormal,
}
}
}
return appDetails
}
func (a AppService) SyncAppList() error {
//TODO 从 oss 拉取最新列表
@ -770,3 +787,78 @@ func syncCanUpdate() {
}
}
}
func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App {
apps := make(map[string]model.App, len(oldApps))
for _, old := range oldApps {
old.Status = constant.AppTakeDown
apps[old.Key] = old
}
for _, item := range items {
app, ok := apps[item.Key]
if !ok {
app = model.App{}
}
app.Name = item.Name
app.Key = item.Key
app.ShortDesc = item.ShortDesc
app.Author = item.Author
app.Source = item.Source
app.Type = item.Type
app.CrossVersionUpdate = item.CrossVersionUpdate
app.Required = item.GetRequired()
app.Status = constant.AppNormal
apps[item.Key] = app
}
return apps
}
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
appDetails := make(map[string]model.AppDetail, len(details))
for _, old := range details {
old.Status = constant.AppTakeDown
appDetails[old.Version] = old
}
for _, v := range versions {
detail, ok := appDetails[v]
if ok {
detail.Status = constant.AppNormal
appDetails[v] = detail
} else {
appDetails[v] = model.AppDetail{
Version: v,
Status: constant.AppNormal,
}
}
}
return appDetails
}
func upApp(composeFilePath string, appInstall model.AppInstall) {
out, err := compose.Up(composeFilePath)
if err != nil {
if out != "" {
appInstall.Message = out
} else {
appInstall.Message = err.Error()
}
appInstall.Status = constant.Error
_ = appInstallRepo.Save(appInstall)
} else {
appInstall.Status = constant.Running
_ = appInstallRepo.Save(appInstall)
}
}
func getSqlStr(key string, exec dto.ContainerExec) string {
var str string
param := exec.DbParam
switch key {
case "mysql":
str = fmt.Sprintf("docker exec -i %s mysql -uroot -p%s -e \"CREATE USER '%s'@'%%' IDENTIFIED BY '%s';\" -e \"create database %s;\" -e \"GRANT ALL ON %s.* TO '%s'@'%%';\"",
exec.ContainerName, exec.Auth.RootPassword, param.DbUser, param.Password, param.DbName, param.DbName, param.DbUser)
}
fmt.Println(str)
return str
}

View File

@ -18,18 +18,19 @@ type ServiceGroup struct {
var ServiceGroupApp = new(ServiceGroup)
var (
hostRepo = repo.RepoGroupApp.HostRepo
backupRepo = repo.RepoGroupApp.BackupRepo
groupRepo = repo.RepoGroupApp.GroupRepo
commandRepo = repo.RepoGroupApp.CommandRepo
operationRepo = repo.RepoGroupApp.OperationRepo
commonRepo = repo.RepoGroupApp.CommonRepo
cronjobRepo = repo.RepoGroupApp.CronjobRepo
settingRepo = repo.RepoGroupApp.SettingRepo
appRepo = repo.RepoGroupApp.AppRepo
appTagRepo = repo.RepoGroupApp.AppTagRepo
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
tagRepo = repo.RepoGroupApp.TagRepo
hostRepo = repo.RepoGroupApp.HostRepo
backupRepo = repo.RepoGroupApp.BackupRepo
groupRepo = repo.RepoGroupApp.GroupRepo
commandRepo = repo.RepoGroupApp.CommandRepo
operationRepo = repo.RepoGroupApp.OperationRepo
commonRepo = repo.RepoGroupApp.CommonRepo
cronjobRepo = repo.RepoGroupApp.CronjobRepo
settingRepo = repo.RepoGroupApp.SettingRepo
appRepo = repo.RepoGroupApp.AppRepo
appTagRepo = repo.RepoGroupApp.AppTagRepo
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
tagRepo = repo.RepoGroupApp.TagRepo
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
appContainerRepo = repo.RepoGroupApp.AppContainerRepo
dataBaseRepo = repo.RepoGroupApp.DatabaseRepo
)

View File

@ -150,6 +150,6 @@ var AddTableCronjob = &gormigrate.Migration{
var AddTableApp = &gormigrate.Migration{
ID: "20200921-add-table-app",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.Tag{}, &model.AppTag{}, &model.AppInstall{}, &model.AppContainer{}, &model.AppContainer{})
return tx.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.Tag{}, &model.AppTag{}, &model.AppInstall{}, &model.AppContainer{}, &model.Database{})
},
}

50
backend/utils/cmd/cmd.go Normal file
View File

@ -0,0 +1,50 @@
package cmd
import (
"bufio"
"context"
"io"
"os/exec"
"sync"
)
func Exec(cmdStr string) (out string, err error) {
command := exec.CommandContext(context.Background(), "bash", "-c", cmdStr)
var wg sync.WaitGroup
wg.Add(1)
stdout, err := command.StdoutPipe()
if err != nil {
return
}
readout := bufio.NewReader(stdout)
go func() {
defer wg.Done()
out = getOutput(readout)
}()
err = command.Run()
if err != nil {
return
}
wg.Wait()
return
}
func getOutput(reader *bufio.Reader) string {
var sumOutput string
outputBytes := make([]byte, 200)
for {
n, err := reader.Read(outputBytes)
if err != nil {
if err == io.EOF {
break
}
sumOutput += err.Error()
}
output := string(outputBytes[:n])
sumOutput += output
}
return sumOutput
}

View File

@ -1,7 +1,9 @@
package docker
import (
"bufio"
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
@ -47,3 +49,30 @@ func (c Client) ListContainersByName(names []string) ([]types.Container, error)
}
return containers, nil
}
func (c Client) ExecCommand(context context.Context, name string, command []string) {
execConfig := types.ExecConfig{Tty: true, AttachStdout: true, AttachStderr: false, Cmd: command}
respIdExecCreate, err := c.cli.ContainerExecCreate(context, name, execConfig)
if err != nil {
fmt.Println(err)
}
respId, err := c.cli.ContainerExecAttach(context, respIdExecCreate.ID, types.ExecStartCheck{})
if err != nil {
fmt.Println(err)
}
//text, _ := respId.Reader.ReadString('\n')
//fmt.Printf("%s\n", text)
scanner := bufio.NewScanner(respId.Reader)
//text, _ := resp.Reader.ReadString('\n')
//log.Print(text)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
//
//respId, err := c.cli.ContainerExecAttach(context, respIdExecCreate.ID, types.ExecStartCheck{})
//if err != nil {
// fmt.Println(err)
//}
}

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="open" :title="$t('app.install')" width="40%">
<el-dialog v-model="open" :title="$t('app.install')" width="40%" :before-close="handleClose" @opened="opened">
<el-form ref="paramForm" label-position="left" :model="form" label-width="150px" :rules="rules">
<el-form-item :label="$t('app.name')" prop="NAME">
<el-input v-model="form['NAME']"></el-input>
@ -40,8 +40,9 @@
import { App } from '@/api/interface/app';
import { InstallApp, GetAppService } from '@/api/modules/app';
import { Rules } from '@/global/form-rules';
import { getRandomStr } from '@/utils/util';
import { FormInstance, FormRules } from 'element-plus';
import { reactive, ref } from 'vue';
import { nextTick, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
@ -68,10 +69,22 @@ const req = reactive({
let services = ref();
const handleClose = () => {
open.value = false;
resetForm();
};
const opened = () => {
nextTick(() => {
if (paramForm.value) {
paramForm.value.clearValidate();
}
});
};
const resetForm = () => {
if (paramForm.value) {
paramForm.value.resetFields();
}
open.value = false;
};
const acceptParams = (props: InstallRrops): void => {
@ -79,24 +92,28 @@ const acceptParams = (props: InstallRrops): void => {
const params = installData.value.params;
if (params?.formFields != undefined) {
for (const p of params?.formFields) {
form[p.envKey] = p.default;
if (p.default == 'random') {
form[p.envKey] = getRandomStr(6);
} else {
form[p.envKey] = p.default;
}
if (p.required) {
rules[p.envKey] = [Rules.requiredInput];
}
if (p.key) {
form[p.envKey] = '';
getServices(form[p.envKey], p.key);
getServices(p.envKey, p.key);
}
}
}
open.value = true;
};
const getServices = (value: any, key: string | undefined) => {
GetAppService(key).then((res) => {
const getServices = async (envKey: string, key: string | undefined) => {
await GetAppService(key).then((res) => {
services.value = res.data;
if (services.value != null) {
value = services.value[0].value;
form[envKey] = services.value[0].value;
}
});
};