suresql

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2025 License: Apache-2.0 Imports: 13 Imported by: 1

README

SureSQL Backend

SureSQL is a robust SQL database abstraction service that provides a RESTful API for accessing and manipulating SQL databases. It's designed to be a secure, reliable, and easy-to-use interface for applications that need to interact with SQL databases.

Table of Contents

Architecture Overview

SureSQL acts as a middleware layer between client applications and the underlying SQL database system. The current implementation supports RQLite as the database backend, with potential for future support of additional database systems.

Key components:

  • RESTful API server with JWT-based authentication
  • Connection pooling for improved performance
  • Middleware for security and logging
  • ORM (Object-Relational Mapping) for simplified data operations

Features

  • Secure Authentication: API key and token-based authentication
  • Connection Pooling: Efficient management of database connections
  • Parameterized Queries: Protection against SQL injection
  • Token Refresh: Support for token refresh to maintain session validity
  • Detailed Logging: Comprehensive logging of all operations
  • Database Status: Get information about the database status and node configuration
  • Structured Response Format: Consistent response structure for all API calls

Configuration

SureSQL is configured via environment files:

  • .env.dev - Development configuration
  • .env.simplehttp - Server configuration

The main configuration options, this is used to connect to the DBMS include:

  • DB_HOST, DB_PORT: Database server connection details
  • DB_USERNAME, DB_PASSWORD: Database credentials
  • DB_SSL: Whether to use SSL for database connections
  • DB_API_KEY, DB_CLIENT_ID: API key and client ID for authentication
  • DB_CONSISTENCY: Consistency level for distributed database operations
  • DB_OPTIONS: Options for the DBMS
  • DB_HTTP_TIMEOUT, DB_RETRY_TIMEOUT, DB_MAX_RETRIES: Connection parameters

Information regarding SureSQL service that will be returned to the client is in the DB itself. These settings are also in the environment:

  • SURESQL_HOST, SURESQL_PORT: SureSQL server connection details
  • SURESQL_IP: SureSQL server IP (which are not used at this moment)
  • SURESQL_SSL: Whether to use SSL for database connections (always true)
  • SURESQL_DBMS: The DBMS used by SureSQL (default is RQLite) Currently the environment takes the precedence, especially if the settings in DB table value is empty. Some of the boolean settings definitely overwritten by environment variables.

Authentication

SureSQL uses a two-level authentication system:

  1. API Key Authentication: Every request must include the API key and client ID in the headers
  2. Token Authentication: After initial authentication, operations use a token-based system

The API key and client ID must be included in the headers for all requests:

API_KEY: your-api-key
CLIENT_ID: your-client-id

For authenticated endpoints, the token must be included in the Authorization header:

Authorization: Bearer your-token

API Endpoints

Authentication and Connection
POST /db/connect

Authenticates a user and creates a new database connection.

Request Body:

{
  "username": "your-username",
  "password": "your-password"
}

Response:

{
  "status": 200,
  "message": "Authentication successful",
  "data": {
    "token": "your-auth-token",
    "refresh_token": "your-refresh-token",
    "token_expired_at": "2023-01-01T12:00:00Z",
    "refresh_expired_at": "2023-01-02T12:00:00Z",
    "user_id": "1"
  }
}
POST /db/refresh

Refreshes an authentication token.

Request Body:

{
  "refresh": "your-refresh-token"
}

Response:

{
  "status": 200,
  "message": "Token refreshed successfully",
  "data": {
    "token": "your-new-auth-token",
    "refresh_token": "your-new-refresh-token",
    "token_expired_at": "2023-01-01T12:00:00Z",
    "refresh_expired_at": "2023-01-02T12:00:00Z",
    "user_id": "1"
  }
}
Database Operations

All database operation endpoints require a valid authentication token.

POST /db/api/sql

Executes one or more SQL statements.

Request Body:

{
  "statements": ["SQL statement 1", "SQL statement 2"],
  "param_sql": [
    {
      "query": "INSERT INTO table (column1, column2) VALUES (?, ?)",
      "values": ["value1", 2]
    }
  ]
}

Response:

{
  "status": 200,
  "message": "SQL executed successfully",
  "data": {
    "results": [
      {
        "error": null,
        "timing": 0.005,
        "rows_affected": 1,
        "last_insert_id": 123
      }
    ],
    "execution_time": 0.005,
    "rows_affected": 1
  }
}
POST /db/api/query

