Skip to content

Commit

Permalink
Merge pull request #127 from Goval-Community/goval-ident
Browse files Browse the repository at this point in the history
feat(services, goval-ident): Implement chat variant of `goval-ident` protocol
  • Loading branch information
PotentialStyx authored Apr 28, 2024
2 parents f060083 + 35ce3ad commit 02ead2b
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 28 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ See https://govaldocs.pages.dev"""
members = [".", "migration", "entity", "services", "protobuf"]

[features]
default = ["replspace", "database", "repldb", "verify_connections"]
default = ["replspace", "database", "repldb", "verify_connections", "goval-ident"]
repldb = ["database"]
database = ["dep:sea-orm", "dep:sea-query", "dep:migration", "dep:entity"]
replspace = []
fun-stuff = ["dep:chrono", "dep:chrono-tz"]
verify_connections = ["dep:hyper", "dep:hyper-tls", "dep:hyper-util", "dep:http-body-util"]
goval-ident = ["homeval_services/goval-ident"]

[dependencies]
goval = { path = "protobuf", package = "protobuf" }
Expand Down
6 changes: 5 additions & 1 deletion services/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
goval-ident = ["dep:serde_json"]

[dependencies]
anyhow = "1.0.81"
Expand All @@ -18,12 +20,14 @@ prost = "0.12.4"
prost-types = "0.12.3"
ropey = "1.6.0"
serde = "1.0.197"
serde_json = "1.0.115"
similar = "2.2.1"
tokio = "1.36.0"
tracing = "0.1.40"
tracing-futures = "0.2.5"

# goval-ident features
serde_json = { version = "1.0.116", optional = true }

