Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: levelzerotechnology/directadmin-go
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1
Choose a base ref
...
head repository: levelzerotechnology/directadmin-go
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 15 commits
  • 27 files changed
  • 1 contributor

Commits on Mar 2, 2024

  1. Copy the full SHA
    63b7793 View commit details

Commits on Mar 25, 2024

  1. Support login key URLs

    wolveix committed Mar 25, 2024
    Copy the full SHA
    5ba1208 View commit details
  2. Transition all Database functions to use the new DA API

    Support database imports
    Support getting database processes
    wolveix committed Mar 25, 2024
    Copy the full SHA
    3d9538d View commit details

Commits on May 18, 2024

  1. Fix CreateLoginURL response

    wolveix committed May 18, 2024
    Copy the full SHA
    9223656 View commit details

Commits on Jun 2, 2024

  1. Refactored all HTTP logic (cleaned up debug logic, log body if smalle…

    …r than 4096 bytes)
    
    Implemented support for Softaculous (for WordPress)
    Implemented support for PHP version retrieval and modification
    Implemented support for downloading files
    Implemented support for creating, retrieving, and restoring backups
    Implemented support for retrieving messages
    wolveix committed Jun 2, 2024
    Copy the full SHA
    e32cb25 View commit details
  2. Utilize new API endpoints for user config and usage

    Support retrieving reseller's users with config and/or data (rather than needing both)
    Up debug body limit to 32768
    wolveix committed Jun 2, 2024
    Copy the full SHA
    f2f78e5 View commit details
  3. Copy the full SHA
    31d96d5 View commit details

Commits on Jun 26, 2024

  1. Copy the full SHA
    a5170af View commit details

Commits on Jun 27, 2024

  1. Use new API for uploading files

    Better handling around empty responses from the new API
    wolveix committed Jun 27, 2024
    Copy the full SHA
    a202103 View commit details
  2. Rename CreateFile to UploadFile to match DownloadFile naming

    Split current `UploadFile` into `UploadFileFromDisk` to support uploading from in-memory
    wolveix committed Jun 27, 2024
    Copy the full SHA
    0104749 View commit details

Commits on Jul 4, 2024

  1. Copy the full SHA
    404b264 View commit details
  2. Copy the full SHA
    d9d1525 View commit details
  3. Copy the full SHA
    213b8df View commit details

Commits on Aug 1, 2024

  1. Copy the full SHA
    633a4df View commit details

Commits on Sep 18, 2024

  1. Copy the full SHA
    54dba82 View commit details
Showing with 1,325 additions and 720 deletions.
  1. +9 −20 admin.go
  2. +47 −11 auth.go
  3. +110 −0 backup.go
  4. +155 −62 database.go
  5. +3 −14 directadmin.go
  6. +4 −4 dns.go
  7. +36 −9 domain.go
  8. +56 −5 email.go
  9. +6 −5 email_forwarder.go
  10. +62 −59 file.go
  11. +2 −3 go.mod
  12. +9 −4 go.sum
  13. +107 −127 http.go
  14. +1 −1 license.go
  15. +28 −0 messages.go
  16. +5 −5 package.go
  17. +5 −5 package_reseller.go
  18. +76 −0 php.go
  19. +179 −0 plugins_softaculous.go
  20. +53 −35 reseller.go
  21. +135 −0 session.go
  22. +46 −0 ssl.go
  23. +4 −25 subdomain.go
  24. +5 −5 system.go
  25. +163 −91 user.go
  26. +0 −220 user_raw.go
  27. +19 −10 wordpress.go
29 changes: 9 additions & 20 deletions admin.go
Original file line number Diff line number Diff line change
@@ -17,15 +17,15 @@ type convertAccount struct {
}

func (c *AdminContext) ConvertResellerToUser(username string, reseller string) error {
if _, err := c.api.makeRequestN(http.MethodPost, "convert-reseller-to-user", c.credentials, convertAccount{Account: username, Creator: reseller}, nil); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "convert-reseller-to-user", convertAccount{Account: username, Creator: reseller}, nil); err != nil {
return err
}

return nil
}

func (c *AdminContext) ConvertUserToReseller(username string) error {
if _, err := c.api.makeRequestN(http.MethodPost, "convert-user-to-reseller", c.credentials, convertAccount{Account: username}, nil); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "convert-user-to-reseller", convertAccount{Account: username}, nil); err != nil {
return err
}