Queries data from a table with optional conditions.

Request Body:

{
  "table": "users",
  "condition": {
    "field": "age",
    "operator": ">",
    "value": 18,
    "order_by": ["name ASC"],
    "limit": 10
  },
  "single_row": false
}

Response:

{
  "status": 200,
  "message": "Query executed successfully",
  "data": {
    "records": [
      {
        "table_name": "users",
        "data": {
          "id": 1,
          "name": "John Doe",
          "age": 30
        }
      }
    ],
    "execution_time": 0.003,
    "count": 1
  }
}
POST /db/api/querysql

Executes SQL queries and returns the results.

Request Body:

{
  "statements": ["SELECT * FROM users WHERE age > 18"],
  "param_sql": [
    {
      "query": "SELECT * FROM users WHERE age > ?",
      "values": [18]
    }
  ],
  "single_row": false
}

Response:

{
  "status": 200,
  "message": "SQL executed successfully",
  "data": [
    {
      "records": [
        {
          "table_name": "users",
          "data": {
            "id": 1,
            "name": "John Doe",
            "age": 30
          }
        }
      ],
      "execution_time": 0.003,
      "count": 1
    }
  ]
}
POST /db/api/insert

Inserts one or more records into the database.

Request Body:

{
  "records": [
    {
      "table_name": "users",
      "data": {
        "name": "Jane Smith",
        "age": 25
      }
    }
  ],
  "queue": false,
  "same_table": true
}

Response:

{
  "status": 200,
  "message": "Successfully inserted 1 records",
  "data": {
    "results": [
      {
        "error": null,
        "timing": 0.004,
        "rows_affected": 1,
        "last_insert_id": 124
      }
    ],
    "execution_time": 0.004,
    "rows_affected": 1
  }
}
GET /db/api/status

Retrieves the status of the database connection.

Response:

{
  "status": 200,
  "message": "Status peers vs config matched",
  "data": {
    "url": "http://localhost:4001",
    "version": "0.0.1",
    "dbms": "rqlite",
    "dbms_driver": "direct-rqlite",
    "start_time": "2023-01-01T00:00:00Z",
    "uptime": "24h0m0s",
    "dir_size": 1024,
    "db_size": 2048,
    "node_id": "1",
    "is_leader": true,
    "leader": "http://localhost:4001",
    "mode": "rw",
    "nodes": 1,
    "node_number": 1,
    "max_pool": 25,
    "peers": {
      "1": {
        "url": "http://localhost:4001",
        "is_leader": true,
        "mode": "rw",
        "nodes": 1,
        "node_number": 1
      }
    }
  }
}
POST /db/api/getschema

Retrieves the database schema information.

Response:

{
  "status": 200,
  "message": "Schema get successfully",
  "data": [
    {
      "type": "table",
      "name": "users",
      "tbl_name": "users",
      "rootpage": 2,
      "sql": "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
      "hidden": false
    }
  ]
}

Usage Examples

Connect to the Database
const response = await fetch('http://your-suresql-server/db/connect', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id'
  },
  body: JSON.stringify({
    username: 'your-username',
    password: 'your-password'
  })
});

const data = await response.json();
const token = data.data.token;
// Save token for subsequent requests
Refresh Token
const response = await fetch('http://your-suresql-server/db/refresh', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id'
  },
  body: JSON.stringify({
    refresh: 'your-refresh-token'
  })
});

const data = await response.json();
const newToken = data.data.token;
// Update the saved token
Execute SQL Statements

Basic example with a single SQL statement:

const response = await fetch('http://your-suresql-server/db/api/sql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    statements: ["CREATE TABLE if not exists test (id INTEGER PRIMARY KEY, value TEXT)"]
  })
});

const data = await response.json();
console.log(data);

Example with parameterized SQL:

const response = await fetch('http://your-suresql-server/db/api/sql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    param_sql: [
      {
        query: "INSERT INTO test (value) VALUES (?)",
        values: ["test value"]
      }
    ]
  })
});

const data = await response.json();
console.log(data);
Query Data

Simple query to get all records from a table:

const response = await fetch('http://your-suresql-server/db/api/query', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    table: "test"
  })
});

const data = await response.json();
console.log(data);

Query with conditions:

const response = await fetch('http://your-suresql-server/db/api/query', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    table: "test",
    condition: {
      field: "id",
      operator: ">",
      value: 5,
      order_by: ["id DESC"],
      limit: 10
    }
  })
});

