@@ -129,6 +129,73 @@ func UpdateUrbitConfig(inputConfig map[string]structs.UrbitDocker) error {
129129 return nil
130130}
131131
132+ func ReplaceUrbitConfigJSON (pier string , raw []byte ) ([]byte , error ) {
133+ var configMap map [string ]any
134+ if err := json .Unmarshal (raw , & configMap ); err != nil {
135+ return nil , fmt .Errorf ("invalid %s config JSON: %v" , pier , err )
136+ }
137+ if len (configMap ) == 0 {
138+ return nil , fmt .Errorf ("refusing to persist empty configuration for pier %s" , pier )
139+ }
140+ formatted , err := json .MarshalIndent (configMap , "" , " " )
141+ if err != nil {
142+ return nil , fmt .Errorf ("error encoding %s config: %v" , pier , err )
143+ }
144+ var targetStruct structs.UrbitDocker
145+ if err := unmarshalUrbitDockerSafe (formatted , & targetStruct ); err != nil {
146+ return nil , err
147+ }
148+ if targetStruct .PierName != "" && targetStruct .PierName != pier {
149+ return nil , fmt .Errorf ("pier_name %q does not match %q" , targetStruct .PierName , pier )
150+ }
151+ if targetStruct .StartramReminder == nil {
152+ targetStruct .StartramReminder = defaults .UrbitConfig .StartramReminder
153+ }
154+ if targetStruct .SnapTime == 0 {
155+ targetStruct .SnapTime = 60
156+ }
157+ structs .SyncCustomS3Domains (& targetStruct )
158+
159+ urbitMutex .Lock ()
160+ defer urbitMutex .Unlock ()
161+ path := filepath .Join (BasePath , "settings" , "pier" , pier + ".json" )
162+ if err := os .MkdirAll (filepath .Dir (path ), os .ModePerm ); err != nil {
163+ return nil , err
164+ }
165+ tmpFile , err := os .CreateTemp (filepath .Dir (path ), pier + ".json.*" )
166+ if err != nil {
167+ return nil , fmt .Errorf ("error creating temp file: %v" , err )
168+ }
169+ tmpPath := tmpFile .Name ()
170+ defer os .Remove (tmpPath )
171+ if _ , err := tmpFile .Write (formatted ); err != nil {
172+ tmpFile .Close ()
173+ return nil , fmt .Errorf ("error writing temp file: %v" , err )
174+ }
175+ if err := tmpFile .Close (); err != nil {
176+ return nil , fmt .Errorf ("error closing temp file: %v" , err )
177+ }
178+ if fi , err := os .Stat (tmpPath ); err != nil {
179+ return nil , fmt .Errorf ("error checking temp file: %v" , err )
180+ } else if fi .Size () == 0 {
181+ return nil , fmt .Errorf ("refusing to persist empty configuration for pier %s" , pier )
182+ }
183+ UrbitsConfig [pier ] = targetStruct
184+ if err := os .Rename (tmpPath , path ); err != nil {
185+ return nil , fmt .Errorf ("error moving temp file: %v" , err )
186+ }
187+ return formatted , nil
188+ }
189+
190+ func unmarshalUrbitDockerSafe (data []byte , target * structs.UrbitDocker ) (err error ) {
191+ defer func () {
192+ if r := recover (); r != nil {
193+ err = fmt .Errorf ("invalid urbit config: %v" , r )
194+ }
195+ }()
196+ return json .Unmarshal (data , target )
197+ }
198+
132199func UpdateUrbitConfigForPier (pier string , mutate func (* structs.UrbitDocker )) error {
133200 if err := LoadUrbitConfig (pier ); err != nil {
134201 return err
0 commit comments