Expose extensions API from api.zed.dev (#8307)

This avoids the need to pay for bandwidth

Co-Authored-By: Marshall <marshall@zed.dev>



Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Conrad Irwin 2024-02-23 14:08:14 -07:00 committed by GitHub
parent 41949d7b6c
commit 351e6a5de2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 22 deletions

View file

@ -1,5 +1,5 @@
pub mod events; pub mod events;
mod extensions; pub mod extensions;
use crate::{ use crate::{
auth, auth,
@ -33,7 +33,6 @@ pub fn routes(rpc_server: Option<Arc<rpc::Server>>, state: Arc<AppState>) -> Rou
.route("/rpc_server_snapshot", get(get_rpc_server_snapshot)) .route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
.route("/contributors", get(get_contributors).post(add_contributor)) .route("/contributors", get(get_contributors).post(add_contributor))
.route("/contributor", get(check_is_contributor)) .route("/contributor", get(check_is_contributor))
.merge(extensions::router())
.layer( .layer(
ServiceBuilder::new() ServiceBuilder::new()
.layer(Extension(state)) .layer(Extension(state))

View file

@ -40,7 +40,16 @@ async fn main() -> Result<()> {
run_migrations().await?; run_migrations().await?;
} }
Some("serve") => { Some("serve") => {
let is_api_only = args.next().is_some_and(|arg| arg == "api"); let (is_api, is_collab) = if let Some(next) = args.next() {
(next == "api", next == "collab")
} else {
(true, true)
};
if !is_api && !is_collab {
Err(anyhow!(
"usage: collab <version | migrate | serve [api|collab]>"
))?;
}
let config = envy::from_env::<Config>().expect("error loading config"); let config = envy::from_env::<Config>().expect("error loading config");
init_tracing(&config); init_tracing(&config);
@ -52,7 +61,7 @@ async fn main() -> Result<()> {
let listener = TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port)) let listener = TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port))
.expect("failed to bind TCP listener"); .expect("failed to bind TCP listener");
let rpc_server = if !is_api_only { let rpc_server = if is_collab {
let epoch = state let epoch = state
.db .db
.create_server(&state.config.zed_environment) .create_server(&state.config.zed_environment)
@ -66,8 +75,7 @@ async fn main() -> Result<()> {
None None
}; };
// TODO: Once we move the extensions endpoints to run inside `api` service, move the background task as well. if is_api {
if !is_api_only {
fetch_extensions_from_blob_store_periodically(state.clone(), Executor::Production); fetch_extensions_from_blob_store_periodically(state.clone(), Executor::Production);
} }
@ -80,6 +88,7 @@ async fn main() -> Result<()> {
Router::new() Router::new()
.route("/", get(handle_root)) .route("/", get(handle_root))
.route("/healthz", get(handle_liveness_probe)) .route("/healthz", get(handle_liveness_probe))
.merge(collab::api::extensions::router())
.merge(collab::api::events::router()) .merge(collab::api::events::router())
.layer(Extension(state.clone())), .layer(Extension(state.clone())),
) )
@ -120,7 +129,9 @@ async fn main() -> Result<()> {
.await?; .await?;
} }
_ => { _ => {
Err(anyhow!("usage: collab <version | migrate | serve>"))?; Err(anyhow!(
"usage: collab <version | migrate | serve [api|collab]>"
))?;
} }
} }
Ok(()) Ok(())

View file

@ -1,7 +1,6 @@
use anyhow::{anyhow, bail, Context as _, Result}; use anyhow::{anyhow, bail, Context as _, Result};
use async_compression::futures::bufread::GzipDecoder; use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive; use async_tar::Archive;
use client::ClientSettings;
use collections::{BTreeMap, HashSet}; use collections::{BTreeMap, HashSet};
use fs::{Fs, RemoveOptions}; use fs::{Fs, RemoveOptions};
use futures::channel::mpsc::unbounded; use futures::channel::mpsc::unbounded;
@ -13,7 +12,6 @@ use language::{
}; };
use parking_lot::RwLock; use parking_lot::RwLock;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings as _;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
@ -22,7 +20,7 @@ use std::{
time::Duration, time::Duration,
}; };
use theme::{ThemeRegistry, ThemeSettings}; use theme::{ThemeRegistry, ThemeSettings};
use util::http::AsyncBody; use util::http::{AsyncBody, ZedHttpClient};
use util::TryFutureExt; use util::TryFutureExt;
use util::{http::HttpClient, paths::EXTENSIONS_DIR, ResultExt}; use util::{http::HttpClient, paths::EXTENSIONS_DIR, ResultExt};
@ -57,7 +55,7 @@ pub enum ExtensionStatus {
pub struct ExtensionStore { pub struct ExtensionStore {
manifest: Arc<RwLock<Manifest>>, manifest: Arc<RwLock<Manifest>>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
http_client: Arc<dyn HttpClient>, http_client: Arc<ZedHttpClient>,
extensions_dir: PathBuf, extensions_dir: PathBuf,
extensions_being_installed: HashSet<Arc<str>>, extensions_being_installed: HashSet<Arc<str>>,
extensions_being_uninstalled: HashSet<Arc<str>>, extensions_being_uninstalled: HashSet<Arc<str>>,
@ -113,7 +111,7 @@ actions!(zed, [ReloadExtensions]);
pub fn init( pub fn init(
fs: Arc<fs::RealFs>, fs: Arc<fs::RealFs>,
http_client: Arc<dyn HttpClient>, http_client: Arc<ZedHttpClient>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
theme_registry: Arc<ThemeRegistry>, theme_registry: Arc<ThemeRegistry>,
cx: &mut AppContext, cx: &mut AppContext,
@ -145,7 +143,7 @@ impl ExtensionStore {
pub fn new( pub fn new(
extensions_dir: PathBuf, extensions_dir: PathBuf,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
http_client: Arc<dyn HttpClient>, http_client: Arc<ZedHttpClient>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
theme_registry: Arc<ThemeRegistry>, theme_registry: Arc<ThemeRegistry>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
@ -224,14 +222,12 @@ impl ExtensionStore {
search: Option<&str>, search: Option<&str>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<Extension>>> { ) -> Task<Result<Vec<Extension>>> {
let url = format!( let url = self.http_client.zed_api_url(&format!(
"{}/{}{query}", "/extensions{query}",
ClientSettings::get_global(cx).server_url,
"api/extensions",
query = search query = search
.map(|search| format!("?filter={search}")) .map(|search| format!("?filter={search}"))
.unwrap_or_default() .unwrap_or_default()
); ));
let http_client = self.http_client.clone(); let http_client = self.http_client.clone();
cx.spawn(move |_, _| async move { cx.spawn(move |_, _| async move {
let mut response = http_client.get(&url, AsyncBody::empty(), true).await?; let mut response = http_client.get(&url, AsyncBody::empty(), true).await?;
@ -264,10 +260,9 @@ impl ExtensionStore {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
log::info!("installing extension {extension_id} {version}"); log::info!("installing extension {extension_id} {version}");
let url = format!( let url = self
"{}/api/extensions/{extension_id}/{version}/download", .http_client
ClientSettings::get_global(cx).server_url .zed_api_url(&format!("/extensions/{extension_id}/{version}/download"));
);
let extensions_dir = self.extensions_dir(); let extensions_dir = self.extensions_dir();
let http_client = self.http_client.clone(); let http_client = self.http_client.clone();