diff --git a/app.vue b/app.vue index 57aa3c1..b60f381 100644 --- a/app.vue +++ b/app.vue @@ -43,4 +43,8 @@ listen("auth/failed", () => { listen("auth/finished", () => { router.push("/"); }); + +useHead({ + title: "Drop" +}) diff --git a/components/Header.vue b/components/Header.vue index 14314eb..b043055 100644 --- a/components/Header.vue +++ b/components/Header.vue @@ -1,15 +1,17 @@ diff --git a/composables/current-page-engine.ts b/composables/current-page-engine.ts new file mode 100644 index 0000000..ca049f8 --- /dev/null +++ b/composables/current-page-engine.ts @@ -0,0 +1,30 @@ +import type { RouteLocationNormalized } from "vue-router"; +import type { NavigationItem } from "~/components/types"; + +export const useCurrentNavigationIndex = ( + navigation: Array +) => { + const router = useRouter(); + const route = useRoute(); + + const currentNavigation = ref(-1); + + function calculateCurrentNavIndex(to: RouteLocationNormalized) { + const validOptions = navigation + .map((e, i) => ({ ...e, index: i })) + .filter((e) => to.fullPath.startsWith(e.prefix)); + const bestOption = validOptions + .sort((a, b) => b.route.length - a.route.length) + .at(0); + + return bestOption?.index ?? -1; + } + + currentNavigation.value = calculateCurrentNavIndex(route); + + router.afterEach((to) => { + currentNavigation.value = calculateCurrentNavIndex(to); + }); + + return currentNavigation; +}; diff --git a/package.json b/package.json index 7af113c..da0bfea 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@heroicons/vue": "^2.1.5", "@tauri-apps/api": ">=2.0.0", "@tauri-apps/plugin-deep-link": "~2", + "@tauri-apps/plugin-dialog": "~2", "@tauri-apps/plugin-shell": ">=2.0.0", "nuxt": "^3.13.0", "scss": "^0.2.4", diff --git a/pages/settings.vue b/pages/settings.vue new file mode 100644 index 0000000..b897920 --- /dev/null +++ b/pages/settings.vue @@ -0,0 +1,74 @@ + + + diff --git a/pages/settings/downloads.vue b/pages/settings/downloads.vue new file mode 100644 index 0000000..27e0f69 --- /dev/null +++ b/pages/settings/downloads.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/settings/index.vue b/pages/settings/index.vue new file mode 100644 index 0000000..12bbd47 --- /dev/null +++ b/pages/settings/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/settings/interface.vue b/pages/settings/interface.vue new file mode 100644 index 0000000..27e0f69 --- /dev/null +++ b/pages/settings/interface.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/setup/server.vue b/pages/setup/server.vue index 19b0794..40e8818 100644 --- a/pages/setup/server.vue +++ b/pages/setup/server.vue @@ -60,7 +60,7 @@ -

+

Don't have one? {{ " " }} Result { +pub fn sign_nonce(private_key: String, nonce: String) -> Result { let client_private_key = EcKey::private_key_from_pem(private_key.as_bytes()).unwrap(); let pkey_private_key = PKey::from_ec_key(client_private_key).unwrap(); @@ -74,7 +75,7 @@ pub fn generate_authorization_header() -> String { return format!("Nonce {} {} {}", certs.clientId, nonce, signature); } -pub fn fetch_user() -> Result { +pub fn fetch_user() -> Result { let base_url = { let handle = DB.borrow_data().unwrap(); Url::parse(&handle.base_url).unwrap() @@ -90,6 +91,11 @@ pub fn fetch_user() -> Result { .send() .unwrap(); + if response.status() != 200 { + warn!("Failed to fetch user: {}", response.status()); + return Err(()); + } + let user = response.json::().unwrap(); return Ok(user); @@ -138,11 +144,10 @@ pub fn recieve_handshake(app: AppHandle, path: String) { let app_state = app.state::>(); let mut app_state_handle = app_state.lock().unwrap(); app_state_handle.status = AppStatus::SignedIn; + app_state_handle.user = Some(fetch_user().unwrap()); } app.emit("auth/finished", ()).unwrap(); - - fetch_user().unwrap(); } #[tauri::command] @@ -152,12 +157,10 @@ pub async fn auth_initiate<'a>() -> Result<(), String> { Url::parse(&db_lock.base_url.clone()).unwrap() }; - let current_os_info = os_info::get(); - let endpoint = base_url.join("/api/v1/client/auth/initiate").unwrap(); let body = InitiateRequestBody { name: format!("Drop Desktop Client"), - platform: current_os_info.os_type().to_string(), + platform: env::consts::OS.to_string(), }; let client = reqwest::Client::new(); @@ -168,6 +171,10 @@ pub async fn auth_initiate<'a>() -> Result<(), String> { .await .unwrap(); + if response.status() != 200 { + return Err("Failed to create redirect URL. Please try again later.".to_string()); + } + let redir_url = response.text().await.unwrap(); let complete_redir_url = base_url.join(&redir_url).unwrap(); @@ -177,7 +184,7 @@ pub async fn auth_initiate<'a>() -> Result<(), String> { return Ok(()); } -pub fn setup() -> Result<(AppStatus, Option), Error> { +pub fn setup() -> Result<(AppStatus, Option), ()> { let data = DB.borrow_data().unwrap(); // If we have certs, exit for now @@ -186,7 +193,7 @@ pub fn setup() -> Result<(AppStatus, Option), Error> { if user_result.is_err() { return Ok((AppStatus::SignedInNeedsReauth, None)); } - return Ok((AppStatus::SignedIn, Some(user_result.unwrap()))) + return Ok((AppStatus::SignedIn, Some(user_result.unwrap()))); } drop(data); diff --git a/src-tauri/src/data.rs b/src-tauri/src/db.rs similarity index 59% rename from src-tauri/src/data.rs rename to src-tauri/src/db.rs index 2dd9ebd..4683f84 100644 --- a/src-tauri/src/data.rs +++ b/src-tauri/src/db.rs @@ -1,4 +1,8 @@ -use std::fs; +use std::{ + fs::{self, create_dir_all}, + path::PathBuf, + sync::LazyLock, +}; use directories::BaseDirs; use rustbreak::{deser::Bincode, PathDatabase}; @@ -13,20 +17,37 @@ pub struct DatabaseAuth { pub clientId: String, } +#[derive(serde::Serialize, Clone, Deserialize)] +pub struct DatabaseApps { + pub apps_base_dir: String, +} + #[derive(serde::Serialize, Clone, Deserialize)] pub struct Database { pub auth: Option, pub base_url: String, + pub downloads: DatabaseApps, } pub type DatabaseInterface = rustbreak::Database; +pub static DATA_ROOT_DIR: LazyLock = + LazyLock::new(|| BaseDirs::new().unwrap().data_dir().join("drop")); + pub fn setup() -> DatabaseInterface { - let db_path = BaseDirs::new().unwrap().data_dir().join("drop"); + let db_path = DATA_ROOT_DIR.join("drop.db"); + let apps_base_dir = DATA_ROOT_DIR.join("apps"); + + create_dir_all(DATA_ROOT_DIR.clone()).unwrap(); + create_dir_all(apps_base_dir.clone()).unwrap(); + let default = Database { auth: None, base_url: "".to_string(), + downloads: DatabaseApps { + apps_base_dir: apps_base_dir.to_str().unwrap().to_string(), + }, }; let db = match fs::exists(db_path.clone()).unwrap() { true => PathDatabase::load_from_path(db_path).expect("Database loading failed"), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 28e319f..6e253da 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,5 @@ mod auth; -mod data; +mod db; mod remote; mod unpacker; @@ -10,7 +10,7 @@ use std::{ }; use auth::{auth_initiate, recieve_handshake}; -use data::DatabaseInterface; +use db::{DatabaseInterface, DATA_ROOT_DIR}; use log::info; use remote::{gen_drop_url, use_remote}; use serde::{Deserialize, Serialize}; @@ -52,7 +52,7 @@ fn setup<'a>() -> AppState { .with_target_writer("*", new_writer(io::stdout())) .init(); - let is_set_up = data::is_set_up(); + let is_set_up = db::is_set_up(); if !is_set_up { return AppState { status: AppStatus::NotConfigured, @@ -67,14 +67,14 @@ fn setup<'a>() -> AppState { }; } -pub static DB: LazyLock = LazyLock::new(|| data::setup()); +pub static DB: LazyLock = LazyLock::new(|| db::setup()); #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let state = setup(); info!("Initialized drop client"); - let mut builder = tauri::Builder::default(); + let mut builder = tauri::Builder::default().plugin(tauri_plugin_dialog::init()); #[cfg(desktop)] { @@ -103,6 +103,19 @@ pub fn run() { let handle = app.handle().clone(); + let main_window = tauri::WebviewWindowBuilder::new( + &handle, + "main", // BTW this is not the name of the window, just the label. Keep this 'main', there are permissions & configs that depend on it + tauri::WebviewUrl::App("index.html".into()), + ) + .title("Drop Desktop App") + .min_inner_size(900.0, 900.0) + .inner_size(1536.0, 864.0) + .decorations(false) + .data_directory(DATA_ROOT_DIR.join(".webview")) + .build() + .unwrap(); + app.deep_link().on_open_url(move |event| { info!("handling drop:// url"); let binding = event.urls(); @@ -112,6 +125,7 @@ pub fn run() { _ => (), } }); + Ok(()) }) .run(tauri::generate_context!()) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e361ef0..8fecb09 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -10,16 +10,6 @@ "frontendDist": "../.output/public" }, "app": { - "windows": [ - { - "title": "Drop", - "width": 1536, - "height": 864, - "minWidth": 820, - "minHeight": 600, - "decorations": false - } - ], "security": { "csp": null } diff --git a/yarn.lock b/yarn.lock index b361d19..b623e1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1335,6 +1335,13 @@ dependencies: "@tauri-apps/api" "^2.0.0" +"@tauri-apps/plugin-dialog@~2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.0.tgz#f1e2840c7f824572a76b375fd1b538a36f28de14" + integrity sha512-ApNkejXP2jpPBSifznPPcHTXxu9/YaRW+eJ+8+nYwqp0lLUtebFHG4QhxitM43wwReHE81WAV1DQ/b+2VBftOA== + dependencies: + "@tauri-apps/api" "^2.0.0" + "@tauri-apps/plugin-shell@>=2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0.tgz#b6fc88ab070fd5f620e46405715779aa44eb8428"