moved generated,spacetime into it's own package + many other things
This commit is contained in:
@@ -19,13 +19,19 @@ serde = "1.0.228"
|
||||
serde_json = "1.0.149"
|
||||
rusqlite = { version = "0.31", features = ["bundled"], optional = true }
|
||||
dioxus = { version = "0.7.1", optional = true }
|
||||
spacetimedb-sdk = "2.1.0"
|
||||
generated = { path = "../generated/" }
|
||||
webbrowser = { version = "1.2.0", optional = true }
|
||||
openidconnect = { version = "4", features = ["reqwest", "rustls-tls"], optional = true }
|
||||
|
||||
[features]
|
||||
default = ["target-os", "desktop", "server"]
|
||||
target-os = [ "clap", "mize/target-os", "rusqlite", "dioxus" ]
|
||||
desktop = ["dioxus/desktop"]
|
||||
server = []
|
||||
default = ["target-os", "desktop", "server", "client", "target-js",]
|
||||
target-os = [ "clap", "mize/target-os", "rusqlite", "dioxus", "dep:webbrowser" ]
|
||||
target-js = [ "mize/target-js" ]
|
||||
target-obsidian = [ "target-js" ]
|
||||
desktop = ["dioxus/desktop"]
|
||||
client = [ "dep:openidconnect" ]
|
||||
server = []
|
||||
clap = ["dep:clap"]
|
||||
|
||||
[lib]
|
||||
|
||||
419
packages/marts/auth/client.rs
Normal file
419
packages/marts/auth/client.rs
Normal file
@@ -0,0 +1,419 @@
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use mize::{
|
||||
item::ItemData, mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult,
|
||||
};
|
||||
use openidconnect::{
|
||||
ClientId, DeviceAuthorizationResponse, EmptyExtraDeviceAuthorizationFields, IssuerUrl,
|
||||
OAuth2TokenResponse, RefreshToken, Scope, TokenResponse,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, thread};
|
||||
|
||||
// console_log macro
|
||||
// that can be copied into other files for debugging purposes
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
macro_rules! console_log {
|
||||
// Note that this is using the `log` function imported above during
|
||||
// `bare_bones`
|
||||
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())})
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
use clap::Command;
|
||||
|
||||
#[derive(Default)]
|
||||
#[mize_part]
|
||||
pub struct ClientAuth {
|
||||
mize: Mize,
|
||||
secrets: Option<ClientSecrets>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ClientSecrets {
|
||||
id_token: String,
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
expiry: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl MizePartCreate for ClientAuth {}
|
||||
|
||||
impl MizePart for ClientAuth {
|
||||
fn name(&self) -> &'static str {
|
||||
"client_auth"
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientAuth {
|
||||
pub fn id_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().id_token)
|
||||
}
|
||||
|
||||
pub fn access_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().access_token)
|
||||
}
|
||||
|
||||
pub fn refresh_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().refresh_token)
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) -> MizeResult<()> {
|
||||
let secrets = self.secrets.clone().ok_or(mize_err!(
|
||||
"no client secrets, meaning the refresh thread can't use the refresh token"
|
||||
))?;
|
||||
self.mize.spawn_async_blocking(
|
||||
"auth.refresh_flow",
|
||||
refresh_flow(self.mize.clone(), secrets),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_refresh_thread(&mut self) -> MizeResult<()> {
|
||||
let mut mize_clone = self.mize.clone();
|
||||
self.mize.spawn_background("auth.refresh", move || loop {
|
||||
let auth = mize_clone.get_part_native::<ClientAuth>("auth")?;
|
||||
let to_sleep =
|
||||
Utc::now() - auth.secrets.as_ref().unwrap().expiry.clone() - TimeDelta::minutes(20);
|
||||
drop(auth);
|
||||
println!("refresh_thread: going to sleep for: {}", to_sleep);
|
||||
thread::sleep(to_sleep.to_std()?);
|
||||
println!("refresh_thread: sleeping done");
|
||||
let mut auth = mize_clone.get_part_native::<ClientAuth>("auth")?;
|
||||
auth.refresh()?;
|
||||
drop(auth);
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_auth(mize: &mut Mize) -> MizeResult<()> {
|
||||
let mize_clone = mize.clone();
|
||||
|
||||
let mut secrets = match read_secrets(mize) {
|
||||
Ok(secrets) => Some(secrets),
|
||||
Err(err) => {
|
||||
println!("err reading secrets: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// do a refresh if the tokens have expired
|
||||
if secrets.is_some() && secrets.clone().unwrap().expiry < Utc::now() {
|
||||
secrets = Some(
|
||||
mize.spawn_async_blocking("refresh_low", refresh_flow(mize.clone(), secrets.unwrap()))?,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
{
|
||||
let mut cli = mize.get_part_native::<crate::CliPart>("cli")?;
|
||||
cli.subcommand(Command::new("login"), |_, mut mize| {
|
||||
let mut auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||
println!("secrets: {:?}", auth.secrets);
|
||||
let secrets = if auth.secrets.is_some() {
|
||||
match mize.spawn_async_blocking(
|
||||
"client_auth",
|
||||
refresh_flow(mize.clone(), auth.secrets.clone().unwrap()),
|
||||
) {
|
||||
Ok(secrets) => secrets,
|
||||
Err(err) => {
|
||||
println!("err refreshing secrets: {:?}", err);
|
||||
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone))?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone))?
|
||||
};
|
||||
write_secrets(&mut mize, &secrets)?;
|
||||
auth.secrets = Some(secrets);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
{}
|
||||
|
||||
// check if login token is stil valid refresh if not
|
||||
/*
|
||||
if secrets.is_some() {
|
||||
secrets = Some(mize.spawn_async_blocking(
|
||||
"client_auth",
|
||||
refresh_flow(mize.clone(), secrets.clone().unwrap()),
|
||||
)?);
|
||||
}
|
||||
*/
|
||||
|
||||
mize.register_part(Box::new(ClientAuth {
|
||||
secrets,
|
||||
mize: mize.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn read_secrets(mize: &mut Mize) -> MizeResult<ClientSecrets> {
|
||||
let mize_dir = mize.get_config("data_dir")?.value_string()?;
|
||||
let secrets_file_path = PathBuf::from(mize_dir).join("client_secrets.json");
|
||||
|
||||
let secrets_string = std::fs::read_to_string(secrets_file_path)?;
|
||||
let secrets: ClientSecrets = serde_json::from_str(&secrets_string)?;
|
||||
Ok(secrets)
|
||||
}
|
||||
|
||||
fn write_secrets(mize: &mut Mize, secrets: &ClientSecrets) -> MizeResult<()> {
|
||||
let mize_dir = mize.get_config("data_dir")?.value_string()?;
|
||||
let secrets_file_path = PathBuf::from(mize_dir).join("client_secrets.json");
|
||||
|
||||
let secrets_string = serde_json::to_string(secrets)?;
|
||||
std::fs::write(secrets_file_path, secrets_string)?;
|
||||
|
||||
// update the spacetimedb_token in XDG_CONFIG_HOME/spacetime/cli.toml config file if configured to do so
|
||||
if mize
|
||||
.get_config("auth.client.update_spacetime_token")?
|
||||
.value_bool()?
|
||||
{
|
||||
update_spacetimedb_token(&secrets.id_token.as_str())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use openidconnect::reqwest::Client as HttpClient;
|
||||
use openidconnect::DeviceAuthorizationUrl;
|
||||
use std::time::Duration;
|
||||
|
||||
async fn refresh_flow(mut mize: Mize, old_secrets: ClientSecrets) -> MizeResult<ClientSecrets> {
|
||||
println!("Refresh flow...");
|
||||
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||
|
||||
let issuer = IssuerUrl::new(issuer)?;
|
||||
let client_id = ClientId::new(client_id);
|
||||
|
||||
let http_client = HttpClient::new();
|
||||
|
||||
// Teach openidconnect-rs about the device authorization url.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct MyExtraProviderMetadata {
|
||||
device_authorization_endpoint: String,
|
||||
}
|
||||
use openidconnect::core::{
|
||||
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
};
|
||||
use openidconnect::AdditionalProviderMetadata;
|
||||
use openidconnect::ProviderMetadata;
|
||||
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||
type MyProviderMetadata = ProviderMetadata<
|
||||
MyExtraProviderMetadata,
|
||||
CoreAuthDisplay,
|
||||
CoreClientAuthMethod,
|
||||
CoreClaimName,
|
||||
CoreClaimType,
|
||||
CoreGrantType,
|
||||
CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm,
|
||||
CoreJsonWebKey,
|
||||
CoreResponseMode,
|
||||
CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
>;
|
||||
|
||||
// Discover provider metadata
|
||||
let provider_metadata: MyProviderMetadata =
|
||||
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||
|
||||
let device_auth_url = provider_metadata
|
||||
.additional_metadata()
|
||||
.device_authorization_endpoint
|
||||
.clone();
|
||||
|
||||
let client = CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
client_id,
|
||||
None, // public client
|
||||
)
|
||||
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||
|
||||
let refresh_token = RefreshToken::new(old_secrets.refresh_token);
|
||||
let res = client
|
||||
.exchange_refresh_token(&refresh_token)?
|
||||
.request_async(&http_client)
|
||||
.await?;
|
||||
let expires_in = res.expires_in().ok_or(mize_err!(
|
||||
"no expiry information was in token response of the refresh flow"
|
||||
))?;
|
||||
|
||||
return Ok(ClientSecrets {
|
||||
id_token: res.id_token().unwrap().to_string(),
|
||||
access_token: res.access_token().secret().to_owned(),
|
||||
refresh_token: res.refresh_token().unwrap().secret().to_owned(),
|
||||
expiry: Utc::now() + expires_in,
|
||||
});
|
||||
}
|
||||
|
||||
async fn auth_flow(mut mize: Mize) -> MizeResult<ClientSecrets> {
|
||||
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||
|
||||
let issuer = IssuerUrl::new(issuer)?;
|
||||
let client_id = ClientId::new(client_id);
|
||||
|
||||
let http_client = HttpClient::new();
|
||||
|
||||
// Teach openidconnect-rs about the device authorization url.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct MyExtraProviderMetadata {
|
||||
device_authorization_endpoint: String,
|
||||
}
|
||||
use openidconnect::core::{
|
||||
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
};
|
||||
use openidconnect::AdditionalProviderMetadata;
|
||||
use openidconnect::ProviderMetadata;
|
||||
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||
type MyProviderMetadata = ProviderMetadata<
|
||||
MyExtraProviderMetadata,
|
||||
CoreAuthDisplay,
|
||||
CoreClientAuthMethod,
|
||||
CoreClaimName,
|
||||
CoreClaimType,
|
||||
CoreGrantType,
|
||||
CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm,
|
||||
CoreJsonWebKey,
|
||||
CoreResponseMode,
|
||||
CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
>;
|
||||
|
||||
// Discover provider metadata
|
||||
let provider_metadata: MyProviderMetadata =
|
||||
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||
|
||||
let device_auth_url = provider_metadata
|
||||
.additional_metadata()
|
||||
.device_authorization_endpoint
|
||||
.clone();
|
||||
|
||||
let client = CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
client_id,
|
||||
None, // public client
|
||||
)
|
||||
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||
|
||||
// ===== DEVICE AUTH REQUEST =====
|
||||
let details: Result<DeviceAuthorizationResponse<EmptyExtraDeviceAuthorizationFields>, _> =
|
||||
client
|
||||
.exchange_device_code()
|
||||
.add_scope(Scope::new("openid".into()))
|
||||
.add_scope(Scope::new("profile".into()))
|
||||
.add_scope(Scope::new("offline_access".into()))
|
||||
.request_async(&http_client)
|
||||
.await;
|
||||
|
||||
let details = match details {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
println!("err: {:?}", err);
|
||||
println!("err: {}", err);
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
"If your browser does not open automatically, open this URL in it:\n{}\n",
|
||||
details.verification_uri_complete().unwrap().secret()
|
||||
);
|
||||
println!(
|
||||
"Or enter this PPC login code:\n{}\n",
|
||||
details.user_code().secret()
|
||||
);
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
{
|
||||
if let Some(_) = details.verification_uri_complete() {
|
||||
let _ = webbrowser::open(details.verification_uri_complete().unwrap().secret());
|
||||
}
|
||||
}
|
||||
|
||||
let token_response = client
|
||||
.exchange_device_access_token(&details)?
|
||||
.request_async(
|
||||
&http_client,
|
||||
async |dur: Duration| {
|
||||
tokio::time::sleep(dur).await;
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// ===== TOKENS =====
|
||||
let id_token = token_response
|
||||
.extra_fields()
|
||||
.id_token()
|
||||
.ok_or(mize_err!("no id_token"))?;
|
||||
|
||||
let refresh = token_response
|
||||
.refresh_token()
|
||||
.ok_or(mize_err!("no refresh token present"))?;
|
||||
|
||||
let expires_in = token_response.expires_in().ok_or(mize_err!(
|
||||
"no expiry information was in token response of the refresh flow"
|
||||
))?;
|
||||
|
||||
Ok(ClientSecrets {
|
||||
id_token: id_token.to_string(),
|
||||
access_token: token_response.access_token().secret().to_string(),
|
||||
refresh_token: refresh.secret().to_owned(),
|
||||
expiry: Utc::now() + expires_in,
|
||||
})
|
||||
}
|
||||
|
||||
fn update_spacetimedb_token(new_token: &str) -> MizeResult<()> {
|
||||
// Resolve config directory
|
||||
let config_dir = match std::env::var("XDG_CONFIG_HOME") {
|
||||
Ok(val) => PathBuf::from(val),
|
||||
Err(_) => {
|
||||
let home = std::env::var("HOME")?;
|
||||
std::path::PathBuf::from(home).join(".config")
|
||||
}
|
||||
};
|
||||
|
||||
let file_path = config_dir.join("spacetime").join("cli.toml");
|
||||
|
||||
// Read existing file (or create empty document if it doesn't exist)
|
||||
let content = std::fs::read_to_string(&file_path).unwrap_or_default();
|
||||
let mut data = ItemData::from_toml(content.as_str())?;
|
||||
|
||||
// Update the field
|
||||
data.set_path("spacetimedb_token", new_token)?;
|
||||
|
||||
// Write back
|
||||
std::fs::write(&file_path, data.to_toml()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -5,10 +5,21 @@ use clap::{Arg, Command};
|
||||
use rusqlite::{params, Connection};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizeResult};
|
||||
use dioxus::prelude::*;
|
||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult};
|
||||
|
||||
use crate::cli::CliPart;
|
||||
use crate::habitica::Habitica;
|
||||
use crate::SpacetimePart;
|
||||
use generated::spacetime::{
|
||||
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
||||
update_activity, update_upcoming, Action, ActivitiesTableAccess, Activity, DbConnection,
|
||||
TaskPart, TaskPartTableAccess,
|
||||
};
|
||||
use spacetimedb_sdk::{
|
||||
credentials, DbContext, Error, Event, Identity, Status, SubscriptionHandle, Table,
|
||||
TableWithPrimaryKey,
|
||||
};
|
||||
|
||||
mod dashboard;
|
||||
|
||||
@@ -24,6 +35,7 @@ impl MizePart for C2vi {
|
||||
mize.new_opt("c2vi.local_storage_path");
|
||||
}
|
||||
}
|
||||
impl MizePartCreate for C2vi {}
|
||||
|
||||
impl C2vi {
|
||||
fn with_db<F, R>(&self, f: F) -> MizeResult<R>
|
||||
@@ -42,12 +54,67 @@ impl C2vi {
|
||||
}
|
||||
|
||||
pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
||||
#[cfg(feature = "client")]
|
||||
c2vi_client(mize)?;
|
||||
#[cfg(feature = "desktop")]
|
||||
if mize
|
||||
.get_config("c2vi.dash.enable")?
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// refresh the credentials every 23h
|
||||
// as this is basically running as a daemon
|
||||
use crate::auth::client::ClientAuth;
|
||||
let mut auth = mize.get_part_native::<ClientAuth>("auth")?;
|
||||
auth.refresh()?;
|
||||
auth.start_refresh_thread()?;
|
||||
drop(auth);
|
||||
|
||||
launch_c2vi_dashboard(mize);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "desktop")]
|
||||
pub fn launch_c2vi_dashboard(mize: &mut Mize) {
|
||||
use dioxus::desktop::{muda::Menu, Config, WindowBuilder};
|
||||
let mize_clone = mize.clone();
|
||||
LaunchBuilder::new()
|
||||
.with_context_provider(move || Box::new(mize_clone.clone()))
|
||||
.with_cfg(desktop! {
|
||||
Config::new().with_window(WindowBuilder::new().with_decorations(false).with_title("PPC"))
|
||||
})
|
||||
.launch(c2vi_dash_main);
|
||||
}
|
||||
|
||||
pub fn c2vi_dash_main() -> Element {
|
||||
let mut current = use_signal_sync(|| "".to_string());
|
||||
use_effect(move || {
|
||||
let mut mize = use_context::<Mize>();
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime").unwrap();
|
||||
let con = spacetime.con();
|
||||
con.subscription_builder()
|
||||
.on_applied(move |ctx| {
|
||||
let data = ctx.db.task_part().identity().find(&ctx.identity()).unwrap();
|
||||
current.set(data.current.name.clone());
|
||||
})
|
||||
.add_query(|q| q.from.task_part())
|
||||
.subscribe();
|
||||
con.db
|
||||
.task_part()
|
||||
.on_update(move |_, old: &TaskPart, new: &TaskPart| {
|
||||
current.set(new.current.name.clone());
|
||||
});
|
||||
});
|
||||
rsx! {
|
||||
div {
|
||||
style: "text-align: center; padding: 2rem; font-size: 50px;",
|
||||
p {
|
||||
"{current}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn c2vi_client(mize: &mut Mize) -> MizeResult<()> {
|
||||
//let storage_path = mize.get_config("c2vi.local_storage_path")?.as_
|
||||
let storage_path = "/home/me/work/app-data/ppc";
|
||||
@@ -97,7 +164,7 @@ fn setup_tables(conn: &Connection) -> MizeResult<()> {
|
||||
fn register_subcommands(cli: &mut CliPart) {
|
||||
cli.subcommand(
|
||||
Command::new("c2vi-dash").about("Run the c2vi dashboard gui"),
|
||||
|_matches, mut mize| Ok(dioxus::launch(dashboard::main)),
|
||||
|_matches, mut _mize| Ok(dioxus::launch(dashboard::main)),
|
||||
);
|
||||
cli.subcommand(
|
||||
Command::new("bed").about("Command to run when c2vi goes to bed"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clap::{ArgMatches, Command};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mize::{mize_part, Mize, MizePart, MizeResult};
|
||||
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||
|
||||
#[mize_part("cli")]
|
||||
#[derive(Default)]
|
||||
@@ -28,6 +28,8 @@ pub fn cli(mize: &mut Mize) -> MizeResult<()> {
|
||||
mize.add_part(Box::new(cli_part))
|
||||
}
|
||||
|
||||
impl MizePartCreate for CliPart {}
|
||||
|
||||
impl MizePart for CliPart {
|
||||
fn init(&mut self, _mize: &mut Mize) -> MizeResult<()> {
|
||||
Ok(())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizeResult};
|
||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult};
|
||||
use reqwest::{blocking::Client, blocking::Response, header, Method};
|
||||
use serde_json::{json, Value};
|
||||
use std::thread::sleep;
|
||||
@@ -20,6 +20,7 @@ pub fn habitica(mize: &mut Mize) -> MizeResult<()> {
|
||||
}))
|
||||
}
|
||||
|
||||
impl MizePartCreate for Habitica {}
|
||||
impl MizePart for Habitica {
|
||||
fn opts(&self, mize: &mut Mize) {
|
||||
mize.new_opt("habitica.api_url");
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
|
||||
use mize::MizeResult;
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
mod cli;
|
||||
#[cfg(feature = "target-os")]
|
||||
@@ -16,6 +13,17 @@ pub mod c2vi;
|
||||
#[cfg(feature = "target-os")]
|
||||
pub use c2vi::*;
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
pub mod spacetime;
|
||||
#[cfg(feature = "target-os")]
|
||||
pub use spacetime::*;
|
||||
|
||||
pub mod auth {
|
||||
pub mod client;
|
||||
pub use client::*;
|
||||
}
|
||||
pub use auth::*;
|
||||
|
||||
//#[cfg(feature = "target-os")]
|
||||
//pub mod js;
|
||||
//#[cfg(feature = "target-os")]
|
||||
|
||||
46
packages/marts/spacetime.rs
Normal file
46
packages/marts/spacetime.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||
|
||||
use crate::auth::client::ClientAuth;
|
||||
use generated::spacetime::DbConnection;
|
||||
|
||||
#[mize_part]
|
||||
#[derive(Default)]
|
||||
pub struct SpacetimePart {
|
||||
mize: Mize,
|
||||
con: Option<DbConnection>,
|
||||
}
|
||||
|
||||
impl MizePartCreate for SpacetimePart {}
|
||||
impl MizePart for SpacetimePart {
|
||||
fn name(&self) -> &'static str {
|
||||
"spacetime"
|
||||
}
|
||||
}
|
||||
|
||||
impl SpacetimePart {
|
||||
pub fn con(&mut self) -> &mut DbConnection {
|
||||
self.con.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spacetime(mize: &mut Mize) -> MizeResult<()> {
|
||||
let auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||
let db_url = mize.get_config("spacetime.url")?.value_string()?;
|
||||
|
||||
let con = DbConnection::builder()
|
||||
.with_token(Some(auth.id_token()?))
|
||||
.with_database_name("ppc")
|
||||
.with_uri(db_url)
|
||||
//.on_connect(callback)
|
||||
//.on_disconnect(callback)
|
||||
//.on_connect_error(callback)
|
||||
//.with_compression(...)
|
||||
.build()?;
|
||||
|
||||
con.run_threaded();
|
||||
|
||||
mize.register_part(Box::new(SpacetimePart {
|
||||
mize: mize.clone(),
|
||||
con: Some(con),
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user