const data = await response.json();
console.log(data);

Complex query with nested conditions:

const response = await fetch('http://your-suresql-server/db/api/query', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    table: "users",
    condition: {
      logic: "OR",
      nested: [
        {
          field: "age",
          operator: ">",
          value: 30
        },
        {
          logic: "AND",
          nested: [
            {
              field: "status",
              operator: "=",
              value: "active"
            },
            {
              field: "role",
              operator: "=",
              value: "admin"
            }
          ]
        }
      ],
      order_by: ["name ASC"],
      limit: 20
    }
  })
});

const data = await response.json();
console.log(data);
SQL Query

Execute a SQL query and get the results:

const response = await fetch('http://your-suresql-server/db/api/querysql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    statements: ["SELECT * FROM users WHERE role = 'admin'"]
  })
});

const data = await response.json();
console.log(data);

Parameterized SQL query:

const response = await fetch('http://your-suresql-server/db/api/querysql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    param_sql: [
      {
        query: "SELECT * FROM users WHERE role = ? AND status = ?",
        values: ["admin", "active"]
      }
    ]
  })
});

const data = await response.json();
console.log(data);
Insert Data

Insert a single record:

const response = await fetch('http://your-suresql-server/db/api/insert', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    records: [
      {
        table_name: "users",
        data: {
          name: "John Smith",
          age: 35,
          role: "user",
          status: "active"
        }
      }
    ]
  })
});

const data = await response.json();
console.log(data);

Insert multiple records into the same table:

const response = await fetch('http://your-suresql-server/db/api/insert', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    records: [
      {
        table_name: "users",
        data: {
          name: "Alice Johnson",
          age: 28,
          role: "user",
          status: "active"
        }
      },
      {
        table_name: "users",
        data: {
          name: "Bob Williams",
          age: 42,
          role: "admin",
          status: "active"
        }
      }
    ],
    same_table: true
  })
});

const data = await response.json();
console.log(data);

Insert records into different tables:

const response = await fetch('http://your-suresql-server/db/api/insert', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify({
    records: [
      {
        table_name: "users",
        data: {
          name: "Charlie Brown",
          age: 32,
          role: "user",
          status: "active"
        }
      },
      {
        table_name: "posts",
        data: {
          title: "My First Post",
          content: "Hello, world!",
          user_id: 1
        }
      }
    ],
    same_table: false
  })
});

const data = await response.json();
console.log(data);
Get Database Status
const response = await fetch('http://your-suresql-server/db/api/status', {
  method: 'GET',
  headers: {
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  }
});

const data = await response.json();
console.log(data);
Get Schema
const response = await fetch('http://your-suresql-server/db/api/getschema', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API_KEY': 'your-api-key',
    'CLIENT_ID': 'your-client-id',
    'Authorization': `Bearer ${token}`
  }
});

const data = await response.json();
console.log(data);

Internal API

SureSQL also provides an internal API accessible only with basic authentication using the internal configuration credentials. This API is intended for administrative purposes.

Internal API endpoints:

  • /suresql/iusers (GET, POST, PUT, DELETE) - Manage users
  • /suresql/schema (GET) - Get database schema information
  • /suresql/dbms_status (GET) - Get DBMS status information

Error Handling

All endpoints return a consistent error response format:

{
  "status": 400, // HTTP status code
  "message": "Error message",
  "data": null // or error details
}

Common error status codes:

  • 400: Bad Request - Invalid input or parameters
  • 401: Unauthorized - Missing or invalid authentication
  • 404: Not Found - Resource not found
  • 500: Internal Server Error - Server-side error

Each error response includes a descriptive message to help diagnose the issue.

Documentation

Index

Constants

