@@ -14,14 +14,26 @@ package main
1414
1515import (
1616 "runtime"
17+ "encoding/json"
1718 "io"
1819 "fmt"
1920 "os"
2021 "os/exec"
22+ "archive/tar"
23+ "compress/gzip"
24+ "net/http"
2125 "path/filepath"
2226 "log"
2327)
2428
29+ type Appliance struct {
30+ Title string `json:"title"`
31+ Short string `json:"short"`
32+ Version int `json:"version"`
33+ Description string `json:"description"`
34+ Requirements string `json:"requirements"`
35+ Action string `json:"action"`
36+ }
2537
2638func copyFile (src , dst string ) error {
2739 sourceFileStat , err := os .Stat (src )
@@ -116,20 +128,50 @@ func setupInitialFiles() {
116128 if appData == "" {
117129 appData = filepath .Join (homeDir , "AppData" , "Local" )
118130 }
119- configPath = filepath .Join (appData , "ipv6rs" , "appliances" )
131+ configPath = filepath .Join (appData , "ipv6rs" )
120132 } else {
121- configPath = filepath .Join (homeDir , ".ipv6rs" , "appliances" )
133+ configPath = filepath .Join (homeDir , ".ipv6rs" )
122134 }
135+ appliancePath := filepath .Join (configPath , "appliances" )
123136
124- if err := os .MkdirAll (configPath , os .ModePerm ); err != nil {
137+ if err := os .MkdirAll (appliancePath , os .ModePerm ); err != nil {
125138 log .Fatalf ("Failed to create directory: %v" , err )
126139 }
127140
128- srcConfigFile := filepath .Join (resourcesPath , "appliances.json" )
129- destConfigFile := filepath .Join (configPath , ".." , "appliances.json" )
141+ url := "https://raw.githubusercontent.com/ipv6rslimited/cloudseeder-appliances/main/appliances.json"
142+
143+ applianceConfig , err := downloadFile (url )
144+ if err != nil {
145+ log .Fatalf ("Unable to get the main appliance config: %v" , err )
146+ }
147+ defer applianceConfig .Close ()
130148
131- if err := copyFile (srcConfigFile , destConfigFile ); err != nil {
132- log .Fatalf ("Failed to copy main init file: %v" , err )
149+ buffer , err := io .ReadAll (applianceConfig )
150+ if err != nil {
151+ log .Fatalf ("Unable to read the main appliance config: %v" , err )
152+ }
153+
154+ if err := saveDownloadedFile (buffer , filepath .Join (configPath , "appliances.json" )); err != nil {
155+ log .Fatalf ("Unable to save the main appliance config: %v" , err )
156+ }
157+
158+ appliances , err := parseApplianceConfig (buffer )
159+ if err != nil {
160+ log .Fatalf ("Unable to parse the main appliance config: %v" , err )
161+ }
162+
163+ if err := downloadAppliances (appliances , appliancePath ); err != nil {
164+ log .Fatalf ("Failed to download appliances: %v" , err )
165+ }
166+
167+ sideloadedApps , err := loadJSONFile (filepath .Join (configPath , "sideload.json" ))
168+ if err == nil {
169+ appliances = mergeAppliances (appliances , sideloadedApps )
170+ if err := saveAppliances (appliances , filepath .Join (configPath , "appliances.json" )); err != nil {
171+ log .Fatalf ("Failed to save merged appliances: %v" , err )
172+ }
173+ } else {
174+ log .Println ("No sideload.json found or error reading: " , err )
133175 }
134176
135177 backup := "backup"
@@ -146,9 +188,9 @@ func setupInitialFiles() {
146188 srcBackupFile := filepath .Join (resourcesPath , backup )
147189 srcCheckerFile := filepath .Join (resourcesPath , checker )
148190 srcUpgradeFile := filepath .Join (resourcesPath , upgrade )
149- destBackupFile := filepath .Join (configPath , ".." , backup )
150- destCheckerFile := filepath .Join (configPath , ".." , checker )
151- destUpgradeFile := filepath .Join (configPath , ".." , upgrade )
191+ destBackupFile := filepath .Join (configPath , backup )
192+ destCheckerFile := filepath .Join (configPath , checker )
193+ destUpgradeFile := filepath .Join (configPath , upgrade )
152194
153195 if err := copyFile (srcBackupFile , destBackupFile ); err != nil {
154196 log .Fatalf ("Failed to copy backup file: %v" , err )
@@ -175,17 +217,165 @@ func setupInitialFiles() {
175217 }
176218 }
177219
178- if err := copyDir (filepath .Join (resourcesPath , "appliances" ), configPath ); err != nil {
220+ iconPath := filepath .Join (configPath , "icons" )
221+ if err := os .MkdirAll (iconPath , os .ModePerm ); err != nil {
222+ log .Fatalf ("Failed to create directory: %v" , err )
223+ }
224+ if err := copyDir (filepath .Join (resourcesPath , "icons" ), iconPath ); err != nil {
179225 log .Fatalf ("Failed to copy files: %v" , err )
180226 }
227+ }
181228
182- iconPath := filepath .Join (configPath , ".." , "icons" )
183- if err := os .MkdirAll (iconPath , os .ModePerm ); err != nil {
184- log .Fatalf ("Failed to create directory: %v" , err )
229+ func downloadFile (url string ) (io.ReadCloser , error ) {
230+ response , err := http .Get (url )
231+ if err != nil {
232+ return nil , fmt .Errorf ("failed to download file: %w" , err )
185233 }
186234
235+ if response .StatusCode != 200 {
236+ response .Body .Close ()
237+ return nil , fmt .Errorf ("received non-200 status code: %d" , response .StatusCode )
238+ }
187239
188- if err := copyDir (filepath .Join (resourcesPath , "icons" ), iconPath ); err != nil {
189- log .Fatalf ("Failed to copy files: %v" , err )
240+ return response .Body , nil
241+ }
242+
243+ func saveDownloadedFile (data []byte , dst string ) error {
244+ out , err := os .Create (dst )
245+ if err != nil {
246+ return fmt .Errorf ("failed to create file: %w" , err )
247+ }
248+ defer out .Close ()
249+
250+ _ , err = out .Write (data )
251+ if err != nil {
252+ return fmt .Errorf ("failed to save file: %w" , err )
253+ }
254+ return nil
255+ }
256+
257+ func parseApplianceConfig (data []byte ) ([]Appliance , error ) {
258+ var appliances []Appliance
259+ if err := json .Unmarshal (data , & appliances ); err != nil {
260+ return nil , fmt .Errorf ("failed to decode JSON: %w" , err )
261+ }
262+ return appliances , nil
263+ }
264+
265+ func downloadAppliances (appliances []Appliance , baseDir string ) error {
266+ baseURL := "https://raw.githubusercontent.com/ipv6rslimited/cloudseeder-appliances/main/"
267+ for _ , app := range appliances {
268+ fmt .Printf ("Downloading and installing %s...\n " , app .Title )
269+ fileURL := baseURL + app .Short + ".cloudseeder"
270+ tempFile := filepath .Join (os .TempDir (), app .Short + ".tgz" )
271+
272+ fileData , err := downloadFile (fileURL )
273+ if err != nil {
274+ log .Fatalf ("Error downloading %s: %v\n " , app .Title , err )
275+ }
276+
277+ buffer , err := io .ReadAll (fileData )
278+ if err != nil {
279+ log .Fatalf ("Error reading %s: %v\n " , app .Title , err )
280+ }
281+ fileData .Close ()
282+
283+ if err := saveDownloadedFile (buffer , tempFile ); err != nil {
284+ log .Fatalf ("Error saving %s: %v\n " , app .Title , err )
285+ continue
286+ }
287+
288+ extractPath := filepath .Join (baseDir )
289+ if err := untarGz (tempFile , extractPath ); err != nil {
290+ log .Fatalf ("Error extracting %s: %v\n " , app .Title , err )
291+ continue
292+ }
293+ fmt .Printf ("%s downloaded and installed.\n " , app .Title )
294+ }
295+ return nil
296+ }
297+
298+ func untarGz (src , dst string ) error {
299+ file , err := os .Open (src )
300+ if err != nil {
301+ return err
302+ }
303+ defer file .Close ()
304+
305+ gzr , err := gzip .NewReader (file )
306+ if err != nil {
307+ return err
308+ }
309+ defer gzr .Close ()
310+
311+ tr := tar .NewReader (gzr )
312+
313+ for {
314+ header , err := tr .Next ()
315+ switch {
316+ case err == io .EOF :
317+ return nil
318+ case err != nil :
319+ return err
320+ case header == nil :
321+ continue
322+ }
323+ target := filepath .Join (dst , header .Name )
324+ switch header .Typeflag {
325+ case tar .TypeDir :
326+ if err := os .MkdirAll (target , 0755 ); err != nil {
327+ return err
328+ }
329+ case tar .TypeReg :
330+ outFile , err := os .Create (target )
331+ if err != nil {
332+ return err
333+ }
334+ if _ , err := io .Copy (outFile , tr ); err != nil {
335+ outFile .Close ()
336+ return err
337+ }
338+ outFile .Close ()
339+ }
340+ }
341+ }
342+
343+ func loadJSONFile (filePath string ) ([]Appliance , error ) {
344+ var appliances []Appliance
345+ file , err := os .Open (filePath )
346+ if err != nil {
347+ return nil , err
348+ }
349+ defer file .Close ()
350+
351+ if err := json .NewDecoder (file ).Decode (& appliances ); err != nil {
352+ return nil , err
353+ }
354+ return appliances , nil
355+ }
356+
357+ func mergeAppliances (main , sideload []Appliance ) []Appliance {
358+ existing := make (map [string ]int )
359+ for index , app := range main {
360+ existing [app .Short ] = index
361+ }
362+ for _ , app := range sideload {
363+ if index , found := existing [app .Short ]; found {
364+ main [index ] = app
365+ } else {
366+ main = append (main , app )
367+ }
368+ }
369+ return main
370+ }
371+
372+ func saveAppliances (appliances []Appliance , filePath string ) error {
373+ file , err := os .Create (filePath )
374+ if err != nil {
375+ return err
190376 }
377+ defer file .Close ()
378+ encoder := json .NewEncoder (file )
379+ encoder .SetIndent ("" , " " )
380+ return encoder .Encode (appliances )
191381}
0 commit comments