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