View Source
const (
	// DEFAULT LEADER NODE
	LEADER_NODE_NUMBER = 1

	// for readibility
	INTERNAL_MODE = false // not copying the result into current node's status
	NODE_MODE     = true  // copying the result into current node's status

	// ConfigTable Categories and keys
	SETTING_CATEGORY_TOKEN  = "token"
	SETTING_KEY_TOKEN_EXP   = "token_exp"   // value int: in minutes
	SETTING_KEY_REFRESH_EXP = "refresh_exp" // value int: in minutes
	SETTING_KEY_TOKEN_TTL   = "token_ttl"   // value int: in minutes, beat for checking expiration

	SETTING_CATEGORY_CONNECTION = "connection"
	SETTING_KEY_MAX_POOL        = "max_pool" // value int: 0 overwrite pool_on, meaning no pooling, automatically pool_on=false
	SETTING_KEY_ENABLE_POOL     = "pool_on"  // value string: true or false

	SETTING_CATEGORY_NODES = "nodes"
	SETTING_KEY_NODE_NAME  = "node_name" // value string: node_number;hostname;ip;mode
	SETTING_NODE_DELIMITER = "|"

	SETTING_CATEGORY_SYSTEM       = "system"
	SETTING_KEY_LABEL             = "label"             // value string: label for this node
	SETTING_KEY_IP                = "ip"                // value string: database name
	SETTING_KEY_HOST              = "host"              // value string: hostname
	SETTING_KEY_PORT              = "port"              // value string: port number
	SETTING_KEY_SSL               = "ssl"               // value bool(int): true or false
	SETTING_KEY_DBMS              = "dbms"              // value string: rqlite or other later implementation
	SETTING_KEY_MODE              = "mode"              // value string: 'r', 'w', 'rw'
	SETTING_KEY_NODES             = "nodes"             // value int: total nodes in the cluster
	SETTING_KEY_NODE_NUMBER       = "node_number"       // value int: node number for this server
	SETTING_KEY_IS_INIT_DONE      = "is_init_done"      // value bool(int): DB init is done
	SETTING_KEY_IS_SPLIT_WRITE    = "is_split_write"    // value bool(int): split write
	SETTING_KEY_ENCRYPTION_METHOD = "encryption_method" // value string: "aes", "rsa", "none"

	SETTING_CATEGORY_EMPTY = "nocategory"
)
View Source
const (
	SURESQL_ENV_FILE = ".env.suresql"
	APP_NAME         = "SureSQL"
	APP_VERSION      = "0.0.1"
)
View Source
const (
	MIGRATION_DIRECTORY          = "migrations/"
	MIGRATION_UP_FILES_SIGNATURE = "_up.sql"
)
View Source
const (
	// Default Token settings
	DEFAULT_TOKEN_EXPIRES_MINUTES   = 24 * 60 * time.Minute // every 24 hours
	DEFAULT_REFRESH_EXPIRES_MINUTES = 48 * 60 * time.Minute // every 48 hours
	DEFAULT_TTL_TICKER_MINUTES      = 5 * time.Minute       // every [value] minute, check for expiration for ttl

	// Default HTTP timeouts
	// DEFAULT_CONNECTION_TIMEOUT            = 60 * time.Second
	DEFAULT_TIMEOUT       = 60 * time.Second
	DEFAULT_RETRY_TIMEOUT = 60 * time.Second
	DEFAULT_RETRY         = 3

	// Default Pool settings
	DEFAULT_MAX_POOL     = 25
	DEFAULT_POOL_ENABLED = true
)

Variables

View Source
var (
	CurrentNode       SureSQLNode
	ReloadEnvironment bool = false

	// Standard error, cannot use constant on struct
	// Should be constant instead?
	ErrNoDBConnection       medaerror.MedaError = medaerror.MedaError{Message: "no db connection"}
	ErrDBInitializedAlready medaerror.MedaError = medaerror.MedaError{Message: "DB already initialized"}
	// ErrTokenNotFound  medaerror.MedaError = medaerror.MedaError{Message: "token not found"}
	// ErrInvalidRequest medaerror.MedaError = medaerror.MedaError{Message: "invalid request param or body"}
	// ErrWrongPassword  medaerror.MedaError = medaerror.MedaError{Message: "password missmatch"}
	SchemaTable string = ""
)

GLOBAL VAR

View Source
var (
	ServerStartTime time.Time
)

Functions

func ConnectInternal

func ConnectInternal() error

This should be run the first time this package got imported, which is connecting to the DB locally / internally. Not yet used by the client.

func GetStatusInternal

func GetStatusInternal(db SureSQLDB, setNodeStatus bool) (orm.NodeStatusStruct, error)

func InitDB

func InitDB(force bool) error

This is more like migrating data from MIGRATION_DIRECTORY TODO: fix the printout to use metrics package so we can have the time elapsed information. Make sure to call this AFTER connect internal is called!! Because we need the DB connection already.

func LoadConfigFromDB

func LoadConfigFromDB(db *SureSQLDB) error

LoadConfigFromDB loads settings from _settings table

