Skip to content

Commit 1c20be6

Browse files
committed
RuntimeConfig
1 parent d34dad6 commit 1c20be6

File tree

7 files changed

+236
-22
lines changed

7 files changed

+236
-22
lines changed

cmd/config/update.go

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type Settable interface {
2525
BeforeSet(paths Segments, obj any) error
2626
}
2727

28+
type AfterSettable interface {
29+
AfterSet(paths Segments, obj any)
30+
}
31+
2832
type Saver interface {
2933
CanSave()
3034
}
@@ -55,21 +59,36 @@ func NewUpdater(ptr any) *Updater {
5559

5660
func (u *Updater) MustApply(path string, value string, save func(path string, value string)) {
5761
segments := u.parse(path)
58-
saver, saveSegmentIndex, settable, settableSegments, p := u.find(u.obj, nil, -1, nil, -1, segments, 0)
62+
saver, saveSegmentIndex, settable, settableSegments, p := u.find(u.obj, segments, 0, nil, -1, nil, -1)
5963

6064
// 创建值的副本,在设置之前先检查是否合法。
6165
new := reflect.New(reflect.TypeOf(p).Elem())
6266
u.set(new.Interface(), value)
67+
newVal := new.Elem().Interface()
6368

64-
if settable != nil {
65-
if err := settable.BeforeSet(segments[settableSegments+1:], new.Elem().Interface()); err != nil {
69+
if saver == nil {
70+
saver, _ = u.obj.(Saver)
71+
if saver == nil {
72+
panic("尝试修改的值找不到存储者。")
73+
}
74+
}
75+
76+
if settable == nil {
77+
settable, _ = u.obj.(Settable)
78+
}
79+
80+
if bs, ok := settable.(Settable); ok {
81+
if err := bs.BeforeSet(segments[settableSegments+1:], newVal); err != nil {
6682
panic(err)
6783
}
6884
}
6985

70-
if saver == nil {
71-
panic("尝试修改的值找不到存储者。")
86+
u.set(p, value)
87+
88+
if as, ok := settable.(AfterSettable); ok {
89+
as.AfterSet(segments[settableSegments+1:], newVal)
7290
}
91+
7392
var saverPath string
7493
for i := 0; i <= saveSegmentIndex; i++ {
7594
if i != saveSegmentIndex && segments[i].Index != nil {
@@ -81,8 +100,6 @@ func (u *Updater) MustApply(path string, value string, save func(path string, va
81100
saverPath += segments[i].Key
82101
}
83102

84-
u.set(p, value)
85-
86103
if b, err := json.Marshal(saver); err != nil {
87104
panic(err)
88105
} else {
@@ -170,22 +187,24 @@ func (u *Updater) parse(path string) []Segment {
170187

171188
func (u *Updater) Find(path string) any {
172189
segments := u.parse(path)
173-
_, _, _, _, p := u.find(u.obj, nil, -1, nil, -1, segments, 0)
190+
_, _, _, _, p := u.find(u.obj, segments, 0, nil, -1, nil, -1)
174191
return p
175192
}
176193

177-
func (u *Updater) find(obj any, saver Saver, saverSegment int, settable Settable, settableSegments int, segments []Segment, index int) (Saver, int, Settable, int, any) {
178-
value := reflect.ValueOf(obj)
194+
// 在 obj 对象中查找 segments 依次对应的元素。
195+
// save 用于记录谁可以保存此元素。
196+
// settable 用于记录谁可以校验此元素。
197+
// 如果 saver, settable 为 nil,则有可能是 obj 本身。
198+
func (u *Updater) find(obj any, segments []Segment, index int, saver Saver, saverSegment int, settable any, settableSegments int) (Saver, int, any, int, any) {
199+
if len(segments[index:]) < 1 {
200+
return saver, saverSegment, settable, settableSegments, obj
201+
}
179202

180-
// 不是也行,那就是简单赋值了,无意义。
203+
value := reflect.ValueOf(obj)
181204
if value.Type().Kind() != reflect.Pointer {
182205
panic(`expect pointer`)
183206
}
184207

185-
if len(segments[index:]) < 1 {
186-
return saver, saverSegment, settable, settableSegments, obj
187-
}
188-
189208
seg := segments[index]
190209

191210
// 必须通过 tag 拿,否则可能出现 json、yaml 不一致。
@@ -226,14 +245,17 @@ func (u *Updater) find(obj any, saver Saver, saverSegment int, settable Settable
226245
}
227246
ownerField = field
228247
field = field.MapIndex(reflect.ValueOf(index))
248+
} else {
249+
// ownerField = value.Elem()
229250
}
230251

231252
if !field.IsValid() {
232253
panic(`invalid field: ` + seg.Key)
233254
}
234255

235256
if ownerField.IsValid() {
236-
if reflect.PointerTo(ownerField.Type()).Implements(reflect.TypeOf((*Settable)(nil)).Elem()) {
257+
if reflect.PointerTo(ownerField.Type()).Implements(reflect.TypeOf((*Settable)(nil)).Elem()) ||
258+
reflect.PointerTo(ownerField.Type()).Implements(reflect.TypeOf((*AfterSettable)(nil)).Elem()) {
237259
reflect.ValueOf(&settable).Elem().Set(ownerField.Addr())
238260
settableSegments = index
239261
}
@@ -245,7 +267,8 @@ func (u *Updater) find(obj any, saver Saver, saverSegment int, settable Settable
245267
saverSegment = index
246268
}
247269
}
248-
if reflect.PointerTo(field.Type()).Implements(reflect.TypeOf((*Settable)(nil)).Elem()) {
270+
if reflect.PointerTo(field.Type()).Implements(reflect.TypeOf((*Settable)(nil)).Elem()) ||
271+
reflect.PointerTo(field.Type()).Implements(reflect.TypeOf((*AfterSettable)(nil)).Elem()) {
249272
reflect.ValueOf(&settable).Elem().Set(field.Addr())
250273
settableSegments = index
251274
}
@@ -257,10 +280,10 @@ func (u *Updater) find(obj any, saver Saver, saverSegment int, settable Settable
257280
saverSegment = index
258281
}
259282

260-
return u.find(field.Addr().Interface(), saver, saverSegment, settable, settableSegments, segments, index+1)
283+
return u.find(field.Addr().Interface(), segments, index+1, saver, saverSegment, settable, settableSegments)
261284
}
262285

263-
func (u *Updater) set(p any, value string) {
286+
func (*Updater) set(p any, value string) {
264287
vpe := reflect.ValueOf(p).Elem()
265288

266289
value = strings.TrimRight(value, "\n")

cmd/config/update_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ type C struct {
7070

7171
func (C) CanSave() {}
7272

73+
func (C) AfterSet(paths config.Segments, obj any) {
74+
log.Println(`AfterSet:`, paths, obj)
75+
}
76+
7377
func TestSaver(t *testing.T) {
7478
var c Config
7579
updater := config.NewUpdater(&c)
@@ -92,3 +96,36 @@ func TestSaver(t *testing.T) {
9296
log.Println(`将会保存:`, path, obj)
9397
})
9498
}
99+
100+
type Set struct {
101+
A int `yaml:"a"`
102+
}
103+
104+
func (Set) CanSave() {}
105+
func (s *Set) BeforeSet(paths config.Segments, obj any) error {
106+
if paths.At(0).Key != `a` {
107+
panic(`expect a`)
108+
}
109+
n, ok := obj.(int)
110+
if !ok || n != 123 {
111+
panic(`expect 123`)
112+
}
113+
return nil
114+
}
115+
func (s *Set) AfterSet(paths config.Segments, obj any) {
116+
if paths.At(0).Key != `a` {
117+
panic(`expect a`)
118+
}
119+
n, ok := obj.(int)
120+
if !ok || n != 123 {
121+
panic(`expect 123`)
122+
}
123+
}
124+
125+
func TestSaver2(t *testing.T) {
126+
var a Set
127+
updater := config.NewUpdater(&a)
128+
updater.MustApply(`a`, `123`, func(path, value string) {
129+
t.Logf(`保存:%s: %s`, path, value)
130+
})
131+
}

cmd/server/main.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"github.com/movsb/taoblog/service/modules/notify/instant"
4242
"github.com/movsb/taoblog/service/modules/notify/mailer"
4343
"github.com/movsb/taoblog/service/modules/request_throttler"
44+
runtime_config "github.com/movsb/taoblog/service/modules/runtime"
4445
"github.com/movsb/taoblog/service/modules/storage"
4546
"github.com/movsb/taoblog/setup/migration"
4647
"github.com/movsb/taoblog/theme"
@@ -153,6 +154,9 @@ func (s *Server) Serve(ctx context.Context, testing bool, cfg *config.Config, re
153154
ctx, cancel := context.WithCancel(ctx)
154155
defer cancel()
155156

157+
rc := runtime_config.NewRuntime()
158+
ctx = runtime_config.Context(ctx, rc)
159+
156160
db := InitDatabase(cfg.Database.Posts, InitForPosts(s.createFirstPost))
157161
defer db.Close()
158162

@@ -176,7 +180,8 @@ func (s *Server) Serve(ctx context.Context, testing bool, cfg *config.Config, re
176180
filesStore := theme_fs.FS(storage.NewSQLite(InitDatabase(cfg.Database.Files, InitForFiles())))
177181
notify := s.createNotifyService(ctx, db, cfg, serviceRegistrar)
178182
s.notify = notify
179-
theService := s.createMainServices(ctx, db, cfg, serviceRegistrar, notify, cancel, theAuth, testing, filesStore)
183+
184+
theService := s.createMainServices(ctx, db, cfg, serviceRegistrar, notify, cancel, theAuth, testing, filesStore, rc)
180185
s.main = theService
181186

182187
go startGRPC()
@@ -289,6 +294,11 @@ func (s *Server) createGitSyncTasks(
289294
ctx context.Context,
290295
client *clients.ProtoClient,
291296
) {
297+
if version.DevMode() {
298+
log.Println(`开发模式不运行 git 同步`)
299+
return
300+
}
301+
292302
ctx = clients.ContextFrom(ctx, fmt.Sprintf(`%d:%s`, auth.SystemID, auth.SystemKey))
293303
gs := backups_git.New(ctx, client, false)
294304

@@ -312,6 +322,11 @@ func (s *Server) createGitSyncTasks(
312322
case <-ctx.Done():
313323
log.Println(`git 同步任务退出`)
314324
return
325+
case <-gs.Do():
326+
log.Println(`立即执行同步中`)
327+
if err := sync(); err != nil {
328+
log.Println(err)
329+
}
315330
case <-ticker.C:
316331
if err := sync(); err != nil {
317332
log.Println(err)
@@ -369,6 +384,7 @@ func (s *Server) createMainServices(
369384
auth *auth.Auth,
370385
testing bool,
371386
filesStore theme_fs.FS,
387+
rc *runtime_config.Runtime,
372388
) *service.Service {
373389
serviceOptions := []service.With{
374390
// service.WithThemeRootFileSystem(),
@@ -380,7 +396,7 @@ func (s *Server) createMainServices(
380396

381397
addons.New()
382398

383-
return service.New(ctx, sr, cfg, db, auth, serviceOptions...)
399+
return service.New(ctx, sr, cfg, db, rc, auth, serviceOptions...)
384400
}
385401

386402
func (s *Server) createNotifyService(ctx context.Context, db *sql.DB, cfg *config.Config, sr grpc.ServiceRegistrar) proto.NotifyServer {

modules/backups/git/git.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import (
1717
"github.com/go-git/go-git/v5/plumbing/transport"
1818
"github.com/go-git/go-git/v5/plumbing/transport/http"
1919
client_common "github.com/movsb/taoblog/cmd/client/common"
20+
"github.com/movsb/taoblog/cmd/config"
2021
"github.com/movsb/taoblog/modules/utils"
2122
"github.com/movsb/taoblog/modules/version"
2223
"github.com/movsb/taoblog/protocols/clients"
2324
"github.com/movsb/taoblog/protocols/go/proto"
2425
"github.com/movsb/taoblog/service/models"
26+
runtime_config "github.com/movsb/taoblog/service/modules/runtime"
2527
)
2628

2729
// 多少时间范围内的更新不应该被检测到。或者说:
@@ -31,6 +33,7 @@ const skewedDurationForUpdating = time.Minute * 15
3133

3234
type GitSync struct {
3335
ctx context.Context
36+
rc *RuntimeConfig
3437
proto *clients.ProtoClient
3538

3639
// 上一次获取更新的时间。
@@ -49,22 +52,49 @@ type GitSync struct {
4952
tmpDir string
5053
}
5154

55+
type RuntimeConfig struct {
56+
SyncNow bool `yaml:"sync_now"`
57+
58+
syncNow chan bool
59+
60+
config.Saver
61+
}
62+
63+
func (c *RuntimeConfig) AfterSet(paths config.Segments, obj any) {
64+
switch paths.At(0).Key {
65+
case `sync_now`:
66+
c.syncNow <- obj.(bool)
67+
}
68+
}
69+
5270
// full: 初次备份是否需要全量扫描备份。如果不设置,则默认为最近 7 天。
5371
func New(ctx context.Context, client *clients.ProtoClient, full bool) *GitSync {
5472
lastCheckedAt := time.Unix(0, 0)
5573
if !full {
5674
lastCheckedAt = time.Now().Add(-7 * time.Hour * 24)
5775
}
5876

77+
rc := &RuntimeConfig{
78+
syncNow: make(chan bool),
79+
}
80+
if r := runtime_config.FromContext(ctx); r != nil {
81+
r.Register(`git`, rc)
82+
}
83+
5984
return &GitSync{
6085
ctx: ctx,
86+
rc: rc,
6187
proto: client,
6288

6389
lastCheckedAt: lastCheckedAt,
6490
pathCache: map[int]string{},
6591
}
6692
}
6793

94+
func (g *GitSync) Do() <-chan bool {
95+
return g.rc.syncNow
96+
}
97+
6898
// 内部会自动大量重试因为网络问题导致的错误。
6999
func (g *GitSync) Sync() error {
70100
const MaxRetry = 20

service/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/movsb/taoblog/service/modules/renderers/exif"
2525
"github.com/movsb/taoblog/service/modules/renderers/friends"
2626
"github.com/movsb/taoblog/service/modules/renderers/reminders"
27+
runtime_config "github.com/movsb/taoblog/service/modules/runtime"
2728
"github.com/movsb/taoblog/service/modules/search"
2829
theme_fs "github.com/movsb/taoblog/theme/modules/fs"
2930
"github.com/movsb/taorm"
@@ -64,6 +65,7 @@ type Service struct {
6465
cfg *config.Config
6566

6667
options utils.PluginStorage
68+
runtime *runtime_config.Runtime
6769

6870
// 服务端渲染的时候一些模块会要求解析 URL(包含相对文件和绝对文件),
6971
// 所以需要用到主题相关的根文件系统。
@@ -130,13 +132,14 @@ func (s *Service) ThemeChangedAt() time.Time {
130132
return s.themeChangedAt
131133
}
132134

133-
func New(ctx context.Context, sr grpc.ServiceRegistrar, cfg *config.Config, db *sql.DB, auther *auth.Auth, options ...With) *Service {
135+
func New(ctx context.Context, sr grpc.ServiceRegistrar, cfg *config.Config, db *sql.DB, rc *runtime_config.Runtime, auther *auth.Auth, options ...With) *Service {
134136
s := &Service{
135137
ctx: ctx,
136138

137139
notifier: &proto.UnimplementedNotifyServer{},
138140

139141
cfg: cfg,
142+
runtime: rc,
140143
themeRootFS: embed.FS{},
141144
postDataFS: &theme_fs.Empty{},
142145

0 commit comments

Comments
 (0)