1package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "net/url"
9 "strconv"
10
11 "github.com/bluesky-social/indigo/xrpc"
12 cli "github.com/urfave/cli/v2"
13)
14
15var bgsAdminCmd = &cli.Command{
16 Name: "bgs",
17 Usage: "sub-commands for administering a BGS",
18 Flags: []cli.Flag{
19 &cli.StringFlag{
20 Name: "key",
21 EnvVars: []string{"BGS_ADMIN_KEY"},
22 },
23 &cli.StringFlag{
24 Name: "bgs",
25 Value: "http://localhost:2470",
26 },
27 },
28 Subcommands: []*cli.Command{
29 bgsListUpstreamsCmd,
30 bgsKickConnectionCmd,
31 bgsListDomainBansCmd,
32 bgsBanDomainCmd,
33 bgsTakedownRepoCmd,
34 bgsSetNewSubsEnabledCmd,
35 bgsCompactRepo,
36 bgsCompactAll,
37 bgsResetRepo,
38 },
39}
40
41var bgsListUpstreamsCmd = &cli.Command{
42 Name: "list",
43 Action: func(cctx *cli.Context) error {
44 url := cctx.String("bgs") + "/admin/subs/getUpstreamConns"
45 req, err := http.NewRequest("GET", url, nil)
46 if err != nil {
47 return err
48 }
49
50 auth := cctx.String("key")
51 req.Header.Set("Authorization", "Bearer "+auth)
52
53 resp, err := http.DefaultClient.Do(req)
54 if err != nil {
55 return err
56 }
57
58 if resp.StatusCode != 200 {
59 var e xrpc.XRPCError
60 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
61 return err
62 }
63
64 return &e
65 }
66
67 var out []string
68 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
69 return err
70 }
71
72 for _, h := range out {
73 fmt.Println(h)
74 }
75
76 return nil
77 },
78}
79
80var bgsKickConnectionCmd = &cli.Command{
81 Name: "kick",
82 Usage: "tell Relay/BGS to drop the subscription connection",
83 ArgsUsage: "<host>",
84 Flags: []cli.Flag{
85 &cli.BoolFlag{
86 Name: "ban",
87 Usage: "make the disconnect sticky",
88 },
89 },
90 Action: func(cctx *cli.Context) error {
91 uu := cctx.String("bgs") + "/admin/subs/killUpstream?host="
92
93 uu += url.QueryEscape(cctx.Args().First())
94
95 if cctx.Bool("ban") {
96 uu += "&block=true"
97 }
98
99 req, err := http.NewRequest("POST", uu, nil)
100 if err != nil {
101 return err
102 }
103
104 auth := cctx.String("key")
105 req.Header.Set("Authorization", "Bearer "+auth)
106
107 resp, err := http.DefaultClient.Do(req)
108 if err != nil {
109 return err
110 }
111
112 if resp.StatusCode != 200 {
113 var e xrpc.XRPCError
114 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
115 return err
116 }
117
118 return &e
119 }
120
121 var out map[string]any
122 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
123 return err
124 }
125
126 fmt.Println(out)
127
128 return nil
129 },
130}
131
132var bgsListDomainBansCmd = &cli.Command{
133 Name: "list-domain-bans",
134 Action: func(cctx *cli.Context) error {
135 url := cctx.String("bgs") + "/admin/subs/listDomainBans"
136 req, err := http.NewRequest("GET", url, nil)
137 if err != nil {
138 return err
139 }
140
141 auth := cctx.String("key")
142 req.Header.Set("Authorization", "Bearer "+auth)
143
144 resp, err := http.DefaultClient.Do(req)
145 if err != nil {
146 return err
147 }
148
149 if resp.StatusCode != 200 {
150 var e xrpc.XRPCError
151 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
152 return err
153 }
154
155 return &e
156 }
157
158 var out []string
159 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
160 return err
161 }
162
163 for _, h := range out {
164 fmt.Println(h)
165 }
166
167 return nil
168 },
169}
170
171var bgsBanDomainCmd = &cli.Command{
172 Name: "ban-domain",
173 ArgsUsage: "<domain>",
174 Action: func(cctx *cli.Context) error {
175 url := cctx.String("bgs") + "/admin/subs/banDomain"
176
177 b, err := json.Marshal(map[string]string{
178 "domain": cctx.Args().First(),
179 })
180 if err != nil {
181 return err
182 }
183
184 req, err := http.NewRequest("POST", url, bytes.NewReader(b))
185 if err != nil {
186 return err
187 }
188
189 req.Header.Set("Content-Type", "application/json")
190
191 auth := cctx.String("key")
192 req.Header.Set("Authorization", "Bearer "+auth)
193
194 resp, err := http.DefaultClient.Do(req)
195 if err != nil {
196 return err
197 }
198
199 if resp.StatusCode != 200 {
200 var e xrpc.XRPCError
201 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
202 return err
203 }
204
205 return &e
206 }
207
208 var out map[string]any
209 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
210 return err
211 }
212
213 fmt.Println(out)
214
215 return nil
216 },
217}
218
219var bgsTakedownRepoCmd = &cli.Command{
220 Name: "take-down-repo",
221 ArgsUsage: "<did>",
222 Action: func(cctx *cli.Context) error {
223 url := cctx.String("bgs") + "/admin/repo/takeDown"
224
225 b, err := json.Marshal(map[string]string{
226 "did": cctx.Args().First(),
227 })
228 if err != nil {
229 return err
230 }
231
232 req, err := http.NewRequest("POST", url, bytes.NewReader(b))
233 if err != nil {
234 return err
235 }
236
237 req.Header.Set("Content-Type", "application/json")
238
239 auth := cctx.String("key")
240 req.Header.Set("Authorization", "Bearer "+auth)
241
242 resp, err := http.DefaultClient.Do(req)
243 if err != nil {
244 return err
245 }
246
247 if resp.StatusCode != 200 {
248 var e xrpc.XRPCError
249 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
250 return err
251 }
252
253 return &e
254 }
255
256 var out map[string]any
257 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
258 return err
259 }
260
261 fmt.Println(out)
262
263 return nil
264 },
265}
266
267var bgsSetNewSubsEnabledCmd = &cli.Command{
268 Name: "set-accept-subs",
269 ArgsUsage: "<boolean>",
270 Usage: "set configuration for whether new subscriptions are allowed",
271 Action: func(cctx *cli.Context) error {
272 url := cctx.String("bgs") + "/admin/subs/setEnabled"
273
274 bv, err := strconv.ParseBool(cctx.Args().First())
275 if err != nil {
276 return err
277 }
278
279 url += fmt.Sprintf("?enabled=%v", bv)
280
281 req, err := http.NewRequest("POST", url, nil)
282 if err != nil {
283 return err
284 }
285
286 auth := cctx.String("key")
287 req.Header.Set("Authorization", "Bearer "+auth)
288
289 resp, err := http.DefaultClient.Do(req)
290 if err != nil {
291 return err
292 }
293
294 if resp.StatusCode != 200 {
295 var e xrpc.XRPCError
296 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
297 return err
298 }
299
300 return &e
301 }
302
303 var out map[string]any
304 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
305 return err
306 }
307
308 fmt.Println(out)
309
310 return nil
311 },
312}
313
314var bgsCompactRepo = &cli.Command{
315 Name: "compact-repo",
316 ArgsUsage: "<did>",
317 Flags: []cli.Flag{
318 &cli.BoolFlag{
319 Name: "fast",
320 },
321 },
322 Action: func(cctx *cli.Context) error {
323 uu, err := url.Parse(cctx.String("bgs") + "/admin/repo/compact")
324 if err != nil {
325 return err
326 }
327
328 q := uu.Query()
329 did := cctx.Args().First()
330 q.Add("did", did)
331
332 if cctx.Bool("fast") {
333 q.Add("fast", "true")
334 }
335
336 uu.RawQuery = q.Encode()
337
338 req, err := http.NewRequest("POST", uu.String(), nil)
339 if err != nil {
340 return err
341 }
342
343 auth := cctx.String("key")
344 req.Header.Set("Authorization", "Bearer "+auth)
345
346 resp, err := http.DefaultClient.Do(req)
347 if err != nil {
348 return err
349 }
350
351 if resp.StatusCode != 200 {
352 var e xrpc.XRPCError
353 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
354 return err
355 }
356
357 return &e
358 }
359
360 var out map[string]any
361 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
362 return err
363 }
364
365 fmt.Println(out)
366
367 return nil
368 },
369}
370
371var bgsCompactAll = &cli.Command{
372 Name: "compact-all",
373 Flags: []cli.Flag{
374 &cli.BoolFlag{
375 Name: "dry",
376 },
377 &cli.IntFlag{
378 Name: "limit",
379 },
380 &cli.IntFlag{
381 Name: "threshold",
382 },
383 &cli.BoolFlag{
384 Name: "fast",
385 },
386 },
387 Action: func(cctx *cli.Context) error {
388 uu, err := url.Parse(cctx.String("bgs") + "/admin/repo/compactAll")
389 if err != nil {
390 return err
391 }
392
393 q := uu.Query()
394 if cctx.Bool("dry") {
395 q.Add("dry", "true")
396 }
397
398 if cctx.Bool("fast") {
399 q.Add("fast", "true")
400 }
401
402 if cctx.IsSet("limit") {
403 q.Add("limit", fmt.Sprint(cctx.Int("limit")))
404 }
405
406 if cctx.IsSet("threshold") {
407 q.Add("threshold", fmt.Sprint(cctx.Int("threshold")))
408 }
409
410 uu.RawQuery = q.Encode()
411
412 req, err := http.NewRequest("POST", uu.String(), nil)
413 if err != nil {
414 return err
415 }
416
417 auth := cctx.String("key")
418 req.Header.Set("Authorization", "Bearer "+auth)
419
420 resp, err := http.DefaultClient.Do(req)
421 if err != nil {
422 return err
423 }
424
425 if resp.StatusCode != 200 {
426 var e xrpc.XRPCError
427 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
428 return err
429 }
430
431 return &e
432 }
433
434 var out map[string]any
435 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
436 return err
437 }
438
439 fmt.Println(out)
440
441 return nil
442 },
443}
444
445var bgsResetRepo = &cli.Command{
446 Name: "reset-repo",
447 ArgsUsage: "<did>",
448 Action: func(cctx *cli.Context) error {
449 url := cctx.String("bgs") + "/admin/repo/reset"
450
451 did := cctx.Args().First()
452 url += fmt.Sprintf("?did=%s", did)
453
454 req, err := http.NewRequest("POST", url, nil)
455 if err != nil {
456 return err
457 }
458
459 auth := cctx.String("key")
460 req.Header.Set("Authorization", "Bearer "+auth)
461
462 resp, err := http.DefaultClient.Do(req)
463 if err != nil {
464 return err
465 }
466
467 if resp.StatusCode != 200 {
468 var e xrpc.XRPCError
469 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
470 return err
471 }
472
473 return &e
474 }
475
476 var out map[string]any
477 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
478 return err
479 }
480
481 fmt.Println(out)
482
483 return nil
484 },
485}
486
487var bgsSetTrustedDomains = &cli.Command{
488 Name: "set-trusted-domain",
489 Action: func(cctx *cli.Context) error {
490 url := cctx.String("bgs") + "/admin/pds/addTrustedDomain"
491
492 domain := cctx.Args().First()
493 url += fmt.Sprintf("?domain=%s", domain)
494
495 req, err := http.NewRequest("POST", url, nil)
496 if err != nil {
497 return err
498 }
499
500 auth := cctx.String("key")
501 req.Header.Set("Authorization", "Bearer "+auth)
502
503 resp, err := http.DefaultClient.Do(req)
504 if err != nil {
505 return err
506 }
507
508 if resp.StatusCode != 200 {
509 var e xrpc.XRPCError
510 if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
511 return err
512 }
513
514 return &e
515 }
516
517 var out map[string]any
518 if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
519 return err
520 }
521
522 fmt.Println(out)
523
524 return nil
525 },
526}