blob: 01161df45ed0f5186c24e2e60eb958a993a08763 [file] [log] [blame]
gio36b23b32024-08-25 12:20:54 +04001package installer
2
3import (
4 "bytes"
gio864b4332024-09-05 13:56:47 +04005 "encoding/json"
gio36b23b32024-08-25 12:20:54 +04006 "errors"
7 "fmt"
8 "io"
9 "net/http"
gio864b4332024-09-05 13:56:47 +040010 "net/url"
gio36b23b32024-08-25 12:20:54 +040011)
12
gio864b4332024-09-05 13:56:47 +040013type VPNAPIClient interface {
14 GenerateAuthKey(username string) (string, error)
15 ExpireKey(username, key string) error
16 ExpireNode(username, node string) error
17 RemoveNode(username, node string) error
gio36b23b32024-08-25 12:20:54 +040018}
19
20type headscaleAPIClient struct {
gio864b4332024-09-05 13:56:47 +040021 c *http.Client
gio36b23b32024-08-25 12:20:54 +040022 apiAddr string
23}
24
gio864b4332024-09-05 13:56:47 +040025func NewHeadscaleAPIClient(apiAddr string) VPNAPIClient {
26 return &headscaleAPIClient{
27 &http.Client{},
28 apiAddr,
29 }
gio36b23b32024-08-25 12:20:54 +040030}
31
gio864b4332024-09-05 13:56:47 +040032func (g *headscaleAPIClient) GenerateAuthKey(username string) (string, error) {
gio36b23b32024-08-25 12:20:54 +040033 resp, err := http.Post(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username), "application/json", nil)
34 if err != nil {
35 return "", err
36 }
37 var buf bytes.Buffer
38 io.Copy(&buf, resp.Body)
39 if resp.StatusCode != http.StatusOK {
40 return "", errors.New(buf.String())
41 }
42 return buf.String(), nil
43}
gio864b4332024-09-05 13:56:47 +040044
45type expirePreAuthKeyReq struct {
46 AuthKey string `json:"authKey"`
47}
48
49func (g *headscaleAPIClient) ExpireKey(username, key string) error {
50 addr, err := url.Parse(fmt.Sprintf("%s/user/%s/preauthkey", g.apiAddr, username))
51 if err != nil {
52 return err
53 }
54 var buf bytes.Buffer
55 if err := json.NewEncoder(&buf).Encode(expirePreAuthKeyReq{key}); err != nil {
56 return err
57 }
58 resp, err := g.c.Do(&http.Request{
59 URL: addr,
60 Method: http.MethodDelete,
61 Body: io.NopCloser(&buf),
62 })
63 if err != nil {
64 return err
65 }
66 if resp.StatusCode != http.StatusOK {
67 var buf bytes.Buffer
68 io.Copy(&buf, resp.Body)
69 return errors.New(buf.String())
70 }
71 return nil
72}
73
74func (g *headscaleAPIClient) ExpireNode(username, node string) error {
75 resp, err := g.c.Post(
76 fmt.Sprintf("%s/user/%s/node/%s/expire", g.apiAddr, username, node),
77 "text/plain",
78 nil,
79 )
80 if err != nil {
81 return err
82 }
83 if resp.StatusCode != http.StatusOK {
84 var buf bytes.Buffer
85 io.Copy(&buf, resp.Body)
86 return errors.New(buf.String())
87 }
88 return nil
89}
90
91func (g *headscaleAPIClient) RemoveNode(username, node string) error {
92 addr, err := url.Parse(fmt.Sprintf("%s/user/%s/node/%s", g.apiAddr, username, node))
93 if err != nil {
94 return err
95 }
96 resp, err := g.c.Do(&http.Request{
97 URL: addr,
98 Method: http.MethodDelete,
99 Body: nil,
100 })
101 if err != nil {
102 return err
103 }
104 if resp.StatusCode != http.StatusOK {
105 var buf bytes.Buffer
106 io.Copy(&buf, resp.Body)
107 return errors.New(buf.String())
108 }
109 return nil
110}