diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | conf/conf.go | 6 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 3 | ||||
-rw-r--r-- | main.go | 7 | ||||
-rw-r--r-- | openbills.toml | 3 | ||||
-rw-r--r-- | user/controller.go | 92 | ||||
-rw-r--r-- | user/router.go | 4 | ||||
-rw-r--r-- | user/service.go | 21 | ||||
-rw-r--r-- | user/user.go | 22 |
10 files changed, 143 insertions, 18 deletions
@@ -1,2 +1,3 @@ /openbills .env +/data diff --git a/conf/conf.go b/conf/conf.go index 7531e4f..843bbea 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -1,5 +1,5 @@ /* openbills - Server for web based Libre Billing Software - * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * Copyright (C) 2023-2024 Vidhu Kant Sharma <vidhukant@vidhukant.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,8 @@ import ( "log" ) +// TODO: check if openbills has read/write access to uploads dir +// TODO: data.upload_dir must have a trailing / func validateConf() { ok := true log.Println("\x1b[46m\x1b[30m[info]\x1b[0m Checking errors in config file...") @@ -99,6 +101,8 @@ func init() { viper.SetDefault("cryptography.password_hashing_cost", bcrypt.DefaultCost) + viper.SetDefault("data.upload_dir", "./data/") + validateConf() log.Printf("\x1b[46m\x1b[30m[info]\x1b[0m Loaded Config \"%s\"\n", viper.ConfigFileUsed()) } @@ -4,8 +4,8 @@ go 1.21.0 require ( github.com/gin-gonic/gin v1.9.1 - github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/google/uuid v1.1.2 github.com/spf13/viper v1.16.0 golang.org/x/crypto v0.9.0 gorm.io/driver/mysql v1.5.1 @@ -86,8 +86,6 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -145,6 +143,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -1,5 +1,5 @@ /* openbills - Server for web based Libre Billing Software - * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * Copyright (C) 2023-2024 Vidhu Kant Sharma <vidhukant@vidhukant.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,7 +38,7 @@ import ( "log" ) -const OPENBILLS_VERSION = "v0.9.0" +const OPENBILLS_VERSION = "v0.10.0" func init() { if !viper.GetBool("debug_mode") { @@ -49,6 +49,9 @@ func init() { func main() { r := gin.New() r.SetTrustedProxies([]string{"127.0.0.1"}) + r.MaxMultipartMemory = 4 << 20 + + r.Static("/pub/", viper.GetString("data.upload_dir")) r.GET("/info", serverInfo) diff --git a/openbills.toml b/openbills.toml index f10f666..0742aaf 100644 --- a/openbills.toml +++ b/openbills.toml @@ -26,3 +26,6 @@ max_username_length = 32 password_hashing_cost = 14 auth_key = "22ELiOfHn19s0z1WWgsOT9RupghRYrXm" refresh_key = "22ELjsdlfkjalsdfjalsdjflajsdfljaiOfHn19s0z1WWgsOT9RupghRYrXm" + +[data] +upload_dir = "./data/" diff --git a/user/controller.go b/user/controller.go index 15061cc..1dc85da 100644 --- a/user/controller.go +++ b/user/controller.go @@ -20,6 +20,8 @@ package user import ( e "vidhukant.com/openbills/errors" "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/spf13/viper" "net/http" ) @@ -51,6 +53,96 @@ func handleGetUser (ctx *gin.Context) { }) } +func handleUploadLogo(ctx *gin.Context) { + var user User + + uId, ok := ctx.Get("UserID") + if !ok { + ctx.Error(e.ErrUnauthorized) + ctx.Abort() + return + } + + userId := uId.(uint) + user.ID = userId + + // TODO: handle potential errors + file, err := ctx.FormFile("logo") + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + dest := uuid.New().String() + + // TODO: handle potential errors + err = ctx.SaveUploadedFile(file, viper.GetString("data.upload_dir") + dest) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + // TODO: delete old file (if any) + err = user.update(map[string]interface{}{"logo_file": dest}) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + }) +} + +func handleUploadSignature(ctx *gin.Context) { + var user User + + uId, ok := ctx.Get("UserID") + if !ok { + ctx.Error(e.ErrUnauthorized) + ctx.Abort() + return + } + + userId := uId.(uint) + user.ID = userId + + // TODO: handle potential errors + file, err := ctx.FormFile("signature") + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + dest := uuid.New().String() + + // TODO: handle potential errors + err = ctx.SaveUploadedFile(file, viper.GetString("data.upload_dir") + dest) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + // TODO: delete old file (if any) + err = user.update(map[string]interface{}{"signature_file": dest}) + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + }) +} + +// TODO: fix this stuff +// also add some kind of 2 factor verification func handleDelUser (ctx *gin.Context) { id := uint(1) // get from JWT diff --git a/user/router.go b/user/router.go index 8a9ad86..d9fa7e0 100644 --- a/user/router.go +++ b/user/router.go @@ -1,5 +1,5 @@ /* openbills - Server for web based Libre Billing Software - * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * Copyright (C) 2023-2024 Vidhu Kant Sharma <vidhukant@vidhukant.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,5 +25,7 @@ func Routes(route *gin.RouterGroup) { g := route.Group("/user") { g.GET("/", handleGetUser) + g.POST("/logo", handleUploadLogo) + g.POST("/signature", handleUploadSignature) } } diff --git a/user/service.go b/user/service.go index 4544cb4..222df4a 100644 --- a/user/service.go +++ b/user/service.go @@ -1,5 +1,5 @@ /* openbills - Server for web based Libre Billing Software - * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * Copyright (C) 2023-2024 Vidhu Kant Sharma <vidhukant@vidhukant.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -75,3 +75,22 @@ func (u *User) del() error { return nil } + +func (u *User) update(changes map[string]interface{}) error { + res := db.Model(&u). + Omit("email"). + Omit("password"). + Omit("username"). + Updates(changes) + + // TODO: handle potential errors + if res.Error != nil { + return res.Error + } + + if res.RowsAffected == 0 { + return e.ErrNotFound + } + + return nil +} diff --git a/user/user.go b/user/user.go index 726c5c2..b130ab9 100644 --- a/user/user.go +++ b/user/user.go @@ -40,17 +40,19 @@ func init() { type User struct { gorm.Model u.Address - FullName string - FirmName string - Gstin string - Phone string - Email string - Website string - Username string - Password string + FullName string + FirmName string + Gstin string + Phone string + Email string + Website string + Username string + Password string + LogoFile string + SignatureFile string // will be printed with address on the invoice - Details string - IsVerified bool + Details string + IsVerified bool // a note is printed on every invoice. // This is the default that gets automatically set DefaultInvoiceNote string |