Skip to content

Commit

Permalink
fix: support import theme from git repo
Browse files Browse the repository at this point in the history
  • Loading branch information
golangboy authored and 1379 committed Jul 13, 2023
1 parent 7bec1f6 commit c2450ab
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 88 deletions.
18 changes: 17 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/fsnotify/fsnotify v1.6.0
github.com/gin-contrib/cors v1.4.0
github.com/gin-gonic/gin v1.8.2
github.com/go-git/go-git/v5 v5.7.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.13.0
Expand Down Expand Up @@ -42,19 +43,29 @@ require (
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/leodido/go-urn v1.2.3 // indirect
Expand All @@ -69,13 +80,17 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/yeqown/reedsolomon v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
Expand All @@ -89,6 +104,7 @@ require (
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/datatypes v1.0.7 // indirect
gorm.io/hints v1.1.0 // indirect
Expand Down
63 changes: 62 additions & 1 deletion go.sum

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion handler/admin/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ func (t *ThemeHandler) UpdateThemeByUpload(ctx *gin.Context) (interface{}, error
}

func (t *ThemeHandler) FetchTheme(ctx *gin.Context) (interface{}, error) {
return nil, xerr.WithMsg(nil, "not support").WithStatus(xerr.StatusInternalServerError)
uri, _ := util.MustGetQueryString(ctx, "uri")
return t.ThemeService.Fetch(ctx, uri)
}

func (t *ThemeHandler) UpdateThemeByFetching(ctx *gin.Context) (interface{}, error) {
Expand Down
26 changes: 21 additions & 5 deletions service/impl/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"time"

"go.uber.org/fx"

"github.com/go-sonic/sonic/config"
"github.com/go-sonic/sonic/dal"
"github.com/go-sonic/sonic/event"
Expand All @@ -27,17 +29,23 @@ type themeServiceImpl struct {
Event event.Bus
PropertyScanner theme.PropertyScanner
FileScanner theme.FileScanner
MultipartZipThemeFetcher theme.MultipartZipThemeFetcher
ThemeFetchers themeFetchers
}

type themeFetchers struct {
fx.In
MultipartZipThemeFetcher theme.ThemeFetcher `name:"multipartZipThemeFetcher"`
GitRepoThemeFetcher theme.ThemeFetcher `name:"gitRepoThemeFetcher"`
}

func NewThemeService(optionService service.OptionService, config *config.Config, event event.Bus, propertyScanner theme.PropertyScanner, fileScanner theme.FileScanner, multipartZipThemeFetcher theme.MultipartZipThemeFetcher) service.ThemeService {
func NewThemeService(optionService service.OptionService, config *config.Config, event event.Bus, propertyScanner theme.PropertyScanner, fileScanner theme.FileScanner, themeFetcher themeFetchers) service.ThemeService {
return &themeServiceImpl{
OptionService: optionService,
Config: config,
Event: event,
PropertyScanner: propertyScanner,
FileScanner: fileScanner,
MultipartZipThemeFetcher: multipartZipThemeFetcher,
ThemeFetchers: themeFetcher,
}
}

Expand Down Expand Up @@ -390,7 +398,7 @@ func (t *themeServiceImpl) DeleteTheme(ctx context.Context, themeID string, dele
}

func (t *themeServiceImpl) UploadTheme(ctx context.Context, file *multipart.FileHeader) (*dto.ThemeProperty, error) {
themeProperty, err := t.MultipartZipThemeFetcher.FetchTheme(ctx, file)
themeProperty, err := t.ThemeFetchers.MultipartZipThemeFetcher.FetchTheme(ctx, file)
if err != nil {
return nil, err
}
Expand All @@ -402,7 +410,7 @@ func (t *themeServiceImpl) UpdateThemeByUpload(ctx context.Context, themeID stri
if err != nil {
return nil, err
}
newThemeProperty, err := t.MultipartZipThemeFetcher.FetchTheme(ctx, file)
newThemeProperty, err := t.ThemeFetchers.MultipartZipThemeFetcher.FetchTheme(ctx, file)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -468,3 +476,11 @@ func (t *themeServiceImpl) Render(ctx context.Context, name string) (string, err
}
return activatedThemeID + "/" + name, nil
}

func (t *themeServiceImpl) Fetch(ctx context.Context, themeURL string) (*dto.ThemeProperty, error) {
fetchTheme, err := t.ThemeFetchers.GitRepoThemeFetcher.FetchTheme(ctx, themeURL)
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg(err.Error())
}
return t.addTheme(ctx, fetchTheme)
}
1 change: 1 addition & 0 deletions service/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ type ThemeService interface {
ReloadTheme(ctx context.Context) error
TemplateExist(ctx context.Context, template string) (bool, error)
Render(ctx context.Context, name string) (string, error)
Fetch(ctx context.Context, themeURL string) (*dto.ThemeProperty, error)
}
45 changes: 45 additions & 0 deletions service/theme/git_fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package theme

import (
"context"
"os"
"path/filepath"
"strings"

"github.com/go-git/go-git/v5"
"go.uber.org/fx"

"github.com/go-sonic/sonic/model/dto"
"github.com/go-sonic/sonic/util/xerr"
)

type gitThemeFetcherImpl struct {
fx.Out
PropertyScanner PropertyScanner
}

func (g gitThemeFetcherImpl) FetchTheme(ctx context.Context, file interface{}) (*dto.ThemeProperty, error) {
gitURL := file.(string)
splits := strings.Split(gitURL, "/")
lastSplit := splits[len(splits)-1]
tempDir := os.TempDir()

themeDirName := lastSplit
_, err := git.PlainClone(filepath.Join(tempDir, themeDirName), false, &git.CloneOptions{
URL: gitURL,
})
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg(err.Error())
}
themeProperty, err := g.PropertyScanner.ReadThemeProperty(ctx, filepath.Join(tempDir, themeDirName))
if err != nil {
return nil, err
}
return themeProperty, nil
}

func NewGitThemeFetcher(propertyScanner PropertyScanner) ThemeFetcher {
return &gitThemeFetcherImpl{
PropertyScanner: propertyScanner,
}
}
9 changes: 7 additions & 2 deletions service/theme/init.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package theme

import "github.com/go-sonic/sonic/injection"
import (
"go.uber.org/fx"

"github.com/go-sonic/sonic/injection"
)

func init() {
injection.Provide(
NewFileScanner,
NewPropertyScanner,
NewMultipartZipThemeFetcher,
fx.Annotated{Target: NewMultipartZipThemeFetcher, Name: "multipartZipThemeFetcher"},
fx.Annotated{Target: NewGitThemeFetcher, Name: "gitRepoThemeFetcher"},
)
}
87 changes: 87 additions & 0 deletions service/theme/multipartzip_fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package theme

import (
"context"
"errors"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"

"go.uber.org/fx"

"github.com/go-sonic/sonic/model/dto"
"github.com/go-sonic/sonic/util"
"github.com/go-sonic/sonic/util/xerr"
)

type multipartZipThemeFetcherImpl struct {
fx.Out
PropertyScanner PropertyScanner
}

func NewMultipartZipThemeFetcher(propertyScanner PropertyScanner) ThemeFetcher {
return &multipartZipThemeFetcherImpl{
PropertyScanner: propertyScanner,
}
}

func (m *multipartZipThemeFetcherImpl) FetchTheme(ctx context.Context, file interface{}) (*dto.ThemeProperty, error) {
themeFileHeader, ok := file.(*multipart.FileHeader)
if !ok {
return nil, xerr.WithStatus(nil, xerr.StatusBadRequest).WithMsg("not support")
}

tempDir := os.TempDir()
srcThemeFile, err := themeFileHeader.Open()
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("upload theme file error")
}
defer srcThemeFile.Close()

fileName := themeFileHeader.Filename
if !strings.HasSuffix(fileName, ".zip") {
return nil, xerr.WithStatus(nil, xerr.StatusBadRequest).WithMsg("not zip file")
}

diskFilePath := filepath.Join(tempDir, fileName)
if util.FileIsExisted(diskFilePath) {
err = os.Remove(diskFilePath)
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError)
}
}

diskFile, err := os.OpenFile(diskFilePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o444)
if err != nil && !errors.Is(err, os.ErrExist) {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("create file error")
}

defer diskFile.Close()

_, err = io.Copy(diskFile, srcThemeFile)
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("save file error")
}
_, err = util.Unzip(filepath.Join(tempDir, fileName), filepath.Join(tempDir, strings.TrimSuffix(fileName, ".zip")))
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("unzip file error")
}
dest := filepath.Join(tempDir, strings.TrimSuffix(fileName, ".zip"))
themeProperty, err := m.PropertyScanner.ReadThemeProperty(ctx, dest)
if err == nil && themeProperty != nil {
return themeProperty, nil
}
dirEntrys, err := os.ReadDir(dest)
for _, dirEntry := range dirEntrys {
if !dirEntry.IsDir() {
continue
}
themeProperty, err = m.PropertyScanner.ReadThemeProperty(ctx, filepath.Join(dest, dirEntry.Name()))
if err == nil && themeProperty != nil {
return themeProperty, nil
}
}
return nil, err
}
79 changes: 1 addition & 78 deletions service/theme/theme_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,10 @@ package theme

