2020-06-29 15:36:01 +00:00
|
|
|
mod backup;
|
2022-07-04 00:01:24 +00:00
|
|
|
mod config;
|
2020-06-29 15:36:01 +00:00
|
|
|
mod export;
|
|
|
|
mod import;
|
|
|
|
mod log;
|
|
|
|
mod start;
|
|
|
|
mod version;
|
|
|
|
|
2022-07-04 00:01:24 +00:00
|
|
|
pub use config::CF;
|
|
|
|
|
2022-06-24 15:33:12 +00:00
|
|
|
use crate::cnf::LOGO;
|
2022-02-22 14:16:50 +00:00
|
|
|
use clap::{Arg, Command};
|
2022-05-07 14:15:11 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
use rand::distributions::Alphanumeric;
|
|
|
|
use rand::Rng;
|
2020-06-29 15:36:01 +00:00
|
|
|
|
2022-06-24 15:33:12 +00:00
|
|
|
const INFO: &str = "
|
|
|
|
To get started using SurrealDB, and for guides on connecting to and building applications
|
|
|
|
on top of SurrealDB, check out the SurrealDB documentation (https://surrealdb.com/docs).
|
|
|
|
|
|
|
|
If you have questions or ideas, join the SurrealDB community (https://surrealdb.com/community).
|
|
|
|
|
|
|
|
If you find a bug, submit an issue on Github (https://github.com/surrealdb/surrealdb/issues).
|
|
|
|
|
|
|
|
We would love it if you could star the repository (https://github.com/surrealdb/surrealdb).
|
|
|
|
|
|
|
|
----------
|
|
|
|
";
|
|
|
|
|
2022-05-07 14:15:11 +00:00
|
|
|
static PASS: Lazy<String> = Lazy::new(|| {
|
|
|
|
rand::thread_rng().sample_iter(&Alphanumeric).take(128).map(char::from).collect::<String>()
|
|
|
|
});
|
2020-06-29 15:36:01 +00:00
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn file_valid(v: &str) -> Result<(), String> {
|
2022-03-04 16:01:32 +00:00
|
|
|
if !v.is_empty() {
|
2020-06-29 15:36:01 +00:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(String::from(
|
|
|
|
"\
|
|
|
|
Provide a valid path to a SQL file\
|
|
|
|
",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn path_valid(v: &str) -> Result<(), String> {
|
2022-01-13 06:56:24 +00:00
|
|
|
if v == "memory" {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.starts_with("file://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.starts_with("tikv://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(String::from(
|
|
|
|
"\
|
|
|
|
Provide a valid database path paramater\
|
|
|
|
",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn conn_valid(v: &str) -> Result<(), String> {
|
2020-06-29 15:36:01 +00:00
|
|
|
if v.starts_with("https://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.starts_with("http://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(String::from(
|
|
|
|
"\
|
|
|
|
Provide a valid database connection string\
|
|
|
|
",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn from_valid(v: &str) -> Result<(), String> {
|
2020-06-29 15:36:01 +00:00
|
|
|
if v.starts_with("https://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.starts_with("http://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.ends_with(".db") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(String::from(
|
|
|
|
"\
|
|
|
|
Provide a valid database connection string, \
|
|
|
|
or specify the path to a database file\
|
|
|
|
",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn into_valid(v: &str) -> Result<(), String> {
|
2020-06-29 15:36:01 +00:00
|
|
|
if v.starts_with("https://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.starts_with("http://") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if v.ends_with(".db") {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(String::from(
|
|
|
|
"\
|
|
|
|
Provide a valid database connection string, \
|
|
|
|
or specify the path to a database file\
|
|
|
|
",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:30:59 +00:00
|
|
|
fn key_valid(v: &str) -> Result<(), String> {
|
2020-06-29 15:36:01 +00:00
|
|
|
match v.len() {
|
|
|
|
16 => Ok(()),
|
|
|
|
24 => Ok(()),
|
|
|
|
32 => Ok(()),
|
|
|
|
_ => Err(String::from(
|
|
|
|
"\
|
|
|
|
For AES-128 encryption use a 16 bit key, \
|
|
|
|
for AES-192 encryption use a 24 bit key, \
|
|
|
|
and for AES-256 encryption use a 32 bit key\
|
|
|
|
",
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn init() {
|
2022-02-22 14:16:50 +00:00
|
|
|
let setup = Command::new("SurrealDB command-line interface and server")
|
2022-06-24 15:33:12 +00:00
|
|
|
.about(INFO)
|
|
|
|
.before_help(LOGO)
|
2022-02-22 14:16:50 +00:00
|
|
|
.disable_version_flag(true)
|
|
|
|
.arg_required_else_help(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("verbose")
|
|
|
|
.short('v')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("verbose")
|
2022-01-23 12:30:59 +00:00
|
|
|
.takes_value(false)
|
|
|
|
.multiple_occurrences(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("Specify the log output verbosity"),
|
|
|
|
);
|
|
|
|
|
|
|
|
let setup = setup.subcommand(
|
2022-02-22 14:16:50 +00:00
|
|
|
Command::new("start")
|
2020-06-29 15:36:01 +00:00
|
|
|
.display_order(1)
|
|
|
|
.about("Start the database server")
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("path")
|
2020-06-29 15:36:01 +00:00
|
|
|
.index(1)
|
2022-01-23 12:30:59 +00:00
|
|
|
.required(false)
|
2022-01-13 06:56:24 +00:00
|
|
|
.validator(path_valid)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("memory")
|
|
|
|
.help("Database path used for storing data"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-05-07 14:06:46 +00:00
|
|
|
Arg::new("user")
|
2022-01-23 12:30:59 +00:00
|
|
|
.short('u')
|
2022-05-07 14:06:46 +00:00
|
|
|
.long("user")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("The master username for the database"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-05-07 14:06:46 +00:00
|
|
|
Arg::new("pass")
|
2022-01-23 12:30:59 +00:00
|
|
|
.short('p')
|
2022-05-07 14:06:46 +00:00
|
|
|
.long("pass")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2022-05-07 14:15:11 +00:00
|
|
|
.default_value(PASS.as_str())
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The master password for the database"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-05-07 14:06:46 +00:00
|
|
|
Arg::new("addr")
|
|
|
|
.long("addr")
|
2020-06-29 15:36:01 +00:00
|
|
|
.number_of_values(1)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
|
|
|
.multiple_occurrences(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("127.0.0.1/32")
|
|
|
|
.help("The allowed networks for master authentication"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("bind")
|
|
|
|
.short('b')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("bind")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2022-07-18 17:49:43 +00:00
|
|
|
.default_value("0.0.0.0:8000")
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The hostname or ip address to listen for connections on"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("key")
|
|
|
|
.short('k')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("key")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.validator(key_valid)
|
|
|
|
.help("Encryption key to use for on-disk encryption"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("kvs-ca")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("kvs-ca")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("Path to the CA file used when connecting to the remote KV store"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("kvs-crt")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("kvs-crt")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help(
|
|
|
|
"Path to the certificate file used when connecting to the remote KV store",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("kvs-key")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("kvs-key")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help(
|
|
|
|
"Path to the private key file used when connecting to the remote KV store",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("web-crt")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("web-crt")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("Path to the certificate file for encrypted client connections"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("web-key")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("web-key")
|
|
|
|
.takes_value(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("Path to the private key file for encrypted client connections"),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let setup = setup.subcommand(
|
2022-02-22 14:16:50 +00:00
|
|
|
Command::new("backup")
|
2020-06-29 15:36:01 +00:00
|
|
|
.display_order(2)
|
|
|
|
.about("Backup data to or from an existing database")
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("from")
|
2020-06-29 15:36:01 +00:00
|
|
|
.index(1)
|
|
|
|
.required(true)
|
|
|
|
.validator(from_valid)
|
|
|
|
.help("Path to the remote database or file from which to export"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("into")
|
2020-06-29 15:36:01 +00:00
|
|
|
.index(2)
|
|
|
|
.required(true)
|
|
|
|
.validator(into_valid)
|
|
|
|
.help("Path to the remote database or file into which to import"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("user")
|
|
|
|
.short('u')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("user")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication username to use when connecting"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("pass")
|
|
|
|
.short('p')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("pass")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication password to use when connecting"),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let setup = setup.subcommand(
|
2022-02-22 14:16:50 +00:00
|
|
|
Command::new("import")
|
2020-06-29 15:36:01 +00:00
|
|
|
.display_order(3)
|
|
|
|
.about("Import a SQL script into an existing database")
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("file")
|
2020-06-29 15:36:01 +00:00
|
|
|
.index(1)
|
|
|
|
.required(true)
|
|
|
|
.validator(file_valid)
|
|
|
|
.help("Path to the sql file to import"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("ns")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("ns")
|
|
|
|
.required(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The namespace to import the data into"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("db")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("db")
|
|
|
|
.required(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The database to import the data into"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("conn")
|
|
|
|
.short('c')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("conn")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.validator(conn_valid)
|
|
|
|
.default_value("https://surreal.io")
|
|
|
|
.help("Remote database server url to connect to"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("user")
|
|
|
|
.short('u')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("user")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication username to use when connecting"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("pass")
|
|
|
|
.short('p')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("pass")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication password to use when connecting"),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let setup = setup.subcommand(
|
2022-02-22 14:16:50 +00:00
|
|
|
Command::new("export")
|
2020-06-29 15:36:01 +00:00
|
|
|
.display_order(4)
|
|
|
|
.about("Export an existing database into a SQL script")
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("file")
|
2020-06-29 15:36:01 +00:00
|
|
|
.index(1)
|
|
|
|
.required(true)
|
|
|
|
.validator(file_valid)
|
|
|
|
.help("Path to the sql file to export"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("ns")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("ns")
|
|
|
|
.required(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The namespace to export the data from"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("db")
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("db")
|
|
|
|
.required(true)
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.help("The database to export the data from"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("conn")
|
|
|
|
.short('c')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("conn")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.validator(conn_valid)
|
|
|
|
.default_value("https://surreal.io")
|
|
|
|
.help("Remote database server url to connect to"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("user")
|
|
|
|
.short('u')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("user")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication username to use when connecting"),
|
|
|
|
)
|
|
|
|
.arg(
|
2022-01-23 12:30:59 +00:00
|
|
|
Arg::new("pass")
|
|
|
|
.short('p')
|
2020-06-29 15:36:01 +00:00
|
|
|
.long("pass")
|
2022-01-23 12:30:59 +00:00
|
|
|
.forbid_empty_values(true)
|
2020-06-29 15:36:01 +00:00
|
|
|
.default_value("root")
|
|
|
|
.help("Database authentication password to use when connecting"),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let setup = setup.subcommand(
|
2022-02-22 14:16:50 +00:00
|
|
|
Command::new("version")
|
2020-06-29 15:36:01 +00:00
|
|
|
.display_order(5)
|
|
|
|
.about("Output the command-line tool version information"),
|
|
|
|
);
|
|
|
|
|
|
|
|
let matches = setup.get_matches();
|
|
|
|
|
|
|
|
let verbose = matches.occurrences_of("verbose") as usize;
|
|
|
|
|
|
|
|
log::init(verbose);
|
|
|
|
|
|
|
|
let output = match matches.subcommand() {
|
2022-01-23 12:30:59 +00:00
|
|
|
Some(("start", m)) => start::init(m),
|
|
|
|
Some(("backup", m)) => backup::init(m),
|
|
|
|
Some(("import", m)) => import::init(m),
|
|
|
|
Some(("export", m)) => export::init(m),
|
|
|
|
Some(("version", m)) => version::init(m),
|
2020-06-29 15:36:01 +00:00
|
|
|
_ => Ok(()),
|
|
|
|
};
|
|
|
|
|
2022-03-04 16:01:32 +00:00
|
|
|
if let Err(e) = output {
|
|
|
|
error!("{}", e);
|
|
|
|
}
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|