mod fiddle_messenger;
use fiddle_messenger::*;
mod http_method;
pub use http_method::HttpMethod;
mod request_payload;
pub use request_payload::RequestPayload;
mod upload;
pub use upload::upload;
use std::sync::mpsc::channel;
use std::thread;
use anyhow::Result;
use log::info;
use url::Url;
use ws::{Sender, WebSocket};
use crate::build::build_target;
use crate::http;
use crate::settings::global_user::GlobalUser;
use crate::settings::toml::{Target, UploadFormat};
use crate::terminal::message::{Message, StdOut};
use crate::terminal::open_browser;
use crate::terminal::styles;
use crate::watch::watch_and_build;
pub fn preview(
mut target: Target,
user: Option<GlobalUser>,
options: PreviewOpt,
verbose: bool,
) -> Result<()> {
if let Some(build) = &target.build {
if matches!(build.upload, UploadFormat::Modules { .. }) {
anyhow::bail!("wrangler preview does not support previewing modules scripts. Please use wrangler dev instead.");
}
}
if target.durable_objects.is_some() {
anyhow::bail!("wrangler preview is not yet supported for scripts that use Durable Objects. Please use wrangler dev instead.");
}
build_target(&target)?;
let sites_preview: bool = target.site.is_some();
if user.is_none() {
let wrangler_config_msg = styles::highlight("`wrangler config`");
let wrangler_login_msg = styles::highlight("`wrangler login`");
let docs_url_msg = styles::url("https://developers.cloudflare.com/workers/tooling/wrangler/configuration/#using-environment-variables");
StdOut::billboard(
&format!("You have not provided your Cloudflare credentials.\n\nPlease run {}, {}, or visit\n{}\nfor info on authenticating with environment variables.", wrangler_login_msg, wrangler_config_msg, docs_url_msg)
);
}
let script_id = upload(&mut target, user.as_ref(), sites_preview, verbose)?;
let request_payload = RequestPayload::create(options.method, options.url, options.body);
let session = &request_payload.session;
let browser_url = &request_payload.browser_url;
if options.livereload {
let server = WebSocket::new(|out| FiddleMessageServer { out })?.bind("127.0.0.1:0")?;
let ws_port = server.local_addr()?.port();
info!("Opened websocket server on port {}", ws_port);
if !options.headless {
open_browser(&format!(
"https://cloudflareworkers.com/?wrangler_session_id={0}&wrangler_ws_port={1}&hide_editor#{2}:{3}",
session, ws_port, script_id, browser_url
))?;
}
client_request(&request_payload, &script_id, sites_preview);
let broadcaster = server.broadcaster();
thread::spawn(move || server.run());
watch_for_changes(
target,
user.as_ref(),
broadcaster,
verbose,
options.headless,
request_payload,
)?;
} else {
if !options.headless {
open_browser(&format!(
"https://cloudflareworkers.com/?hide_editor#{0}:{1}",
script_id, browser_url
))?;
}
client_request(&request_payload, &script_id, sites_preview);
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct PreviewOpt {
pub method: HttpMethod,
pub url: Url,
pub body: Option<String>,
pub livereload: bool,
pub headless: bool,
}
fn client_request(payload: &RequestPayload, script_id: &str, sites_preview: bool) {
let client = http::client();
let method = &payload.method;
let url = &payload.service_url;
let body = &payload.body;
let cookie = payload.cookie(script_id);
let worker_res = match method {
HttpMethod::Get => get(url, &cookie, &client).unwrap(),
HttpMethod::Post => post(url, &cookie, body, &client).unwrap(),
};
let msg = if sites_preview {
"Your Worker is a Workers Site, please preview it in browser window.".to_string()
} else {
format!("Your Worker responded with: {}", worker_res)
};
StdOut::preview(&msg);
}
fn get(url: &str, cookie: &str, client: &reqwest::blocking::Client) -> Result<String> {
let res = client.get(url).header("Cookie", cookie).send();
Ok(res?.text()?)
}
fn post(
url: &str,
cookie: &str,
body: &Option<String>,
client: &reqwest::blocking::Client,
) -> Result<String> {
let res = match body {
Some(s) => client
.post(url)
.header("Cookie", cookie)
.body(s.to_string())
.send(),
None => client.post(url).header("Cookie", cookie).send(),
};
let msg = format!("POST {}", url);
StdOut::preview(&msg);
Ok(res?.text()?)
}
fn watch_for_changes(
mut target: Target,
user: Option<&GlobalUser>,
broadcaster: Sender,
verbose: bool,
headless: bool,
request_payload: RequestPayload,
) -> Result<()> {
let sites_preview: bool = target.site.is_some();
let (tx, rx) = channel();
watch_and_build(&target, Some(tx), None)?;
while rx.recv().is_ok() {
if let Ok(new_id) = upload(&mut target, user, sites_preview, verbose) {
let script_id = new_id.to_string();
let msg = FiddleMessage {
session_id: request_payload.session.clone(),
data: FiddleMessageData::LiveReload {
new_id: new_id.clone(),
},
};
if !headless {
match broadcaster.send(serde_json::to_string(&msg)?) {
Ok(_) => {
StdOut::preview("Updated preview with changes");
}
Err(_e) => StdOut::user_error("communication with preview failed"),
}
}
client_request(&request_payload, &script_id, sites_preview);
}
}
broadcaster.shutdown()?;
Ok(())
}