import (
"context"
"errors"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"

"github.com/go-sonic/sonic/model/dto"
"github.com/go-sonic/sonic/util"
"github.com/go-sonic/sonic/util/xerr"
)

type MultipartZipThemeFetcher interface {
type ThemeFetcher interface {
FetchTheme(ctx context.Context, file interface{}) (*dto.ThemeProperty, error)
}

type multipartZipThemeFetcherImpl struct {
PropertyScanner PropertyScanner
}

func NewMultipartZipThemeFetcher(propertyScanner PropertyScanner) MultipartZipThemeFetcher {
return &multipartZipThemeFetcherImpl{
PropertyScanner: propertyScanner,
}
}

func (m *multipartZipThemeFetcherImpl) FetchTheme(ctx context.Context, file interface{}) (*dto.ThemeProperty, error) {
themeFileHeader, ok := file.(*multipart.FileHeader)
if !ok {
return nil, xerr.WithStatus(nil, xerr.StatusBadRequest).WithMsg("not support")
}

tempDir := os.TempDir()
srcThemeFile, err := themeFileHeader.Open()
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("upload theme file error")
}
defer srcThemeFile.Close()

fileName := themeFileHeader.Filename
if !strings.HasSuffix(fileName, ".zip") {
return nil, xerr.WithStatus(nil, xerr.StatusBadRequest).WithMsg("not zip file")
}

diskFilePath := filepath.Join(tempDir, fileName)
if util.FileIsExisted(diskFilePath) {
err = os.Remove(diskFilePath)
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError)
}
}

diskFile, err := os.OpenFile(diskFilePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o444)
if err != nil && !errors.Is(err, os.ErrExist) {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("create file error")
}

defer diskFile.Close()

_, err = io.Copy(diskFile, srcThemeFile)
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("save file error")
}
_, err = util.Unzip(filepath.Join(tempDir, fileName), filepath.Join(tempDir, strings.TrimSuffix(fileName, ".zip")))
if err != nil {
return nil, xerr.WithStatus(err, xerr.StatusInternalServerError).WithMsg("unzip file error")
}
dest := filepath.Join(tempDir, strings.TrimSuffix(fileName, ".zip"))
themeProperty, err := m.PropertyScanner.ReadThemeProperty(ctx, dest)
if err == nil && themeProperty != nil {
return themeProperty, nil
}
dirEntrys, err := os.ReadDir(dest)
for _, dirEntry := range dirEntrys {
if !dirEntry.IsDir() {
continue
}
themeProperty, err = m.PropertyScanner.ReadThemeProperty(ctx, filepath.Join(dest, dirEntry.Name()))
if err == nil && themeProperty != nil {
return themeProperty, nil
}
}
return nil, err
}

0 comments on commit c2450ab

Please sign in to comment.