342 lines
8.1 KiB
Go
342 lines
8.1 KiB
Go
package couchdb
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Server represents a CouchDB server instance.
|
|
type Server struct {
|
|
resource *Resource
|
|
}
|
|
|
|
// NewServer creates a CouchDB server instance in address urlStr.
|
|
func NewServer(urlStr string) (*Server, error) {
|
|
return newServer(urlStr, true)
|
|
}
|
|
|
|
// NewServerNoFullCommit creates a CouchDB server instance in address urlStr
|
|
// with X-Couch-Full-Commit disabled.
|
|
func NewServerNoFullCommit(urlStr string) (*Server, error) {
|
|
return newServer(urlStr, false)
|
|
}
|
|
|
|
func newServer(urlStr string, fullCommit bool) (*Server, error) {
|
|
res, err := NewResource(urlStr, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s := &Server{
|
|
resource: res,
|
|
}
|
|
|
|
if !fullCommit {
|
|
s.resource.header.Set("X-Couch-Full-Commit", "false")
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// Config returns the entire CouchDB server configuration as JSON structure.
|
|
func (s *Server) Config(node string) (map[string]map[string]string, error) {
|
|
_, data, err := s.resource.GetJSON(fmt.Sprintf("_node/%s/_config", node), nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var config map[string]map[string]string
|
|
err = json.Unmarshal(data, &config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return config, nil
|
|
}
|
|
|
|
// Version returns the version info about CouchDB instance.
|
|
func (s *Server) Version() (string, error) {
|
|
var jsonMap map[string]interface{}
|
|
|
|
_, data, err := s.resource.GetJSON("", nil, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = json.Unmarshal(data, &jsonMap)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return jsonMap["version"].(string), nil
|
|
}
|
|
|
|
func (s *Server) String() string {
|
|
return fmt.Sprintf("Server %s", s.resource.base)
|
|
}
|
|
|
|
// ActiveTasks lists of running tasks.
|
|
func (s *Server) ActiveTasks() ([]interface{}, error) {
|
|
var tasks []interface{}
|
|
_, data, err := s.resource.GetJSON("_active_tasks", nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = json.Unmarshal(data, &tasks)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tasks, nil
|
|
}
|
|
|
|
// DBs returns a list of all the databases in the CouchDB server instance.
|
|
func (s *Server) DBs() ([]string, error) {
|
|
var dbs []string
|
|
_, data, err := s.resource.GetJSON("_all_dbs", nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = json.Unmarshal(data, &dbs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return dbs, nil
|
|
}
|
|
|
|
// Stats returns a JSON object containing the statistics for the running server.
|
|
func (s *Server) Stats(node, entry string) (map[string]interface{}, error) {
|
|
var stats map[string]interface{}
|
|
_, data, err := s.resource.GetJSON(fmt.Sprintf("_node/%s/_stats/%s", node, entry), nil, url.Values{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = json.Unmarshal(data, &stats)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return stats, nil
|
|
}
|
|
|
|
// Len returns the number of dbs in CouchDB server instance.
|
|
func (s *Server) Len() (int, error) {
|
|
dbs, err := s.DBs()
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return len(dbs), nil
|
|
}
|
|
|
|
// Create returns a database instance with the given name, returns true if created,
|
|
// if database already existed, returns false, *Database will be nil if failed.
|
|
func (s *Server) Create(name string) (*Database, error) {
|
|
_, _, err := s.resource.PutJSON(name, nil, nil, nil)
|
|
|
|
// ErrPreconditionFailed means database with the given name already existed
|
|
if err != nil && err != ErrPreconditionFailed {
|
|
return nil, err
|
|
}
|
|
|
|
db, getErr := s.Get(name)
|
|
if getErr != nil {
|
|
return nil, getErr
|
|
}
|
|
return db, err
|
|
}
|
|
|
|
// Delete deletes a database with the given name. Return false if failed.
|
|
func (s *Server) Delete(name string) error {
|
|
_, _, err := s.resource.DeleteJSON(name, nil, nil)
|
|
return err
|
|
}
|
|
|
|
// Get gets a database instance with the given name. Return nil if failed.
|
|
func (s *Server) Get(name string) (*Database, error) {
|
|
res, err := s.resource.NewResourceWithURL(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
db, err := NewDatabaseWithResource(res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, _, err = db.resource.Head("", nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return db, nil
|
|
}
|
|
|
|
// Contains returns true if a db with given name exsited.
|
|
func (s *Server) Contains(name string) bool {
|
|
_, _, err := s.resource.Head(name, nil, nil)
|
|
return err == nil
|
|
}
|
|
|
|
// Membership displays the nodes that are part of the cluster as clusterNodes.
|
|
// The field allNodes displays all nodes this node knows about, including the
|
|
// ones that are part of cluster.
|
|
func (s *Server) Membership() ([]string, []string, error) {
|
|
var jsonMap map[string]*json.RawMessage
|
|
|
|
_, data, err := s.resource.GetJSON("_membership", nil, nil)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
err = json.Unmarshal(data, &jsonMap)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var allNodes []string
|
|
var clusterNodes []string
|
|
|
|
err = json.Unmarshal(*jsonMap["all_nodes"], &allNodes)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
err = json.Unmarshal(*jsonMap["cluster_nodes"], &clusterNodes)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return allNodes, clusterNodes, nil
|
|
}
|
|
|
|
// Replicate requests, configure or stop a replication operation.
|
|
func (s *Server) Replicate(source, target string, options map[string]interface{}) (map[string]interface{}, error) {
|
|
var result map[string]interface{}
|
|
|
|
body := map[string]interface{}{
|
|
"source": source,
|
|
"target": target,
|
|
}
|
|
|
|
if options != nil {
|
|
for k, v := range options {
|
|
body[k] = v
|
|
}
|
|
}
|
|
|
|
_, data, err := s.resource.PostJSON("_replicate", nil, body, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
json.Unmarshal(data, &result)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// UUIDs requests one or more Universally Unique Identifiers from the CouchDB instance.
|
|
// The response is a JSON object providing a list of UUIDs.
|
|
// count - Number of UUIDs to return. Default is 1.
|
|
func (s *Server) UUIDs(count int) ([]string, error) {
|
|
if count <= 0 {
|
|
count = 1
|
|
}
|
|
|
|
values := url.Values{}
|
|
values.Set("count", strconv.Itoa(count))
|
|
|
|
_, data, err := s.resource.GetJSON("_uuids", nil, values)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var jsonMap map[string]*json.RawMessage
|
|
err = json.Unmarshal(data, &jsonMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var uuids []string
|
|
err = json.Unmarshal(*jsonMap["uuids"], &uuids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return uuids, nil
|
|
}
|
|
|
|
// newResource returns an url string representing a resource under server.
|
|
func (s *Server) newResource(resource string) string {
|
|
resourceURL, err := s.resource.base.Parse(resource)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return resourceURL.String()
|
|
}
|
|
|
|
// AddUser adds regular user in authentication database.
|
|
// Returns id and rev of the registered user.
|
|
func (s *Server) AddUser(name, password string, roles []string) (string, string, error) {
|
|
var id, rev string
|
|
db, err := s.Get("_users")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
if roles == nil {
|
|
roles = []string{}
|
|
}
|
|
|
|
userDoc := map[string]interface{}{
|
|
"_id": "org.couchdb.user:" + name,
|
|
"name": name,
|
|
"password": password,
|
|
"roles": roles,
|
|
"type": "user",
|
|
}
|
|
|
|
id, rev, err = db.Save(userDoc, nil)
|
|
if err != nil {
|
|
return id, rev, err
|
|
}
|
|
return id, rev, nil
|
|
}
|
|
|
|
// Login regular user in CouchDB, returns authentication token.
|
|
func (s *Server) Login(name, password string) (string, error) {
|
|
body := map[string]interface{}{
|
|
"name": name,
|
|
"password": password,
|
|
}
|
|
header, _, err := s.resource.PostJSON("_session", nil, body, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
tokenPart := strings.Split(header.Get("Set-Cookie"), ";")[0]
|
|
token := strings.Split(tokenPart, "=")[1]
|
|
return token, err
|
|
}
|
|
|
|
// VerifyToken returns error if user's token is invalid.
|
|
func (s *Server) VerifyToken(token string) error {
|
|
header := http.Header{}
|
|
header.Set("Cookie", strings.Join([]string{"AuthSession", token}, "="))
|
|
_, _, err := s.resource.GetJSON("_session", header, nil)
|
|
return err
|
|
}
|
|
|
|
// Logout regular user in CouchDB
|
|
func (s *Server) Logout(token string) error {
|
|
header := http.Header{}
|
|
header.Set("Cookie", strings.Join([]string{"AuthSession", token}, "="))
|
|
_, _, err := s.resource.DeleteJSON("_session", header, nil)
|
|
return err
|
|
}
|
|
|
|
// RemoveUser removes regular user in authentication database.
|
|
func (s *Server) RemoveUser(name string) error {
|
|
db, err := s.Get("_users")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
docID := "org.couchdb.user:" + name
|
|
return db.Delete(docID)
|
|
}
|