Add *very* basic login flow

This commit is contained in:
2026-05-17 13:04:51 +02:00
parent 0d28c4172c
commit 7d75e010c7
10 changed files with 187 additions and 22 deletions

View File

@@ -15,6 +15,8 @@ use either::Either;
use futures::{FutureExt, future::WeakShared};
use tokio::{fs, task::JoinHandle};
use crate::xdg::BASE_DIRECTORIES;
pub trait Fetcher<V>: Send + Sync + 'static {
type Key: ToString + FromStr;
@@ -86,9 +88,8 @@ where
serialize: fn(&K, &V) -> Vec<u8>,
deserialize: fn(&K, &[u8]) -> anyhow::Result<V>,
) -> anyhow::Result<Self> {
let crate_name = env!("CARGO_CRATE_NAME");
let data_dir = xdg::BaseDirectories::new()
.create_cache_directory(format!("{crate_name}/thumbnails"))
let data_dir = BASE_DIRECTORIES
.create_cache_directory("thumbnails")
.context(anyhow!(
"Failed to create XDG data folder for {cache_name:?}"
))?;

46
src/config.rs Normal file
View File

@@ -0,0 +1,46 @@
use anyhow::{Context, anyhow, bail};
use serde::{Deserialize, Serialize};
use tokio::fs;
use crate::xdg::BASE_DIRECTORIES;
const CONFIG_FILE: &str = "config.toml";
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub immich: Option<ImmichLogin>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImmichLogin {
pub url: String,
pub api_key: String,
}
impl Config {
pub async fn load() -> anyhow::Result<Self> {
let Some(config_path) = BASE_DIRECTORIES.get_config_file(CONFIG_FILE) else {
bail!("No config file exists")
};
let config = fs::read_to_string(&config_path)
.await
.context(anyhow!("Failed to read config file at {config_path:?}"))?;
toml::from_str(&config).context(anyhow!(
"Failed to deserialize config file at {config_path:?}"
))
}
pub async fn save(&self) -> anyhow::Result<()> {
let config_path = BASE_DIRECTORIES
.place_config_file(CONFIG_FILE)
.context(anyhow!("Failed to create config folder"))?;
let config = toml::to_string_pretty(self)?;
fs::write(&config_path, config)
.await
.context(anyhow!("Failed to write config file to {config_path:?}"))
}
}

View File

@@ -13,6 +13,7 @@ use tracing::Level;
use crate::{
api::{Api, TimeBucketKey},
config::Config,
ui::{AppWindow, ImageBucket},
};
@@ -20,22 +21,20 @@ use crate::{
#[global_allocator]
static ALLOCATOR: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
mod api;
pub mod api;
pub mod cachemap;
mod thumbhash;
pub mod config;
pub mod thumbhash;
pub mod xdg;
pub const CRATE_NAME: &str = env!("CARGO_CRATE_NAME");
mod ui {
slint::include_modules!();
}
#[derive(clap::Parser)]
struct Opt {
#[clap(long, env = "IMMICH_BASE_URL")]
pub immich_base_url: String,
#[clap(long, env = "IMMICH_API_KEY")]
pub immich_api_key: String,
}
struct Opt {}
// enum ApiReq<M: Send + 'static>
// where
@@ -59,7 +58,7 @@ struct Opt {
// }
fn main() -> anyhow::Result<()> {
let opt = Opt::parse();
let _opt = Opt::parse();
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
@@ -68,12 +67,53 @@ fn main() -> anyhow::Result<()> {
let runtime = tokio::runtime::Runtime::new().unwrap();
let _rt_guard = runtime.enter();
let immich_config =
immich_sdk::Config::new(opt.immich_base_url).with_api_key(opt.immich_api_key);
let api_ = Api::new(immich_sdk::Client::new(immich_config).unwrap());
let config_ = runtime
.block_on(Config::load())
.inspect_err(|e| tracing::debug!("{e}"))
.map(Arc::new)
.unwrap_or_default();
let app = ui::AppWindow::new()?;
let global = app.global::<ui::Global>();
let config = Arc::clone(&config_);
let app_weak = app.as_weak();
global.on_login_api_key(move |url, api_key| {
tracing::debug!("url: {url}, api_key: {api_key}");
let mut config = config.as_ref().clone();
let immich_config = config::ImmichLogin {
url: url.to_string(),
api_key: api_key.to_string(),
};
config.immich = Some(immich_config.clone());
tokio::spawn(async move {
if let Err(e) = config.save().await {
tracing::error!("{e}");
}
});
let _ = app_weak.upgrade_in_event_loop(move |app| {
start_api(&app, &immich_config);
});
});
if let Some(immich) = &config_.immich {
start_api(&app, immich);
}
app.run()?;
Ok(())
}
fn start_api(app: &AppWindow, immich: &config::ImmichLogin) {
let immich_config = immich_sdk::Config::new(&immich.url).with_api_key(&immich.api_key);
let api_ = Api::new(immich_sdk::Client::new(immich_config).unwrap());
let global = app.global::<ui::Global>();
global.set_logged_in(true);
global.set_image_buckets(ModelRc::new(VecModel::default()));
let app_weak = app.as_weak();
@@ -130,10 +170,6 @@ fn main() -> anyhow::Result<()> {
calculate_timeline_visibility(&app, api, scroll);
});
});
app.run()?;
Ok(())
}
fn calculate_timeline_visibility(app: &AppWindow, api: Arc<Api>, scroll: f32) {

8
src/xdg.rs Normal file
View File

@@ -0,0 +1,8 @@
use std::sync::LazyLock;
use xdg::BaseDirectories;
use crate::CRATE_NAME;
pub static BASE_DIRECTORIES: LazyLock<BaseDirectories> =
LazyLock::new(|| BaseDirectories::with_prefix(CRATE_NAME));