-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
132 lines (115 loc) · 3.03 KB
/
main.go
File metadata and controls
132 lines (115 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"time"
"github.com/billingcat/crm/controller"
"github.com/billingcat/crm/model"
"github.com/pelletier/go-toml/v2"
// migrate imports
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/database/sqlite3"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
func loadConfig() (*model.Config, error) {
fmt.Println("Read config file: config.toml")
data, err := os.ReadFile("config.toml")
if err != nil {
return nil, err
}
cfg := &model.Config{}
if err := toml.Unmarshal(data, cfg); err != nil {
return nil, err
}
// if base directory is not a directory, return an error
fi, err := os.Stat(cfg.Basedir)
// fallback to current working directory
if err != nil {
cfg.Basedir, err = os.Getwd()
if err != nil {
return nil, err
}
} else if !fi.IsDir() {
cfg.Basedir, err = os.Getwd()
if err != nil {
return nil, err
}
}
fmt.Println("Use base dir:", cfg.Basedir)
return cfg, nil
}
// runMigrations applies all pending migrations.
// In development it runs automatically; in production only when explicitly requested.
func runMigrations(cfg *model.Config) {
src := "file://" + filepath.ToSlash(migrationsDir())
dsn := migrateDSN(cfg)
m, err := migrate.New(src, dsn)
if err != nil {
log.Fatalf("migration setup failed: %v", err)
}
defer func() { _, _ = m.Close() }()
for {
v, dirty, verr := m.Version()
if verr == migrate.ErrNilVersion {
v = 0
dirty = false
} else if verr != nil {
log.Fatalf("read migration version failed: %v", verr)
}
log.Printf("▶ applying next migration (current version=%d, dirty=%v)", v, dirty)
err := m.Steps(1)
if err == migrate.ErrNoChange {
log.Println("migrations applied")
return
}
// Some versions of golang-migrate report "no more migrations"
// as something like "file does not exist" / ErrUnknownVersion instead.
// You can treat that as "we're done" as well:
if errors.Is(err, os.ErrNotExist) {
log.Println("no further migrations – done")
return
}
if err != nil {
log.Fatalf("❌ migration step starting from version %d failed: %v", v, err)
}
}
}
func main() {
var maintenance bool
var migrateOnly bool
flag.BoolVar(&maintenance, "maintenance", false, "run maintenance tasks and exit")
flag.BoolVar(&migrateOnly, "migrate", false, "run database migrations and exit")
flag.Parse()
cfg, err := loadConfig()
if err != nil {
log.Fatal(err)
}
// Automatically migrate in dev; explicit in prod
if cfg.Mode == "development" && !migrateOnly {
runMigrations(cfg)
} else if migrateOnly {
runMigrations(cfg)
return
}
s, err := model.InitDatabase(cfg)
if err != nil {
log.Fatal(err)
}
if maintenance {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
if err := model.RunMaintenance(ctx, s); err != nil {
log.Fatal(err)
}
return
}
if err := controller.NewController(s); err != nil {
log.Fatal(err)
}
}