[lib]
name = "services"
path = "src/lib.rs"
18 changes: 5 additions & 13 deletions services/src/gcsfiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tracing::{debug, warn};
impl traits::Service for GCSFiles {
async fn message(
&mut self,
_info: &super::types::ChannelInfo,
#[allow(unused)] info: &super::types::ChannelInfo,
message: goval::Command,
_session: SessionID,
) -> Result<Option<goval::Command>> {
Expand Down Expand Up @@ -64,19 +64,11 @@ impl traits::Service for GCSFiles {
let contents = match file.path.as_str() {
// TODO: Read this from in the db
".env" => vec![],
#[cfg(feature = "goval-ident")]
".config/goval/info" => {
let val = serde_json::json!({
"server": "homeval",
"version": env!("CARGO_PKG_VERSION").to_string(),
"license": "AGPL",
"authors": vec!["PotentialStyx <[email protected]>"],
"repository": "https://github.com/goval-community/homeval",
"description": "", // TODO: do dis
"uptime": 0, // TODO: impl fo realz
"services": super::IMPLEMENTED_SERVICES
});

val.to_string().as_bytes().to_vec()
serde_json::to_string(&info.server_info.get_serializable())?
.as_bytes()
.to_vec()
}
_ => match fs::read(&file.path).await {
Err(err) => {
Expand Down
131 changes: 131 additions & 0 deletions services/src/goval_ident.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
pub struct GovalIdent {}

use crate::{server_info::ServerInfoSerialize, ClientInfo, IPCMessage, SessionID};

use super::traits;
use anyhow::{format_err, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tracing::{error, warn};

#[derive(Deserialize)]
enum IdentReqType {
ServerInfo,

#[serde(untagged)]
Unknown(String),
}

#[derive(Deserialize)]
struct IdentRequest {
r#type: IdentReqType,
r#ref: String,
}

#[derive(Serialize)]
#[serde(untagged)]
enum IdentResponseEnum<'a> {
Error(String),
Data(ServerInfoSerialize<'a>),
}

#[derive(Serialize)]
struct IdentResponse<'a> {
body: IdentResponseEnum<'a>,
r#ref: String,
}

#[async_trait]
impl traits::Service for GovalIdent {
async fn message(
&mut self,
info: &super::types::ChannelInfo,
message: goval::Command,
session: SessionID,
) -> Result<Option<goval::Command>> {
let body = match message.body.clone() {
None => return Err(format_err!("Expected command body")),
Some(body) => body,
};

match body {
goval::command::Body::ChatMessage(msg) => {
let req: IdentRequest = serde_json::from_str(&msg.text)?;

match req.r#type {
IdentReqType::ServerInfo => {
let msg_text = serde_json::to_string(&IdentResponse {
body: IdentResponseEnum::Data(info.server_info.get_serializable()),
r#ref: req.r#ref,
})?;

let response_body = goval::command::Body::ChatMessage(goval::ChatMessage {
username: "goval".to_string(),
text: msg_text,
});

let response = goval::Command {
body: Some(response_body),
..Default::default()
};

Ok(Some(response))
}
IdentReqType::Unknown(req_type) => {
error!(req_type, "Unknown `goval-ident` request type");

let msg_text = serde_json::to_string(&IdentResponse {
body: IdentResponseEnum::Error(format!(
"Unknown request type {req_type}"
)),
r#ref: req.r#ref,
})?;

let response_body = goval::command::Body::ChatMessage(goval::ChatMessage {
username: "goval".to_string(),
text: msg_text,
});

let response = goval::Command {
body: Some(response_body),
..Default::default()
};

Ok(Some(response))
}
}
}
goval::command::Body::ChatTyping(typing) => {
warn!(
%session,
username = typing.username,
"Chat#ChatTyping isn't supported by `goval-ident`",
);
Ok(None)
}
_ => {
warn!(cmd = ?message, "Unknown goval-ident command");
Ok(None)
}
}
}

async fn attach(
&mut self,
_info: &super::types::ChannelInfo,
_client: ClientInfo,
_session: SessionID,
_sender: tokio::sync::mpsc::UnboundedSender<IPCMessage>,
) -> Result<Option<goval::Command>> {
let mut scrollback = goval::Command::default();

let _inner = goval::ChatMessage {
username: "goval".to_string(),
text: "{\"type\": \"notification\"}".to_string(),
};

scrollback.body = Some(goval::command::Body::ChatMessage(_inner));

Ok(Some(scrollback))
}
}
26 changes: 24 additions & 2 deletions services/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(extract_if)]
#![feature(extract_if, lazy_cell)]
#![warn(
clippy::pedantic,
clippy::unwrap_used,
Expand Down Expand Up @@ -36,6 +36,9 @@ mod toolchain;
mod traits;
mod types;

#[cfg(feature = "goval-ident")]
mod goval_ident;

use anyhow::format_err;
use anyhow::Result;
use std::collections::HashMap;
Expand All @@ -58,7 +61,12 @@ impl Channel {
dotreplit: Arc<RwLock<DotReplit>>,
child_env_vars: Arc<RwLock<HashMap<String, String>>>,
sender: tokio::sync::mpsc::UnboundedSender<ChannelMessage>,

server_info: &'static ServerInfo<'static>,
) -> Result<Channel> {
#[cfg(feature = "goval-ident")]
let goval_ident = name.as_deref() == Some("goval-ident");

let info = ChannelInfo {
id,
name,
Expand All @@ -68,10 +76,24 @@ impl Channel {
sender: sender.clone(),
dotreplit,
child_env_vars,

server_info,
};

let channel: Box<dyn traits::Service + Send> = match service.as_str() {
"chat" => Box::new(chat::Chat::new()),
"chat" => {
#[cfg(feature = "goval-ident")]
{
if goval_ident {
Box::new(goval_ident::GovalIdent {})
} else {
Box::new(chat::Chat::new())
}
}

#[cfg(not(feature = "goval-ident"))]
Box::new(chat::Chat::new())
}
"gcsfiles" => Box::new(gcsfiles::GCSFiles {}),
"presence" => Box::new(presence::Presence::new()),
"ot" => Box::new(ot::OT::new(sender).await?),
Expand Down
4 changes: 3 additions & 1 deletion services/src/types/channel_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tokio::sync::RwLock;
use tracing::error;

use crate::config::dotreplit::DotReplit;
use crate::{ChannelID, SessionID};
use crate::{ChannelID, ServerInfo, SessionID};

use super::client::ClientInfo;
use super::messaging::IPCMessage;
Expand All @@ -27,6 +27,8 @@ pub struct ChannelInfo {
pub sender: tokio::sync::mpsc::UnboundedSender<super::ChannelMessage>,
pub dotreplit: Arc<RwLock<DotReplit>>,
pub child_env_vars: Arc<RwLock<HashMap<String, String>>>,

pub server_info: &'static ServerInfo<'static>,
}

impl ChannelInfo {
Expand Down
4 changes: 1 addition & 3 deletions services/src/types/fs_watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use notify_debouncer_full::{
notify::{self, event::ModifyKind, Event, EventKind, RecommendedWatcher, Watcher},
DebounceEventResult, Debouncer,
};
use serde::Serialize;
use tracing::error;

use anyhow::{format_err, Result};
Expand All @@ -20,8 +19,7 @@ use crate::ChannelMessage;
// > = LazyLock::new(|| RwLock::new(HashMap::new()));
// static MAX_WATCHER: LazyLock<Mutex<u32>> = LazyLock::new(|| Mutex::new(0));

#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone)]
pub enum FSEvent {
Remove(String),
Create(String),
Expand Down
4 changes: 1 addition & 3 deletions services/src/types/messaging.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use anyhow::Result;
use prost::Message;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender;

use crate::{SendSessions, SessionID};

use super::client::ClientInfo;

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone)]
pub enum ReplspaceMessage {
GithubTokenReq(String), // nonce
OpenFileReq(String, bool, String), // file, wait for close, nonce
Expand Down
3 changes: 3 additions & 0 deletions services/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub mod channel_info;
pub use channel_info::{ChannelInfo, SendSessions};

pub mod server_info;
pub use server_info::ServerInfo;

pub mod messaging;
pub use messaging::{ChannelMessage, IPCMessage, ReplspaceMessage};

Expand Down
46 changes: 46 additions & 0 deletions services/src/types/server_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::{sync::LazyLock, time::Instant};

#[cfg(feature = "goval-ident")]
use serde::Serialize;

pub struct ServerInfo<'a> {
pub name: &'a str,
pub version: &'a str,
pub license: &'a str,
pub repository: &'a str,
pub description: &'a str,

pub start_time: &'a LazyLock<Instant>,

// TODO: make this &[&str] (is this easily possible?)
pub authors: &'a LazyLock<Vec<&'a str>>,
}

#[cfg(feature = "goval-ident")]
#[derive(Serialize, Debug)]
pub struct ServerInfoSerialize<'a> {
pub server: &'a str,
pub version: &'a str,
pub license: &'a str,
pub authors: &'a [&'a str],
pub repository: &'a str,
pub description: &'a str,
pub uptime: u64,
pub services: &'a [&'a str],
}

#[cfg(feature = "goval-ident")]
impl<'a> ServerInfo<'a> {
pub fn get_serializable(&self) -> ServerInfoSerialize<'a> {
ServerInfoSerialize {
server: self.name,
version: self.version,
license: self.license,
authors: self.authors,
repository: self.repository,
description: self.description,
uptime: self.start_time.elapsed().as_secs(),
services: crate::IMPLEMENTED_SERVICES,
}
}
}
5 changes: 3 additions & 2 deletions src/goval_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use tokio::sync::mpsc;
use tracing::{debug, error, info, trace, warn};

use crate::{
parse_paseto::parse, ChannelMessage, IPCMessage, CHANNEL_MESSAGES, CHILD_PROCS_ENV_BASE,
DOTREPLIT_CONFIG, LAST_SESSION_USING_CHANNEL,
parse_paseto::parse, server_info::SERVER_INFO, ChannelMessage, IPCMessage, CHANNEL_MESSAGES,
CHILD_PROCS_ENV_BASE, DOTREPLIT_CONFIG, LAST_SESSION_USING_CHANNEL,
};

static MAX_SESSION: LazyLock<Mutex<i32>> = LazyLock::new(|| Mutex::new(0));
Expand Down Expand Up @@ -281,6 +281,7 @@ async fn open_channel(
DOTREPLIT_CONFIG.clone(),
CHILD_PROCS_ENV_BASE.clone(),
writer,
&SERVER_INFO,
)
.await
.expect("TODO: Deal with this");
Expand Down
Loading

0 comments on commit 02ead2b

Please sign in to comment.