37 Commits

Author SHA1 Message Date
Adrian Zürcher
8400f29f7e update dbhandler 2025-11-30 21:35:39 +01:00
Adrian Zürcher
4d86d9a8a0 update dbhandler 2025-11-30 21:29:42 +01:00
Adrian Zürcher
c94f47060d add new database creation indicator 2025-11-30 21:11:58 +01:00
Adrian Zürcher
5832df28c9 add missing decrypt of member before deleting attendance 2025-11-28 16:48:08 +01:00
Adrian Zürcher
55653d996b fix random name changes, only once decrypt each member 2025-11-28 08:21:45 +01:00
Adrian Zürcher
b0895aee01 add new relational table requests 2025-11-27 07:58:43 +01:00
Adrian Zürcher
f28a3edbcf change function and gorm tag 2025-11-20 18:00:12 +01:00
Adrian Zürcher
1d203d1d2f add json tag to model 2025-11-20 17:49:08 +01:00
Adrian Zürcher
2f539c254b minor changes 2025-11-20 17:47:41 +01:00
Adrian Zürcher
8e8a6d58aa fix add Responsible 2025-11-20 16:09:56 +01:00
Adrian Zürcher
3b804ff8d1 add new decrypt encrypt function to member 2025-11-20 15:17:44 +01:00
Adrian Zürcher
11b9c61113 new model 2025-11-20 10:43:49 +01:00
Adrian Zürcher
fe194754b1 change table to references 2025-11-20 10:40:56 +01:00
Adrian Zürcher
4a469d1fd4 add new group table to database 2025-11-13 16:20:04 +01:00
Adrian Zürcher
dd5d7afdff remove required birthday 2025-11-10 18:02:46 +01:00
Adrian Zürcher
a02a7e1646 add empty string check to get member 2025-11-10 16:35:47 +01:00
Adrian Zürcher
b85caeadbd add new column comment to member table 2025-11-10 15:59:16 +01:00
Adrian Zürcher
b3e58805fc fix find existing if more than one record 2025-11-07 08:29:17 +01:00
Adrian Zürcher
2223b96432 fix delete function 2025-11-07 08:18:58 +01:00
Adrian Zürcher
21388b63fb fix exist check 2025-11-07 08:09:10 +01:00
Adrian Zürcher
86a4ef798a fix json error 2025-11-06 16:47:31 +01:00
Adrian Zürcher
442afcb44c change responsible person to struct 2025-11-06 16:30:37 +01:00
Adrian Zürcher
8b8d99e162 new set id to 0 to create new uniq id by gorm 2025-11-06 15:22:23 +01:00
Adrian Zürcher
103a07b7ac extend add responsible from single to array 2025-11-06 15:10:28 +01:00
Adrian Zürcher
a20a21d251 change start events to return event as data 2025-11-06 09:03:56 +01:00
Adrian Zürcher
ee7ce526f3 change new event from get to post 2025-11-06 08:56:00 +01:00
Adrian Zürcher
a938f41e94 add new function update event 2025-11-05 11:18:30 +01:00
Adrian Zürcher
1d09705adb fix wrong api call 2025-11-04 09:39:11 +01:00
Adrian Zürcher
4ee558b340 fix wrong api call 2025-11-04 09:38:35 +01:00
Adrian Zürcher
ad44a65087 update member from one to several ids 2025-11-04 09:35:52 +01:00
Adrian Zürcher
e3870de7d5 fix wrong mix of new current ids 2025-11-04 08:24:27 +01:00
Adrian Zürcher
25f965cd72 fix wrong event id 2025-11-04 08:20:15 +01:00
Adrian Zürcher
25f744b26c change error output of startnewevent 2025-11-01 23:22:46 +01:00
Adrian Zürcher
ec77c8a488 add exists check to events and fix wrong event name 2025-11-01 23:17:58 +01:00
Adrian Zürcher
80675ed328 implement dbhandler package and new test file 2025-10-31 08:11:07 +01:00
Adrian Zürcher
1568ee2482 add gitignore 2025-10-31 08:10:28 +01:00
Adrian Zürcher
6e3ff95e8c fix last comma error 2025-10-17 15:54:16 +02:00
23 changed files with 1111 additions and 781 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.db
*.log

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"time" "time"
"gitea.tecamino.com/paadi/tecamino-logger/logging"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -14,7 +15,7 @@ type API struct {
router *gin.Engine router *gin.Engine
} }
func NewAPI(host string, port int) *API { func NewAPI(host string, port int, logger *logging.Logger) (*API, error) {
r := gin.Default() r := gin.Default()
r.Use(cors.New(cors.Config{ r.Use(cors.New(cors.Config{
//AllowOrigins: []string{"http://localhost:9000"}, // frontend origin //AllowOrigins: []string{"http://localhost:9000"}, // frontend origin
@@ -26,18 +27,21 @@ func NewAPI(host string, port int) *API {
MaxAge: 12 * time.Hour, MaxAge: 12 * time.Hour,
})) }))
apiHandler := NewAPIHandler() apiHandler, err := NewAPIHandler(logger)
if err != nil {
return nil, err
}
v1 := r.Group("v1") v1 := r.Group("v1")
v1.GET("/events", apiHandler.GetEventById) v1.GET("/events", apiHandler.GetEvent)
v1.GET("/events/new", apiHandler.StartNewEvent) v1.GET("/events/new", apiHandler.StartNewEvent)
v1.GET("/events/delete", apiHandler.DeleteEvent) v1.GET("/events/delete", apiHandler.DeleteEvent)
v1.GET("/members", apiHandler.GetMemberById) v1.GET("/members", apiHandler.GetMember)
v1.GET("/responsible", apiHandler.GetResponsibleById) v1.GET("/responsible", apiHandler.GetResponsible)
v1.POST("/database/open", apiHandler.OpenDatabase) v1.POST("/database/open", apiHandler.OpenDatabase)
v1.POST("/members/add", apiHandler.AddNewMember) v1.POST("/members/add", apiHandler.AddNewMember)
v1.POST("/members/edit", apiHandler.EditMember) v1.POST("/members/edit", apiHandler.UpdateMember)
v1.POST("/members/delete", apiHandler.DeleteMember) v1.POST("/members/delete", apiHandler.DeleteMember)
v1.POST("/members/import/csv", apiHandler.ImportCSV) v1.POST("/members/import/csv", apiHandler.ImportCSV)
@@ -50,7 +54,7 @@ func NewAPI(host string, port int) *API {
return &API{host: host, return &API{host: host,
port: port, port: port,
router: r, router: r,
} }, nil
} }
func (api *API) Run() error { func (api *API) Run() error {

View File

@@ -1,20 +1,38 @@
package api package api
import ( import (
"fmt"
"net/http" "net/http"
"os"
"path/filepath"
"gitea.tecamino.com/paadi/memberDB/handlers" "gitea.tecamino.com/paadi/memberDB/handlers"
"gitea.tecamino.com/paadi/memberDB/models" "gitea.tecamino.com/paadi/memberDB/models"
"gitea.tecamino.com/paadi/tecamino-logger/logging"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type APIHandler struct { type APIHandler struct {
DbHandler *handlers.DatabaseHandler DbHandler *handlers.DatabaseHandler
//dataHandler *dbHandler.DBHandler
logger *logging.Logger
} }
func NewAPIHandler() *APIHandler { func NewAPIHandler(logger *logging.Logger) (aH *APIHandler, err error) {
return &APIHandler{} if logger == nil {
logger, err = logging.NewLogger("memberDb.log", logging.DefaultConfig())
if err != nil {
return nil, err
}
}
logger.Debug("NewAPIHandler", "initialize new api handler")
aH = &APIHandler{logger: logger}
return
}
func (a *APIHandler) DBHandlerIsInitialized() bool {
return a.DbHandler != nil
} }
func (a *APIHandler) OpenDatabase(c *gin.Context) { func (a *APIHandler) OpenDatabase(c *gin.Context) {
@@ -28,63 +46,64 @@ func (a *APIHandler) OpenDatabase(c *gin.Context) {
} }
if database.Path == "" { if database.Path == "" {
a.logger.Debug("OpenDatabase", "set default database path")
database.SetDefaultPath() database.SetDefaultPath()
} }
if database.Token == "" { if database.Token == "" {
a.logger.Debug("OpenDatabase", "set default token")
database.SetDefaultToken() database.SetDefaultToken()
} }
a.DbHandler, err = handlers.NewDatabaseHandler(database.Path, database.Create) if _, err := os.Stat(database.Path); err != nil && !database.Create {
a.logger.Error("OpenDatabase", fmt.Sprintf("%s not found", database.Path))
c.JSON(http.StatusBadRequest, gin.H{
"message": fmt.Sprintf("%s not found", database.Path),
})
return
}
dbName := filepath.Base(database.Path)
folderpath := filepath.Dir(database.Path)
a.DbHandler, err = handlers.NewDatabaseHandler(dbName, folderpath, a.logger)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("OpenDatabase", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
if err := a.DbHandler.CreateNewMemberTable(); err != nil { if err := a.DbHandler.SetTimezone(os.Getenv("TIMEZONE")); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("OpenDatabase", err)
"message": err.Error(), }
})
if err := a.DbHandler.AddNewTable(&models.Member{}); err != nil {
a.logger.Error("OpenDatabase", err)
c.JSON(http.StatusInternalServerError, nil)
return return
} }
if err := a.DbHandler.CreateNewEventTable(); err != nil { if err := a.DbHandler.AddNewTable(&models.Event{}); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("OpenDatabase", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
if err := a.DbHandler.CreateNewResponsibleTable(); err != nil { if err := a.DbHandler.AddNewTable(&models.Responsible{}); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("OpenDatabase", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
}) return
}
if err := a.DbHandler.AddNewTable(&models.Group{}); err != nil {
a.logger.Error("OpenDatabase", err)
c.JSON(http.StatusInternalServerError, nil)
return return
} }
a.DbHandler.SetToken(database.Token) a.DbHandler.SetToken(database.Token)
_, err = a.DbHandler.GetMember(0)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "database opened", "message": "database opened",
}) })
} }
// databaseOpened is a helper function to first check wheter database is open for requests
func (a *APIHandler) databaseOpened(c *gin.Context) bool {
if a.DbHandler == nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "no database opened",
})
return false
}
return true
}

