Skip to content

Commit 1a138fa

Browse files
committed
Added internet appliance download and sideload
1 parent 5a6abb5 commit 1a138fa

File tree

1 file changed

+206
-16
lines changed

1 file changed

+206
-16
lines changed

src/init.go

Lines changed: 206 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,26 @@ package main
1414

1515
import (
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

2638
func 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

Comments
 (0)