diff --git a/internal/application/deploy.go b/internal/application/deploy.go index 86d5157..58c5e93 100644 --- a/internal/application/deploy.go +++ b/internal/application/deploy.go @@ -31,7 +31,7 @@ func DeployToNetlify(target string, deployment *valueobject.SiteDeployment, toke SiteID: deployment.NetlifySiteID, Directory: path.Join(target, "public"), Draft: false, - DeployMessage: "Deployed from Hugoverse", + DeployMessage: "Deployed by MDFriday", LogLevel: "debug", LogFormat: "text", } diff --git a/internal/domain/content/entity/deploy.go b/internal/domain/content/entity/deploy.go index 2e225ae..6db7955 100644 --- a/internal/domain/content/entity/deploy.go +++ b/internal/domain/content/entity/deploy.go @@ -5,18 +5,17 @@ import ( "errors" "fmt" "github.com/gohugonet/hugoverse/internal/domain/content/valueobject" - "net/url" "time" ) -func (c *Content) GetDeployment(siteId string) (*valueobject.SiteDeployment, error) { +func (c *Content) GetDeployment(siteId string, domain *valueobject.Domain) (*valueobject.SiteDeployment, error) { content, err := c.getContent("Site", siteId) if err != nil { return nil, err } if site, ok := content.(*valueobject.Site); ok { - sd, err := c.searchDeployment(siteId) + sd, err := c.searchDeployment(domain.Root, domain.Sub, domain.Owner) if err != nil { return nil, err } @@ -30,7 +29,7 @@ func (c *Content) GetDeployment(siteId string) (*valueobject.SiteDeployment, err sd = &valueobject.SiteDeployment{ Item: *item, Site: site.QueryString(), - Netlify: fmt.Sprintf("mdf-%d-%s", time.Now().Unix(), site.ItemSlug()), + Netlify: fmt.Sprintf("mdf-%d-%s", time.Now().UnixMilli(), site.ItemSlug()), Domain: site.ItemSlug(), Status: "Not Started", } @@ -47,27 +46,35 @@ func (c *Content) GetDeployment(siteId string) (*valueobject.SiteDeployment, err return nil, errors.New("only site could be deployed") } -func (c *Content) searchDeployment(siteId string) (*valueobject.SiteDeployment, error) { - q := fmt.Sprintf(`site%s`, siteId) - encodedQ := url.QueryEscape(q) +func (c *Content) searchDeployment(root string, sub string, owner string) (*valueobject.SiteDeployment, error) { + conditions := map[string]string{ + "root": root, + "domain": sub, + "owner": owner, + } - siteDeployments, err := c.search("SiteDeployment", fmt.Sprintf("slug:%s", encodedQ)) + // 查询域名信息 + deploys, err := c.termSearch("SiteDeployment", conditions) if err != nil { return nil, err } - if len(siteDeployments) == 1 && siteDeployments[0] == nil { + // 如果返回仅一个结果且为 nil,返回 nil + if len(deploys) == 1 && deploys[0] == nil { return nil, nil } - for _, data := range siteDeployments { - var sd valueobject.SiteDeployment - if err := json.Unmarshal(data, &sd); err != nil { + // 遍历查询结果并解析 + for _, data := range deploys { + var deployment valueobject.SiteDeployment + if err := json.Unmarshal(data, &deployment); err != nil { return nil, err } - return &sd, nil + // 返回匹配的域名对象 + return &deployment, nil } + // 未找到匹配结果 return nil, nil } diff --git a/internal/domain/content/entity/domain.go b/internal/domain/content/entity/domain.go new file mode 100644 index 0000000..e8c9440 --- /dev/null +++ b/internal/domain/content/entity/domain.go @@ -0,0 +1,83 @@ +package entity + +import ( + "encoding/json" + "errors" + "github.com/gohugonet/hugoverse/internal/domain/content/valueobject" +) + +func (c *Content) ApplyDomain(siteId string, domain string) (*valueobject.Domain, error) { + site, err := c.getContent("Site", siteId) + if err != nil { + return nil, err + } + + if site, ok := site.(*valueobject.Site); ok { + slug, err := Slug(site) + if err != nil { + return nil, err + } + + sd, err := c.searchDomain(domain, slug, site.Owner) + if err != nil { + return nil, err + } + + if sd == nil { + item, err := valueobject.NewItemWithNamespace("Domain") + if err != nil { + return nil, err + } + + sd = &valueobject.Domain{ + Item: *item, + Root: domain, + Sub: slug, + Owner: site.Owner, + } + + _, err = c.newContent("Domain", sd) + if err != nil { + return nil, err + } + } + + return sd, nil + } + + return nil, errors.New("only site could be deployed with domain") +} + +func (c *Content) searchDomain(root string, sub string, owner string) (*valueobject.Domain, error) { + // 构建精确匹配的查询条件 + conditions := map[string]string{ + "root": root, + "sub": sub, + "owner": owner, + } + + // 查询域名信息 + domains, err := c.termSearch("Domain", conditions) + if err != nil { + return nil, err + } + + // 如果返回仅一个结果且为 nil,返回 nil + if len(domains) == 1 && domains[0] == nil { + return nil, nil + } + + // 遍历查询结果并解析 + for _, data := range domains { + var domain valueobject.Domain + if err := json.Unmarshal(data, &domain); err != nil { + return nil, err + } + + // 返回匹配的域名对象 + return &domain, nil + } + + // 未找到匹配结果 + return nil, nil +} diff --git a/internal/domain/content/entity/query.go b/internal/domain/content/entity/query.go index 74fdb08..129a184 100644 --- a/internal/domain/content/entity/query.go +++ b/internal/domain/content/entity/query.go @@ -29,6 +29,29 @@ func (c *Content) search(contentType string, query string) ([][]byte, error) { return bb, nil } +func (c *Content) termSearch(contentType string, keyValue map[string]string) ([][]byte, error) { + // execute search for query provided, if no index for type send 404 + indices, err := c.Search.TermQuery(contentType, keyValue, 10, 0) + if errors.Is(err, content.ErrNoIndex) { + c.Log.Errorf("Index for type %s not found", contentType) + + return nil, err + } + if err != nil { + c.Log.Errorf("Error searching for type %s: %v", contentType, err) + return nil, err + } + + // respond with json formatted results + bb, err := c.GetContents(indices) + if err != nil { + c.Log.Errorf("Error getting content: %v", err) + return nil, err + } + + return bb, nil +} + func (c *Content) getContent(contentType, id string) (any, error) { bs, err := c.GetContent(contentType, id, "") if err != nil { diff --git a/internal/domain/content/entity/search.go b/internal/domain/content/entity/search.go index 629049f..d25f922 100644 --- a/internal/domain/content/entity/search.go +++ b/internal/domain/content/entity/search.go @@ -184,6 +184,46 @@ func (s *Search) TypeQuery(typeName, query string, count, offset int) ([]content return results, nil } +func (s *Search) TermQuery(typeName string, keyValues map[string]string, count, offset int) ([]content.Identifier, error) { + // 获取索引 + idx, err := s.getSearchIndex(typeName) + if err != nil { + s.Log.Debugln("Index for type ", typeName, " not found", err.Error()) + return nil, content.ErrNoIndex + } + + s.Log.Debugln("KeyValueQuery KeyValues: ", keyValues) + + // 创建每个 Key-Value 的查询 + var termQueries []bleve.Query + for key, value := range keyValues { + tq := bleve.NewTermQuery(value) + tq.SetField(key) + + termQueries = append(termQueries, tq) + } + + // 将查询组合成一个 ConjunctionQuery + finalQuery := bleve.NewConjunctionQuery(termQueries...) + + // 创建搜索请求 + req := bleve.NewSearchRequestOptions(finalQuery, count, offset, false) + + // 执行搜索 + res, err := idx.Search(req) + if err != nil { + return nil, err + } + + // 处理搜索结果 + var results []content.Identifier + for _, hit := range res.Hits { + results = append(results, valueobject.CreateIndex(hit.ID)) + } + + return results, nil +} + // UpdateIndex sets data into a content type's search index at the given // identifier func (s *Search) UpdateIndex(ns, id string, data []byte) error { diff --git a/internal/domain/content/valueobject/domain.go b/internal/domain/content/valueobject/domain.go index 9bc7bf3..4eacbf4 100644 --- a/internal/domain/content/valueobject/domain.go +++ b/internal/domain/content/valueobject/domain.go @@ -9,31 +9,38 @@ import ( type Domain struct { Item - Id uint64 `json:"id"` + Root string `json:"root"` Sub string `json:"sub"` - Email string `json:"email"` + Owner string `json:"owner"` } func (d *Domain) Name() string { - return d.Sub + return fmt.Sprintf("%s.%s - %s", d.Sub, d.Root, d.Owner) } // MarshalEditor writes a buffer of html to edit a Song within the CMS // and implements editor.Editable func (d *Domain) MarshalEditor() ([]byte, error) { view, err := editor.Form(d, + editor.Field{ + View: editor.Input("Root", d, map[string]string{ + "label": "Root", + "type": "text", + "placeholder": "Enter the root domain here", + }), + }, editor.Field{ View: editor.Input("Sub", d, map[string]string{ - "label": "SubDomain", + "label": "Sub", "type": "text", - "placeholder": "Enter the subdomain here", + "placeholder": "Enter the sub domain here", }), }, editor.Field{ - View: editor.Input("Email", d, map[string]string{ - "label": "Email", + View: editor.Input("Owner", d, map[string]string{ + "label": "Owner", "type": "text", - "placeholder": "Enter the Email here", + "placeholder": "Enter the Owner here", }), }, ) @@ -54,8 +61,9 @@ func (d *Domain) String() string { return d.Name() } func (d *Domain) Create(res http.ResponseWriter, req *http.Request) error { // do form data validation for required fields required := []string{ + "root", "sub", - "email", + "owner", } for _, r := range required { @@ -125,3 +133,7 @@ func (d *Domain) AutoApprove(res http.ResponseWriter, req *http.Request) error { return nil } + +func (d *Domain) IndexContent() bool { + return true +} diff --git a/internal/domain/content/valueobject/sitedeployment.go b/internal/domain/content/valueobject/sitedeployment.go index f8de9b5..1169ffb 100644 --- a/internal/domain/content/valueobject/sitedeployment.go +++ b/internal/domain/content/valueobject/sitedeployment.go @@ -8,6 +8,11 @@ import ( "strings" ) +// TODO: deployment belongs to admin +// need netlify sub domain, and site id +// need domain root, sub, and owner info +// no need site info + type SiteDeployment struct { Item @@ -54,6 +59,13 @@ func (s *SiteDeployment) MarshalEditor() ([]byte, error) { "placeholder": "Enter the Customized sub domain here", }), }, + editor.Field{ + View: editor.Input("Root", s, map[string]string{ + "label": "Root", + "type": "text", + "placeholder": "Enter the Customized root domain here", + }), + }, editor.Field{ View: editor.Input("Status", s, map[string]string{ "label": "Status", diff --git a/internal/interfaces/api/handler/handledeploy.go b/internal/interfaces/api/handler/handledeploy.go index c9954d5..d5d3386 100644 --- a/internal/interfaces/api/handler/handledeploy.go +++ b/internal/interfaces/api/handler/handledeploy.go @@ -20,6 +20,27 @@ func (s *Handler) DeployContentHandler(res http.ResponseWriter, req *http.Reques return } + err := req.ParseForm() + if err != nil { + s.log.Errorf("Error parsing deploy form: %v", err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + netlify := req.PostForm.Get("netlify") + root := req.PostForm.Get("domain") + if netlify == "" || root == "" { + netlify = s.adminApp.Netlify.Token() + root = "app.mdfriday.com" + } + + d, err := s.contentApp.ApplyDomain(id, root) + if err != nil { + s.log.Errorf("Error applying domain: %v", err) + res.WriteHeader(http.StatusInternalServerError) + return + } + pt, ok := s.contentApp.GetContentCreator(t) if !ok { res.WriteHeader(http.StatusNotFound) @@ -34,7 +55,7 @@ func (s *Handler) DeployContentHandler(res http.ResponseWriter, req *http.Reques return } - t, err := s.contentApp.BuildTarget(t, id, status) + t, err = s.contentApp.BuildTarget(t, id, status) if err != nil { s.log.Errorf("Error building: %v", err) res.WriteHeader(http.StatusInternalServerError) @@ -48,14 +69,14 @@ func (s *Handler) DeployContentHandler(res http.ResponseWriter, req *http.Reques return } - sd, err := s.contentApp.GetDeployment(id) + sd, err := s.contentApp.GetDeployment(id, d) if err != nil { s.log.Errorf("Error getting deployment: %v", err) res.WriteHeader(http.StatusInternalServerError) return } - err = application.DeployToNetlify(t, sd, s.adminApp.Netlify.Token()) + err = application.DeployToNetlify(t, sd, netlify) if err != nil { s.log.Errorf("Error building: %v", err) res.WriteHeader(http.StatusInternalServerError)