Remove updated_by, remember last location

This commit is contained in:
Gregory Schier
2023-03-30 08:11:51 -07:00
parent 904d20b9b8
commit 7fcf709efe
13 changed files with 286 additions and 346 deletions

View File

@@ -1,7 +0,0 @@
ALTER TABLE http_requests ADD COLUMN updated_by TEXT NOT NULL DEFAULT '';
ALTER TABLE http_responses ADD COLUMN updated_by TEXT NOT NULL DEFAULT '';
ALTER TABLE workspaces ADD COLUMN updated_by TEXT NOT NULL DEFAULT '';
ALTER TABLE key_values ADD COLUMN updated_by TEXT NOT NULL DEFAULT '';
ALTER TABLE http_requests ADD COLUMN authentication TEXT NOT NULL DEFAULT '{}';
ALTER TABLE http_requests ADD COLUMN authentication_type TEXT;

View File

@@ -0,0 +1,2 @@
ALTER TABLE http_requests ADD COLUMN authentication TEXT NOT NULL DEFAULT '{}';
ALTER TABLE http_requests ADD COLUMN authentication_type TEXT;

View File

@@ -1,5 +1,53 @@
{
"db": "SQLite",
"06aaf8f4a17566f1d25da2a60f0baf4b5fc28c3cf0c001a84e25edf9eab3c7e3": {
"describe": {
"columns": [
{
"name": "model",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 1,
"type_info": "Datetime"
},
{
"name": "updated_at",
"ordinal": 2,
"type_info": "Datetime"
},
{
"name": "namespace",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "key",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "value",
"ordinal": 5,
"type_info": "Text"
}
],
"nullable": [
false,
false,
false,
false,
false,
false
],
"parameters": {
"Right": 2
}
},
"query": "\n SELECT model, created_at, updated_at, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n "
},
"07d1a1c7b4f3d9625a766e60fd57bb779b71dae30e5bbce34885a911a5a42428": {
"describe": {
"columns": [],
@@ -20,69 +68,15 @@
},
"query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n "
},
"19e0076c3cd13b73a46619b5c0ee5bf304fe27245db3d19a648f625bf5231cb0": {
"318ed5a1126fe00719393cf4e6c788ee5a265af88b7253f61a475f78c6774ef6": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 4
"Right": 9
}
},
"query": "\n INSERT INTO workspaces (id, updated_by, name, description)\n VALUES (?, ?, ?, ?)\n "
},
"2f93d7bd211af59c7cc15765a746216632fbe4f02301acf8382f1cf3f8d24c8d": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "model",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 2,
"type_info": "Datetime"
},
{
"name": "updated_at",
"ordinal": 3,
"type_info": "Datetime"
},
{
"name": "updated_by",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "description",
"ordinal": 6,
"type_info": "Text"
}
],
"nullable": [
false,
false,
false,
false,
false,
false,
false
],
"parameters": {
"Right": 1
}
},
"query": "\n SELECT id, model, created_at, updated_at, updated_by, name, description\n FROM workspaces WHERE id = ?\n "
"query": "\n INSERT INTO http_responses (\n id,\n request_id,\n workspace_id,\n elapsed,\n url,\n status,\n status_reason,\n body,\n headers\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\n "
},
"448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": {
"describe": {
@@ -94,7 +88,7 @@
},
"query": "\n DELETE FROM http_requests\n WHERE id = ?\n "
},
"51ee4652a889417dce585e4da457a629dd9e064b8866c3a916bc3bd2c933e14f": {
"6f0cb5a6d1e8dbc8cdfcc3c7e7944b2c83c22cb795b9d6b98fe067dabec9680b": {
"describe": {
"columns": [
{
@@ -113,9 +107,9 @@
"type_info": "Text"
},
{
"name": "request_id",
"name": "created_at",
"ordinal": 3,
"type_info": "Text"
"type_info": "Datetime"
},
{
"name": "updated_at",
@@ -123,47 +117,47 @@
"type_info": "Datetime"
},
{
"name": "updated_by",
"name": "name",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "created_at",
"name": "url",
"ordinal": 6,
"type_info": "Datetime"
"type_info": "Text"
},
{
"name": "status",
"name": "method",
"ordinal": 7,
"type_info": "Int64"
},
{
"name": "status_reason",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "body",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "body_type",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "elapsed",
"name": "authentication!: Json<HashMap<String, JsonValue>>",
"ordinal": 10,
"type_info": "Int64"
"type_info": "Text"
},
{
"name": "url",
"name": "authentication_type",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "error",
"name": "sort_priority",
"ordinal": 12,
"type_info": "Text"
"type_info": "Float"
},
{
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 13,
"type_info": "Text"
}
@@ -178,17 +172,17 @@
false,
false,
true,
false,
false,
true,
false,
true,
false,
false
],
"parameters": {
"Right": 1
}
},
"query": "\n SELECT id, model, workspace_id, request_id, updated_at, updated_by,\n created_at, status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpResponseHeader>>\"\n FROM http_responses\n WHERE request_id = ?\n ORDER BY created_at ASC\n "
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
},
"84be2b954870ab181738656ecd4d03fca2ff21012947014c79626abfce8e999b": {
"describe": {
@@ -200,61 +194,27 @@
},
"query": "\n DELETE FROM workspaces\n WHERE id = ?\n "
},
"8d71216aa3902af45acc36bb4671e36bea6da2d30b42cfe8b70cff00cd00f256": {
"a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": {
"describe": {
"columns": [
{
"name": "model",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 1,
"type_info": "Datetime"
},
{
"name": "updated_at",
"ordinal": 2,
"type_info": "Datetime"
},
{
"name": "updated_by",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "namespace",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "key",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "value",
"ordinal": 6,
"type_info": "Text"
}
],
"nullable": [
false,
false,
false,
false,
false,
false,
false
],
"columns": [],
"nullable": [],
"parameters": {
"Right": 2
"Right": 8
}
},
"query": "\n SELECT model, created_at, updated_at, updated_by, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n "
"query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n "
},
"9f09f300e04d9b77d408bea52069b7c812dcad163d144df4b7b02a9ba7969345": {
"b19c275180909a39342b13c3cdcf993781636913ae590967f5508c46a56dc961": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 11
}
},
"query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n authentication,\n authentication_type,\n headers,\n sort_priority\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n body_type = excluded.body_type,\n authentication = excluded.authentication,\n authentication_type = excluded.authentication_type,\n url = excluded.url,\n sort_priority = excluded.sort_priority\n "
},
"caf3f21bf291dfbd36446592066e96c1f83abe96f6ea9211a3e049eb9c58a8c8": {
"describe": {
"columns": [
{
@@ -278,23 +238,65 @@
"type_info": "Datetime"
},
{
"name": "updated_by",
"name": "name",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "description",
"ordinal": 6,
"ordinal": 5,
"type_info": "Text"
}
],
"nullable": [
false,
false,
false,
false,
false,
false
],
"parameters": {
"Right": 1
}
},
"query": "\n SELECT id, model, created_at, updated_at, name, description\n FROM workspaces WHERE id = ?\n "
},
"cea4cae52f16ec78aca9a47b17117422d4f165e5a3b308c70fd1a180382475ea": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "model",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 2,
"type_info": "Datetime"
},
{
"name": "updated_at",
"ordinal": 3,
"type_info": "Datetime"
},
{
"name": "name",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "description",
"ordinal": 5,
"type_info": "Text"
}
],
"nullable": [
false,
false,
false,
@@ -306,19 +308,9 @@
"Right": 0
}
},
"query": "\n SELECT id, model, created_at, updated_at, updated_by, name, description\n FROM workspaces\n "
"query": "\n SELECT id, model, created_at, updated_at, name, description\n FROM workspaces\n "
},
"afe2d3a2b2198582d2a4360a0947785d435528eb77f67c420e830a997b5ad101": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 9
}
},
"query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_by, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n "
},
"bc1b3220c104567176ff741b5648a14054485603ebb04222a7b9f60fc54f0970": {
"ced098adb79c0ee64e223b6e02371ef253920a2c342275de0fa9c181529a4adc": {
"describe": {
"columns": [
{
@@ -386,14 +378,9 @@
"ordinal": 12,
"type_info": "Float"
},
{
"name": "updated_by",
"ordinal": 13,
"type_info": "Text"
},
{
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 14,
"ordinal": 13,
"type_info": "Text"
}
],
@@ -408,9 +395,8 @@
false,
true,
true,
true,
true,
false,
true,
false,
false
],
@@ -418,29 +404,9 @@
"Right": 1
}
},
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n "
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n "
},
"ce62a799babc731dc53e43144751006c3905367c47c511b8abee832c58f8111d": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 12
}
},
"query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n authentication,\n authentication_type,\n headers,\n sort_priority,\n updated_by\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n body_type = excluded.body_type,\n authentication = excluded.authentication,\n authentication_type = excluded.authentication_type,\n url = excluded.url,\n sort_priority = excluded.sort_priority,\n updated_by = excluded.updated_by\n "
},
"d56b00aeaca0edd9a9fea4c7fdce0229a6d6500c8294854974dd4fc30af8bda8": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 10
}
},
"query": "\n INSERT INTO http_responses (\n id,\n request_id,\n workspace_id,\n elapsed,\n url,\n status,\n status_reason,\n body,\n headers,\n updated_by\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);\n "
},
"d68f12980ff00de36f02a82a626f99e86abf5a26ebdf74c95832d3be396206da": {
"d5ad6d5f82fe837fa9215bd4619ec18a7c95b3088d4fbf9825f2d1d28069d1ce": {
"describe": {
"columns": [
{
@@ -468,49 +434,44 @@
"ordinal": 4,
"type_info": "Datetime"
},
{
"name": "updated_by",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "created_at",
"ordinal": 6,
"ordinal": 5,
"type_info": "Datetime"
},
{
"name": "status",
"ordinal": 7,
"ordinal": 6,
"type_info": "Int64"
},
{
"name": "status_reason",
"ordinal": 8,
"ordinal": 7,
"type_info": "Text"
},
{
"name": "body",
"ordinal": 9,
"ordinal": 8,
"type_info": "Text"
},
{
"name": "elapsed",
"ordinal": 10,
"ordinal": 9,
"type_info": "Int64"
},
{
"name": "url",
"ordinal": 11,
"ordinal": 10,
"type_info": "Text"
},
{
"name": "error",
"ordinal": 12,
"ordinal": 11,
"type_info": "Text"
},
{
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
"ordinal": 13,
"ordinal": 12,
"type_info": "Text"
}
],
@@ -522,7 +483,6 @@
false,
false,
false,
false,
true,
false,
false,
@@ -534,7 +494,7 @@
"Right": 1
}
},
"query": "\n SELECT id, model, workspace_id, request_id, updated_at, updated_by, created_at,\n status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpResponseHeader>>\"\n FROM http_responses\n WHERE id = ?\n "
"query": "\n SELECT id, model, workspace_id, request_id, updated_at,\n created_at, status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpResponseHeader>>\"\n FROM http_responses\n WHERE request_id = ?\n ORDER BY created_at ASC\n "
},
"d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": {
"describe": {
@@ -546,7 +506,7 @@
},
"query": "\n INSERT INTO key_values (namespace, key, value)\n VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n value = excluded.value\n "
},
"dc749b8ed41ac55776e4f9dae36a82b3b880eb781eaa6fb53382e6db10f5a741": {
"e3ade0a69348d512e47e964bded9d7d890b92fdc1e01c6c22fa5e91f943639f2": {
"describe": {
"columns": [
{
@@ -565,9 +525,9 @@
"type_info": "Text"
},
{
"name": "created_at",
"name": "request_id",
"ordinal": 3,
"type_info": "Datetime"
"type_info": "Text"
},
{
"name": "updated_at",
@@ -575,17 +535,17 @@
"type_info": "Datetime"
},
{
"name": "name",
"name": "created_at",
"ordinal": 5,
"type_info": "Text"
"type_info": "Datetime"
},
{
"name": "url",
"name": "status",
"ordinal": 6,
"type_info": "Text"
"type_info": "Int64"
},
{
"name": "method",
"name": "status_reason",
"ordinal": 7,
"type_info": "Text"
},
@@ -595,33 +555,23 @@
"type_info": "Text"
},
{
"name": "body_type",
"name": "elapsed",
"ordinal": 9,
"type_info": "Text"
"type_info": "Int64"
},
{
"name": "authentication!: Json<HashMap<String, JsonValue>>",
"name": "url",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "authentication_type",
"name": "error",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "sort_priority",
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
"ordinal": 12,
"type_info": "Float"
},
{
"name": "updated_by",
"ordinal": 13,
"type_info": "Text"
},
{
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 14,
"type_info": "Text"
}
],
@@ -633,19 +583,27 @@
false,
false,
false,
false,
true,
true,
true,
true,
false,
false,
false,
true,
false
],
"parameters": {
"Right": 1
}
},
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
"query": "\n SELECT id, model, workspace_id, request_id, updated_at, created_at,\n status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpResponseHeader>>\"\n FROM http_responses\n WHERE id = ?\n "
},
"f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 3
}
},
"query": "\n INSERT INTO workspaces (id, name, description)\n VALUES (?, ?, ?)\n "
}
}

