Skip to content

Commit

Permalink
feat: add System API support
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Feb 12, 2024
1 parent 58e8751 commit 8bd672d
Show file tree
Hide file tree
Showing 16 changed files with 552 additions and 3 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ issues:
linters:
- funlen
- gosec
- path: mailinabox_test.go
linters:
- unparam
text: "testHandler` - `statusCode` always receives `http.StatusOK` \\(`200`\\)"
9 changes: 9 additions & 0 deletions fixtures/system/getSystemBackupConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"enc_pw_file": "/home/user-data/backup/secret_key.txt",
"file_target_directory": "/home/user-data/backup/encrypted",
"min_age_in_days": 3,
"ssh_pub_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDb root@box.example.com\\n",
"target": "s3://s3.eu-central-1.amazonaws.com/box-example-com",
"target_user": "string",
"target_pass": "string"
}
15 changes: 15 additions & 0 deletions fixtures/system/getSystemBackupStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"backups": [
{
"date": "20200801T023706Z",
"date_delta": "15 hours, 40 minutes",
"date_str": "2020-08-01 03:37:06 BST",
"deleted_in": "approx. 6 days",
"full": false,
"size": 125332,
"volumes": 1
}
],
"unmatched_file_size": 0,
"error": "Something is wrong with the backup"
}
1 change: 1 addition & 0 deletions fixtures/system/getSystemPrivacyStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
false
1 change: 1 addition & 0 deletions fixtures/system/getSystemRebootStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
17 changes: 17 additions & 0 deletions fixtures/system/getSystemStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"type": "heading",
"text": "System",
"extra": []
},
{
"type": "warning",
"text": "This domain's DNSSEC DS record is not set",
"extra": [
{
"monospace": false,
"text": "Digest Type: 2 / SHA-25"
}
]
}
]
2 changes: 2 additions & 0 deletions fixtures/system/getSystemUpdates.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
libgnutls30 (3.5.18-1ubuntu1.4)
libxau6 (1:1.0.8-1ubuntu1)
1 change: 1 addition & 0 deletions fixtures/system/getSystemUpstreamVersion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.47
1 change: 1 addition & 0 deletions fixtures/system/getSystemVersion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.46
1 change: 1 addition & 0 deletions fixtures/system/rebootSystem.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No reboot is required, so it is not allowed.
1 change: 1 addition & 0 deletions fixtures/system/updateSystemBackupConfig.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OK
3 changes: 3 additions & 0 deletions fixtures/system/updateSystemPackages.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Calculating upgrade...
The following packages will be upgraded:
cloud-init grub-common
1 change: 1 addition & 0 deletions fixtures/system/updateSystemPrivacy.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OK
8 changes: 5 additions & 3 deletions mailinabox.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ type Client struct {

common service // Reuse a single struct instead of allocating one for each service on the heap.

DNS *DNSService
User *UserService
Mail *MailService
DNS *DNSService
User *UserService
Mail *MailService
System *SystemService
}

// NewClient creates a new Client.
Expand All @@ -48,6 +49,7 @@ func NewClient(apiURL, email, password string) (*Client, error) {
client.DNS = (*DNSService)(&client.common)
client.User = (*UserService)(&client.common)
client.Mail = (*MailService)(&client.common)
client.System = (*SystemService)(&client.common)

return client, nil
}
Expand Down
299 changes: 299 additions & 0 deletions system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
package mailinabox

import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
)

// SystemStatus Represents a system status.
type SystemStatus struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
Extra []ExtraStatus `json:"extra,omitempty"`
}

// ExtraStatus Represents extra status.
type ExtraStatus struct {
Monospace bool `json:"monospace,omitempty"`
Text string `json:"text,omitempty"`
}

// BackupStatus Represents a backup status.
type BackupStatus struct {
Backups []Backup `json:"backups,omitempty"`
UnmatchedFileSize int `json:"unmatched_file_size,omitempty"`
Error string `json:"error,omitempty"`
}

// Backup Represents a backup.
type Backup struct {
Date string `json:"date,omitempty"`
DateDelta string `json:"date_delta,omitempty"`
DateStr string `json:"date_str,omitempty"`
DeletedIn string `json:"deleted_in,omitempty"`
Full bool `json:"full,omitempty"`
Size int `json:"size,omitempty"`
Volumes int `json:"volumes,omitempty"`
}

// BackupConfig Represents a backup configuration.
type BackupConfig struct {
EncPwFile string `json:"enc_pw_file,omitempty"`
FileTargetDirectory string `json:"file_target_directory,omitempty"`
MinAgeInDays int `json:"min_age_in_days,omitempty"`
SSHPubKey string `json:"ssh_pub_key,omitempty"`
Target string `json:"target,omitempty"`
TargetUser string `json:"target_user,omitempty"`
TargetPass string `json:"target_pass,omitempty"`
}