@@ -35,7 +35,7 @@ func (c *AdminContext) ConvertUserToReseller(username string) error {
func (c *AdminContext) DisableRedis() error {
var response apiGenericResponseN

if _, err := c.api.makeRequestN(http.MethodPost, "redis/disable", c.credentials, nil, &response); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "redis/disable", nil, &response); err != nil {
return err
}

@@ -45,7 +45,7 @@ func (c *AdminContext) DisableRedis() error {
func (c *AdminContext) EnableRedis() error {
var response apiGenericResponseN

if _, err := c.api.makeRequestN(http.MethodPost, "redis/enable", c.credentials, nil, &response); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "redis/enable", nil, &response); err != nil {
return err
}

@@ -56,7 +56,7 @@ func (c *AdminContext) EnableRedis() error {
func (c *AdminContext) GetAllUsers() ([]string, error) {
var users []string

if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_ALL_USERS", c.credentials, nil, &users); err != nil {
if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_ALL_USERS", nil, &users); err != nil {
return nil, err
}

@@ -67,26 +67,15 @@ func (c *AdminContext) GetAllUsers() ([]string, error) {
func (c *AdminContext) GetResellers() ([]string, error) {
var users []string

if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_RESELLERS", c.credentials, nil, &users); err != nil {
return nil, err
}

return users, nil
}

// TODO: finish implementation
func (c *AdminContext) GetResellersWithUsage() ([]string, error) {
var users []string

if _, err := c.api.makeRequest(http.MethodGet, "RESELLER_SHOW", c.credentials, nil, &users); err != nil {
if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_RESELLERS", nil, &users); err != nil {
return nil, err
}

return users, nil
}

func (c *AdminContext) MoveUserToReseller(username string, reseller string) error {
if _, err := c.api.makeRequestN(http.MethodPost, "change-user-creator", c.credentials, convertAccount{Account: username, Creator: reseller}, nil); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "change-user-creator", convertAccount{Account: username, Creator: reseller}, nil); err != nil {
return err
}

@@ -96,7 +85,7 @@ func (c *AdminContext) MoveUserToReseller(username string, reseller string) erro
func (c *AdminContext) RestartDirectAdmin() error {
var response apiGenericResponseN

if _, err := c.api.makeRequestN(http.MethodPost, "restart", c.credentials, nil, &response); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "restart", nil, &response); err != nil {
return err
}

@@ -106,7 +95,7 @@ func (c *AdminContext) RestartDirectAdmin() error {
func (c *AdminContext) UpdateDirectAdmin() error {
var response apiGenericResponseN

if _, err := c.api.makeRequestN(http.MethodPost, "version/update", c.credentials, nil, &response); err != nil {
if _, err := c.makeRequestNew(http.MethodPost, "version/update", nil, &response); err != nil {
return err
}

58 changes: 47 additions & 11 deletions auth.go
Original file line number Diff line number Diff line change
@@ -8,22 +8,56 @@ import (
"time"
)

type credentials struct {
username string
passkey string
type (
credentials struct {
username string
passkey string
}

LoginHistory struct {
Attempts int `json:"attempts"`
Host string `json:"host"`
Timestamp time.Time `json:"timestamp"`
}

LoginKeyURL struct {
AllowNetworks []string `json:"allowNetworks"`
Created time.Time `json:"created"`
CreatedBy string `json:"createdBy"`
Expires time.Time `json:"expires"`
Id string `json:"id"`
RedirectURL string `json:"redirectURL"`
URL string `json:"url"`
}
)

func (c *UserContext) CreateLoginURL(loginKeyURL *LoginKeyURL) error {
if loginKeyURL == nil {
return errors.New("failed to create login key URL: loginKeyURL is nil")
}

if _, err := c.makeRequestNew(http.MethodPost, "login-keys/urls", loginKeyURL, loginKeyURL); err != nil {
return fmt.Errorf("failed to create login URL: %w", err)
}

return nil
}

type LoginHistory struct {
Attempts int `json:"attempts"`
Host string `json:"host"`
Timestamp time.Time `json:"timestamp"`
func (c *UserContext) GetLoginURLs() ([]*LoginKeyURL, error) {
var loginKeyURLs []*LoginKeyURL

if _, err := c.makeRequestNew(http.MethodGet, "login-keys/urls", nil, &loginKeyURLs); err != nil {
return nil, fmt.Errorf("failed to get login URLs: %w", err)
}

return loginKeyURLs, nil
}

func (c *AdminContext) GetLoginHistory() ([]*LoginHistory, error) {
var loginHistory []*LoginHistory

if _, err := c.api.makeRequestN(http.MethodGet, "login-history", c.credentials, nil, &loginHistory); err != nil {
return nil, fmt.Errorf("failed to get login history: %v", err)
if _, err := c.makeRequestNew(http.MethodGet, "login-history", nil, &loginHistory); err != nil {
return nil, fmt.Errorf("failed to get login history: %w", err)
}

if len(loginHistory) == 0 {
@@ -33,6 +67,8 @@ func (c *AdminContext) GetLoginHistory() ([]*LoginHistory, error) {
return loginHistory, nil
}

// GetMyUsername returns the current user's username. This is particularly useful when logging in as another user, as it
// trims the admin/reseller username automatically
func (c *UserContext) GetMyUsername() string {
// if user is logged in via reseller, we need to remove the reseller username from the context's username
if strings.Contains(c.credentials.username, "|") {
@@ -45,12 +81,12 @@ func (c *UserContext) GetMyUsername() string {
func (c *UserContext) Login() error {
var response apiGenericResponse

if _, err := c.api.makeRequest(http.MethodGet, "API_LOGIN_TEST", c.credentials, nil, &response); err != nil {
if _, err := c.makeRequestOld(http.MethodGet, "API_LOGIN_TEST", nil, &response); err != nil {
return err
}

if response.Success != "Login OK" {
return errors.New("login failed")
return fmt.Errorf("login failed: %v", response)
}

return nil
110 changes: 110 additions & 0 deletions backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package directadmin

import (
"fmt"
"net/http"
"net/url"
)

// CreateBackup (user) creates an account backup for the given domain, and the given items
func (c *UserContext) CreateBackup(domain string, backupItems ...string) error {
var response apiGenericResponse

body := url.Values{}
body.Set("action", "backup")
body.Set("domain", domain)
body.Set("form_version", "4")

for index, backupItem := range backupItems {
body.Set(fmt.Sprintf("select%d", index), backupItem)
}

if _, err := c.makeRequestOld(http.MethodPost, "SITE_BACKUP", body, &response); err != nil {
return err
}

if response.Success != "Backup creation added to queue" {
return fmt.Errorf("failed to create backup: %v", response.Result)
}

return nil
}

// CreateBackupAllItems (user) wraps around CreateBackup and provides all available backup items
func (c *UserContext) CreateBackupAllItems(domain string) error {
return c.CreateBackup(
domain,
"domain",
"subdomain",
"email",
"email_data",
"emailsettings",
"forwarder",
"autoresponder",
"vacation",
"list",
"ftp",
"ftpsettings",
"database",
"database_data",
"trash",
)
}

// GetBackups (user) returns an array of the session user's backups for the given domain
func (c *UserContext) GetBackups(domain string) ([]string, error) {
var backups []string

if _, err := c.makeRequestOld(http.MethodGet, "SITE_BACKUP?domain="+domain+"&ipp=50", nil, &backups); err != nil {
return nil, err
}

return backups, nil
}

// RestoreBackup (user) restores an account backup for the given domain, and the given items
func (c *UserContext) RestoreBackup(domain string, backupFilename string, backupItems ...string) error {
var response apiGenericResponse

body := url.Values{}
body.Set("action", "restore")
body.Set("domain", domain)
body.Set("file", backupFilename)
body.Set("form_version", "3")

for index, backupItem := range backupItems {
body.Set(fmt.Sprintf("select%d", index), backupItem)
}

if _, err := c.makeRequestOld(http.MethodPost, "SITE_BACKUP", body, &response); err != nil {
return err
}

if response.Success != "Restore will run in the background" {
return fmt.Errorf("failed to restore backup: %v", response.Result)
}

return nil
}

// RestoreBackupAllItems (user) wraps around RestoreBackup and provides all available backup items
func (c *UserContext) RestoreBackupAllItems(domain string, backupFilename string) error {
return c.RestoreBackup(
domain,
backupFilename,
"domain",
"subdomain",
"email",
"email_data",
"emailsettings",
"forwarder",
"autoresponder",
"vacation",
"list",
"ftp",
"ftpsettings",
"database",
"database_data",
"trash",
)
}
Loading