From 4de286d8f73cbaf7f409c3f73c1fcabcd4075869 Mon Sep 17 00:00:00 2001 From: leo Date: Thu, 18 Nov 2021 16:27:28 +0800 Subject: [PATCH] add new ledger api --- config.go | 22 --------- config/config.json | 2 +- script/config.go | 108 +++++++++++++++++++++++++++++++++++++++++++++ script/file.go | 31 ++++++++++--- script/log.go | 4 +- script/paths.go | 15 +++++++ server.go | 51 +++++++++++++-------- service/error.go | 26 +++++++++++ service/ledger.go | 102 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 311 insertions(+), 50 deletions(-) delete mode 100644 config.go create mode 100644 script/config.go create mode 100644 script/paths.go create mode 100644 service/error.go create mode 100644 service/ledger.go diff --git a/config.go b/config.go deleted file mode 100644 index 5a0b3a5..0000000 --- a/config.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "encoding/json" - "github.com/beancount-gs/script" -) - -type Config struct { - Title string `json:"title"` - DataPath string `json:"dataPath"` - OperatingCurrency string `json:"operatingCurrency"` - StartDate string `json:"startDate"` - IsBak bool `json:"isBak"` -} - -func LoadConfig(globalConfig Config) Config { - err := json.Unmarshal(script.ReadFile("./config/config.json"), &globalConfig) - if err != nil { - script.LogError("config file (/config/config.json) unmarshall failed") - } - return globalConfig -} diff --git a/config/config.json b/config/config.json index 84f8779..1afa7ad 100644 --- a/config/config.json +++ b/config/config.json @@ -1,6 +1,6 @@ { "title": "我的账本", - "dataPath": "/beancount", + "dataPath": "D:\\beancount", "operatingCurrency": "CNY", "startDate": "1970-01-01", "isBak": true diff --git a/script/config.go b/script/config.go new file mode 100644 index 0000000..5cd6202 --- /dev/null +++ b/script/config.go @@ -0,0 +1,108 @@ +package script + +import ( + "encoding/json" +) + +var serverConfig Config +var ledgerConfigMap ConfigMap +var whiteList []string + +type Config struct { + Id string `json:"id"` + Mail string `json:"mail"` + Title string `json:"title"` + DataPath string `json:"dataPath"` + OperatingCurrency string `json:"operatingCurrency"` + StartDate string `json:"startDate"` + IsBak bool `json:"isBak"` +} + +type ConfigMap map[string]Config + +func GetServerConfig() Config { + return serverConfig +} + +func GetLedgerConfigMap() map[string]Config { + return ledgerConfigMap +} + +func GetLedgerConfig(ledgerId string) Config { + return ledgerConfigMap[ledgerId] +} + +func GetLedgerConfigByMail(mail string) *Config { + for _, v := range ledgerConfigMap { + if v.Mail == mail { + return &v + } + } + return nil +} + +func IsInWhiteList(ledgerId string) bool { + // ledger white list is empty, return true + if whiteList == nil || len(whiteList) == 0 { + return true + } + for i := range whiteList { + if whiteList[i] == ledgerId { + return true + } + } + return false +} + +func LoadServerConfig() error { + fileContent, err := ReadFile("./config/config.json") + if err != nil { + return err + } + err = json.Unmarshal(fileContent, &serverConfig) + if err != nil { + LogError("Failed unmarshall config file (/config/config.json)") + return err + } + LogInfo("Success load config file (/config/config.json)") + // load white list + fileContent, err = ReadFile("./config/white_list.json") + if err != nil { + return err + } + err = json.Unmarshal(fileContent, &whiteList) + if err != nil { + LogError("Failed unmarshal whitelist file (/config/white_list.json)") + return err + } + LogInfo("Success load whitelist file (/config/white_list.json)") + return nil +} + +func LoadLedgerConfigMap() error { + path := GetServerLedgerConfigFilePath() + fileContent, err := ReadFile(path) + if err != nil { + return err + } + err = json.Unmarshal(fileContent, &ledgerConfigMap) + if err != nil { + LogError("Failed unmarshal config file (" + path + ")") + return err + } + LogInfo("Success load ledger_config file (" + path + ")") + return nil +} + +func WriteLedgerConfigMap(newLedgerConfigMap ConfigMap) error { + path := GetServerLedgerConfigFilePath() + mapBytes, err := json.Marshal(ledgerConfigMap) + if err != nil { + LogError("Failed marshal ConfigMap") + return err + } + err = WriteFile(path, string(mapBytes)) + ledgerConfigMap = newLedgerConfigMap + LogInfo("Success write ledger_config file (" + path + ")") + return err +} diff --git a/script/file.go b/script/file.go index c2b2e23..42f4980 100644 --- a/script/file.go +++ b/script/file.go @@ -16,26 +16,43 @@ func FileIfExist(filePath string) bool { return true } -func ReadFile(filePath string) []byte { +func ReadFile(filePath string) ([]byte, error) { content, err := ioutil.ReadFile(filePath) if nil != err { - LogError(filePath + " read failed") + LogError("Failed to read file (" + filePath + ")") + return content, err } - return content + LogInfo("Success read file (" + filePath + ")") + return content, nil } -func CreateFile(filePath string) { +func WriteFile(filePath string, content string) error { + err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm) + if err != nil { + LogError("Failed to write file (" + filePath + ")") + return err + } + LogInfo("Success write file (" + filePath + ")") + return nil +} + +func CreateFile(filePath string) error { f, err := os.Create(filePath) if nil != err { LogError(filePath + " create failed") - return + return err } defer f.Close() + LogInfo("Success create file " + filePath) + return nil } -func MkDir(dirPath string) { +func MkDir(dirPath string) error { err := os.MkdirAll(dirPath, os.ModePerm) if nil != err { - LogError(dirPath + " mkdir failed") + LogError("Failed mkdir " + dirPath) + return err } + LogInfo("Success mkdir " + dirPath) + return nil } diff --git a/script/log.go b/script/log.go index 39deb49..bb3a6c3 100644 --- a/script/log.go +++ b/script/log.go @@ -6,9 +6,9 @@ import ( ) func LogInfo(message string) { - fmt.Printf("[Info] %s System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) + fmt.Printf("[Info] [%s] System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) } func LogError(message string) { - fmt.Printf("[Error] %s System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) + fmt.Printf("[Error] [%s] System: %s\n", time.Now().Format("2006-01-02 15:04:05"), message) } diff --git a/script/paths.go b/script/paths.go new file mode 100644 index 0000000..2323191 --- /dev/null +++ b/script/paths.go @@ -0,0 +1,15 @@ +package script + +import "os" + +func GetServerLedgerConfigFilePath() string { + return GetServerConfig().DataPath + "/ledger_config.json" +} + +func GetExampleLedgerConfigDirPath() string { + currentPath, err := os.Getwd() + if err != nil { + return "" + } + return currentPath + "/example" +} diff --git a/server.go b/server.go index f5fdce1..3d9679c 100644 --- a/server.go +++ b/server.go @@ -2,40 +2,55 @@ package main import ( "github.com/beancount-gs/script" + "github.com/beancount-gs/service" "github.com/gin-gonic/gin" "net/http" ) -var GlobalConfig Config - -func InitLedgerFiles() { +func InitServerFiles() error { + dataPath := script.GetServerConfig().DataPath // 账本目录不存在,则创建 - if !script.FileIfExist(GlobalConfig.DataPath) { - script.MkDir(GlobalConfig.DataPath) - script.LogInfo("Success mkdir " + GlobalConfig.DataPath) + if !script.FileIfExist(dataPath) { + return script.MkDir(dataPath) } + return nil } -func LoadLedgerCache() { - +func LoadServerCache() error { + return script.LoadLedgerConfigMap() } -func RegisterRoute(route *gin.Engine) { - route.StaticFS("/", http.Dir("./public")) +func RegisterRouter(router *gin.Engine) { + router.StaticFS("/", http.Dir("./public")) + router.POST("/api/ledger", service.OpenOrCreateLedger) } func main() { - // 默认端口号 - var port = ":3001" // 读取配置文件 - GlobalConfig = LoadConfig(GlobalConfig) + err := script.LoadServerConfig() + if err != nil { + script.LogError("Failed to load server config, " + err.Error()) + return + } // 初始化账本文件结构 - InitLedgerFiles() + err = InitServerFiles() + if err != nil { + script.LogError("Failed to init server files, " + err.Error()) + return + } // 加载缓存 - LoadLedgerCache() - route := gin.Default() + err = LoadServerCache() + if err != nil { + script.LogError("Failed to load server cache, " + err.Error()) + return + } + router := gin.Default() // 注册路由 - RegisterRoute(route) + RegisterRouter(router) // 启动服务 - _ = http.ListenAndServe(port, nil) + var port = ":3001" + err = router.Run(port) + if err != nil { + script.LogError("Failed to start server, " + err.Error()) + } } diff --git a/service/error.go b/service/error.go new file mode 100644 index 0000000..cf14681 --- /dev/null +++ b/service/error.go @@ -0,0 +1,26 @@ +package service + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +func OK(c *gin.Context, data string) { + c.JSON(http.StatusOK, gin.H{"code": 200, "message": "ok", "data": data}) +} + +func BadRequest(c *gin.Context, message string) { + c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": message}) +} + +func InternalError(c *gin.Context, message string) { + c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": message}) +} + +func LedgerIsNotExist(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"code": 1006, "message": "ledger is not exist"}) +} + +func LedgerIsNotAllowAccess(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"code": 1006, "message": "ledger is not allow access"}) +} diff --git a/service/ledger.go b/service/ledger.go new file mode 100644 index 0000000..286966d --- /dev/null +++ b/service/ledger.go @@ -0,0 +1,102 @@ +package service + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "github.com/beancount-gs/script" + "github.com/gin-gonic/gin" + "io" + "io/ioutil" + "strings" +) + +type LoginForm struct { + Mail string `form:"mail" binding:"required"` + Secret string `form:"secret" binding:"required"` +} + +func OpenOrCreateLedger(c *gin.Context) { + var loginForm LoginForm + if err := c.ShouldBindJSON(&loginForm); err != nil { + BadRequest(c, err.Error()) + return + } + // is mail exist white list + if !script.IsInWhiteList(loginForm.Mail) { + LedgerIsNotExist(c) + return + } + + t := sha1.New() + _, err := io.WriteString(t, loginForm.Mail+loginForm.Secret) + if err != nil { + LedgerIsNotAllowAccess(c) + return + } + + ledgerId := hex.EncodeToString(t.Sum(nil)) + fmt.Println(ledgerId) + userLedger := script.GetLedgerConfigByMail(loginForm.Mail) + if userLedger != nil { + if ledgerId != userLedger.Id { + LedgerIsNotAllowAccess(c) + return + } + } + // create new ledger + serverConfig := script.GetServerConfig() + ledgerConfigMap := script.GetLedgerConfigMap() + ledgerConfig := script.Config{ + Id: ledgerId, + Mail: loginForm.Mail, + Title: serverConfig.Title, + DataPath: serverConfig.DataPath + "/" + ledgerId, + OperatingCurrency: serverConfig.OperatingCurrency, + StartDate: serverConfig.StartDate, + IsBak: serverConfig.IsBak, + } + // init ledger files + err = initLedgerFiles(script.GetExampleLedgerConfigDirPath(), ledgerConfig.DataPath, ledgerConfig.StartDate) + if err != nil { + InternalError(c, err.Error()) + return + } + // add ledger config to ledger_config.json + ledgerConfigMap[ledgerId] = ledgerConfig + err = script.WriteLedgerConfigMap(ledgerConfigMap) + if err != nil { + InternalError(c, err.Error()) + return + } + OK(c, ledgerId) +} + +func initLedgerFiles(sourceFilePath string, targetFilePath string, startDate string) error { + return copyFile(sourceFilePath, targetFilePath, startDate) +} + +func copyFile(sourceFilePath string, targetFilePath string, startDate string) error { + rd, err := ioutil.ReadDir(sourceFilePath) + if err != nil { + return err + } + for _, fi := range rd { + newSourceFilePath := sourceFilePath + "/" + fi.Name() + newTargetFilePath := targetFilePath + "/" + fi.Name() + if fi.IsDir() { + err = script.MkDir(newTargetFilePath) + err = copyFile(newSourceFilePath, newTargetFilePath, startDate) + } else { + fileContent, err := script.ReadFile(newSourceFilePath) + if err != nil { + return err + } + err = script.WriteFile(newTargetFilePath, strings.Replace(string(fileContent), "%startDate%", startDate, -1)) + } + if err != nil { + return err + } + } + return nil +}