embed.go

 1package plugins
 2
 3import (
 4	"encoding/json"
 5	"fmt"
 6	"io"
 7	"net/http"
 8
 9	"github.com/floatpane/matcha/internal/httpclient"
10)
11
12const RegistryURL = "https://raw.githubusercontent.com/floatpane/matcha/master/plugins/registry.json"
13const RawPluginBaseURL = "https://raw.githubusercontent.com/floatpane/matcha/master/plugins/"
14
15// PluginEntry represents a single plugin in the registry.
16type PluginEntry struct {
17	Name        string `json:"name"`
18	Title       string `json:"title"`
19	Description string `json:"description"`
20	File        string `json:"file"`
21	URL         string `json:"url,omitempty"`
22}
23
24// FetchRegistry fetches the plugin registry from GitHub.
25func FetchRegistry() ([]PluginEntry, error) {
26	client := httpclient.New(httpclient.RegistryFetchTimeout)
27	resp, err := client.Get(RegistryURL) //nolint:noctx
28	if err != nil {
29		return nil, fmt.Errorf("failed to fetch registry: %w", err)
30	}
31	defer resp.Body.Close() //nolint:errcheck
32
33	if resp.StatusCode != http.StatusOK {
34		return nil, fmt.Errorf("registry returned status %d", resp.StatusCode)
35	}
36
37	body, err := io.ReadAll(resp.Body)
38	if err != nil {
39		return nil, fmt.Errorf("failed to read registry: %w", err)
40	}
41
42	var entries []PluginEntry
43	if err := json.Unmarshal(body, &entries); err != nil {
44		return nil, fmt.Errorf("failed to parse registry: %w", err)
45	}
46	return entries, nil
47}
48
49// FetchPlugin downloads a plugin file. If the entry has a URL, it downloads
50// from there; otherwise it falls back to the default repo location.
51func FetchPlugin(entry PluginEntry) ([]byte, error) {
52	url := entry.URL
53	if url == "" {
54		url = RawPluginBaseURL + entry.File
55	}
56
57	client := httpclient.New(httpclient.RegistryFetchTimeout)
58	resp, err := client.Get(url) //nolint:noctx
59	if err != nil {
60		return nil, fmt.Errorf("failed to fetch plugin: %w", err)
61	}
62	defer resp.Body.Close() //nolint:errcheck
63
64	if resp.StatusCode != http.StatusOK {
65		return nil, fmt.Errorf("plugin download returned status %d", resp.StatusCode)
66	}
67
68	return io.ReadAll(resp.Body)
69}