From a7de2e639966c774b0964486e92f73a54fe8383c Mon Sep 17 00:00:00 2001 From: hexxa Date: Sat, 9 Oct 2021 11:49:47 +0800 Subject: [PATCH] feat(db): add site store --- src/db/sitestore/site_store.go | 115 ++++++++++++++++++++++++++++ src/db/sitestore/site_store_test.go | 57 ++++++++++++++ src/depidx/deps.go | 12 ++- src/handlers/settings/handler.go | 26 ------- src/handlers/settings/handlers.go | 71 +++++++++++++++++ src/server/server.go | 6 ++ 6 files changed, 260 insertions(+), 27 deletions(-) create mode 100644 src/db/sitestore/site_store.go create mode 100644 src/db/sitestore/site_store_test.go delete mode 100644 src/handlers/settings/handler.go create mode 100644 src/handlers/settings/handlers.go diff --git a/src/db/sitestore/site_store.go b/src/db/sitestore/site_store.go new file mode 100644 index 0000000..b52c8aa --- /dev/null +++ b/src/db/sitestore/site_store.go @@ -0,0 +1,115 @@ +package sitestore + +import ( + "encoding/json" + "errors" + "fmt" + "sync" + "time" + + "github.com/ihexxa/quickshare/src/kvstore" +) + +const ( + InitNs = "SiteInit" + SiteNs = "Site" + InitTimeKey = "initTime" + SiteCfgKey = "siteCfg" +) + +var ( + ErrNotFound = errors.New("site config not found") +) + +func IsNotFound(err error) bool { + return err == ErrNotFound +} + +type ISiteStore interface { + SetClientCfg(cfg *ClientConfig) error + GetCfg() (*SiteConfig, error) +} + +type ClientConfig struct { + SiteName string `json:"siteName"` + SiteDesc string `json:"siteDesc"` + Bg *BgConfig `json:"bg"` +} + +type BgConfig struct { + Url string `json:"url"` + Repeat string `json:"repeat"` + Position string `json:"position"` + Align string `json:"align"` +} + +type SiteConfig struct { + ClientCfg *ClientConfig `json:"clientCfg"` +} + +type SiteStore struct { + mtx *sync.RWMutex + store kvstore.IKVStore +} + +func NewSiteStore(store kvstore.IKVStore) (*SiteStore, error) { + _, ok := store.GetStringIn(InitNs, InitTimeKey) + if !ok { + var err error + for _, nsName := range []string{ + InitNs, + SiteNs, + } { + if err = store.AddNamespace(nsName); err != nil { + return nil, err + } + } + } + + err := store.SetStringIn(InitNs, InitTimeKey, fmt.Sprintf("%d", time.Now().Unix())) + if err != nil { + return nil, err + } + + return &SiteStore{ + store: store, + mtx: &sync.RWMutex{}, + }, nil +} + +func (fi *SiteStore) SetClientCfg(cfg *ClientConfig) error { + fi.mtx.Lock() + defer fi.mtx.Unlock() + + siteCfg := &SiteConfig{} + cfgStr, ok := fi.store.GetStringIn(SiteNs, SiteCfgKey) + if ok { + err := json.Unmarshal([]byte(cfgStr), siteCfg) + if err != nil { + return err + } + } + siteCfg.ClientCfg = cfg + + cfgBytes, err := json.Marshal(siteCfg) + if err != nil { + return err + } + return fi.store.SetStringIn(SiteNs, SiteCfgKey, string(cfgBytes)) +} + +func (fi *SiteStore) GetCfg() (*SiteConfig, error) { + fi.mtx.RLock() + defer fi.mtx.RUnlock() + + cfgStr, ok := fi.store.GetStringIn(SiteNs, SiteCfgKey) + if !ok { + return nil, ErrNotFound + } + siteCfg := &SiteConfig{} + err := json.Unmarshal([]byte(cfgStr), siteCfg) + if err != nil { + return nil, err + } + return siteCfg, nil +} diff --git a/src/db/sitestore/site_store_test.go b/src/db/sitestore/site_store_test.go new file mode 100644 index 0000000..310e48e --- /dev/null +++ b/src/db/sitestore/site_store_test.go @@ -0,0 +1,57 @@ +package sitestore + +import ( + "io/ioutil" + "os" + "reflect" + "testing" + + "github.com/ihexxa/quickshare/src/kvstore/boltdbpvd" +) + +func TestSiteStore(t *testing.T) { + + testSiteMethods := func(t *testing.T, store ISiteStore) { + siteCfg := &SiteConfig{ + ClientCfg: &ClientConfig{ + SiteName: "quickshare", + SiteDesc: "simpel file sharing", + Bg: &BgConfig{ + Url: "/imgs/bg.jpg", + Repeat: "no-repeat", + Position: "fixed", + Align: "center", + }, + }, + } + + err := store.SetClientCfg(siteCfg.ClientCfg) + if err != nil { + t.Fatal(err) + } + newSiteCfg, err := store.GetCfg() + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(newSiteCfg, siteCfg) { + t.Fatalf("not equal new(%v) original(%v)", newSiteCfg, siteCfg) + } + } + + t.Run("Get/Set", func(t *testing.T) { + rootPath, err := ioutil.TempDir("./", "quickshare_sitestore_test_") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(rootPath) + + kvstore := boltdbpvd.New(rootPath, 1024) + defer kvstore.Close() + + store, err := NewSiteStore(kvstore) + if err != nil { + t.Fatal("fail to new kvstore", err) + } + + testSiteMethods(t, store) + }) +} diff --git a/src/depidx/deps.go b/src/depidx/deps.go index 661a893..09dfd4c 100644 --- a/src/depidx/deps.go +++ b/src/depidx/deps.go @@ -6,11 +6,12 @@ import ( "github.com/ihexxa/quickshare/src/cryptoutil" "github.com/ihexxa/quickshare/src/db/fileinfostore" + "github.com/ihexxa/quickshare/src/db/sitestore" + "github.com/ihexxa/quickshare/src/db/userstore" "github.com/ihexxa/quickshare/src/fs" "github.com/ihexxa/quickshare/src/idgen" "github.com/ihexxa/quickshare/src/iolimiter" "github.com/ihexxa/quickshare/src/kvstore" - "github.com/ihexxa/quickshare/src/db/userstore" "github.com/ihexxa/quickshare/src/worker" ) @@ -28,6 +29,7 @@ type Deps struct { kv kvstore.IKVStore users userstore.IUserStore fileInfos fileinfostore.IFileInfoStore + siteStore sitestore.ISiteStore id idgen.IIDGen logger *zap.SugaredLogger limiter iolimiter.ILimiter @@ -94,6 +96,14 @@ func (deps *Deps) SetFileInfos(fileInfos fileinfostore.IFileInfoStore) { deps.fileInfos = fileInfos } +func (deps *Deps) SiteStore() sitestore.ISiteStore { + return deps.siteStore +} + +func (deps *Deps) SetSiteStore(siteStore sitestore.ISiteStore) { + deps.siteStore = siteStore +} + func (deps *Deps) Limiter() iolimiter.ILimiter { return deps.limiter } diff --git a/src/handlers/settings/handler.go b/src/handlers/settings/handler.go deleted file mode 100644 index 0ec249f..0000000 --- a/src/handlers/settings/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package settings - -import ( - "github.com/gin-gonic/gin" - "github.com/ihexxa/gocfg" - - "github.com/ihexxa/quickshare/src/depidx" - q "github.com/ihexxa/quickshare/src/handlers" -) - -type SettingsSvc struct { - cfg gocfg.ICfg - deps *depidx.Deps -} - -func NewSettingsSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*SettingsSvc, error) { - return &SettingsSvc{ - cfg: cfg, - deps: deps, - }, nil -} - -func (h *SettingsSvc) Health(c *gin.Context) { - // TODO: currently it checks nothing - c.JSON(q.Resp(200)) -} diff --git a/src/handlers/settings/handlers.go b/src/handlers/settings/handlers.go new file mode 100644 index 0000000..786c3e2 --- /dev/null +++ b/src/handlers/settings/handlers.go @@ -0,0 +1,71 @@ +package settings + +import ( + "errors" + + "github.com/gin-gonic/gin" + "github.com/ihexxa/gocfg" + + "github.com/ihexxa/quickshare/src/db/sitestore" + "github.com/ihexxa/quickshare/src/depidx" + q "github.com/ihexxa/quickshare/src/handlers" +) + +type SettingsSvc struct { + cfg gocfg.ICfg + deps *depidx.Deps +} + +func NewSettingsSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*SettingsSvc, error) { + return &SettingsSvc{ + cfg: cfg, + deps: deps, + }, nil +} + +func (h *SettingsSvc) Health(c *gin.Context) { + // TODO: currently it checks nothing + c.JSON(q.Resp(200)) +} + +type ClientCfgMsg struct { + ClientCfg *sitestore.ClientConfig +} + +func (h *SettingsSvc) GetClientCfg(c *gin.Context) { + // TODO: add cache + siteCfg, err := h.deps.SiteStore().GetCfg() + if err != nil { + c.JSON(q.ErrResp(c, 500, err)) + return + } + + c.JSON(200, &ClientCfgMsg{ClientCfg: siteCfg.ClientCfg}) +} + +func (h *SettingsSvc) SetClientCfg(c *gin.Context) { + var err error + req := &ClientCfgMsg{} + if err = c.ShouldBindJSON(&req); err != nil { + c.JSON(q.ErrResp(c, 400, err)) + return + } + if err = validateClientCfg(req.ClientCfg); err != nil { + c.JSON(q.ErrResp(c, 400, err)) + return + } + + err = h.deps.SiteStore().SetClientCfg(req.ClientCfg) + if err != nil { + c.JSON(q.ErrResp(c, 500, err)) + return + } + c.JSON(q.Resp(200)) +} + +func validateClientCfg(cfg *sitestore.ClientConfig) error { + if cfg.SiteName == "" { + return errors.New("site name is empty") + } + return nil +} diff --git a/src/server/server.go b/src/server/server.go index 882b681..3883e7e 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -24,6 +24,7 @@ import ( "github.com/ihexxa/quickshare/src/cryptoutil/jwt" "github.com/ihexxa/quickshare/src/db/fileinfostore" + "github.com/ihexxa/quickshare/src/db/sitestore" "github.com/ihexxa/quickshare/src/db/userstore" "github.com/ihexxa/quickshare/src/depidx" "github.com/ihexxa/quickshare/src/fs" @@ -124,6 +125,10 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps { if err != nil { panic(fmt.Sprintf("fail to init file info store: %s", err)) } + siteStore, err := sitestore.NewSiteStore(kv) + if err != nil { + panic(fmt.Sprintf("fail to init site config store: %s", err)) + } limiterCap := cfg.IntOr("Users.LimiterCapacity", 10000) limiterCyc := cfg.IntOr("Users.LimiterCyc", 1000) @@ -135,6 +140,7 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps { deps.SetKV(kv) deps.SetUsers(users) deps.SetFileInfos(fileInfos) + deps.SetSiteStore(siteStore) deps.SetID(ider) deps.SetLog(logger) deps.SetLimiter(limiter)