func LoadSettingsFromDB added in v0.0.2

func LoadSettingsFromDB(db *SureSQLDB) error

func OverwriteConfigFromEnvironment added in v0.0.2

func OverwriteConfigFromEnvironment()

if DB settings is not there, get from environment. DB's settings table always wins

func PingPong added in v0.0.2

func PingPong() string

Just for debugging, pingpong function

Types

type ConfigTable

type ConfigTable struct {
	ID               int           `json:"id,omitempty"                  db:"id"`
	Label            string        `json:"label,omitempty"               db:"label"`
	IP               string        `json:"ip,omitempty"                  db:"ip"`
	Host             string        `json:"host,omitempty"                db:"host"`
	Port             string        `json:"port,omitempty"                db:"port"`
	SSL              bool          `json:"ssl,omitempty"                 db:"ssl"`
	DBMS             string        `json:"dbms,omitempty"                db:"dbms"`
	Mode             string        `json:"mode,omitempty"                db:"mode"`
	Nodes            int           `json:"nodes,omitempty"               db:"nodes"`       // total number of nodes in the cluster
	NodeNumber       int           `json:"node_number,omitempty"         db:"node_number"` // this is node number .. X
	NodeID           int           `json:"node_id,omitempty"             db:"node_id"`     // this is node ID from rqlite cluster
	IsInitDone       bool          `json:"is_init_done,omitempty"        db:"is_init_done"`
	IsSplitWrite     bool          `json:"is_split_write,omitempty"      db:"is_split_write"`
	EncryptionMethod string        `json:"encryption_method,omitempty"   db:"encryption_method"`
	TokenExp         time.Duration `json:"token_exp,omitempty"           db:"token_exp"`   // token expiration in minutes
	RefreshExp       time.Duration `json:"refresh_exp,omitempty"         db:"refresh_exp"` // refresh token expiration in minutes
	TTLTicker        time.Duration `json:"ttl_ticker,omitempty"          db:"ttl_ticker"`  // ttl ticker to check expiration in minutes
	EnvConfig
}

This is the config for the current SureSQL node, it inserted inside the table!

func (ConfigTable) TableName

func (s ConfigTable) TableName() string

type EnvConfig added in v0.0.2

type EnvConfig struct {
	Token        string        `json:"token,omitempty"           db:"token"`
	RefreshToken string        `json:"refresh_token,omitempty"   db:"refresh_token"`
	JWEKey       string        `json:"jwe_key,omitempty"         db:"jwe_key"`
	JWTKey       string        `json:"jwt_key,omitempty"         db:"jwt_key"`
	APIKey       string        `json:"api_key,omitempty"         db:"api_key"`
	ClientID     string        `json:"client_id,omitempty"       db:"client_id"`
	HttpTimeout  time.Duration `json:"http_timeout,omitempty"    db:"http_timeout"`
	RetryTimeout time.Duration `json:"retry_timeout,omitempty"   db:"retry_timeout"`
	MaxRetries   int           `json:"max_retries,omitempty"     db:"max_retries"`
}

This is reserved to be configuration that usually taken from environment variables for safety

type InsertRequest

type InsertRequest struct {
	Records   []orm.DBRecord `json:"records"`              // Records to insert
	Queue     bool           `json:"queue,omitempty"`      // Whether to use queue operations (optional)
	SameTable bool           `json:"same_table,omitempty"` // Indicates if all records belong to the same table
}

===== Used in handle_Insert endpoints InsertRequest represents the request structure for inserting records

type QueryRequest

type QueryRequest struct {
	Table     string         `json:"table"`                // Table name for queries
	Condition *orm.Condition `json:"condition,omitempty"`  // Optional condition for filtering
	SingleRow bool           `json:"single_row,omitempty"` // If true, return only first row
}

===== Used in handle_Query endpoints QueryRequest represents the simplified request structure for executing SELECT queries

type QueryResponse

type QueryResponse struct {
	Records       []orm.DBRecord `json:"records"` // Always returns as array, even for single record
	ExecutionTime float64        `json:"execution_time"`
	Count         int            `json:"count"`
}

QueryResponse represents the response structure for query results

type QueryResponseSQL

type QueryResponseSQL []QueryResponse

QueryResponse represents the response structure for query results

type SQLRequest

