mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 11:13:03 +00:00
482 lines
9.8 KiB
Go
482 lines
9.8 KiB
Go
package db
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/auth"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// DBCmd is the root database command
|
|
var DBCmd = &cobra.Command{
|
|
Use: "db",
|
|
Short: "Manage SQLite databases",
|
|
Long: "Create and manage per-namespace SQLite databases",
|
|
}
|
|
|
|
// CreateCmd creates a new database
|
|
var CreateCmd = &cobra.Command{
|
|
Use: "create <database_name>",
|
|
Short: "Create a new SQLite database",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: createDatabase,
|
|
}
|
|
|
|
// QueryCmd executes a SQL query
|
|
var QueryCmd = &cobra.Command{
|
|
Use: "query <database_name> <sql>",
|
|
Short: "Execute a SQL query",
|
|
Args: cobra.ExactArgs(2),
|
|
RunE: queryDatabase,
|
|
}
|
|
|
|
// ListCmd lists all databases
|
|
var ListCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List all databases",
|
|
RunE: listDatabases,
|
|
}
|
|
|
|
// BackupCmd backs up a database to IPFS
|
|
var BackupCmd = &cobra.Command{
|
|
Use: "backup <database_name>",
|
|
Short: "Backup database to IPFS",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: backupDatabase,
|
|
}
|
|
|
|
// BackupsCmd lists backups for a database
|
|
var BackupsCmd = &cobra.Command{
|
|
Use: "backups <database_name>",
|
|
Short: "List backups for a database",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: listBackups,
|
|
}
|
|
|
|
func init() {
|
|
DBCmd.AddCommand(CreateCmd)
|
|
DBCmd.AddCommand(QueryCmd)
|
|
DBCmd.AddCommand(ListCmd)
|
|
DBCmd.AddCommand(BackupCmd)
|
|
DBCmd.AddCommand(BackupsCmd)
|
|
}
|
|
|
|
func createDatabase(cmd *cobra.Command, args []string) error {
|
|
dbName := args[0]
|
|
|
|
apiURL := getAPIURL()
|
|
url := apiURL + "/v1/db/sqlite/create"
|
|
|
|
payload := map[string]string{
|
|
"database_name": dbName,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
return fmt.Errorf("failed to create database: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("✅ Database created successfully!\n\n")
|
|
fmt.Printf("Name: %s\n", result["database_name"])
|
|
fmt.Printf("Home Node: %s\n", result["home_node_id"])
|
|
fmt.Printf("Created: %s\n", result["created_at"])
|
|
|
|
return nil
|
|
}
|
|
|
|
func queryDatabase(cmd *cobra.Command, args []string) error {
|
|
dbName := args[0]
|
|
sql := args[1]
|
|
|
|
apiURL := getAPIURL()
|
|
url := apiURL + "/v1/db/sqlite/query"
|
|
|
|
payload := map[string]interface{}{
|
|
"database_name": dbName,
|
|
"query": sql,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("query failed: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Print results
|
|
if rows, ok := result["rows"].([]interface{}); ok && len(rows) > 0 {
|
|
// Print as table
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
|
|
|
// Print headers
|
|
firstRow := rows[0].(map[string]interface{})
|
|
for col := range firstRow {
|
|
fmt.Fprintf(w, "%s\t", col)
|
|
}
|
|
fmt.Fprintln(w)
|
|
|
|
// Print rows
|
|
for _, row := range rows {
|
|
r := row.(map[string]interface{})
|
|
for _, val := range r {
|
|
fmt.Fprintf(w, "%v\t", val)
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
fmt.Printf("\nRows returned: %d\n", len(rows))
|
|
} else if rowsAffected, ok := result["rows_affected"].(float64); ok {
|
|
fmt.Printf("✅ Query executed successfully\n")
|
|
fmt.Printf("Rows affected: %d\n", int(rowsAffected))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func listDatabases(cmd *cobra.Command, args []string) error {
|
|
apiURL := getAPIURL()
|
|
url := apiURL + "/v1/db/sqlite/list"
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("failed to list databases: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
databases, ok := result["databases"].([]interface{})
|
|
if !ok || len(databases) == 0 {
|
|
fmt.Println("No databases found")
|
|
return nil
|
|
}
|
|
|
|
// Print table
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
|
fmt.Fprintln(w, "NAME\tSIZE\tBACKUP CID\tCREATED")
|
|
|
|
for _, db := range databases {
|
|
d := db.(map[string]interface{})
|
|
|
|
size := "0 B"
|
|
if sizeBytes, ok := d["size_bytes"].(float64); ok {
|
|
size = formatBytes(int64(sizeBytes))
|
|
}
|
|
|
|
backupCID := "-"
|
|
if cid, ok := d["backup_cid"].(string); ok && cid != "" {
|
|
if len(cid) > 12 {
|
|
backupCID = cid[:12] + "..."
|
|
} else {
|
|
backupCID = cid
|
|
}
|
|
}
|
|
|
|
createdAt := ""
|
|
if created, ok := d["created_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, created); err == nil {
|
|
createdAt = t.Format("2006-01-02 15:04")
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
|
|
d["database_name"],
|
|
size,
|
|
backupCID,
|
|
createdAt,
|
|
)
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
fmt.Printf("\nTotal: %v\n", result["total"])
|
|
|
|
return nil
|
|
}
|
|
|
|
func backupDatabase(cmd *cobra.Command, args []string) error {
|
|
dbName := args[0]
|
|
|
|
fmt.Printf("📦 Backing up database '%s' to IPFS...\n", dbName)
|
|
|
|
apiURL := getAPIURL()
|
|
url := apiURL + "/v1/db/sqlite/backup"
|
|
|
|
payload := map[string]string{
|
|
"database_name": dbName,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("backup failed: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("\n✅ Backup successful!\n\n")
|
|
fmt.Printf("Database: %s\n", result["database_name"])
|
|
fmt.Printf("Backup CID: %s\n", result["backup_cid"])
|
|
fmt.Printf("IPFS URL: %s\n", result["ipfs_url"])
|
|
fmt.Printf("Backed up: %s\n", result["backed_up_at"])
|
|
|
|
return nil
|
|
}
|
|
|
|
func listBackups(cmd *cobra.Command, args []string) error {
|
|
dbName := args[0]
|
|
|
|
apiURL := getAPIURL()
|
|
url := fmt.Sprintf("%s/v1/db/sqlite/backups?database_name=%s", apiURL, dbName)
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
token, err := getAuthToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("failed to list backups: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
backups, ok := result["backups"].([]interface{})
|
|
if !ok || len(backups) == 0 {
|
|
fmt.Println("No backups found")
|
|
return nil
|
|
}
|
|
|
|
// Print table
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
|
fmt.Fprintln(w, "CID\tSIZE\tBACKED UP")
|
|
|
|
for _, backup := range backups {
|
|
b := backup.(map[string]interface{})
|
|
|
|
cid := b["backup_cid"].(string)
|
|
if len(cid) > 20 {
|
|
cid = cid[:20] + "..."
|
|
}
|
|
|
|
size := "0 B"
|
|
if sizeBytes, ok := b["size_bytes"].(float64); ok {
|
|
size = formatBytes(int64(sizeBytes))
|
|
}
|
|
|
|
backedUpAt := ""
|
|
if backed, ok := b["backed_up_at"].(string); ok {
|
|
if t, err := time.Parse(time.RFC3339, backed); err == nil {
|
|
backedUpAt = t.Format("2006-01-02 15:04")
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s\t%s\t%s\n", cid, size, backedUpAt)
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
fmt.Printf("\nTotal: %v\n", result["total"])
|
|
|
|
return nil
|
|
}
|
|
|
|
func getAPIURL() string {
|
|
if url := os.Getenv("ORAMA_API_URL"); url != "" {
|
|
return url
|
|
}
|
|
return auth.GetDefaultGatewayURL()
|
|
}
|
|
|
|
func getAuthToken() (string, error) {
|
|
if token := os.Getenv("ORAMA_TOKEN"); token != "" {
|
|
return token, nil
|
|
}
|
|
|
|
// Try to get from enhanced credentials store
|
|
store, err := auth.LoadEnhancedCredentials()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to load credentials: %w", err)
|
|
}
|
|
|
|
gatewayURL := auth.GetDefaultGatewayURL()
|
|
creds := store.GetDefaultCredential(gatewayURL)
|
|
if creds == nil {
|
|
return "", fmt.Errorf("no credentials found for %s. Run 'orama auth login' to authenticate", gatewayURL)
|
|
}
|
|
|
|
if !creds.IsValid() {
|
|
return "", fmt.Errorf("credentials expired for %s. Run 'orama auth login' to re-authenticate", gatewayURL)
|
|
}
|
|
|
|
return creds.APIKey, nil
|
|
}
|
|
|
|
func formatBytes(bytes int64) string {
|
|
const unit = 1024
|
|
if bytes < unit {
|
|
return fmt.Sprintf("%d B", bytes)
|
|
}
|
|
div, exp := int64(unit), 0
|
|
for n := bytes / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
|
}
|