Skip to content

Commit e0e697f

Browse files
committed
feat: add schema_version table for future migrations
1 parent 1593076 commit e0e697f

File tree

13 files changed

+154
-32
lines changed

13 files changed

+154
-32
lines changed

cmd/observer/app.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131

3232
cleaner_close_database "github.com/anyshake/observer/internal/hook/cleaner/close_database"
3333
cleaner_close_explorer "github.com/anyshake/observer/internal/hook/cleaner/close_explorer"
34+
startup_migrate_database "github.com/anyshake/observer/internal/hook/startup/migrate_database"
3435
startup_setup_admin "github.com/anyshake/observer/internal/hook/startup/setup_admin"
3536
startup_setup_station "github.com/anyshake/observer/internal/hook/startup/setup_station"
3637

@@ -68,7 +69,7 @@ func appStart(ver *semver.Version, build *unibuild.UniBuild, args arguments) {
6869
if err := conf.Parse(args.configPath, "json"); err != nil {
6970
logger.GetLogger(main).Fatalln(err)
7071
}
71-
if err := migrateConfig(conf); err != nil {
72+
if err := conf.Migrate(logger.GetLogger(main)); err != nil {
7273
logger.GetLogger(main).Fatalln(err)
7374
}
7475
logger.GetLogger(main).Info("global configuration has been loaded")
@@ -114,15 +115,16 @@ func appStart(ver *semver.Version, build *unibuild.UniBuild, args arguments) {
114115
}
115116
logger.GetLogger(main).Info("database connection has been established")
116117

117-
if err = daoObj.Migrate(
118+
if err = daoObj.AutoMigrate(
119+
&model.SchemaVersion{},
118120
&model.SeisRecord{},
119121
&model.SysUser{},
120122
&model.UserSettings{},
121123
); err != nil {
122124
logger.GetLogger(main).Fatalln(err)
123125
}
124126
logger.GetLogger(main).Info("database schema has been configured")
125-
actionHandler := action.New(daoObj)
127+
actionHandler := action.NewHandler(daoObj)
126128

127129
var hardwareDevice hardware.IHardware
128130

@@ -142,6 +144,9 @@ func appStart(ver *semver.Version, build *unibuild.UniBuild, args arguments) {
142144

143145
stationConfigConstraints := config.NewStationConstraints()
144146
startupTasks := []hook.IHook{
147+
&startup_migrate_database.MigrateDatabaseStartupImpl{
148+
ActionHandler: actionHandler,
149+
},
145150
&startup_setup_station.SetupStationStartupImpl{
146151
ActionHandler: actionHandler,
147152
StationConfigConstraints: stationConfigConstraints,

cmd/observer/migrate.go

Lines changed: 0 additions & 17 deletions
This file was deleted.

config/base.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
"github.com/go-playground/validator/v10"
7+
"github.com/sirupsen/logrus"
78
"github.com/spf13/viper"
89
)
910

@@ -21,7 +22,7 @@ type hardware struct {
2122
}
2223

2324
type ntpClient struct {
24-
// Endpoint is a deprecated field since v4.2.0, use Pool instead
25+
// Deprecated: Endpoint is a deprecated field since v4.2.0, use Pool instead
2526
Endpoint string `mapstructure:"endpoint"`
2627
Timeout int `mapstructure:"timeout" validate:"gte=0"`
2728
Retry int `mapstructure:"retry" validate:"gte=0"`
@@ -79,3 +80,14 @@ func (c *BaseConfig) Parse(configPath, configType string) error {
7980

8081
return nil
8182
}
83+
84+
func (cfg *BaseConfig) Migrate(logger *logrus.Entry) error {
85+
// 2025-08-22: starting from v4.2.0, NTP Client configuration has deprecated the `endpoint` field.
86+
if cfg.NtpClient.Endpoint != "" && len(cfg.NtpClient.Pool) == 0 {
87+
cfg.NtpClient.Pool = []string{cfg.NtpClient.Endpoint}
88+
cfg.NtpClient.Endpoint = ""
89+
logger.Warnln("configuration field `ntpclient.endpoint` has been deprecated, please use the `ntpclient.pool` field instead")
90+
}
91+
92+
return nil
93+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ import "github.com/anyshake/observer/internal/dao"
55
type Handler struct {
66
daoObj *dao.DAO
77
}
8+
9+
func NewHandler(daoObj *dao.DAO) *Handler {
10+
return &Handler{daoObj}
11+
}

internal/dao/action/new.go

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package action
2+
3+
import (
4+
"time"
5+
6+
"github.com/anyshake/observer/internal/dao/model"
7+
"gorm.io/gorm"
8+
)
9+
10+
func (h *Handler) SchemaVersionInit() error {
11+
var schemaVersion model.SchemaVersion
12+
13+
var count int64
14+
if err := h.daoObj.Database.
15+
Table(schemaVersion.GetName(h.daoObj.GetPrefix())).
16+
Model(&schemaVersion).
17+
Count(&count).
18+
Error; err != nil {
19+
return err
20+
}
21+
22+
if count == 0 {
23+
return h.daoObj.Database.
24+
Table(schemaVersion.GetName(h.daoObj.GetPrefix())).
25+
Create(&model.SchemaVersion{
26+
Version: 1,
27+
AppliedAt: 0,
28+
Description: "Initial schema since v4.3.4",
29+
}).
30+
Error
31+
}
32+
33+
return nil
34+
}
35+
36+
func (h *Handler) SchemaVersionGetCurrent() (int, error) {
37+
var sv model.SchemaVersion
38+
if err := h.daoObj.Database.
39+
Table(sv.GetName(h.daoObj.GetPrefix())).
40+
First(&sv).
41+
Error; err != nil {
42+
return 0, err
43+
}
44+
45+
return sv.Version, nil
46+
}
47+
48+
func (h *Handler) SchemaVersionUpdate(oldVer, newVer int, description string) error {
49+
var sv model.SchemaVersion
50+
51+
result := h.daoObj.Database.
52+
Table(sv.GetName(h.daoObj.GetPrefix())).
53+
Model(&sv).
54+
Where("id = ? AND version = ?", 1, oldVer).
55+
Updates(map[string]any{
56+
"version": newVer,
57+
"description": description,
58+
"applied_at": time.Now().UnixMilli(),
59+
})
60+
if result.Error != nil {
61+
return result.Error
62+
}
63+
64+
if result.RowsAffected == 0 {
65+
return gorm.ErrInvalidData
66+
}
67+
68+
return nil
69+
}

internal/dao/action/sys_users.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (h *Handler) SysUserLogin(username, password, userAgent, userIp string) (us
129129
user.LastLogin = time.Now().UnixMilli()
130130
user.UserAgent = userAgent
131131
user.UserIp = userIp
132-
err = h.SysUserUpdte(user.UserId, user)
132+
err = h.SysUserUpdate(user.UserId, user)
133133
if err != nil {
134134
return "", fmt.Errorf("failed to update login status: %w", err)
135135
}
@@ -138,7 +138,7 @@ func (h *Handler) SysUserLogin(username, password, userAgent, userIp string) (us
138138
return user.UserId, nil
139139
}
140140

141-
func (h *Handler) SysUserUpdte(userId string, user model.SysUser) error {
141+
func (h *Handler) SysUserUpdate(userId string, user model.SysUser) error {
142142
if h.daoObj == nil {
143143
return errors.New("database is not opened")
144144
}

internal/dao/migrate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"fmt"
55
)
66

7-
func (d *DAO) Migrate(tables ...ITable) error {
7+
func (d *DAO) AutoMigrate(tables ...ITable) error {
88
if d.Database == nil {
99
return fmt.Errorf("database is not opened")
1010
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package model
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/anyshake/observer/internal/dao"
7+
"gorm.io/gorm"
8+
)
9+
10+
type SchemaVersion struct {
11+
dao.BaseTable
12+
13+
Version int `gorm:"column:version;not null"`
14+
Description string `gorm:"column:description;not null"`
15+
AppliedAt int64 `gorm:"column:applied_at;not null"`
16+
}
17+
18+
func (t *SchemaVersion) GetModel() any {
19+
return &SchemaVersion{}
20+
}
21+
22+
func (t *SchemaVersion) GetName(tablePrefix string) string {
23+
return fmt.Sprintf("%s%s", tablePrefix, "schema_version")
24+
}
25+
26+
func (t *SchemaVersion) UseAutoMigrate() bool {
27+
return true
28+
}
29+
30+
func (t *SchemaVersion) AddPlugins(dbObj *gorm.DB, tablePrefix string) ([]gorm.Plugin, error) {
31+
return nil, nil
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package migrate_database
2+
3+
func (t *MigrateDatabaseStartupImpl) Execute() error {
4+
if err := t.ActionHandler.SchemaVersionInit(); err != nil {
5+
return err
6+
}
7+
8+
// Manual database schema migrations goes here.
9+
// ... will be implemented in the future
10+
11+
return nil
12+
}

0 commit comments

Comments
 (0)