type SQLRequest struct {
	Statements []string                `json:"statements,omitempty"` // Raw SQL statements to execute
	ParamSQL   []orm.ParametereizedSQL `json:"param_sql,omitempty"`  // Parameterized SQL statements to execute
	SingleRow  bool                    `json:"single_row,omitempty"` // If true, return only first row
}

===== Used in handle_SQL endpoints SQLRequest represents the request structure for executing SQL commands: UPDATE, DELETE, DROP, INSERT, SELECT

type SQLResponse

type SQLResponse struct {
	Results       []orm.BasicSQLResult `json:"results"`        // Results for each executed statement
	ExecutionTime float64              `json:"execution_time"` // Total execution time in milliseconds
	RowsAffected  int                  `json:"rows_affected"`  // Total number of rows affected
}

SQLResponse represents the response structure for SQL execution results

type SettingTable added in v0.0.2

type SettingTable struct {
	ID         int     `json:"id,omitempty"                  db:"id"`
	Category   string  `json:"category,omitempty"            db:"category"`
	DataType   string  `json:"data_type,omitempty"           db:"data_type"`
	SettingKey string  `json:"setting_key,omitempty"         db:"setting_key"`
	TextValue  string  `json:"text_value,omitempty"          db:"text_value"`
	FloatValue float64 `json:"float_value,omitempty"         db:"float_value"`
	IntValue   int     `json:"int_value,omitempty"           db:"int_value"`
}

This is how we store the config for SureSQL. It can contains the peers information, timeouts etc (depends on the category) Ie: category: token , rows are: ConfigKey: token_exp , IntValue: 20 (in minutes) ConfigKey: refresh_exp , IntValue: 200 (in minutes) ConfigKey: token_ttl , IntValue: 5 (in minutes)

func (SettingTable) GetValue added in v0.0.2

func (c SettingTable) GetValue() interface{}

GetValue returns the value of the config entry as an interface{} based on data_type

func (SettingTable) TableName added in v0.0.2

func (c SettingTable) TableName() string

type Settings added in v0.0.2

type Settings map[string]SettingsMap

Map SettingsMap by the category, which is the same as inside SettingTable.Category Finding key based on category: Settings[category][Key] ie: Settings[token][token_exp].IntValue =

func (Settings) SettingExist added in v0.0.2

func (c Settings) SettingExist(category, key string) (SettingTable, bool)

By category and key

type SettingsMap added in v0.0.2

type SettingsMap map[string]SettingTable

map SettingTable by the key (string) which is same as SettingTable.SettingKey instead of using array, this is faster to search for specific setting key

func (SettingsMap) SettingExist added in v0.0.2

func (c SettingsMap) SettingExist(key string) (SettingTable, bool)

type StandardResponse

