mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-27 12:39:01 +08:00
feat: 增加网站创建功能
This commit is contained in:
parent
a1ac689a5e
commit
ef789cdfea
@ -10,7 +10,7 @@
|
||||
"name": "服务器"
|
||||
},
|
||||
{
|
||||
"key": "Datastore",
|
||||
"key": "Database",
|
||||
"name": "数据库"
|
||||
}
|
||||
],
|
||||
@ -18,7 +18,7 @@
|
||||
{
|
||||
"key": "mysql5.7",
|
||||
"name": "Mysql5.7",
|
||||
"tags": ["Datastore"],
|
||||
"tags": ["Database"],
|
||||
"versions": ["5.7.39"],
|
||||
"short_desc": "常用关系型数据库",
|
||||
"icon": "mysql.png",
|
||||
@ -32,7 +32,7 @@
|
||||
{
|
||||
"key": "mysql8.0",
|
||||
"name": "Mysql8.0",
|
||||
"tags": ["Datastore"],
|
||||
"tags": ["Database"],
|
||||
"versions": ["8.0.30"],
|
||||
"short_desc": "常用关系型数据库",
|
||||
"icon": "mysql.png",
|
||||
@ -66,7 +66,7 @@
|
||||
"icon": "wordpress.png",
|
||||
"author": "Wordpress",
|
||||
"type": "website",
|
||||
"required": ["mysql"],
|
||||
"required": ["mysql8.0"],
|
||||
"limit": 0,
|
||||
"crossVersionUpdate": true,
|
||||
"source": "http://wordpress.org/"
|
||||
@ -74,7 +74,7 @@
|
||||
{
|
||||
"key": "redis",
|
||||
"name": "redis",
|
||||
"tags": ["Datastore"],
|
||||
"tags": ["Database"],
|
||||
"versions": ["7.0.5","6.0.16"],
|
||||
"short_desc": "缓存数据库",
|
||||
"icon": "redis.png",
|
||||
|
99
apps/nginx/1.23.1/conf/mime.types
Normal file
99
apps/nginx/1.23.1/conf/mime.types
Normal file
@ -0,0 +1,99 @@
|
||||
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
text/xml xml;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
application/javascript js;
|
||||
application/atom+xml atom;
|
||||
application/rss+xml rss;
|
||||
|
||||
text/mathml mml;
|
||||
text/plain txt;
|
||||
text/vnd.sun.j2me.app-descriptor jad;
|
||||
text/vnd.wap.wml wml;
|
||||
text/x-component htc;
|
||||
|
||||
image/avif avif;
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
image/vnd.wap.wbmp wbmp;
|
||||
image/webp webp;
|
||||
image/x-icon ico;
|
||||
image/x-jng jng;
|
||||
image/x-ms-bmp bmp;
|
||||
|
||||
font/woff woff;
|
||||
font/woff2 woff2;
|
||||
|
||||
application/java-archive jar war ear;
|
||||
application/json json;
|
||||
application/mac-binhex40 hqx;
|
||||
application/msword doc;
|
||||
application/pdf pdf;
|
||||
application/postscript ps eps ai;
|
||||
application/rtf rtf;
|
||||
application/vnd.apple.mpegurl m3u8;
|
||||
application/vnd.google-earth.kml+xml kml;
|
||||
application/vnd.google-earth.kmz kmz;
|
||||
application/vnd.ms-excel xls;
|
||||
application/vnd.ms-fontobject eot;
|
||||
application/vnd.ms-powerpoint ppt;
|
||||
application/vnd.oasis.opendocument.graphics odg;
|
||||
application/vnd.oasis.opendocument.presentation odp;
|
||||
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||
application/vnd.oasis.opendocument.text odt;
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
pptx;
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
xlsx;
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
docx;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/wasm wasm;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
application/x-java-jnlp-file jnlp;
|
||||
application/x-makeself run;
|
||||
application/x-perl pl pm;
|
||||
application/x-pilot prc pdb;
|
||||
application/x-rar-compressed rar;
|
||||
application/x-redhat-package-manager rpm;
|
||||
application/x-sea sea;
|
||||
application/x-shockwave-flash swf;
|
||||
application/x-stuffit sit;
|
||||
application/x-tcl tcl tk;
|
||||
application/x-x509-ca-cert der pem crt;
|
||||
application/x-xpinstall xpi;
|
||||
application/xhtml+xml xhtml;
|
||||
application/xspf+xml xspf;
|
||||
application/zip zip;
|
||||
|
||||
application/octet-stream bin exe dll;
|
||||
application/octet-stream deb;
|
||||
application/octet-stream dmg;
|
||||
application/octet-stream iso img;
|
||||
application/octet-stream msi msp msm;
|
||||
|
||||
audio/midi mid midi kar;
|
||||
audio/mpeg mp3;
|
||||
audio/ogg ogg;
|
||||
audio/x-m4a m4a;
|
||||
audio/x-realaudio ra;
|
||||
|
||||
video/3gpp 3gpp 3gp;
|
||||
video/mp2t ts;
|
||||
video/mp4 mp4;
|
||||
video/mpeg mpeg mpg;
|
||||
video/quicktime mov;
|
||||
video/webm webm;
|
||||
video/x-flv flv;
|
||||
video/x-m4v m4v;
|
||||
video/x-mng mng;
|
||||
video/x-ms-asf asx asf;
|
||||
video/x-ms-wmv wmv;
|
||||
video/x-msvideo avi;
|
||||
}
|
@ -11,7 +11,7 @@ events {
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
@ -23,14 +23,4 @@ http {
|
||||
keepalive_timeout 65;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name 127.0.0.1;
|
||||
allow 127.0.0.1;
|
||||
location /nginx_status {
|
||||
stub_status on;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,10 +10,8 @@ services:
|
||||
- ${PANEL_APP_PORT_HTTPS}:443
|
||||
volumes:
|
||||
- ./conf/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./www:/home/www
|
||||
- ./log:/var/log/nginx
|
||||
- ./conf/conf.d:/etc/nginx/conf.d/
|
||||
- ./html:/usr/share/nginx/html
|
||||
|
||||
networks:
|
||||
1panel:
|
||||
|
@ -2,7 +2,7 @@
|
||||
"formFields": [
|
||||
{
|
||||
"type": "service",
|
||||
"key": "mysql",
|
||||
"key": "mysql8.0",
|
||||
"labelZh": "数据库服务",
|
||||
"labelEn": "Database Service",
|
||||
"required": true,
|
||||
|
@ -2,7 +2,7 @@
|
||||
"formFields": [
|
||||
{
|
||||
"type": "service",
|
||||
"key": "mysql",
|
||||
"key": "mysql8.0",
|
||||
"labelZh": "数据库服务",
|
||||
"labelEn": "Database Service",
|
||||
"required": true,
|
||||
|
@ -1,13 +1,13 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
@ -98,16 +98,26 @@ func (b *BaseApi) SearchInstalled(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
total, list, err := appService.PageInstalled(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(req.PageInfo, dto.PageInfo{}) {
|
||||
total, list, err := appService.PageInstalled(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
} else {
|
||||
list, err := appService.SearchInstalled(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BaseApi) SearchInstalledBackup(c *gin.Context) {
|
||||
|
@ -25,11 +25,13 @@ var (
|
||||
|
||||
hostService = service.ServiceGroupApp.HostService
|
||||
groupService = service.ServiceGroupApp.GroupService
|
||||
commandService = service.ServiceGroupApp.CommandService
|
||||
fileService = service.ServiceGroupApp.FileService
|
||||
|
||||
settingService = service.ServiceGroupApp.SettingService
|
||||
backupService = service.ServiceGroupApp.BackupService
|
||||
|
||||
operationService = service.ServiceGroupApp.OperationService
|
||||
commandService = service.ServiceGroupApp.CommandService
|
||||
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
|
||||
websiteService = service.ServiceGroupApp.WebsiteService
|
||||
)
|
||||
|
24
backend/app/api/v1/website.go
Normal file
24
backend/app/api/v1/website.go
Normal file
@ -0,0 +1,24 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) CreateWebsite(c *gin.Context) {
|
||||
|
||||
var req dto.WebSiteCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := websiteService.CreateWebsite(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
17
backend/app/api/v1/website_group.go
Normal file
17
backend/app/api/v1/website_group.go
Normal file
@ -0,0 +1,17 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) GetGroups(c *gin.Context) {
|
||||
|
||||
list, err := websiteGroupService.GetGroups()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
@ -48,6 +48,7 @@ type AppInstalled struct {
|
||||
|
||||
type AppInstalledRequest struct {
|
||||
PageInfo
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type AppBackupRequest struct {
|
||||
|
16
backend/app/dto/website.go
Normal file
16
backend/app/dto/website.go
Normal file
@ -0,0 +1,16 @@
|
||||
package dto
|
||||
|
||||
type WebPage struct {
|
||||
PageInfo
|
||||
}
|
||||
|
||||
type WebSiteCreate struct {
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Type string `json:"type"`
|
||||
Alias string `json:"alias"`
|
||||
Remark string `json:"remark"`
|
||||
Domains []string `json:"domains"`
|
||||
AppType string `json:"appType"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
WebSiteGroupID uint `json:"webSiteGroupID"`
|
||||
}
|
10
backend/app/dto/website_group.go
Normal file
10
backend/app/dto/website_group.go
Normal file
@ -0,0 +1,10 @@
|
||||
package dto
|
||||
|
||||
type WebSiteGroupCreateReq struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type WebSiteGroupUpdateReq struct {
|
||||
ID uint
|
||||
Name string
|
||||
}
|
16
backend/app/model/website.go
Normal file
16
backend/app/model/website.go
Normal file
@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type WebSite struct {
|
||||
BaseModel
|
||||
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
|
||||
Type string `gorm:"type:varchar(64);not null" json:"type"`
|
||||
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
|
||||
Remark string `gorm:"type:longtext;" json:"remark"`
|
||||
Status string `gorm:"type:varchar(64);not null" json:"status"`
|
||||
ExpireDate time.Time `json:"expireDate"`
|
||||
AppInstallID uint `gorm:"type:integer" json:"appInstallID"`
|
||||
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupID"`
|
||||
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLID"`
|
||||
}
|
8
backend/app/model/website_domain.go
Normal file
8
backend/app/model/website_domain.go
Normal file
@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
type WebSiteDomain struct {
|
||||
BaseModel
|
||||
WebSiteID uint `gorm:"type:varchar(64);not null" json:"web_site_id"`
|
||||
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
|
||||
Port int `gorm:"type:integer" json:"port"`
|
||||
}
|
7
backend/app/model/website_group.go
Normal file
7
backend/app/model/website_group.go
Normal file
@ -0,0 +1,7 @@
|
||||
package model
|
||||
|
||||
type WebSiteGroup struct {
|
||||
BaseModel
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Default bool `json:"default"`
|
||||
}
|
5
backend/app/model/website_ssl.go
Normal file
5
backend/app/model/website_ssl.go
Normal file
@ -0,0 +1,5 @@
|
||||
package model
|
||||
|
||||
type WebSiteSSL struct {
|
||||
BaseModel
|
||||
}
|
@ -16,6 +16,12 @@ func (a AppRepo) WithKey(key string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) WithType(typeStr string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("type = ?", typeStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) {
|
||||
var apps []model.App
|
||||
db := getDb(opts...).Model(&model.App{})
|
||||
|
@ -25,6 +25,13 @@ func (a AppInstallRepo) WithAppId(appId uint) DBOption {
|
||||
return g.Where("app_id = ?", appId)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("app_id in (?)", appIds)
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppInstallRepo) WithStatus(status string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("status = ?", status)
|
||||
|
@ -3,7 +3,6 @@ package repo
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -17,24 +16,16 @@ func (d DatabaseRepo) ByAppInstallId(installId uint) DBOption {
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) Create(ctx context.Context, database *model.AppDatabase) error {
|
||||
db := ctx.Value("db").(*gorm.DB).Model(&model.AppDatabase{})
|
||||
return db.Create(&database).Error
|
||||
return getTx(ctx).Model(&model.AppDatabase{}).Create(&database).Error
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
db := ctx.Value("db").(*gorm.DB).Model(&model.AppDatabase{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.AppDatabase{}).Error
|
||||
return getTx(ctx, opts...).Model(&model.AppDatabase{}).Delete(&model.AppDatabase{}).Error
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.AppDatabase, error) {
|
||||
db := global.DB.Model(model.AppDatabase{})
|
||||
db := getDb(opts...)
|
||||
var databases []model.AppDatabase
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&databases).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -43,11 +34,8 @@ func (d DatabaseRepo) GetBy(opts ...DBOption) ([]model.AppDatabase, error) {
|
||||
}
|
||||
|
||||
func (d DatabaseRepo) GetFirst(opts ...DBOption) (model.AppDatabase, error) {
|
||||
db := global.DB.Model(model.AppDatabase{})
|
||||
db := getDb(opts...)
|
||||
var database model.AppDatabase
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&database).Error
|
||||
if err != nil {
|
||||
return database, err
|
||||
|
@ -2,7 +2,6 @@ package repo
|
||||
|
||||
type RepoGroup struct {
|
||||
CommonRepo
|
||||
|
||||
AppRepo
|
||||
AppTagRepo
|
||||
TagRepo
|
||||
@ -11,22 +10,19 @@ type RepoGroup struct {
|
||||
AppInstallResourceRpo
|
||||
DatabaseRepo
|
||||
AppInstallBackupRepo
|
||||
|
||||
ImageRepoRepo
|
||||
ComposeTemplateRepo
|
||||
|
||||
MysqlRepo
|
||||
|
||||
CronjobRepo
|
||||
|
||||
HostRepo
|
||||
CommandRepo
|
||||
GroupRepo
|
||||
|
||||
SettingRepo
|
||||
BackupRepo
|
||||
|
||||
OperationRepo
|
||||
WebSiteRepo
|
||||
WebSiteDomainRepo
|
||||
WebSiteGroupRepo
|
||||
}
|
||||
|
||||
var RepoGroupApp = new(RepoGroup)
|
||||
|
45
backend/app/repo/website.go
Normal file
45
backend/app/repo/website.go
Normal file
@ -0,0 +1,45 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type WebSiteRepo struct {
|
||||
}
|
||||
|
||||
func (w WebSiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSite, error) {
|
||||
var websites []model.WebSite
|
||||
db := getDb(opts...).Model(&model.WebSite{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&websites).Error
|
||||
return count, websites, err
|
||||
}
|
||||
|
||||
func (w WebSiteRepo) GetFirst(opts ...DBOption) (model.WebSite, error) {
|
||||
var website model.WebSite
|
||||
db := getDb(opts...).Model(&model.WebSite{})
|
||||
if err := db.First(&website).Error; err != nil {
|
||||
return website, err
|
||||
}
|
||||
return website, nil
|
||||
}
|
||||
|
||||
func (w WebSiteRepo) GetBy(opts ...DBOption) ([]model.WebSite, error) {
|
||||
var websites []model.WebSite
|
||||
db := getDb(opts...).Model(&model.WebSite{})
|
||||
if err := db.Find(&websites).Error; err != nil {
|
||||
return websites, err
|
||||
}
|
||||
return websites, nil
|
||||
}
|
||||
|
||||
func (w WebSiteRepo) Create(ctx context.Context, app *model.WebSite) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Create(app).Error
|
||||
}
|
||||
|
||||
func (w WebSiteRepo) Save(ctx context.Context, app *model.WebSite) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Save(app).Error
|
||||
}
|
49
backend/app/repo/website_domain.go
Normal file
49
backend/app/repo/website_domain.go
Normal file
@ -0,0 +1,49 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type WebSiteDomainRepo struct {
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteDomain, error) {
|
||||
var domains []model.WebSiteDomain
|
||||
db := getDb(opts...).Model(&model.WebSiteDomain{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&domains).Error
|
||||
return count, domains, err
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) GetFirst(opts ...DBOption) (model.WebSiteDomain, error) {
|
||||
var domain model.WebSiteDomain
|
||||
db := getDb(opts...).Model(&model.WebSiteDomain{})
|
||||
if err := db.First(&domain).Error; err != nil {
|
||||
return domain, err
|
||||
}
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) GetBy(opts ...DBOption) ([]model.WebSiteDomain, error) {
|
||||
var domains []model.WebSiteDomain
|
||||
db := getDb(opts...).Model(&model.WebSiteDomain{})
|
||||
if err := db.Find(&domains).Error; err != nil {
|
||||
return domains, err
|
||||
}
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) BatchCreate(ctx context.Context, domains []model.WebSiteDomain) error {
|
||||
return getTx(ctx).Model(&model.WebSiteDomain{}).Create(&domains).Error
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) Create(ctx context.Context, app *model.WebSiteDomain) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Create(app).Error
|
||||
}
|
||||
|
||||
func (w WebSiteDomainRepo) Save(ctx context.Context, app *model.WebSiteDomain) error {
|
||||
return getTx(ctx).Omit(clause.Associations).Save(app).Error
|
||||
}
|
39
backend/app/repo/website_group.go
Normal file
39
backend/app/repo/website_group.go
Normal file
@ -0,0 +1,39 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type WebSiteGroupRepo struct {
|
||||
}
|
||||
|
||||
func (w WebSiteGroupRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteGroup, error) {
|
||||
var groups []model.WebSiteGroup
|
||||
db := getDb(opts...).Model(&model.WebSiteGroup{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&groups).Error
|
||||
return count, groups, err
|
||||
}
|
||||
|
||||
func (w WebSiteGroupRepo) GetBy(opts ...DBOption) ([]model.WebSiteGroup, error) {
|
||||
var groups []model.WebSiteGroup
|
||||
db := getDb(opts...).Model(&model.WebSiteGroup{})
|
||||
if err := db.Find(&groups).Error; err != nil {
|
||||
return groups, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (w WebSiteGroupRepo) Create(app *model.WebSiteGroup) error {
|
||||
return getDb().Omit(clause.Associations).Create(app).Error
|
||||
}
|
||||
|
||||
func (w WebSiteGroupRepo) Save(app *model.WebSiteGroup) error {
|
||||
return getDb().Omit(clause.Associations).Save(app).Error
|
||||
}
|
||||
|
||||
func (w WebSiteGroupRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.WebSiteGroup{}).Error
|
||||
}
|
@ -127,6 +127,40 @@ func (a AppService) PageInstalled(req dto.AppInstalledRequest) (int64, []dto.App
|
||||
return total, installDTOs, nil
|
||||
}
|
||||
|
||||
func (a AppService) SearchInstalled(req dto.AppInstalledRequest) ([]dto.AppInstalled, error) {
|
||||
var installs []model.AppInstall
|
||||
var err error
|
||||
if req.Type != "" {
|
||||
apps, err := appRepo.GetBy(appRepo.WithType(req.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ids []uint
|
||||
for _, app := range apps {
|
||||
ids = append(ids, app.ID)
|
||||
}
|
||||
installs, err = appInstallRepo.GetBy(appInstallRepo.WithAppIdsIn(ids))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
installs, err = appInstallRepo.GetBy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var installDTOs []dto.AppInstalled
|
||||
for _, in := range installs {
|
||||
installDto := dto.AppInstalled{
|
||||
AppInstall: in,
|
||||
}
|
||||
installDTOs = append(installDTOs, installDto)
|
||||
}
|
||||
|
||||
return installDTOs, nil
|
||||
}
|
||||
|
||||
func (a AppService) GetAppDetail(appId uint, version string) (dto.AppDetailDTO, error) {
|
||||
|
||||
var (
|
||||
|
@ -26,6 +26,8 @@ type ServiceGroup struct {
|
||||
BackupService
|
||||
|
||||
OperationService
|
||||
WebsiteGroupService
|
||||
WebsiteService
|
||||
}
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
@ -57,4 +59,7 @@ var (
|
||||
backupRepo = repo.RepoGroupApp.BackupRepo
|
||||
|
||||
operationRepo = repo.RepoGroupApp.OperationRepo
|
||||
websiteRepo = repo.RepoGroupApp.WebSiteRepo
|
||||
websiteGroupRepo = repo.RepoGroupApp.WebSiteGroupRepo
|
||||
websiteDomainRepo = repo.RepoGroupApp.WebSiteDomainRepo
|
||||
)
|
||||
|
@ -2,16 +2,18 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type dbStr string
|
||||
type DBContext string
|
||||
|
||||
const (
|
||||
DB DBContext = "db"
|
||||
)
|
||||
|
||||
func getTxAndContext() (tx *gorm.DB, ctx context.Context) {
|
||||
db := dbStr("db")
|
||||
tx = global.DB.Begin()
|
||||
ctx = context.WithValue(context.Background(), db, tx)
|
||||
ctx = context.WithValue(context.Background(), DB, tx)
|
||||
return
|
||||
}
|
||||
|
60
backend/app/service/website.go
Normal file
60
backend/app/service/website.go
Normal file
@ -0,0 +1,60 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebsiteService struct {
|
||||
}
|
||||
|
||||
func (w WebsiteService) CreateWebsite(create dto.WebSiteCreate) error {
|
||||
|
||||
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
||||
|
||||
website := &model.WebSite{
|
||||
PrimaryDomain: create.PrimaryDomain,
|
||||
Type: create.Type,
|
||||
Alias: create.Alias,
|
||||
Remark: create.Remark,
|
||||
Status: constant.WebRunning,
|
||||
ExpireDate: defaultDate,
|
||||
AppInstallID: create.AppInstallID,
|
||||
WebSiteGroupID: create.WebSiteGroupID,
|
||||
}
|
||||
|
||||
tx, ctx := getTxAndContext()
|
||||
if err := websiteRepo.Create(ctx, website); err != nil {
|
||||
return err
|
||||
}
|
||||
var domains []model.WebSiteDomain
|
||||
domains = append(domains, model.WebSiteDomain{Domain: website.PrimaryDomain, WebSiteID: website.ID, Port: 80})
|
||||
for _, domain := range create.Domains {
|
||||
domainModel, err := getDomain(domain, website.ID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if reflect.DeepEqual(domainModel, model.WebSiteDomain{}) {
|
||||
continue
|
||||
}
|
||||
domains = append(domains, domainModel)
|
||||
}
|
||||
if len(domains) > 0 {
|
||||
if err := websiteDomainRepo.BatchCreate(ctx, domains); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := configDefaultNginx(*website, domains); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
32
backend/app/service/website_group.go
Normal file
32
backend/app/service/website_group.go
Normal file
@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
)
|
||||
|
||||
type WebsiteGroupService struct {
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) CreateGroup(create dto.WebSiteGroupCreateReq) error {
|
||||
return websiteGroupRepo.Create(&model.WebSiteGroup{
|
||||
Name: create.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) GetGroups() ([]model.WebSiteGroup, error) {
|
||||
return websiteGroupRepo.GetBy()
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) UpdateGroup(update dto.WebSiteGroupUpdateReq) error {
|
||||
return websiteGroupRepo.Save(&model.WebSiteGroup{
|
||||
BaseModel: model.BaseModel{
|
||||
ID: update.ID,
|
||||
},
|
||||
Name: update.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) DeleteGroup(id uint) error {
|
||||
return websiteGroupRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
}
|
92
backend/app/service/website_utils.go
Normal file
92
backend/app/service/website_utils.go
Normal file
@ -0,0 +1,92 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
|
||||
"github.com/pkg/errors"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getDomain(domainStr string, websiteID uint) (model.WebSiteDomain, error) {
|
||||
domain := model.WebSiteDomain{
|
||||
WebSiteID: websiteID,
|
||||
}
|
||||
domainArray := strings.Split(domainStr, ":")
|
||||
if len(domainArray) == 1 {
|
||||
domain.Domain = domainArray[0]
|
||||
return domain, nil
|
||||
}
|
||||
if len(domainArray) > 1 {
|
||||
domain.Domain = domainArray[0]
|
||||
portStr := domainArray[1]
|
||||
portN, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return model.WebSiteDomain{}, err
|
||||
}
|
||||
domain.Port = portN
|
||||
return domain, nil
|
||||
}
|
||||
return model.WebSiteDomain{}, nil
|
||||
}
|
||||
|
||||
func configDefaultNginx(website model.WebSite, domains []model.WebSiteDomain) error {
|
||||
|
||||
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nginxFileName := website.PrimaryDomain + ".conf"
|
||||
configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName)
|
||||
|
||||
nginxContent := string(nginx_conf.WebsiteDefault)
|
||||
config := parser.NewStringParser(nginxContent).Parse()
|
||||
servers := config.FindServers()
|
||||
if len(servers) == 0 {
|
||||
return errors.New("nginx config is not valid")
|
||||
}
|
||||
server := servers[0]
|
||||
var serverNames []string
|
||||
for _, domain := range domains {
|
||||
serverNames = append(serverNames, domain.Domain)
|
||||
server.UpdateListen(string(rune(domain.Port)), false)
|
||||
}
|
||||
server.UpdateServerName(serverNames)
|
||||
proxy := fmt.Sprintf("%s:%d", appInstall.ServiceName, appInstall.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
config.FilePath = configPath
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := opNginx(nginxInstall.ContainerName, "check"); err != nil {
|
||||
return err
|
||||
}
|
||||
return opNginx(nginxInstall.ContainerName, "reload")
|
||||
}
|
||||
|
||||
func opNginx(containerName, operate string) error {
|
||||
nginxCmd := fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -s reload")
|
||||
if operate == "check" {
|
||||
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
|
||||
}
|
||||
if _, err := cmd.Exec(nginxCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
11
backend/constant/website.go
Normal file
11
backend/constant/website.go
Normal file
@ -0,0 +1,11 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
WebRunning = "Running"
|
||||
WebStopped = "Stopped"
|
||||
)
|
||||
|
||||
const (
|
||||
DateLayout = "2006-01-02"
|
||||
DefaultDate = "1970-01-01"
|
||||
)
|
@ -17,6 +17,7 @@ func Init() {
|
||||
migrations.AddTableCronjob,
|
||||
migrations.AddTableApp,
|
||||
migrations.AddTableImageRepo,
|
||||
migrations.AddTableWebsite,
|
||||
migrations.AddTableDatabaseMysql,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
|
@ -177,3 +177,20 @@ var AddTableDatabaseMysql = &gormigrate.Migration{
|
||||
return tx.AutoMigrate(&model.DatabaseMysql{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddTableWebsite = &gormigrate.Migration{
|
||||
ID: "20201009-add-table-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.WebSite{}, &model.WebSiteDomain{}, &model.WebSiteGroup{}); err != nil {
|
||||
return err
|
||||
}
|
||||
item := &model.WebSiteGroup{
|
||||
Name: "默认分组",
|
||||
Default: true,
|
||||
}
|
||||
if err := tx.Create(item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
@ -79,6 +79,8 @@ func Routers() *gin.Engine {
|
||||
systemRouter.InitCronjobRouter(PrivateGroup)
|
||||
systemRouter.InitSettingRouter(PrivateGroup)
|
||||
systemRouter.InitAppRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteRouter(PrivateGroup)
|
||||
systemRouter.InitWebsiteGroupRouter(PrivateGroup)
|
||||
systemRouter.InitDatabaseRouter(PrivateGroup)
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@ type RouterGroup struct {
|
||||
CronjobRouter
|
||||
SettingRouter
|
||||
AppRouter
|
||||
WebsiteRouter
|
||||
WebsiteGroupRouter
|
||||
DatabaseRouter
|
||||
}
|
||||
|
||||
|
20
backend/router/ro_website.go
Normal file
20
backend/router/ro_website.go
Normal file
@ -0,0 +1,20 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WebsiteRouter struct {
|
||||
}
|
||||
|
||||
func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
|
||||
groupRouter := Router.Group("websites")
|
||||
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
groupRouter.POST("", baseApi.CreateWebsite)
|
||||
}
|
||||
}
|
20
backend/router/ro_website_group.go
Normal file
20
backend/router/ro_website_group.go
Normal file
@ -0,0 +1,20 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WebsiteGroupRouter struct {
|
||||
}
|
||||
|
||||
func (a *WebsiteGroupRouter) InitWebsiteGroupRouter(Router *gin.RouterGroup) {
|
||||
groupRouter := Router.Group("websites/groups")
|
||||
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
groupRouter.GET("", baseApi.GetGroups)
|
||||
}
|
||||
}
|
@ -48,3 +48,19 @@ func (b *Block) AddDirectives(directive Directive) {
|
||||
directives := append(b.GetDirectives(), &directive)
|
||||
b.Directives = directives
|
||||
}
|
||||
|
||||
func (b *Block) RemoveDirectives(names []string) {
|
||||
nameMaps := make(map[string]struct{}, len(names))
|
||||
for _, name := range names {
|
||||
nameMaps[name] = struct{}{}
|
||||
}
|
||||
directives := b.GetDirectives()
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if _, ok := nameMaps[dir.GetName()]; ok {
|
||||
continue
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
b.Directives = newDirectives
|
||||
}
|
||||
|
@ -88,6 +88,22 @@ func (h *Http) AddDirectives(directive Directive) {
|
||||
h.Directives = directives
|
||||
}
|
||||
|
||||
func (h *Http) RemoveDirectives(names []string) {
|
||||
nameMaps := make(map[string]struct{}, len(names))
|
||||
for _, name := range names {
|
||||
nameMaps[name] = struct{}{}
|
||||
}
|
||||
directives := h.GetDirectives()
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if _, ok := nameMaps[dir.GetName()]; ok {
|
||||
continue
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
h.Directives = newDirectives
|
||||
}
|
||||
|
||||
func (h *Http) GetBlock() IBlock {
|
||||
return h
|
||||
}
|
||||
|
@ -16,9 +16,11 @@ func NewServer(directive IDirective) (*Server, error) {
|
||||
server.Comment = block.GetComment()
|
||||
directives := block.GetDirectives()
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == "listen" {
|
||||
|
||||
switch dir.GetName() {
|
||||
case "listen":
|
||||
server.Listens = append(server.Listens, NewServerListen(dir.GetParameters()))
|
||||
} else {
|
||||
default:
|
||||
server.Directives = append(server.Directives, dir)
|
||||
}
|
||||
}
|
||||
@ -63,6 +65,56 @@ func (s *Server) AddListen(bind string, defaultServer bool, params ...string) {
|
||||
s.Listens = append(s.Listens, listen)
|
||||
}
|
||||
|
||||
func (s *Server) UpdateListen(bind string, defaultServer bool, params ...string) {
|
||||
listen := &ServerListen{
|
||||
Bind: bind,
|
||||
Parameters: params,
|
||||
}
|
||||
if defaultServer {
|
||||
listen.DefaultServer = DefaultServer
|
||||
}
|
||||
var newListens []*ServerListen
|
||||
for _, li := range s.Listens {
|
||||
if li.Bind == bind {
|
||||
newListens = append(newListens, listen)
|
||||
} else {
|
||||
newListens = append(newListens, li)
|
||||
}
|
||||
}
|
||||
|
||||
s.Listens = newListens
|
||||
}
|
||||
|
||||
func (s *Server) UpdateServerName(names []string) {
|
||||
serverNameDirective := Directive{
|
||||
Name: "server_name",
|
||||
Parameters: names,
|
||||
}
|
||||
s.UpdateDirectives("server_name", serverNameDirective)
|
||||
}
|
||||
|
||||
func (s *Server) UpdateRootProxy(proxy []string) {
|
||||
//TODD 根据ID修改
|
||||
dirs := s.FindDirectives("location")
|
||||
for _, dir := range dirs {
|
||||
location, ok := dir.(*Location)
|
||||
if ok && location.Match == "/" {
|
||||
newDir := Directive{
|
||||
Name: "location",
|
||||
Parameters: []string{"/"},
|
||||
Block: &Block{},
|
||||
}
|
||||
block := &Block{}
|
||||
block.Directives = append(block.Directives, &Directive{
|
||||
Name: "proxy_pass",
|
||||
Parameters: proxy,
|
||||
})
|
||||
newDir.Block = block
|
||||
s.UpdateDirectives("location", newDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RemoveListenByBind(bind string) {
|
||||
index := 0
|
||||
listens := s.Listens
|
||||
@ -105,3 +157,19 @@ func (s *Server) AddDirectives(directive Directive) {
|
||||
directives := append(s.Directives, &directive)
|
||||
s.Directives = directives
|
||||
}
|
||||
|
||||
func (s *Server) RemoveDirectives(names []string) {
|
||||
nameMaps := make(map[string]struct{}, len(names))
|
||||
for _, name := range names {
|
||||
nameMaps[name] = struct{}{}
|
||||
}
|
||||
directives := s.GetDirectives()
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if _, ok := nameMaps[dir.GetName()]; ok {
|
||||
continue
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
s.Directives = newDirectives
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ type IBlock interface {
|
||||
FindDirectives(directiveName string) []IDirective
|
||||
UpdateDirectives(directiveName string, directive Directive)
|
||||
AddDirectives(directive Directive)
|
||||
RemoveDirectives(names []string)
|
||||
GetComment() string
|
||||
}
|
||||
|
||||
|
@ -92,3 +92,19 @@ func (us *Upstream) AddDirectives(directive Directive) {
|
||||
directives := append(us.GetDirectives(), &directive)
|
||||
us.Directives = directives
|
||||
}
|
||||
|
||||
func (us *Upstream) RemoveDirectives(names []string) {
|
||||
nameMaps := make(map[string]struct{}, len(names))
|
||||
for _, name := range names {
|
||||
nameMaps[name] = struct{}{}
|
||||
}
|
||||
directives := us.GetDirectives()
|
||||
var newDirectives []IDirective
|
||||
for _, dir := range directives {
|
||||
if _, ok := nameMaps[dir.GetName()]; ok {
|
||||
continue
|
||||
}
|
||||
newDirectives = append(newDirectives, dir)
|
||||
}
|
||||
us.Directives = newDirectives
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"fmt"
|
||||
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@ -123,10 +121,5 @@ func DumpConfig(c *components.Config, style *Style) string {
|
||||
}
|
||||
|
||||
func WriteConfig(c *components.Config, style *Style) error {
|
||||
dir, _ := filepath.Split(c.FilePath)
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), 0644)
|
||||
}
|
||||
|
4
cmd/server/nginx_conf/gzip.conf
Normal file
4
cmd/server/nginx_conf/gzip.conf
Normal file
@ -0,0 +1,4 @@
|
||||
gzip on;
|
||||
gzip_comp_level 6;
|
||||
gzip_min_length 1k;
|
||||
gzip_types text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;
|
3
cmd/server/nginx_conf/http2https.conf
Normal file
3
cmd/server/nginx_conf/http2https.conf
Normal file
@ -0,0 +1,3 @@
|
||||
if ($server_port !~ 443){
|
||||
rewrite ^(/.*)$ https://$host$1 permanent;
|
||||
}
|
3
cmd/server/nginx_conf/limit.conf
Normal file
3
cmd/server/nginx_conf/limit.conf
Normal file
@ -0,0 +1,3 @@
|
||||
limit_conn perserver 300;
|
||||
limit_conn perip 25;
|
||||
limit_rate 512k;
|
14
cmd/server/nginx_conf/nginx_conf.go
Normal file
14
cmd/server/nginx_conf/nginx_conf.go
Normal file
@ -0,0 +1,14 @@
|
||||
package nginx_conf
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed ssl.conf
|
||||
var SSL []byte
|
||||
|
||||
//go:embed http2https.conf
|
||||
var HTTPS []byte
|
||||
|
||||
//go:embed website_default.conf
|
||||
var WebsiteDefault []byte
|
9
cmd/server/nginx_conf/ssl.conf
Normal file
9
cmd/server/nginx_conf/ssl.conf
Normal file
@ -0,0 +1,9 @@
|
||||
ssl_certificate /www/server/panel/vhost/cert/1panel.cloud/fullchain.pem;
|
||||
ssl_certificate_key /www/server/panel/vhost/cert/1panel.cloud/privkey.pem;
|
||||
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
add_header Strict-Transport-Security "max-age=31536000";
|
||||
error_page 497 https://$host$request_uri;
|
17
cmd/server/nginx_conf/website_default.conf
Normal file
17
cmd/server/nginx_conf/website_default.conf
Normal file
@ -0,0 +1,17 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name ko.wp-1.com;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
access_log /var/log/nginx/nginx-log.log;
|
||||
}
|
@ -88,6 +88,10 @@ export namespace App {
|
||||
detailId?: number;
|
||||
}
|
||||
|
||||
export interface AppInstalledSearch {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface AppService {
|
||||
label: string;
|
||||
value: string;
|
||||
|
19
frontend/src/api/interface/website.ts
Normal file
19
frontend/src/api/interface/website.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { CommonModel } from '.';
|
||||
|
||||
export namespace WebSite {
|
||||
export interface WebSiteCreateReq {
|
||||
primaryDomain: string;
|
||||
type: string;
|
||||
alias: string;
|
||||
remark: string;
|
||||
domains: string[];
|
||||
appType: string;
|
||||
appInstallID: number;
|
||||
webSiteGroupID: number;
|
||||
otherDomains: string;
|
||||
}
|
||||
|
||||
export interface Group extends CommonModel {
|
||||
name: string;
|
||||
}
|
||||
}
|
@ -30,6 +30,10 @@ export const GetAppInstalled = (info: ReqPage) => {
|
||||
return http.post<ResPage<App.AppInstalled>>('apps/installed', info);
|
||||
};
|
||||
|
||||
export const SearchAppInstalled = (search: App.AppInstalledSearch) => {
|
||||
return http.post<App.AppInstalled[]>('apps/installed', search);
|
||||
};
|
||||
|
||||
export const InstalledOp = (op: App.AppInstalledOp) => {
|
||||
return http.post<any>('apps/installed/op', op);
|
||||
};
|
||||
|
10
frontend/src/api/modules/website.ts
Normal file
10
frontend/src/api/modules/website.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import http from '@/api';
|
||||
import { WebSite } from '../interface/website';
|
||||
|
||||
export const listGroups = () => {
|
||||
return http.get<WebSite.Group[]>(`/websites/groups`);
|
||||
};
|
||||
|
||||
export const CreateWebsite = (req: WebSite.WebSiteCreateReq) => {
|
||||
return http.post<any>(`/websites`, req);
|
||||
};
|
@ -662,4 +662,19 @@ export default {
|
||||
update: '更新',
|
||||
versioneSelect: '请选择版本',
|
||||
},
|
||||
website: {
|
||||
primaryDomain: '主域名',
|
||||
otherDomains: '其他域名',
|
||||
type: '类型',
|
||||
static: '静态网站',
|
||||
deployment: '一键部署',
|
||||
proxy: '反向代理',
|
||||
alias: '代号',
|
||||
remark: '备注',
|
||||
group: '分组',
|
||||
app: '应用',
|
||||
app_new: '新装应用',
|
||||
app_installed: '已装应用',
|
||||
create: '创建网站',
|
||||
},
|
||||
};
|
||||
|
@ -18,6 +18,15 @@ const webSiteRouter = {
|
||||
title: 'menu.project',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/websites/projects/config',
|
||||
name: 'WebsiteConfig',
|
||||
component: () => import('@/views/website/project/config/index.vue'),
|
||||
hidden: true,
|
||||
meta: {
|
||||
activeMenu: '/websites',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/websites/config',
|
||||
name: 'Config',
|
||||
|
@ -9,7 +9,7 @@ export const GlobalStore = defineStore({
|
||||
state: (): GlobalState => ({
|
||||
isLogin: false,
|
||||
csrfToken: '',
|
||||
assemblySize: 'small',
|
||||
assemblySize: 'default',
|
||||
language: '',
|
||||
themeConfig: {
|
||||
panelName: '',
|
||||
|
37
frontend/src/views/website/project/config/index.vue
Normal file
37
frontend/src/views/website/project/config/index.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<LayoutContent :header="'网站设置'" :back-name="'Website'">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane label="基本">
|
||||
<el-tabs tab-position="left" type="border-card">
|
||||
<el-tab-pane label="域名设置">
|
||||
<ComplexTable>
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain>{{ '新增域名' }}</el-button>
|
||||
</template>
|
||||
<el-table-column :label="'域名'" prop="backup"></el-table-column>
|
||||
<el-table-column :label="'端口'" prop="remark"></el-table-column>
|
||||
</ComplexTable>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="目录设置">Config</el-tab-pane>
|
||||
<el-tab-pane label="HTTPS">Role</el-tab-pane>
|
||||
<el-tab-pane label="域名跳转">Task</el-tab-pane>
|
||||
<el-tab-pane label="错误页面">Task</el-tab-pane>
|
||||
<el-tab-pane label="过期时间">Task</el-tab-pane>
|
||||
<el-tab-pane label="流量限制">Task</el-tab-pane>
|
||||
<el-tab-pane label="默认文档">Task</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="优化">优化</el-tab-pane>
|
||||
<el-tab-pane label="反代">反代</el-tab-pane>
|
||||
<el-tab-pane label="安全">反代</el-tab-pane>
|
||||
<el-tab-pane label="备份">反代</el-tab-pane>
|
||||
<el-tab-pane label="日志">反代</el-tab-pane>
|
||||
<el-tab-pane label="源文">反代</el-tab-pane>
|
||||
</el-tabs>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LayoutContent from '@/layout/layout-content.vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
</script>
|
135
frontend/src/views/website/project/create/index.vue
Normal file
135
frontend/src/views/website/project/create/index.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<el-dialog v-model="open" :title="$t('website.create')" width="40%" :before-close="handleClose">
|
||||
<el-form ref="websiteForm" label-position="right" :model="website" label-width="100px" :rules="rules">
|
||||
<el-form-item :label="$t('website.type')" prop="type">
|
||||
<el-select v-model="website.type">
|
||||
<el-option :label="$t('website.deployment')" value="deployment"></el-option>
|
||||
<el-option :label="$t('website.static')" value="static"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.group')" prop="webSiteGroupID">
|
||||
<el-select v-model="website.webSiteGroupID">
|
||||
<el-option
|
||||
v-for="(group, index) in groups"
|
||||
:key="index"
|
||||
:label="group.name"
|
||||
:value="group.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-if="website.type === 'deployment'">
|
||||
<el-form-item prop="appType">
|
||||
<el-radio-group v-model="website.appType">
|
||||
<el-radio :label="'installed'" :value="'installed'">
|
||||
{{ $t('website.app_installed') }}
|
||||
</el-radio>
|
||||
<el-radio :label="'new'">
|
||||
{{ $t('website.app_new') }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="website.appType == 'installed'"
|
||||
:label="$t('website.app_installed')"
|
||||
prop="appInstallID"
|
||||
>
|
||||
<el-select v-model="website.appInstallID">
|
||||
<el-option
|
||||
v-for="(appInstall, index) in appInstalles"
|
||||
:key="index"
|
||||
:label="appInstall.name"
|
||||
:value="appInstall.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item :label="$t('website.primaryDomain')" prop="primaryDomain">
|
||||
<el-input v-model="website.primaryDomain"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.otherDomains')" prop="otherDomains">
|
||||
<el-input v-model="website.otherDomains"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.alias')" prop="alias">
|
||||
<el-input v-model="website.alias"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.remark')" prop="remark">
|
||||
<el-input v-model="website.remark"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(websiteForm)" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="CreateWebSite">
|
||||
import { App } from '@/api/interface/app';
|
||||
import { WebSite } from '@/api/interface/website';
|
||||
import { SearchAppInstalled } from '@/api/modules/app';
|
||||
import { CreateWebsite, listGroups } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { FormRules, ElDialog, ElForm, FormInstance } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
const websiteForm = ref<FormInstance>();
|
||||
const website = reactive({
|
||||
primaryDomain: '',
|
||||
type: 'deployment',
|
||||
alias: '',
|
||||
remark: '',
|
||||
domains: [],
|
||||
appType: 'installed',
|
||||
appInstallID: 0,
|
||||
webSiteGroupID: 1,
|
||||
otherDomains: '',
|
||||
});
|
||||
let rules = reactive<FormRules>({
|
||||
primaryDomain: [Rules.requiredInput],
|
||||
alias: [Rules.requiredInput],
|
||||
type: [Rules.requiredInput],
|
||||
webSiteGroupID: [Rules.requiredInput],
|
||||
appInstallID: [Rules.requiredInput],
|
||||
appType: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
let open = ref(false);
|
||||
let loading = ref(false);
|
||||
let groups = ref<WebSite.Group[]>([]);
|
||||
let appInstalles = ref<App.AppInstalled[]>([]);
|
||||
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
};
|
||||
|
||||
const acceptParams = async () => {
|
||||
await listGroups().then((res) => {
|
||||
groups.value = res.data;
|
||||
open.value = true;
|
||||
});
|
||||
await SearchAppInstalled({ type: 'website' }).then((res) => {
|
||||
appInstalles.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
website.appInstallID = res.data[0].id;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
CreateWebsite(website).then(() => {});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -1,7 +1,69 @@
|
||||
<template>
|
||||
<LayoutContent></LayoutContent>
|
||||
<LayoutContent :header="'网站'">
|
||||
<ComplexTable :data="data" @search="search()">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain @click="openCreate">{{ '新建网站' }}</el-button>
|
||||
<el-button type="primary" plain>{{ '修改默认页' }}</el-button>
|
||||
<el-button type="primary" plain>{{ '默认站点' }}</el-button>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="primaryDomain">
|
||||
<template #default="{ row }">
|
||||
<el-link @click="openConfig">{{ row.primaryDomain }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status"></el-table-column>
|
||||
<el-table-column :label="'备份'" prop="backup"></el-table-column>
|
||||
<el-table-column :label="'备注'" prop="remark"></el-table-column>
|
||||
<el-table-column :label="'SSL证书'" prop="ssl"></el-table-column>
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
<CreateWebSite ref="createRef"></CreateWebSite>
|
||||
</LayoutContent>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import LayoutContent from '@/layout/layout-content.vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { onMounted, ref } from '@vue/runtime-core';
|
||||
import router from '@/routers';
|
||||
import CreateWebSite from './create/index.vue';
|
||||
|
||||
const createRef = ref();
|
||||
|
||||
const data = ref();
|
||||
const search = async () => {
|
||||
data.value = [
|
||||
{
|
||||
primaryDomain: 'www.baicu.com',
|
||||
status: 'Running',
|
||||
backup: '1',
|
||||
remark: '主网站',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const openConfig = () => {
|
||||
router.push({ name: 'WebsiteConfig' });
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: '设置',
|
||||
click: open,
|
||||
},
|
||||
];
|
||||
|
||||
const openCreate = () => {
|
||||
createRef.value.acceptParams();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user