forked from toolshed/abra
This allows AppsReadFS/AppsReadWeb to be used behind the scenes of this API for the conditional loading logic. All functions are left as public for now while we're experimenting.
184 lines
3.8 KiB
Go
184 lines
3.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"time"
|
|
|
|
"coopcloud.tech/abra/config"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
type Image struct {
|
|
Image string `json:"image"`
|
|
Rating string `json:"rating"`
|
|
Source string `json:"source"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
type Feature struct {
|
|
Backups string `json:"backups"`
|
|
Email string `json:"email"`
|
|
Healthcheck string `json:"healthcheck"`
|
|
Image Image `json:"image"`
|
|
Status int `json:"status"`
|
|
Tests string `json:"tests"`
|
|
}
|
|
|
|
type Version struct {
|
|
Digest string `json:"digest"`
|
|
Image string `json:"image"`
|
|
Tag string `json:"tag"`
|
|
}
|
|
|
|
type App struct {
|
|
Category string `json:"category"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
Description string `json:"description"`
|
|
Features Feature `json:"features"`
|
|
Icon string `json:"icon"`
|
|
Name string `json:"name"`
|
|
Repository string `json:"repository"`
|
|
Versions map[string]map[string]Version `json:"versions"`
|
|
Website string `json:"website"`
|
|
}
|
|
|
|
type Apps map[string]App
|
|
|
|
var httpClient = &http.Client{Timeout: 5 * time.Second}
|
|
|
|
var AppsUrl = "https://apps.coopcloud.tech"
|
|
|
|
func readJson(url string, target interface{}) error {
|
|
res, err := httpClient.Get(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
return json.NewDecoder(res.Body).Decode(target)
|
|
}
|
|
|
|
func AppsFSIsLatest() (bool, error) {
|
|
res, err := httpClient.Head(AppsUrl)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
lastModified := res.Header["Last-Modified"][0]
|
|
parsed, err := time.Parse(time.RFC1123, lastModified)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
info, err := os.Stat(config.APPS_JSON)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
localModifiedTime := info.ModTime().Unix()
|
|
remoteModifiedTime := parsed.Unix()
|
|
|
|
if localModifiedTime < remoteModifiedTime {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func ReadApps() (Apps, error) {
|
|
apps := make(Apps)
|
|
|
|
appsFSIsLatest, err := AppsFSIsLatest()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !appsFSIsLatest {
|
|
if err := ReadAppsWeb(&apps); err != nil {
|
|
return nil, err
|
|
}
|
|
return apps, nil
|
|
}
|
|
|
|
if err := ReadAppsFS(&apps); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return apps, nil
|
|
}
|
|
|
|
func ReadAppsFS(target interface{}) error {
|
|
appsJsonFS, err := ioutil.ReadFile(config.APPS_JSON)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Unmarshal(appsJsonFS, &target); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReadAppsWeb(target interface{}) error {
|
|
if err := readJson(AppsUrl, &target); err != nil {
|
|
return err
|
|
}
|
|
|
|
appsJson, err := json.MarshalIndent(target, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ioutil.WriteFile(config.APPS_JSON, appsJson, 0644); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func SortByAppName(apps Apps) []string {
|
|
var names []string
|
|
for name := range apps {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|
|
|
|
var recipeListCommand = &cli.Command{
|
|
Name: "list",
|
|
Aliases: []string{"ls"},
|
|
Action: func(c *cli.Context) error {
|
|
apps, err := ReadApps()
|
|
if err != nil {
|
|
logrus.Fatal(err.Error())
|
|
}
|
|
tableCol := []string{"Name", "Category", "Status"}
|
|
table := createTable(tableCol)
|
|
for _, appName := range SortByAppName(appSpecs) {
|
|
appSpec := appSpecs[appName]
|
|
status := fmt.Sprintf("%v", appSpec.Features.Status)
|
|
tableRow := []string{appSpec.Name, appSpec.Category, status}
|
|
table.Append(tableRow)
|
|
}
|
|
table.Render()
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var RecipeCommand = &cli.Command{
|
|
Name: "recipe",
|
|
HideHelp: true,
|
|
Subcommands: []*cli.Command{
|
|
recipeListCommand,
|
|
},
|
|
}
|