Skip to content

Commit

Permalink
Transition all Database functions to use the new DA API
Browse files Browse the repository at this point in the history
Support database imports
Support getting database processes
  • Loading branch information
wolveix committed Mar 25, 2024
1 parent 5ba1208 commit 3d9538d
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 142 deletions.
8 changes: 5 additions & 3 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ type (
)

func (c *UserContext) CreateLoginURL(loginKeyURL *LoginKeyURL) (*LoginKeyURL, error) {
var response LoginKeyURL
var response *LoginKeyURL

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

return &response, nil
return response, nil
}

func (c *UserContext) GetLoginURLs() ([]*LoginKeyURL, error) {
Expand All @@ -65,6 +65,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, "|") {
Expand Down
190 changes: 131 additions & 59 deletions database.go
Original file line number Diff line number Diff line change
@@ -1,68 +1,95 @@
package directadmin

import (
"errors"
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"strings"

"github.com/spf13/cast"
)

const (
DatabaseFormatGz = DatabaseFormat("gz")
DatabaseFormatSql = DatabaseFormat("sql")
)

type DatabaseFormat string

type Database struct {
Name string `json:"name" yaml:"name"`
Size int `json:"size" yaml:"size"`
Users int `json:"users" yaml:"users"`
}
type (
DatabaseFormat string

Database struct {
Name string `json:"database"`
DefaultCharset string `json:"defaultCharset"`
DefaultCollation string `json:"defaultCollation"`
DefinerIssues int `json:"definerIssues"`
EventCount int `json:"eventCount"`
RoutineCount int `json:"routineCount"`
SizeBytes int `json:"sizeBytes"`
TableCount int `json:"tableCount"`
TriggerCount int `json:"triggerCount"`
UserCount int `json:"userCount"`
ViewCount int `json:"viewCount"`
}

func (c *UserContext) CreateDatabase(name string, dbUser string, dbPassword string) error {
var response apiGenericResponse
DatabaseProcess struct {
Command string `json:"command"`
Database string `json:"database"`
Host string `json:"host"`
Id int `json:"id"`
Info string `json:"info"`
State string `json:"state"`
Time int `json:"time"`
User string `json:"user"`
}

body := url.Values{}
body.Set("name", name)
body.Set("user", dbUser)
body.Set("passwd", dbPassword)
body.Set("passwd2", dbPassword)
DatabaseUser struct {
HostPatterns []string `json:"hostPatterns"`
Password string `json:"password"`
User string `json:"dbuser"`
}

if _, err := c.api.makeRequest(http.MethodPost, "API_DATABASES?action=create", c.credentials, body, &response); err != nil {
return err
DatabaseWithUser struct {
Database
Password string `json:"password"`
User string `json:"dbuser"`
}
)

func (c *UserContext) CreateDatabase(database *Database) error {
database.Name = c.addUsernamePrefix(database.Name)

if response.Success != "Database Created" {
return fmt.Errorf("failed to create database: %v", response.Result)
if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-db", c.credentials, database, nil); err != nil {
return err
}

return nil
}

func (c *UserContext) DeleteDatabases(names ...string) error {
var response apiGenericResponse
func (c *UserContext) CreateDatabaseWithUser(database *DatabaseWithUser) error {
database.Name = c.addUsernamePrefix(database.Name)
database.User = c.addUsernamePrefix(database.User)

body := url.Values{}
if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-db-with-user", c.credentials, database, nil); err != nil {
return err
}

for index, name := range names {
if !strings.Contains(name, c.GetMyUsername()+"_") {
name = c.GetMyUsername() + "_" + name
}
return nil
}

body.Set("select"+cast.ToString(index), name)
}
func (c *UserContext) CreateDatabaseUser(databaseUser *DatabaseUser) error {
databaseUser.User = c.addUsernamePrefix(databaseUser.User)

if _, err := c.api.makeRequest(http.MethodPost, "API_DATABASES?action=delete", c.credentials, body, &response); err != nil {
if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-user", c.credentials, databaseUser, nil); err != nil {
return err
}

if response.Success != "Databases Deleted" {
return fmt.Errorf("failed to delete database: %v", response.Result)
return nil
}

func (c *UserContext) DeleteDatabase(databaseName string) error {
databaseName = c.addUsernamePrefix(databaseName)

if _, err := c.api.makeRequestN(http.MethodDelete, "db-manage/databases/"+databaseName, c.credentials, nil, nil); err != nil {
return err
}

return nil
Expand Down Expand Up @@ -99,43 +126,88 @@ func (c *UserContext) DownloadDatabase(name string, format DatabaseFormat, fileP
return nil
}

// GetDatabases (user) returns an array of the session user's databases
func (c *UserContext) GetDatabases() ([]Database, error) {
var databases []Database
rawDatabases := struct {
Databases map[string]struct {
Name string `json:"database"`
Size string `json:"size"`
Users string `json:"nusers"`
}
}{}
// ExportDatabase (user) returns an export of the given database
func (c *UserContext) ExportDatabase(databaseName string, gzip bool) ([]byte, error) {
databaseName = c.addUsernamePrefix(databaseName)

if _, err := c.api.makeRequest(http.MethodGet, "DB", c.credentials, nil, &rawDatabases); err != nil {
export, err := c.api.makeRequestN(http.MethodGet, "db-manage/databases/"+databaseName+"/export?gzip="+strconv.FormatBool(gzip), c.credentials, nil, nil)
if err != nil {
return nil, err
}

for id, database := range rawDatabases.Databases {
if id != "info" {
databases = append(databases, Database{
Name: strings.Replace(database.Name, c.credentials.username+"_", "", 1),
Size: cast.ToInt(database.Size),
Users: cast.ToInt(database.Users),
})
}
return export, nil
}

// GetDatabase (user) returns the given database
func (c *UserContext) GetDatabase(databaseName string) (*Database, error) {
databaseName = c.addUsernamePrefix(databaseName)

var database Database

if _, err := c.api.makeRequestN(http.MethodGet, "db-show/databases/"+databaseName, c.credentials, nil, &database); err != nil {
return nil, err
}

if len(databases) == 0 {
return nil, errors.New("no databases were found")
return &database, nil
}

// GetDatabases (user) returns an array of the session user's databases
func (c *UserContext) GetDatabases() ([]*Database, error) {
var databases []*Database

if _, err := c.api.makeRequestN(http.MethodGet, "db-show/databases", c.credentials, nil, &databases); err != nil {
return nil, err
}

return databases, nil
}

// ListDatabases (user) returns an array of all databases for the session user
func (c *UserContext) ListDatabases() (databaseList []string, err error) {
if _, err = c.api.makeRequest(http.MethodGet, "API_DATABASES", c.credentials, nil, &databaseList); err != nil {
// GetDatabaseProcesses (admin) returns an array of current database processes
func (c *UserContext) GetDatabaseProcesses() ([]*DatabaseProcess, error) {
var databaseProcesses []*DatabaseProcess

if _, err := c.api.makeRequestN(http.MethodGet, "db-monitor/processes", c.credentials, nil, &databaseProcesses); err != nil {
return nil, err
}

return databaseList, nil
return databaseProcesses, nil
}

// ImportDatabase (user) imports the given database export into the given database
func (c *UserContext) ImportDatabase(databaseName string, emptyExistingDatabase bool, sql []byte) error {
databaseName = c.addUsernamePrefix(databaseName)

if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/databases/"+databaseName+"/import?clean="+strconv.FormatBool(emptyExistingDatabase), c.credentials, sql, nil, true); err != nil {
return err
}

return nil
}

// UpdateDatabaseUserHosts (user) updates the given database user's hosts
func (c *UserContext) UpdateDatabaseUserHosts(username string, hosts []string) error {
username = c.addUsernamePrefix(username)

if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/users/"+username+"/change-hosts", c.credentials, hosts, nil); err != nil {
return err
}

return nil
}

// UpdateDatabaseUserPassword (user) updates the given database user's password
func (c *UserContext) UpdateDatabaseUserPassword(username string, password string) error {
username = c.addUsernamePrefix(username)

newPassword := struct {
NewPassword string `json:"newPassword"`
}{
password,
}

if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/users/"+username+"/change-password", c.credentials, newPassword, nil); err != nil {
return err
}

return nil
}
34 changes: 29 additions & 5 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"os"
Expand Down Expand Up @@ -80,16 +81,39 @@ func (a *API) makeRequest(method string, endpoint string, accountCredentials cre
}

// makeRequestN supports DA's new API
func (a *API) makeRequestN(method string, endpoint string, accountCredentials credentials, body any, object any) ([]byte, error) {
func (a *API) makeRequestN(method string, endpoint string, accountCredentials credentials, body any, object any, uploadFile ...bool) ([]byte, error) {
defer a.queryTime(endpoint, time.Now())

var err error
var requestBytes, responseBytes []byte

contentType := "application/json"

if body != nil {
requestBytes, err = json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("error marshalling body: %w", err)
if len(uploadFile) != 1 {
requestBytes, err = json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("error marshalling body: %w", err)
}
} else {
var byteBuffer bytes.Buffer
multipartWriter := multipart.NewWriter(&byteBuffer)

formFile, err := multipartWriter.CreateFormFile("sqlfile", "filename")
if err != nil {
return nil, err
}

if _, err = formFile.Write(body.([]byte)); err != nil {
return nil, err
}

if err = multipartWriter.Close(); err != nil {
return nil, err
}

requestBytes = byteBuffer.Bytes()
contentType = multipartWriter.FormDataContentType()
}
}

Expand All @@ -99,7 +123,7 @@ func (a *API) makeRequestN(method string, endpoint string, accountCredentials cr
}

req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Type", contentType)
req.SetBasicAuth(accountCredentials.username, accountCredentials.passkey)

resp, err := a.httpClient.Do(req)
Expand Down
Loading

0 comments on commit 3d9538d

Please sign in to comment.