first commit
This commit is contained in:
50
api/api.go
Normal file
50
api/api.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
host string
|
||||
port int
|
||||
router *gin.Engine
|
||||
}
|
||||
|
||||
func NewAPI(host string, port int) *API {
|
||||
r := gin.Default()
|
||||
r.Use(cors.New(cors.Config{
|
||||
//AllowOrigins: []string{"http://localhost:9000"}, // frontend origin
|
||||
AllowOrigins: []string{"*"}, // frontend origin
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
|
||||
apiHandler := NewAPIHandler()
|
||||
|
||||
v1 := r.Group("v1")
|
||||
v1.GET("/members", apiHandler.GetMemberById)
|
||||
v1.GET("/events", apiHandler.GetEventById)
|
||||
v1.GET("/events/new", apiHandler.StartNewEvent)
|
||||
|
||||
v1.POST("/database/open", apiHandler.OpenDatabase)
|
||||
v1.POST("/members/add", apiHandler.AddNewMember)
|
||||
v1.POST("/members/edit", apiHandler.EditMember)
|
||||
v1.POST("/members/delete", apiHandler.DeleteMember)
|
||||
v1.POST("/members/import/csv", apiHandler.ImportCSV)
|
||||
|
||||
return &API{host: host,
|
||||
port: port,
|
||||
router: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) Run() error {
|
||||
return api.router.Run(fmt.Sprintf("%s:%d", api.host, api.port))
|
||||
}
|
81
api/apiHandler.go
Normal file
81
api/apiHandler.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"memberDB/handlers"
|
||||
"memberDB/models"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type APIHandler struct {
|
||||
DbHandler *handlers.DatabaseHandler
|
||||
}
|
||||
|
||||
func NewAPIHandler() *APIHandler {
|
||||
return &APIHandler{}
|
||||
}
|
||||
|
||||
func (a *APIHandler) OpenDatabase(c *gin.Context) {
|
||||
var database models.Database
|
||||
var err error
|
||||
if err := c.BindJSON(&database); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if database.Path == "" {
|
||||
database.SetDefaultPath()
|
||||
}
|
||||
|
||||
if database.Token == "" {
|
||||
database.SetDefaultToken()
|
||||
}
|
||||
|
||||
a.DbHandler, err = handlers.NewDatabaseHandler(database.Path, database.Create)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.DbHandler.CreateNewMemberTable(); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.DbHandler.CreateNewAttendanceTable(); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
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{
|
||||
"message": "database opened",
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
63
api/attendanceHandler.go
Normal file
63
api/attendanceHandler.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (a *APIHandler) StartNewEvent(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": "missing query 'name'",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.DbHandler.StartNewEvent(name); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "New Event added " + name,
|
||||
})
|
||||
}
|
||||
|
||||
func (a *APIHandler) GetEventById(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
|
||||
var i int
|
||||
var err error
|
||||
|
||||
id := c.Query("id")
|
||||
if id != "" {
|
||||
i, err = strconv.Atoi(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
events, err := a.DbHandler.GetEvent(i)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, events)
|
||||
}
|
296
api/membersHandler.go
Normal file
296
api/membersHandler.go
Normal file
@@ -0,0 +1,296 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"memberDB/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/saintfish/chardet"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func (a *APIHandler) AddNewMember(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
|
||||
var member models.Member
|
||||
c.BindJSON(&member)
|
||||
|
||||
var text string
|
||||
if member.FirstName == "" {
|
||||
text = "firstName "
|
||||
}
|
||||
if member.LastName == "" {
|
||||
text += "lastName "
|
||||
}
|
||||
if member.Birthday == "" {
|
||||
text += "birthday "
|
||||
}
|
||||
if text != "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": text + "can not be empty",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err := a.DbHandler.AddNewMember(member)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "member added",
|
||||
})
|
||||
}
|
||||
|
||||
func (a *APIHandler) GetMemberById(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
|
||||
var i int
|
||||
var err error
|
||||
|
||||
id := c.Query("id")
|
||||
if id != "" {
|
||||
i, err = strconv.Atoi(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
members, err := a.DbHandler.GetMember(i)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, members)
|
||||
}
|
||||
|
||||
func (a *APIHandler) EditMember(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
|
||||
var i int
|
||||
var err error
|
||||
|
||||
id := c.Query("id")
|
||||
if id != "" {
|
||||
i, err = strconv.Atoi(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": "query parameter 'id' missing",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var member models.Member
|
||||
c.BindJSON(&member)
|
||||
|
||||
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{
|
||||
"message": "member updated",
|
||||
})
|
||||
}
|
||||
|
||||
func (a *APIHandler) DeleteMember(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
|
||||
var request struct {
|
||||
Ids []int `json:"ids"`
|
||||
}
|
||||
err = c.BindJSON(&request)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = a.DbHandler.DeleteMember(request.Ids...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "member deleted",
|
||||
})
|
||||
}
|
||||
|
||||
func (a *APIHandler) ImportCSV(c *gin.Context) {
|
||||
if !a.databaseOpened(c) {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
|
||||
fileHeader, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
rowIndex := c.PostForm("rowIndex")
|
||||
comma := c.PostForm("seperator")
|
||||
firstName := c.PostForm("firstName")
|
||||
lastName := c.PostForm("lastName")
|
||||
birthday := c.PostForm("birthday")
|
||||
address := c.PostForm("address")
|
||||
town := c.PostForm("town")
|
||||
zip := c.PostForm("zip")
|
||||
phone := c.PostForm("phone")
|
||||
email := c.PostForm("email")
|
||||
|
||||
rowIndexI, err := strconv.Atoi(rowIndex)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open uploaded file"})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Read first N bytes to detect encoding
|
||||
const sniffSize = 1024 * 10 // 10KB for detection
|
||||
buf := make([]byte, sniffSize)
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Detect encoding
|
||||
detector := chardet.NewTextDetector()
|
||||
result, err := detector.DetectBest(buf[:n])
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect encoding"})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = file.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to rewind file"})
|
||||
return
|
||||
}
|
||||
|
||||
// Wrap reader based on encoding
|
||||
var reader io.Reader
|
||||
switch result.Charset {
|
||||
case "UTF-8":
|
||||
reader = file
|
||||
case "windows-1252":
|
||||
reader = transform.NewReader(file, charmap.Windows1252.NewDecoder())
|
||||
case "ISO-8859-1":
|
||||
reader = transform.NewReader(file, charmap.ISO8859_1.NewDecoder())
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "Unsupported encoding: " + result.Charset})
|
||||
return
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(reader)
|
||||
csvReader.Comma = rune(comma[0])
|
||||
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse CSV"})
|
||||
return
|
||||
}
|
||||
|
||||
var firstNameIndex, lastNameIndex, birthdayIndex, addressIndex, townIndex, zipIndex, phoneIndex, emailIndex int
|
||||
|
||||
var message string
|
||||
for i, row := range records {
|
||||
if i < rowIndexI {
|
||||
continue
|
||||
} else if i == rowIndexI {
|
||||
for c, column := range row {
|
||||
switch column {
|
||||
case firstName:
|
||||
firstNameIndex = c
|
||||
case lastName:
|
||||
lastNameIndex = c
|
||||
case birthday:
|
||||
birthdayIndex = c
|
||||
case address:
|
||||
addressIndex = c
|
||||
case town:
|
||||
townIndex = c
|
||||
case zip:
|
||||
zipIndex = c
|
||||
case phone:
|
||||
phoneIndex = c
|
||||
case email:
|
||||
emailIndex = c
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if row[firstNameIndex] == "" && row[lastNameIndex] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := a.DbHandler.AddNewMember(models.Member{
|
||||
FirstName: row[firstNameIndex],
|
||||
LastName: row[lastNameIndex],
|
||||
Birthday: row[birthdayIndex],
|
||||
Address: row[addressIndex],
|
||||
Town: row[townIndex],
|
||||
Zip: row[zipIndex],
|
||||
Phone: row[phoneIndex],
|
||||
Email: row[emailIndex],
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
message += "Row: " + fmt.Sprint(i) + " " + row[firstNameIndex] + " " + row[lastNameIndex] + " error: " + err.Error() + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
if message != "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": message,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "CSV imported",
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user