From 333329c631f91c9a22c91a6fea905c12f2b9ec0a Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sun, 6 Feb 2022 15:32:33 -0700 Subject: [PATCH] Added local api server + geoffrey_cli + Local api server is designed to allow local access to Geoffrey without a token + Uses a unix domain socket, allowing permissions to be handled by the OS + Started work on a tool to exploit this, geoffrey-cli --- Cargo.lock | 91 +++++++++---- Cargo.toml | 3 +- README.md | 1 + geoffrey_api/Cargo.toml | 3 +- geoffrey_api/src/api_endpoint.rs | 11 ++ geoffrey_api/src/commands/add_item.rs | 25 ++-- geoffrey_api/src/commands/add_location.rs | 25 ++-- geoffrey_api/src/commands/add_token.rs | 28 ++-- geoffrey_api/src/commands/delete.rs | 23 ++-- geoffrey_api/src/commands/edit.rs | 23 ++-- geoffrey_api/src/commands/find.rs | 23 ++-- geoffrey_api/src/commands/info.rs | 26 ++-- geoffrey_api/src/commands/link.rs | 28 ++-- geoffrey_api/src/commands/mod.rs | 54 +++----- geoffrey_api/src/commands/register.rs | 23 ++-- geoffrey_api/src/commands/remove_item.rs | 23 ++-- .../src/commands/report_out_of_stock.rs | 25 ++-- geoffrey_api/src/commands/restock.rs | 25 ++-- geoffrey_api/src/commands/selling.rs | 39 +++--- geoffrey_api/src/commands/set_portal.rs | 23 ++-- geoffrey_api/src/commands/settings.rs | 30 ----- geoffrey_api/src/config.rs | 1 + geoffrey_api/src/helper/mod.rs | 8 +- geoffrey_api/src/main.rs | 57 +++++++- geoffrey_api/src/model/mod.rs | 123 ++++++++++++++++++ geoffrey_api/src/model/settings.rs | 35 +++++ geoffrey_cli/Cargo.toml | 14 ++ geoffrey_cli/src/main.rs | 28 ++++ geoffrey_models/src/models/parameters/mod.rs | 31 +++++ 29 files changed, 612 insertions(+), 237 deletions(-) create mode 100644 geoffrey_api/src/api_endpoint.rs create mode 100644 geoffrey_api/src/model/mod.rs create mode 100644 geoffrey_api/src/model/settings.rs create mode 100644 geoffrey_cli/Cargo.toml create mode 100644 geoffrey_cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index e2efbb7..60f2a16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,7 +158,7 @@ dependencies = [ "libc", "num-integer", "num-traits 0.2.14", - "serde 1.0.133", + "serde 1.0.136", "time", "winapi", ] @@ -198,7 +198,7 @@ dependencies = [ "lazy_static", "nom", "rust-ini", - "serde 1.0.133", + "serde 1.0.136", "serde-hjson", "serde_json", "toml", @@ -369,6 +369,17 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" +[[package]] +name = "futures-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.19" @@ -390,6 +401,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -428,12 +440,13 @@ dependencies = [ "log", "rand 0.8.4", "regex", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "simple_logger", "strsim 0.10.0", "structopt", "tokio", + "tokio-stream", "warp", ] @@ -447,7 +460,7 @@ dependencies = [ "geoffrey_models", "log", "reqwest", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "serde_plain", "serenity", @@ -456,6 +469,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "geoffrey_cli" +version = "0.1.0" +dependencies = [ + "geoffrey_models", + "hyper", + "hyperlocal", + "serde 1.0.136", + "serde_json", + "tokio", +] + [[package]] name = "geoffrey_db" version = "0.1.0" @@ -466,7 +491,7 @@ dependencies = [ "lazy_static", "log", "regex", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "sled", ] @@ -477,7 +502,7 @@ version = "0.1.0" dependencies = [ "chrono", "log", - "serde 1.0.133", + "serde 1.0.136", "serde_json", ] @@ -571,6 +596,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.6" @@ -655,6 +686,19 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project", + "tokio", +] + [[package]] name = "idna" version = "0.2.3" @@ -1225,15 +1269,16 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4e0a76dc12a116108933f6301b95e83634e0c47b0afbed6abbaa0601e99258" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" dependencies = [ "base64 0.13.0", "bytes 1.1.0", "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "hyper", @@ -1250,7 +1295,7 @@ dependencies = [ "pin-project-lite", "rustls 0.20.2", "rustls-pemfile", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "serde_urlencoded", "tokio", @@ -1405,9 +1450,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.133" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] @@ -1426,9 +1471,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.133" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -1437,13 +1482,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.74" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" dependencies = [ "itoa 1.0.1", "ryu", - "serde 1.0.133", + "serde 1.0.136", ] [[package]] @@ -1452,7 +1497,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95455e7e29fada2052e72170af226fbe368a4ca33dee847875325d9fdb133858" dependencies = [ - "serde 1.0.133", + "serde 1.0.136", ] [[package]] @@ -1464,7 +1509,7 @@ dependencies = [ "form_urlencoded", "itoa 0.4.8", "ryu", - "serde 1.0.133", + "serde 1.0.136", ] [[package]] @@ -1482,7 +1527,7 @@ dependencies = [ "futures", "percent-encoding", "reqwest", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "tokio", "tracing", @@ -1692,9 +1737,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ "bytes 1.1.0", "libc", @@ -1796,7 +1841,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde 1.0.133", + "serde 1.0.136", ] [[package]] @@ -2016,7 +2061,7 @@ dependencies = [ "percent-encoding", "pin-project", "scoped-tls", - "serde 1.0.133", + "serde 1.0.136", "serde_json", "serde_urlencoded", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 7022159..18995fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ members = [ "geoffrey_models", "geoffrey_db", "geoffrey_api", - "geoffrey_bot" + "geoffrey_bot", + "geoffrey_cli" ] \ No newline at end of file diff --git a/README.md b/README.md index f782055..6620f77 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ own library for reuse. * [`geoffrey_api`](./geoffrey_api): API wrapper around the database to provide data to the website, bot, plugin, etc. It will implement the command API and the model API. A lot of Geoffrey's logic is implemented here. * [`geoffrey_bot`](./geoffrey_bot): Discord bot for Geoffrey. Uses [serenity](https://github.com/serenity-rs/serenity) +* ['geoffrey_cli`](./geoffrey_cli): CLI tool for interacting with Geoffrey locally or on a remote server. ## License [License](LICENSE) \ No newline at end of file diff --git a/geoffrey_api/Cargo.toml b/geoffrey_api/Cargo.toml index 1e4f2a3..161e1a8 100644 --- a/geoffrey_api/Cargo.toml +++ b/geoffrey_api/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.16.1", features = ["full"] } warp = "0.3" serde = "1.0.124" serde_json = "1.0.64" @@ -20,6 +20,7 @@ rand = "0.8.4" regex = "1.5.4" chrono = { version = "0.4.19", features = ["serde"] } strsim = "0.10.0" +tokio-stream = { version = "0.1.8", features = ["net"] } # Doing this for now, as there seems to be an issue with using timestamps [dependencies.simple_logger] diff --git a/geoffrey_api/src/api_endpoint.rs b/geoffrey_api/src/api_endpoint.rs new file mode 100644 index 0000000..4106ca7 --- /dev/null +++ b/geoffrey_api/src/api_endpoint.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone, PartialEq)] +#[allow(clippy::upper_case_acronyms)] +pub enum RequestType { + POST, + GET, +} + +pub trait ApiEndpoint { + fn endpoint_name() -> String; + fn request_type() -> RequestType; +} diff --git a/geoffrey_api/src/commands/add_item.rs b/geoffrey_api/src/commands/add_item.rs index 882b95e..eacb327 100644 --- a/geoffrey_api/src/commands/add_item.rs +++ b/geoffrey_api/src/commands/add_item.rs @@ -1,7 +1,5 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::helper::validate_string_parameter; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::item::ItemListing; @@ -11,21 +9,28 @@ use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::settings::GeoffreySettings; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::helper::validate_string_parameter; +use crate::Result; pub struct AddItem {} -impl Command for AddItem { - type Req = AddItemParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for AddItem { + fn endpoint_name() -> String { "add_item".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for AddItem { + type Req = AddItemParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/add_location.rs b/geoffrey_api/src/commands/add_location.rs index f6988eb..b46f8c3 100644 --- a/geoffrey_api/src/commands/add_location.rs +++ b/geoffrey_api/src/commands/add_location.rs @@ -1,28 +1,33 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::helper::validate_string_parameter; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_models::models::locations::{Location, LocationDb}; use geoffrey_models::models::parameters::add_location_params::AddLocationParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::settings::GeoffreySettings; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::helper::validate_string_parameter; +use crate::Result; pub struct AddLocation {} -impl Command for AddLocation { - type Req = AddLocationParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for AddLocation { + fn endpoint_name() -> String { "add_location".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for AddLocation { + type Req = AddLocationParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/add_token.rs b/geoffrey_api/src/commands/add_token.rs index 27063da..c842c7f 100644 --- a/geoffrey_api/src/commands/add_token.rs +++ b/geoffrey_api/src/commands/add_token.rs @@ -1,28 +1,34 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + +use rand::distributions::Alphanumeric; +use rand::Rng; + use geoffrey_models::models::parameters::add_token_params::AddTokenParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::token::Token; use geoffrey_models::models::CommandLevel; -use rand::distributions::Alphanumeric; -use rand::Rng; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct AddToken {} -impl Command for AddToken { - type Req = AddTokenParams; - type Resp = Token; - - fn command_name() -> String { +impl ApiEndpoint for AddToken { + fn endpoint_name() -> String { "add_token".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for AddToken { + type Req = AddTokenParams; + type Resp = Token; fn command_level() -> CommandLevel { CommandLevel::ADMIN diff --git a/geoffrey_api/src/commands/delete.rs b/geoffrey_api/src/commands/delete.rs index 4a4dc65..f0c3bda 100644 --- a/geoffrey_api/src/commands/delete.rs +++ b/geoffrey_api/src/commands/delete.rs @@ -1,6 +1,5 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::locations::{Location, LocationDb}; @@ -8,21 +7,27 @@ use geoffrey_models::models::parameters::delete_params::DeleteParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct Delete {} -impl Command for Delete { - type Req = DeleteParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for Delete { + fn endpoint_name() -> String { "delete".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for Delete { + type Req = DeleteParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/edit.rs b/geoffrey_api/src/commands/edit.rs index 8bcbd22..6e68ac5 100644 --- a/geoffrey_api/src/commands/edit.rs +++ b/geoffrey_api/src/commands/edit.rs @@ -1,6 +1,5 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::locations::{Location, LocationDb}; @@ -8,21 +7,27 @@ use geoffrey_models::models::parameters::edit_params::EditParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct Edit {} -impl Command for Edit { - type Req = EditParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for Edit { + fn endpoint_name() -> String { "edit".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for Edit { + type Req = EditParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/find.rs b/geoffrey_api/src/commands/find.rs index ffd8eb7..0444d3e 100644 --- a/geoffrey_api/src/commands/find.rs +++ b/geoffrey_api/src/commands/find.rs @@ -1,27 +1,32 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_models::models::locations::{Location, LocationDb}; use geoffrey_models::models::parameters::find_params::FindParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct FindCommand {} -impl Command for FindCommand { - type Req = FindParams; - type Resp = Vec; - - fn command_name() -> String { +impl ApiEndpoint for FindCommand { + fn endpoint_name() -> String { "find".to_string() } fn request_type() -> RequestType { RequestType::GET } +} + +impl Command for FindCommand { + type Req = FindParams; + type Resp = Vec; fn command_level() -> CommandLevel { CommandLevel::ALL diff --git a/geoffrey_api/src/commands/info.rs b/geoffrey_api/src/commands/info.rs index dc67310..5654329 100644 --- a/geoffrey_api/src/commands/info.rs +++ b/geoffrey_api/src/commands/info.rs @@ -1,6 +1,7 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + +use strsim::normalized_damerau_levenshtein; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::locations::{Location, LocationDb}; @@ -8,22 +9,27 @@ use geoffrey_models::models::parameters::info_params::InfoParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; -use strsim::normalized_damerau_levenshtein; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct InfoCommand {} -impl Command for InfoCommand { - type Req = InfoParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for InfoCommand { + fn endpoint_name() -> String { "info".to_string() } fn request_type() -> RequestType { RequestType::GET } +} + +impl Command for InfoCommand { + type Req = InfoParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::ALL diff --git a/geoffrey_api/src/commands/link.rs b/geoffrey_api/src/commands/link.rs index 0fd28de..d2faed0 100644 --- a/geoffrey_api/src/commands/link.rs +++ b/geoffrey_api/src/commands/link.rs @@ -1,29 +1,35 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use chrono::{Duration, Utc}; +use rand::distributions::Alphanumeric; +use rand::Rng; + use geoffrey_models::models::link::Link; use geoffrey_models::models::parameters::link_params::LinkParameters; use geoffrey_models::models::player::Player; use geoffrey_models::models::CommandLevel; use geoffrey_models::GeoffreyDatabaseModel; -use rand::distributions::Alphanumeric; -use rand::Rng; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct LinkCommand {} -impl Command for LinkCommand { - type Req = LinkParameters; - type Resp = Link; - - fn command_name() -> String { +impl ApiEndpoint for LinkCommand { + fn endpoint_name() -> String { "link".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for LinkCommand { + type Req = LinkParameters; + type Resp = Link; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/mod.rs b/geoffrey_api/src/commands/mod.rs index 0427cee..e8a158f 100644 --- a/geoffrey_api/src/commands/mod.rs +++ b/geoffrey_api/src/commands/mod.rs @@ -1,3 +1,20 @@ +use std::fmt::Debug; +use std::sync::Arc; + +use serde::de::DeserializeOwned; +use serde::Serialize; +use warp::filters::BoxedFilter; +use warp::Filter; + +use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; +use geoffrey_models::models::player::Player; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use geoffrey_models::models::response::APIResponse; +use geoffrey_models::models::settings::GeoffreySettings; +use geoffrey_models::models::token::{Permissions, Token}; +use geoffrey_models::models::CommandLevel; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; use crate::commands::add_item::AddItem; use crate::commands::add_location::AddLocation; use crate::commands::delete::Delete; @@ -11,23 +28,9 @@ use crate::commands::report_out_of_stock::ReportOutOfStock; use crate::commands::restock::Restock; use crate::commands::selling::Selling; use crate::commands::set_portal::SetPortal; -use crate::commands::settings::Settings; use crate::context::Context; use crate::helper::{get_player_from_req, get_token_from_req}; use crate::Result; -use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; -use geoffrey_models::models::player::Player; -use geoffrey_models::models::response::api_error::GeoffreyAPIError; -use geoffrey_models::models::response::APIResponse; -use geoffrey_models::models::settings::GeoffreySettings; -use geoffrey_models::models::token::{Permissions, Token}; -use geoffrey_models::models::CommandLevel; -use serde::de::DeserializeOwned; -use serde::Serialize; -use std::fmt::Debug; -use std::sync::Arc; -use warp::filters::BoxedFilter; -use warp::Filter; pub mod add_item; pub mod add_location; @@ -45,19 +48,10 @@ pub mod selling; pub mod set_portal; pub mod settings; -#[derive(Debug, Clone, PartialEq)] -#[allow(clippy::upper_case_acronyms)] -pub enum RequestType { - POST, - GET, -} - -pub trait Command { +pub trait Command: ApiEndpoint { type Req: GeoffreyParam + 'static; type Resp: Serialize + DeserializeOwned + Send + Debug; - fn command_name() -> String; - fn request_type() -> RequestType; fn command_level() -> CommandLevel; fn run_command(ctx: Arc, req: &Self::Req, user: Option) -> Result; @@ -96,11 +90,11 @@ pub fn handle_command( ctx: Arc, req: CommandRequest, ) -> Result { - log::info!("Running command {}", T::command_name()); + log::info!("Running command {}", T::endpoint_name()); log::debug!("User: {:?} Request params: {:?}", req.user_id, req.params); let user = get_player_from_req::(&ctx.db, &req)?; - let token = get_token_from_req::(&ctx.db, &req)?; + let token = get_token_from_req(&ctx.db, &req)?; match T::user_is_authorized(&token, &user) { Ok(_) => { @@ -113,7 +107,7 @@ pub fn handle_command( #[allow(clippy::needless_return)] pub fn create_command_filter(ctx: Arc) -> BoxedFilter<(impl warp::Reply,)> { - let filter = warp::path(T::command_name()) + let filter = warp::path(T::endpoint_name()) .and(warp::any().map(move || ctx.clone())) .and(warp::body::json()) .map(|ctx: Arc, req: CommandRequest| { @@ -155,9 +149,3 @@ pub fn command_filter( .or(create_command_filter::(ctx)), ) } - -pub fn model_filter( - ctx: Arc, -) -> impl Filter + Clone { - warp::path("model").and(create_command_filter::(ctx)) -} diff --git a/geoffrey_api/src/commands/register.rs b/geoffrey_api/src/commands/register.rs index 8c7b4fd..ceed7c8 100644 --- a/geoffrey_api/src/commands/register.rs +++ b/geoffrey_api/src/commands/register.rs @@ -1,27 +1,32 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_models::models::link::Link; use geoffrey_models::models::parameters::register_params::RegisterParameters; use geoffrey_models::models::player::{Player, UserID}; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; use geoffrey_models::GeoffreyDatabaseModel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct Register {} -impl Command for Register { - type Req = RegisterParameters; - type Resp = Player; - - fn command_name() -> String { +impl ApiEndpoint for Register { + fn endpoint_name() -> String { "register".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for Register { + type Req = RegisterParameters; + type Resp = Player; fn command_level() -> CommandLevel { CommandLevel::ALL diff --git a/geoffrey_api/src/commands/remove_item.rs b/geoffrey_api/src/commands/remove_item.rs index 80fadd4..4735bd0 100644 --- a/geoffrey_api/src/commands/remove_item.rs +++ b/geoffrey_api/src/commands/remove_item.rs @@ -1,6 +1,5 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::locations::{Location, LocationDataDb, LocationDb, LocationType}; @@ -8,21 +7,27 @@ use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct RemoveItem {} -impl Command for RemoveItem { - type Req = ItemCommandParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for RemoveItem { + fn endpoint_name() -> String { "remove_item".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for RemoveItem { + type Req = ItemCommandParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/report_out_of_stock.rs b/geoffrey_api/src/commands/report_out_of_stock.rs index 8b6979d..42fa3eb 100644 --- a/geoffrey_api/src/commands/report_out_of_stock.rs +++ b/geoffrey_api/src/commands/report_out_of_stock.rs @@ -1,6 +1,6 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::collections::HashSet; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::item::ItemListing; @@ -9,22 +9,27 @@ use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::collections::HashSet; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct ReportOutOfStock {} -impl Command for ReportOutOfStock { - type Req = ItemCommandParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for ReportOutOfStock { + fn endpoint_name() -> String { "report_out_of_stock".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for ReportOutOfStock { + type Req = ItemCommandParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/restock.rs b/geoffrey_api/src/commands/restock.rs index b660cad..203590c 100644 --- a/geoffrey_api/src/commands/restock.rs +++ b/geoffrey_api/src/commands/restock.rs @@ -1,6 +1,6 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::collections::HashSet; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::item::ItemListing; @@ -9,22 +9,27 @@ use geoffrey_models::models::parameters::item_command_params::ItemCommandParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::collections::HashSet; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct Restock {} -impl Command for Restock { - type Req = ItemCommandParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for Restock { + fn endpoint_name() -> String { "restock".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for Restock { + type Req = ItemCommandParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/selling.rs b/geoffrey_api/src/commands/selling.rs index b335d22..4539d9e 100644 --- a/geoffrey_api/src/commands/selling.rs +++ b/geoffrey_api/src/commands/selling.rs @@ -1,27 +1,32 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_models::models::item::ItemListing; use geoffrey_models::models::locations::{LocationDataDb, LocationDb}; use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, SellingParams}; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::selling_listing::SellingListing; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct Selling {} -impl Command for Selling { - type Req = SellingParams; - type Resp = Vec; - - fn command_name() -> String { +impl ApiEndpoint for Selling { + fn endpoint_name() -> String { "selling".to_string() } fn request_type() -> RequestType { RequestType::GET } +} + +impl Command for Selling { + type Req = SellingParams; + type Resp = Vec; fn command_level() -> CommandLevel { CommandLevel::ALL @@ -99,19 +104,21 @@ impl Command for Selling { #[cfg(test)] mod test { - use crate::commands::selling::Selling; - use crate::commands::Command; - use crate::config::{GeoffreyAPIConfig, ServerConfig}; - use crate::context::Context; - use crate::Args; + use std::path::PathBuf; + use std::time::Instant; + use geoffrey_models::models::item::ItemListing; use geoffrey_models::models::locations::shop::Shop; use geoffrey_models::models::locations::{LocationDataDb, LocationDb}; use geoffrey_models::models::parameters::selling_params::SellingParams; use geoffrey_models::models::settings::GeoffreySettings; use geoffrey_models::models::Position; - use std::path::PathBuf; - use std::time::Instant; + + use crate::commands::selling::Selling; + use crate::commands::Command; + use crate::config::{GeoffreyAPIConfig, ServerConfig}; + use crate::context::Context; + use crate::Args; fn test_selling_lookup_speed(iter: i32, shop_count: i32, items_for_sale: i32) -> f32 { let config = GeoffreyAPIConfig { diff --git a/geoffrey_api/src/commands/set_portal.rs b/geoffrey_api/src/commands/set_portal.rs index 6dea219..da067d0 100644 --- a/geoffrey_api/src/commands/set_portal.rs +++ b/geoffrey_api/src/commands/set_portal.rs @@ -1,6 +1,5 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; +use std::sync::Arc; + use geoffrey_db::helper::load_location; use geoffrey_db::query::QueryBuilder; use geoffrey_models::models::locations::{Location, LocationDb}; @@ -8,21 +7,27 @@ use geoffrey_models::models::parameters::set_portal_params::SetPortalParams; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::CommandLevel; -use std::sync::Arc; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::commands::Command; +use crate::context::Context; +use crate::Result; pub struct SetPortal {} -impl Command for SetPortal { - type Req = SetPortalParams; - type Resp = Location; - - fn command_name() -> String { +impl ApiEndpoint for SetPortal { + fn endpoint_name() -> String { "set_portal".to_string() } fn request_type() -> RequestType { RequestType::POST } +} + +impl Command for SetPortal { + type Req = SetPortalParams; + type Resp = Location; fn command_level() -> CommandLevel { CommandLevel::REGISTERED diff --git a/geoffrey_api/src/commands/settings.rs b/geoffrey_api/src/commands/settings.rs index 66c5cd7..8b13789 100644 --- a/geoffrey_api/src/commands/settings.rs +++ b/geoffrey_api/src/commands/settings.rs @@ -1,31 +1 @@ -use crate::commands::{Command, RequestType}; -use crate::context::Context; -use crate::Result; -use geoffrey_models::models::parameters::EmptyRequest; -use geoffrey_models::models::player::Player; -use geoffrey_models::models::settings::GeoffreySettings; -use geoffrey_models::models::CommandLevel; -use std::sync::Arc; -pub struct Settings {} - -impl Command for Settings { - type Req = EmptyRequest; - type Resp = GeoffreySettings; - - fn command_name() -> String { - "settings".to_string() - } - - fn request_type() -> RequestType { - RequestType::GET - } - - fn command_level() -> CommandLevel { - CommandLevel::ALL - } - - fn run_command(ctx: Arc, _: &Self::Req, _: Option) -> Result { - Ok(ctx.cfg.geoffrey_settings.clone()) - } -} diff --git a/geoffrey_api/src/config.rs b/geoffrey_api/src/config.rs index c09e31d..6372779 100644 --- a/geoffrey_api/src/config.rs +++ b/geoffrey_api/src/config.rs @@ -13,6 +13,7 @@ pub struct GeoffreyAPIConfig { pub struct ServerConfig { pub db_path: PathBuf, pub host: String, + pub local_socket: Option, } impl GeoffreyAPIConfig { diff --git a/geoffrey_api/src/helper/mod.rs b/geoffrey_api/src/helper/mod.rs index de315cb..ea2b737 100644 --- a/geoffrey_api/src/helper/mod.rs +++ b/geoffrey_api/src/helper/mod.rs @@ -1,6 +1,6 @@ use crate::Result; use geoffrey_db::database::Database; -use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam}; +use geoffrey_models::models::parameters::{CommandRequest, GeoffreyParam, GeoffreyRequest}; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::token::Token; @@ -18,12 +18,12 @@ pub fn get_player_from_req( } } -pub fn get_token_from_req( +pub fn get_token_from_req>( db: &Database, - req: &CommandRequest, + req: &U, ) -> Result> { Ok(db - .filter(|_, token: &Token| token.secret == req.token)? + .filter(|_, token: &Token| token.secret == req.get_token())? .next()) } diff --git a/geoffrey_api/src/main.rs b/geoffrey_api/src/main.rs index 3363bcb..899a899 100644 --- a/geoffrey_api/src/main.rs +++ b/geoffrey_api/src/main.rs @@ -8,22 +8,28 @@ use warp::Filter; use structopt::StructOpt; +use tokio::net::UnixListener; + use geoffrey_models::logging::LogLevel; use geoffrey_models::models::parameters::add_token_params::AddTokenParams; use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::token::Permissions; use crate::commands::add_token::AddToken; -use crate::commands::{command_filter, model_filter, Command}; +use crate::commands::{command_filter, Command}; use crate::config::GeoffreyAPIConfig; use crate::context::Context; use crate::logging::init_logging; +use crate::model::{local_model_filter, remote_model_filter}; +use tokio_stream::wrappers::UnixListenerStream; +mod api_endpoint; mod commands; mod config; mod context; mod helper; mod logging; +mod model; pub type Result = std::result::Result; @@ -66,7 +72,39 @@ pub struct CreateTokenCommand { pub permissions: Vec, } -async fn run_server(ctx: Arc) { +async fn run_local_server(ctx: &Arc) { + let socket_path = ctx + .cfg + .server_config + .local_socket + .as_ref() + .expect("Expected local_socket to be defined"); + + if socket_path.exists() { + if let Err(e) = std::fs::remove_file(&socket_path) { + log::error!("Unable to cleanup local socket: {}", e); + return; + } + } + + let listener = match UnixListener::bind(socket_path) { + Ok(l) => l, + Err(e) => { + log::error!("Unable to open local socket: {}", e); + return; + } + }; + + let unix_listener_stream = UnixListenerStream::new(listener); + + let local_api = local_model_filter(ctx.clone()); + + warp::serve(local_api.clone()) + .run_incoming(unix_listener_stream) + .await; +} + +async fn run_remote_server(ctx: &Arc) { let socket_addr = match SocketAddr::from_str(ctx.cfg.server_config.host.as_str()) { Ok(socket_addr) => socket_addr, Err(e) => { @@ -79,11 +117,24 @@ async fn run_server(ctx: Arc) { } }; - let api = command_filter(ctx.clone()).or(model_filter(ctx.clone())); + let api = command_filter(ctx.clone()).or(remote_model_filter(ctx.clone())); warp::serve(api).run(socket_addr).await; } +async fn run_server(ctx: Arc) { + tokio::select! { + // Start local api + _ = run_local_server(&ctx), if ctx.cfg.server_config.local_socket.is_some() => { + log::warn!("Socket API exited") + } + // Start HTTP api + _ = run_remote_server(&ctx) => { + log::warn!("HTTP API exited") + } + } +} + fn create_token(ctx: Arc, perms: Vec) { match AddToken::run_command(ctx, &AddTokenParams { permissions: perms }, None) { Ok(token) => { diff --git a/geoffrey_api/src/model/mod.rs b/geoffrey_api/src/model/mod.rs new file mode 100644 index 0000000..d537d79 --- /dev/null +++ b/geoffrey_api/src/model/mod.rs @@ -0,0 +1,123 @@ +mod settings; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::context::Context; +use crate::helper::get_token_from_req; +use crate::model::settings::Settings; +use crate::Result; +use geoffrey_models::models::parameters::{GeoffreyParam, ModelRequest}; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use geoffrey_models::models::response::APIResponse; +use geoffrey_models::models::token::{Permissions, Token}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::fmt::Debug; +use std::sync::Arc; +use warp::filters::BoxedFilter; +use warp::Filter; + +pub trait ModelEndpoint: ApiEndpoint { + type Req: GeoffreyParam + 'static; + type Resp: Serialize + DeserializeOwned + Send + Debug; + + fn token_permission() -> Vec; + fn run_endpoint(ctx: Arc, req: &Self::Req) -> Result; + + fn check_token_permission(token: &Token) -> bool { + for perm in Self::token_permission() { + if !token.check_permission(perm) { + return false; + } + } + + true + } +} + +pub fn handle_remote_model_request( + ctx: Arc, + req: ModelRequest, +) -> Result { + let token = get_token_from_req(&ctx.db, &req)?; + + if let Some(token) = token { + if T::check_token_permission(&token) { + return T::run_endpoint(ctx, &req.params); + } + } + + Err(GeoffreyAPIError::TokenNotAuthorized) +} + +#[allow(clippy::needless_return)] +pub fn create_remote_model_filter( + ctx: Arc, +) -> BoxedFilter<(impl warp::Reply,)> { + let filter = warp::path(T::endpoint_name()) + .and(warp::any().map(move || ctx.clone())) + .and(warp::body::json()) + .map(|ctx: Arc, req: ModelRequest| { + log::info!("Running model query {}", T::endpoint_name()); + log::debug!("Request params: {:?}", req.params); + + let reply = handle_remote_model_request::(ctx, req); + + if let Ok(reply) = reply { + log::debug!("Successfully processed model request"); + warp::reply::json(&APIResponse::Response::(reply)) + } else { + let e = reply.err().unwrap(); + let msg = e.to_string(); + log::warn!("Got error when processing model request '{:?}': {}", e, msg); + warp::reply::json(&APIResponse::::Error { error: e, msg }) + } + }); + + if T::request_type() == RequestType::POST { + return filter.and(warp::post()).boxed(); + } else { + return filter.and(warp::get()).boxed(); + } +} + +#[allow(clippy::needless_return)] +pub fn create_local_model_filter( + ctx: Arc, +) -> BoxedFilter<(impl warp::Reply,)> { + let filter = warp::path(T::endpoint_name()) + .and(warp::any().map(move || ctx.clone())) + .and(warp::body::json()) + .map(|ctx: Arc, req: T::Req| { + log::info!("Running local model query {}", T::endpoint_name()); + log::debug!("Request params: {:?}", req); + let reply = T::run_endpoint(ctx, &req); + + if let Ok(reply) = reply { + log::debug!("Successfully processed model request"); + warp::reply::json(&APIResponse::Response::(reply)) + } else { + let e = reply.err().unwrap(); + let msg = e.to_string(); + log::warn!("Got error when processing model request '{:?}': {}", e, msg); + warp::reply::json(&APIResponse::::Error { error: e, msg }) + } + }); + + if T::request_type() == RequestType::POST { + return filter.and(warp::post()).boxed(); + } else { + return filter.and(warp::get()).boxed(); + } +} + +pub fn remote_model_filter( + ctx: Arc, +) -> impl Filter + Clone { + warp::path("model").and(create_remote_model_filter::(ctx)) +} + +pub fn local_model_filter( + ctx: Arc, +) -> impl Filter + Clone { + warp::path("model").and(create_local_model_filter::(ctx)) +} diff --git a/geoffrey_api/src/model/settings.rs b/geoffrey_api/src/model/settings.rs new file mode 100644 index 0000000..29848e6 --- /dev/null +++ b/geoffrey_api/src/model/settings.rs @@ -0,0 +1,35 @@ +use std::sync::Arc; + +use geoffrey_models::models::settings::GeoffreySettings; + +use crate::api_endpoint::{ApiEndpoint, RequestType}; +use crate::context::Context; +use crate::model::ModelEndpoint; +use crate::Result; +use geoffrey_models::models::parameters::EmptyRequest; +use geoffrey_models::models::token::Permissions; + +pub struct Settings {} + +impl ApiEndpoint for Settings { + fn endpoint_name() -> String { + "settings".to_string() + } + + fn request_type() -> RequestType { + RequestType::GET + } +} + +impl ModelEndpoint for Settings { + type Req = EmptyRequest; + type Resp = GeoffreySettings; + + fn token_permission() -> Vec { + vec![Permissions::ModelGet] + } + + fn run_endpoint(ctx: Arc, _: &Self::Req) -> Result { + Ok(ctx.cfg.geoffrey_settings.clone()) + } +} diff --git a/geoffrey_cli/Cargo.toml b/geoffrey_cli/Cargo.toml new file mode 100644 index 0000000..32f1204 --- /dev/null +++ b/geoffrey_cli/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "geoffrey_cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hyper = { version = "0.14", features = ["full"] } +hyperlocal = "0.8.0" +tokio = { version = "1", features = ["full"] } +geoffrey_models = { path = "../geoffrey_models" } +serde = "1.0.136" +serde_json = "1.0.78" \ No newline at end of file diff --git a/geoffrey_cli/src/main.rs b/geoffrey_cli/src/main.rs new file mode 100644 index 0000000..caa8447 --- /dev/null +++ b/geoffrey_cli/src/main.rs @@ -0,0 +1,28 @@ +use geoffrey_models::models::parameters::EmptyRequest; +use hyper::body::{Body, HttpBody}; +use hyper::Client; +use hyper::{Method, Request}; +use hyperlocal::{UnixClientExt, Uri}; +use tokio::io::{stdout, AsyncWriteExt as _}; + +#[tokio::main] +async fn main() { + let client = Client::unix(); + + let uri: hyper::Uri = Uri::new("/tmp/geoffrey.socket", "/model/settings/").into(); + + let params = EmptyRequest {}; + + let req = Request::builder() + .method(Method::GET) + .uri(uri) + .header("content-type", "application/json") + .body(Body::from(serde_json::to_string(¶ms).unwrap())) + .unwrap(); + + let mut resp = client.request(req).await.unwrap(); + + while let Some(chunk) = resp.body_mut().data().await { + stdout().write_all(&chunk.unwrap()).await.unwrap(); + } +} diff --git a/geoffrey_models/src/models/parameters/mod.rs b/geoffrey_models/src/models/parameters/mod.rs index 6473208..f152cc0 100644 --- a/geoffrey_models/src/models/parameters/mod.rs +++ b/geoffrey_models/src/models/parameters/mod.rs @@ -18,6 +18,11 @@ use std::fmt::Debug; pub trait GeoffreyParam: Serialize + DeserializeOwned + Debug + Clone + Send + Sync {} +pub trait GeoffreyRequest { + fn get_token(&self) -> String; + fn get_params(&self) -> &T; +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CommandRequest { pub token: String, @@ -25,6 +30,32 @@ pub struct CommandRequest { pub params: T, } +impl GeoffreyRequest for CommandRequest { + fn get_token(&self) -> String { + self.token.clone() + } + + fn get_params(&self) -> &T { + &self.params + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ModelRequest { + pub token: String, + pub params: T, +} + +impl GeoffreyRequest for ModelRequest { + fn get_token(&self) -> String { + self.token.clone() + } + + fn get_params(&self) -> &T { + &self.params + } +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct EmptyRequest {}