From 02718284efda71192d2d2d51931f9d295c563557 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 24 Oct 2024 11:14:03 -0700 Subject: [PATCH] Remove dev servers (#19638) TODO: - [ ] Check that workspace migration worked - [ ] Add server migrations and make sure SeaORM files are in sync (maybe?) Release Notes: - N/A --------- Co-authored-by: Conrad Co-authored-by: Conrad Irwin --- Cargo.lock | 45 - Cargo.toml | 4 - crates/assistant/src/assistant_panel.rs | 2 +- crates/call/src/room.rs | 27 +- crates/cli/src/cli.rs | 1 - crates/cli/src/main.rs | 7 +- crates/client/src/client.rs | 58 +- crates/client/src/test.rs | 4 +- crates/client/src/user.rs | 3 - crates/collab/Cargo.toml | 2 - crates/collab/src/auth.rs | 54 +- crates/collab/src/db.rs | 1 - crates/collab/src/db/ids.rs | 8 - crates/collab/src/db/queries.rs | 2 - .../src/db/queries/dev_server_projects.rs | 364 ----- crates/collab/src/db/queries/dev_servers.rs | 221 --- crates/collab/src/db/queries/projects.rs | 170 +-- crates/collab/src/db/queries/rooms.rs | 54 +- crates/collab/src/db/tables.rs | 2 - crates/collab/src/db/tables/dev_server.rs | 39 - .../src/db/tables/dev_server_project.rs | 59 - crates/collab/src/db/tables/project.rs | 15 +- crates/collab/src/db/tests/db_tests.rs | 6 +- crates/collab/src/rpc.rs | 1275 +++-------------- crates/collab/src/rpc/connection_pool.rs | 127 +- crates/collab/src/tests.rs | 1 - crates/collab/src/tests/dev_server_tests.rs | 643 --------- crates/collab/src/tests/test_server.rs | 135 +- crates/dev_server_projects/Cargo.toml | 23 - crates/dev_server_projects/LICENSE-GPL | 1 - .../src/dev_server_projects.rs | 248 ---- crates/headless/Cargo.toml | 37 - crates/headless/LICENSE-GPL | 1 - crates/headless/src/headless.rs | 397 ----- crates/project/Cargo.toml | 1 - crates/project/src/project.rs | 110 +- crates/project/src/terminals.rs | 28 +- crates/project/src/worktree_store.rs | 63 +- crates/project_panel/src/project_panel.rs | 13 +- crates/proto/proto/zed.proto | 141 +- crates/proto/src/proto.rs | 30 - crates/recent_projects/Cargo.toml | 4 - .../src/disconnected_overlay.rs | 74 +- crates/recent_projects/src/recent_projects.rs | 240 +--- crates/recent_projects/src/remote_servers.rs | 158 -- crates/title_bar/Cargo.toml | 1 - crates/title_bar/src/collab.rs | 5 +- crates/title_bar/src/title_bar.rs | 35 +- crates/workspace/Cargo.toml | 1 - crates/workspace/src/persistence.rs | 171 +-- crates/workspace/src/persistence/model.rs | 48 - crates/workspace/src/workspace.rs | 105 +- crates/zed/Cargo.toml | 2 - crates/zed/src/main.rs | 113 +- crates/zed/src/zed/open_listener.rs | 36 +- 55 files changed, 391 insertions(+), 5024 deletions(-) delete mode 100644 crates/collab/src/db/tables/dev_server.rs delete mode 100644 crates/collab/src/db/tables/dev_server_project.rs delete mode 100644 crates/collab/src/tests/dev_server_tests.rs delete mode 100644 crates/dev_server_projects/Cargo.toml delete mode 120000 crates/dev_server_projects/LICENSE-GPL delete mode 100644 crates/headless/Cargo.toml delete mode 120000 crates/headless/LICENSE-GPL delete mode 100644 crates/headless/src/headless.rs diff --git a/Cargo.lock b/Cargo.lock index 4e86627d80..f4e84f7a03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2550,7 +2550,6 @@ dependencies = [ "ctor", "dashmap 6.0.1", "derive_more", - "dev_server_projects", "editor", "env_logger", "envy", @@ -2561,7 +2560,6 @@ dependencies = [ "git_hosting_providers", "google_ai", "gpui", - "headless", "hex", "http_client", "hyper 0.14.30", @@ -3476,18 +3474,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "dev_server_projects" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "gpui", - "rpc", - "serde", - "serde_json", -] - [[package]] name = "diagnostics" version = "0.1.0" @@ -5274,28 +5260,6 @@ dependencies = [ "http 0.2.12", ] -[[package]] -name = "headless" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "extension", - "fs", - "futures 0.3.30", - "gpui", - "language", - "log", - "node_runtime", - "postage", - "project", - "proto", - "settings", - "shellexpand 2.1.2", - "signal-hook", - "util", -] - [[package]] name = "heck" version = "0.3.3" @@ -8443,7 +8407,6 @@ dependencies = [ "client", "clock", "collections", - "dev_server_projects", "env_logger", "fs", "futures 0.3.30", @@ -8981,8 +8944,6 @@ version = "0.1.0" dependencies = [ "anyhow", "auto_update", - "client", - "dev_server_projects", "editor", "file_finder", "futures 0.3.30", @@ -8999,14 +8960,12 @@ dependencies = [ "project", "release_channel", "remote", - "rpc", "schemars", "serde", "serde_json", "settings", "smol", "task", - "terminal_view", "theme", "ui", "util", @@ -11912,7 +11871,6 @@ dependencies = [ "client", "collections", "command_palette", - "dev_server_projects", "editor", "extensions_ui", "feature_flags", @@ -14309,7 +14267,6 @@ dependencies = [ "collections", "db", "derive_more", - "dev_server_projects", "env_logger", "fs", "futures 0.3.30", @@ -14628,7 +14585,6 @@ dependencies = [ "command_palette_hooks", "copilot", "db", - "dev_server_projects", "diagnostics", "editor", "env_logger", @@ -14644,7 +14600,6 @@ dependencies = [ "git_hosting_providers", "go_to_line", "gpui", - "headless", "http_client", "image_viewer", "inline_completion_button", diff --git a/Cargo.toml b/Cargo.toml index a64be70661..732306a9af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ members = [ "crates/context_servers", "crates/copilot", "crates/db", - "crates/dev_server_projects", "crates/diagnostics", "crates/docs_preprocessor", "crates/editor", @@ -45,7 +44,6 @@ members = [ "crates/google_ai", "crates/gpui", "crates/gpui_macros", - "crates/headless", "crates/html_to_markdown", "crates/http_client", "crates/image_viewer", @@ -201,7 +199,6 @@ command_palette_hooks = { path = "crates/command_palette_hooks" } context_servers = { path = "crates/context_servers" } copilot = { path = "crates/copilot" } db = { path = "crates/db" } -dev_server_projects = { path = "crates/dev_server_projects" } diagnostics = { path = "crates/diagnostics" } editor = { path = "crates/editor" } extension = { path = "crates/extension" } @@ -219,7 +216,6 @@ go_to_line = { path = "crates/go_to_line" } google_ai = { path = "crates/google_ai" } gpui = { path = "crates/gpui", default-features = false, features = ["http_client"]} gpui_macros = { path = "crates/gpui_macros" } -headless = { path = "crates/headless" } html_to_markdown = { path = "crates/html_to_markdown" } http_client = { path = "crates/http_client" } image_viewer = { path = "crates/image_viewer" } diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index f9a277f827..d4da36aded 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -963,7 +963,7 @@ impl AssistantPanel { fn new_context(&mut self, cx: &mut ViewContext) -> Option> { let project = self.project.read(cx); - if project.is_via_collab() && project.dev_server_project_id().is_none() { + if project.is_via_collab() { let task = self .context_store .update(cx, |store, cx| store.create_remote_context(cx)); diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index a637bfd43f..3eb98f3109 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1194,26 +1194,15 @@ impl Room { project: Model, cx: &mut ModelContext, ) -> Task> { - let request = if let Some(dev_server_project_id) = project.read(cx).dev_server_project_id() - { - self.client.request(proto::ShareProject { - room_id: self.id(), - worktrees: vec![], - dev_server_project_id: Some(dev_server_project_id.0), - is_ssh_project: false, - }) - } else { - if let Some(project_id) = project.read(cx).remote_id() { - return Task::ready(Ok(project_id)); - } + if let Some(project_id) = project.read(cx).remote_id() { + return Task::ready(Ok(project_id)); + } - self.client.request(proto::ShareProject { - room_id: self.id(), - worktrees: project.read(cx).worktree_metadata_protos(cx), - dev_server_project_id: None, - is_ssh_project: project.read(cx).is_via_ssh(), - }) - }; + let request = self.client.request(proto::ShareProject { + room_id: self.id(), + worktrees: project.read(cx).worktree_metadata_protos(cx), + is_ssh_project: project.read(cx).is_via_ssh(), + }); cx.spawn(|this, mut cx| async move { let response = request.await?; diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 8e76ae759c..9d23cf7ad5 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -15,7 +15,6 @@ pub enum CliRequest { urls: Vec, wait: bool, open_new_workspace: Option, - dev_server_token: Option, env: Option>, }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e69183d1ea..cb457b8a9d 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -151,6 +151,12 @@ fn main() -> Result<()> { } } + if let Some(_) = args.dev_server_token { + return Err(anyhow::anyhow!( + "Dev servers were removed in v0.157.x please upgrade to SSH remoting: https://zed.dev/docs/remote-development" + ))?; + } + let sender: JoinHandle> = thread::spawn({ let exit_status = exit_status.clone(); move || { @@ -162,7 +168,6 @@ fn main() -> Result<()> { urls, wait: args.wait, open_new_workspace, - dev_server_token: args.dev_server_token, env, })?; diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 4a42554ebf..b145ef99d0 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -30,7 +30,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; use socks::connect_socks_proxy_stream; -use std::fmt; use std::pin::Pin; use std::{ any::TypeId, @@ -54,15 +53,6 @@ pub use rpc::*; pub use telemetry_events::Event; pub use user::*; -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct DevServerToken(pub String); - -impl fmt::Display for DevServerToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - static ZED_SERVER_URL: LazyLock> = LazyLock::new(|| std::env::var("ZED_SERVER_URL").ok()); static ZED_RPC_URL: LazyLock> = LazyLock::new(|| std::env::var("ZED_RPC_URL").ok()); @@ -304,20 +294,14 @@ struct ClientState { } #[derive(Clone, Debug, Eq, PartialEq)] -pub enum Credentials { - DevServer { token: DevServerToken }, - User { user_id: u64, access_token: String }, +pub struct Credentials { + pub user_id: u64, + pub access_token: String, } impl Credentials { pub fn authorization_header(&self) -> String { - match self { - Credentials::DevServer { token } => format!("dev-server-token {}", token), - Credentials::User { - user_id, - access_token, - } => format!("{} {}", user_id, access_token), - } + format!("{} {}", self.user_id, self.access_token) } } @@ -600,11 +584,11 @@ impl Client { } pub fn user_id(&self) -> Option { - if let Some(Credentials::User { user_id, .. }) = self.state.read().credentials.as_ref() { - Some(*user_id) - } else { - None - } + self.state + .read() + .credentials + .as_ref() + .map(|credentials| credentials.user_id) } pub fn peer_id(&self) -> Option { @@ -793,11 +777,6 @@ impl Client { .is_some() } - pub fn set_dev_server_token(&self, token: DevServerToken) -> &Self { - self.state.write().credentials = Some(Credentials::DevServer { token }); - self - } - #[async_recursion(?Send)] pub async fn authenticate_and_connect( self: &Arc, @@ -848,9 +827,7 @@ impl Client { } } let credentials = credentials.unwrap(); - if let Credentials::User { user_id, .. } = &credentials { - self.set_id(*user_id); - } + self.set_id(credentials.user_id); if was_disconnected { self.set_status(Status::Connecting, cx); @@ -866,9 +843,8 @@ impl Client { Ok(conn) => { self.state.write().credentials = Some(credentials.clone()); if !read_from_provider && IMPERSONATE_LOGIN.is_none() { - if let Credentials::User{user_id, access_token} = credentials { - self.credentials_provider.write_credentials(user_id, access_token, cx).await.log_err(); - } + self.credentials_provider.write_credentials(credentials.user_id, credentials.access_token, cx).await.log_err(); + } futures::select_biased! { @@ -1301,7 +1277,7 @@ impl Client { .decrypt_string(&access_token) .context("failed to decrypt access token")?; - Ok(Credentials::User { + Ok(Credentials { user_id: user_id.parse()?, access_token, }) @@ -1422,7 +1398,7 @@ impl Client { // Use the admin API token to authenticate as the impersonated user. api_token.insert_str(0, "ADMIN_TOKEN:"); - Ok(Credentials::User { + Ok(Credentials { user_id: response.user.id, access_token: api_token, }) @@ -1667,7 +1643,7 @@ impl CredentialsProvider for DevelopmentCredentialsProvider { let credentials: DevelopmentCredentials = serde_json::from_slice(&json).log_err()?; - Some(Credentials::User { + Some(Credentials { user_id: credentials.user_id, access_token: credentials.access_token, }) @@ -1721,7 +1697,7 @@ impl CredentialsProvider for KeychainCredentialsProvider { .await .log_err()??; - Some(Credentials::User { + Some(Credentials { user_id: user_id.parse().ok()?, access_token: String::from_utf8(access_token).ok()?, }) @@ -1855,7 +1831,7 @@ mod tests { // Time out when client tries to connect. client.override_authenticate(move |cx| { cx.background_executor().spawn(async move { - Ok(Credentials::User { + Ok(Credentials { user_id, access_token: "token".into(), }) diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index bc39661e29..5a93c5edd9 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -49,7 +49,7 @@ impl FakeServer { let mut state = state.lock(); state.auth_count += 1; let access_token = state.access_token.to_string(); - Ok(Credentials::User { + Ok(Credentials { user_id: client_user_id, access_token, }) @@ -73,7 +73,7 @@ impl FakeServer { } if credentials - != (Credentials::User { + != (Credentials { user_id: client_user_id, access_token: state.lock().access_token.to_string(), }) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index a312dd3495..f6ee279dc8 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -28,9 +28,6 @@ impl std::fmt::Display for ChannelId { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct ProjectId(pub u64); -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub struct DevServerId(pub u64); - #[derive( Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, serde::Serialize, serde::Deserialize, )] diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index ad2c013668..417353e39d 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -86,7 +86,6 @@ client = { workspace = true, features = ["test-support"] } collab_ui = { workspace = true, features = ["test-support"] } collections = { workspace = true, features = ["test-support"] } ctor.workspace = true -dev_server_projects.workspace = true editor = { workspace = true, features = ["test-support"] } env_logger.workspace = true file_finder.workspace = true @@ -94,7 +93,6 @@ fs = { workspace = true, features = ["test-support"] } git = { workspace = true, features = ["test-support"] } git_hosting_providers.workspace = true gpui = { workspace = true, features = ["test-support"] } -headless.workspace = true hyper.workspace = true indoc.workspace = true language = { workspace = true, features = ["test-support"] } diff --git a/crates/collab/src/auth.rs b/crates/collab/src/auth.rs index 13c1c75fde..1d7edd8172 100644 --- a/crates/collab/src/auth.rs +++ b/crates/collab/src/auth.rs @@ -1,5 +1,5 @@ use crate::{ - db::{self, dev_server, AccessTokenId, Database, DevServerId, UserId}, + db::{self, AccessTokenId, Database, UserId}, rpc::Principal, AppState, Error, Result, }; @@ -44,19 +44,10 @@ pub async fn validate_header(mut req: Request, next: Next) -> impl Into let first = auth_header.next().unwrap_or(""); if first == "dev-server-token" { - let dev_server_token = auth_header.next().ok_or_else(|| { - Error::http( - StatusCode::BAD_REQUEST, - "missing dev-server-token token in authorization header".to_string(), - ) - })?; - let dev_server = verify_dev_server_token(dev_server_token, &state.db) - .await - .map_err(|e| Error::http(StatusCode::UNAUTHORIZED, format!("{}", e)))?; - - req.extensions_mut() - .insert(Principal::DevServer(dev_server)); - return Ok::<_, Error>(next.run(req).await); + Err(Error::http( + StatusCode::UNAUTHORIZED, + "Dev servers were removed in Zed 0.157 please upgrade to SSH remoting".to_string(), + ))?; } let user_id = UserId(first.parse().map_err(|_| { @@ -240,41 +231,6 @@ pub async fn verify_access_token( }) } -pub fn generate_dev_server_token(id: usize, access_token: String) -> String { - format!("{}.{}", id, access_token) -} - -pub async fn verify_dev_server_token( - dev_server_token: &str, - db: &Arc, -) -> anyhow::Result { - let (id, token) = split_dev_server_token(dev_server_token)?; - let token_hash = hash_access_token(token); - let server = db.get_dev_server(id).await?; - - if server - .hashed_token - .as_bytes() - .ct_eq(token_hash.as_ref()) - .into() - { - Ok(server) - } else { - Err(anyhow!("wrong token for dev server")) - } -} - -// a dev_server_token has the format .. This is to make them -// relatively easy to copy/paste around. -pub fn split_dev_server_token(dev_server_token: &str) -> anyhow::Result<(DevServerId, &str)> { - let mut parts = dev_server_token.splitn(2, '.'); - let id = DevServerId(parts.next().unwrap_or_default().parse()?); - let token = parts - .next() - .ok_or_else(|| anyhow!("invalid dev server token format"))?; - Ok((id, token)) -} - #[cfg(test)] mod test { use rand::thread_rng; diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index e966548493..9c02e0c801 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -726,7 +726,6 @@ pub struct Project { pub collaborators: Vec, pub worktrees: BTreeMap, pub language_servers: Vec, - pub dev_server_project_id: Option, } pub struct ProjectCollaborator { diff --git a/crates/collab/src/db/ids.rs b/crates/collab/src/db/ids.rs index 3a5bcff558..698b1c5693 100644 --- a/crates/collab/src/db/ids.rs +++ b/crates/collab/src/db/ids.rs @@ -79,7 +79,6 @@ id_type!(ChannelChatParticipantId); id_type!(ChannelId); id_type!(ChannelMemberId); id_type!(ContactId); -id_type!(DevServerId); id_type!(ExtensionId); id_type!(FlagId); id_type!(FollowerId); @@ -89,7 +88,6 @@ id_type!(NotificationId); id_type!(NotificationKindId); id_type!(ProjectCollaboratorId); id_type!(ProjectId); -id_type!(DevServerProjectId); id_type!(ReplicaId); id_type!(RoomId); id_type!(RoomParticipantId); @@ -277,12 +275,6 @@ impl From for i32 { } } -#[derive(Copy, Clone, Debug, Serialize, PartialEq)] -pub enum PrincipalId { - UserId(UserId), - DevServerId(DevServerId), -} - /// Indicate whether a [Buffer] has permissions to edit. #[derive(PartialEq, Clone, Copy, Debug)] pub enum Capability { diff --git a/crates/collab/src/db/queries.rs b/crates/collab/src/db/queries.rs index 9c277790f9..79523444ab 100644 --- a/crates/collab/src/db/queries.rs +++ b/crates/collab/src/db/queries.rs @@ -8,8 +8,6 @@ pub mod buffers; pub mod channels; pub mod contacts; pub mod contributors; -pub mod dev_server_projects; -pub mod dev_servers; pub mod embeddings; pub mod extensions; pub mod hosted_projects; diff --git a/crates/collab/src/db/queries/dev_server_projects.rs b/crates/collab/src/db/queries/dev_server_projects.rs index dae550b668..8b13789179 100644 --- a/crates/collab/src/db/queries/dev_server_projects.rs +++ b/crates/collab/src/db/queries/dev_server_projects.rs @@ -1,365 +1 @@ -use anyhow::anyhow; -use rpc::{ - proto::{self}, - ConnectionId, -}; -use sea_orm::{ - ActiveModelTrait, ActiveValue, ColumnTrait, Condition, DatabaseTransaction, EntityTrait, - IntoActiveModel, ModelTrait, QueryFilter, -}; -use crate::db::ProjectId; - -use super::{ - dev_server, dev_server_project, project, project_collaborator, worktree, Database, DevServerId, - DevServerProjectId, RejoinedProject, ResharedProject, ServerId, UserId, -}; - -impl Database { - pub async fn get_dev_server_project( - &self, - dev_server_project_id: DevServerProjectId, - ) -> crate::Result { - self.transaction(|tx| async move { - Ok( - dev_server_project::Entity::find_by_id(dev_server_project_id) - .one(&*tx) - .await? - .ok_or_else(|| { - anyhow!("no dev server project with id {}", dev_server_project_id) - })?, - ) - }) - .await - } - - pub async fn get_projects_for_dev_server( - &self, - dev_server_id: DevServerId, - ) -> crate::Result> { - self.transaction(|tx| async move { - self.get_projects_for_dev_server_internal(dev_server_id, &tx) - .await - }) - .await - } - - pub async fn get_projects_for_dev_server_internal( - &self, - dev_server_id: DevServerId, - tx: &DatabaseTransaction, - ) -> crate::Result> { - let servers = dev_server_project::Entity::find() - .filter(dev_server_project::Column::DevServerId.eq(dev_server_id)) - .find_also_related(project::Entity) - .all(tx) - .await?; - Ok(servers - .into_iter() - .map(|(dev_server_project, project)| dev_server_project.to_proto(project)) - .collect()) - } - - pub async fn dev_server_project_ids_for_user( - &self, - user_id: UserId, - tx: &DatabaseTransaction, - ) -> crate::Result> { - let dev_servers = dev_server::Entity::find() - .filter(dev_server::Column::UserId.eq(user_id)) - .find_with_related(dev_server_project::Entity) - .all(tx) - .await?; - - Ok(dev_servers - .into_iter() - .flat_map(|(_, projects)| projects.into_iter().map(|p| p.id)) - .collect()) - } - - pub async fn owner_for_dev_server_project( - &self, - dev_server_project_id: DevServerProjectId, - tx: &DatabaseTransaction, - ) -> crate::Result { - let dev_server = dev_server_project::Entity::find_by_id(dev_server_project_id) - .find_also_related(dev_server::Entity) - .one(tx) - .await? - .and_then(|(_, dev_server)| dev_server) - .ok_or_else(|| anyhow!("no dev server project"))?; - - Ok(dev_server.user_id) - } - - pub async fn get_stale_dev_server_projects( - &self, - connection: ConnectionId, - ) -> crate::Result> { - self.transaction(|tx| async move { - let projects = project::Entity::find() - .filter( - Condition::all() - .add(project::Column::HostConnectionId.eq(connection.id)) - .add(project::Column::HostConnectionServerId.eq(connection.owner_id)), - ) - .all(&*tx) - .await?; - - Ok(projects.into_iter().map(|p| p.id).collect()) - }) - .await - } - - pub async fn create_dev_server_project( - &self, - dev_server_id: DevServerId, - path: &str, - user_id: UserId, - ) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> { - self.transaction(|tx| async move { - let dev_server = dev_server::Entity::find_by_id(dev_server_id) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no dev server with id {}", dev_server_id))?; - if dev_server.user_id != user_id { - return Err(anyhow!("not your dev server"))?; - } - - let project = dev_server_project::Entity::insert(dev_server_project::ActiveModel { - id: ActiveValue::NotSet, - dev_server_id: ActiveValue::Set(dev_server_id), - paths: ActiveValue::Set(dev_server_project::JSONPaths(vec![path.to_string()])), - }) - .exec_with_returning(&*tx) - .await?; - - let status = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok((project, status)) - }) - .await - } - - pub async fn update_dev_server_project( - &self, - id: DevServerProjectId, - paths: &[String], - user_id: UserId, - ) -> crate::Result<(dev_server_project::Model, proto::DevServerProjectsUpdate)> { - self.transaction(move |tx| async move { - let paths = paths.to_owned(); - let Some((project, Some(dev_server))) = dev_server_project::Entity::find_by_id(id) - .find_also_related(dev_server::Entity) - .one(&*tx) - .await? - else { - return Err(anyhow!("no such dev server project"))?; - }; - - if dev_server.user_id != user_id { - return Err(anyhow!("not your dev server"))?; - } - let mut project = project.into_active_model(); - project.paths = ActiveValue::Set(dev_server_project::JSONPaths(paths)); - let project = project.update(&*tx).await?; - - let status = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok((project, status)) - }) - .await - } - - pub async fn delete_dev_server_project( - &self, - dev_server_project_id: DevServerProjectId, - dev_server_id: DevServerId, - user_id: UserId, - ) -> crate::Result<(Vec, proto::DevServerProjectsUpdate)> { - self.transaction(|tx| async move { - project::Entity::delete_many() - .filter(project::Column::DevServerProjectId.eq(dev_server_project_id)) - .exec(&*tx) - .await?; - let result = dev_server_project::Entity::delete_by_id(dev_server_project_id) - .exec(&*tx) - .await?; - if result.rows_affected != 1 { - return Err(anyhow!( - "no dev server project with id {}", - dev_server_project_id - ))?; - } - - let status = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - let projects = self - .get_projects_for_dev_server_internal(dev_server_id, &tx) - .await?; - Ok((projects, status)) - }) - .await - } - - pub async fn share_dev_server_project( - &self, - dev_server_project_id: DevServerProjectId, - dev_server_id: DevServerId, - connection: ConnectionId, - worktrees: &[proto::WorktreeMetadata], - ) -> crate::Result<( - proto::DevServerProject, - UserId, - proto::DevServerProjectsUpdate, - )> { - self.transaction(|tx| async move { - let dev_server = dev_server::Entity::find_by_id(dev_server_id) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no dev server with id {}", dev_server_id))?; - - let dev_server_project = dev_server_project::Entity::find_by_id(dev_server_project_id) - .one(&*tx) - .await? - .ok_or_else(|| { - anyhow!("no dev server project with id {}", dev_server_project_id) - })?; - - if dev_server_project.dev_server_id != dev_server_id { - return Err(anyhow!("dev server project shared from wrong server"))?; - } - - let project = project::ActiveModel { - room_id: ActiveValue::Set(None), - host_user_id: ActiveValue::Set(None), - host_connection_id: ActiveValue::set(Some(connection.id as i32)), - host_connection_server_id: ActiveValue::set(Some(ServerId( - connection.owner_id as i32, - ))), - id: ActiveValue::NotSet, - hosted_project_id: ActiveValue::Set(None), - dev_server_project_id: ActiveValue::Set(Some(dev_server_project_id)), - } - .insert(&*tx) - .await?; - - if !worktrees.is_empty() { - worktree::Entity::insert_many(worktrees.iter().map(|worktree| { - worktree::ActiveModel { - id: ActiveValue::set(worktree.id as i64), - project_id: ActiveValue::set(project.id), - abs_path: ActiveValue::set(worktree.abs_path.clone()), - root_name: ActiveValue::set(worktree.root_name.clone()), - visible: ActiveValue::set(worktree.visible), - scan_id: ActiveValue::set(0), - completed_scan_id: ActiveValue::set(0), - } - })) - .exec(&*tx) - .await?; - } - - let status = self - .dev_server_projects_update_internal(dev_server.user_id, &tx) - .await?; - - Ok(( - dev_server_project.to_proto(Some(project)), - dev_server.user_id, - status, - )) - }) - .await - } - - pub async fn reshare_dev_server_projects( - &self, - reshared_projects: &Vec, - dev_server_id: DevServerId, - connection: ConnectionId, - ) -> crate::Result> { - self.transaction(|tx| async move { - let mut ret = Vec::new(); - for reshared_project in reshared_projects { - let project_id = ProjectId::from_proto(reshared_project.project_id); - let (project, dev_server_project) = project::Entity::find_by_id(project_id) - .find_also_related(dev_server_project::Entity) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("project does not exist"))?; - - if dev_server_project.map(|rp| rp.dev_server_id) != Some(dev_server_id) { - return Err(anyhow!("dev server project reshared from wrong server"))?; - } - - let Ok(old_connection_id) = project.host_connection() else { - return Err(anyhow!("dev server project was not shared"))?; - }; - - project::Entity::update(project::ActiveModel { - id: ActiveValue::set(project_id), - host_connection_id: ActiveValue::set(Some(connection.id as i32)), - host_connection_server_id: ActiveValue::set(Some(ServerId( - connection.owner_id as i32, - ))), - ..Default::default() - }) - .exec(&*tx) - .await?; - - let collaborators = project - .find_related(project_collaborator::Entity) - .all(&*tx) - .await?; - - self.update_project_worktrees(project_id, &reshared_project.worktrees, &tx) - .await?; - - ret.push(super::ResharedProject { - id: project_id, - old_connection_id, - collaborators: collaborators - .iter() - .map(|collaborator| super::ProjectCollaborator { - connection_id: collaborator.connection(), - user_id: collaborator.user_id, - replica_id: collaborator.replica_id, - is_host: collaborator.is_host, - }) - .collect(), - worktrees: reshared_project.worktrees.clone(), - }); - } - Ok(ret) - }) - .await - } - - pub async fn rejoin_dev_server_projects( - &self, - rejoined_projects: &Vec, - user_id: UserId, - connection_id: ConnectionId, - ) -> crate::Result> { - self.transaction(|tx| async move { - let mut ret = Vec::new(); - for rejoined_project in rejoined_projects { - if let Some(project) = self - .rejoin_project_internal(&tx, rejoined_project, user_id, connection_id) - .await? - { - ret.push(project); - } - } - Ok(ret) - }) - .await - } -} diff --git a/crates/collab/src/db/queries/dev_servers.rs b/crates/collab/src/db/queries/dev_servers.rs index 16cbfedee3..8b13789179 100644 --- a/crates/collab/src/db/queries/dev_servers.rs +++ b/crates/collab/src/db/queries/dev_servers.rs @@ -1,222 +1 @@ -use rpc::proto; -use sea_orm::{ - ActiveValue, ColumnTrait, DatabaseTransaction, EntityTrait, IntoActiveModel, QueryFilter, -}; -use super::{dev_server, dev_server_project, Database, DevServerId, UserId}; - -impl Database { - pub async fn get_dev_server( - &self, - dev_server_id: DevServerId, - ) -> crate::Result { - self.transaction(|tx| async move { - Ok(dev_server::Entity::find_by_id(dev_server_id) - .one(&*tx) - .await? - .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?) - }) - .await - } - - pub async fn get_dev_server_for_user( - &self, - dev_server_id: DevServerId, - user_id: UserId, - ) -> crate::Result { - self.transaction(|tx| async move { - let server = dev_server::Entity::find_by_id(dev_server_id) - .one(&*tx) - .await? - .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?; - if server.user_id != user_id { - return Err(anyhow::anyhow!( - "dev server {} is not owned by user {}", - dev_server_id, - user_id - ))?; - } - Ok(server) - }) - .await - } - - pub async fn get_dev_servers(&self, user_id: UserId) -> crate::Result> { - self.transaction(|tx| async move { - Ok(dev_server::Entity::find() - .filter(dev_server::Column::UserId.eq(user_id)) - .all(&*tx) - .await?) - }) - .await - } - - pub async fn dev_server_projects_update( - &self, - user_id: UserId, - ) -> crate::Result { - self.transaction(|tx| async move { - self.dev_server_projects_update_internal(user_id, &tx).await - }) - .await - } - - pub async fn dev_server_projects_update_internal( - &self, - user_id: UserId, - tx: &DatabaseTransaction, - ) -> crate::Result { - let dev_servers = dev_server::Entity::find() - .filter(dev_server::Column::UserId.eq(user_id)) - .all(tx) - .await?; - - let dev_server_projects = dev_server_project::Entity::find() - .filter( - dev_server_project::Column::DevServerId - .is_in(dev_servers.iter().map(|d| d.id).collect::>()), - ) - .find_also_related(super::project::Entity) - .all(tx) - .await?; - - Ok(proto::DevServerProjectsUpdate { - dev_servers: dev_servers - .into_iter() - .map(|d| d.to_proto(proto::DevServerStatus::Offline)) - .collect(), - dev_server_projects: dev_server_projects - .into_iter() - .map(|(dev_server_project, project)| dev_server_project.to_proto(project)) - .collect(), - }) - } - - pub async fn create_dev_server( - &self, - name: &str, - ssh_connection_string: Option<&str>, - hashed_access_token: &str, - user_id: UserId, - ) -> crate::Result<(dev_server::Model, proto::DevServerProjectsUpdate)> { - self.transaction(|tx| async move { - if name.trim().is_empty() { - return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?; - } - - let dev_server = dev_server::Entity::insert(dev_server::ActiveModel { - id: ActiveValue::NotSet, - hashed_token: ActiveValue::Set(hashed_access_token.to_string()), - name: ActiveValue::Set(name.trim().to_string()), - user_id: ActiveValue::Set(user_id), - ssh_connection_string: ActiveValue::Set( - ssh_connection_string.map(ToOwned::to_owned), - ), - }) - .exec_with_returning(&*tx) - .await?; - - let dev_server_projects = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok((dev_server, dev_server_projects)) - }) - .await - } - - pub async fn update_dev_server_token( - &self, - id: DevServerId, - hashed_token: &str, - user_id: UserId, - ) -> crate::Result { - self.transaction(|tx| async move { - let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else { - return Err(anyhow::anyhow!("no dev server with id {}", id))?; - }; - if dev_server.user_id != user_id { - return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?; - } - - dev_server::Entity::update(dev_server::ActiveModel { - hashed_token: ActiveValue::Set(hashed_token.to_string()), - ..dev_server.clone().into_active_model() - }) - .exec(&*tx) - .await?; - - let dev_server_projects = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok(dev_server_projects) - }) - .await - } - - pub async fn rename_dev_server( - &self, - id: DevServerId, - name: &str, - ssh_connection_string: Option<&str>, - user_id: UserId, - ) -> crate::Result { - self.transaction(|tx| async move { - let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else { - return Err(anyhow::anyhow!("no dev server with id {}", id))?; - }; - if dev_server.user_id != user_id || name.trim().is_empty() { - return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?; - } - - dev_server::Entity::update(dev_server::ActiveModel { - name: ActiveValue::Set(name.trim().to_string()), - ssh_connection_string: ActiveValue::Set( - ssh_connection_string.map(ToOwned::to_owned), - ), - ..dev_server.clone().into_active_model() - }) - .exec(&*tx) - .await?; - - let dev_server_projects = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok(dev_server_projects) - }) - .await - } - - pub async fn delete_dev_server( - &self, - id: DevServerId, - user_id: UserId, - ) -> crate::Result { - self.transaction(|tx| async move { - let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else { - return Err(anyhow::anyhow!("no dev server with id {}", id))?; - }; - if dev_server.user_id != user_id { - return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?; - } - - dev_server_project::Entity::delete_many() - .filter(dev_server_project::Column::DevServerId.eq(id)) - .exec(&*tx) - .await?; - - dev_server::Entity::delete(dev_server.into_active_model()) - .exec(&*tx) - .await?; - - let dev_server_projects = self - .dev_server_projects_update_internal(user_id, &tx) - .await?; - - Ok(dev_server_projects) - }) - .await - } -} diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index b03cead589..27bec21ca1 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -32,7 +32,6 @@ impl Database { connection: ConnectionId, worktrees: &[proto::WorktreeMetadata], is_ssh_project: bool, - dev_server_project_id: Option, ) -> Result> { self.room_transaction(room_id, |tx| async move { let participant = room_participant::Entity::find() @@ -61,38 +60,6 @@ impl Database { return Err(anyhow!("guests cannot share projects"))?; } - if let Some(dev_server_project_id) = dev_server_project_id { - let project = project::Entity::find() - .filter(project::Column::DevServerProjectId.eq(Some(dev_server_project_id))) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no remote project"))?; - - let (_, dev_server) = dev_server_project::Entity::find_by_id(dev_server_project_id) - .find_also_related(dev_server::Entity) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no dev_server_project"))?; - - if !dev_server.is_some_and(|dev_server| dev_server.user_id == participant.user_id) { - return Err(anyhow!("not your dev server"))?; - } - - if project.room_id.is_some() { - return Err(anyhow!("project already shared"))?; - }; - - let project = project::Entity::update(project::ActiveModel { - room_id: ActiveValue::Set(Some(room_id)), - ..project.into_active_model() - }) - .exec(&*tx) - .await?; - - let room = self.get_room(room_id, &tx).await?; - return Ok((project.id, room)); - } - let project = project::ActiveModel { room_id: ActiveValue::set(Some(participant.room_id)), host_user_id: ActiveValue::set(Some(participant.user_id)), @@ -102,7 +69,6 @@ impl Database { ))), id: ActiveValue::NotSet, hosted_project_id: ActiveValue::Set(None), - dev_server_project_id: ActiveValue::Set(None), } .insert(&*tx) .await?; @@ -156,7 +122,6 @@ impl Database { &self, project_id: ProjectId, connection: ConnectionId, - user_id: Option, ) -> Result, Vec)>> { self.project_transaction(project_id, |tx| async move { let guest_connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; @@ -172,25 +137,6 @@ impl Database { if project.host_connection()? == connection { return Ok((true, room, guest_connection_ids)); } - if let Some(dev_server_project_id) = project.dev_server_project_id { - if let Some(user_id) = user_id { - if user_id - != self - .owner_for_dev_server_project(dev_server_project_id, &tx) - .await? - { - Err(anyhow!("cannot unshare a project hosted by another user"))? - } - project::Entity::update(project::ActiveModel { - room_id: ActiveValue::Set(None), - ..project.into_active_model() - }) - .exec(&*tx) - .await?; - return Ok((false, room, guest_connection_ids)); - } - } - Err(anyhow!("cannot unshare a project hosted by another user"))? }) .await @@ -633,17 +579,6 @@ impl Database { .await } - pub async fn find_dev_server_project(&self, id: DevServerProjectId) -> Result { - self.transaction(|tx| async move { - Ok(project::Entity::find() - .filter(project::Column::DevServerProjectId.eq(id)) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no such project"))?) - }) - .await - } - /// Adds the given connection to the specified project /// in the current room. pub async fn join_project( @@ -654,13 +589,7 @@ impl Database { ) -> Result> { self.project_transaction(project_id, |tx| async move { let (project, role) = self - .access_project( - project_id, - connection, - PrincipalId::UserId(user_id), - Capability::ReadOnly, - &tx, - ) + .access_project(project_id, connection, Capability::ReadOnly, &tx) .await?; self.join_project_internal(project, user_id, connection, role, &tx) .await @@ -851,7 +780,6 @@ impl Database { worktree_id: None, }) .collect(), - dev_server_project_id: project.dev_server_project_id, }; Ok((project, replica_id as ReplicaId)) } @@ -1007,29 +935,14 @@ impl Database { &self, project_id: ProjectId, connection_id: ConnectionId, - principal_id: PrincipalId, capability: Capability, tx: &DatabaseTransaction, ) -> Result<(project::Model, ChannelRole)> { - let (mut project, dev_server_project) = project::Entity::find_by_id(project_id) - .find_also_related(dev_server_project::Entity) + let project = project::Entity::find_by_id(project_id) .one(tx) .await? .ok_or_else(|| anyhow!("no such project"))?; - let user_id = match principal_id { - PrincipalId::DevServerId(_) => { - if project - .host_connection() - .is_ok_and(|connection| connection == connection_id) - { - return Ok((project, ChannelRole::Admin)); - } - return Err(anyhow!("not the project host"))?; - } - PrincipalId::UserId(user_id) => user_id, - }; - let role_from_room = if let Some(room_id) = project.room_id { room_participant::Entity::find() .filter(room_participant::Column::RoomId.eq(room_id)) @@ -1040,34 +953,8 @@ impl Database { } else { None }; - let role_from_dev_server = if let Some(dev_server_project) = dev_server_project { - let dev_server = dev_server::Entity::find_by_id(dev_server_project.dev_server_id) - .one(tx) - .await? - .ok_or_else(|| anyhow!("no such channel"))?; - if user_id == dev_server.user_id { - // If the user left the room "uncleanly" they may rejoin the - // remote project before leave_room runs. IN that case kick - // the project out of the room pre-emptively. - if role_from_room.is_none() { - project = project::Entity::update(project::ActiveModel { - room_id: ActiveValue::Set(None), - ..project.into_active_model() - }) - .exec(tx) - .await?; - } - Some(ChannelRole::Admin) - } else { - None - } - } else { - None - }; - let role = role_from_dev_server - .or(role_from_room) - .unwrap_or(ChannelRole::Banned); + let role = role_from_room.unwrap_or(ChannelRole::Banned); match capability { Capability::ReadWrite => { @@ -1090,17 +977,10 @@ impl Database { &self, project_id: ProjectId, connection_id: ConnectionId, - user_id: UserId, ) -> Result { self.project_transaction(project_id, |tx| async move { let (project, _) = self - .access_project( - project_id, - connection_id, - PrincipalId::UserId(user_id), - Capability::ReadOnly, - &tx, - ) + .access_project(project_id, connection_id, Capability::ReadOnly, &tx) .await?; project.host_connection() }) @@ -1113,17 +993,10 @@ impl Database { &self, project_id: ProjectId, connection_id: ConnectionId, - user_id: UserId, ) -> Result { self.project_transaction(project_id, |tx| async move { let (project, _) = self - .access_project( - project_id, - connection_id, - PrincipalId::UserId(user_id), - Capability::ReadWrite, - &tx, - ) + .access_project(project_id, connection_id, Capability::ReadWrite, &tx) .await?; project.host_connection() }) @@ -1131,47 +1004,16 @@ impl Database { .map(|guard| guard.into_inner()) } - /// Returns the host connection for a request to join a shared project. - pub async fn host_for_owner_project_request( - &self, - project_id: ProjectId, - _connection_id: ConnectionId, - user_id: UserId, - ) -> Result { - self.project_transaction(project_id, |tx| async move { - let (project, dev_server_project) = project::Entity::find_by_id(project_id) - .find_also_related(dev_server_project::Entity) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no such project"))?; - - let Some(dev_server_project) = dev_server_project else { - return Err(anyhow!("not a dev server project"))?; - }; - let dev_server = dev_server::Entity::find_by_id(dev_server_project.dev_server_id) - .one(&*tx) - .await? - .ok_or_else(|| anyhow!("no such dev server"))?; - if dev_server.user_id != user_id { - return Err(anyhow!("not your project"))?; - } - project.host_connection() - }) - .await - .map(|guard| guard.into_inner()) - } - pub async fn connections_for_buffer_update( &self, project_id: ProjectId, - principal_id: PrincipalId, connection_id: ConnectionId, capability: Capability, ) -> Result)>> { self.project_transaction(project_id, |tx| async move { // Authorize let (project, _) = self - .access_project(project_id, connection_id, principal_id, capability, &tx) + .access_project(project_id, connection_id, capability, &tx) .await?; let host_connection_id = project.host_connection()?; diff --git a/crates/collab/src/db/queries/rooms.rs b/crates/collab/src/db/queries/rooms.rs index 9235b8550b..682c4ed389 100644 --- a/crates/collab/src/db/queries/rooms.rs +++ b/crates/collab/src/db/queries/rooms.rs @@ -858,25 +858,6 @@ impl Database { .all(&*tx) .await?; - // if any project in the room has a remote-project-id that belongs to a dev server that this user owns. - let dev_server_projects_for_user = self - .dev_server_project_ids_for_user(leaving_participant.user_id, &tx) - .await?; - - let dev_server_projects_to_unshare = project::Entity::find() - .filter( - Condition::all() - .add(project::Column::RoomId.eq(room_id)) - .add( - project::Column::DevServerProjectId - .is_in(dev_server_projects_for_user.clone()), - ), - ) - .all(&*tx) - .await? - .into_iter() - .map(|project| project.id) - .collect::>(); let mut left_projects = HashMap::default(); let mut collaborators = project_collaborator::Entity::find() .filter(project_collaborator::Column::ProjectId.is_in(project_ids)) @@ -899,9 +880,7 @@ impl Database { left_project.connection_ids.push(collaborator_connection_id); } - if (collaborator.is_host && collaborator.connection() == connection) - || dev_server_projects_to_unshare.contains(&collaborator.project_id) - { + if collaborator.is_host && collaborator.connection() == connection { left_project.should_unshare = true; } } @@ -944,17 +923,6 @@ impl Database { .exec(&*tx) .await?; - if !dev_server_projects_to_unshare.is_empty() { - project::Entity::update_many() - .filter(project::Column::Id.is_in(dev_server_projects_to_unshare)) - .set(project::ActiveModel { - room_id: ActiveValue::Set(None), - ..Default::default() - }) - .exec(&*tx) - .await?; - } - let (channel, room) = self.get_channel_room(room_id, &tx).await?; let deleted = if room.participants.is_empty() { let result = room::Entity::delete_by_id(room_id).exec(&*tx).await?; @@ -1323,26 +1291,6 @@ impl Database { project.worktree_root_names.push(db_worktree.root_name); } } - } else if let Some(dev_server_project_id) = db_project.dev_server_project_id { - let host = self - .owner_for_dev_server_project(dev_server_project_id, tx) - .await?; - if let Some((_, participant)) = participants - .iter_mut() - .find(|(_, v)| v.user_id == host.to_proto()) - { - participant.projects.push(proto::ParticipantProject { - id: db_project.id.to_proto(), - worktree_root_names: Default::default(), - }); - let project = participant.projects.last_mut().unwrap(); - - for db_worktree in db_worktrees { - if db_worktree.visible { - project.worktree_root_names.push(db_worktree.root_name); - } - } - } } } diff --git a/crates/collab/src/db/tables.rs b/crates/collab/src/db/tables.rs index 01d3835dc1..23dced800b 100644 --- a/crates/collab/src/db/tables.rs +++ b/crates/collab/src/db/tables.rs @@ -13,8 +13,6 @@ pub mod channel_message; pub mod channel_message_mention; pub mod contact; pub mod contributor; -pub mod dev_server; -pub mod dev_server_project; pub mod embedding; pub mod extension; pub mod extension_version; diff --git a/crates/collab/src/db/tables/dev_server.rs b/crates/collab/src/db/tables/dev_server.rs deleted file mode 100644 index a9615ca14b..0000000000 --- a/crates/collab/src/db/tables/dev_server.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::db::{DevServerId, UserId}; -use rpc::proto; -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "dev_servers")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: DevServerId, - pub name: String, - pub user_id: UserId, - pub hashed_token: String, - pub ssh_connection_string: Option, -} - -impl ActiveModelBehavior for ActiveModel {} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::dev_server_project::Entity")] - RemoteProject, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::RemoteProject.def() - } -} - -impl Model { - pub fn to_proto(&self, status: proto::DevServerStatus) -> proto::DevServer { - proto::DevServer { - dev_server_id: self.id.to_proto(), - name: self.name.clone(), - status: status as i32, - ssh_connection_string: self.ssh_connection_string.clone(), - } - } -} diff --git a/crates/collab/src/db/tables/dev_server_project.rs b/crates/collab/src/db/tables/dev_server_project.rs deleted file mode 100644 index ba487b8d02..0000000000 --- a/crates/collab/src/db/tables/dev_server_project.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::project; -use crate::db::{DevServerId, DevServerProjectId}; -use rpc::proto; -use sea_orm::{entity::prelude::*, FromJsonQueryResult}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "dev_server_projects")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: DevServerProjectId, - pub dev_server_id: DevServerId, - pub paths: JSONPaths, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)] -pub struct JSONPaths(pub Vec); - -impl ActiveModelBehavior for ActiveModel {} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_one = "super::project::Entity")] - Project, - #[sea_orm( - belongs_to = "super::dev_server::Entity", - from = "Column::DevServerId", - to = "super::dev_server::Column::Id" - )] - DevServer, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Project.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::DevServer.def() - } -} - -impl Model { - pub fn to_proto(&self, project: Option) -> proto::DevServerProject { - proto::DevServerProject { - id: self.id.to_proto(), - project_id: project.map(|p| p.id.to_proto()), - dev_server_id: self.dev_server_id.to_proto(), - path: self.paths().first().cloned().unwrap_or_default(), - paths: self.paths().clone(), - } - } - - pub fn paths(&self) -> &Vec { - &self.paths.0 - } -} diff --git a/crates/collab/src/db/tables/project.rs b/crates/collab/src/db/tables/project.rs index 6858af0237..a357634aff 100644 --- a/crates/collab/src/db/tables/project.rs +++ b/crates/collab/src/db/tables/project.rs @@ -1,4 +1,4 @@ -use crate::db::{DevServerProjectId, HostedProjectId, ProjectId, Result, RoomId, ServerId, UserId}; +use crate::db::{HostedProjectId, ProjectId, Result, RoomId, ServerId, UserId}; use anyhow::anyhow; use rpc::ConnectionId; use sea_orm::entity::prelude::*; @@ -13,7 +13,6 @@ pub struct Model { pub host_connection_id: Option, pub host_connection_server_id: Option, pub hosted_project_id: Option, - pub dev_server_project_id: Option, } impl Model { @@ -57,12 +56,6 @@ pub enum Relation { to = "super::hosted_project::Column::Id" )] HostedProject, - #[sea_orm( - belongs_to = "super::dev_server_project::Entity", - from = "Column::DevServerProjectId", - to = "super::dev_server_project::Column::Id" - )] - RemoteProject, } impl Related for Entity { @@ -101,10 +94,4 @@ impl Related for Entity { } } -impl Related for Entity { - fn to() -> RelationDef { - Relation::RemoteProject.def() - } -} - impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/tests/db_tests.rs b/crates/collab/src/db/tests/db_tests.rs index 6263350287..cd3a194357 100644 --- a/crates/collab/src/db/tests/db_tests.rs +++ b/crates/collab/src/db/tests/db_tests.rs @@ -540,18 +540,18 @@ async fn test_project_count(db: &Arc) { .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 0); - db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None) + db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 1); - db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false, None) + db.share_project(room_id, ConnectionId { owner_id, id: 1 }, &[], false) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2); // Projects shared by admins aren't counted. - db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false, None) + db.share_project(room_id, ConnectionId { owner_id, id: 0 }, &[], false) .await .unwrap(); assert_eq!(db.project_count_excluding_admins().await.unwrap(), 2); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index c162129db6..90277242f1 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -5,11 +5,10 @@ use crate::llm::LlmTokenClaims; use crate::{ auth, db::{ - self, dev_server, BufferId, Capability, Channel, ChannelId, ChannelRole, ChannelsForUser, - CreatedChannelMessage, Database, DevServerId, DevServerProjectId, InviteMemberResult, - MembershipUpdated, MessageId, NotificationId, PrincipalId, Project, ProjectId, - RejoinedProject, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, - ServerId, UpdatedChannelMessage, User, UserId, + self, BufferId, Capability, Channel, ChannelId, ChannelRole, ChannelsForUser, + CreatedChannelMessage, Database, InviteMemberResult, MembershipUpdated, MessageId, + NotificationId, Project, ProjectId, RejoinedProject, RemoveChannelMemberResult, ReplicaId, + RespondToChannelInvite, RoomId, ServerId, UpdatedChannelMessage, User, UserId, }, executor::Executor, AppState, Config, Error, RateLimit, Result, @@ -42,10 +41,8 @@ use sha2::Digest; use supermaven_api::{CreateExternalUserRequest, SupermavenAdminApi}; use futures::{ - channel::oneshot, - future::{self, BoxFuture}, - stream::FuturesUnordered, - FutureExt, SinkExt, StreamExt, TryStreamExt, + channel::oneshot, future::BoxFuture, stream::FuturesUnordered, FutureExt, SinkExt, StreamExt, + TryStreamExt, }; use prometheus::{register_int_gauge, IntGauge}; use rpc::{ @@ -109,7 +106,6 @@ impl Response { pub enum Principal { User(User), Impersonated { user: User, admin: User }, - DevServer(dev_server::Model), } impl Principal { @@ -124,9 +120,6 @@ impl Principal { span.record("login", &user.github_login); span.record("impersonator", &admin.github_login); } - Principal::DevServer(dev_server) => { - span.record("dev_server_id", dev_server.id.0); - } } } } @@ -167,27 +160,10 @@ impl Session { } } - fn for_user(self) -> Option { - UserSession::new(self) - } - - fn for_dev_server(self) -> Option { - DevServerSession::new(self) - } - - fn user_id(&self) -> Option { - match &self.principal { - Principal::User(user) => Some(user.id), - Principal::Impersonated { user, .. } => Some(user.id), - Principal::DevServer(_) => None, - } - } - fn is_staff(&self) -> bool { match &self.principal { Principal::User(user) => user.admin, Principal::Impersonated { .. } => true, - Principal::DevServer(_) => false, } } @@ -199,9 +175,7 @@ impl Session { return Ok(true); } - let Some(user_id) = self.user_id() else { - return Ok(false); - }; + let user_id = self.user_id(); Ok(db.has_active_billing_subscription(user_id).await?) } @@ -217,18 +191,17 @@ impl Session { } } - fn dev_server_id(&self) -> Option { + fn user_id(&self) -> UserId { match &self.principal { - Principal::User(_) | Principal::Impersonated { .. } => None, - Principal::DevServer(dev_server) => Some(dev_server.id), + Principal::User(user) => user.id, + Principal::Impersonated { user, .. } => user.id, } } - fn principal_id(&self) -> PrincipalId { + pub fn email(&self) -> Option { match &self.principal { - Principal::User(user) => PrincipalId::UserId(user.id), - Principal::Impersonated { user, .. } => PrincipalId::UserId(user.id), - Principal::DevServer(dev_server) => PrincipalId::DevServerId(dev_server.id), + Principal::User(user) => user.email_address.clone(), + Principal::Impersonated { user, .. } => user.email_address.clone(), } } } @@ -244,143 +217,11 @@ impl Debug for Session { result.field("user", &user.github_login); result.field("impersonator", &admin.github_login); } - Principal::DevServer(dev_server) => { - result.field("dev_server", &dev_server.id); - } } result.field("connection_id", &self.connection_id).finish() } } -struct UserSession(Session); - -impl UserSession { - pub fn new(s: Session) -> Option { - s.user_id().map(|_| UserSession(s)) - } - pub fn user_id(&self) -> UserId { - self.0.user_id().unwrap() - } - - pub fn email(&self) -> Option { - match &self.0.principal { - Principal::User(user) => user.email_address.clone(), - Principal::Impersonated { user, .. } => user.email_address.clone(), - Principal::DevServer(..) => None, - } - } -} - -impl Deref for UserSession { - type Target = Session; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for UserSession { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -struct DevServerSession(Session); - -impl DevServerSession { - pub fn new(s: Session) -> Option { - s.dev_server_id().map(|_| DevServerSession(s)) - } - pub fn dev_server_id(&self) -> DevServerId { - self.0.dev_server_id().unwrap() - } - - fn dev_server(&self) -> &dev_server::Model { - match &self.0.principal { - Principal::DevServer(dev_server) => dev_server, - _ => unreachable!(), - } - } -} - -impl Deref for DevServerSession { - type Target = Session; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for DevServerSession { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -fn user_handler( - handler: impl 'static + Send + Sync + Fn(M, Response, UserSession) -> Fut, -) -> impl 'static + Send + Sync + Fn(M, Response, Session) -> BoxFuture<'static, Result<()>> -where - Fut: Send + Future>, -{ - let handler = Arc::new(handler); - move |message, response, session| { - let handler = handler.clone(); - Box::pin(async move { - if let Some(user_session) = session.for_user() { - Ok(handler(message, response, user_session).await?) - } else { - Err(Error::Internal(anyhow!( - "must be a user to call {}", - M::NAME - ))) - } - }) - } -} - -fn dev_server_handler( - handler: impl 'static + Send + Sync + Fn(M, Response, DevServerSession) -> Fut, -) -> impl 'static + Send + Sync + Fn(M, Response, Session) -> BoxFuture<'static, Result<()>> -where - Fut: Send + Future>, -{ - let handler = Arc::new(handler); - move |message, response, session| { - let handler = handler.clone(); - Box::pin(async move { - if let Some(dev_server_session) = session.for_dev_server() { - Ok(handler(message, response, dev_server_session).await?) - } else { - Err(Error::Internal(anyhow!( - "must be a dev server to call {}", - M::NAME - ))) - } - }) - } -} - -fn user_message_handler( - handler: impl 'static + Send + Sync + Fn(M, UserSession) -> InnertRetFut, -) -> impl 'static + Send + Sync + Fn(M, Session) -> BoxFuture<'static, Result<()>> -where - InnertRetFut: Send + Future>, -{ - let handler = Arc::new(handler); - move |message, session| { - let handler = handler.clone(); - Box::pin(async move { - if let Some(user_session) = session.for_user() { - Ok(handler(message, user_session).await?) - } else { - Err(Error::Internal(anyhow!( - "must be a user to call {}", - M::NAME - ))) - } - }) - } -} - struct DbHandle(Arc); impl Deref for DbHandle { @@ -434,141 +275,64 @@ impl Server { server .add_request_handler(ping) - .add_request_handler(user_handler(create_room)) - .add_request_handler(user_handler(join_room)) - .add_request_handler(user_handler(rejoin_room)) - .add_request_handler(user_handler(leave_room)) - .add_request_handler(user_handler(set_room_participant_role)) - .add_request_handler(user_handler(call)) - .add_request_handler(user_handler(cancel_call)) - .add_message_handler(user_message_handler(decline_call)) - .add_request_handler(user_handler(update_participant_location)) - .add_request_handler(user_handler(share_project)) + .add_request_handler(create_room) + .add_request_handler(join_room) + .add_request_handler(rejoin_room) + .add_request_handler(leave_room) + .add_request_handler(set_room_participant_role) + .add_request_handler(call) + .add_request_handler(cancel_call) + .add_message_handler(decline_call) + .add_request_handler(update_participant_location) + .add_request_handler(share_project) .add_message_handler(unshare_project) - .add_request_handler(user_handler(join_project)) - .add_request_handler(user_handler(join_hosted_project)) - .add_request_handler(user_handler(rejoin_dev_server_projects)) - .add_request_handler(user_handler(create_dev_server_project)) - .add_request_handler(user_handler(update_dev_server_project)) - .add_request_handler(user_handler(delete_dev_server_project)) - .add_request_handler(user_handler(create_dev_server)) - .add_request_handler(user_handler(regenerate_dev_server_token)) - .add_request_handler(user_handler(rename_dev_server)) - .add_request_handler(user_handler(delete_dev_server)) - .add_request_handler(user_handler(list_remote_directory)) - .add_request_handler(dev_server_handler(share_dev_server_project)) - .add_request_handler(dev_server_handler(shutdown_dev_server)) - .add_request_handler(dev_server_handler(reconnect_dev_server)) - .add_message_handler(user_message_handler(leave_project)) + .add_request_handler(join_project) + .add_request_handler(join_hosted_project) + .add_message_handler(leave_project) .add_request_handler(update_project) .add_request_handler(update_worktree) .add_message_handler(start_language_server) .add_message_handler(update_language_server) .add_message_handler(update_diagnostic_summary) .add_message_handler(update_worktree_settings) - .add_request_handler(user_handler( - forward_project_request_for_owner::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler(forward_find_search_candidates_request)) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_read_only_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_find_search_candidates_request) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler( forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( + ) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler( forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) + ) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) .add_message_handler(create_buffer_for_peer) .add_request_handler(update_buffer) .add_message_handler(broadcast_project_message_from_host::) @@ -577,53 +341,47 @@ impl Server { .add_message_handler(broadcast_project_message_from_host::) .add_message_handler(broadcast_project_message_from_host::) .add_request_handler(get_users) - .add_request_handler(user_handler(fuzzy_search_users)) - .add_request_handler(user_handler(request_contact)) - .add_request_handler(user_handler(remove_contact)) - .add_request_handler(user_handler(respond_to_contact_request)) + .add_request_handler(fuzzy_search_users) + .add_request_handler(request_contact) + .add_request_handler(remove_contact) + .add_request_handler(respond_to_contact_request) .add_message_handler(subscribe_to_channels) - .add_request_handler(user_handler(create_channel)) - .add_request_handler(user_handler(delete_channel)) - .add_request_handler(user_handler(invite_channel_member)) - .add_request_handler(user_handler(remove_channel_member)) - .add_request_handler(user_handler(set_channel_member_role)) - .add_request_handler(user_handler(set_channel_visibility)) - .add_request_handler(user_handler(rename_channel)) - .add_request_handler(user_handler(join_channel_buffer)) - .add_request_handler(user_handler(leave_channel_buffer)) - .add_message_handler(user_message_handler(update_channel_buffer)) - .add_request_handler(user_handler(rejoin_channel_buffers)) - .add_request_handler(user_handler(get_channel_members)) - .add_request_handler(user_handler(respond_to_channel_invite)) - .add_request_handler(user_handler(join_channel)) - .add_request_handler(user_handler(join_channel_chat)) - .add_message_handler(user_message_handler(leave_channel_chat)) - .add_request_handler(user_handler(send_channel_message)) - .add_request_handler(user_handler(remove_channel_message)) - .add_request_handler(user_handler(update_channel_message)) - .add_request_handler(user_handler(get_channel_messages)) - .add_request_handler(user_handler(get_channel_messages_by_id)) - .add_request_handler(user_handler(get_notifications)) - .add_request_handler(user_handler(mark_notification_as_read)) - .add_request_handler(user_handler(move_channel)) - .add_request_handler(user_handler(follow)) - .add_message_handler(user_message_handler(unfollow)) - .add_message_handler(user_message_handler(update_followers)) - .add_request_handler(user_handler(get_private_user_info)) - .add_request_handler(user_handler(get_llm_api_token)) - .add_request_handler(user_handler(accept_terms_of_service)) - .add_message_handler(user_message_handler(acknowledge_channel_message)) - .add_message_handler(user_message_handler(acknowledge_buffer_version)) - .add_request_handler(user_handler(get_supermaven_api_key)) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) - .add_request_handler(user_handler( - forward_mutating_project_request::, - )) + .add_request_handler(create_channel) + .add_request_handler(delete_channel) + .add_request_handler(invite_channel_member) + .add_request_handler(remove_channel_member) + .add_request_handler(set_channel_member_role) + .add_request_handler(set_channel_visibility) + .add_request_handler(rename_channel) + .add_request_handler(join_channel_buffer) + .add_request_handler(leave_channel_buffer) + .add_message_handler(update_channel_buffer) + .add_request_handler(rejoin_channel_buffers) + .add_request_handler(get_channel_members) + .add_request_handler(respond_to_channel_invite) + .add_request_handler(join_channel) + .add_request_handler(join_channel_chat) + .add_message_handler(leave_channel_chat) + .add_request_handler(send_channel_message) + .add_request_handler(remove_channel_message) + .add_request_handler(update_channel_message) + .add_request_handler(get_channel_messages) + .add_request_handler(get_channel_messages_by_id) + .add_request_handler(get_notifications) + .add_request_handler(mark_notification_as_read) + .add_request_handler(move_channel) + .add_request_handler(follow) + .add_message_handler(unfollow) + .add_message_handler(update_followers) + .add_request_handler(get_private_user_info) + .add_request_handler(get_llm_api_token) + .add_request_handler(accept_terms_of_service) + .add_message_handler(acknowledge_channel_message) + .add_message_handler(acknowledge_buffer_version) + .add_request_handler(get_supermaven_api_key) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) .add_message_handler(broadcast_project_message_from_host::) .add_message_handler(update_context) .add_request_handler({ @@ -636,21 +394,17 @@ impl Server { } } }) - .add_request_handler({ - user_handler(move |request, response, session| { - get_cached_embeddings(request, response, session) - }) - }) + .add_request_handler(get_cached_embeddings) .add_request_handler({ let app_state = app_state.clone(); - user_handler(move |request, response, session| { + move |request, response, session| { compute_embeddings( request, response, session, app_state.config.openai_api_key.clone(), ) - }) + } }); Arc::new(server) @@ -936,7 +690,6 @@ impl Server { user_id=field::Empty, login=field::Empty, impersonator=field::Empty, - dev_server_id=field::Empty, geoip_country_code=field::Empty ); principal.update_span(&span); @@ -1031,7 +784,6 @@ impl Server { user_id=field::Empty, login=field::Empty, impersonator=field::Empty, - dev_server_id=field::Empty ); principal.update_span(&span); let span_enter = span.enter(); @@ -1100,11 +852,7 @@ impl Server { update_user_plan(user.id, session).await?; - let (contacts, dev_server_projects) = future::try_join( - self.app_state.db.get_contacts(user.id), - self.app_state.db.dev_server_projects_update(user.id), - ) - .await?; + let contacts = self.app_state.db.get_contacts(user.id).await?; { let mut pool = self.connection_pool.lock(); @@ -1119,8 +867,6 @@ impl Server { subscribe_user_to_channels(user.id, session).await?; } - send_dev_server_projects_update(user.id, dev_server_projects, session).await; - if let Some(incoming_call) = self.app_state.db.incoming_call_for_user(user.id).await? { @@ -1129,39 +875,6 @@ impl Server { update_user_contacts(user.id, session).await?; } - Principal::DevServer(dev_server) => { - { - let mut pool = self.connection_pool.lock(); - if let Some(stale_connection_id) = pool.dev_server_connection_id(dev_server.id) - { - self.peer.send( - stale_connection_id, - proto::ShutdownDevServer { - reason: Some( - "another dev server connected with the same token".to_string(), - ), - }, - )?; - pool.remove_connection(stale_connection_id)?; - }; - pool.add_dev_server(connection_id, dev_server.id, zed_version); - } - - let projects = self - .app_state - .db - .get_projects_for_dev_server(dev_server.id) - .await?; - self.peer - .send(connection_id, proto::DevServerInstructions { projects })?; - - let status = self - .app_state - .db - .dev_server_projects_update(dev_server.user_id) - .await?; - send_dev_server_projects_update(dev_server.user_id, status, session).await; - } } Ok(()) @@ -1452,33 +1165,25 @@ async fn connection_lost( futures::select_biased! { _ = executor.sleep(RECONNECT_TIMEOUT).fuse() => { - match &session.principal { - Principal::User(_) | Principal::Impersonated{ user: _, admin:_ } => { - let session = session.for_user().unwrap(); - log::info!("connection lost, removing all resources for user:{}, connection:{:?}", session.user_id(), session.connection_id); - leave_room_for_session(&session, session.connection_id).await.trace_err(); - leave_channel_buffers_for_session(&session) - .await - .trace_err(); + log::info!("connection lost, removing all resources for user:{}, connection:{:?}", session.user_id(), session.connection_id); + leave_room_for_session(&session, session.connection_id).await.trace_err(); + leave_channel_buffers_for_session(&session) + .await + .trace_err(); - if !session - .connection_pool() - .await - .is_user_online(session.user_id()) - { - let db = session.db().await; - if let Some(room) = db.decline_call(None, session.user_id()).await.trace_err().flatten() { - room_updated(&room, &session.peer); - } - } + if !session + .connection_pool() + .await + .is_user_online(session.user_id()) + { + let db = session.db().await; + if let Some(room) = db.decline_call(None, session.user_id()).await.trace_err().flatten() { + room_updated(&room, &session.peer); + } + } - update_user_contacts(session.user_id(), &session).await?; - }, - Principal::DevServer(_) => { - lost_dev_server_connection(&session.for_dev_server().unwrap()).await?; - }, - } + update_user_contacts(session.user_id(), &session).await?; }, _ = teardown.changed().fuse() => {} } @@ -1496,7 +1201,7 @@ async fn ping(_: proto::Ping, response: Response, _session: Session async fn create_room( _request: proto::CreateRoom, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let live_kit_room = nanoid::nanoid!(30); @@ -1536,7 +1241,7 @@ async fn create_room( async fn join_room( request: proto::JoinRoom, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let room_id = RoomId::from_proto(request.id); @@ -1603,7 +1308,7 @@ async fn join_room( async fn rejoin_room( request: proto::RejoinRoom, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let room; let channel; @@ -1693,7 +1398,7 @@ async fn rejoin_room( fn notify_rejoined_projects( rejoined_projects: &mut Vec, - session: &UserSession, + session: &Session, ) -> Result<()> { for project in rejoined_projects.iter() { for collaborator in &project.collaborators { @@ -1778,7 +1483,7 @@ fn notify_rejoined_projects( async fn leave_room( _: proto::LeaveRoom, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { leave_room_for_session(&session, session.connection_id).await?; response.send(proto::Ack {})?; @@ -1789,7 +1494,7 @@ async fn leave_room( async fn set_room_participant_role( request: proto::SetRoomParticipantRole, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let user_id = UserId::from_proto(request.user_id); let role = ChannelRole::from(request.role()); @@ -1837,7 +1542,7 @@ async fn set_room_participant_role( async fn call( request: proto::Call, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let room_id = RoomId::from_proto(request.room_id); let calling_user_id = session.user_id(); @@ -1906,7 +1611,7 @@ async fn call( async fn cancel_call( request: proto::CancelCall, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let called_user_id = UserId::from_proto(request.called_user_id); let room_id = RoomId::from_proto(request.room_id); @@ -1941,7 +1646,7 @@ async fn cancel_call( } /// Decline an incoming call. -async fn decline_call(message: proto::DeclineCall, session: UserSession) -> Result<()> { +async fn decline_call(message: proto::DeclineCall, session: Session) -> Result<()> { let room_id = RoomId::from_proto(message.room_id); { let room = session @@ -1976,7 +1681,7 @@ async fn decline_call(message: proto::DeclineCall, session: UserSession) -> Resu async fn update_participant_location( request: proto::UpdateParticipantLocation, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let room_id = RoomId::from_proto(request.room_id); let location = request @@ -1997,7 +1702,7 @@ async fn update_participant_location( async fn share_project( request: proto::ShareProject, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let (project_id, room) = &*session .db() @@ -2007,9 +1712,6 @@ async fn share_project( session.connection_id, &request.worktrees, request.is_ssh_project, - request - .dev_server_project_id - .map(DevServerProjectId::from_proto), ) .await?; response.send(proto::ShareProjectResponse { @@ -2023,26 +1725,19 @@ async fn share_project( /// Unshare a project from the room. async fn unshare_project(message: proto::UnshareProject, session: Session) -> Result<()> { let project_id = ProjectId::from_proto(message.project_id); - unshare_project_internal( - project_id, - session.connection_id, - session.user_id(), - &session, - ) - .await + unshare_project_internal(project_id, session.connection_id, &session).await } async fn unshare_project_internal( project_id: ProjectId, connection_id: ConnectionId, - user_id: Option, session: &Session, ) -> Result<()> { let delete = { let room_guard = session .db() .await - .unshare_project(project_id, connection_id, user_id) + .unshare_project(project_id, connection_id) .await?; let (delete, room, guest_connection_ids) = &*room_guard; @@ -2071,38 +1766,11 @@ async fn unshare_project_internal( Ok(()) } -/// DevServer makes a project available online -async fn share_dev_server_project( - request: proto::ShareDevServerProject, - response: Response, - session: DevServerSession, -) -> Result<()> { - let (dev_server_project, user_id, status) = session - .db() - .await - .share_dev_server_project( - DevServerProjectId::from_proto(request.dev_server_project_id), - session.dev_server_id(), - session.connection_id, - &request.worktrees, - ) - .await?; - let Some(project_id) = dev_server_project.project_id else { - return Err(anyhow!("failed to share remote project"))?; - }; - - send_dev_server_projects_update(user_id, status, &session).await; - - response.send(proto::ShareProjectResponse { project_id })?; - - Ok(()) -} - /// Join someone elses shared project. async fn join_project( request: proto::JoinProject, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let project_id = ProjectId::from_proto(request.project_id); @@ -2133,7 +1801,7 @@ impl JoinProjectInternalResponse for Response { fn join_project_internal( response: impl JoinProjectInternalResponse, - session: UserSession, + session: Session, project: &mut Project, replica_id: &ReplicaId, ) -> Result<()> { @@ -2184,9 +1852,6 @@ fn join_project_internal( collaborators: collaborators.clone(), language_servers: project.language_servers.clone(), role: project.role.into(), - dev_server_project_id: project - .dev_server_project_id - .map(|dev_server_project_id| dev_server_project_id.0 as u64), })?; for (worktree_id, worktree) in mem::take(&mut project.worktrees) { @@ -2252,7 +1917,7 @@ fn join_project_internal( } /// Leave someone elses shared project. -async fn leave_project(request: proto::LeaveProject, session: UserSession) -> Result<()> { +async fn leave_project(request: proto::LeaveProject, session: Session) -> Result<()> { let sender_id = session.connection_id; let project_id = ProjectId::from_proto(request.project_id); let db = session.db().await; @@ -2279,7 +1944,7 @@ async fn leave_project(request: proto::LeaveProject, session: UserSession) -> Re async fn join_hosted_project( request: proto::JoinHostedProject, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let (mut project, replica_id) = session .db() @@ -2294,481 +1959,6 @@ async fn join_hosted_project( join_project_internal(response, session, &mut project, &replica_id) } -async fn list_remote_directory( - request: proto::ListRemoteDirectory, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_id = DevServerId(request.dev_server_id as i32); - let dev_server_connection_id = session - .connection_pool() - .await - .online_dev_server_connection_id(dev_server_id)?; - - session - .db() - .await - .get_dev_server_for_user(dev_server_id, session.user_id()) - .await?; - - response.send( - session - .peer - .forward_request(session.connection_id, dev_server_connection_id, request) - .await?, - )?; - Ok(()) -} - -async fn update_dev_server_project( - request: proto::UpdateDevServerProject, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_project_id = DevServerProjectId(request.dev_server_project_id as i32); - - let (dev_server_project, update) = session - .db() - .await - .update_dev_server_project(dev_server_project_id, &request.paths, session.user_id()) - .await?; - - let projects = session - .db() - .await - .get_projects_for_dev_server(dev_server_project.dev_server_id) - .await?; - - let dev_server_connection_id = session - .connection_pool() - .await - .online_dev_server_connection_id(dev_server_project.dev_server_id)?; - - session.peer.send( - dev_server_connection_id, - proto::DevServerInstructions { projects }, - )?; - - send_dev_server_projects_update(session.user_id(), update, &session).await; - - response.send(proto::Ack {}) -} - -async fn create_dev_server_project( - request: proto::CreateDevServerProject, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_id = DevServerId(request.dev_server_id as i32); - let dev_server_connection_id = session - .connection_pool() - .await - .dev_server_connection_id(dev_server_id); - let Some(dev_server_connection_id) = dev_server_connection_id else { - Err(ErrorCode::DevServerOffline - .message("Cannot create a remote project when the dev server is offline".to_string()) - .anyhow())? - }; - - let path = request.path.clone(); - //Check that the path exists on the dev server - session - .peer - .forward_request( - session.connection_id, - dev_server_connection_id, - proto::ValidateDevServerProjectRequest { path: path.clone() }, - ) - .await?; - - let (dev_server_project, update) = session - .db() - .await - .create_dev_server_project( - DevServerId(request.dev_server_id as i32), - &request.path, - session.user_id(), - ) - .await?; - - let projects = session - .db() - .await - .get_projects_for_dev_server(dev_server_project.dev_server_id) - .await?; - - session.peer.send( - dev_server_connection_id, - proto::DevServerInstructions { projects }, - )?; - - send_dev_server_projects_update(session.user_id(), update, &session).await; - - response.send(proto::CreateDevServerProjectResponse { - dev_server_project: Some(dev_server_project.to_proto(None)), - })?; - Ok(()) -} - -async fn create_dev_server( - request: proto::CreateDevServer, - response: Response, - session: UserSession, -) -> Result<()> { - let access_token = auth::random_token(); - let hashed_access_token = auth::hash_access_token(&access_token); - - if request.name.is_empty() { - return Err(proto::ErrorCode::Forbidden - .message("Dev server name cannot be empty".to_string()) - .anyhow())?; - } - - let (dev_server, status) = session - .db() - .await - .create_dev_server( - &request.name, - request.ssh_connection_string.as_deref(), - &hashed_access_token, - session.user_id(), - ) - .await?; - - send_dev_server_projects_update(session.user_id(), status, &session).await; - - response.send(proto::CreateDevServerResponse { - dev_server_id: dev_server.id.0 as u64, - access_token: auth::generate_dev_server_token(dev_server.id.0 as usize, access_token), - name: request.name, - })?; - Ok(()) -} - -async fn regenerate_dev_server_token( - request: proto::RegenerateDevServerToken, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_id = DevServerId(request.dev_server_id as i32); - let access_token = auth::random_token(); - let hashed_access_token = auth::hash_access_token(&access_token); - - let connection_id = session - .connection_pool() - .await - .dev_server_connection_id(dev_server_id); - if let Some(connection_id) = connection_id { - shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?; - session.peer.send( - connection_id, - proto::ShutdownDevServer { - reason: Some("dev server token was regenerated".to_string()), - }, - )?; - let _ = remove_dev_server_connection(dev_server_id, &session).await; - } - - let status = session - .db() - .await - .update_dev_server_token(dev_server_id, &hashed_access_token, session.user_id()) - .await?; - - send_dev_server_projects_update(session.user_id(), status, &session).await; - - response.send(proto::RegenerateDevServerTokenResponse { - dev_server_id: dev_server_id.to_proto(), - access_token: auth::generate_dev_server_token(dev_server_id.0 as usize, access_token), - })?; - Ok(()) -} - -async fn rename_dev_server( - request: proto::RenameDevServer, - response: Response, - session: UserSession, -) -> Result<()> { - if request.name.trim().is_empty() { - return Err(proto::ErrorCode::Forbidden - .message("Dev server name cannot be empty".to_string()) - .anyhow())?; - } - - let dev_server_id = DevServerId(request.dev_server_id as i32); - let dev_server = session.db().await.get_dev_server(dev_server_id).await?; - if dev_server.user_id != session.user_id() { - return Err(anyhow!(ErrorCode::Forbidden))?; - } - - let status = session - .db() - .await - .rename_dev_server( - dev_server_id, - &request.name, - request.ssh_connection_string.as_deref(), - session.user_id(), - ) - .await?; - - send_dev_server_projects_update(session.user_id(), status, &session).await; - - response.send(proto::Ack {})?; - Ok(()) -} - -async fn delete_dev_server( - request: proto::DeleteDevServer, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_id = DevServerId(request.dev_server_id as i32); - let dev_server = session.db().await.get_dev_server(dev_server_id).await?; - if dev_server.user_id != session.user_id() { - return Err(anyhow!(ErrorCode::Forbidden))?; - } - - let connection_id = session - .connection_pool() - .await - .dev_server_connection_id(dev_server_id); - if let Some(connection_id) = connection_id { - shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?; - session.peer.send( - connection_id, - proto::ShutdownDevServer { - reason: Some("dev server was deleted".to_string()), - }, - )?; - let _ = remove_dev_server_connection(dev_server_id, &session).await; - } - - let status = session - .db() - .await - .delete_dev_server(dev_server_id, session.user_id()) - .await?; - - send_dev_server_projects_update(session.user_id(), status, &session).await; - - response.send(proto::Ack {})?; - Ok(()) -} - -async fn delete_dev_server_project( - request: proto::DeleteDevServerProject, - response: Response, - session: UserSession, -) -> Result<()> { - let dev_server_project_id = DevServerProjectId(request.dev_server_project_id as i32); - let dev_server_project = session - .db() - .await - .get_dev_server_project(dev_server_project_id) - .await?; - - let dev_server = session - .db() - .await - .get_dev_server(dev_server_project.dev_server_id) - .await?; - if dev_server.user_id != session.user_id() { - return Err(anyhow!(ErrorCode::Forbidden))?; - } - - let dev_server_connection_id = session - .connection_pool() - .await - .dev_server_connection_id(dev_server.id); - - if let Some(dev_server_connection_id) = dev_server_connection_id { - let project = session - .db() - .await - .find_dev_server_project(dev_server_project_id) - .await; - if let Ok(project) = project { - unshare_project_internal( - project.id, - dev_server_connection_id, - Some(session.user_id()), - &session, - ) - .await?; - } - } - - let (projects, status) = session - .db() - .await - .delete_dev_server_project(dev_server_project_id, dev_server.id, session.user_id()) - .await?; - - if let Some(dev_server_connection_id) = dev_server_connection_id { - session.peer.send( - dev_server_connection_id, - proto::DevServerInstructions { projects }, - )?; - } - - send_dev_server_projects_update(session.user_id(), status, &session).await; - - response.send(proto::Ack {})?; - Ok(()) -} - -async fn rejoin_dev_server_projects( - request: proto::RejoinRemoteProjects, - response: Response, - session: UserSession, -) -> Result<()> { - let mut rejoined_projects = { - let db = session.db().await; - db.rejoin_dev_server_projects( - &request.rejoined_projects, - session.user_id(), - session.0.connection_id, - ) - .await? - }; - response.send(proto::RejoinRemoteProjectsResponse { - rejoined_projects: rejoined_projects - .iter() - .map(|project| project.to_proto()) - .collect(), - })?; - notify_rejoined_projects(&mut rejoined_projects, &session) -} - -async fn reconnect_dev_server( - request: proto::ReconnectDevServer, - response: Response, - session: DevServerSession, -) -> Result<()> { - let reshared_projects = { - let db = session.db().await; - db.reshare_dev_server_projects( - &request.reshared_projects, - session.dev_server_id(), - session.0.connection_id, - ) - .await? - }; - - for project in &reshared_projects { - for collaborator in &project.collaborators { - session - .peer - .send( - collaborator.connection_id, - proto::UpdateProjectCollaborator { - project_id: project.id.to_proto(), - old_peer_id: Some(project.old_connection_id.into()), - new_peer_id: Some(session.connection_id.into()), - }, - ) - .trace_err(); - } - - broadcast( - Some(session.connection_id), - project - .collaborators - .iter() - .map(|collaborator| collaborator.connection_id), - |connection_id| { - session.peer.forward_send( - session.connection_id, - connection_id, - proto::UpdateProject { - project_id: project.id.to_proto(), - worktrees: project.worktrees.clone(), - }, - ) - }, - ); - } - - response.send(proto::ReconnectDevServerResponse { - reshared_projects: reshared_projects - .iter() - .map(|project| proto::ResharedProject { - id: project.id.to_proto(), - collaborators: project - .collaborators - .iter() - .map(|collaborator| collaborator.to_proto()) - .collect(), - }) - .collect(), - })?; - - Ok(()) -} - -async fn shutdown_dev_server( - _: proto::ShutdownDevServer, - response: Response, - session: DevServerSession, -) -> Result<()> { - response.send(proto::Ack {})?; - shutdown_dev_server_internal(session.dev_server_id(), session.connection_id, &session).await?; - remove_dev_server_connection(session.dev_server_id(), &session).await -} - -async fn shutdown_dev_server_internal( - dev_server_id: DevServerId, - connection_id: ConnectionId, - session: &Session, -) -> Result<()> { - let (dev_server_projects, dev_server) = { - let db = session.db().await; - let dev_server_projects = db.get_projects_for_dev_server(dev_server_id).await?; - let dev_server = db.get_dev_server(dev_server_id).await?; - (dev_server_projects, dev_server) - }; - - for project_id in dev_server_projects.iter().filter_map(|p| p.project_id) { - unshare_project_internal( - ProjectId::from_proto(project_id), - connection_id, - None, - session, - ) - .await?; - } - - session - .connection_pool() - .await - .set_dev_server_offline(dev_server_id); - - let status = session - .db() - .await - .dev_server_projects_update(dev_server.user_id) - .await?; - send_dev_server_projects_update(dev_server.user_id, status, session).await; - - Ok(()) -} - -async fn remove_dev_server_connection(dev_server_id: DevServerId, session: &Session) -> Result<()> { - let dev_server_connection = session - .connection_pool() - .await - .dev_server_connection_id(dev_server_id); - - if let Some(dev_server_connection) = dev_server_connection { - session - .connection_pool() - .await - .remove_connection(dev_server_connection)?; - } - Ok(()) -} - /// Updates other participants with changes to the project async fn update_project( request: proto::UpdateProject, @@ -2922,7 +2112,7 @@ async fn update_language_server( async fn forward_read_only_project_request( request: T, response: Response, - session: UserSession, + session: Session, ) -> Result<()> where T: EntityMessage + RequestMessage, @@ -2931,7 +2121,7 @@ where let host_connection_id = session .db() .await - .host_for_read_only_project_request(project_id, session.connection_id, session.user_id()) + .host_for_read_only_project_request(project_id, session.connection_id) .await?; let payload = session .peer @@ -2944,38 +2134,13 @@ where async fn forward_find_search_candidates_request( request: proto::FindSearchCandidates, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let project_id = ProjectId::from_proto(request.remote_entity_id()); let host_connection_id = session .db() .await - .host_for_read_only_project_request(project_id, session.connection_id, session.user_id()) - .await?; - let payload = session - .peer - .forward_request(session.connection_id, host_connection_id, request) - .await?; - response.send(payload)?; - Ok(()) -} - -/// forward a project request to the dev server. Only allowed -/// if it's your dev server. -async fn forward_project_request_for_owner( - request: T, - response: Response, - session: UserSession, -) -> Result<()> -where - T: EntityMessage + RequestMessage, -{ - let project_id = ProjectId::from_proto(request.remote_entity_id()); - - let host_connection_id = session - .db() - .await - .host_for_owner_project_request(project_id, session.connection_id, session.user_id()) + .host_for_read_only_project_request(project_id, session.connection_id) .await?; let payload = session .peer @@ -2990,7 +2155,7 @@ where async fn forward_mutating_project_request( request: T, response: Response, - session: UserSession, + session: Session, ) -> Result<()> where T: EntityMessage + RequestMessage, @@ -3000,7 +2165,7 @@ where let host_connection_id = session .db() .await - .host_for_mutating_project_request(project_id, session.connection_id, session.user_id()) + .host_for_mutating_project_request(project_id, session.connection_id) .await?; let payload = session .peer @@ -3051,12 +2216,7 @@ async fn update_buffer( let guard = session .db() .await - .connections_for_buffer_update( - project_id, - session.principal_id(), - session.connection_id, - capability, - ) + .connections_for_buffer_update(project_id, session.connection_id, capability) .await?; let (host, guests) = &*guard; @@ -3109,12 +2269,7 @@ async fn update_context(message: proto::UpdateContext, session: Session) -> Resu let guard = session .db() .await - .connections_for_buffer_update( - project_id, - session.principal_id(), - session.connection_id, - capability, - ) + .connections_for_buffer_update(project_id, session.connection_id, capability) .await?; let (host, guests) = &*guard; @@ -3160,7 +2315,7 @@ async fn broadcast_project_message_from_host, - session: UserSession, + session: Session, ) -> Result<()> { let room_id = RoomId::from_proto(request.room_id); let project_id = request.project_id.map(ProjectId::from_proto); @@ -3195,7 +2350,7 @@ async fn follow( } /// Stop following another user in a call. -async fn unfollow(request: proto::Unfollow, session: UserSession) -> Result<()> { +async fn unfollow(request: proto::Unfollow, session: Session) -> Result<()> { let room_id = RoomId::from_proto(request.room_id); let project_id = request.project_id.map(ProjectId::from_proto); let leader_id = request @@ -3227,7 +2382,7 @@ async fn unfollow(request: proto::Unfollow, session: UserSession) -> Result<()> } /// Notify everyone following you of your current location. -async fn update_followers(request: proto::UpdateFollowers, session: UserSession) -> Result<()> { +async fn update_followers(request: proto::UpdateFollowers, session: Session) -> Result<()> { let room_id = RoomId::from_proto(request.room_id); let database = session.db.lock().await; @@ -3289,7 +2444,7 @@ async fn get_users( async fn fuzzy_search_users( request: proto::FuzzySearchUsers, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let query = request.query; let users = match query.len() { @@ -3320,7 +2475,7 @@ async fn fuzzy_search_users( async fn request_contact( request: proto::RequestContact, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let requester_id = session.user_id(); let responder_id = UserId::from_proto(request.responder_id); @@ -3367,7 +2522,7 @@ async fn request_contact( async fn respond_to_contact_request( request: proto::RespondToContactRequest, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let responder_id = session.user_id(); let requester_id = UserId::from_proto(request.requester_id); @@ -3425,7 +2580,7 @@ async fn respond_to_contact_request( async fn remove_contact( request: proto::RemoveContact, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let requester_id = session.user_id(); let responder_id = UserId::from_proto(request.user_id); @@ -3491,11 +2646,7 @@ async fn update_user_plan(_user_id: UserId, session: &Session) -> Result<()> { } async fn subscribe_to_channels(_: proto::SubscribeToChannels, session: Session) -> Result<()> { - subscribe_user_to_channels( - session.user_id().ok_or_else(|| anyhow!("must be a user"))?, - &session, - ) - .await?; + subscribe_user_to_channels(session.user_id(), &session).await?; Ok(()) } @@ -3520,7 +2671,7 @@ async fn subscribe_user_to_channels(user_id: UserId, session: &Session) -> Resul async fn create_channel( request: proto::CreateChannel, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; @@ -3575,7 +2726,7 @@ async fn create_channel( async fn delete_channel( request: proto::DeleteChannel, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; @@ -3603,7 +2754,7 @@ async fn delete_channel( async fn invite_channel_member( request: proto::InviteChannelMember, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3640,7 +2791,7 @@ async fn invite_channel_member( async fn remove_channel_member( request: proto::RemoveChannelMember, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3684,7 +2835,7 @@ async fn remove_channel_member( async fn set_channel_visibility( request: proto::SetChannelVisibility, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3729,7 +2880,7 @@ async fn set_channel_visibility( async fn set_channel_member_role( request: proto::SetChannelMemberRole, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3777,7 +2928,7 @@ async fn set_channel_member_role( async fn rename_channel( request: proto::RenameChannel, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3809,7 +2960,7 @@ async fn rename_channel( async fn move_channel( request: proto::MoveChannel, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); let to = ChannelId::from_proto(request.to); @@ -3852,7 +3003,7 @@ async fn move_channel( async fn get_channel_members( request: proto::GetChannelMembers, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3872,7 +3023,7 @@ async fn get_channel_members( async fn respond_to_channel_invite( request: proto::RespondToChannelInvite, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -3913,7 +3064,7 @@ async fn respond_to_channel_invite( async fn join_channel( request: proto::JoinChannel, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); join_channel_internal(channel_id, Box::new(response), session).await @@ -3936,7 +3087,7 @@ impl JoinChannelInternalResponse for Response { async fn join_channel_internal( channel_id: ChannelId, response: Box, - session: UserSession, + session: Session, ) -> Result<()> { let joined_room = { let mut db = session.db().await; @@ -4033,7 +3184,7 @@ async fn join_channel_internal( async fn join_channel_buffer( request: proto::JoinChannelBuffer, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -4064,7 +3215,7 @@ async fn join_channel_buffer( /// Edit the channel notes async fn update_channel_buffer( request: proto::UpdateChannelBuffer, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -4116,7 +3267,7 @@ async fn update_channel_buffer( async fn rejoin_channel_buffers( request: proto::RejoinChannelBuffers, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let buffers = db @@ -4151,7 +3302,7 @@ async fn rejoin_channel_buffers( async fn leave_channel_buffer( request: proto::LeaveChannelBuffer, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; let channel_id = ChannelId::from_proto(request.channel_id); @@ -4213,7 +3364,7 @@ fn send_notifications( async fn send_channel_message( request: proto::SendChannelMessage, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { // Validate the message body. let body = request.body.trim().to_string(); @@ -4308,7 +3459,7 @@ async fn send_channel_message( async fn remove_channel_message( request: proto::RemoveChannelMessage, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); let message_id = MessageId::from_proto(request.message_id); @@ -4343,7 +3494,7 @@ async fn remove_channel_message( async fn update_channel_message( request: proto::UpdateChannelMessage, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); let message_id = MessageId::from_proto(request.message_id); @@ -4430,7 +3581,7 @@ async fn update_channel_message( /// Mark a channel message as read async fn acknowledge_channel_message( request: proto::AckChannelMessage, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); let message_id = MessageId::from_proto(request.message_id); @@ -4450,7 +3601,7 @@ async fn acknowledge_channel_message( /// Mark a buffer version as synced async fn acknowledge_buffer_version( request: proto::AckBufferOperation, - session: UserSession, + session: Session, ) -> Result<()> { let buffer_id = BufferId::from_proto(request.buffer_id); session @@ -4472,9 +3623,6 @@ async fn count_language_model_tokens( session: Session, config: &Config, ) -> Result<()> { - let Some(session) = session.for_user() else { - return Err(anyhow!("user not found"))?; - }; authorize_access_to_legacy_llm_endpoints(&session).await?; let rate_limit: Box = match session.current_plan(&session.db().await).await? { @@ -4592,7 +3740,7 @@ impl RateLimit for FreeComputeEmbeddingsRateLimit { async fn compute_embeddings( request: proto::ComputeEmbeddings, response: Response, - session: UserSession, + session: Session, api_key: Option>, ) -> Result<()> { let api_key = api_key.context("no OpenAI API key configured on the server")?; @@ -4658,7 +3806,7 @@ async fn compute_embeddings( async fn get_cached_embeddings( request: proto::GetCachedEmbeddings, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { authorize_access_to_legacy_llm_endpoints(&session).await?; @@ -4677,7 +3825,7 @@ async fn get_cached_embeddings( /// This is leftover from before the LLM service. /// /// The endpoints protected by this check will be moved there eventually. -async fn authorize_access_to_legacy_llm_endpoints(session: &UserSession) -> Result<(), Error> { +async fn authorize_access_to_legacy_llm_endpoints(session: &Session) -> Result<(), Error> { if session.is_staff() { Ok(()) } else { @@ -4689,7 +3837,7 @@ async fn authorize_access_to_legacy_llm_endpoints(session: &UserSession) -> Resu async fn get_supermaven_api_key( _request: proto::GetSupermavenApiKey, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let user_id: String = session.user_id().to_string(); if !session.is_staff() { @@ -4720,7 +3868,7 @@ async fn get_supermaven_api_key( async fn join_channel_chat( request: proto::JoinChannelChat, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); @@ -4738,7 +3886,7 @@ async fn join_channel_chat( } /// Stop receiving chat updates for a channel -async fn leave_channel_chat(request: proto::LeaveChannelChat, session: UserSession) -> Result<()> { +async fn leave_channel_chat(request: proto::LeaveChannelChat, session: Session) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); session .db() @@ -4752,7 +3900,7 @@ async fn leave_channel_chat(request: proto::LeaveChannelChat, session: UserSessi async fn get_channel_messages( request: proto::GetChannelMessages, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let channel_id = ChannelId::from_proto(request.channel_id); let messages = session @@ -4776,7 +3924,7 @@ async fn get_channel_messages( async fn get_channel_messages_by_id( request: proto::GetChannelMessagesById, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let message_ids = request .message_ids @@ -4799,7 +3947,7 @@ async fn get_channel_messages_by_id( async fn get_notifications( request: proto::GetNotifications, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let notifications = session .db() @@ -4821,7 +3969,7 @@ async fn get_notifications( async fn mark_notification_as_read( request: proto::MarkNotificationRead, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let database = &session.db().await; let notifications = database @@ -4843,7 +3991,7 @@ async fn mark_notification_as_read( async fn get_private_user_info( _request: proto::GetPrivateUserInfo, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; @@ -4867,7 +4015,7 @@ async fn get_private_user_info( async fn accept_terms_of_service( _request: proto::AcceptTermsOfService, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; @@ -4887,7 +4035,7 @@ const MIN_ACCOUNT_AGE_FOR_LLM_USE: chrono::Duration = chrono::Duration::days(30) async fn get_llm_api_token( _request: proto::GetLlmToken, response: Response, - session: UserSession, + session: Session, ) -> Result<()> { let db = session.db().await; @@ -5140,22 +4288,6 @@ fn channel_updated( ); } -async fn send_dev_server_projects_update( - user_id: UserId, - mut status: proto::DevServerProjectsUpdate, - session: &Session, -) { - let pool = session.connection_pool().await; - for dev_server in &mut status.dev_servers { - dev_server.status = - pool.dev_server_status(DevServerId(dev_server.dev_server_id as i32)) as i32; - } - let connections = pool.user_connection_ids(user_id); - for connection_id in connections { - session.peer.send(connection_id, status.clone()).trace_err(); - } -} - async fn update_user_contacts(user_id: UserId, session: &Session) -> Result<()> { let db = session.db().await; @@ -5191,32 +4323,7 @@ async fn update_user_contacts(user_id: UserId, session: &Session) -> Result<()> Ok(()) } -async fn lost_dev_server_connection(session: &DevServerSession) -> Result<()> { - log::info!("lost dev server connection, unsharing projects"); - let project_ids = session - .db() - .await - .get_stale_dev_server_projects(session.connection_id) - .await?; - - for project_id in project_ids { - // not unshare re-checks the connection ids match, so we get away with no transaction - unshare_project_internal(project_id, session.connection_id, None, session).await?; - } - - let user_id = session.dev_server().user_id; - let update = session - .db() - .await - .dev_server_projects_update(user_id) - .await?; - - send_dev_server_projects_update(user_id, update, session).await; - - Ok(()) -} - -async fn leave_room_for_session(session: &UserSession, connection_id: ConnectionId) -> Result<()> { +async fn leave_room_for_session(session: &Session, connection_id: ConnectionId) -> Result<()> { let mut contacts_to_update = HashSet::default(); let room_id; @@ -5312,7 +4419,7 @@ async fn leave_channel_buffers_for_session(session: &Session) -> Result<()> { Ok(()) } -fn project_left(project: &db::LeftProject, session: &UserSession) { +fn project_left(project: &db::LeftProject, session: &Session) { for connection_id in &project.connection_ids { if project.should_unshare { session diff --git a/crates/collab/src/rpc/connection_pool.rs b/crates/collab/src/rpc/connection_pool.rs index 96deefba79..6af90770dc 100644 --- a/crates/collab/src/rpc/connection_pool.rs +++ b/crates/collab/src/rpc/connection_pool.rs @@ -1,7 +1,7 @@ -use crate::db::{ChannelId, ChannelRole, DevServerId, PrincipalId, UserId}; +use crate::db::{ChannelId, ChannelRole, UserId}; use anyhow::{anyhow, Result}; use collections::{BTreeMap, HashMap, HashSet}; -use rpc::{proto, ConnectionId}; +use rpc::ConnectionId; use semantic_version::SemanticVersion; use serde::Serialize; use std::fmt; @@ -11,9 +11,7 @@ use tracing::instrument; pub struct ConnectionPool { connections: BTreeMap, connected_users: BTreeMap, - connected_dev_servers: BTreeMap, channels: ChannelPool, - offline_dev_servers: HashSet, } #[derive(Default, Serialize)] @@ -32,13 +30,13 @@ impl fmt::Display for ZedVersion { impl ZedVersion { pub fn can_collaborate(&self) -> bool { - self.0 >= SemanticVersion::new(0, 151, 0) + self.0 >= SemanticVersion::new(0, 157, 0) } } #[derive(Serialize)] pub struct Connection { - pub principal_id: PrincipalId, + pub user_id: UserId, pub admin: bool, pub zed_version: ZedVersion, } @@ -47,7 +45,6 @@ impl ConnectionPool { pub fn reset(&mut self) { self.connections.clear(); self.connected_users.clear(); - self.connected_dev_servers.clear(); self.channels.clear(); } @@ -66,7 +63,7 @@ impl ConnectionPool { self.connections.insert( connection_id, Connection { - principal_id: PrincipalId::UserId(user_id), + user_id, admin, zed_version, }, @@ -75,25 +72,6 @@ impl ConnectionPool { connected_user.connection_ids.insert(connection_id); } - pub fn add_dev_server( - &mut self, - connection_id: ConnectionId, - dev_server_id: DevServerId, - zed_version: ZedVersion, - ) { - self.connections.insert( - connection_id, - Connection { - principal_id: PrincipalId::DevServerId(dev_server_id), - admin: false, - zed_version, - }, - ); - - self.connected_dev_servers - .insert(dev_server_id, connection_id); - } - #[instrument(skip(self))] pub fn remove_connection(&mut self, connection_id: ConnectionId) -> Result<()> { let connection = self @@ -101,28 +79,18 @@ impl ConnectionPool { .get_mut(&connection_id) .ok_or_else(|| anyhow!("no such connection"))?; - match connection.principal_id { - PrincipalId::UserId(user_id) => { - let connected_user = self.connected_users.get_mut(&user_id).unwrap(); - connected_user.connection_ids.remove(&connection_id); - if connected_user.connection_ids.is_empty() { - self.connected_users.remove(&user_id); - self.channels.remove_user(&user_id); - } - } - PrincipalId::DevServerId(dev_server_id) => { - self.connected_dev_servers.remove(&dev_server_id); - self.offline_dev_servers.remove(&dev_server_id); - } - } + let user_id = connection.user_id; + + let connected_user = self.connected_users.get_mut(&user_id).unwrap(); + connected_user.connection_ids.remove(&connection_id); + if connected_user.connection_ids.is_empty() { + self.connected_users.remove(&user_id); + self.channels.remove_user(&user_id); + }; self.connections.remove(&connection_id).unwrap(); Ok(()) } - pub fn set_dev_server_offline(&mut self, dev_server_id: DevServerId) { - self.offline_dev_servers.insert(dev_server_id); - } - pub fn connections(&self) -> impl Iterator { self.connections.values() } @@ -147,42 +115,6 @@ impl ConnectionPool { .copied() } - pub fn dev_server_status(&self, dev_server_id: DevServerId) -> proto::DevServerStatus { - if self.dev_server_connection_id(dev_server_id).is_some() - && !self.offline_dev_servers.contains(&dev_server_id) - { - proto::DevServerStatus::Online - } else { - proto::DevServerStatus::Offline - } - } - - pub fn dev_server_connection_id(&self, dev_server_id: DevServerId) -> Option { - self.connected_dev_servers.get(&dev_server_id).copied() - } - - pub fn online_dev_server_connection_id( - &self, - dev_server_id: DevServerId, - ) -> Result { - match self.connected_dev_servers.get(&dev_server_id) { - Some(cid) => Ok(*cid), - None => Err(anyhow!(proto::ErrorCode::DevServerOffline)), - } - } - - pub fn dev_server_connection_id_supporting( - &self, - dev_server_id: DevServerId, - required: ZedVersion, - ) -> Result { - match self.connected_dev_servers.get(&dev_server_id) { - Some(cid) if self.connections[cid].zed_version >= required => Ok(*cid), - Some(_) => Err(anyhow!(proto::ErrorCode::RemoteUpgradeRequired)), - None => Err(anyhow!(proto::ErrorCode::DevServerOffline)), - } - } - pub fn channel_user_ids( &self, channel_id: ChannelId, @@ -227,39 +159,22 @@ impl ConnectionPool { #[cfg(test)] pub fn check_invariants(&self) { for (connection_id, connection) in &self.connections { - match &connection.principal_id { - PrincipalId::UserId(user_id) => { - assert!(self - .connected_users - .get(user_id) - .unwrap() - .connection_ids - .contains(connection_id)); - } - PrincipalId::DevServerId(dev_server_id) => { - assert_eq!( - self.connected_dev_servers.get(dev_server_id).unwrap(), - connection_id - ); - } - } + assert!(self + .connected_users + .get(&connection.user_id) + .unwrap() + .connection_ids + .contains(connection_id)); } for (user_id, state) in &self.connected_users { for connection_id in &state.connection_ids { assert_eq!( - self.connections.get(connection_id).unwrap().principal_id, - PrincipalId::UserId(*user_id) + self.connections.get(connection_id).unwrap().user_id, + *user_id ); } } - - for (dev_server_id, connection_id) in &self.connected_dev_servers { - assert_eq!( - self.connections.get(connection_id).unwrap().principal_id, - PrincipalId::DevServerId(*dev_server_id) - ); - } } } diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index f6e0bc3036..29373bc6ea 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -8,7 +8,6 @@ mod channel_buffer_tests; mod channel_guest_tests; mod channel_message_tests; mod channel_tests; -mod dev_server_tests; mod editor_tests; mod following_tests; mod integration_tests; diff --git a/crates/collab/src/tests/dev_server_tests.rs b/crates/collab/src/tests/dev_server_tests.rs deleted file mode 100644 index cbeb2a85a0..0000000000 --- a/crates/collab/src/tests/dev_server_tests.rs +++ /dev/null @@ -1,643 +0,0 @@ -use std::{path::Path, sync::Arc}; - -use call::ActiveCall; -use editor::Editor; -use fs::Fs; -use gpui::{TestAppContext, VisualTestContext, WindowHandle}; -use rpc::{proto::DevServerStatus, ErrorCode, ErrorExt}; -use serde_json::json; -use workspace::{AppState, Workspace}; - -use crate::tests::{following_tests::join_channel, TestServer}; - -use super::TestClient; - -#[gpui::test] -async fn test_dev_server(cx: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) { - let (server, client) = TestServer::start1(cx).await; - - let store = cx.update(|cx| dev_server_projects::Store::global(cx).clone()); - - let resp = store - .update(cx, |store, cx| { - store.create_dev_server("server-1".to_string(), None, cx) - }) - .await - .unwrap(); - - store.update(cx, |store, _| { - assert_eq!(store.dev_servers().len(), 1); - assert_eq!(store.dev_servers()[0].name, "server-1"); - assert_eq!(store.dev_servers()[0].status, DevServerStatus::Offline); - }); - - let dev_server = server.create_dev_server(resp.access_token, cx2).await; - cx.executor().run_until_parked(); - store.update(cx, |store, _| { - assert_eq!(store.dev_servers()[0].status, DevServerStatus::Online); - }); - - dev_server - .fs() - .insert_tree( - "/remote", - json!({ - "1.txt": "remote\nremote\nremote", - "2.js": "function two() { return 2; }", - "3.rs": "mod test", - }), - ) - .await; - - store - .update(cx, |store, cx| { - store.create_dev_server_project( - client::DevServerId(resp.dev_server_id), - "/remote".to_string(), - cx, - ) - }) - .await - .unwrap(); - - cx.executor().run_until_parked(); - - let remote_workspace = store - .update(cx, |store, cx| { - let projects = store.dev_server_projects(); - assert_eq!(projects.len(), 1); - assert_eq!(projects[0].paths, vec!["/remote"]); - workspace::join_dev_server_project( - projects[0].id, - projects[0].project_id.unwrap(), - client.app_state.clone(), - None, - cx, - ) - }) - .await - .unwrap(); - - cx.executor().run_until_parked(); - - let cx = VisualTestContext::from_window(remote_workspace.into(), cx).as_mut(); - cx.simulate_keystrokes("cmd-p 1 enter"); - - let editor = remote_workspace - .update(cx, |ws, cx| { - ws.active_item_as::(cx).unwrap().clone() - }) - .unwrap(); - editor.update(cx, |ed, cx| { - assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote"); - }); - cx.simulate_input("wow!"); - cx.simulate_keystrokes("cmd-s"); - - let content = dev_server - .fs() - .load(Path::new("/remote/1.txt")) - .await - .unwrap(); - assert_eq!(content, "wow!remote\nremote\nremote\n"); -} - -#[gpui::test] -async fn test_dev_server_env_files( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.executor().run_until_parked(); - - let cx1 = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut(); - cx1.simulate_keystrokes("cmd-p . e enter"); - - let editor = remote_workspace - .update(cx1, |ws, cx| { - ws.active_item_as::(cx).unwrap().clone() - }) - .unwrap(); - editor.update(cx1, |ed, cx| { - assert_eq!(ed.text(cx).to_string(), "SECRET"); - }); - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - join_channel(channel_id, &client2, cx2).await.unwrap(); - cx2.executor().run_until_parked(); - - let (workspace2, cx2) = client2.active_workspace(cx2); - let editor = workspace2.update(cx2, |ws, cx| { - ws.active_item_as::(cx).unwrap().clone() - }); - // TODO: it'd be nice to hide .env files from other people - editor.update(cx2, |ed, cx| { - assert_eq!(ed.text(cx).to_string(), "SECRET"); - }); -} - -async fn create_dev_server_project( - server: &TestServer, - client_app_state: Arc, - cx: &mut TestAppContext, - cx_devserver: &mut TestAppContext, -) -> (TestClient, WindowHandle) { - let store = cx.update(|cx| dev_server_projects::Store::global(cx).clone()); - - let resp = store - .update(cx, |store, cx| { - store.create_dev_server("server-1".to_string(), None, cx) - }) - .await - .unwrap(); - let dev_server = server - .create_dev_server(resp.access_token, cx_devserver) - .await; - - cx.executor().run_until_parked(); - - dev_server - .fs() - .insert_tree( - "/remote", - json!({ - "1.txt": "remote\nremote\nremote", - ".env": "SECRET", - }), - ) - .await; - - store - .update(cx, |store, cx| { - store.create_dev_server_project( - client::DevServerId(resp.dev_server_id), - "/remote".to_string(), - cx, - ) - }) - .await - .unwrap(); - - cx.executor().run_until_parked(); - - let workspace = store - .update(cx, |store, cx| { - let projects = store.dev_server_projects(); - assert_eq!(projects.len(), 1); - assert_eq!(projects[0].paths, vec!["/remote"]); - workspace::join_dev_server_project( - projects[0].id, - projects[0].project_id.unwrap(), - client_app_state, - None, - cx, - ) - }) - .await - .unwrap(); - - cx.executor().run_until_parked(); - - (dev_server, workspace) -} - -#[gpui::test] -async fn test_dev_server_leave_room( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - join_channel(channel_id, &client2, cx2).await.unwrap(); - cx2.executor().run_until_parked(); - - cx1.update(|cx| ActiveCall::global(cx).update(cx, |active_call, cx| active_call.hang_up(cx))) - .await - .unwrap(); - - cx1.executor().run_until_parked(); - - let (workspace, cx2) = client2.active_workspace(cx2); - cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx))); -} - -#[gpui::test] -async fn test_dev_server_delete( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - join_channel(channel_id, &client2, cx2).await.unwrap(); - cx2.executor().run_until_parked(); - - cx1.update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, cx| { - store.delete_dev_server_project(store.dev_server_projects().first().unwrap().id, cx) - }) - }) - .await - .unwrap(); - - cx1.executor().run_until_parked(); - - let (workspace, cx2) = client2.active_workspace(cx2); - cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx))); - - cx1.update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, _| { - assert_eq!(store.dev_server_projects().len(), 0); - }) - }) -} - -#[gpui::test] -async fn test_dev_server_rename( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - join_channel(channel_id, &client2, cx2).await.unwrap(); - cx2.executor().run_until_parked(); - - cx1.update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, cx| { - store.rename_dev_server( - store.dev_servers().first().unwrap().id, - "name-edited".to_string(), - None, - cx, - ) - }) - }) - .await - .unwrap(); - - cx1.executor().run_until_parked(); - - cx1.update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, _| { - assert_eq!(store.dev_servers().first().unwrap().name, "name-edited"); - }) - }) -} - -#[gpui::test] -async fn test_dev_server_refresh_access_token( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, - cx4: &mut gpui::TestAppContext, -) { - let (server, client1, client2, channel_id) = TestServer::start2(cx1, cx2).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - join_channel(channel_id, &client2, cx2).await.unwrap(); - cx2.executor().run_until_parked(); - - // Regenerate the access token - let new_token_response = cx1 - .update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, cx| { - store.regenerate_dev_server_token(store.dev_servers().first().unwrap().id, cx) - }) - }) - .await - .unwrap(); - - cx1.executor().run_until_parked(); - - // Assert that the other client was disconnected - let (workspace, cx2) = client2.active_workspace(cx2); - cx2.update(|cx| assert!(workspace.read(cx).project().read(cx).is_disconnected(cx))); - - // Assert that the owner of the dev server does not see the dev server as online anymore - let (workspace, cx1) = client1.active_workspace(cx1); - cx1.update(|cx| { - assert!(workspace.read(cx).project().read(cx).is_disconnected(cx)); - dev_server_projects::Store::global(cx).update(cx, |store, _| { - assert_eq!( - store.dev_servers().first().unwrap().status, - DevServerStatus::Offline - ); - }) - }); - - // Reconnect the dev server with the new token - let _dev_server = server - .create_dev_server(new_token_response.access_token, cx4) - .await; - - cx1.executor().run_until_parked(); - - // Assert that the dev server is online again - cx1.update(|cx| { - dev_server_projects::Store::global(cx).update(cx, |store, _| { - assert_eq!(store.dev_servers().len(), 1); - assert_eq!( - store.dev_servers().first().unwrap().status, - DevServerStatus::Online - ); - }) - }); -} - -#[gpui::test] -async fn test_dev_server_reconnect( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (mut server, client1) = TestServer::start1(cx1).await; - let channel_id = server - .make_channel("test", None, (&client1, cx1), &mut []) - .await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx3).await; - - cx1.update(|cx| { - workspace::join_channel( - channel_id, - client1.app_state.clone(), - Some(remote_workspace), - cx, - ) - }) - .await - .unwrap(); - cx1.executor().run_until_parked(); - - remote_workspace - .update(cx1, |ws, cx| { - assert!(ws.project().read(cx).is_shared()); - }) - .unwrap(); - - drop(client1); - - let client2 = server.create_client(cx2, "user_a").await; - - let store = cx2.update(|cx| dev_server_projects::Store::global(cx).clone()); - - store - .update(cx2, |store, cx| { - let projects = store.dev_server_projects(); - workspace::join_dev_server_project( - projects[0].id, - projects[0].project_id.unwrap(), - client2.app_state.clone(), - None, - cx, - ) - }) - .await - .unwrap(); -} - -#[gpui::test] -async fn test_dev_server_restart(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) { - let (server, client1) = TestServer::start1(cx1).await; - - let (_dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await; - let cx = VisualTestContext::from_window(remote_workspace.into(), cx1).as_mut(); - - server.reset().await; - cx.run_until_parked(); - - cx.simulate_keystrokes("cmd-p 1 enter"); - remote_workspace - .update(cx, |ws, cx| { - ws.active_item_as::(cx) - .unwrap() - .update(cx, |ed, cx| { - assert_eq!(ed.text(cx).to_string(), "remote\nremote\nremote"); - }) - }) - .unwrap(); -} - -#[gpui::test] -async fn test_create_dev_server_project_path_validation( - cx1: &mut gpui::TestAppContext, - cx2: &mut gpui::TestAppContext, - cx3: &mut gpui::TestAppContext, -) { - let (server, client1) = TestServer::start1(cx1).await; - let _channel_id = server - .make_channel("test", None, (&client1, cx1), &mut []) - .await; - - // Creating a project with a path that does exist should not fail - let (_dev_server, _) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await; - - cx1.executor().run_until_parked(); - - let store = cx1.update(|cx| dev_server_projects::Store::global(cx).clone()); - - let resp = store - .update(cx1, |store, cx| { - store.create_dev_server("server-2".to_string(), None, cx) - }) - .await - .unwrap(); - - cx1.executor().run_until_parked(); - - let _dev_server = server.create_dev_server(resp.access_token, cx3).await; - - cx1.executor().run_until_parked(); - - // Creating a remote project with a path that does not exist should fail - let result = store - .update(cx1, |store, cx| { - store.create_dev_server_project( - client::DevServerId(resp.dev_server_id), - "/notfound".to_string(), - cx, - ) - }) - .await; - - cx1.executor().run_until_parked(); - - let error = result.unwrap_err(); - assert!(matches!( - error.error_code(), - ErrorCode::DevServerProjectPathDoesNotExist - )); -} - -#[gpui::test] -async fn test_save_as_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) { - let (server, client1) = TestServer::start1(cx1).await; - - // Creating a project with a path that does exist should not fail - let (dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await; - - let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1); - - cx.simulate_keystrokes("cmd-p 1 enter"); - cx.simulate_keystrokes("cmd-shift-s"); - cx.simulate_input("2.txt"); - cx.simulate_keystrokes("enter"); - - cx.executor().run_until_parked(); - - let title = remote_workspace - .update(&mut cx, |ws, cx| { - let active_item = ws.active_item(cx).unwrap(); - active_item.tab_description(0, cx).unwrap() - }) - .unwrap(); - - assert_eq!(title, "2.txt"); - - let path = Path::new("/remote/2.txt"); - assert_eq!( - dev_server.fs().load(path).await.unwrap(), - "remote\nremote\nremote" - ); -} - -#[gpui::test] -async fn test_new_file_remote(cx1: &mut gpui::TestAppContext, cx2: &mut gpui::TestAppContext) { - let (server, client1) = TestServer::start1(cx1).await; - - // Creating a project with a path that does exist should not fail - let (dev_server, remote_workspace) = - create_dev_server_project(&server, client1.app_state.clone(), cx1, cx2).await; - - let mut cx = VisualTestContext::from_window(remote_workspace.into(), cx1); - - cx.simulate_keystrokes("cmd-n"); - cx.simulate_input("new!"); - cx.simulate_keystrokes("cmd-shift-s"); - cx.simulate_input("2.txt"); - cx.simulate_keystrokes("enter"); - - cx.executor().run_until_parked(); - - let title = remote_workspace - .update(&mut cx, |ws, cx| { - ws.active_item(cx).unwrap().tab_description(0, cx).unwrap() - }) - .unwrap(); - - assert_eq!(title, "2.txt"); - - let path = Path::new("/remote/2.txt"); - assert_eq!(dev_server.fs().load(path).await.unwrap(), "new!"); -} diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 210a049e0b..0e8d0fd808 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -1,5 +1,4 @@ use crate::{ - auth::split_dev_server_token, db::{tests::TestDb, NewUserParams, UserId}, executor::Executor, rpc::{Principal, Server, ZedVersion, CLEANUP_TIMEOUT, RECONNECT_TIMEOUT}, @@ -204,7 +203,7 @@ impl TestServer { .override_authenticate(move |cx| { cx.spawn(|_| async move { let access_token = "the-token".to_string(); - Ok(Credentials::User { + Ok(Credentials { user_id: user_id.to_proto(), access_token, }) @@ -213,7 +212,7 @@ impl TestServer { .override_establish_connection(move |credentials, cx| { assert_eq!( credentials, - &Credentials::User { + &Credentials { user_id: user_id.0 as u64, access_token: "the-token".into() } @@ -297,7 +296,6 @@ impl TestServer { collab_ui::init(&app_state, cx); file_finder::init(cx); menu::init(); - dev_server_projects::init(client.clone(), cx); settings::KeymapFile::load_asset(os_keymap, cx).unwrap(); language_model::LanguageModelRegistry::test(cx); assistant::context_store::init(&client.clone().into()); @@ -319,135 +317,6 @@ impl TestServer { client } - pub async fn create_dev_server( - &self, - access_token: String, - cx: &mut TestAppContext, - ) -> TestClient { - cx.update(|cx| { - if cx.has_global::() { - panic!("Same cx used to create two test clients") - } - let settings = SettingsStore::test(cx); - cx.set_global(settings); - release_channel::init(SemanticVersion::default(), cx); - client::init_settings(cx); - }); - let (dev_server_id, _) = split_dev_server_token(&access_token).unwrap(); - - let clock = Arc::new(FakeSystemClock::default()); - let http = FakeHttpClient::with_404_response(); - let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx)); - let server = self.server.clone(); - let db = self.app_state.db.clone(); - let connection_killers = self.connection_killers.clone(); - let forbid_connections = self.forbid_connections.clone(); - Arc::get_mut(&mut client) - .unwrap() - .set_id(1) - .set_dev_server_token(client::DevServerToken(access_token.clone())) - .override_establish_connection(move |credentials, cx| { - assert_eq!( - credentials, - &Credentials::DevServer { - token: client::DevServerToken(access_token.to_string()) - } - ); - - let server = server.clone(); - let db = db.clone(); - let connection_killers = connection_killers.clone(); - let forbid_connections = forbid_connections.clone(); - cx.spawn(move |cx| async move { - if forbid_connections.load(SeqCst) { - Err(EstablishConnectionError::other(anyhow!( - "server is forbidding connections" - ))) - } else { - let (client_conn, server_conn, killed) = - Connection::in_memory(cx.background_executor().clone()); - let (connection_id_tx, connection_id_rx) = oneshot::channel(); - let dev_server = db - .get_dev_server(dev_server_id) - .await - .expect("retrieving dev_server failed"); - cx.background_executor() - .spawn(server.handle_connection( - server_conn, - "dev-server".to_string(), - Principal::DevServer(dev_server), - ZedVersion(SemanticVersion::new(1, 0, 0)), - None, - Some(connection_id_tx), - Executor::Deterministic(cx.background_executor().clone()), - )) - .detach(); - let connection_id = connection_id_rx.await.map_err(|e| { - EstablishConnectionError::Other(anyhow!( - "{} (is server shutting down?)", - e - )) - })?; - connection_killers - .lock() - .insert(connection_id.into(), killed); - Ok(client_conn) - } - }) - }); - - let fs = FakeFs::new(cx.executor()); - let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); - let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); - let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); - let session = cx.new_model(|cx| AppSession::new(Session::test(), cx)); - let app_state = Arc::new(workspace::AppState { - client: client.clone(), - user_store: user_store.clone(), - workspace_store, - languages: language_registry, - fs: fs.clone(), - build_window_options: |_, _| Default::default(), - node_runtime: NodeRuntime::unavailable(), - session, - }); - - cx.update(|cx| { - theme::init(theme::LoadThemes::JustBase, cx); - Project::init(&client, cx); - client::init(&client, cx); - language::init(cx); - editor::init(cx); - workspace::init(app_state.clone(), cx); - call::init(client.clone(), user_store.clone(), cx); - channel::init(&client, user_store.clone(), cx); - notifications::init(client.clone(), user_store, cx); - collab_ui::init(&app_state, cx); - file_finder::init(cx); - menu::init(); - headless::init( - client.clone(), - headless::AppState { - languages: app_state.languages.clone(), - user_store: app_state.user_store.clone(), - fs: fs.clone(), - node_runtime: app_state.node_runtime.clone(), - }, - cx, - ) - }) - .await - .unwrap(); - - TestClient { - app_state, - username: "dev-server".to_string(), - channel_store: cx.read(ChannelStore::global).clone(), - notification_store: cx.read(NotificationStore::global).clone(), - state: Default::default(), - } - } - pub fn disconnect_client(&self, peer_id: PeerId) { self.connection_killers .lock() diff --git a/crates/dev_server_projects/Cargo.toml b/crates/dev_server_projects/Cargo.toml deleted file mode 100644 index 81d50301bc..0000000000 --- a/crates/dev_server_projects/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "dev_server_projects" -version = "0.1.0" -edition = "2021" -publish = false -license = "GPL-3.0-or-later" - -[lints] -workspace = true - -[lib] -path = "src/dev_server_projects.rs" -doctest = false - -[dependencies] -anyhow.workspace = true -gpui.workspace = true -serde.workspace = true -client.workspace = true -rpc.workspace = true - -[dev-dependencies] -serde_json.workspace = true diff --git a/crates/dev_server_projects/LICENSE-GPL b/crates/dev_server_projects/LICENSE-GPL deleted file mode 120000 index 89e542f750..0000000000 --- a/crates/dev_server_projects/LICENSE-GPL +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE-GPL \ No newline at end of file diff --git a/crates/dev_server_projects/src/dev_server_projects.rs b/crates/dev_server_projects/src/dev_server_projects.rs index 0852b1d20f..8b13789179 100644 --- a/crates/dev_server_projects/src/dev_server_projects.rs +++ b/crates/dev_server_projects/src/dev_server_projects.rs @@ -1,249 +1 @@ -use anyhow::Result; -use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, SharedString, Task}; -use rpc::{ - proto::{self, DevServerStatus}, - TypedEnvelope, -}; -use std::{collections::HashMap, sync::Arc}; -use client::{Client, ProjectId}; -pub use client::{DevServerId, DevServerProjectId}; - -pub struct Store { - dev_server_projects: HashMap, - dev_servers: HashMap, - _subscriptions: Vec, - client: Arc, -} - -#[derive(Debug, Clone)] -pub struct DevServerProject { - pub id: DevServerProjectId, - pub project_id: Option, - pub paths: Vec, - pub dev_server_id: DevServerId, -} - -impl From for DevServerProject { - fn from(project: proto::DevServerProject) -> Self { - Self { - id: DevServerProjectId(project.id), - project_id: project.project_id.map(ProjectId), - paths: project.paths.into_iter().map(|path| path.into()).collect(), - dev_server_id: DevServerId(project.dev_server_id), - } - } -} - -#[derive(Debug, Clone)] -pub struct DevServer { - pub id: DevServerId, - pub name: SharedString, - pub ssh_connection_string: Option, - pub status: DevServerStatus, -} - -impl From for DevServer { - fn from(dev_server: proto::DevServer) -> Self { - Self { - id: DevServerId(dev_server.dev_server_id), - status: dev_server.status(), - name: dev_server.name.into(), - ssh_connection_string: dev_server.ssh_connection_string.map(|s| s.into()), - } - } -} - -struct GlobalStore(Model); - -impl Global for GlobalStore {} - -pub fn init(client: Arc, cx: &mut AppContext) { - let store = cx.new_model(|cx| Store::new(client, cx)); - cx.set_global(GlobalStore(store)); -} - -impl Store { - pub fn global(cx: &AppContext) -> Model { - cx.global::().0.clone() - } - - pub fn new(client: Arc, cx: &ModelContext) -> Self { - Self { - dev_server_projects: Default::default(), - dev_servers: Default::default(), - _subscriptions: vec![client - .add_message_handler(cx.weak_model(), Self::handle_dev_server_projects_update)], - client, - } - } - - pub fn projects_for_server(&self, id: DevServerId) -> Vec { - let mut projects: Vec = self - .dev_server_projects - .values() - .filter(|project| project.dev_server_id == id) - .cloned() - .collect(); - projects.sort_by_key(|p| (p.paths.clone(), p.id)); - projects - } - - pub fn dev_servers(&self) -> Vec { - let mut dev_servers: Vec = self.dev_servers.values().cloned().collect(); - dev_servers.sort_by_key(|d| (d.status == DevServerStatus::Offline, d.name.clone(), d.id)); - dev_servers - } - - pub fn dev_server(&self, id: DevServerId) -> Option<&DevServer> { - self.dev_servers.get(&id) - } - - pub fn dev_server_status(&self, id: DevServerId) -> DevServerStatus { - self.dev_server(id) - .map(|server| server.status) - .unwrap_or(DevServerStatus::Offline) - } - - pub fn dev_server_projects(&self) -> Vec { - let mut projects: Vec = - self.dev_server_projects.values().cloned().collect(); - projects.sort_by_key(|p| (p.paths.clone(), p.id)); - projects - } - - pub fn dev_server_project(&self, id: DevServerProjectId) -> Option<&DevServerProject> { - self.dev_server_projects.get(&id) - } - - pub fn dev_server_for_project(&self, id: DevServerProjectId) -> Option<&DevServer> { - self.dev_server_project(id) - .and_then(|project| self.dev_server(project.dev_server_id)) - } - - async fn handle_dev_server_projects_update( - this: Model, - envelope: TypedEnvelope, - mut cx: AsyncAppContext, - ) -> Result<()> { - this.update(&mut cx, |this, cx| { - this.dev_servers = envelope - .payload - .dev_servers - .into_iter() - .map(|dev_server| (DevServerId(dev_server.dev_server_id), dev_server.into())) - .collect(); - this.dev_server_projects = envelope - .payload - .dev_server_projects - .into_iter() - .map(|project| (DevServerProjectId(project.id), project.into())) - .collect(); - - cx.notify(); - })?; - Ok(()) - } - - pub fn create_dev_server_project( - &mut self, - dev_server_id: DevServerId, - path: String, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - client - .request(proto::CreateDevServerProject { - dev_server_id: dev_server_id.0, - path, - }) - .await - }) - } - - pub fn create_dev_server( - &mut self, - name: String, - ssh_connection_string: Option, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - let result = client - .request(proto::CreateDevServer { - name, - ssh_connection_string, - }) - .await?; - Ok(result) - }) - } - - pub fn rename_dev_server( - &mut self, - dev_server_id: DevServerId, - name: String, - ssh_connection_string: Option, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - client - .request(proto::RenameDevServer { - dev_server_id: dev_server_id.0, - name, - ssh_connection_string, - }) - .await?; - Ok(()) - }) - } - - pub fn regenerate_dev_server_token( - &mut self, - dev_server_id: DevServerId, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - client - .request(proto::RegenerateDevServerToken { - dev_server_id: dev_server_id.0, - }) - .await - }) - } - - pub fn delete_dev_server( - &mut self, - id: DevServerId, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - client - .request(proto::DeleteDevServer { - dev_server_id: id.0, - }) - .await?; - Ok(()) - }) - } - - pub fn delete_dev_server_project( - &mut self, - id: DevServerProjectId, - cx: &mut ModelContext, - ) -> Task> { - let client = self.client.clone(); - cx.background_executor().spawn(async move { - client - .request(proto::DeleteDevServerProject { - dev_server_project_id: id.0, - }) - .await?; - Ok(()) - }) - } -} diff --git a/crates/headless/Cargo.toml b/crates/headless/Cargo.toml deleted file mode 100644 index 209e843c04..0000000000 --- a/crates/headless/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "headless" -version = "0.1.0" -edition = "2021" -publish = false -license = "GPL-3.0-or-later" - -[lints] -workspace = true - -[lib] -path = "src/headless.rs" -doctest = false - -[dependencies] -anyhow.workspace = true -client.workspace = true -extension.workspace = true -signal-hook.workspace = true -gpui.workspace = true -log.workspace = true -util.workspace = true -node_runtime.workspace = true -language.workspace = true -project.workspace = true -proto.workspace = true -fs.workspace = true -futures.workspace = true -settings.workspace = true -shellexpand.workspace = true -postage.workspace = true - -[dev-dependencies] -client = { workspace = true, features = ["test-support"] } -fs = { workspace = true, features = ["test-support"] } -gpui = { workspace = true, features = ["test-support"] } -util = { workspace = true, features = ["test-support"] } diff --git a/crates/headless/LICENSE-GPL b/crates/headless/LICENSE-GPL deleted file mode 120000 index 89e542f750..0000000000 --- a/crates/headless/LICENSE-GPL +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE-GPL \ No newline at end of file diff --git a/crates/headless/src/headless.rs b/crates/headless/src/headless.rs deleted file mode 100644 index 6f8f42fc0f..0000000000 --- a/crates/headless/src/headless.rs +++ /dev/null @@ -1,397 +0,0 @@ -use anyhow::{anyhow, Result}; -use client::DevServerProjectId; -use client::{user::UserStore, Client, ClientSettings}; -use extension::ExtensionStore; -use fs::Fs; -use futures::{Future, StreamExt}; -use gpui::{AppContext, AsyncAppContext, Context, Global, Model, ModelContext, Task, WeakModel}; -use language::LanguageRegistry; -use node_runtime::NodeRuntime; -use postage::stream::Stream; -use project::Project; -use proto::{self, ErrorCode, TypedEnvelope}; -use settings::{Settings, SettingsStore}; -use std::path::Path; -use std::{collections::HashMap, sync::Arc}; -use util::{ResultExt, TryFutureExt}; - -pub struct DevServer { - client: Arc, - app_state: AppState, - remote_shutdown: bool, - projects: HashMap>, - _subscriptions: Vec, - _maintain_connection: Task>, -} - -pub struct AppState { - pub node_runtime: NodeRuntime, - pub user_store: Model, - pub languages: Arc, - pub fs: Arc, -} - -struct GlobalDevServer(Model); - -impl Global for GlobalDevServer {} - -pub fn init(client: Arc, app_state: AppState, cx: &mut AppContext) -> Task> { - let dev_server = cx.new_model(|cx| DevServer::new(client.clone(), app_state, cx)); - cx.set_global(GlobalDevServer(dev_server.clone())); - - #[cfg(not(target_os = "windows"))] - { - use signal_hook::consts::{SIGINT, SIGTERM}; - use signal_hook::iterator::Signals; - // Set up a handler when the dev server is shut down - // with ctrl-c or kill - let (tx, rx) = futures::channel::oneshot::channel(); - let mut signals = Signals::new([SIGTERM, SIGINT]).unwrap(); - std::thread::spawn({ - move || { - if let Some(sig) = signals.forever().next() { - tx.send(sig).log_err(); - } - } - }); - cx.spawn(|cx| async move { - if let Ok(sig) = rx.await { - log::info!("received signal {sig:?}"); - cx.update(|cx| cx.quit()).log_err(); - } - }) - .detach(); - } - - let server_url = ClientSettings::get_global(cx).server_url.clone(); - cx.spawn(|cx| async move { - client - .authenticate_and_connect(false, &cx) - .await - .map_err(|e| anyhow!("Error connecting to '{}': {}", server_url, e)) - }) -} - -impl DevServer { - pub fn global(cx: &AppContext) -> Model { - cx.global::().0.clone() - } - - pub fn new(client: Arc, app_state: AppState, cx: &mut ModelContext) -> Self { - cx.on_app_quit(Self::app_will_quit).detach(); - - let maintain_connection = cx.spawn({ - let client = client.clone(); - move |this, cx| Self::maintain_connection(this, client.clone(), cx).log_err() - }); - - cx.observe_global::(|_, cx| { - ExtensionStore::global(cx).update(cx, |store, cx| store.auto_install_extensions(cx)) - }) - .detach(); - - DevServer { - _subscriptions: vec![ - client.add_message_handler(cx.weak_model(), Self::handle_dev_server_instructions), - client.add_request_handler( - cx.weak_model(), - Self::handle_validate_dev_server_project_request, - ), - client.add_request_handler(cx.weak_model(), Self::handle_list_remote_directory), - client.add_message_handler(cx.weak_model(), Self::handle_shutdown), - ], - _maintain_connection: maintain_connection, - projects: Default::default(), - remote_shutdown: false, - app_state, - client, - } - } - - fn app_will_quit(&mut self, _: &mut ModelContext) -> impl Future { - let request = if self.remote_shutdown { - None - } else { - Some( - self.client - .request(proto::ShutdownDevServer { reason: None }), - ) - }; - async move { - if let Some(request) = request { - request.await.log_err(); - } - } - } - - async fn handle_dev_server_instructions( - this: Model, - envelope: TypedEnvelope, - mut cx: AsyncAppContext, - ) -> Result<()> { - let (added_projects, retained_projects, removed_projects_ids) = - this.read_with(&mut cx, |this, _| { - let removed_projects = this - .projects - .keys() - .filter(|dev_server_project_id| { - !envelope - .payload - .projects - .iter() - .any(|p| p.id == dev_server_project_id.0) - }) - .cloned() - .collect::>(); - - let mut added_projects = vec![]; - let mut retained_projects = vec![]; - - for project in envelope.payload.projects.iter() { - if this.projects.contains_key(&DevServerProjectId(project.id)) { - retained_projects.push(project.clone()); - } else { - added_projects.push(project.clone()); - } - } - - (added_projects, retained_projects, removed_projects) - })?; - - for dev_server_project in added_projects { - DevServer::share_project(this.clone(), &dev_server_project, &mut cx).await?; - } - - for dev_server_project in retained_projects { - DevServer::update_project(this.clone(), &dev_server_project, &mut cx).await?; - } - - this.update(&mut cx, |this, cx| { - for old_project_id in &removed_projects_ids { - this.unshare_project(old_project_id, cx)?; - } - Ok::<(), anyhow::Error>(()) - })??; - Ok(()) - } - - async fn handle_validate_dev_server_project_request( - this: Model, - envelope: TypedEnvelope, - cx: AsyncAppContext, - ) -> Result { - let expanded = shellexpand::tilde(&envelope.payload.path).to_string(); - let path = std::path::Path::new(&expanded); - let fs = cx.read_model(&this, |this, _| this.app_state.fs.clone())?; - - let path_exists = fs.metadata(path).await.is_ok_and(|result| result.is_some()); - if !path_exists { - return Err(anyhow!(ErrorCode::DevServerProjectPathDoesNotExist))?; - } - - Ok(proto::Ack {}) - } - - async fn handle_list_remote_directory( - this: Model, - envelope: TypedEnvelope, - cx: AsyncAppContext, - ) -> Result { - let expanded = shellexpand::tilde(&envelope.payload.path).to_string(); - let fs = cx.read_model(&this, |this, _| this.app_state.fs.clone())?; - - let mut entries = Vec::new(); - let mut response = fs.read_dir(Path::new(&expanded)).await?; - while let Some(path) = response.next().await { - if let Some(file_name) = path?.file_name() { - entries.push(file_name.to_string_lossy().to_string()); - } - } - Ok(proto::ListRemoteDirectoryResponse { entries }) - } - - async fn handle_shutdown( - this: Model, - _envelope: TypedEnvelope, - mut cx: AsyncAppContext, - ) -> Result<()> { - this.update(&mut cx, |this, cx| { - this.remote_shutdown = true; - cx.quit(); - }) - } - - fn unshare_project( - &mut self, - dev_server_project_id: &DevServerProjectId, - cx: &mut ModelContext, - ) -> Result<()> { - if let Some(project) = self.projects.remove(dev_server_project_id) { - project.update(cx, |project, cx| project.unshare(cx))?; - } - Ok(()) - } - - async fn share_project( - this: Model, - dev_server_project: &proto::DevServerProject, - cx: &mut AsyncAppContext, - ) -> Result<()> { - let (client, project) = this.update(cx, |this, cx| { - let project = Project::local( - this.client.clone(), - this.app_state.node_runtime.clone(), - this.app_state.user_store.clone(), - this.app_state.languages.clone(), - this.app_state.fs.clone(), - None, - cx, - ); - - (this.client.clone(), project) - })?; - - for path in &dev_server_project.paths { - let path = shellexpand::tilde(path).to_string(); - - let (worktree, _) = project - .update(cx, |project, cx| { - project.find_or_create_worktree(&path, true, cx) - })? - .await?; - - worktree.update(cx, |worktree, cx| { - worktree.as_local_mut().unwrap().share_private_files(cx) - })?; - } - - let worktrees = - project.read_with(cx, |project, cx| project.worktree_metadata_protos(cx))?; - - let response = client - .request(proto::ShareDevServerProject { - dev_server_project_id: dev_server_project.id, - worktrees, - }) - .await?; - - let project_id = response.project_id; - project.update(cx, |project, cx| project.shared(project_id, cx))??; - this.update(cx, |this, _| { - this.projects - .insert(DevServerProjectId(dev_server_project.id), project); - })?; - Ok(()) - } - - async fn update_project( - this: Model, - dev_server_project: &proto::DevServerProject, - cx: &mut AsyncAppContext, - ) -> Result<()> { - let tasks = this.update(cx, |this, cx| { - let Some(project) = this - .projects - .get(&DevServerProjectId(dev_server_project.id)) - else { - return vec![]; - }; - - let mut to_delete = vec![]; - let mut tasks = vec![]; - - project.update(cx, |project, cx| { - for worktree in project.visible_worktrees(cx) { - let mut delete = true; - for config in dev_server_project.paths.iter() { - if worktree.read(cx).abs_path().to_string_lossy() - == shellexpand::tilde(config) - { - delete = false; - } - } - if delete { - to_delete.push(worktree.read(cx).id()) - } - } - - for worktree_id in to_delete { - project.remove_worktree(worktree_id, cx) - } - - for config in dev_server_project.paths.iter() { - tasks.push(project.find_or_create_worktree( - shellexpand::tilde(config).to_string(), - true, - cx, - )); - } - - tasks - }) - })?; - futures::future::join_all(tasks).await; - Ok(()) - } - - async fn maintain_connection( - this: WeakModel, - client: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - let mut client_status = client.status(); - - let _ = client_status.try_recv(); - let current_status = *client_status.borrow(); - if current_status.is_connected() { - // wait for first disconnect - client_status.recv().await; - } - - loop { - let Some(current_status) = client_status.recv().await else { - return Ok(()); - }; - let Some(this) = this.upgrade() else { - return Ok(()); - }; - - if !current_status.is_connected() { - continue; - } - - this.update(&mut cx, |this, cx| this.rejoin(cx))?.await?; - } - } - - fn rejoin(&mut self, cx: &mut ModelContext) -> Task> { - let mut projects: HashMap> = HashMap::default(); - let request = self.client.request(proto::ReconnectDevServer { - reshared_projects: self - .projects - .iter() - .flat_map(|(_, handle)| { - let project = handle.read(cx); - let project_id = project.remote_id()?; - projects.insert(project_id, handle.clone()); - Some(proto::UpdateProject { - project_id, - worktrees: project.worktree_metadata_protos(cx), - }) - }) - .collect(), - }); - cx.spawn(|_, mut cx| async move { - let response = request.await?; - - for reshared_project in response.reshared_projects { - if let Some(project) = projects.get(&reshared_project.id) { - project.update(&mut cx, |project, cx| { - project.reshared(reshared_project, cx).log_err(); - })?; - } - } - Ok(()) - }) - } -} diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 2a9bb82a35..c360617173 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -30,7 +30,6 @@ async-trait.workspace = true client.workspace = true clock.workspace = true collections.workspace = true -dev_server_projects.workspace = true fs.workspace = true futures.workspace = true fuzzy.workspace = true diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4a3eaf98ba..9459641f86 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,8 +25,7 @@ mod yarn; use anyhow::{anyhow, Context as _, Result}; use buffer_store::{BufferStore, BufferStoreEvent}; use client::{ - proto, Client, Collaborator, DevServerProjectId, PendingEntitySubscription, ProjectId, - TypedEnvelope, UserStore, + proto, Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore, }; use clock::ReplicaId; use collections::{BTreeSet, HashMap, HashSet}; @@ -156,7 +155,6 @@ pub struct Project { terminals: Terminals, node: Option, hosted_project_id: Option, - dev_server_project_id: Option, search_history: SearchHistory, search_included_history: SearchHistory, search_excluded_history: SearchHistory, @@ -217,7 +215,6 @@ enum ProjectClientState { capability: Capability, remote_id: u64, replica_id: ReplicaId, - in_room: bool, }, } @@ -675,7 +672,6 @@ impl Project { }, node: Some(node), hosted_project_id: None, - dev_server_project_id: None, search_history: Self::new_search_history(), environment, remotely_created_models: Default::default(), @@ -705,7 +701,7 @@ impl Project { let ssh_proto = ssh.read(cx).proto_client(); let worktree_store = - cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0, None)); + cx.new_model(|_| WorktreeStore::remote(false, ssh_proto.clone(), 0)); cx.subscribe(&worktree_store, Self::on_worktree_store_event) .detach(); @@ -794,7 +790,6 @@ impl Project { }, node: Some(node), hosted_project_id: None, - dev_server_project_id: None, search_history: Self::new_search_history(), environment, remotely_created_models: Default::default(), @@ -898,15 +893,7 @@ impl Project { let role = response.payload.role(); let worktree_store = cx.new_model(|_| { - WorktreeStore::remote( - true, - client.clone().into(), - response.payload.project_id, - response - .payload - .dev_server_project_id - .map(DevServerProjectId), - ) + WorktreeStore::remote(true, client.clone().into(), response.payload.project_id) })?; let buffer_store = cx.new_model(|cx| { BufferStore::remote(worktree_store.clone(), client.clone().into(), remote_id, cx) @@ -992,7 +979,6 @@ impl Project { capability: Capability::ReadWrite, remote_id, replica_id, - in_room: response.payload.dev_server_project_id.is_none(), }, buffers_needing_diff: Default::default(), git_diff_debouncer: DebouncedDelay::new(), @@ -1001,10 +987,6 @@ impl Project { }, node: None, hosted_project_id: None, - dev_server_project_id: response - .payload - .dev_server_project_id - .map(DevServerProjectId), search_history: Self::new_search_history(), search_included_history: Self::new_search_history(), search_excluded_history: Self::new_search_history(), @@ -1305,39 +1287,23 @@ impl Project { self.hosted_project_id } - pub fn dev_server_project_id(&self) -> Option { - self.dev_server_project_id - } - - pub fn supports_terminal(&self, cx: &AppContext) -> bool { + pub fn supports_terminal(&self, _cx: &AppContext) -> bool { if self.is_local() { return true; } if self.is_via_ssh() { return true; } - let Some(id) = self.dev_server_project_id else { - return false; - }; - let Some(server) = dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_for_project(id) - else { - return false; - }; - server.ssh_connection_string.is_some() + + return false; } pub fn ssh_connection_string(&self, cx: &AppContext) -> Option { if let Some(ssh_state) = &self.ssh_client { return Some(ssh_state.read(cx).connection_string().into()); } - let dev_server_id = self.dev_server_project_id()?; - dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_for_project(dev_server_id)? - .ssh_connection_string - .clone() + + return None; } pub fn ssh_connection_state(&self, cx: &AppContext) -> Option { @@ -1549,17 +1515,9 @@ impl Project { pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext) -> Result<()> { if !matches!(self.client_state, ProjectClientState::Local) { - if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state { - if *in_room || self.dev_server_project_id.is_none() { - return Err(anyhow!("project was already shared")); - } else { - *in_room = true; - return Ok(()); - } - } else { - return Err(anyhow!("project was already shared")); - } + return Err(anyhow!("project was already shared")); } + self.client_subscriptions.extend([ self.client .subscribe_to_entity(project_id)? @@ -1657,14 +1615,7 @@ impl Project { fn unshare_internal(&mut self, cx: &mut AppContext) -> Result<()> { if self.is_via_collab() { - if self.dev_server_project_id().is_some() { - if let ProjectClientState::Remote { in_room, .. } = &mut self.client_state { - *in_room = false - } - return Ok(()); - } else { - return Err(anyhow!("attempted to unshare a remote project")); - } + return Err(anyhow!("attempted to unshare a remote project")); } if let ProjectClientState::Shared { remote_id, .. } = self.client_state { @@ -2265,29 +2216,6 @@ impl Project { } fn on_worktree_released(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext) { - if let Some(dev_server_project_id) = self.dev_server_project_id { - let paths: Vec = self - .visible_worktrees(cx) - .filter_map(|worktree| { - if worktree.read(cx).id() == id_to_remove { - None - } else { - Some(worktree.read(cx).abs_path().to_string_lossy().to_string()) - } - }) - .collect(); - if !paths.is_empty() { - let request = self.client.request(proto::UpdateDevServerProject { - dev_server_project_id: dev_server_project_id.0, - paths, - }); - cx.background_executor() - .spawn(request) - .detach_and_log_err(cx); - } - return; - } - if let Some(ssh) = &self.ssh_client { ssh.read(cx) .proto_client() @@ -3152,7 +3080,7 @@ impl Project { match &self.client_state { ProjectClientState::Shared { .. } => true, ProjectClientState::Local => false, - ProjectClientState::Remote { in_room, .. } => *in_room, + ProjectClientState::Remote { .. } => true, } } @@ -3279,20 +3207,6 @@ impl Project { let response = response.await?; Ok(response.entries.into_iter().map(PathBuf::from).collect()) }) - } else if let Some(dev_server) = self.dev_server_project_id().and_then(|id| { - dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_for_project(id) - }) { - let request = proto::ListRemoteDirectory { - dev_server_id: dev_server.id.0, - path: query, - }; - let response = self.client.request(request); - cx.background_executor().spawn(async move { - let response = response.await?; - Ok(response.entries.into_iter().map(PathBuf::from).collect()) - }) } else { Task::ready(Err(anyhow!("cannot list directory in remote project"))) } diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index d516610590..0ccf730d9e 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -37,11 +37,8 @@ pub enum TerminalKind { /// SshCommand describes how to connect to a remote server #[derive(Debug, Clone, PartialEq, Eq)] -pub enum SshCommand { - /// DevServers give a string from the user - DevServer(String), - /// Direct ssh has a list of arguments to pass to ssh - Direct(Vec), +pub struct SshCommand { + arguments: Vec, } impl Project { @@ -73,19 +70,12 @@ impl Project { if let Some(args) = ssh_client.ssh_args() { return Some(( ssh_client.connection_options().host.clone(), - SshCommand::Direct(args), + SshCommand { arguments: args }, )); } } - let dev_server_project_id = self.dev_server_project_id()?; - let projects_store = dev_server_projects::Store::global(cx).read(cx); - let ssh_command = projects_store - .dev_server_for_project(dev_server_project_id)? - .ssh_connection_string - .as_ref()? - .to_string(); - Some(("".to_string(), SshCommand::DevServer(ssh_command))) + return None; } pub fn create_terminal( @@ -399,14 +389,8 @@ pub fn wrap_for_ssh( }; let shell_invocation = format!("sh -c {}", shlex::try_quote(&commands).unwrap()); - let (program, mut args) = match ssh_command { - SshCommand::DevServer(ssh_command) => { - let mut args = shlex::split(ssh_command).unwrap_or_default(); - let program = args.drain(0..1).next().unwrap_or("ssh".to_string()); - (program, args) - } - SshCommand::Direct(ssh_args) => ("ssh".to_string(), ssh_args.clone()), - }; + let program = "ssh".to_string(); + let mut args = ssh_command.arguments.clone(); args.push("-t".to_string()); args.push(shell_invocation); diff --git a/crates/project/src/worktree_store.rs b/crates/project/src/worktree_store.rs index 7196ae199f..df190d03f3 100644 --- a/crates/project/src/worktree_store.rs +++ b/crates/project/src/worktree_store.rs @@ -1,11 +1,9 @@ use std::{ - cell::RefCell, path::{Path, PathBuf}, sync::{atomic::AtomicUsize, Arc}, }; use anyhow::{anyhow, Context as _, Result}; -use client::DevServerProjectId; use collections::{HashMap, HashSet}; use fs::Fs; use futures::{ @@ -41,7 +39,6 @@ enum WorktreeStoreState { fs: Arc, }, Remote { - dev_server_project_id: Option, upstream_client: AnyProtoClient, upstream_project_id: u64, }, @@ -94,7 +91,6 @@ impl WorktreeStore { retain_worktrees: bool, upstream_client: AnyProtoClient, upstream_project_id: u64, - dev_server_project_id: Option, ) -> Self { Self { next_entry_id: Default::default(), @@ -106,7 +102,6 @@ impl WorktreeStore { state: WorktreeStoreState::Remote { upstream_client, upstream_project_id, - dev_server_project_id, }, } } @@ -196,18 +191,9 @@ impl WorktreeStore { if !self.loading_worktrees.contains_key(&path) { let task = match &self.state { WorktreeStoreState::Remote { - upstream_client, - dev_server_project_id, - .. + upstream_client, .. } => { - if let Some(dev_server_project_id) = dev_server_project_id { - self.create_dev_server_worktree( - upstream_client.clone(), - *dev_server_project_id, - abs_path, - cx, - ) - } else if upstream_client.is_via_collab() { + if upstream_client.is_via_collab() { Task::ready(Err(Arc::new(anyhow!("cannot create worktrees via collab")))) } else { self.create_ssh_worktree(upstream_client.clone(), abs_path, visible, cx) @@ -322,51 +308,6 @@ impl WorktreeStore { }) } - fn create_dev_server_worktree( - &mut self, - client: AnyProtoClient, - dev_server_project_id: DevServerProjectId, - abs_path: impl AsRef, - cx: &mut ModelContext, - ) -> Task, Arc>> { - let path: Arc = abs_path.as_ref().into(); - let mut paths: Vec = self - .visible_worktrees(cx) - .map(|worktree| worktree.read(cx).abs_path().to_string_lossy().to_string()) - .collect(); - paths.push(path.to_string_lossy().to_string()); - let request = client.request(proto::UpdateDevServerProject { - dev_server_project_id: dev_server_project_id.0, - paths, - }); - - let abs_path = abs_path.as_ref().to_path_buf(); - cx.spawn(move |project, cx| async move { - let (tx, rx) = futures::channel::oneshot::channel(); - let tx = RefCell::new(Some(tx)); - let Some(project) = project.upgrade() else { - return Err(anyhow!("project dropped"))?; - }; - let observer = cx.update(|cx| { - cx.observe(&project, move |project, cx| { - let abs_path = abs_path.clone(); - project.update(cx, |project, cx| { - if let Some((worktree, _)) = project.find_worktree(&abs_path, cx) { - if let Some(tx) = tx.borrow_mut().take() { - tx.send(worktree).ok(); - } - } - }) - }) - })?; - - request.await?; - let worktree = rx.await.map_err(|e| anyhow!(e))?; - drop(observer); - Ok(worktree) - }) - } - pub fn add(&mut self, worktree: &Model, cx: &mut ModelContext) { let worktree_id = worktree.read(cx).id(); debug_assert!(self.worktrees().all(|w| w.read(cx).id() != worktree_id)); diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 0f503c696b..cb62a7f04b 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -503,7 +503,7 @@ impl ProjectPanel { let is_unfoldable = auto_fold_dirs && self.is_unfoldable(entry, worktree); let worktree_id = worktree.id(); let is_read_only = project.is_read_only(cx); - let is_remote = project.is_via_collab() && project.dev_server_project_id().is_none(); + let is_remote = project.is_via_collab(); let is_local = project.is_local(); let context_menu = ContextMenu::build(cx, |menu, cx| { @@ -3334,12 +3334,11 @@ impl Panel for ProjectPanel { fn starts_open(&self, cx: &WindowContext) -> bool { let project = &self.project.read(cx); - project.dev_server_project_id().is_some() - || project.visible_worktrees(cx).any(|tree| { - tree.read(cx) - .root_entry() - .map_or(false, |entry| entry.is_dir()) - }) + project.visible_worktrees(cx).any(|tree| { + tree.read(cx) + .root_entry() + .map_or(false, |entry| entry.is_dir()) + }) } } diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 6539604c86..5635eb8800 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -217,33 +217,14 @@ message Envelope { MultiLspQueryResponse multi_lsp_query_response = 176; RestartLanguageServers restart_language_servers = 208; - CreateDevServerProject create_dev_server_project = 177; - CreateDevServerProjectResponse create_dev_server_project_response = 188; - CreateDevServer create_dev_server = 178; - CreateDevServerResponse create_dev_server_response = 179; - ShutdownDevServer shutdown_dev_server = 180; - DevServerInstructions dev_server_instructions = 181; - ReconnectDevServer reconnect_dev_server = 182; - ReconnectDevServerResponse reconnect_dev_server_response = 183; - - ShareDevServerProject share_dev_server_project = 184; - JoinDevServerProject join_dev_server_project = 185; RejoinRemoteProjects rejoin_remote_projects = 186; RejoinRemoteProjectsResponse rejoin_remote_projects_response = 187; - DevServerProjectsUpdate dev_server_projects_update = 193; - ValidateDevServerProjectRequest validate_dev_server_project_request = 194; - DeleteDevServer delete_dev_server = 195; OpenNewBuffer open_new_buffer = 196; - DeleteDevServerProject delete_dev_server_project = 197; GetSupermavenApiKey get_supermaven_api_key = 198; GetSupermavenApiKeyResponse get_supermaven_api_key_response = 199; - RegenerateDevServerToken regenerate_dev_server_token = 200; - RegenerateDevServerTokenResponse regenerate_dev_server_token_response = 201; - RenameDevServer rename_dev_server = 202; - TaskContextForLocation task_context_for_location = 203; TaskContext task_context = 204; @@ -264,7 +245,6 @@ message Envelope { ListRemoteDirectory list_remote_directory = 219; ListRemoteDirectoryResponse list_remote_directory_response = 220; - UpdateDevServerProject update_dev_server_project = 221; AddWorktree add_worktree = 222; AddWorktreeResponse add_worktree_response = 223; @@ -304,10 +284,17 @@ message Envelope { LanguageServerPromptResponse language_server_prompt_response = 269; // current max } + reserved 87 to 88; reserved 158 to 161; reserved 166 to 169; + reserved 177 to 185; + reserved 188; + reserved 193 to 195; + reserved 197; + reserved 200 to 202; reserved 205 to 206; + reserved 221; reserved 224 to 229; reserved 247 to 254; } @@ -342,12 +329,11 @@ enum ErrorCode { WrongMoveTarget = 11; UnsharedItem = 12; NoSuchProject = 13; - DevServerAlreadyOnline = 14; - DevServerOffline = 15; DevServerProjectPathDoesNotExist = 16; RemoteUpgradeRequired = 17; RateLimitExceeded = 18; reserved 6; + reserved 14 to 15; } message EndStream {} @@ -511,7 +497,7 @@ message LiveKitConnectionInfo { message ShareProject { uint64 room_id = 1; repeated WorktreeMetadata worktrees = 2; - optional uint64 dev_server_project_id = 3; + reserved 3; bool is_ssh_project = 4; } @@ -536,19 +522,6 @@ message JoinHostedProject { uint64 project_id = 1; } -message CreateDevServerProject { - reserved 1; - reserved 2; - uint64 dev_server_id = 3; - string path = 4; -} -message CreateDevServerProjectResponse { - DevServerProject dev_server_project = 1; -} - -message ValidateDevServerProjectRequest { - string path = 1; -} message ListRemoteDirectory { uint64 dev_server_id = 1; @@ -559,77 +532,6 @@ message ListRemoteDirectoryResponse { repeated string entries = 1; } -message UpdateDevServerProject { - uint64 dev_server_project_id = 1; - repeated string paths = 2; -} - -message CreateDevServer { - reserved 1; - string name = 2; - optional string ssh_connection_string = 3; -} - -message RegenerateDevServerToken { - uint64 dev_server_id = 1; -} - -message RegenerateDevServerTokenResponse { - uint64 dev_server_id = 1; - string access_token = 2; -} - -message CreateDevServerResponse { - uint64 dev_server_id = 1; - reserved 2; - string access_token = 3; - string name = 4; -} - -message ShutdownDevServer { - optional string reason = 1; -} - -message RenameDevServer { - uint64 dev_server_id = 1; - string name = 2; - optional string ssh_connection_string = 3; -} - -message DeleteDevServer { - uint64 dev_server_id = 1; -} - -message DeleteDevServerProject { - uint64 dev_server_project_id = 1; -} - -message ReconnectDevServer { - repeated UpdateProject reshared_projects = 1; -} - -message ReconnectDevServerResponse { - repeated ResharedProject reshared_projects = 1; -} - -message DevServerInstructions { - repeated DevServerProject projects = 1; -} - -message DevServerProjectsUpdate { - repeated DevServer dev_servers = 1; - repeated DevServerProject dev_server_projects = 2; -} - -message ShareDevServerProject { - uint64 dev_server_project_id = 1; - repeated WorktreeMetadata worktrees = 2; -} - -message JoinDevServerProject { - uint64 dev_server_project_id = 1; -} - message JoinProjectResponse { uint64 project_id = 5; uint32 replica_id = 1; @@ -637,7 +539,7 @@ message JoinProjectResponse { repeated Collaborator collaborators = 3; repeated LanguageServer language_servers = 4; ChannelRole role = 6; - optional uint64 dev_server_project_id = 7; + reserved 7; } message LeaveProject { @@ -1429,29 +1331,6 @@ message HostedProject { ChannelVisibility visibility = 4; } -message DevServerProject { - uint64 id = 1; - optional uint64 project_id = 2; - reserved 3; - reserved 4; - uint64 dev_server_id = 5; - string path = 6; - repeated string paths = 7; -} - -message DevServer { - reserved 1; - uint64 dev_server_id = 2; - string name = 3; - DevServerStatus status = 4; - optional string ssh_connection_string = 5; -} - -enum DevServerStatus { - Offline = 0; - Online = 1; -} - message JoinChannel { uint64 channel_id = 1; } diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index 85d9f572ee..7a31e7cc7a 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -318,30 +318,12 @@ messages!( (SetRoomParticipantRole, Foreground), (BlameBuffer, Foreground), (BlameBufferResponse, Foreground), - (CreateDevServerProject, Background), - (CreateDevServerProjectResponse, Foreground), - (CreateDevServer, Foreground), - (CreateDevServerResponse, Foreground), - (DevServerInstructions, Foreground), - (ShutdownDevServer, Foreground), - (ReconnectDevServer, Foreground), - (ReconnectDevServerResponse, Foreground), - (ShareDevServerProject, Foreground), - (JoinDevServerProject, Foreground), (RejoinRemoteProjects, Foreground), (RejoinRemoteProjectsResponse, Foreground), (MultiLspQuery, Background), (MultiLspQueryResponse, Background), - (DevServerProjectsUpdate, Foreground), - (ValidateDevServerProjectRequest, Background), (ListRemoteDirectory, Background), (ListRemoteDirectoryResponse, Background), - (UpdateDevServerProject, Background), - (DeleteDevServer, Foreground), - (DeleteDevServerProject, Foreground), - (RegenerateDevServerToken, Foreground), - (RegenerateDevServerTokenResponse, Foreground), - (RenameDevServer, Foreground), (OpenNewBuffer, Foreground), (RestartLanguageServers, Foreground), (LinkedEditingRange, Background), @@ -419,7 +401,6 @@ request_messages!( (GetTypeDefinition, GetTypeDefinitionResponse), (LinkedEditingRange, LinkedEditingRangeResponse), (ListRemoteDirectory, ListRemoteDirectoryResponse), - (UpdateDevServerProject, Ack), (GetUsers, UsersResponse), (IncomingCall, Ack), (InlayHints, InlayHintsResponse), @@ -477,19 +458,8 @@ request_messages!( (LspExtExpandMacro, LspExtExpandMacroResponse), (SetRoomParticipantRole, Ack), (BlameBuffer, BlameBufferResponse), - (CreateDevServerProject, CreateDevServerProjectResponse), - (CreateDevServer, CreateDevServerResponse), - (ShutdownDevServer, Ack), - (ShareDevServerProject, ShareProjectResponse), - (JoinDevServerProject, JoinProjectResponse), (RejoinRemoteProjects, RejoinRemoteProjectsResponse), - (ReconnectDevServer, ReconnectDevServerResponse), - (ValidateDevServerProjectRequest, Ack), (MultiLspQuery, MultiLspQueryResponse), - (DeleteDevServer, Ack), - (DeleteDevServerProject, Ack), - (RegenerateDevServerToken, RegenerateDevServerTokenResponse), - (RenameDevServer, Ack), (RestartLanguageServers, Ack), (OpenContext, OpenContextResponse), (CreateContext, CreateContextResponse), diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index f69c6c1c21..b1759de778 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -16,7 +16,6 @@ doctest = false anyhow.workspace = true auto_update.workspace = true release_channel.workspace = true -client.workspace = true editor.workspace = true file_finder.workspace = true futures.workspace = true @@ -30,15 +29,12 @@ menu.workspace = true ordered-float.workspace = true picker.workspace = true project.workspace = true -dev_server_projects.workspace = true remote.workspace = true -rpc.workspace = true schemars.workspace = true serde.workspace = true settings.workspace = true smol.workspace = true task.workspace = true -terminal_view.workspace = true theme.workspace = true ui.workspace = true util.workspace = true diff --git a/crates/recent_projects/src/disconnected_overlay.rs b/crates/recent_projects/src/disconnected_overlay.rs index 09342c1d3c..34a9b895a2 100644 --- a/crates/recent_projects/src/disconnected_overlay.rs +++ b/crates/recent_projects/src/disconnected_overlay.rs @@ -1,6 +1,5 @@ use std::path::PathBuf; -use dev_server_projects::DevServer; use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render, WeakView}; use project::project_settings::ProjectSettings; use remote::SshConnectionOptions; @@ -12,14 +11,10 @@ use ui::{ }; use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Workspace}; -use crate::{ - open_dev_server_project, open_ssh_project, remote_servers::reconnect_to_dev_server_project, - RemoteServerProjects, SshSettings, -}; +use crate::{open_ssh_project, SshSettings}; enum Host { RemoteProject, - DevServerProject(DevServer), SshRemoteProject(SshConnectionOptions), } @@ -55,20 +50,9 @@ impl DisconnectedOverlay { return; } let handle = cx.view().downgrade(); - let dev_server = project - .read(cx) - .dev_server_project_id() - .and_then(|id| { - dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_for_project(id) - }) - .cloned(); let ssh_connection_options = project.read(cx).ssh_connection_options(cx); - let host = if let Some(dev_server) = dev_server { - Host::DevServerProject(dev_server) - } else if let Some(ssh_connection_options) = ssh_connection_options { + let host = if let Some(ssh_connection_options) = ssh_connection_options { Host::SshRemoteProject(ssh_connection_options) } else { Host::RemoteProject @@ -89,9 +73,6 @@ impl DisconnectedOverlay { cx.emit(DismissEvent); match &self.host { - Host::DevServerProject(dev_server) => { - self.reconnect_to_dev_server(dev_server.clone(), cx); - } Host::SshRemoteProject(ssh_connection_options) => { self.reconnect_to_ssh_remote(ssh_connection_options.clone(), cx); } @@ -99,50 +80,6 @@ impl DisconnectedOverlay { } } - fn reconnect_to_dev_server(&self, dev_server: DevServer, cx: &mut ViewContext) { - let Some(workspace) = self.workspace.upgrade() else { - return; - }; - let Some(dev_server_project_id) = workspace - .read(cx) - .project() - .read(cx) - .dev_server_project_id() - else { - return; - }; - - if let Some(project_id) = dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_project(dev_server_project_id) - .and_then(|project| project.project_id) - { - return workspace.update(cx, move |_, cx| { - open_dev_server_project(true, dev_server_project_id, project_id, cx) - .detach_and_prompt_err("Failed to reconnect", cx, |_, _| None) - }); - } - - if dev_server.ssh_connection_string.is_some() { - let task = workspace.update(cx, |_, cx| { - reconnect_to_dev_server_project( - cx.view().clone(), - dev_server, - dev_server_project_id, - true, - cx, - ) - }); - - task.detach_and_prompt_err("Failed to reconnect", cx, |_, _| None); - } else { - return workspace.update(cx, |workspace, cx| { - let handle = cx.view().downgrade(); - workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle)) - }); - } - } - fn reconnect_to_ssh_remote( &self, connection_options: SshConnectionOptions, @@ -200,13 +137,10 @@ impl DisconnectedOverlay { impl Render for DisconnectedOverlay { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - let can_reconnect = matches!( - self.host, - Host::DevServerProject(_) | Host::SshRemoteProject(_) - ); + let can_reconnect = matches!(self.host, Host::SshRemoteProject(_)); let message = match &self.host { - Host::RemoteProject | Host::DevServerProject(_) => { + Host::RemoteProject => { "Your connection to the remote project has been lost.".to_string() } Host::SshRemoteProject(options) => { diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index b31bc1b509..6032e7d996 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -4,7 +4,6 @@ mod ssh_connections; use remote::SshConnectionOptions; pub use ssh_connections::open_ssh_project; -use client::{DevServerProjectId, ProjectId}; use disconnected_overlay::DisconnectedOverlay; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ @@ -17,9 +16,7 @@ use picker::{ highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText}, Picker, PickerDelegate, }; -use remote_servers::reconnect_to_dev_server_project; pub use remote_servers::RemoteServerProjects; -use rpc::proto::DevServerStatus; use serde::Deserialize; use settings::Settings; pub use ssh_connections::SshSettings; @@ -28,13 +25,12 @@ use std::{ sync::Arc, }; use ui::{ - prelude::*, tooltip_container, ButtonLike, IconWithIndicator, Indicator, KeyBinding, ListItem, - ListItemSpacing, Tooltip, + prelude::*, tooltip_container, ButtonLike, KeyBinding, ListItem, ListItemSpacing, Tooltip, }; use util::{paths::PathExt, ResultExt}; use workspace::{ - AppState, CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace, - WorkspaceId, WORKSPACE_DB, + CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace, WorkspaceId, + WORKSPACE_DB, }; #[derive(PartialEq, Clone, Deserialize, Default)] @@ -101,7 +97,7 @@ impl RecentProjects { } } - fn register(workspace: &mut Workspace, cx: &mut ViewContext) { + fn register(workspace: &mut Workspace, _cx: &mut ViewContext) { workspace.register_action(|workspace, open_recent: &OpenRecent, cx| { let Some(recent_projects) = workspace.active_modal::(cx) else { Self::open(workspace, open_recent.create_new_window, cx); @@ -114,20 +110,6 @@ impl RecentProjects { .update(cx, |picker, cx| picker.cycle_selection(cx)) }); }); - if workspace - .project() - .read(cx) - .dev_server_project_id() - .is_some() - { - workspace.register_action(|workspace, _: &workspace::Open, cx| { - if workspace.active_modal::(cx).is_some() { - cx.propagate(); - } else { - Self::open(workspace, true, cx); - } - }); - } } pub fn open( @@ -254,13 +236,6 @@ impl PickerDelegate for RecentProjectsDelegate { .map(|(_, path)| path.compact().to_string_lossy().into_owned()) .collect::>() .join(""), - SerializedWorkspaceLocation::DevServer(dev_server_project) => { - format!( - "{}{}", - dev_server_project.dev_server_name, - dev_server_project.paths.join("") - ) - } SerializedWorkspaceLocation::Ssh(ssh_project) => ssh_project .ssh_urls() .iter() @@ -321,7 +296,10 @@ impl PickerDelegate for RecentProjectsDelegate { cx.spawn(move |workspace, mut cx| async move { let continue_replacing = workspace .update(&mut cx, |workspace, cx| { - workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx) + workspace.prepare_to_close( + CloseIntent::ReplaceWindow, + cx, + ) })? .await?; if continue_replacing { @@ -339,74 +317,56 @@ impl PickerDelegate for RecentProjectsDelegate { workspace.open_workspace_for_paths(false, paths, cx) } } - SerializedWorkspaceLocation::DevServer(dev_server_project) => { - let store = dev_server_projects::Store::global(cx); - let Some(project_id) = store.read(cx) - .dev_server_project(dev_server_project.id) - .and_then(|p| p.project_id) - else { - let server = store.read(cx).dev_server_for_project(dev_server_project.id); - if server.is_some_and(|server| server.ssh_connection_string.is_some()) { - return reconnect_to_dev_server_project(cx.view().clone(), server.unwrap().clone(), dev_server_project.id, replace_current_window, cx); - } else { - let dev_server_name = dev_server_project.dev_server_name.clone(); - return cx.spawn(|workspace, mut cx| async move { - let response = - cx.prompt(gpui::PromptLevel::Warning, - "Dev Server is offline", - Some(format!("Cannot connect to {}. To debug open the remote project settings.", dev_server_name).as_str()), - &["Ok", "Open Settings"] - ).await?; - if response == 1 { - workspace.update(&mut cx, |workspace, cx| { - let handle = cx.view().downgrade(); - workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle)) - })?; - } else { - workspace.update(&mut cx, |workspace, cx| { - RecentProjects::open(workspace, true, cx); - })?; - } - Ok(()) - }) - } + SerializedWorkspaceLocation::Ssh(ssh_project) => { + let app_state = workspace.app_state().clone(); + + let replace_window = if replace_current_window { + cx.window_handle().downcast::() + } else { + None }; - open_dev_server_project(replace_current_window, dev_server_project.id, project_id, cx) - } - SerializedWorkspaceLocation::Ssh(ssh_project) => { - let app_state = workspace.app_state().clone(); - let replace_window = if replace_current_window { - cx.window_handle().downcast::() - } else { - None - }; + let open_options = OpenOptions { + replace_window, + ..Default::default() + }; - let open_options = OpenOptions { - replace_window, - ..Default::default() - }; + let args = SshSettings::get_global(cx).args_for( + &ssh_project.host, + ssh_project.port, + &ssh_project.user, + ); + let nickname = SshSettings::get_global(cx).nickname_for( + &ssh_project.host, + ssh_project.port, + &ssh_project.user, + ); + let connection_options = SshConnectionOptions { + host: ssh_project.host.clone(), + username: ssh_project.user.clone(), + port: ssh_project.port, + password: None, + args, + }; - let args = SshSettings::get_global(cx).args_for(&ssh_project.host, ssh_project.port, &ssh_project.user); - let nickname = SshSettings::get_global(cx).nickname_for(&ssh_project.host, ssh_project.port, &ssh_project.user); - let connection_options = SshConnectionOptions { - host: ssh_project.host.clone(), - username: ssh_project.user.clone(), - port: ssh_project.port, - password: None, - args, - }; + let paths = ssh_project.paths.iter().map(PathBuf::from).collect(); - let paths = ssh_project.paths.iter().map(PathBuf::from).collect(); - - cx.spawn(|_, mut cx| async move { - open_ssh_project(connection_options, paths, app_state, open_options, nickname, &mut cx).await - }) + cx.spawn(|_, mut cx| async move { + open_ssh_project( + connection_options, + paths, + app_state, + open_options, + nickname, + &mut cx, + ) + .await + }) + } } } - } }) - .detach_and_log_err(cx); + .detach_and_log_err(cx); cx.emit(DismissEvent); } } @@ -431,20 +391,6 @@ impl PickerDelegate for RecentProjectsDelegate { let (_, location) = self.workspaces.get(hit.candidate_id)?; - let dev_server_status = - if let SerializedWorkspaceLocation::DevServer(dev_server_project) = location { - let store = dev_server_projects::Store::global(cx).read(cx); - Some( - store - .dev_server_project(dev_server_project.id) - .and_then(|p| store.dev_server(p.dev_server_id)) - .map(|s| s.status) - .unwrap_or_default(), - ) - } else { - None - }; - let mut path_start_offset = 0; let paths = match location { SerializedWorkspaceLocation::Local(paths, order) => Arc::new( @@ -457,13 +403,6 @@ impl PickerDelegate for RecentProjectsDelegate { .collect(), ), SerializedWorkspaceLocation::Ssh(ssh_project) => Arc::new(ssh_project.ssh_urls()), - SerializedWorkspaceLocation::DevServer(dev_server_project) => { - Arc::new(vec![PathBuf::from(format!( - "{}:{}", - dev_server_project.dev_server_name, - dev_server_project.paths.join(", ") - ))]) - } }; let (match_labels, paths): (Vec<_>, Vec<_>) = paths @@ -478,13 +417,7 @@ impl PickerDelegate for RecentProjectsDelegate { .unzip(); let highlighted_match = HighlightedMatchWithPaths { - match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", ").color( - if matches!(dev_server_status, Some(DevServerStatus::Offline)) { - Color::Disabled - } else { - Color::Default - }, - ), + match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", "), paths, }; @@ -507,24 +440,6 @@ impl PickerDelegate for RecentProjectsDelegate { SerializedWorkspaceLocation::Ssh(_) => Icon::new(IconName::Server) .color(Color::Muted) .into_any_element(), - SerializedWorkspaceLocation::DevServer(_) => { - let indicator_color = match dev_server_status { - Some(DevServerStatus::Online) => Color::Created, - Some(DevServerStatus::Offline) => Color::Hidden, - _ => unreachable!(), - }; - IconWithIndicator::new( - Icon::new(IconName::Server).color(Color::Muted), - Some(Indicator::dot()), - ) - .indicator_color(indicator_color) - .indicator_border_color(if selected { - Some(cx.theme().colors().element_selected) - } else { - None - }) - .into_any_element() - } }) }) .child({ @@ -597,59 +512,6 @@ impl PickerDelegate for RecentProjectsDelegate { } } -fn open_dev_server_project( - replace_current_window: bool, - dev_server_project_id: DevServerProjectId, - project_id: ProjectId, - cx: &mut ViewContext, -) -> Task> { - if let Some(app_state) = AppState::global(cx).upgrade() { - let handle = if replace_current_window { - cx.window_handle().downcast::() - } else { - None - }; - - if let Some(handle) = handle { - cx.spawn(move |workspace, mut cx| async move { - let continue_replacing = workspace - .update(&mut cx, |workspace, cx| { - workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx) - })? - .await?; - if continue_replacing { - workspace - .update(&mut cx, |_workspace, cx| { - workspace::join_dev_server_project( - dev_server_project_id, - project_id, - app_state, - Some(handle), - cx, - ) - })? - .await?; - } - Ok(()) - }) - } else { - let task = workspace::join_dev_server_project( - dev_server_project_id, - project_id, - app_state, - None, - cx, - ); - cx.spawn(|_, _| async move { - task.await?; - Ok(()) - }) - } - } else { - Task::ready(Err(anyhow::anyhow!("App state not found"))) - } -} - // Compute the highlighted text for the name and path fn highlights_for_path( path: &Path, diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index 101f87f1ff..7081afc903 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -1,19 +1,12 @@ -use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; -use std::time::Duration; -use anyhow::anyhow; -use anyhow::Context; -use anyhow::Result; -use dev_server_projects::{DevServer, DevServerId, DevServerProjectId}; use editor::Editor; use file_finder::OpenPathDelegate; use futures::channel::oneshot; use futures::future::Shared; use futures::FutureExt; use gpui::canvas; -use gpui::AsyncWindowContext; use gpui::ClipboardItem; use gpui::Task; use gpui::WeakView; @@ -22,17 +15,10 @@ use gpui::{ PromptLevel, ScrollHandle, View, ViewContext, }; use picker::Picker; -use project::terminals::wrap_for_ssh; -use project::terminals::SshCommand; use project::Project; use remote::SshConnectionOptions; -use rpc::proto::DevServerStatus; use settings::update_settings_file; use settings::Settings; -use task::HideStrategy; -use task::RevealStrategy; -use task::SpawnInTerminal; -use terminal_view::terminal_panel::TerminalPanel; use ui::{ prelude::*, IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Scrollbar, ScrollbarState, Section, Tooltip, @@ -43,7 +29,6 @@ use workspace::OpenOptions; use workspace::Toast; use workspace::{notifications::DetachAndPromptErr, ModalView, Workspace}; -use crate::open_dev_server_project; use crate::ssh_connections::connect_over_ssh; use crate::ssh_connections::open_ssh_project; use crate::ssh_connections::RemoteSettingsContent; @@ -1319,146 +1304,3 @@ impl Render for RemoteServerProjects { }) } } - -pub fn reconnect_to_dev_server_project( - workspace: View, - dev_server: DevServer, - dev_server_project_id: DevServerProjectId, - replace_current_window: bool, - cx: &mut WindowContext, -) -> Task> { - let store = dev_server_projects::Store::global(cx); - let reconnect = reconnect_to_dev_server(workspace.clone(), dev_server, cx); - cx.spawn(|mut cx| async move { - reconnect.await?; - - cx.background_executor() - .timer(Duration::from_millis(1000)) - .await; - - if let Some(project_id) = store.update(&mut cx, |store, _| { - store - .dev_server_project(dev_server_project_id) - .and_then(|p| p.project_id) - })? { - workspace - .update(&mut cx, move |_, cx| { - open_dev_server_project( - replace_current_window, - dev_server_project_id, - project_id, - cx, - ) - })? - .await?; - } - - Ok(()) - }) -} - -pub fn reconnect_to_dev_server( - workspace: View, - dev_server: DevServer, - cx: &mut WindowContext, -) -> Task> { - let Some(ssh_connection_string) = dev_server.ssh_connection_string else { - return Task::ready(Err(anyhow!("Can't reconnect, no ssh_connection_string"))); - }; - let dev_server_store = dev_server_projects::Store::global(cx); - let get_access_token = dev_server_store.update(cx, |store, cx| { - store.regenerate_dev_server_token(dev_server.id, cx) - }); - - cx.spawn(|mut cx| async move { - let access_token = get_access_token.await?.access_token; - - spawn_ssh_task( - workspace, - dev_server_store, - dev_server.id, - ssh_connection_string.to_string(), - access_token, - &mut cx, - ) - .await - }) -} - -pub async fn spawn_ssh_task( - workspace: View, - dev_server_store: Model, - dev_server_id: DevServerId, - ssh_connection_string: String, - access_token: String, - cx: &mut AsyncWindowContext, -) -> Result<()> { - let terminal_panel = workspace - .update(cx, |workspace, cx| workspace.panel::(cx)) - .ok() - .flatten() - .with_context(|| anyhow!("No terminal panel"))?; - - let command = "sh".to_string(); - let args = vec![ - "-x".to_string(), - "-c".to_string(), - format!( - r#"~/.local/bin/zed -v >/dev/stderr || (curl -f https://zed.dev/install.sh || wget -qO- https://zed.dev/install.sh) | sh && ZED_HEADLESS=1 ~/.local/bin/zed --dev-server-token {}"#, - access_token - ), - ]; - - let ssh_connection_string = ssh_connection_string.to_string(); - let (command, args) = wrap_for_ssh( - &SshCommand::DevServer(ssh_connection_string.clone()), - Some((&command, &args)), - None, - HashMap::default(), - None, - ); - - let terminal = terminal_panel - .update(cx, |terminal_panel, cx| { - terminal_panel.spawn_in_new_terminal( - SpawnInTerminal { - id: task::TaskId("ssh-remote".into()), - full_label: "Install zed over ssh".into(), - label: "Install zed over ssh".into(), - command, - args, - command_label: ssh_connection_string.clone(), - cwd: None, - use_new_terminal: true, - allow_concurrent_runs: false, - reveal: RevealStrategy::Always, - hide: HideStrategy::Never, - env: Default::default(), - shell: Default::default(), - }, - cx, - ) - })? - .await?; - - terminal - .update(cx, |terminal, cx| terminal.wait_for_completed_task(cx))? - .await; - - // There's a race-condition between the task completing successfully, and the server sending us the online status. Make it less likely we'll show the error state. - if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))? - == DevServerStatus::Offline - { - cx.background_executor() - .timer(Duration::from_millis(200)) - .await - } - - if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))? - == DevServerStatus::Offline - { - return Err(anyhow!("couldn't reconnect"))?; - } - - Ok(()) -} diff --git a/crates/title_bar/Cargo.toml b/crates/title_bar/Cargo.toml index dcfe289ca0..93a181e926 100644 --- a/crates/title_bar/Cargo.toml +++ b/crates/title_bar/Cargo.toml @@ -33,7 +33,6 @@ auto_update.workspace = true call.workspace = true client.workspace = true command_palette.workspace = true -dev_server_projects.workspace = true extensions_ui.workspace = true feedback.workspace = true feature_flags.workspace = true diff --git a/crates/title_bar/src/collab.rs b/crates/title_bar/src/collab.rs index 2f53458905..edbc147926 100644 --- a/crates/title_bar/src/collab.rs +++ b/crates/title_bar/src/collab.rs @@ -285,8 +285,7 @@ impl TitleBar { let room = room.read(cx); let project = self.project.read(cx); let is_local = project.is_local() || project.is_via_ssh(); - let is_dev_server_project = project.dev_server_project_id().is_some(); - let is_shared = (is_local || is_dev_server_project) && project.is_shared(); + let is_shared = is_local && project.is_shared(); let is_muted = room.is_muted(); let is_deafened = room.is_deafened().unwrap_or(false); let is_screen_sharing = room.is_screen_sharing(); @@ -299,7 +298,7 @@ impl TitleBar { let mut children = Vec::new(); - if (is_local || is_dev_server_project) && can_share_projects { + if is_local && can_share_projects { children.push( Button::new( "toggle_sharing", diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index c279d2eb6b..8b3fb5739f 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -20,7 +20,7 @@ use gpui::{ use project::{Project, RepositoryEntry}; use recent_projects::{OpenRemote, RecentProjects, SshSettings}; use remote::SshConnectionOptions; -use rpc::proto::{self, DevServerStatus}; +use rpc::proto; use settings::Settings; use smallvec::SmallVec; use std::sync::Arc; @@ -334,39 +334,6 @@ impl TitleBar { } pub fn render_project_host(&self, cx: &mut ViewContext) -> Option { - if let Some(dev_server) = - self.project - .read(cx) - .dev_server_project_id() - .and_then(|dev_server_project_id| { - dev_server_projects::Store::global(cx) - .read(cx) - .dev_server_for_project(dev_server_project_id) - }) - { - return Some( - ButtonLike::new("dev_server_trigger") - .child(Indicator::dot().color( - if dev_server.status == DevServerStatus::Online { - Color::Created - } else { - Color::Disabled - }, - )) - .child( - Label::new(dev_server.name.clone()) - .size(LabelSize::Small) - .line_height_style(LineHeightStyle::UiLabel), - ) - .tooltip(move |cx| Tooltip::text("Project is hosted on a dev server", cx)) - .on_click(cx.listener(|this, _, cx| { - if let Some(workspace) = this.workspace.upgrade() { - recent_projects::RemoteServerProjects::open(workspace, cx) - } - })) - .into_any_element(), - ); - } if self.project.read(cx).is_via_ssh() { return self.render_ssh_project_host(cx); } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 47f6c138c8..6486302152 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -49,7 +49,6 @@ node_runtime.workspace = true parking_lot.workspace = true postage.workspace = true project.workspace = true -dev_server_projects.workspace = true task.workspace = true release_channel.workspace = true remote.workspace = true diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index d0cb684b52..7c4fb93ba1 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -24,9 +24,7 @@ use model::{ SerializedSshProject, SerializedWorkspace, }; -use self::model::{ - DockStructure, LocalPathsOrder, SerializedDevServerProject, SerializedWorkspaceLocation, -}; +use self::model::{DockStructure, LocalPathsOrder, SerializedWorkspaceLocation}; #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) struct SerializedAxis(pub(crate) gpui::Axis); @@ -460,89 +458,6 @@ impl WorkspaceDb { }) } - pub(crate) fn workspace_for_dev_server_project( - &self, - dev_server_project_id: DevServerProjectId, - ) -> Option { - // Note that we re-assign the workspace_id here in case it's empty - // and we've grabbed the most recent workspace - let ( - workspace_id, - dev_server_project_id, - window_bounds, - display, - centered_layout, - docks, - window_id, - ): ( - WorkspaceId, - Option, - Option, - Option, - Option, - DockStructure, - Option, - ) = self - .select_row_bound(sql! { - SELECT - workspace_id, - dev_server_project_id, - window_state, - window_x, - window_y, - window_width, - window_height, - display, - centered_layout, - left_dock_visible, - left_dock_active_panel, - left_dock_zoom, - right_dock_visible, - right_dock_active_panel, - right_dock_zoom, - bottom_dock_visible, - bottom_dock_active_panel, - bottom_dock_zoom, - window_id - FROM workspaces - WHERE dev_server_project_id = ? - }) - .and_then(|mut prepared_statement| (prepared_statement)(dev_server_project_id.0)) - .context("No workspaces found") - .warn_on_err() - .flatten()?; - - let dev_server_project_id = dev_server_project_id?; - - let dev_server_project: SerializedDevServerProject = self - .select_row_bound(sql! { - SELECT id, path, dev_server_name - FROM dev_server_projects - WHERE id = ? - }) - .and_then(|mut prepared_statement| (prepared_statement)(dev_server_project_id)) - .context("No remote project found") - .warn_on_err() - .flatten()?; - - let location = SerializedWorkspaceLocation::DevServer(dev_server_project); - - Some(SerializedWorkspace { - id: workspace_id, - location, - center_group: self - .get_center_pane_group(workspace_id) - .context("Getting center group") - .log_err()?, - window_bounds, - centered_layout: centered_layout.unwrap_or(false), - display, - docks, - session_id: None, - window_id, - }) - } - pub(crate) fn workspace_for_ssh_project( &self, ssh_project: &SerializedSshProject, @@ -659,61 +574,6 @@ impl WorkspaceDb { prepared_query(args).context("Updating workspace")?; } - SerializedWorkspaceLocation::DevServer(dev_server_project) => { - conn.exec_bound(sql!( - DELETE FROM workspaces WHERE dev_server_project_id = ? AND workspace_id != ? - ))?((dev_server_project.id.0, workspace.id)) - .context("clearing out old locations")?; - - conn.exec_bound(sql!( - INSERT INTO dev_server_projects( - id, - path, - dev_server_name - ) VALUES (?1, ?2, ?3) - ON CONFLICT DO - UPDATE SET - path = ?2, - dev_server_name = ?3 - ))?(&dev_server_project)?; - - // Upsert - conn.exec_bound(sql!( - INSERT INTO workspaces( - workspace_id, - dev_server_project_id, - left_dock_visible, - left_dock_active_panel, - left_dock_zoom, - right_dock_visible, - right_dock_active_panel, - right_dock_zoom, - bottom_dock_visible, - bottom_dock_active_panel, - bottom_dock_zoom, - timestamp - ) - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, CURRENT_TIMESTAMP) - ON CONFLICT DO - UPDATE SET - dev_server_project_id = ?2, - left_dock_visible = ?3, - left_dock_active_panel = ?4, - left_dock_zoom = ?5, - right_dock_visible = ?6, - right_dock_active_panel = ?7, - right_dock_zoom = ?8, - bottom_dock_visible = ?9, - bottom_dock_active_panel = ?10, - bottom_dock_zoom = ?11, - timestamp = CURRENT_TIMESTAMP - ))?(( - workspace.id, - dev_server_project.id.0, - workspace.docks, - )) - .context("Updating workspace")?; - }, SerializedWorkspaceLocation::Ssh(ssh_project) => { conn.exec_bound(sql!( DELETE FROM workspaces WHERE ssh_project_id = ? AND workspace_id != ? @@ -824,11 +684,10 @@ impl WorkspaceDb { } query! { - fn recent_workspaces() -> Result, Option)>> { - SELECT workspace_id, local_paths, local_paths_order, dev_server_project_id, ssh_project_id + fn recent_workspaces() -> Result)>> { + SELECT workspace_id, local_paths, local_paths_order, ssh_project_id FROM workspaces WHERE local_paths IS NOT NULL - OR dev_server_project_id IS NOT NULL OR ssh_project_id IS NOT NULL ORDER BY timestamp DESC } @@ -843,13 +702,6 @@ impl WorkspaceDb { } } - query! { - fn dev_server_projects() -> Result> { - SELECT id, path, dev_server_name - FROM dev_server_projects - } - } - query! { fn ssh_projects() -> Result> { SELECT id, host, port, paths, user @@ -913,24 +765,9 @@ impl WorkspaceDb { ) -> Result> { let mut result = Vec::new(); let mut delete_tasks = Vec::new(); - let dev_server_projects = self.dev_server_projects()?; let ssh_projects = self.ssh_projects()?; - for (id, location, order, dev_server_project_id, ssh_project_id) in - self.recent_workspaces()? - { - if let Some(dev_server_project_id) = dev_server_project_id.map(DevServerProjectId) { - if let Some(dev_server_project) = dev_server_projects - .iter() - .find(|rp| rp.id == dev_server_project_id) - { - result.push((id, dev_server_project.clone().into())); - } else { - delete_tasks.push(self.delete_workspace_by_id(id)); - } - continue; - } - + for (id, location, order, ssh_project_id) in self.recent_workspaces()? { if let Some(ssh_project_id) = ssh_project_id.map(SshProjectId) { if let Some(ssh_project) = ssh_projects.iter().find(|rp| rp.id == ssh_project_id) { result.push((id, SerializedWorkspaceLocation::Ssh(ssh_project.clone()))); diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 7528e4c393..a2510b8bec 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -4,7 +4,6 @@ use crate::{ }; use anyhow::{Context, Result}; use async_recursion::async_recursion; -use client::DevServerProjectId; use db::sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, @@ -17,7 +16,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use ui::SharedString; use util::ResultExt; use uuid::Uuid; @@ -92,13 +90,6 @@ impl Column for SerializedSshProject { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct SerializedDevServerProject { - pub id: DevServerProjectId, - pub dev_server_name: String, - pub paths: Vec, -} - #[derive(Debug, PartialEq, Clone)] pub struct LocalPaths(Arc>); @@ -176,49 +167,10 @@ impl Column for LocalPathsOrder { } } -impl From for SerializedWorkspaceLocation { - fn from(dev_server_project: SerializedDevServerProject) -> Self { - Self::DevServer(dev_server_project) - } -} - -impl StaticColumnCount for SerializedDevServerProject {} -impl Bind for &SerializedDevServerProject { - fn bind(&self, statement: &Statement, start_index: i32) -> Result { - let next_index = statement.bind(&self.id.0, start_index)?; - let next_index = statement.bind(&self.dev_server_name, next_index)?; - let paths = serde_json::to_string(&self.paths)?; - statement.bind(&paths, next_index) - } -} - -impl Column for SerializedDevServerProject { - fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> { - let id = statement.column_int64(start_index)?; - let dev_server_name = statement.column_text(start_index + 1)?.to_string(); - let paths = statement.column_text(start_index + 2)?.to_string(); - let paths: Vec = if paths.starts_with('[') { - serde_json::from_str(&paths).context("JSON deserialization of paths failed")? - } else { - vec![paths.into()] - }; - - Ok(( - Self { - id: DevServerProjectId(id as u64), - dev_server_name, - paths, - }, - start_index + 3, - )) - } -} - #[derive(Debug, PartialEq, Clone)] pub enum SerializedWorkspaceLocation { Local(LocalPaths, LocalPathsOrder), Ssh(SerializedSshProject), - DevServer(SerializedDevServerProject), } impl SerializedWorkspaceLocation { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index adc3e68741..6338c6fcbd 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -16,7 +16,7 @@ use anyhow::{anyhow, Context as _, Result}; use call::{call_settings::CallSettings, ActiveCall}; use client::{ proto::{self, ErrorCode, PanelId, PeerId}, - ChannelId, Client, DevServerProjectId, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore, + ChannelId, Client, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore, }; use collections::{hash_map, HashMap, HashSet}; use derive_more::{Deref, DerefMut}; @@ -52,7 +52,7 @@ use notifications::{ pub use pane::*; pub use pane_group::*; pub use persistence::{ - model::{ItemId, LocalPaths, SerializedDevServerProject, SerializedWorkspaceLocation}, + model::{ItemId, LocalPaths, SerializedWorkspaceLocation}, WorkspaceDb, DB as WORKSPACE_DB, }; use persistence::{ @@ -97,7 +97,7 @@ use ui::{ IntoElement, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext, VisualContext as _, WindowContext, }; -use util::{maybe, ResultExt, TryFutureExt}; +use util::{ResultExt, TryFutureExt}; use uuid::Uuid; pub use workspace_settings::{ AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings, @@ -2057,7 +2057,7 @@ impl Workspace { fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext) { let project = self.project.read(cx); - if project.is_via_collab() && project.dev_server_project_id().is_none() { + if project.is_via_collab() { self.show_error( &anyhow!("You cannot add folders to someone else's project"), cx, @@ -4133,20 +4133,6 @@ impl Workspace { } else { None } - } else if let Some(dev_server_project_id) = self.project().read(cx).dev_server_project_id() - { - let store = dev_server_projects::Store::global(cx).read(cx); - maybe!({ - let project = store.dev_server_project(dev_server_project_id)?; - let dev_server = store.dev_server(project.dev_server_id)?; - - let dev_server_project = SerializedDevServerProject { - id: dev_server_project_id, - dev_server_name: dev_server.name.to_string(), - paths: project.paths.to_vec(), - }; - Some(SerializedWorkspaceLocation::DevServer(dev_server_project)) - }) } else { None }; @@ -5180,13 +5166,12 @@ async fn join_channel_internal( if let Some(workspace) = requesting_window { let project = workspace.update(cx, |workspace, cx| { let project = workspace.project.read(cx); - let is_dev_server = project.dev_server_project_id().is_some(); - if !is_dev_server && !CallSettings::get_global(cx).share_on_join { + if !CallSettings::get_global(cx).share_on_join { return None; } - if (project.is_local() || project.is_via_ssh() || is_dev_server) + if (project.is_local() || project.is_via_ssh()) && project.visible_worktrees(cx).any(|tree| { tree.read(cx) .root_entry() @@ -5685,84 +5670,6 @@ fn serialize_ssh_project( }) } -pub fn join_dev_server_project( - dev_server_project_id: DevServerProjectId, - project_id: ProjectId, - app_state: Arc, - window_to_replace: Option>, - cx: &mut AppContext, -) -> Task>> { - let windows = cx.windows(); - cx.spawn(|mut cx| async move { - let existing_workspace = windows.into_iter().find_map(|window| { - window.downcast::().and_then(|window| { - window - .update(&mut cx, |workspace, cx| { - if workspace.project().read(cx).remote_id() == Some(project_id.0) { - Some(window) - } else { - None - } - }) - .unwrap_or(None) - }) - }); - - let serialized_workspace: Option = - persistence::DB.workspace_for_dev_server_project(dev_server_project_id); - - let workspace = if let Some(existing_workspace) = existing_workspace { - existing_workspace - } else { - let project = Project::remote( - project_id.0, - app_state.client.clone(), - app_state.user_store.clone(), - app_state.languages.clone(), - app_state.fs.clone(), - cx.clone(), - ) - .await?; - - let workspace_id = if let Some(ref serialized_workspace) = serialized_workspace { - serialized_workspace.id - } else { - persistence::DB.next_id().await? - }; - - if let Some(window_to_replace) = window_to_replace { - cx.update_window(window_to_replace.into(), |_, cx| { - cx.replace_root_view(|cx| { - Workspace::new(Some(workspace_id), project, app_state.clone(), cx) - }); - })?; - window_to_replace - } else { - let window_bounds_override = window_bounds_env_override(); - cx.update(|cx| { - let mut options = (app_state.build_window_options)(None, cx); - options.window_bounds = window_bounds_override.map(WindowBounds::Windowed); - cx.open_window(options, |cx| { - cx.new_view(|cx| { - Workspace::new(Some(workspace_id), project, app_state.clone(), cx) - }) - }) - })?? - } - }; - - workspace - .update(&mut cx, |_, cx| { - cx.activate(true); - cx.activate_window(); - open_items(serialized_workspace, vec![], app_state, cx) - })? - .await?; - - anyhow::Ok(workspace) - }) -} - pub fn join_in_room_project( project_id: u64, follow_user_id: u64, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d80db98393..58728d504b 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -36,7 +36,6 @@ command_palette.workspace = true command_palette_hooks.workspace = true copilot.workspace = true db.workspace = true -dev_server_projects.workspace = true diagnostics.workspace = true editor.workspace = true env_logger.workspace = true @@ -52,7 +51,6 @@ git.workspace = true git_hosting_providers.workspace = true go_to_line.workspace = true gpui = { workspace = true, features = ["wayland", "x11", "font-kit"] } -headless.workspace = true http_client.workspace = true image_viewer.workspace = true inline_completion_button.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 5608d84776..01c9d86c60 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -11,7 +11,7 @@ use assistant::PromptBuilder; use chrono::Offset; use clap::{command, Parser}; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; -use client::{parse_zed_link, Client, DevServerToken, ProxySettings, UserStore}; +use client::{parse_zed_link, Client, ProxySettings, UserStore}; use collab_ui::channel_view::ChannelView; use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE}; use editor::Editor; @@ -20,8 +20,8 @@ use fs::{Fs, RealFs}; use futures::{future, StreamExt}; use git::GitHostingProviderRegistry; use gpui::{ - Action, App, AppContext, AsyncAppContext, Context, DismissEvent, Global, Task, - UpdateGlobal as _, VisualContext, + Action, App, AppContext, AsyncAppContext, Context, DismissEvent, UpdateGlobal as _, + VisualContext, }; use http_client::{read_proxy_from_env, Uri}; use language::LanguageRegistry; @@ -136,43 +136,6 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut AppContext) { } } -enum AppMode { - Headless(DevServerToken), - Ui, -} -impl Global for AppMode {} - -fn init_headless( - dev_server_token: DevServerToken, - app_state: Arc, - cx: &mut AppContext, -) -> Task> { - match cx.try_global::() { - Some(AppMode::Headless(token)) if token == &dev_server_token => return Task::ready(Ok(())), - Some(_) => { - return Task::ready(Err(anyhow!( - "zed is already running. Use `kill {}` to stop it", - process::id() - ))) - } - None => { - cx.set_global(AppMode::Headless(dev_server_token.clone())); - } - }; - let client = app_state.client.clone(); - client.set_dev_server_token(dev_server_token); - headless::init( - client.clone(), - headless::AppState { - languages: app_state.languages.clone(), - user_store: app_state.user_store.clone(), - fs: app_state.fs.clone(), - node_runtime: app_state.node_runtime.clone(), - }, - cx, - ) -} - // init_common is called for both headless and normal mode. fn init_common(app_state: Arc, cx: &mut AppContext) -> Arc { SystemAppearance::init(cx); @@ -223,19 +186,6 @@ fn init_ui( prompt_builder: Arc, cx: &mut AppContext, ) -> Result<()> { - match cx.try_global::() { - Some(AppMode::Headless(_)) => { - return Err(anyhow!( - "zed is already running in headless mode. Use `kill {}` to stop it", - process::id() - )) - } - Some(AppMode::Ui) => return Ok(()), - None => { - cx.set_global(AppMode::Ui); - } - }; - load_embedded_fonts(cx); #[cfg(target_os = "linux")] @@ -252,7 +202,6 @@ fn init_ui( go_to_line::init(cx); file_finder::init(cx); tab_switcher::init(cx); - dev_server_projects::init(app_state.client.clone(), cx); outline::init(cx); project_symbols::init(cx); project_panel::init(Assets, cx); @@ -426,22 +375,15 @@ fn main() { app.on_reopen(move |cx| { if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade()) { - let ui_has_launched = cx - .try_global::() - .map(|mode| matches!(mode, AppMode::Ui)) - .unwrap_or(false); - - if ui_has_launched { - cx.spawn({ - let app_state = app_state.clone(); - |mut cx| async move { - if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await { - fail_to_open_window_async(e, &mut cx) - } + cx.spawn({ + let app_state = app_state.clone(); + |mut cx| async move { + if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await { + fail_to_open_window_async(e, &mut cx) } - }) - .detach(); - } + } + }) + .detach(); } }); @@ -590,30 +532,16 @@ fn main() { handle_open_request(request, app_state.clone(), prompt_builder.clone(), cx); } None => { - if let Some(dev_server_token) = args.dev_server_token { - let task = - init_headless(DevServerToken(dev_server_token), app_state.clone(), cx); - cx.spawn(|cx| async move { - if let Err(e) = task.await { - log::error!("{}", e); - cx.update(|cx| cx.quit()).log_err(); - } else { - log::info!("connected!"); + init_ui(app_state.clone(), prompt_builder.clone(), cx).unwrap(); + cx.spawn({ + let app_state = app_state.clone(); + |mut cx| async move { + if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await { + fail_to_open_window_async(e, &mut cx) } - }) - .detach(); - } else { - init_ui(app_state.clone(), prompt_builder.clone(), cx).unwrap(); - cx.spawn({ - let app_state = app_state.clone(); - |mut cx| async move { - if let Err(e) = restore_or_create_workspace(app_state, &mut cx).await { - fail_to_open_window_async(e, &mut cx) - } - } - }) - .detach(); - } + } + }) + .detach(); } } @@ -927,7 +855,6 @@ async fn restore_or_create_workspace( }) .detach(); } - SerializedWorkspaceLocation::DevServer(_) => {} } } } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { diff --git a/crates/zed/src/zed/open_listener.rs b/crates/zed/src/zed/open_listener.rs index bb217d6b16..32bfdd42ae 100644 --- a/crates/zed/src/zed/open_listener.rs +++ b/crates/zed/src/zed/open_listener.rs @@ -1,5 +1,5 @@ use crate::restorable_workspace_locations; -use crate::{handle_open_request, init_headless, init_ui}; +use crate::{handle_open_request, init_ui}; use anyhow::{anyhow, Context, Result}; use assistant::PromptBuilder; use cli::{ipc, IpcHandshake}; @@ -21,8 +21,8 @@ use remote::SshConnectionOptions; use settings::Settings; use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::thread; use std::time::Duration; -use std::{process, thread}; use util::paths::PathWithPosition; use util::ResultExt; use welcome::{show_welcome_view, FIRST_OPEN}; @@ -262,38 +262,9 @@ pub async fn handle_cli_connection( paths, wait, open_new_workspace, - dev_server_token, + env, } => { - if let Some(dev_server_token) = dev_server_token { - match cx - .update(|cx| { - init_headless(client::DevServerToken(dev_server_token), app_state, cx) - }) - .unwrap() - .await - { - Ok(_) => { - responses - .send(CliResponse::Stdout { - message: format!("zed (pid {}) connected!", process::id()), - }) - .log_err(); - responses.send(CliResponse::Exit { status: 0 }).log_err(); - } - Err(error) => { - responses - .send(CliResponse::Stderr { - message: format!("{error}"), - }) - .log_err(); - responses.send(CliResponse::Exit { status: 1 }).log_err(); - cx.update(|cx| cx.quit()).log_err(); - } - } - return; - } - if !urls.is_empty() { cx.update(|cx| { match OpenRequest::parse(urls, cx) { @@ -459,7 +430,6 @@ async fn open_workspaces( // We don't set `errored` here, because for ssh projects, the // error is displayed in the window. } - SerializedWorkspaceLocation::DevServer(_) => {} } }