Go implementation of pdsadmin cli

feat: add requestCrawl command

+99
+97
pdsadmin/cmd/requestCrawl.go
··· 1 + /* 2 + Copyright © 2025 QuietEngineer <qtengineer@proton.me> 3 + 4 + Permission is hereby granted, free of charge, to any person obtaining a copy 5 + of this software and associated documentation files (the "Software"), to deal 6 + in the Software without restriction, including without limitation the rights 7 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 + copies of the Software, and to permit persons to whom the Software is 9 + furnished to do so, subject to the following conditions: 10 + 11 + The above copyright notice and this permission notice shall be included in 12 + all copies or substantial portions of the Software. 13 + 14 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 + THE SOFTWARE. 21 + */ 22 + package cmd 23 + 24 + import ( 25 + "bytes" 26 + "encoding/json" 27 + "fmt" 28 + "io" 29 + "net/http" 30 + "os" 31 + "strings" 32 + 33 + "github.com/spf13/cobra" 34 + "github.com/spf13/viper" 35 + ) 36 + 37 + type SyncRequestCrawl_Input struct { 38 + // hostname: Hostname of the current service (eg, PDS) that is requesting to be crawled. 39 + Hostname string `json:"hostname"` 40 + } 41 + 42 + // requestCrawlCmd represents the requestCrawl command 43 + var requestCrawlCmd = &cobra.Command{ 44 + Use: "requestCrawl", 45 + Short: "Request a crawl from a relay host", 46 + Example: `pdsadmin requestCrawl bsky.network 47 + pdsadmin requestCrawl bsky.network,second-relay.example.com`, 48 + Args: cobra.MaximumNArgs(1), 49 + Run: func(cmd *cobra.Command, args []string) { 50 + var relayHosts []string 51 + if len(args) == 1 { 52 + relayHosts = strings.Split(args[0], ",") 53 + } else { 54 + relayHosts = viper.GetStringSlice("crawlers") 55 + } 56 + if len(relayHosts) == 0 { 57 + fmt.Println("ERROR: missing RELAY HOST parameter") 58 + os.Exit(1) 59 + } 60 + 61 + client := &http.Client{} 62 + for _, host := range relayHosts { 63 + if host == "" { 64 + continue 65 + } 66 + fmt.Printf("Requesting crawl from %s\n", host) 67 + if !strings.HasPrefix(host, "https:") && !strings.HasPrefix(host, "http:") { 68 + host = fmt.Sprintf("https://%s", host) 69 + } 70 + body := SyncRequestCrawl_Input{ 71 + Hostname: viper.GetString("hostname"), 72 + } 73 + jsonBody, err := json.Marshal(body) 74 + if err != nil { 75 + fmt.Printf("could not create json body: %s\n", err) 76 + continue 77 + } 78 + bodyReader := bytes.NewReader(jsonBody) 79 + 80 + res, err := client.Post(fmt.Sprintf("%s/xrpc/com.atproto.sync.requestCrawl", host), "application/json", bodyReader) 81 + if err != nil { 82 + fmt.Printf("error making http request: %s\n", err) 83 + continue 84 + } 85 + 86 + if _, err := io.ReadAll(res.Body); err != nil { 87 + fmt.Printf("could not read response body: %s\n", err) 88 + continue 89 + } 90 + } 91 + fmt.Println("done") 92 + }, 93 + } 94 + 95 + func init() { 96 + rootCmd.AddCommand(requestCrawlCmd) 97 + }
+2
pdsadmin/sample.config.yaml
··· 1 1 admin_password: a_secure_password 2 2 hostname: pds.example.com # without protocol (https) or path 3 + crawlers: 4 + - bsky.network