type StandardResponse struct {
	Status  int         `json:"status"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

StandardResponse is a structured response format for all API responses

type SureSQLDB

type SureSQLDB orm.Database

func NewDatabase

func NewDatabase(conf SureSQLDBMSConfig) (SureSQLDB, error)

Making connection to internal DB This is where implementation selection happens, right now is only RQlite

type SureSQLDBMSConfig added in v0.0.2

type SureSQLDBMSConfig struct {
	Host        string `json:"host,omitempty"            db:"host"`
	Port        string `json:"port,omitempty"            db:"port"`
	Username    string `json:"username,omitempty"        db:"username"` // this is not used, we use _users table instead
	Password    string `json:"password,omitempty"        db:"password"` // this is not used, we use _users table instead
	Database    string `json:"database,omitempty"        db:"database"`
	SSL         bool   `json:"ssl,omitempty"             db:"ssl"`
	Options     string `json:"options,omitempty"         db:"options"`
	Consistency string `json:"consistency,omitempty"     db:"consistency"`
	// below are not yet used. Previously those are SureSQL Config instead of DBMS config
	URL string `json:"url,omitempty"             db:"url"`
	EnvConfig
}

This is config needed by SureSQL to connect to Internal DB (DBMS), at this point only RQLite

func LoadDBMSConfigFromEnvironment added in v0.0.2

func LoadDBMSConfigFromEnvironment() SureSQLDBMSConfig

Reading internal DB configuration for this SureSQL Node, from environment TODO: maybe add second return parameter: error, so caller can check if error then quit the app

func (*SureSQLDBMSConfig) GenerateGoRQLiteURL added in v0.0.2

func (sc *SureSQLDBMSConfig) GenerateGoRQLiteURL()

NOTE: this is not used, because we are using direct-rqlite implementation If using the gorqlite implementation, then we need to put username+password in the URL then gorqlite use this to connect to the rqlite server

func (*SureSQLDBMSConfig) GenerateRQLiteURL added in v0.0.2

func (sc *SureSQLDBMSConfig) GenerateRQLiteURL()

If using direct-rqlite (our own) implementation, then no need, because when direct-rqlite connects to RQLite server it will use basic-auth format for the username and password.

func (*SureSQLDBMSConfig) PrintDebug added in v0.0.2

func (sc *SureSQLDBMSConfig) PrintDebug(secure bool)

type SureSQLNode

type SureSQLNode struct {
	InternalConfig     SureSQLDBMSConfig    `json:"internal_config,omitempty"      db:"internal_config"`
	InternalAPI        string               `json:"internal_api,omitempty"         db:"internal_api"`        // This is for the node internal API (CRUD users)
	Config             ConfigTable          `json:"settings,omitempty"             db:"settings"`            // Settings for this node, from DB table
	Settings           Settings             `json:"configs,omitempty"              db:"configs"`             // Configs for this node, from DB table
	Status             orm.NodeStatusStruct `json:"status,omitempty"               db:"status"`              // Status for SureSQL DB Node that is standard from orm
	InternalConnection SureSQLDB            `json:"internal_connection,omitempty"  db:"internal_connection"` // master connection to InternalDB
	DBConnections      *medattlmap.TTLMap   `json:"db_connections,omitempty"       db:"db_connections"`      // another connection based on Token
	MaxPool            int                  `json:"max_pool,omitempty"             db:"max_pool"`            // total nodes for this project
	IsPoolEnabled      bool                 `json:"is_poolenabled,omitempty"       db:"is_poolenabled"`      // if this DB already initialized
	IsEncrypted        bool                 `json:"is_encrypted,omitempty"         db:"is_encrypted"`        // none/AES/Bcrypt (already in Settings)

}

This is the whole SureSQL Node is all about NOTE: do we need IP? because we can put IP address in the hostname field if we are connecting based on IP.

func (*SureSQLNode) ApplyAllConfig

func (n *SureSQLNode) ApplyAllConfig() bool

This is to get all the config table and put it as SureSQLNode config

func (*SureSQLNode) ApplySettings added in v0.0.2

func (n *SureSQLNode) ApplySettings(category, key string) bool

Apply config if they are changed from DB, only few that can be changed and effected at run-time NOTE: this is hard-coded

func (SureSQLNode) GetDBConnectionByToken

func (n SureSQLNode) GetDBConnectionByToken(token string) (SureSQLDB, error)

Get the DB connection from pool based on token

func (*SureSQLNode) GetStatusFromSettings

func (n *SureSQLNode) GetStatusFromSettings(conf SureSQLDBMSConfig)

This is the status for SureSQL Nodes (not the internal DBMS nodes) Status is pretty much taken from Settings, but this is used for response

func (SureSQLNode) IsPoolAvailable

func (n SureSQLNode) IsPoolAvailable() bool

Check if pool is enabled, and max pool has not reached

func (SureSQLNode) PrintWelcomePretty

func (n SureSQLNode) PrintWelcomePretty()

Print the node information for console log

func (*SureSQLNode) RenameDBConnection

func (n *SureSQLNode) RenameDBConnection(old, new string)

rename the key for DB connection pool to use new token, this is usually because refresh token. TODO: please don't use this anymore, when token is refreshed, the DB connection should be deleted - and re-create it again fresh with new expiration same with the token expiration.

type TokenTable

type TokenTable struct {
	ID               string    `json:"id,omitempty"                  db:"id"`
	UserID           string    `json:"user_id,omitempty"             db:"user_id"`
	Token            string    `json:"token,omitempty"               db:"token"`
	Refresh          string    `json:"refresh_token,omitempty"       db:"refresh_token"`
	TokenExpiresAt   time.Time `json:"token_expired_at,omitempty"    db:"token_expired_at"`
	RefreshExpiresAt time.Time `json:"refresh_expired_at,omitempty"  db:"refresh_expired_at"`
	CreatedAt        time.Time `json:"created_at,omitempty"          db:"created_at"`
	// additional members
	UserName string
}

Originally this was saved in DB as table, but maybe Redis or some auto-expire system is better

func (TokenTable) TableName

func (t TokenTable) TableName() string

Directories

Path Synopsis
app
suresql command
test command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL