From ad5090715812cb1c27097c66b80dfacc32506899 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 Date: Sun, 9 Oct 2022 23:35:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=88=9B=E5=BB=BA=20?= =?UTF-8?q?WORDPRESS=20=E4=B9=8B=E5=89=8D=E5=88=9B=E5=BB=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/list.json | 2 +- apps/mysql/5.7.39/docker-compose.yml | 7 +- apps/mysql/5.7.39/params.json | 36 +-- apps/mysql/8.0.30/docker-compose.yml | 7 +- apps/mysql/8.0.30/params.json | 36 +-- apps/nginx/1.23.1/docker-compose.yml | 4 +- apps/nginx/1.23.1/params.json | 2 +- apps/wordpress/6.0.1/docker-compose.yml | 12 +- apps/wordpress/6.0.1/params.json | 18 +- backend/app/dto/app.go | 17 ++ backend/app/model/app_container.go | 10 +- backend/app/model/app_install.go | 2 +- backend/app/model/database.go | 10 + backend/app/repo/app.go | 2 +- backend/app/repo/app_container.go | 16 ++ backend/app/repo/app_install.go | 12 +- backend/app/repo/database.go | 47 ++++ backend/app/repo/entry.go | 1 + backend/app/repo/tag.go | 8 + backend/app/service/app.go | 240 ++++++++++++------ backend/app/service/entry.go | 25 +- backend/init/migration/migrations/init.go | 2 +- backend/utils/cmd/cmd.go | 50 ++++ backend/utils/docker/docker.go | 29 +++ .../src/views/app-store/detail/install.vue | 33 ++- 25 files changed, 427 insertions(+), 201 deletions(-) create mode 100644 backend/app/model/database.go create mode 100644 backend/app/repo/database.go create mode 100644 backend/utils/cmd/cmd.go diff --git a/apps/list.json b/apps/list.json index 7cf657b91..639d3ec0f 100644 --- a/apps/list.json +++ b/apps/list.json @@ -39,7 +39,7 @@ "author": "Nginx", "type": "internal", "required": [""], - "limit": 0, + "limit": 1, "crossVersionUpdate": true, "source": "http://nginx.org/" }, diff --git a/apps/mysql/5.7.39/docker-compose.yml b/apps/mysql/5.7.39/docker-compose.yml index 0cdcf2df9..f1bd8dab6 100644 --- a/apps/mysql/5.7.39/docker-compose.yml +++ b/apps/mysql/5.7.39/docker-compose.yml @@ -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 diff --git a/apps/mysql/5.7.39/params.json b/apps/mysql/5.7.39/params.json index 5e4ce3d2f..273f6a9ed 100644 --- a/apps/mysql/5.7.39/params.json +++ b/apps/mysql/5.7.39/params.json @@ -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" } ] } \ No newline at end of file diff --git a/apps/mysql/8.0.30/docker-compose.yml b/apps/mysql/8.0.30/docker-compose.yml index 20349a7b1..e7727b6aa 100644 --- a/apps/mysql/8.0.30/docker-compose.yml +++ b/apps/mysql/8.0.30/docker-compose.yml @@ -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 diff --git a/apps/mysql/8.0.30/params.json b/apps/mysql/8.0.30/params.json index 5e4ce3d2f..273f6a9ed 100644 --- a/apps/mysql/8.0.30/params.json +++ b/apps/mysql/8.0.30/params.json @@ -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" } ] } \ No newline at end of file diff --git a/apps/nginx/1.23.1/docker-compose.yml b/apps/nginx/1.23.1/docker-compose.yml index 2119703f3..2f7cdd256 100644 --- a/apps/nginx/1.23.1/docker-compose.yml +++ b/apps/nginx/1.23.1/docker-compose.yml @@ -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 \ No newline at end of file diff --git a/apps/nginx/1.23.1/params.json b/apps/nginx/1.23.1/params.json index bb6d620c1..127f1b9be 100644 --- a/apps/nginx/1.23.1/params.json +++ b/apps/nginx/1.23.1/params.json @@ -6,7 +6,7 @@ "labelEn": "Port", "required": true, "default": 80, - "envKey": "PORT" + "envKey": "PANEL_APP_PORT" } ] } \ No newline at end of file diff --git a/apps/wordpress/6.0.1/docker-compose.yml b/apps/wordpress/6.0.1/docker-compose.yml index a2e2d6e9d..bf9b6701c 100644 --- a/apps/wordpress/6.0.1/docker-compose.yml +++ b/apps/wordpress/6.0.1/docker-compose.yml @@ -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: diff --git a/apps/wordpress/6.0.1/params.json b/apps/wordpress/6.0.1/params.json index 378eff384..a08cf4ef0 100644 --- a/apps/wordpress/6.0.1/params.json +++ b/apps/wordpress/6.0.1/params.json @@ -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" } ] } \ No newline at end of file diff --git a/backend/app/dto/app.go b/backend/app/dto/app.go index 8e0118159..a640d5dec 100644 --- a/backend/app/dto/app.go +++ b/backend/app/dto/app.go @@ -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"` +} diff --git a/backend/app/model/app_container.go b/backend/app/model/app_container.go index 42c17ec91..3a0253213 100644 --- a/backend/app/model/app_container.go +++ b/backend/app/model/app_container.go @@ -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:"-"` } diff --git a/backend/app/model/app_install.go b/backend/app/model/app_install.go index ecf606510..74c977de9 100644 --- a/backend/app/model/app_install.go +++ b/backend/app/model/app_install.go @@ -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 } diff --git a/backend/app/model/database.go b/backend/app/model/database.go new file mode 100644 index 000000000..1838f3fae --- /dev/null +++ b/backend/app/model/database.go @@ -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"` +} diff --git a/backend/app/repo/app.go b/backend/app/repo/app.go index 71e43b225..841181a2c 100644 --- a/backend/app/repo/app.go +++ b/backend/app/repo/app.go @@ -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 diff --git a/backend/app/repo/app_container.go b/backend/app/repo/app_container.go index 775fe6ae6..b7e7f7820 100644 --- a/backend/app/repo/app_container.go +++ b/backend/app/repo/app_container.go @@ -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 diff --git a/backend/app/repo/app_install.go b/backend/app/repo/app_install.go index dde550864..c848552fc 100644 --- a/backend/app/repo/app_install.go +++ b/backend/app/repo/app_install.go @@ -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{}) diff --git a/backend/app/repo/database.go b/backend/app/repo/database.go new file mode 100644 index 000000000..d53ccb81e --- /dev/null +++ b/backend/app/repo/database.go @@ -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 +//} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index 908fa67cf..a666e1411 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -15,6 +15,7 @@ type RepoGroup struct { AppDetailRepo AppInstallRepo AppContainerRepo + DatabaseRepo } var RepoGroupApp = new(RepoGroup) diff --git a/backend/app/repo/tag.go b/backend/app/repo/tag.go index 541d4b6cb..76130aca4 100644 --- a/backend/app/repo/tag.go +++ b/backend/app/repo/tag.go @@ -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 +} diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 6c550a329..4288309b9 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -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 +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index 6ac5d2434..e9c6d2403 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -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 ) diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 6be0797ec..96032c324 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -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{}) }, } diff --git a/backend/utils/cmd/cmd.go b/backend/utils/cmd/cmd.go new file mode 100644 index 000000000..8aca285be --- /dev/null +++ b/backend/utils/cmd/cmd.go @@ -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 +} diff --git a/backend/utils/docker/docker.go b/backend/utils/docker/docker.go index 655cf2363..03ec40022 100644 --- a/backend/utils/docker/docker.go +++ b/backend/utils/docker/docker.go @@ -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) + //} + +} diff --git a/frontend/src/views/app-store/detail/install.vue b/frontend/src/views/app-store/detail/install.vue index 6af7546a8..a27d519b6 100644 --- a/frontend/src/views/app-store/detail/install.vue +++ b/frontend/src/views/app-store/detail/install.vue @@ -1,5 +1,5 @@