Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support import theme from git repo #271

Merged
merged 1 commit into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).
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)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).
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)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This path depends on a [user-provided value](1).
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
}