axiodb 16.2.0 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,6 +30,7 @@
30
30
  - [Simple: connect without authentication](#simple-connect-without-authentication)
31
31
  - [Advanced: TCP authentication](#advanced-tcp-authentication)
32
32
  - [Advanced: TLS encryption](#advanced-tls-encryption)
33
+ - [AxioDB CLI — Command Line Interface](#-axiodb-cli--command-line-interface)
33
34
  - [Troubleshooting](#-troubleshooting)
34
35
  - [Docker Deployment](#-docker-deployment)
35
36
  - [Simple: run the container](#simple-run-the-container)
@@ -338,6 +339,92 @@ The rule: the `-e AXIODB_TLS_CERT_PATH=...` value must always match the *right-h
338
339
 
339
340
  ---
340
341
 
342
+ ## 💻 AxioDB CLI — Command Line Interface
343
+
344
+ A Go-based CLI tool for interacting with AxioDB servers via the TCP protocol. Supports all database operations with both single-command and interactive REPL modes. Export and import databases via the HTTP API.
345
+
346
+ ### Quick Install
347
+
348
+ **Linux/macOS:**
349
+ ```bash
350
+ curl -fsSL https://raw.githubusercontent.com/nexoral/AxioDB/main/cli/Scripts/install.sh | bash
351
+ ```
352
+
353
+ **Windows (PowerShell):**
354
+ ```powershell
355
+ irm https://raw.githubusercontent.com/nexoral/AxioDB/main/cli/Scripts/install.ps1 | iex
356
+ ```
357
+
358
+ **Or download directly:** [GitHub Releases](https://github.com/nexoral/AxioDB/releases?q=cli-v&expanded=true)
359
+
360
+ ### Supported Platforms
361
+
362
+ | OS | Architectures |
363
+ |------|--------------|
364
+ | Linux | amd64, arm64, 386, armv7 |
365
+ | macOS | amd64 (Intel), arm64 (Apple Silicon) |
366
+ | Windows | amd64, arm64, 386 |
367
+ | FreeBSD | amd64 |
368
+ | OpenBSD | amd64 |
369
+ | NetBSD | amd64 |
370
+
371
+ ### Usage
372
+
373
+ **Single commands:**
374
+ ```bash
375
+ axiodb -c axiodb://127.0.0.1:27019 ping
376
+ axiodb -c axiodb://127.0.0.1:27019 db list
377
+ axiodb -c axiodb://127.0.0.1:27019 document insert '{"name":"Alice"}' --db mydb --collection users
378
+ axiodb -c axiodb://127.0.0.1:27019 document query '{}' --db mydb --collection users
379
+ ```
380
+
381
+ **Interactive REPL (MongoDB shell style):**
382
+ ```bash
383
+ axiodb connect
384
+ # axiodb> use mydb
385
+ # axiodb:mydb> show collections
386
+ # axiodb:mydb> use mydb.users
387
+ # axiodb:mydb:users> db.users.find({})
388
+ # axiodb:mydb:users> db.users.insert({name: "Bob"})
389
+ # axiodb:mydb:users> exit
390
+ ```
391
+
392
+ **With authentication:**
393
+ ```bash
394
+ axiodb -c axiodb://127.0.0.1:27019 -u admin -p admin connect
395
+ ```
396
+
397
+ **With TLS:**
398
+ ```bash
399
+ axiodb -c axiodb://127.0.0.1:27019 --tls --tls-cert ./cert.pem connect
400
+ ```
401
+
402
+ **Export database (via HTTP API):**
403
+ ```bash
404
+ axiodb export mydb --http-host localhost --http-port 27018 -u admin -p secret
405
+ # Saves mydb.tar.gz in current directory
406
+ ```
407
+
408
+ **Import database (via HTTP API):**
409
+ ```bash
410
+ axiodb import ./backups/mydb.tar.gz --http-host localhost --http-port 27018 -u admin -p secret
411
+ # Tab completes .tar.gz file paths
412
+ ```
413
+
414
+ ### Features
415
+
416
+ - **All 23 commands:** database, collection, document CRUD, aggregation, indexing, export, import
417
+ - **Interactive REPL:** MongoDB shell syntax (`use`, `show dbs`, `db.coll.find()`)
418
+ - **Export & Import:** backup/restore databases via HTTP API, tab-completes file paths
419
+ - **TLS support:** `--tls`, `--tls-cert`, `--tls-skip-verify`
420
+ - **Auth support:** `-u` / `-p` flags (TCP and HTTP)
421
+ - **JSON output:** `--output json` for scripting
422
+ - **Tab autocomplete** in REPL mode and file path completion
423
+
424
+ 👉 **[Full CLI Documentation](https://github.com/nexoral/AxioDB/tree/main/cli)**
425
+
426
+ ---
427
+
341
428
  ## 🔧 Troubleshooting
342
429
 
343
430
  ### "Not connected to server" right after calling `connect()`
package/cli/VERSION CHANGED
@@ -1 +1 @@
1
- 16.2.0
1
+ 17.0.0
@@ -0,0 +1,66 @@
1
+ package cmd
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+
7
+ "github.com/nexoral/axiodb-cli/internal/config"
8
+ "github.com/spf13/cobra"
9
+ )
10
+
11
+ var exportCmd = &cobra.Command{
12
+ Use: "export <dbname>",
13
+ Short: "Export a database to a .tar.gz file via HTTP",
14
+ Long: "Export an AxioDB database to a .tar.gz archive. Uses the HTTP API (port 27018), not TCP.",
15
+ Args: cobra.ExactArgs(1),
16
+ RunE: func(cmd *cobra.Command, args []string) error {
17
+ dbName := args[0]
18
+ cfg, err := config.FromFlags(cmd)
19
+ if err != nil {
20
+ return err
21
+ }
22
+
23
+ if cfg.Username == "" || cfg.Password == "" {
24
+ return fmt.Errorf("username and password required for HTTP auth (-u, -p)")
25
+ }
26
+
27
+ client := cfg.NewHTTPClient()
28
+
29
+ fmt.Fprintf(os.Stderr, "Logging in to %s:%d...\n", cfg.HTTPHost, cfg.HTTPPort)
30
+ if err := client.Login(cfg.Username, cfg.Password); err != nil {
31
+ return err
32
+ }
33
+ defer client.Logout()
34
+
35
+ fmt.Fprintf(os.Stderr, "Exporting %s...\n", dbName)
36
+ filename, size, err := client.Export(dbName)
37
+ if err != nil {
38
+ return err
39
+ }
40
+
41
+ fmt.Fprintf(os.Stderr, "Exported %s (%s)\n", filename, formatBytes(size))
42
+ return nil
43
+ },
44
+ }
45
+
46
+ func formatBytes(bytes int64) string {
47
+ const (
48
+ KB = 1024
49
+ MB = 1024 * KB
50
+ GB = 1024 * MB
51
+ )
52
+ switch {
53
+ case bytes >= GB:
54
+ return fmt.Sprintf("%.1f GB", float64(bytes)/float64(GB))
55
+ case bytes >= MB:
56
+ return fmt.Sprintf("%.1f MB", float64(bytes)/float64(MB))
57
+ case bytes >= KB:
58
+ return fmt.Sprintf("%.1f KB", float64(bytes)/float64(KB))
59
+ default:
60
+ return fmt.Sprintf("%d B", bytes)
61
+ }
62
+ }
63
+
64
+ func init() {
65
+ rootCmd.AddCommand(exportCmd)
66
+ }
@@ -0,0 +1,84 @@
1
+ package cmd
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+
9
+ "github.com/nexoral/axiodb-cli/internal/config"
10
+ "github.com/spf13/cobra"
11
+ )
12
+
13
+ var importCmd = &cobra.Command{
14
+ Use: "import <file>",
15
+ Short: "Import a database from a .tar.gz file via HTTP",
16
+ Long: "Import an AxioDB database from a .tar.gz archive. Uses the HTTP API (port 27018), not TCP.",
17
+ Args: cobra.ExactArgs(1),
18
+ ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
19
+ dir := "."
20
+ prefix := toComplete
21
+
22
+ if idx := strings.LastIndex(toComplete, "/"); idx >= 0 {
23
+ dir = toComplete[:idx+1]
24
+ prefix = toComplete[idx+1:]
25
+ }
26
+
27
+ entries, err := os.ReadDir(dir)
28
+ if err != nil {
29
+ return nil, cobra.ShellCompDirectiveDefault
30
+ }
31
+
32
+ var completions []string
33
+ for _, entry := range entries {
34
+ if entry.IsDir() {
35
+ continue
36
+ }
37
+ name := entry.Name()
38
+ if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, ".tar.gz") {
39
+ completions = append(completions, filepath.Join(dir, name))
40
+ }
41
+ }
42
+ return completions, cobra.ShellCompDirectiveNoFileComp
43
+ },
44
+ RunE: func(cmd *cobra.Command, args []string) error {
45
+ filePath := args[0]
46
+ cfg, err := config.FromFlags(cmd)
47
+ if err != nil {
48
+ return err
49
+ }
50
+
51
+ if cfg.Username == "" || cfg.Password == "" {
52
+ return fmt.Errorf("username and password required for HTTP auth (-u, -p)")
53
+ }
54
+
55
+ info, err := os.Stat(filePath)
56
+ if err != nil {
57
+ return fmt.Errorf("file not found: %s", filePath)
58
+ }
59
+ if info.IsDir() {
60
+ return fmt.Errorf("not a file: %s", filePath)
61
+ }
62
+
63
+ client := cfg.NewHTTPClient()
64
+
65
+ fmt.Fprintf(os.Stderr, "Logging in to %s:%d...\n", cfg.HTTPHost, cfg.HTTPPort)
66
+ if err := client.Login(cfg.Username, cfg.Password); err != nil {
67
+ return err
68
+ }
69
+ defer client.Logout()
70
+
71
+ fmt.Fprintf(os.Stderr, "Importing %s...\n", filepath.Base(filePath))
72
+ dbName, err := client.Import(filePath)
73
+ if err != nil {
74
+ return err
75
+ }
76
+
77
+ fmt.Fprintf(os.Stderr, "Imported database: %s\n", dbName)
78
+ return nil
79
+ },
80
+ }
81
+
82
+ func init() {
83
+ rootCmd.AddCommand(importCmd)
84
+ }
package/cli/cmd/root.go CHANGED
@@ -33,4 +33,6 @@ func init() {
33
33
  rootCmd.PersistentFlags().Int("timeout", 30, "Request timeout in seconds")
34
34
  rootCmd.PersistentFlags().String("db", "", "Database name")
35
35
  rootCmd.PersistentFlags().String("collection", "", "Collection name")
36
+ rootCmd.PersistentFlags().String("http-host", "localhost", "HTTP server host (for export/import)")
37
+ rootCmd.PersistentFlags().Int("http-port", 27018, "HTTP server port (for export/import)")
36
38
  }
@@ -6,6 +6,7 @@ import (
6
6
  "strings"
7
7
  "time"
8
8
 
9
+ "github.com/nexoral/axiodb-cli/pkg/httpclient"
9
10
  "github.com/nexoral/axiodb-cli/pkg/protocol"
10
11
  "github.com/spf13/cobra"
11
12
  )
@@ -23,6 +24,8 @@ type Config struct {
23
24
  Timeout int
24
25
  DB string
25
26
  Collection string
27
+ HTTPHost string
28
+ HTTPPort int
26
29
  }
27
30
 
28
31
  func FromFlags(cmd *cobra.Command) (*Config, error) {
@@ -40,6 +43,8 @@ func FromFlags(cmd *cobra.Command) (*Config, error) {
40
43
  cfg.Timeout, _ = cmd.Flags().GetInt("timeout")
41
44
  cfg.DB, _ = cmd.Flags().GetString("db")
42
45
  cfg.Collection, _ = cmd.Flags().GetString("collection")
46
+ cfg.HTTPHost, _ = cmd.Flags().GetString("http-host")
47
+ cfg.HTTPPort, _ = cmd.Flags().GetInt("http-port")
43
48
 
44
49
  if cfg.ConnString != "" {
45
50
  host, port, err := parseConnectionString(cfg.ConnString)
@@ -109,3 +114,7 @@ func GetCollectionContext(cmd *cobra.Command) string {
109
114
  coll, _ := cmd.Flags().GetString("collection")
110
115
  return coll
111
116
  }
117
+
118
+ func (c *Config) NewHTTPClient() *httpclient.HTTPClient {
119
+ return httpclient.New(c.HTTPHost, c.HTTPPort, time.Duration(c.Timeout)*time.Second)
120
+ }
@@ -0,0 +1,160 @@
1
+ package httpclient
2
+
3
+ import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "fmt"
7
+ "io"
8
+ "mime/multipart"
9
+ "net/http"
10
+ "net/http/cookiejar"
11
+ "os"
12
+ "path/filepath"
13
+ "strings"
14
+ "time"
15
+ )
16
+
17
+ type HTTPClient struct {
18
+ baseURL string
19
+ httpClient *http.Client
20
+ }
21
+
22
+ func New(host string, port int, timeout time.Duration) *HTTPClient {
23
+ jar, _ := cookiejar.New(nil)
24
+ return &HTTPClient{
25
+ baseURL: fmt.Sprintf("http://%s:%d", host, port),
26
+ httpClient: &http.Client{
27
+ Timeout: timeout,
28
+ Jar: jar,
29
+ },
30
+ }
31
+ }
32
+
33
+ func (c *HTTPClient) Login(username, password string) error {
34
+ body, _ := json.Marshal(map[string]string{
35
+ "username": username,
36
+ "password": password,
37
+ })
38
+
39
+ resp, err := c.httpClient.Post(c.baseURL+"/api/auth/login", "application/json", bytes.NewReader(body))
40
+ if err != nil {
41
+ return fmt.Errorf("login request failed: %w", err)
42
+ }
43
+ defer resp.Body.Close()
44
+
45
+ if resp.StatusCode == http.StatusOK {
46
+ return nil
47
+ }
48
+
49
+ var errResp struct {
50
+ Message string `json:"message"`
51
+ }
52
+ json.NewDecoder(resp.Body).Decode(&errResp)
53
+ msg := errResp.Message
54
+ if msg == "" {
55
+ msg = resp.Status
56
+ }
57
+ return fmt.Errorf("login failed: %s", msg)
58
+ }
59
+
60
+ func (c *HTTPClient) Logout() error {
61
+ resp, err := c.httpClient.Post(c.baseURL+"/api/auth/logout", "application/json", nil)
62
+ if err != nil {
63
+ return err
64
+ }
65
+ defer resp.Body.Close()
66
+ return nil
67
+ }
68
+
69
+ func (c *HTTPClient) Export(dbName string) (string, int64, error) {
70
+ url := fmt.Sprintf("%s/api/db/export-database/?dbName=%s", c.baseURL, dbName)
71
+ resp, err := c.httpClient.Get(url)
72
+ if err != nil {
73
+ return "", 0, fmt.Errorf("export request failed: %w", err)
74
+ }
75
+ defer resp.Body.Close()
76
+
77
+ if resp.StatusCode != http.StatusOK {
78
+ var errResp struct {
79
+ Message string `json:"message"`
80
+ }
81
+ json.NewDecoder(resp.Body).Decode(&errResp)
82
+ msg := errResp.Message
83
+ if msg == "" {
84
+ msg = resp.Status
85
+ }
86
+ return "", 0, fmt.Errorf("export failed: %s", msg)
87
+ }
88
+
89
+ filename := dbName + ".tar.gz"
90
+ if cd := resp.Header.Get("Content-Disposition"); cd != "" {
91
+ if idx := strings.Index(cd, "filename="); idx >= 0 {
92
+ name := cd[idx+9:]
93
+ name = strings.Trim(name, "\"")
94
+ if name != "" {
95
+ filename = filepath.Base(name)
96
+ }
97
+ }
98
+ }
99
+
100
+ out, err := os.Create(filename)
101
+ if err != nil {
102
+ return "", 0, fmt.Errorf("create file: %w", err)
103
+ }
104
+ defer out.Close()
105
+
106
+ written, err := io.Copy(out, resp.Body)
107
+ if err != nil {
108
+ os.Remove(filename)
109
+ return "", 0, fmt.Errorf("write file: %w", err)
110
+ }
111
+
112
+ return filename, written, nil
113
+ }
114
+
115
+ func (c *HTTPClient) Import(filePath string) (string, error) {
116
+ file, err := os.Open(filePath)
117
+ if err != nil {
118
+ return "", fmt.Errorf("open file: %w", err)
119
+ }
120
+ defer file.Close()
121
+
122
+ var body bytes.Buffer
123
+ writer := multipart.NewWriter(&body)
124
+ part, err := writer.CreateFormFile("file", filepath.Base(filePath))
125
+ if err != nil {
126
+ return "", fmt.Errorf("create form file: %w", err)
127
+ }
128
+ if _, err := io.Copy(part, file); err != nil {
129
+ return "", fmt.Errorf("write form data: %w", err)
130
+ }
131
+ writer.Close()
132
+
133
+ resp, err := c.httpClient.Post(
134
+ c.baseURL+"/api/db/import-database/",
135
+ writer.FormDataContentType(),
136
+ &body,
137
+ )
138
+ if err != nil {
139
+ return "", fmt.Errorf("import request failed: %w", err)
140
+ }
141
+ defer resp.Body.Close()
142
+
143
+ var result struct {
144
+ Message string `json:"message"`
145
+ Database string `json:"database"`
146
+ }
147
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
148
+ return "", fmt.Errorf("parse response: %w", err)
149
+ }
150
+
151
+ if resp.StatusCode != http.StatusOK {
152
+ msg := result.Message
153
+ if msg == "" {
154
+ msg = fmt.Sprintf("HTTP %d", resp.StatusCode)
155
+ }
156
+ return "", fmt.Errorf("import failed: %s", msg)
157
+ }
158
+
159
+ return result.Database, nil
160
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axiodb",
3
- "version": "16.2.0",
3
+ "version": "17.0.0",
4
4
  "description": "The Pure JavaScript Alternative to SQLite. Embedded NoSQL database for Node.js with MongoDB-style queries, zero native dependencies, built-in InMemoryCache, and web GUI. Perfect for desktop apps, CLI tools, and embedded systems. No compilation, no platform issues—pure JavaScript from npm install to production.",
5
5
  "main": "./lib/config/DB.js",
6
6
  "types": "./lib/config/DB.d.ts",