forked from pterodactyl/wings
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservers.go
213 lines (190 loc) · 6.08 KB
/
servers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package remote
import (
"context"
"fmt"
"strconv"
"sync"
"github.com/pterodactyl/wings/internal/models"
"emperror.dev/errors"
"github.com/apex/log"
"golang.org/x/sync/errgroup"
)
const (
ProcessStopCommand = "command"
ProcessStopSignal = "signal"
ProcessStopNativeStop = "stop"
)
// GetServers returns all the servers that are present on the Panel making
// parallel API calls to the endpoint if more than one page of servers is
// returned.
func (c *client) GetServers(ctx context.Context, limit int) ([]RawServerData, error) {
servers, meta, err := c.getServersPaged(ctx, 0, limit)
if err != nil {
return nil, err
}
var mu sync.Mutex
if meta.LastPage > 1 {
g, ctx := errgroup.WithContext(ctx)
for page := meta.CurrentPage + 1; page <= meta.LastPage; page++ {
page := page
g.Go(func() error {
ps, _, err := c.getServersPaged(ctx, int(page), limit)
if err != nil {
return err
}
mu.Lock()
servers = append(servers, ps...)
mu.Unlock()
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
}
return servers, nil
}
// ResetServersState updates the state of all servers on the node that are
// currently marked as "installing" or "restoring from backup" to be marked as
// a normal successful install state.
//
// This handles Wings exiting during either of these processes which will leave
// things in a bad state within the Panel. This API call is executed once Wings
// has fully booted all the servers.
func (c *client) ResetServersState(ctx context.Context) error {
res, err := c.Post(ctx, "/servers/reset", nil)
if err != nil {
return errors.WrapIf(err, "remote: failed to reset server state on Panel")
}
_ = res.Body.Close()
return nil
}
func (c *client) GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error) {
var config ServerConfigurationResponse
res, err := c.Get(ctx, fmt.Sprintf("/servers/%s", uuid), nil)
if err != nil {
return config, err
}
defer res.Body.Close()
err = res.BindJSON(&config)
return config, err
}
func (c *client) GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error) {
res, err := c.Get(ctx, fmt.Sprintf("/servers/%s/install", uuid), nil)
if err != nil {
return InstallationScript{}, err
}
defer res.Body.Close()
var config InstallationScript
err = res.BindJSON(&config)
return config, err
}
func (c *client) SetInstallationStatus(ctx context.Context, uuid string, data InstallStatusRequest) error {
resp, err := c.Post(ctx, fmt.Sprintf("/servers/%s/install", uuid), data)
if err != nil {
return err
}
_ = resp.Body.Close()
return nil
}
func (c *client) SetArchiveStatus(ctx context.Context, uuid string, successful bool) error {
resp, err := c.Post(ctx, fmt.Sprintf("/servers/%s/archive", uuid), d{"successful": successful})
if err != nil {
return err
}
_ = resp.Body.Close()
return nil
}
func (c *client) SetTransferStatus(ctx context.Context, uuid string, successful bool) error {
state := "failure"
if successful {
state = "success"
}
resp, err := c.Post(ctx, fmt.Sprintf("/servers/%s/transfer/%s", uuid, state), nil)
if err != nil {
return err
}
_ = resp.Body.Close()
return nil
}
// ValidateSftpCredentials makes a request to determine if the username and
// password combination provided is associated with a valid server on the instance
// using the Panel's authentication control mechanisms. This will get itself
// throttled if too many requests are made, allowing us to completely offload
// all the authorization security logic to the Panel.
func (c *client) ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error) {
var auth SftpAuthResponse
res, err := c.Post(ctx, "/sftp/auth", request)
if err != nil {
if err := AsRequestError(err); err != nil && (err.StatusCode() >= 400 && err.StatusCode() < 500) {
log.WithFields(log.Fields{"subsystem": "sftp", "username": request.User, "ip": request.IP}).Warn(err.Error())
return auth, &SftpInvalidCredentialsError{}
}
return auth, err
}
defer res.Body.Close()
if err := res.BindJSON(&auth); err != nil {
return auth, err
}
return auth, nil
}
func (c *client) GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error) {
var data BackupRemoteUploadResponse
res, err := c.Get(ctx, fmt.Sprintf("/backups/%s", backup), q{"size": strconv.FormatInt(size, 10)})
if err != nil {
return data, err
}
defer res.Body.Close()
if err := res.BindJSON(&data); err != nil {
return data, err
}
return data, nil
}
func (c *client) SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error {
resp, err := c.Post(ctx, fmt.Sprintf("/backups/%s", backup), data)
if err != nil {
return err
}
_ = resp.Body.Close()
return nil
}
// SendRestorationStatus triggers a request to the Panel to notify it that a
// restoration has been completed and the server should be marked as being
// activated again.
func (c *client) SendRestorationStatus(ctx context.Context, backup string, successful bool) error {
resp, err := c.Post(ctx, fmt.Sprintf("/backups/%s/restore", backup), d{"successful": successful})
if err != nil {
return err
}
_ = resp.Body.Close()
return nil
}
// SendActivityLogs sends activity logs back to the Panel for processing.
func (c *client) SendActivityLogs(ctx context.Context, activity []models.Activity) error {
resp, err := c.Post(ctx, "/activity", d{"data": activity})
if err != nil {
return errors.WithStackIf(err)
}
_ = resp.Body.Close()
return nil
}
// getServersPaged returns a subset of servers from the Panel API using the
// pagination query parameters.
func (c *client) getServersPaged(ctx context.Context, page, limit int) ([]RawServerData, Pagination, error) {
var r struct {
Data []RawServerData `json:"data"`
Meta Pagination `json:"meta"`
}
res, err := c.Get(ctx, "/servers", q{
"page": strconv.Itoa(page),
"per_page": strconv.Itoa(limit),
})
if err != nil {
return nil, r.Meta, err
}
defer res.Body.Close()
if err := res.BindJSON(&r); err != nil {
return nil, r.Meta, err
}
return r.Data, r.Meta, nil
}