View File

@@ -22,7 +22,7 @@ use sqlx::sqlite::SqlitePoolOptions;
use sqlx::types::Json;
use sqlx::{Pool, Sqlite};
use tauri::regex::Regex;
use tauri::{AppHandle, Menu, MenuItem, State, Submenu, TitleBarStyle, Window, Wry};
use tauri::{AppHandle, Menu, MenuItem, RunEvent, State, Submenu, TitleBarStyle, Window, Wry};
use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent};
use tokio::sync::Mutex;
@@ -213,7 +213,7 @@ async fn actually_send_ephemeral_request(
response.url = v.url().to_string();
response.body = v.text().await.expect("Failed to get body");
response.elapsed = start.elapsed().as_millis() as i64;
response = models::update_response_if_id(response, window.label(), pool)
response = models::update_response_if_id(response, pool)
.await
.expect("Failed to update response");
emit_all_others(&window, "updated_response", &response);
@@ -235,10 +235,9 @@ async fn send_request(
.await
.expect("Failed to get request");
let response =
models::create_response(&req.id, 0, "", 0, None, "", vec![], window.label(), pool)
.await
.expect("Failed to create response");
let response = models::create_response(&req.id, 0, "", 0, None, "", vec![], pool)
.await
.expect("Failed to create response");
emit_all_others(&window, "updated_response", &response);
actually_send_ephemeral_request(req, response, window, pool).await?;
@@ -252,7 +251,7 @@ async fn response_err(
pool: &Pool<Sqlite>,
) -> Result<models::HttpResponse, String> {
response.error = Some(error.clone());
response = models::update_response_if_id(response, window.label(), pool)
response = models::update_response_if_id(response, pool)
.await
.expect("Failed to update response");
emit_all_others(&window, "updated_response", &response);
@@ -295,7 +294,7 @@ async fn create_workspace(
db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> {
let pool = &*db_instance.lock().await;
let created_workspace = models::create_workspace(name, "", window.label(), pool)
let created_workspace = models::create_workspace(name, "", pool)
.await
.expect("Failed to create workspace");
@@ -326,7 +325,6 @@ async fn create_request(
"",
headers,
sort_priority,
window.label(),
pool,
)
.await
@@ -344,7 +342,7 @@ async fn duplicate_request(
db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> {
let pool = &*db_instance.lock().await;
let request = models::duplicate_request(id, window.label(), pool)
let request = models::duplicate_request(id, pool)
.await
.expect("Failed to duplicate request");
emit_all_others(&window, "updated_request", &request);
@@ -382,7 +380,6 @@ async fn update_request(
request.url.as_str(),
request.headers.0,
request.sort_priority,
window.label(),
pool,
)
.await
@@ -468,21 +465,16 @@ async fn delete_all_responses(
#[tauri::command]
async fn workspaces(
db_instance: State<'_, Mutex<Pool<Sqlite>>>,
window: Window<Wry>,
) -> Result<Vec<models::Workspace>, String> {
let pool = &*db_instance.lock().await;
let workspaces = models::find_workspaces(pool)
.await
.expect("Failed to find workspaces");
if workspaces.is_empty() {
let workspace = models::create_workspace(
"My Project",
"This is the default workspace",
window.label(),
pool,
)
.await
.expect("Failed to create workspace");
let workspace =
models::create_workspace("My Project", "This is the default workspace", pool)
.await
.expect("Failed to create workspace");
Ok(vec![workspace])
} else {
Ok(workspaces)
@@ -516,7 +508,6 @@ fn main() {
tauri::Builder::default()
.system_tray(system_tray)
.setup(|app| {
let handle = app.handle();
let dir = match is_dev() {
true => current_dir().unwrap(),
false => app.path_resolver().app_data_dir().unwrap(),
@@ -533,13 +524,6 @@ fn main() {
.await
.expect("Failed to connect to database");
// Create the initial window
let app_id = get_or_create_client_id(&pool).await;
let win = create_window(handle, app_id);
if let Err(e) = win.show() {
println!("Failed to show window {}", e)
}
// Setup the DB handle
let m = Mutex::new(pool);
migrate_db(app.handle(), &m)
@@ -583,8 +567,17 @@ fn main() {
delete_response,
delete_all_responses,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
.build(tauri::generate_context!())
.expect("error while running tauri application")
.run(|app_handle, event| match event {
RunEvent::Ready => {
create_window(app_handle);
}
// ExitRequested { api, .. } => {
// }
_ => {}
});
}
fn is_dev() -> bool {
@@ -592,7 +585,7 @@ fn is_dev() -> bool {
env.unwrap_or("production") != "production"
}
fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
let default_menu = Menu::os_default("Yaak".to_string().as_str());
let mut test_menu = Menu::new()
.add_item(
@@ -630,9 +623,9 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
let submenu = Submenu::new("Test Menu", test_menu);
let window_num = handle.windows().len();
let window_id = format!("{}_{}", app_id, window_num);
let window_id = format!("wnd_{}", window_num);
let menu = default_menu.add_submenu(submenu);
let win = tauri::WindowBuilder::new(&handle, window_id, tauri::WindowUrl::App("".into()))
let win = tauri::WindowBuilder::new(handle, window_id, tauri::WindowUrl::App("".into()))
.menu(menu)
.fullscreen(false)
.resizable(true)
@@ -644,6 +637,7 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
.expect("failed to build window");
let win2 = win.clone();
let handle2 = handle.clone();
win.on_menu_event(move |event| match event.menu_item_id() {
"quit" => std::process::exit(0),
"close" => win2.close().unwrap(),
@@ -653,9 +647,7 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
"toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(),
"refresh" => win2.emit("refresh", true).unwrap(),
"send_request" => win2.emit("send_request", true).unwrap(),
"new_window" => {
create_window(handle.clone(), app_id.clone());
}
"new_window" => _ = create_window(&handle2),
"toggle_devtools" => {
if win2.is_devtools_open() {
win2.close_devtools();
@@ -676,6 +668,10 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
match e {
WindowEvent::Resized(..) => apply_offset(),
WindowEvent::ThemeChanged(..) => apply_offset(),
WindowEvent::CloseRequested { .. } => {
println!("CLOSE REQUESTED");
// api.prevent_close();
}
_ => {}
}
});
@@ -686,19 +682,6 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
win
}
async fn get_or_create_client_id(pool: &Pool<Sqlite>) -> String {
match models::get_key_value("global", "client_id", pool).await {
Some(kv) => kv.value,
None => {
let id = &models::generate_id("yaak");
models::set_key_value("global", "client_id", id, pool)
.await
.expect("Failed to set client id")
.value
}
}
}
/// Emit an event to all windows except the current one
fn emit_all_others<S: Serialize + Clone>(current_window: &Window<Wry>, event: &str, payload: S) {
let windows = current_window.app_handle().windows();

View File

@@ -1,9 +1,10 @@
use std::collections::HashMap;
use rand::distributions::{Alphanumeric, DistString};
use serde::{Deserialize, Serialize};
use sqlx::types::chrono::NaiveDateTime;
use sqlx::types::{Json, JsonValue};
use sqlx::{Pool, Sqlite};
use std::collections::HashMap;
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -12,7 +13,6 @@ pub struct Workspace {
pub model: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub updated_by: String,
pub name: String,
pub description: String,
}
@@ -33,7 +33,6 @@ pub struct HttpRequest {
pub model: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub updated_by: String,
pub sort_priority: f64,
pub workspace_id: String,
pub name: String,
@@ -62,7 +61,6 @@ pub struct HttpResponse {
pub request_id: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub updated_by: String,
pub error: Option<String>,
pub url: String,
pub elapsed: i64,
@@ -78,7 +76,6 @@ pub struct KeyValue {
pub model: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub updated_by: String,
pub namespace: String,
pub key: String,
pub value: String,
@@ -112,7 +109,7 @@ pub async fn get_key_value(namespace: &str, key: &str, pool: &Pool<Sqlite>) -> O
sqlx::query_as!(
KeyValue,
r#"
SELECT model, created_at, updated_at, updated_by, namespace, key, value
SELECT model, created_at, updated_at, namespace, key, value
FROM key_values
WHERE namespace = ? AND key = ?
"#,
@@ -128,7 +125,7 @@ pub async fn find_workspaces(pool: &Pool<Sqlite>) -> Result<Vec<Workspace>, sqlx
sqlx::query_as!(
Workspace,
r#"
SELECT id, model, created_at, updated_at, updated_by, name, description
SELECT id, model, created_at, updated_at, name, description
FROM workspaces
"#,
)
@@ -140,7 +137,7 @@ pub async fn get_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace, s
sqlx::query_as!(
Workspace,
r#"
SELECT id, model, created_at, updated_at, updated_by, name, description
SELECT id, model, created_at, updated_at, name, description
FROM workspaces WHERE id = ?
"#,
id,
@@ -168,19 +165,17 @@ pub async fn delete_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace
pub async fn create_workspace(
name: &str,
description: &str,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<Workspace, sqlx::Error> {
let id = generate_id("wk");
sqlx::query!(
r#"
INSERT INTO workspaces (id, updated_by, name, description)
VALUES (?, ?, ?, ?)
INSERT INTO workspaces (id, name, description)
VALUES (?, ?, ?)
"#,
id,
name,
description,
updated_by,
)
.execute(pool)
.await
@@ -189,11 +184,7 @@ pub async fn create_workspace(
get_workspace(&id, pool).await
}
pub async fn duplicate_request(
id: &str,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpRequest, sqlx::Error> {
pub async fn duplicate_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, sqlx::Error> {
let existing = get_request(id, pool)
.await
.expect("Failed to get request to duplicate");
@@ -219,7 +210,6 @@ pub async fn duplicate_request(
existing.url.as_str(),
existing.headers.0,
existing.sort_priority,
updated_by,
pool,
)
.await
@@ -237,7 +227,6 @@ pub async fn upsert_request(
url: &str,
headers: Vec<HttpRequestHeader>,
sort_priority: f64,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpRequest, sqlx::Error> {
let generated_id;
@@ -263,10 +252,9 @@ pub async fn upsert_request(
authentication,
authentication_type,
headers,
sort_priority,
updated_by
sort_priority
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET
updated_at = CURRENT_TIMESTAMP,
name = excluded.name,
@@ -277,8 +265,7 @@ pub async fn upsert_request(
authentication = excluded.authentication,
authentication_type = excluded.authentication_type,
url = excluded.url,
sort_priority = excluded.sort_priority,
updated_by = excluded.updated_by
sort_priority = excluded.sort_priority
"#,
id,
workspace_id,
@@ -291,7 +278,6 @@ pub async fn upsert_request(
authentication_type,
headers_json,
sort_priority,
updated_by,
)
.execute(pool)
.await
@@ -320,7 +306,6 @@ pub async fn find_requests(
authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
authentication_type,
sort_priority,
updated_by,
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
FROM http_requests
WHERE workspace_id = ?
@@ -349,7 +334,6 @@ pub async fn get_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, s
authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
authentication_type,
sort_priority,
updated_by,
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
FROM http_requests
WHERE id = ?
@@ -385,7 +369,6 @@ pub async fn create_response(
status_reason: Option<&str>,
body: &str,
headers: Vec<HttpResponseHeader>,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> {
let req = get_request(request_id, pool)
@@ -404,10 +387,9 @@ pub async fn create_response(
status,
status_reason,
body,
headers,
updated_by
headers
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
"#,
id,
request_id,
@@ -418,7 +400,6 @@ pub async fn create_response(
status_reason,
body,
headers_json,
updated_by,
)
.execute(pool)
.await
@@ -429,25 +410,23 @@ pub async fn create_response(
pub async fn update_response_if_id(
response: HttpResponse,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> {
if response.id == "" {
return Ok(response);
}
return update_response(response, updated_by, pool).await;
return update_response(response, pool).await;
}
pub async fn update_response(
response: HttpResponse,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> {
let headers_json = Json(response.headers);
sqlx::query!(
r#"
UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_by, updated_at) =
(?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;
UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =
(?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;
"#,
response.elapsed,
response.url,
@@ -456,7 +435,6 @@ pub async fn update_response(
response.body,
response.error,
headers_json,
updated_by,
response.id,
)
.execute(pool)
@@ -469,7 +447,7 @@ pub async fn get_response(id: &str, pool: &Pool<Sqlite>) -> Result<HttpResponse,
sqlx::query_as_unchecked!(
HttpResponse,
r#"
SELECT id, model, workspace_id, request_id, updated_at, updated_by, created_at,
SELECT id, model, workspace_id, request_id, updated_at, created_at,
status, status_reason, body, elapsed, url, error,
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
FROM http_responses
@@ -488,7 +466,7 @@ pub async fn find_responses(
sqlx::query_as!(
HttpResponse,
r#"
SELECT id, model, workspace_id, request_id, updated_at, updated_by,
SELECT id, model, workspace_id, request_id, updated_at,
created_at, status, status_reason, body, elapsed, url, error,
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
FROM http_responses

View File

@@ -14,6 +14,7 @@ import { keyValueQueryKey } from '../hooks/useKeyValue';
import { requestsQueryKey } from '../hooks/useRequests';
import { responsesQueryKey } from '../hooks/useResponses';
import { routePaths } from '../hooks/useRoutes';
import { UPDATE_DEBOUNCE_MILLIS } from '../hooks/useTauriListeners';
import { workspacesQueryKey } from '../hooks/useWorkspaces';
import { DEFAULT_FONT_SIZE } from '../lib/constants';
import { debounce } from '../lib/debounce';
@@ -22,8 +23,6 @@ import type { HttpRequest, HttpResponse, KeyValue, Workspace } from '../lib/mode
import { AppRouter } from './AppRouter';
import { DialogProvider } from './DialogContext';
const UPDATE_DEBOUNCE_MILLIS = 500;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
@@ -145,7 +144,6 @@ await listen('zoom', ({ payload: zoomDelta }: { payload: number }) => {
});
export function App() {
console.log('STARTING APP');
return (
<QueryClientProvider client={queryClient}>
<MotionConfig transition={{ duration: 0.1 }}>

View File

@@ -1,6 +1,14 @@
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';
import { useEffect } from 'react';
import {
createBrowserRouter,
Navigate,
Outlet,
RouterProvider,
useLocation,
} from 'react-router-dom';
import { routePaths } from '../hooks/useRoutes';
import { useTauriListeners } from '../hooks/useTauriListeners';
import { setLastLocation } from '../lib/lastLocation';
import RouteError from './RouteError';
import Workspace from './Workspace';
import Workspaces from './Workspaces';
@@ -9,6 +17,7 @@ const router = createBrowserRouter([
{
path: '/',
errorElement: <RouteError />,
element: <RouterRoot />,
children: [
{
path: '/',
@@ -34,8 +43,14 @@ const router = createBrowserRouter([
]);
export function AppRouter() {
console.log('AppRouter');
useTauriListeners();
console.log('AppRouter 2');
return <RouterProvider router={router} />;
}
function RouterRoot() {
const { pathname } = useLocation();
useEffect(() => {
setLastLocation(pathname).catch(console.error);
}, [pathname]);
return <Outlet />;
}

View File

@@ -1,18 +1,18 @@
import { Navigate } from 'react-router-dom';
import { useKeyValue } from '../hooks/useKeyValue';
import { useRoutes } from '../hooks/useRoutes';
import { useWorkspaces } from '../hooks/useWorkspaces';
import { Button } from './core/Button';
import { Heading } from './core/Heading';
import { VStack } from './core/Stacks';
export default function Workspaces() {
const lastWorkspace = useKeyValue<string | null>({ key: 'last_workspace', defaultValue: null });
const routes = useRoutes();
const workspaces = useWorkspaces();
return (
<VStack as="ul" className="p-12" space={1}>
<Heading>Workspaces</Heading>
{workspaces.map((w) => (
<Button key={w.id} color="gray" to={`/workspaces/${w.id}`}>
{w.name}
</Button>
))}
</VStack>
);
const workspace = workspaces[0];
if (workspace === undefined) {
return <Heading>There are no workspaces</Heading>;
}
return <Navigate to={routes.paths.workspace({ workspaceId: workspace.id })} />;
}

View File

@@ -15,7 +15,7 @@ export function keyValueQueryKey({
}
// eslint-disable-next-line @typescript-eslint/ban-types
export function useKeyValue<T extends Object>({
export function useKeyValue<T extends Object | null>({
namespace = DEFAULT_NAMESPACE,
key,
defaultValue,
@@ -30,12 +30,10 @@ export function useKeyValue<T extends Object>({
queryFn: async () => getKeyValue({ namespace, key, fallback: defaultValue }),
});
const mutate = useMutation<T, unknown, T>({
const mutate = useMutation<void, unknown, T>({
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
onMutate: (value) => {
// k/v should be as fast as possible, so optimistically update the cache
queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value);
},
// k/v should be as fast as possible, so optimistically update the cache
onMutate: (value) => queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value),
});
const set = useCallback(

View File

@@ -9,7 +9,7 @@ import { useRequestUpdateKey } from './useRequestUpdateKey';
import { useSidebarDisplay } from './useSidebarDisplay';
const unsubFns: (() => void)[] = [];
const UPDATE_DEBOUNCE_MILLIS = 500;
export const UPDATE_DEBOUNCE_MILLIS = 1000;
export function useTauriListeners() {
const sidebarDisplay = useSidebarDisplay();

View File

@@ -11,13 +11,12 @@ export async function setKeyValue<T>({
namespace?: string;
key: string | string[];
value: T;
}): Promise<T> {
}): Promise<void> {
await invoke('set_key_value', {
namespace,
key: buildKeyValueKey(key),
value: JSON.stringify(value),
});
return value;
}
export async function getKeyValue<T>({

View File

@@ -0,0 +1,17 @@
import { getKeyValue, setKeyValue } from './keyValueStore';
export async function getLastLocation(): Promise<string> {
return getKeyValue({ key: 'last_location', fallback: '/' });
}
export async function setLastLocation(pathname: string): Promise<void> {
return setKeyValue({ key: 'last_location', value: pathname });
}
export async function syncLastLocation(): Promise<void> {
const lastPathname = await getLastLocation();
if (lastPathname !== window.location.pathname) {
console.log(`Redirecting to last location: ${lastPathname}`);
window.location.assign(lastPathname);
}
}

View File

@@ -1,14 +1,13 @@
console.log('FIRST 0');
import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './components/App';
import { getKeyValue } from './lib/keyValueStore';
import { syncLastLocation } from './lib/lastLocation';
import { getPreferredAppearance, setAppearance } from './lib/theme/window';
import './main.css';
console.log('FIRST');
setAppearance(await getKeyValue({ key: 'appearance', fallback: getPreferredAppearance() }));
console.log('SECOND');
await syncLastLocation();
// root holds our app's root DOM Element:
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(