Add data migration export/import endpoints
All checks were successful
Backend Build and Test / build (push) Successful in 40s
All checks were successful
Backend Build and Test / build (push) Successful in 40s
Add GET /api/export to the Go backend that dumps all users, allowances, history, and tasks (including completed) as a single JSON snapshot. Add POST /api/import to the Spring backend that accepts the same JSON, wipes existing data, inserts all records with original IDs preserved via native SQL, and resets PostgreSQL sequences to avoid future collisions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -779,3 +779,59 @@ func (db *Db) TransferAllowance(fromId int, toId int, amount float64) error {
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (db *Db) ExportAllData() (*ExportData, error) {
|
||||
var err error
|
||||
data := &ExportData{
|
||||
Users: make([]ExportUser, 0),
|
||||
Allowances: make([]ExportAllowance, 0),
|
||||
History: make([]ExportHistory, 0),
|
||||
Tasks: make([]ExportTask, 0),
|
||||
}
|
||||
|
||||
for row := range db.db.Query("select id, name, balance, weight from users").Range(&err) {
|
||||
u := ExportUser{}
|
||||
if err = row.Scan(&u.ID, &u.Name, &u.Balance, &u.Weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Users = append(data.Users, u)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for row := range db.db.Query("select id, user_id, name, target, balance, weight, colour from allowances").Range(&err) {
|
||||
a := ExportAllowance{}
|
||||
if err = row.Scan(&a.ID, &a.UserID, &a.Name, &a.Target, &a.Balance, &a.Weight, &a.Colour); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Allowances = append(data.Allowances, a)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for row := range db.db.Query("select id, user_id, timestamp, amount, description from history").Range(&err) {
|
||||
h := ExportHistory{}
|
||||
if err = row.Scan(&h.ID, &h.UserID, &h.Timestamp, &h.Amount, &h.Description); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.History = append(data.History, h)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for row := range db.db.Query("select id, name, reward, assigned, schedule, completed, next_run from tasks").Range(&err) {
|
||||
t := ExportTask{}
|
||||
if err = row.Scan(&t.ID, &t.Name, &t.Reward, &t.Assigned, &t.Schedule, &t.Completed, &t.NextRun); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Tasks = append(data.Tasks, t)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -86,3 +86,45 @@ type TransferRequest struct {
|
||||
To int `json:"to"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
type ExportUser struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Balance int64 `json:"balance"`
|
||||
Weight float64 `json:"weight"`
|
||||
}
|
||||
|
||||
type ExportAllowance struct {
|
||||
ID int `json:"id"`
|
||||
UserID int `json:"userId"`
|
||||
Name string `json:"name"`
|
||||
Target int64 `json:"target"`
|
||||
Balance int64 `json:"balance"`
|
||||
Weight float64 `json:"weight"`
|
||||
Colour *int `json:"colour"`
|
||||
}
|
||||
|
||||
type ExportHistory struct {
|
||||
ID int `json:"id"`
|
||||
UserID int `json:"userId"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Amount int64 `json:"amount"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type ExportTask struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Reward int64 `json:"reward"`
|
||||
Assigned *int `json:"assigned"`
|
||||
Schedule *string `json:"schedule"`
|
||||
Completed *int64 `json:"completed"`
|
||||
NextRun *int64 `json:"nextRun"`
|
||||
}
|
||||
|
||||
type ExportData struct {
|
||||
Users []ExportUser `json:"users"`
|
||||
Allowances []ExportAllowance `json:"allowances"`
|
||||
History []ExportHistory `json:"history"`
|
||||
Tasks []ExportTask `json:"tasks"`
|
||||
}
|
||||
|
||||
@@ -51,6 +51,16 @@ const DefaultDomain = "localhost:8080"
|
||||
// The domain that the server is reachable at.
|
||||
var domain = DefaultDomain
|
||||
|
||||
func exportData(c *gin.Context) {
|
||||
data, err := db.ExportAllData()
|
||||
if err != nil {
|
||||
log.Printf("Error exporting data: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError})
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, data)
|
||||
}
|
||||
|
||||
func getUsers(c *gin.Context) {
|
||||
users, err := db.GetUsers()
|
||||
if err != nil {
|
||||
@@ -713,6 +723,7 @@ func start(ctx context.Context, config *ServerConfig) {
|
||||
router.DELETE("/api/task/:taskId", deleteTask)
|
||||
router.POST("/api/task/:taskId/complete", completeTask)
|
||||
router.POST("/api/transfer", transfer)
|
||||
router.GET("/api/export", exportData)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: config.Addr,
|
||||
|
||||
Reference in New Issue
Block a user