// SystemService System API.
// https://mailinabox.email/api-docs.html#tag/System
type SystemService service

// GetStatus Returns an array of statuses which can include headings.
// https://mailinabox.email/api-docs.html#operation/getSystemStatus
func (s *SystemService) GetStatus(ctx context.Context) ([]SystemStatus, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "status")

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), http.NoBody)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

var results []SystemStatus

err = s.client.doJSON(req, &results)
if err != nil {
return nil, err
}

return results, nil
}

// GetVersion Returns installed Mail-in-a-Box version.
// https://mailinabox.email/api-docs.html#operation/getSystemVersion
func (s *SystemService) GetVersion(ctx context.Context) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "version")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}

// GetUpstreamVersion Returns Mail-in-a-Box upstream version.
// https://mailinabox.email/api-docs.html#operation/getSystemUpstreamVersion
func (s *SystemService) GetUpstreamVersion(ctx context.Context) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "latest-upstream-version")

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), http.NoBody)
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}

// GetUpdates Returns system (apt) updates.
// https://mailinabox.email/api-docs.html#operation/getSystemUpdates
func (s *SystemService) GetUpdates(ctx context.Context) ([]string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "updates")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

resp, err := s.client.doPlain(req)
if err != nil {
return nil, err
}

updates := strings.Split(strings.TrimSpace(string(resp)), "\n")

return updates, nil
}

// UpdatePackages Updates system (apt) packages.
// https://mailinabox.email/api-docs.html#operation/getSystemUpdates
func (s *SystemService) UpdatePackages(ctx context.Context) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "update-packages")

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), http.NoBody)
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}

// GetPrivacyStatus Returns system privacy (new-version check) status.
// https://mailinabox.email/api-docs.html#operation/getSystemPrivacyStatus
func (s *SystemService) GetPrivacyStatus(ctx context.Context) (bool, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "privacy")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return false, fmt.Errorf("unable to create request: %w", err)
}

var result bool

err = s.client.doJSON(req, &result)
if err != nil {
return false, err
}

return result, nil
}

// UpdatePrivacyStatus Updates system privacy (new-version checks).
// - value: `private`: Disable new version checks
// - value: `off`: Enable new version checks
// https://mailinabox.email/api-docs.html#operation/updateSystemPrivacy
func (s *SystemService) UpdatePrivacyStatus(ctx context.Context, value string) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "privacy")

data := url.Values{}
data.Set("value", value)

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), strings.NewReader(data.Encode()))
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}

// GetRebootStatus Returns the system reboot status.
// https://mailinabox.email/api-docs.html#operation/getSystemRebootStatus
func (s *SystemService) GetRebootStatus(ctx context.Context) (bool, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "reboot")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return false, fmt.Errorf("unable to create request: %w", err)
}

var result bool

err = s.client.doJSON(req, &result)
if err != nil {
return false, err
}

return result, nil
}

// Reboot Reboots the system.
// https://mailinabox.email/api-docs.html#operation/rebootSystem
func (s *SystemService) Reboot(ctx context.Context) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "reboot")

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), http.NoBody)
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}

// GetBackupStatus Returns the system backup status.
// https://mailinabox.email/api-docs.html#operation/getSystemBackupStatus
func (s *SystemService) GetBackupStatus(ctx context.Context) (*BackupStatus, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "backup", "status")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

var result BackupStatus

err = s.client.doJSON(req, &result)
if err != nil {
return nil, err
}

return &result, nil
}

// GetBackupConfig Returns the system backup config.
// https://mailinabox.email/api-docs.html#operation/getSystemBackupConfig
func (s *SystemService) GetBackupConfig(ctx context.Context) (*BackupConfig, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "backup", "config")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), http.NoBody)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

var result BackupConfig

err = s.client.doJSON(req, &result)
if err != nil {
return nil, err
}

return &result, nil
}

// UpdateBackupConfig Updates the system backup config.
// https://mailinabox.email/api-docs.html#operation/updateSystemBackupConfig
func (s *SystemService) UpdateBackupConfig(ctx context.Context, target, targetUser, targetPass string, minAge int) (string, error) {
endpoint := s.client.baseURL.JoinPath("admin", "system", "backup", "config")

data := url.Values{}
data.Set("target", target)
data.Set("targetUser", targetUser)
data.Set("targetPass", targetPass)
data.Set("minAge", strconv.Itoa(minAge))

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), strings.NewReader(data.Encode()))
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}

req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

resp, err := s.client.doPlain(req)
if err != nil {
return "", err
}

return strings.TrimSpace(string(resp)), nil
}
Loading

0 comments on commit 8bd672d

Please sign in to comment.