View File

@@ -9,32 +9,36 @@ import (
) )
func (a *APIHandler) StartNewEvent(c *gin.Context) { func (a *APIHandler) StartNewEvent(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("StartNewEvent", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
name := c.Query("name") var event models.Event
if name == "" { err := c.BindJSON(&event)
c.JSON(http.StatusBadRequest, gin.H{ if err != nil {
"message": "missing query 'name'", a.logger.Error("StartNewEvent", err)
}) c.JSON(http.StatusInternalServerError, nil)
return return
} }
if err := a.DbHandler.StartNewEvent(name); err != nil { if err := a.DbHandler.StartNewEvent(&event); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("StartNewEvent", err)
"message": err.Error(), c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
})
return return
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "New Event added " + name, "message": "New Event added " + event.Name,
"data": event,
}) })
} }
func (a *APIHandler) GetEventById(c *gin.Context) { func (a *APIHandler) GetEvent(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("GetEvent", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
@@ -45,43 +49,75 @@ func (a *APIHandler) GetEventById(c *gin.Context) {
if id != "" { if id != "" {
i, err = strconv.Atoi(id) i, err = strconv.Atoi(id)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("GetEvent", err)
"message": err.Error(),
}) c.JSON(http.StatusInternalServerError, nil)
return return
} }
} }
events, err := a.DbHandler.GetEvent(i) events, err := a.DbHandler.GetEvent(uint(i))
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("GetEvent", err)
"message": err.Error(), c.JSON(http.StatusBadRequest, nil)
})
return return
} }
c.JSON(http.StatusOK, events) c.JSON(http.StatusOK, events)
} }
func (a *APIHandler) DeleteEvent(c *gin.Context) { func (a *APIHandler) UpdateEvent(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("UpdateEvent", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var err error
var request struct { var events []models.Event
Ids []int `json:"ids"` err := c.BindJSON(&events)
}
err = c.BindJSON(&request)
if err != nil { if err != nil {
a.logger.Error("UpdateEvent", err)
c.JSON(http.StatusInternalServerError, nil)
return
}
for _, event := range events {
err = a.DbHandler.UpdateEvent(event.Id, event)
if err != nil {
a.logger.Error("UpdateEvent", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
} }
}
c.JSON(http.StatusOK, gin.H{
"message": "event(s) updated",
})
}
func (a *APIHandler) DeleteEvent(c *gin.Context) {
if !a.DBHandlerIsInitialized() {
a.logger.Error("DeleteEvent", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return
}
var err error
var request struct {
Ids []uint `json:"ids"`
}
err = c.BindJSON(&request)
if err != nil {
a.logger.Error("DeleteEvent", err)
c.JSON(http.StatusBadRequest, nil)
return
}
err = a.DbHandler.DeleteEvent(request.Ids...) err = a.DbHandler.DeleteEvent(request.Ids...)
if err != nil { if err != nil {
a.logger.Error("DeleteEvent", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
@@ -93,22 +129,25 @@ func (a *APIHandler) DeleteEvent(c *gin.Context) {
} }
func (a *APIHandler) AddNewAttendees(c *gin.Context) { func (a *APIHandler) AddNewAttendees(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("AddNewAttendees", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var event models.Event var event models.Event
err := c.BindJSON(&event) err := c.BindJSON(&event)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("AddNewAttendees", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
err = a.DbHandler.AddAttendeesToEvent(event) err = a.DbHandler.AddAttendeesToEvent(event)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("AddNewAttendees", err)
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
@@ -120,23 +159,26 @@ func (a *APIHandler) AddNewAttendees(c *gin.Context) {
} }
func (a *APIHandler) DeleteAttendee(c *gin.Context) { func (a *APIHandler) DeleteAttendee(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("DeleteAttendee", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var err error var err error
var event models.Event var event models.Event
err = c.BindJSON(&event) err = c.BindJSON(&event)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("DeleteAttendee", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
err = a.DbHandler.DeleteAttendeesFromEvent(event) err = a.DbHandler.DeleteAttendeesFromEvent(event)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("DeleteAttendee", err)
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return

128
api/groupHandler.go Normal file
View File

@@ -0,0 +1,128 @@
package api
import (
"net/http"
"strconv"
"gitea.tecamino.com/paadi/memberDB/models"
"github.com/gin-gonic/gin"
)
func (a *APIHandler) NewGroup(c *gin.Context) {
if !a.DBHandlerIsInitialized() {
a.logger.Error("StartNewEvent", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return
}
var group models.Group
err := c.BindJSON(&group)
if err != nil {
a.logger.Error("NewGroup", err)
c.JSON(http.StatusInternalServerError, nil)
return
}
if err := a.DbHandler.NewGroup(&group); err != nil {
a.logger.Error("NewGroup", err)
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "New group added " + group.Name,
})
}
func (a *APIHandler) GetGroup(c *gin.Context) {
if !a.DBHandlerIsInitialized() {
a.logger.Error("GetGroup", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return
}
var i int
var err error
id := c.Query("id")
if id != "" {
i, err = strconv.Atoi(id)
if err != nil {
a.logger.Error("GetGroup", err)
c.JSON(http.StatusInternalServerError, nil)
return
}
}
groups, err := a.DbHandler.GetGroup(uint(i))
if err != nil {
a.logger.Error("GetGroup", err)
c.JSON(http.StatusBadRequest, nil)
return
}
c.JSON(http.StatusOK, groups)
}
func (a *APIHandler) UpdateGroup(c *gin.Context) {
if !a.DBHandlerIsInitialized() {
a.logger.Error("UpdateGroup", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return
}
var groups []models.Group
err := c.BindJSON(&groups)
if err != nil {
a.logger.Error("UpdateGroup", err)
c.JSON(http.StatusInternalServerError, nil)
return
}
for _, event := range groups {
err = a.DbHandler.UpdateGroup(event.Id, event)
if err != nil {
a.logger.Error("UpdateGroup", err)
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
}
}
c.JSON(http.StatusOK, gin.H{
"message": "group(s) updated",
})
}
func (a *APIHandler) DeleteGroup(c *gin.Context) {
if !a.DBHandlerIsInitialized() {
a.logger.Error("DeleteGroup", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return
}
var err error
var request struct {
Ids []uint `json:"ids"`
}
err = c.BindJSON(&request)
if err != nil {
a.logger.Error("DeleteGroup", err)
c.JSON(http.StatusBadRequest, nil)
return
}
err = a.DbHandler.DeleteGroup(request.Ids...)
if err != nil {
a.logger.Error("DeleteGroup", err)
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "group deleted",
})
}

View File

@@ -16,16 +16,17 @@ import (
) )
func (a *APIHandler) AddNewMember(c *gin.Context) { func (a *APIHandler) AddNewMember(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("AddNewMember", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var member models.Member var member models.Member
err := c.BindJSON(&member) err := c.BindJSON(&member)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("AddNewMember", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
@@ -36,10 +37,9 @@ func (a *APIHandler) AddNewMember(c *gin.Context) {
if member.LastName == "" { if member.LastName == "" {
text += "lastName " text += "lastName "
} }
if member.Birthday == "" {
text += "birthday "
}
if text != "" { if text != "" {
a.logger.Error("AddNewMember", text+"can not be empty")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": text + "can not be empty", "message": text + "can not be empty",
}) })
@@ -48,6 +48,7 @@ func (a *APIHandler) AddNewMember(c *gin.Context) {
err = a.DbHandler.AddNewMember(member) err = a.DbHandler.AddNewMember(member)
if err != nil { if err != nil {
a.logger.Error("AddNewMember", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
@@ -59,8 +60,10 @@ func (a *APIHandler) AddNewMember(c *gin.Context) {
}) })
} }
func (a *APIHandler) GetMemberById(c *gin.Context) { func (a *APIHandler) GetMember(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("GetMember", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
@@ -71,15 +74,15 @@ func (a *APIHandler) GetMemberById(c *gin.Context) {
if id != "" { if id != "" {
i, err = strconv.Atoi(id) i, err = strconv.Atoi(id)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("GetMember", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
} }
members, err := a.DbHandler.GetMember(i) members, err := a.DbHandler.GetMember(uint(i))
if err != nil { if err != nil {
a.logger.Error("GetMember", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
@@ -88,88 +91,80 @@ func (a *APIHandler) GetMemberById(c *gin.Context) {
c.JSON(http.StatusOK, members) c.JSON(http.StatusOK, members)
} }
func (a *APIHandler) EditMember(c *gin.Context) { func (a *APIHandler) UpdateMember(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("EditMember", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var i int var members []models.Member
var err error err := c.BindJSON(&members)
id := c.Query("id")
if id != "" {
i, err = strconv.Atoi(id)
if err != nil { if err != nil {
a.logger.Error("EditMember", err)
c.JSON(http.StatusInternalServerError, nil)
return
}
for _, member := range members {
err = a.DbHandler.UpdateMember(member.Id, member)
if err != nil {
a.logger.Error("EditMember", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
} }
} else {
c.JSON(http.StatusBadRequest, gin.H{
"message": "query parameter 'id' missing",
})
return
}
var member models.Member
err = c.BindJSON(&member)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
}
err = a.DbHandler.UpdateMember(i, member)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "member updated", "message": "member(s) updated",
}) })
} }
func (a *APIHandler) DeleteMember(c *gin.Context) { func (a *APIHandler) DeleteMember(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("DeleteMember", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var err error var err error
var request struct { var request struct {
Ids []int `json:"ids"` Ids []uint `json:"ids"`
} }
err = c.BindJSON(&request) err = c.BindJSON(&request)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("DeleteMember", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
err = a.DbHandler.DeleteMember(request.Ids...) err = a.DbHandler.DeleteMember(request.Ids...)
if err != nil { if err != nil {
a.logger.Error("DeleteMember", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "member deleted", "message": "member(s) deleted",
}) })
} }
func (a *APIHandler) ImportCSV(c *gin.Context) { func (a *APIHandler) ImportCSV(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("ImportCSV", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var err error var err error
fileHeader, err := c.FormFile("file") fileHeader, err := c.FormFile("file")
if err != nil { if err != nil {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
@@ -188,7 +183,8 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
rowIndexI, err := strconv.Atoi(rowIndex) rowIndexI, err := strconv.Atoi(rowIndex)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("ImportCSV", err)
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
@@ -196,6 +192,7 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
file, err := fileHeader.Open() file, err := fileHeader.Open()
if err != nil { if err != nil {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open uploaded file"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open uploaded file"})
return return
} }
@@ -206,6 +203,7 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
buf := make([]byte, sniffSize) buf := make([]byte, sniffSize)
n, err := file.Read(buf) n, err := file.Read(buf)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return return
} }
@@ -214,12 +212,14 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
detector := chardet.NewTextDetector() detector := chardet.NewTextDetector()
result, err := detector.DetectBest(buf[:n]) result, err := detector.DetectBest(buf[:n])
if err != nil { if err != nil {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect encoding"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect encoding"})
return return
} }
_, err = file.Seek(0, io.SeekStart) _, err = file.Seek(0, io.SeekStart)
if err != nil { if err != nil {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to rewind file"}) c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to rewind file"})
return return
} }
@@ -234,6 +234,7 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
case "ISO-8859-1": case "ISO-8859-1":
reader = transform.NewReader(file, charmap.ISO8859_1.NewDecoder()) reader = transform.NewReader(file, charmap.ISO8859_1.NewDecoder())
default: default:
a.logger.Error("ImportCSV", "Unsupported encoding: "+result.Charset)
c.JSON(http.StatusBadRequest, gin.H{"message": "Unsupported encoding: " + result.Charset}) c.JSON(http.StatusBadRequest, gin.H{"message": "Unsupported encoding: " + result.Charset})
return return
} }
@@ -243,6 +244,7 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
records, err := csvReader.ReadAll() records, err := csvReader.ReadAll()
if err != nil { if err != nil {
a.logger.Error("ImportCSV", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse CSV"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse CSV"})
return return
} }
@@ -298,6 +300,7 @@ func (a *APIHandler) ImportCSV(c *gin.Context) {
} }
if message != "" { if message != "" {
a.logger.Error("ImportCSV", message)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": message, "message": message,
}) })

View File

@@ -10,20 +10,21 @@ import (
) )
func (a *APIHandler) AddNewResponsible(c *gin.Context) { func (a *APIHandler) AddNewResponsible(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("AddNewResponsible", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var responsible models.Person var responsibles []models.Responsible
err := c.BindJSON(&responsible) err := c.BindJSON(&responsibles)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("AddNewResponsible", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
err = a.DbHandler.AddNewResponsible(responsible) err = a.DbHandler.AddNewResponsible(responsibles...)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
@@ -32,15 +33,16 @@ func (a *APIHandler) AddNewResponsible(c *gin.Context) {
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "responsible added", "message": "responsible(s) added",
}) })
} }
func (a *APIHandler) GetResponsibleById(c *gin.Context) { func (a *APIHandler) GetResponsible(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("GetResponsible", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var i int var i int
var err error var err error
@@ -48,42 +50,44 @@ func (a *APIHandler) GetResponsibleById(c *gin.Context) {
if id != "" { if id != "" {
i, err = strconv.Atoi(id) i, err = strconv.Atoi(id)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("GetResponsible", err)
"message": err.Error(), c.JSON(http.StatusInternalServerError, nil)
})
return return
} }
} }
members, err := a.DbHandler.GetResponsible(i) responsibles, err := a.DbHandler.GetResponsible(uint(i))
if err != nil { if err != nil {
a.logger.Error("GetResponsible", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })
return return
} }
c.JSON(http.StatusOK, members) c.JSON(http.StatusOK, responsibles)
} }
func (a *APIHandler) DeleteResponsible(c *gin.Context) { func (a *APIHandler) DeleteResponsible(c *gin.Context) {
if !a.databaseOpened(c) { if !a.DBHandlerIsInitialized() {
a.logger.Error("DeleteResponsible", "database handler is not initialized")
c.JSON(http.StatusInternalServerError, nil)
return return
} }
var err error var err error
var request struct { var request struct {
Ids []int `json:"ids"` Ids []uint `json:"ids"`
} }
err = c.BindJSON(&request) err = c.BindJSON(&request)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{ a.logger.Error("DeleteResponsible", err)
"message": err.Error(), c.JSON(http.StatusBadRequest, nil)
})
return return
} }
err = a.DbHandler.DeleteResponsible(request.Ids...) err = a.DbHandler.DeleteResponsible(request.Ids...)
if err != nil { if err != nil {
a.logger.Error("DeleteResponsible", err)
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": err.Error(), "message": err.Error(),
}) })

Binary file not shown.

27
go.mod
View File

@@ -3,11 +3,13 @@ module gitea.tecamino.com/paadi/memberDB
go 1.24.5 go 1.24.5
require ( require (
gitea.tecamino.com/paadi/dbHandler v1.1.10
gitea.tecamino.com/paadi/tecamino-logger v0.2.1
github.com/gin-contrib/cors v1.7.6 github.com/gin-contrib/cors v1.7.6
github.com/gin-gonic/gin v1.11.0 github.com/gin-gonic/gin v1.11.0
github.com/go-playground/assert/v2 v2.2.0
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
golang.org/x/text v0.27.0 golang.org/x/text v0.30.0
modernc.org/sqlite v1.39.0
) )
require ( require (
@@ -17,12 +19,16 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/glebarez/sqlite v1.11.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // 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/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
@@ -37,16 +43,21 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.20.0 // indirect golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.40.0 // indirect golang.org/x/crypto v0.43.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/mod v0.25.0 // indirect golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.42.0 // indirect golang.org/x/net v0.45.0 // indirect
golang.org/x/sync v0.16.0 // indirect golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.35.0 // indirect golang.org/x/sys v0.37.0 // indirect
golang.org/x/tools v0.34.0 // indirect golang.org/x/tools v0.37.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gorm.io/gorm v1.31.0 // indirect
modernc.org/libc v1.66.3 // indirect modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.39.0 // indirect
) )

50
go.sum
View File

@@ -1,3 +1,7 @@
gitea.tecamino.com/paadi/dbHandler v1.1.10 h1:zZQbDTJ0bu6CIW90Zms8yYIzTLHtWPNhVKRxLUXEDuE=
gitea.tecamino.com/paadi/dbHandler v1.1.10/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw=
gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE=
gitea.tecamino.com/paadi/tecamino-logger v0.2.1/go.mod h1:FkzRTldUBBOd/iy2upycArDftSZ5trbsX5Ira5OzJgM=
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
@@ -17,6 +21,10 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -36,6 +44,10 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17k
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
@@ -76,33 +88,43 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=

View File

@@ -3,52 +3,46 @@ package handlers
import ( import (
"crypto/hmac" "crypto/hmac"
"crypto/sha256" "crypto/sha256"
"database/sql"
"encoding/hex" "encoding/hex"
"fmt" "time"
"os"
"path/filepath"
_ "modernc.org/sqlite" "gitea.tecamino.com/paadi/dbHandler"
"gitea.tecamino.com/paadi/tecamino-logger/logging"
) )
type DatabaseHandler struct { type DatabaseHandler struct {
database *sql.DB database *dbHandler.DBHandler
timeLocation *time.Location
token []byte token []byte
} }
func NewDatabaseHandler(file string, create bool) (*DatabaseHandler, error) { func NewDatabaseHandler(name, path string, logger *logging.Logger) (*DatabaseHandler, error) {
if create {
// createOrOpenDB creates or opens a SQLite database at the given path.
// Create the directory if it doesn't exist
dir := filepath.Dir(file)
err := os.MkdirAll(dir, os.ModePerm) database, err := dbHandler.NewDBHandler(name, path, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create directory: %w", err) return nil, err
} }
} else { return &DatabaseHandler{database: database}, nil
if _, err := os.Stat(file); err != nil {
return nil, fmt.Errorf("%s not found", file)
}
}
// Open the database (creates it if it doesn't exist)
db, err := sql.Open("sqlite", file)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
// Test the connection
if err = db.Ping(); err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
return &DatabaseHandler{database: db}, nil
} }
func (dh *DatabaseHandler) SetToken(token string) { func (dH *DatabaseHandler) SetTimezone(timezone string) (err error) {
dh.token = []byte(token) if timezone == "" {
timezone = "Local"
}
dH.timeLocation, err = time.LoadLocation(timezone)
return
}
func (dH *DatabaseHandler) DatabaseOpened() bool {
return dH.database != nil
}
func (dH *DatabaseHandler) AddNewTable(model any) error {
return dH.database.AddNewTable(model)
}
func (dH *DatabaseHandler) AddNewColum(model any) error {
return dH.database.AddNewColum(model)
} }
func (dh *DatabaseHandler) hashField(field string) string { func (dh *DatabaseHandler) hashField(field string) string {
@@ -56,3 +50,7 @@ func (dh *DatabaseHandler) hashField(field string) string {
h.Write([]byte(field)) h.Write([]byte(field))
return hex.EncodeToString(h.Sum(nil)) return hex.EncodeToString(h.Sum(nil))
} }
func (dh *DatabaseHandler) SetToken(token string) {
dh.token = []byte(token)
}

View File

@@ -1,170 +1,111 @@
package handlers package handlers
import ( import (
"encoding/json"
"errors" "errors"
"fmt"
"slices"
"strings"
"time"
"gitea.tecamino.com/paadi/memberDB/models" "gitea.tecamino.com/paadi/memberDB/models"
"gitea.tecamino.com/paadi/memberDB/utils"
) )
func (dh *DatabaseHandler) CreateNewEventTable() error { func (dh *DatabaseHandler) StartNewEvent(event *models.Event) error {
createTableSQL := ` if !dh.DatabaseOpened() {
CREATE TABLE IF NOT EXISTS events ( return errors.New("database not opened")
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
date TEXT UNIQUE NOT NULL,
attendees TEXT NOT NULL,
count INTEGER NOT NULL
);`
_, err := dh.database.Exec(createTableSQL)
if err != nil {
return fmt.Errorf("failed to create table: %w", err)
} }
return nil
if dh.database.Exists(&models.Event{}, "name", event.Name, false) {
return errors.New("event with name: " + event.Name + " exists already")
}
event.Date = utils.GetCurrentTime(dh.timeLocation)
event.Day = utils.GetCurrentDay(dh.timeLocation)
return dh.database.AddNewColum(&event)
} }
func (dh *DatabaseHandler) StartNewEvent(name string) error { func (dh *DatabaseHandler) GetEvent(id uint) (events []models.Event, err error) {
_, err := dh.database.Exec("INSERT INTO events (name, date, attendees, count) VALUES (?, ?, ?, ?)", name, time.Now().Format("2006-01-02 15:04:05"), "[]", 0) if !dh.DatabaseOpened() {
return events, errors.New("database not opened")
}
err = dh.database.GetById(&events, id, "Attendees")
for i := range events {
for j := range events[i].Attendees {
events[i].Attendees[j].Decrypt(dh.token)
}
}
if err != nil {
return
}
return
}
func (dh *DatabaseHandler) UpdateEvent(id int, event models.Event) (err error) {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
if event.Date != "" {
event.Day, err = utils.GetDayFromDate(event.Date)
if err != nil {
return err return err
}
}
return dh.database.UpdateValuesById(&event, uint(event.Id))
} }
func (dh *DatabaseHandler) GetEvent(id int) (attendees []models.Event, err error) { func (dh *DatabaseHandler) DeleteEvent(ids ...uint) error {
var args any if !dh.DatabaseOpened() {
query := `SELECT id, name, date, attendees, count FROM events` return errors.New("database not opened")
if id > 0 {
query += ` WHERE id = ?`
args = id
} }
rows, err := dh.database.Query(query, args)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
var id, count int
var name, date, attendance string
if err = rows.Scan(&id, &name, &date, &attendance, &count); err != nil {
return
}
var a []models.Person
if err := json.Unmarshal([]byte(attendance), &a); err != nil {
return attendees, err
}
attendees = append(attendees, models.Event{
Id: id,
Name: name,
Date: date,
Attendees: a,
Count: count,
})
}
return attendees, nil
}
func (dh *DatabaseHandler) DeleteEvent(ids ...int) error {
if len(ids) == 0 { if len(ids) == 0 {
return errors.New("no ids given to be deleted") return errors.New("no ids given to be deleted")
} }
placeholders := make([]string, len(ids)) return dh.database.DeleteById(&models.Event{}, "", ids...)
args := make([]any, len(ids))
for i, id := range ids {
placeholders[i] = "?"
args[i] = id
}
query := fmt.Sprintf("DELETE FROM events WHERE id IN (%s)", strings.Join(placeholders, ","))
_, err := dh.database.Exec(query, args...)
return err
} }
func (dh *DatabaseHandler) AddAttendeesToEvent(event models.Event) error { func (dh *DatabaseHandler) AddAttendeesToEvent(newEvent models.Event) error {
if !dh.DatabaseOpened() {
//get event from database by id return errors.New("database not opened")
row := dh.database.QueryRow(`SELECT attendees FROM events WHERE id = ?`, event.Id)
var attendeesString string
if err := row.Scan(&attendeesString); err != nil {
return err
} }
fmt.Println(attendeesString)
var attendees []models.Person var event models.Event
err := json.Unmarshal([]byte(attendeesString), &attendees) err := dh.database.GetById(&event, uint(newEvent.Id), "Attendees")
if err != nil { if err != nil {
return err return err
} }
next: next:
for _, newAttendee := range event.Attendees { for _, newAttendee := range newEvent.Attendees {
for _, attendee := range attendees { if !dh.database.Exists(&models.Member{}, "id", newAttendee.Id, false) {
continue
}
for _, attendee := range event.Attendees {
if attendee.FirstName == newAttendee.FirstName && attendee.LastName == newAttendee.LastName { if attendee.FirstName == newAttendee.FirstName && attendee.LastName == newAttendee.LastName {
continue next continue next
} }
} }
attendees = append(attendees, newAttendee) dh.database.AddRelation(&event, newAttendee, "Attendees")
} }
attendeesByte, err := json.Marshal(attendees)
if err != nil {
return err
}
count := len(attendees)
_, err = dh.database.Exec("UPDATE events SET attendees= ? count= ? WHERE id= ?", string(attendeesByte), event.Id, count)
if err != nil {
return err
}
return nil return nil
} }
func (dh *DatabaseHandler) DeleteAttendeesFromEvent(event models.Event) error { func (dh *DatabaseHandler) DeleteAttendeesFromEvent(newEvent models.Event) error {
//get event from database by id if !dh.DatabaseOpened() {
row := dh.database.QueryRow(`SELECT attendees FROM events WHERE id = ?`, event.Id) return errors.New("database not opened")
var attendeesString string
if err := row.Scan(&attendeesString); err != nil {
return err
}
fmt.Println(attendeesString)
var attendees []models.Person
err := json.Unmarshal([]byte(attendeesString), &attendees)
if err != nil {
return err
} }
for _, newAttendee := range event.Attendees { var event models.Event
for i, attendee := range attendees { dh.database.GetById(&event, uint(newEvent.Id), "Attendees")
if attendee.FirstName == newAttendee.FirstName && attendee.LastName == newAttendee.LastName {
attendees = slices.Delete(attendees, i, i+1)
}
}
}
attendeesByte, err := json.Marshal(attendees) for _, newAttendee := range newEvent.Attendees {
if err != nil { for _, a := range event.Attendees {
return err a.Decrypt(dh.token)
if a.FirstName == newAttendee.FirstName && a.LastName == newAttendee.LastName {
dh.database.DeleteRelation(&event, a, "Attendees")
}
} }
count := len(attendees)
_, err = dh.database.Exec("UPDATE events SET attendees= ? count= ? WHERE id= ?", string(attendeesByte), event.Id, count)
if err != nil {
return err
} }
return nil return nil
} }

49
handlers/group.go Normal file
View File

@@ -0,0 +1,49 @@
package handlers
import (
"errors"
"gitea.tecamino.com/paadi/memberDB/models"
)
func (dh *DatabaseHandler) NewGroup(group *models.Group) error {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
if dh.database.Exists(&models.Group{}, "name", group.Name, false) {
return errors.New("group with name: " + group.Name + " exists already")
}
return dh.database.AddNewColum(&group)
}
func (dh *DatabaseHandler) GetGroup(id uint) (group []models.Group, err error) {
if !dh.DatabaseOpened() {
return group, errors.New("database not opened")
}
err = dh.database.GetById(&group, id)
if err != nil {
return
}
return
}
func (dh *DatabaseHandler) UpdateGroup(id uint, group models.Group) (err error) {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
return dh.database.UpdateValuesById(&group, uint(group.Id))
}
func (dh *DatabaseHandler) DeleteGroup(ids ...uint) error {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
if len(ids) == 0 {
return errors.New("no ids given to be deleted")
}
return dh.database.DeleteById(&models.Group{}, "", ids...)
}

View File

@@ -1,48 +1,19 @@
package handlers package handlers
import ( import (
"database/sql"
"errors" "errors"
"fmt" "fmt"
"strings"
"time"
"gitea.tecamino.com/paadi/memberDB/crypto"
"gitea.tecamino.com/paadi/memberDB/models" "gitea.tecamino.com/paadi/memberDB/models"
"gitea.tecamino.com/paadi/memberDB/utils" "gitea.tecamino.com/paadi/memberDB/utils"
) )
// CreateNewMemberTable creates a new member table.
func (dh *DatabaseHandler) CreateNewMemberTable() error {
createTableSQL := `
CREATE TABLE IF NOT EXISTS members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
first_name_hash TEXT NOT NULL,
last_name TEXT NOT NULL,
last_name_hash TEXT NOT NULL,
birthday TEXT NOT NULL,
birthday_hash TEXT NOT NULL,
address TEXT NOT NULL,
zip_code TEXT NOT NULL,
town TEXT NOT NULL,
phone TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
first_visit TEXT NOT NULL,
last_visit TEXT NOT NULL,
member_group TEXT NOT NULL,
responsible_person TEXT NOT NULL
);`
_, err := dh.database.Exec(createTableSQL)
if err != nil {
return fmt.Errorf("failed to create table: %w", err)
}
return nil
}
// AddNewMember adds a new member to memeber table at least fist, las name and birthday has to be entered // AddNewMember adds a new member to memeber table at least fist, las name and birthday has to be entered
func (dh *DatabaseHandler) AddNewMember(members ...models.Member) error { func (dh *DatabaseHandler) AddNewMember(members ...models.Member) error {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
for _, member := range members { for _, member := range members {
exists, err := dh.memberExists(member) exists, err := dh.memberExists(member)
@@ -52,78 +23,23 @@ func (dh *DatabaseHandler) AddNewMember(members ...models.Member) error {
return fmt.Errorf("member %s %s %s exists already", member.FirstName, member.LastName, member.Birthday) return fmt.Errorf("member %s %s %s exists already", member.FirstName, member.LastName, member.Birthday)
} }
encFirstName, err := crypto.Encrypt(member.FirstName, dh.token)
if err != nil {
return err
}
encLastName, err := crypto.Encrypt(member.LastName, dh.token)
if err != nil {
return err
}
//check correct birtday format //check correct birtday format
if member.Birthday != "" && !utils.IsValidBirthday(member.Birthday) { if member.Birthday != "" && !utils.IsValidBirthday(member.Birthday) {
return errors.New("incorrect birthday format") return errors.New("incorrect birthday format")
} }
encBirthday, err := crypto.Encrypt(member.Birthday, dh.token)
if err != nil {
return err
}
encAddress, err := crypto.Encrypt(member.Address, dh.token)
if err != nil {
return err
}
encZip, err := crypto.Encrypt(member.Zip, dh.token)
if err != nil {
return err
}
encTown, err := crypto.Encrypt(member.Town, dh.token)
if err != nil {
return err
}
encPhone, err := crypto.Encrypt(member.Phone, dh.token)
if err != nil {
return err
}
//check correct email format //check correct email format
if member.Email != "" && !utils.IsValidEmail(member.Email) { if member.Email != "" && !utils.IsValidEmail(member.Email) {
return errors.New("incorrect email format") return errors.New("incorrect email format")
} }
encEmail, err := crypto.Encrypt(member.Email, dh.token) now := utils.GetCurrentTime(dh.timeLocation)
if err != nil { member.FirstVisit = now
return err member.LastVisit = now
}
now := time.Now().Format("2006-01-02 15:04:05") member.Encrypt(dh.token)
encFirstVisit, err := crypto.Encrypt(now, dh.token)
if err != nil {
return err
}
encLastVisit, err := crypto.Encrypt(now, dh.token) err = dh.database.AddNewColum(&member)
if err != nil {
return err
}
encGroup, err := crypto.Encrypt(member.Group, dh.token)
if err != nil {
return err
}
encResponsiblePerson, err := crypto.Encrypt(member.ResponsiblePerson, dh.token)
if err != nil {
return err
}
_, err = dh.database.Exec("INSERT INTO members (first_name, first_name_hash, last_name, last_name_hash, birthday, birthday_hash, address, zip_code, town, phone, email, first_visit, last_visit, member_group, responsible_person) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", encFirstName, dh.hashField(member.FirstName), encLastName, dh.hashField(member.LastName), encBirthday, dh.hashField(member.Birthday), encAddress, encZip, encTown, encPhone, encEmail, encFirstVisit, encLastVisit, encGroup, encResponsiblePerson)
if err != nil { if err != nil {
return err return err
} }
@@ -132,290 +48,89 @@ func (dh *DatabaseHandler) AddNewMember(members ...models.Member) error {
} }
// DeleteMember removes members by given ids // DeleteMember removes members by given ids
func (dh *DatabaseHandler) DeleteMember(ids ...int) error { func (dh *DatabaseHandler) DeleteMember(ids ...uint) error {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
if len(ids) == 0 { if len(ids) == 0 {
return errors.New("no ids given to be deleted") return errors.New("no ids given to be deleted")
} }
return dh.database.DeleteById(&models.Member{}, "", ids...)
placeholders := make([]string, len(ids))
args := make([]any, len(ids))
for i, id := range ids {
placeholders[i] = "?"
args[i] = id
}
query := fmt.Sprintf("DELETE FROM members WHERE id IN (%s)", strings.Join(placeholders, ","))
_, err := dh.database.Exec(query, args...)
return err
} }
// GetMember returns one member by given id // GetMember returns one member by given id
func (dh *DatabaseHandler) GetMember(id int) (members []models.Member, err error) { func (dh *DatabaseHandler) GetMember(id uint) (members []models.Member, err error) {
var args any if !dh.DatabaseOpened() {
query := `SELECT id, first_name, last_name, birthday, address, zip_code, town, phone, email, first_visit, last_visit, member_group, responsible_person FROM members` return members, errors.New("database not opened")
if id > 0 {
query = ` WHERE id = ?`
args = id
} }
rows, err := dh.database.Query(query, args) err = dh.database.GetById(&members, id, "Group", "Responsible", "Responsible.Member", "Events")
if err != nil { if err != nil {
return return
} }
defer rows.Close()
for rows.Next() { //map for list all decrypted members so it only once derypt each member
var id int decryptedList := make(map[uint]bool, 1)
var encFirstName, encLastName, encBirthday, encAddress, encZip, encTown, encPhone, encEmail, encFirstVisit, encLastVisit, encGroup, encResponsiblePerson string for i := range members {
if err = rows.Scan(&id, &encFirstName, &encLastName, &encBirthday, &encAddress, &encZip, &encTown, &encPhone, &encEmail, &encFirstVisit, &encLastVisit, &encGroup, &encResponsiblePerson); err != nil { if err := members[i].Decrypt(dh.token); err != nil {
return members, err
}
if members[i].Responsible != nil {
//check if already decrypted
if decryptedList[members[i].Responsible.Member.Id] {
continue
}
members[i].Responsible.Member.Decrypt(dh.token)
decryptedList[members[i].Responsible.Member.Id] = true
}
}
return return
}
firstName, err := crypto.Decrypt(encFirstName, dh.token)
if err != nil {
return members, err
}
lastName, err := crypto.Decrypt(encLastName, dh.token)
if err != nil {
return members, err
}
birthday, err := crypto.Decrypt(encBirthday, dh.token)
if err != nil {
return members, err
}
address, err := crypto.Decrypt(encAddress, dh.token)
if err != nil {
return members, err
}
zip, err := crypto.Decrypt(encZip, dh.token)
if err != nil {
return members, err
}
town, err := crypto.Decrypt(encTown, dh.token)
if err != nil {
return members, err
}
phone, err := crypto.Decrypt(encPhone, dh.token)
if err != nil {
return members, err
}
email, err := crypto.Decrypt(encEmail, dh.token)
if err != nil {
return members, err
}
firstVisit, err := crypto.Decrypt(encFirstVisit, dh.token)
if err != nil {
return members, err
}
lastVisit, err := crypto.Decrypt(encLastVisit, dh.token)
if err != nil {
return members, err
}
group, err := crypto.Decrypt(encGroup, dh.token)
if err != nil {
return members, err
}
responsiblePerson, err := crypto.Decrypt(encResponsiblePerson, dh.token)
if err != nil {
return members, err
}
members = append(members, models.Member{
Id: id,
FirstName: firstName,
LastName: lastName,
Birthday: birthday,
Address: address,
Zip: zip,
Town: town,
Phone: phone,
Email: email,
FirstVisit: firstVisit,
LastVisit: lastVisit,
Group: group,
ResponsiblePerson: responsiblePerson,
})
}
return members, nil
} }
// UpdateMember updates/overrides all information given meber id // UpdateMember updates/overrides all information given meber id
func (dh *DatabaseHandler) UpdateMember(id int, member models.Member) (err error) { func (dh *DatabaseHandler) UpdateMember(id uint, member models.Member) (err error) {
var queryParameters []string if !dh.DatabaseOpened() {
var args []any return errors.New("database not opened")
if member.FirstName != "" {
encFirstName, err := crypto.Encrypt(member.FirstName, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "first_name = ?")
queryParameters = append(queryParameters, "first_name_hash = ?")
args = append(args, encFirstName)
args = append(args, dh.hashField(member.FirstName))
}
if member.LastName != "" {
encLastName, err := crypto.Encrypt(member.LastName, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "last_name = ?")
queryParameters = append(queryParameters, "last_name_hash = ?")
args = append(args, encLastName)
args = append(args, dh.hashField(member.LastName))
} }
//check correct birtday format //check correct birtday format
if member.Birthday != "" && utils.IsValidBirthday(member.Birthday) { if member.Birthday != "" && !utils.IsValidBirthday(member.Birthday) {
encBirthday, err := crypto.Encrypt(member.Birthday, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "birthday = ?")
queryParameters = append(queryParameters, "birthday_hash = ?")
args = append(args, encBirthday)
args = append(args, dh.hashField(member.Birthday))
} else if member.Birthday != "" {
return errors.New("incorrect birthday format") return errors.New("incorrect birthday format")
} }
if member.Address != "" {
encAddress, err := crypto.Encrypt(member.Address, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "address = ?")
args = append(args, encAddress)
}
if member.Zip != "" {
encZip, err := crypto.Encrypt(member.Zip, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "zip_code = ?")
args = append(args, encZip)
}
if member.Town != "" {
encTown, err := crypto.Encrypt(member.Town, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "town = ?")
args = append(args, encTown)
}
if member.Phone != "" {
encPhone, err := crypto.Encrypt(member.Phone, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "phone = ?")
args = append(args, encPhone)
}
//check correct email format //check correct email format
if member.Email != "" && utils.IsValidEmail(member.Email) { if member.Email != "" && !utils.IsValidEmail(member.Email) {
encEmail, err := crypto.Encrypt(member.Email, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "email = ?")
args = append(args, encEmail)
} else if member.Email != "" {
return errors.New("incorrect email format") return errors.New("incorrect email format")
} }
if member.FirstVisit != "" { if member.Group != nil {
encFirstVisit, err := crypto.Encrypt(member.FirstVisit, dh.token) member.GroupId = &member.Group.Id
if err != nil {
return err
}
queryParameters = append(queryParameters, "first_visit = ?")
args = append(args, encFirstVisit)
} }
if member.LastVisit != "" { if member.Responsible != nil {
encLastVisit, err := crypto.Encrypt(member.LastVisit, dh.token) member.ResponsibleId = &member.Responsible.Id
if err != nil {
return err
} }
queryParameters = append(queryParameters, "last_visit = ?") member.Encrypt(dh.token)
return dh.database.UpdateValuesById(&member, uint(member.Id), "Group", "Responsible", "Responsible.Member")
args = append(args, encLastVisit)
}
if member.Group != "" {
encFirstVisit, err := crypto.Encrypt(member.Group, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "member_group = ?")
args = append(args, encFirstVisit)
}
if member.ResponsiblePerson != "" {
encResponsiblePerson, err := crypto.Encrypt(member.ResponsiblePerson, dh.token)
if err != nil {
return err
}
queryParameters = append(queryParameters, "responsible_person = ?")
args = append(args, encResponsiblePerson)
}
query := `UPDATE members SET `
query += strings.Join(queryParameters, ", ")
query += ` WHERE id = ?`
args = append(args, id)
_, err = dh.database.Exec(query, args...)
return err
} }
// memberExists helper to check wheter member already exists // memberExists helper to check wheter member already exists
func (dh *DatabaseHandler) memberExists(member models.Member) (bool, error) { func (dh *DatabaseHandler) memberExists(checkMember models.Member) (bool, error) {
query := ` if !dh.DatabaseOpened() {
SELECT 1 FROM members return false, errors.New("database not opened")
WHERE first_name_hash = ? AND last_name_hash = ? AND birthday_hash = ?
LIMIT 1
`
var exists int
err := dh.database.QueryRow(query, dh.hashField(member.FirstName), dh.hashField(member.LastName), dh.hashField(member.Birthday)).Scan(&exists)
if err == sql.ErrNoRows {
return false, nil // no match
} else if err != nil {
return false, err // db error
} }
return true, nil // match found var members []models.Member
if !dh.database.Exists(&members, "birthdayHash", dh.hashField(checkMember.Birthday), false) {
return false, nil
}
for _, member := range members {
if dh.hashField(checkMember.FirstName) == member.FirstNameHash && dh.hashField(checkMember.LastName) == member.LastNameHash && dh.hashField(checkMember.Birthday) == member.BirthdayHash {
return true, nil
}
}
return false, nil
} }

View File

@@ -1,110 +1,70 @@
package handlers package handlers
import ( import (
"database/sql"
"errors" "errors"
"fmt"
"strings"
"gitea.tecamino.com/paadi/memberDB/models" "gitea.tecamino.com/paadi/memberDB/models"
) )
func (dh *DatabaseHandler) CreateNewResponsibleTable() error { func (dh *DatabaseHandler) AddNewResponsible(responsibles ...models.Responsible) error {
createTableSQL := ` if !dh.DatabaseOpened() {
CREATE TABLE IF NOT EXISTS responsible ( return errors.New("database not opened")
id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT NOT NULL,
last_name TEXT UNIQUE NOT NULL,
);`
_, err := dh.database.Exec(createTableSQL)
if err != nil {
return fmt.Errorf("failed to create table: %w", err)
} }
return nil
}
func (dh *DatabaseHandler) AddNewResponsible(responsibles ...models.Person) error { next:
for _, responsible := range responsibles { for _, r := range responsibles {
exists, err := dh.responsibleExists(responsible) //check if member exists
if err != nil { if !dh.database.Exists(&models.Member{}, "id", &r.MemberId, false) {
return err
} else if exists {
continue continue
} }
// check if already exists
var oldResponsibles models.Responsibles
err := dh.database.GetById(&oldResponsibles, 0, "Member")
if err != nil {
return err
}
_, err = dh.database.Exec("INSERT INTO responsible (first_name, last_name) VALUES (?, ?)", responsible.FirstName, responsible.LastName) for _, oldR := range oldResponsibles {
if oldR.Member == nil {
continue
} else if oldR.Member.Id == r.MemberId {
continue next
}
}
err = dh.database.AddNewColum(&r)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
func (dh *DatabaseHandler) GetResponsible(id int) (persons []models.Person, err error) { func (dh *DatabaseHandler) GetResponsible(id uint) (responsibles models.Responsibles, err error) {
var args any if !dh.DatabaseOpened() {
query := `SELECT id, first_name, last_name FROM responsible` return responsibles, errors.New("database not opened")
if id > 0 {
query = ` WHERE id = ?`
args = id
} }
rows, err := dh.database.Query(query, args) err = dh.database.GetById(&responsibles, id, "Member")
for i := range responsibles {
err := responsibles[i].Member.Decrypt(dh.token)
if err != nil { if err != nil {
return responsibles, err
}
}
return return
}
defer rows.Close()
for rows.Next() {
var id int
var firstName, lastName string
if err = rows.Scan(&id, &firstName, &lastName); err != nil {
return
}
persons = append(persons, models.Person{
Id: id,
FirstName: firstName,
LastName: lastName,
})
}
return persons, nil
} }
func (dh *DatabaseHandler) DeleteResponsible(ids ...int) error { func (dh *DatabaseHandler) DeleteResponsible(ids ...uint) error {
if !dh.DatabaseOpened() {
return errors.New("database not opened")
}
if len(ids) == 0 { if len(ids) == 0 {
return errors.New("no ids given to be deleted") return errors.New("no ids given to be deleted")
} }
return dh.database.DeleteById(&models.Responsible{}, "", ids...)
placeholders := make([]string, len(ids))
args := make([]any, len(ids))
for i, id := range ids {
placeholders[i] = "?"
args[i] = id
}
query := fmt.Sprintf("DELETE FROM responsible WHERE id IN (%s)", strings.Join(placeholders, ","))
_, err := dh.database.Exec(query, args...)
return err
}
func (dh *DatabaseHandler) responsibleExists(responsible models.Person) (bool, error) {
query := `
SELECT 1 FROM responsible
WHERE first_name = ? AND last_name = ?
LIMIT 1
`
var exists int
err := dh.database.QueryRow(query, responsible.FirstName, responsible.LastName).Scan(&exists)
if err == sql.ErrNoRows {
return false, nil // no match
} else if err != nil {
return false, err // db error
}
return true, nil // match found
} }

View File

@@ -12,8 +12,13 @@ func main() {
url := flag.String("url", "127.0.0.1", "server url") url := flag.String("url", "127.0.0.1", "server url")
flag.Parse() flag.Parse()
a := api.NewAPI(*url, *port) a, err := api.NewAPI(*url, *port, nil)
if err := a.Run(); err != nil { if err != nil {
log.Fatal(err)
}
err = a.Run()
if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }

221
member_test.go Normal file
View File

@@ -0,0 +1,221 @@
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"gitea.tecamino.com/paadi/memberDB/api"
"gitea.tecamino.com/paadi/memberDB/models"
"github.com/gin-gonic/gin"
"github.com/go-playground/assert/v2"
)
func TestErrors(t *testing.T) {
dbName := "test.db"
if _, err := os.Stat(dbName); err == nil {
t.Log("remove user.db to start test with empty database")
if err := os.Remove(dbName); err != nil {
t.Fatal(err)
}
}
t.Log("start member db test")
t.Log("initialize accessHandler")
r := gin.Default()
apiHandler, err := api.NewAPIHandler(nil)
if err != nil {
t.Fatal(err)
}
v1 := r.Group("v1")
v1.GET("/events", apiHandler.GetEvent)
v1.GET("/events/new", apiHandler.StartNewEvent)
v1.GET("/events/delete", apiHandler.DeleteEvent)
v1.GET("/members", apiHandler.GetMember)
v1.GET("/responsible", apiHandler.GetResponsible)
v1.POST("/database/open", apiHandler.OpenDatabase)
v1.POST("/members/add", apiHandler.AddNewMember)
v1.POST("/members/edit", apiHandler.UpdateMember)
v1.POST("/members/delete", apiHandler.DeleteMember)
v1.POST("/members/import/csv", apiHandler.ImportCSV)
v1.POST("/events/attendees/add", apiHandler.AddNewAttendees)
v1.POST("/events/attendees/delete", apiHandler.DeleteAttendee)
v1.POST("/responsible/add", apiHandler.AddNewResponsible)
v1.POST("/responsible/delete", apiHandler.DeleteResponsible)
type request struct {
Log string
Name string
Method string
Path string
Payload any
Cookie *http.Cookie
ignoreError bool
}
var requests []request
type payload struct {
DBPath string `json:"dbPath,omitempty"`
Create bool `json:"create,omitempty"`
FirstName string `json:"FirstName,omitempty"`
LastName string `json:"lastName,omitempty"`
Birthday string `json:"birthday,omitempty"`
}
requests = append(requests,
request{Log: "error db not opened", Name: "error db not opened", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Adrian", LastName: "Zürcher", Birthday: "23.06.1987"}, ignoreError: true},
request{Log: "open member db", Name: "open member db", Method: "POST", Path: "/v1/database/open", Payload: payload{DBPath: "test.db", Create: true}},
request{Log: "add new member", Name: "add new member", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Adrian", LastName: "Zürcher", Birthday: "23.06.1987"}, ignoreError: true},
request{Log: "error first name missing", Name: "error first name missing", Method: "POST", Path: "/v1/members/add", Payload: payload{LastName: "Zürcher", Birthday: "23.06.1987"}, ignoreError: true},
request{Log: "error last name missing", Name: "error last name missing", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Adrian", Birthday: "23.06.1987"}, ignoreError: true},
request{Log: "error birthday missing", Name: "error birthday missing", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Paulina", LastName: "Zürcher"}, ignoreError: true},
request{Log: "error wrong birthday format", Name: "error wrong birthday format", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Paulina", LastName: "Zürcher", Birthday: "213.06.1987"}, ignoreError: true},
)
for _, request := range requests {
if request.Log != "" {
t.Log(request.Log)
}
var bodyReader io.Reader
if request.Payload != nil {
jsonBytes, _ := json.Marshal(request.Payload)
bodyReader = bytes.NewBuffer(jsonBytes)
}
req, _ := http.NewRequest(request.Method, request.Path, bodyReader)
if request.Cookie != nil {
req.AddCookie(request.Cookie) // attach refresh_token cookie
}
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
t.Log(request.Name+" response:", w.Body.String())
if !request.ignoreError {
assert.Equal(t, http.StatusOK, w.Code)
}
}
}
func TestMemberDB(t *testing.T) {
dbName := "test.db"
if _, err := os.Stat(dbName); err == nil {
t.Log("remove user.db to start test with empty database")
if err := os.Remove(dbName); err != nil {
t.Fatal(err)
}
}
t.Log("start member db test")
t.Log("initialize accessHandler")
r := gin.Default()
apiHandler, err := api.NewAPIHandler(nil)
if err != nil {
t.Fatal(err)
}
v1 := r.Group("v1")
v1.GET("/events", apiHandler.GetEvent)
v1.GET("/members", apiHandler.GetMember)
v1.GET("/responsible", apiHandler.GetResponsible)
v1.POST("/database/open", apiHandler.OpenDatabase)
v1.POST("/members/add", apiHandler.AddNewMember)
v1.POST("/members/edit", apiHandler.UpdateMember)
v1.POST("/members/delete", apiHandler.DeleteMember)
v1.POST("/members/import/csv", apiHandler.ImportCSV)
v1.POST("/events/new", apiHandler.StartNewEvent)
v1.POST("/events/attendees/add", apiHandler.AddNewAttendees)
v1.POST("/events/attendees/delete", apiHandler.DeleteAttendee)
v1.POST("/events/edit", apiHandler.UpdateEvent)
v1.POST("/events/delete", apiHandler.DeleteEvent)
v1.POST("/responsible/add", apiHandler.AddNewResponsible)
v1.POST("/responsible/delete", apiHandler.DeleteResponsible)
type request struct {
Log string
Name string
Method string
Path string
Payload any
Cookie *http.Cookie
ignoreError bool
}
var requests []request
type payload struct {
DBPath string `json:"dbPath,omitempty"`
Create bool `json:"create,omitempty"`
Ids []uint `json:"ids,omitempty"`
FirstName string `json:"FirstName,omitempty"`
LastName string `json:"lastName,omitempty"`
Birthday string `json:"birthday,omitempty"`
Group string `json:"group,omitempty"`
}
requests = append(requests,
request{Log: "open member db", Name: "open member db", Method: "POST", Path: "/v1/database/open", Payload: payload{DBPath: "test.db", Create: true}},
request{Log: "add new member", Name: "add new member", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Adrian", LastName: "Zürcher", Birthday: "23.06.1987"}},
request{Log: "add existing member", Name: "add existing member", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Adrian", LastName: "Zürcher", Birthday: "23.06.1987"}, ignoreError: true},
request{Log: "add new member", Name: "add new member", Method: "POST", Path: "/v1/members/add", Payload: payload{FirstName: "Paulina", LastName: "Zürcher", Birthday: "15.01.1991"}},
request{Log: "get members", Name: "get members", Method: "GET", Path: "/v1/members"},
request{Log: "update members", Name: "update members", Method: "POST", Path: "/v1/members/edit?id=1", Payload: []models.Member{{Group: &models.Group{Name: "testGroup"}}}},
request{Log: "get again members", Name: "get again members", Method: "GET", Path: "/v1/members"},
//request{Log: "delete members", Name: "delete members", Method: "POST", Path: "/v1/members/delete", Payload: payload{Ids: []uint{2, 1}}},
request{Log: "get again members", Name: "get again members", Method: "GET", Path: "/v1/members"},
request{Log: "new event", Name: "new event", Method: "POST", Path: "/v1/events/new", Payload: models.Event{Name: "testEvent"}},
request{Log: "add new attendee", Name: "add new attendee", Method: "POST", Path: "/v1/events/attendees/add", Payload: models.Event{Attendees: []*models.Member{{Id: 26}}}},
request{Log: "add another attendee", Name: "add another attendee", Method: "POST", Path: "/v1/events/attendees/add", Payload: models.Event{Attendees: []*models.Member{{Id: 2}}}},
request{Log: "get events", Name: "get events", Method: "GET", Path: "/v1/events"},
//request{Log: "delete attendee", Name: "add delete attendee", Method: "POST", Path: "/v1/events/attendees/delete", Payload: models.Event{Attendees: []*models.Member{{FirstName: "Adi", LastName: "Züri"}}}},
request{Log: "get events", Name: "get events", Method: "GET", Path: "/v1/events"},
//request{Log: "add responsible", Name: "add responsible", Method: "POST", Path: "/v1/responsible/add", Payload: models.Responsibles{models.Responsible{Member: &models.Member{FirstName: "Adi", LastName: "Züri"}}}},
request{Log: "add another responsible", Name: "add another responsible", Method: "POST", Path: "/v1/responsible/add", Payload: models.Responsibles{models.Responsible{MemberId: 1}}},
request{Log: "add another responsible", Name: "add another responsible", Method: "POST", Path: "/v1/responsible/add", Payload: models.Responsibles{models.Responsible{MemberId: 25}}},
request{Log: "get responsible", Name: "get responsible", Method: "GET", Path: "/v1/responsible"},
//request{Log: "delete responsible id 1", Name: "delete responsible id 1", Method: "POST", Path: "/v1/responsible/delete", Payload: payload{Ids: []uint{1}}},
request{Log: "get responsible", Name: "get responsible", Method: "GET", Path: "/v1/responsible"},
)
for _, request := range requests {
if request.Log != "" {
t.Log(request.Log)
}
var bodyReader io.Reader
if request.Payload != nil {
jsonBytes, _ := json.Marshal(request.Payload)
bodyReader = bytes.NewBuffer(jsonBytes)
}
req, _ := http.NewRequest(request.Method, request.Path, bodyReader)
if request.Cookie != nil {
req.AddCookie(request.Cookie) // attach refresh_token cookie
}
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
t.Log(request.Name+" response:", w.Body.String())
if !request.ignoreError {
assert.Equal(t, http.StatusOK, w.Code)
}
}
}

View File

@@ -1,9 +1,9 @@
package models package models
type Event struct { type Event struct {
Id int `json:"id"` Id int `gorm:"primaryKey" json:"id"`
Name string `json:"name"` Name string `gorm:"column:name" json:"name"`
Date string `json:"date"` Date string `gorm:"column:date" json:"date"`
Attendees []Person `json:"attendees"` Day string `gorm:"column:day" json:"day"`
Count int `json:"count"` Attendees []*Member `gorm:"many2many:member_events;constraint:OnDelete:CASCADE;" json:"attendees"`
} }

6
models/group.go Normal file
View File

@@ -0,0 +1,6 @@
package models
type Group struct {
Id uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"column:name" json:"name"`
}

View File

@@ -1,17 +1,192 @@
package models package models
import (
"gitea.tecamino.com/paadi/memberDB/crypto"
"gitea.tecamino.com/paadi/memberDB/utils"
)
type Member struct { type Member struct {
Id int `json:"id,omitempty"` Id uint `gorm:"primaryKey" json:"id,omitempty"`
FirstName string `json:"firstName,omitempty"` FirstName string `gorm:"column:firstName" json:"firstName,omitempty"`
LastName string `json:"lastName,omitempty"` FirstNameHash string `gorm:"column:firstNameHash" json:"-"`
Birthday string `json:"birthday,omitempty"` LastName string `gorm:"column:lastName" json:"lastName,omitempty"`
Address string `json:"address,omitempty"` LastNameHash string `gorm:"column:lastNameHash" json:"-"`
Zip string `json:"zip,omitempty"` Birthday string `gorm:"column:birthday" json:"birthday,omitempty"`
Town string `json:"town,omitempty"` BirthdayHash string `gorm:"column:birthdayHash" json:"-"`
Phone string `json:"phone,omitempty"` Address string `gorm:"column:address" json:"address,omitempty"`
Email string `json:"email,omitempty"` Zip string `gorm:"column:zip" json:"zip,omitempty"`
FirstVisit string `json:"firstVisit,omitempty"` Town string `gorm:"column:town" json:"town,omitempty"`
LastVisit string `json:"lastVisit,omitempty"` Phone string `gorm:"column:phone" json:"phone,omitempty"`
Group string `json:"group,omitempty"` Email string `gorm:"column:email" json:"email,omitempty"`
ResponsiblePerson string `json:"responsiblePerson,omitempty"` FirstVisit string `gorm:"column:firstVisit" json:"firstVisit,omitempty"`
LastVisit string `gorm:"column:lastVisit" json:"lastVisit,omitempty"`
GroupId *uint `json:"GroupId,omitempty"`
Group *Group `gorm:"foreignKey:GroupId;constraint:OnDelete:CASCADE;" json:"group,omitempty"`
ResponsibleId *uint `json:"ResponsibleId,omitempty"`
Responsible *Responsible `gorm:"foreignKey:ResponsibleId;references:Id" json:"responsible,omitempty"`
Comment string `gorm:"column:comment" json:"comment,omitempty"`
Events []*Event `gorm:"many2many:member_events;" `
}
func (m *Member) Encrypt(token []byte) (err error) {
if m.FirstName != "" {
m.FirstNameHash = utils.HashField(m.FirstName, token)
m.FirstName, err = crypto.Encrypt(m.FirstName, token)
if err != nil {
return
}
}
if m.LastName != "" {
m.LastNameHash = utils.HashField(m.LastName, token)
m.LastName, err = crypto.Encrypt(m.LastName, token)
if err != nil {
return
}
}
if m.Birthday != "" {
m.BirthdayHash = utils.HashField(m.Birthday, token)
m.Birthday, err = crypto.Encrypt(m.Birthday, token)
if err != nil {
return
}
}
if m.Address != "" {
m.Address, err = crypto.Encrypt(m.Address, token)
if err != nil {
return
}
}
if m.Zip != "" {
m.Zip, err = crypto.Encrypt(m.Zip, token)
if err != nil {
return
}
}
if m.Town != "" {
m.Town, err = crypto.Encrypt(m.Town, token)
if err != nil {
return
}
}
if m.Phone != "" {
m.Phone, err = crypto.Encrypt(m.Phone, token)
if err != nil {
return
}
}
if m.Email != "" {
m.Email, err = crypto.Encrypt(m.Email, token)
if err != nil {
return
}
}
if m.FirstVisit != "" {
m.FirstVisit, err = crypto.Encrypt(m.FirstVisit, token)
if err != nil {
return
}
}
if m.LastVisit != "" {
m.LastVisit, err = crypto.Encrypt(m.LastVisit, token)
if err != nil {
return
}
}
if m.Comment != "" {
m.Comment, err = crypto.Encrypt(m.Comment, token)
if err != nil {
return
}
}
return
}
func (m *Member) Decrypt(token []byte) (err error) {
if m.FirstName != "" {
m.FirstName, err = crypto.Decrypt(m.FirstName, token)
if err != nil {
return
}
}
if m.LastName != "" {
m.LastName, err = crypto.Decrypt(m.LastName, token)
if err != nil {
return
}
}
if m.Birthday != "" {
m.Birthday, err = crypto.Decrypt(m.Birthday, token)
if err != nil {
return
}
}
if m.Address != "" {
m.Address, err = crypto.Decrypt(m.Address, token)
if err != nil {
return
}
}
if m.Zip != "" {
m.Zip, err = crypto.Decrypt(m.Zip, token)
if err != nil {
return
}
}
if m.Town != "" {
m.Town, err = crypto.Decrypt(m.Town, token)
if err != nil {
return
}
}
if m.Phone != "" {
m.Phone, err = crypto.Decrypt(m.Phone, token)
if err != nil {
return
}
}
if m.Email != "" {
m.Email, err = crypto.Decrypt(m.Email, token)
if err != nil {
return
}
}
if m.FirstVisit != "" {
m.FirstVisit, err = crypto.Decrypt(m.FirstVisit, token)
if err != nil {
return
}
}
if m.LastVisit != "" {
m.LastVisit, err = crypto.Decrypt(m.LastVisit, token)
if err != nil {
return
}
}
if m.Comment != "" {
m.Comment, err = crypto.Decrypt(m.Comment, token)
if err != nil {
return
}
}
return
} }

View File

@@ -1,7 +0,0 @@
package models
type Person struct {
Id int `json:"id,omitEmpty"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
}

9
models/responsible.go Normal file
View File

@@ -0,0 +1,9 @@
package models
type Responsible struct {
Id uint `gorm:"primaryKey" json:"id"`
MemberId uint `json:"memberId"`
Member *Member `gorm:"foreignKey:MemberId;constraint:OnDelete:CASCADE;" json:"member"`
}
type Responsibles []Responsible

View File

@@ -1,6 +1,10 @@
package utils package utils
import ( import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/mail" "net/mail"
"time" "time"
) )
@@ -25,3 +29,22 @@ func IsValidBirthday(birthday string) bool {
} }
return false return false
} }
func GetCurrentTime(loc *time.Location) string {
return time.Now().In(loc).Format("2006-01-02 15:04:05")
}
func GetCurrentDay(loc *time.Location) string {
return fmt.Sprint(time.Now().In(loc).Weekday())
}
func GetDayFromDate(dateTime string) (string, error) {
t, err := time.Parse("2006-01-02 15:04:05", dateTime)
return fmt.Sprint(t.Weekday()), err
}
func HashField(field string, token []byte) string {
h := hmac.New(sha256.New, token)
h.Write([]byte(field))
return hex.EncodeToString(h.Sum(nil))
}