2020-08-27 00:11:50 +02:00
|
|
|
#[macro_use]
|
2023-06-25 15:29:52 +02:00
|
|
|
extern crate upend_db;
|
|
|
|
|
2024-01-26 22:41:43 +01:00
|
|
|
use crate::common::{REQWEST_ASYNC_CLIENT, WEBUI_PATH};
|
2023-06-25 15:29:52 +02:00
|
|
|
use crate::config::UpEndConfig;
|
2023-06-07 21:09:33 +02:00
|
|
|
use actix_web::HttpServer;
|
2024-06-11 17:28:39 +02:00
|
|
|
use anyhow::{anyhow, Result};
|
ci: switch to Earthly
Squashed commit of the following:
commit 06baa23fc82f1f723bbfb1ab69c97802d28efa19
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:10:19 2023 +0200
ci, fix: forgot push
commit 6494be49d282368dd7c4aa78f56ccb33acac3eaa
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:01:14 2023 +0200
fix, ci: docker tag arg
commit 38682ba930abfeec5bf7facffbd0b94f6d61af9e
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:54:45 2023 +0200
ci: parallelize push steps
commit 5eeab18aa0d3fa3a7ef88e71cb5e9b5be5e7b9df
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:52:37 2023 +0200
ci, fix: docker login
commit ce10d0d04a55282cb4d95136e695705f95f11a86
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:52 2023 +0200
ci: remove earthly verbose
commit ff9b84296868bd18f004bdfcc23832acadeac388
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:23 2023 +0200
ci, fix: typo
commit df80ee061006c5eaa9b729a69818fd8bd273865c
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:06:47 2023 +0200
ci, refactor: better step names
commit 80093f8964cfd4cfa9e8b5ce4b6854c5c48060e4
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:05:03 2023 +0200
ci, fix: earthly config for publish:appimage step
commit 650824df99495afcc3feb4fcaa3e336b691d3008
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:04:50 2023 +0200
ci, refactor: only explicitly copy AppImages in sign target
commit 3b53e2dc6475e55f64fa9d00a2f2d31f07a9d8bb
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 08:01:43 2023 +0200
ci: EARTHLY_VERBOSE=1
commit cec95ea29a4f207fe1b7790b70562b1eeabee195
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:10:09 2023 +0200
ci: earthly bootstrap after conf
commit 7afe653d575a8ff76488d8fe3a93bc5e679124e5
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:04:08 2023 +0200
ci, fix: remove ssh_key secret
commit b549d891ed789743066d3c29e12b6b82fcab35a3
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 22:02:01 2023 +0200
ci, fix: missing gpg-agent
commit 47938c71474ec39b0d989742f9e1446b6df94bca
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 20:55:15 2023 +0200
ci, fix: unify earthly config
commit 7b89ea7ef4957a9925c4250c662bdd4d204b53dd
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 19:59:37 2023 +0200
ci: publishing docker, appimage, nightlies
commit f4f94d98644c7cc1506103538a30db241108b589
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 18:19:00 2023 +0200
ci: add lint & test step
commit be180ed59b216cfc85953ac5841eb11933a3a000
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:13:03 2023 +0200
ci, wip: earthly integration
commit 39db638cbdaaf5a436de8d531da2f68f85264430
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:12:21 2023 +0200
ci: use `upend --version` for AppImage, move get_version.sh logic to cli
commit 5188336c7eca877c0174ffa655893b287bc97baa
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 12:30:47 2023 +0200
ci: refix AppImage, switch to appimage-builder, build docker
commit 27f7941020bcf2e103d1e476038a6bc65f4153ea
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:55:03 2023 +0200
wip: remote woodpecker CI config for the time being
commit 53e775b85d2408060b034c2eff76e81b30b3dae5
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:59 2023 +0200
wip: delete .env
it's interpreted by Earthly and I'm not sure it's necessary anyway
commit 26bec328036c03dcb9a1917f9cf531bf7d10bb7d
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:32 2023 +0200
wip: initial somewhat functional Earthfile
2023-08-23 12:13:24 +02:00
|
|
|
use clap::{Args, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
|
2023-04-25 19:27:31 +02:00
|
|
|
use filebuffer::FileBuffer;
|
2022-03-24 11:10:51 +01:00
|
|
|
use rand::{thread_rng, Rng};
|
2023-05-04 19:16:01 +02:00
|
|
|
use regex::Captures;
|
|
|
|
use regex::Regex;
|
2023-04-25 19:27:31 +02:00
|
|
|
use reqwest::Url;
|
2024-06-11 17:28:39 +02:00
|
|
|
use serde_json::{json, Value};
|
2023-05-04 19:32:08 +02:00
|
|
|
use std::collections::HashMap;
|
2023-04-25 19:27:31 +02:00
|
|
|
use std::net::SocketAddr;
|
2023-05-04 17:39:05 +02:00
|
|
|
use std::path::Path;
|
2023-04-25 19:27:31 +02:00
|
|
|
use std::path::PathBuf;
|
2024-06-11 17:28:39 +02:00
|
|
|
use std::str::FromStr;
|
2024-03-27 19:23:35 +01:00
|
|
|
use std::sync::{Arc, Mutex};
|
2023-04-25 19:27:31 +02:00
|
|
|
use tracing::trace;
|
|
|
|
use tracing::{debug, error, info, warn};
|
2022-08-07 12:13:12 +02:00
|
|
|
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
|
2023-06-25 15:29:52 +02:00
|
|
|
use upend_base::addressing::Address;
|
|
|
|
use upend_base::entry::EntryValue;
|
2023-06-29 14:29:38 +02:00
|
|
|
use upend_base::hash::{sha256hash, UpMultihash};
|
2023-06-25 15:29:52 +02:00
|
|
|
use upend_db::jobs::JobContainer;
|
|
|
|
use upend_db::stores::fs::FsStore;
|
|
|
|
use upend_db::stores::UpStore;
|
2024-03-31 00:31:32 +01:00
|
|
|
use upend_db::{BlobMode, OperationContext, UpEndDatabase};
|
2021-12-23 11:10:16 +01:00
|
|
|
|
2023-04-20 16:02:41 +02:00
|
|
|
use crate::util::exec::block_background;
|
|
|
|
|
2023-05-24 11:20:13 +02:00
|
|
|
mod common;
|
2023-06-25 15:29:52 +02:00
|
|
|
mod config;
|
2020-08-27 01:07:25 +02:00
|
|
|
mod routes;
|
2023-06-07 21:09:33 +02:00
|
|
|
mod serve;
|
2020-09-14 21:18:53 +02:00
|
|
|
mod util;
|
2020-08-27 00:11:50 +02:00
|
|
|
|
2023-04-20 16:02:41 +02:00
|
|
|
mod extractors;
|
|
|
|
mod previews;
|
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
#[derive(Debug, Parser)]
|
ci: switch to Earthly
Squashed commit of the following:
commit 06baa23fc82f1f723bbfb1ab69c97802d28efa19
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:10:19 2023 +0200
ci, fix: forgot push
commit 6494be49d282368dd7c4aa78f56ccb33acac3eaa
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:01:14 2023 +0200
fix, ci: docker tag arg
commit 38682ba930abfeec5bf7facffbd0b94f6d61af9e
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:54:45 2023 +0200
ci: parallelize push steps
commit 5eeab18aa0d3fa3a7ef88e71cb5e9b5be5e7b9df
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:52:37 2023 +0200
ci, fix: docker login
commit ce10d0d04a55282cb4d95136e695705f95f11a86
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:52 2023 +0200
ci: remove earthly verbose
commit ff9b84296868bd18f004bdfcc23832acadeac388
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:23 2023 +0200
ci, fix: typo
commit df80ee061006c5eaa9b729a69818fd8bd273865c
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:06:47 2023 +0200
ci, refactor: better step names
commit 80093f8964cfd4cfa9e8b5ce4b6854c5c48060e4
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:05:03 2023 +0200
ci, fix: earthly config for publish:appimage step
commit 650824df99495afcc3feb4fcaa3e336b691d3008
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:04:50 2023 +0200
ci, refactor: only explicitly copy AppImages in sign target
commit 3b53e2dc6475e55f64fa9d00a2f2d31f07a9d8bb
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 08:01:43 2023 +0200
ci: EARTHLY_VERBOSE=1
commit cec95ea29a4f207fe1b7790b70562b1eeabee195
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:10:09 2023 +0200
ci: earthly bootstrap after conf
commit 7afe653d575a8ff76488d8fe3a93bc5e679124e5
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:04:08 2023 +0200
ci, fix: remove ssh_key secret
commit b549d891ed789743066d3c29e12b6b82fcab35a3
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 22:02:01 2023 +0200
ci, fix: missing gpg-agent
commit 47938c71474ec39b0d989742f9e1446b6df94bca
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 20:55:15 2023 +0200
ci, fix: unify earthly config
commit 7b89ea7ef4957a9925c4250c662bdd4d204b53dd
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 19:59:37 2023 +0200
ci: publishing docker, appimage, nightlies
commit f4f94d98644c7cc1506103538a30db241108b589
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 18:19:00 2023 +0200
ci: add lint & test step
commit be180ed59b216cfc85953ac5841eb11933a3a000
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:13:03 2023 +0200
ci, wip: earthly integration
commit 39db638cbdaaf5a436de8d531da2f68f85264430
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:12:21 2023 +0200
ci: use `upend --version` for AppImage, move get_version.sh logic to cli
commit 5188336c7eca877c0174ffa655893b287bc97baa
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 12:30:47 2023 +0200
ci: refix AppImage, switch to appimage-builder, build docker
commit 27f7941020bcf2e103d1e476038a6bc65f4153ea
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:55:03 2023 +0200
wip: remote woodpecker CI config for the time being
commit 53e775b85d2408060b034c2eff76e81b30b3dae5
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:59 2023 +0200
wip: delete .env
it's interpreted by Earthly and I'm not sure it's necessary anyway
commit 26bec328036c03dcb9a1917f9cf531bf7d10bb7d
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:32 2023 +0200
wip: initial somewhat functional Earthfile
2023-08-23 12:13:24 +02:00
|
|
|
#[command(name = "upend", author)]
|
2023-04-25 19:27:31 +02:00
|
|
|
struct Cli {
|
|
|
|
#[command(subcommand)]
|
|
|
|
command: Commands,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Subcommand)]
|
|
|
|
enum Commands {
|
|
|
|
/// Perform a query against an UpEnd server instance.
|
|
|
|
Query {
|
|
|
|
/// URL of the UpEnd instance to query.
|
2023-04-25 19:33:57 +02:00
|
|
|
#[arg(short, long, default_value = "http://localhost:8093")]
|
2023-04-25 19:27:31 +02:00
|
|
|
url: Url,
|
2023-05-04 19:47:45 +02:00
|
|
|
/// The query itself, in L-expression format; prefix a filepath by `@=` to insert its hash in its place.
|
2023-04-25 19:27:31 +02:00
|
|
|
query: String,
|
|
|
|
/// Output format
|
|
|
|
#[arg(short, long, default_value = "tsv")]
|
|
|
|
format: OutputFormat,
|
2024-06-11 17:28:39 +02:00
|
|
|
/// Credentials
|
|
|
|
#[arg(short, long)]
|
|
|
|
credentials: Credentials,
|
2023-04-25 19:27:31 +02:00
|
|
|
},
|
2023-06-06 19:01:38 +02:00
|
|
|
Get {
|
|
|
|
/// URL of the UpEnd instance to query.
|
|
|
|
#[arg(short, long, default_value = "http://localhost:8093")]
|
|
|
|
url: Url,
|
|
|
|
/// The address of the entity; prefix a filepath by `=` to insert its hash.
|
|
|
|
entity: String,
|
|
|
|
/// The attribute to get the value(s) of. Optional.
|
|
|
|
attribute: Option<String>,
|
|
|
|
/// Output format
|
|
|
|
#[arg(short, long, default_value = "tsv")]
|
|
|
|
format: OutputFormat,
|
2024-06-11 17:28:39 +02:00
|
|
|
/// Credentials
|
|
|
|
#[arg(short, long)]
|
|
|
|
credentials: Credentials,
|
2023-06-06 19:01:38 +02:00
|
|
|
},
|
2023-05-04 19:47:45 +02:00
|
|
|
/// Insert an entry into an UpEnd server instance.
|
2023-04-25 19:27:31 +02:00
|
|
|
Insert {
|
|
|
|
/// URL of the UpEnd instance to query.
|
2023-06-06 19:01:38 +02:00
|
|
|
#[arg(short, long, default_value = "http://localhost:8093")]
|
|
|
|
url: Url,
|
|
|
|
/// The address of the entity; prefix a filepath by `=` to insert its hash.
|
2023-04-25 19:27:31 +02:00
|
|
|
entity: String,
|
2023-05-04 19:47:45 +02:00
|
|
|
/// The attribute of the entry.
|
2023-04-25 19:27:31 +02:00
|
|
|
attribute: String,
|
2024-03-27 19:23:35 +01:00
|
|
|
/// The value; its type will be heuristically determined.
|
2023-05-04 18:54:17 +02:00
|
|
|
value: String,
|
2023-04-25 19:27:31 +02:00
|
|
|
/// Output format
|
|
|
|
#[arg(short, long, default_value = "tsv")]
|
|
|
|
format: OutputFormat,
|
2024-06-11 17:28:39 +02:00
|
|
|
/// Credentials
|
|
|
|
#[arg(short, long)]
|
|
|
|
credentials: Credentials,
|
|
|
|
},
|
|
|
|
/// Get authorization token from an UpEnd server instance.
|
|
|
|
Authenticate {
|
|
|
|
/// URL of the UpEnd instance to query.
|
|
|
|
#[arg(short, long, default_value = "http://localhost:8093")]
|
|
|
|
url: Url,
|
|
|
|
/// Credentials
|
|
|
|
credentials: Credentials,
|
2023-04-25 19:27:31 +02:00
|
|
|
},
|
2023-05-04 19:47:45 +02:00
|
|
|
/// Get the address of a file, attribute, or URL.
|
2023-04-25 19:27:31 +02:00
|
|
|
Address {
|
|
|
|
/// Type of input to be addressed
|
|
|
|
_type: AddressType,
|
|
|
|
/// Path to a file, hash...
|
|
|
|
input: String,
|
|
|
|
/// Output format
|
|
|
|
#[arg(short, long, default_value = "tsv")]
|
|
|
|
format: OutputFormat,
|
|
|
|
},
|
|
|
|
/// Start an UpEnd server instance.
|
|
|
|
Serve(ServeArgs),
|
|
|
|
}
|
|
|
|
|
2024-06-11 17:28:39 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
enum Credentials {
|
|
|
|
/// Use an API token to authenticate.
|
|
|
|
Token(String),
|
|
|
|
/// Use a username and password to authenticate.
|
|
|
|
Password(String, String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for Credentials {
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
|
|
let parts: Vec<&str> = s.split(':').collect();
|
|
|
|
match parts.len() {
|
|
|
|
1 => Ok(Credentials::Token(parts[0].to_string())),
|
|
|
|
2 => Ok(Credentials::Password(
|
|
|
|
parts[0].to_string(),
|
|
|
|
parts[1].to_string(),
|
|
|
|
)),
|
|
|
|
_ => Err(anyhow::anyhow!("Invalid credentials format.")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, ValueEnum)]
|
|
|
|
enum OutputFormat {
|
2023-05-04 19:47:45 +02:00
|
|
|
/// JSON
|
2023-04-25 19:27:31 +02:00
|
|
|
Json,
|
2023-05-04 19:47:45 +02:00
|
|
|
/// Tab Separated Values
|
2023-04-25 19:27:31 +02:00
|
|
|
Tsv,
|
2023-05-04 19:47:45 +02:00
|
|
|
/// Raw, as received from the server
|
2023-04-25 19:27:31 +02:00
|
|
|
Raw,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, ValueEnum)]
|
|
|
|
enum AddressType {
|
|
|
|
/// Hash a file and output its address.
|
|
|
|
File,
|
|
|
|
/// Compute an address from the output of `sha256sum`
|
|
|
|
Sha256sum,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Args)]
|
|
|
|
struct ServeArgs {
|
2023-04-24 23:00:21 +02:00
|
|
|
/// Directory to serve a vault from.
|
|
|
|
#[arg()]
|
|
|
|
directory: PathBuf,
|
|
|
|
|
|
|
|
/// Address and port to bind the Web interface on.
|
|
|
|
#[arg(long, default_value = "127.0.0.1:8093")]
|
|
|
|
bind: String,
|
|
|
|
|
|
|
|
/// Path to blob store ($VAULT_PATH by default).
|
|
|
|
#[arg(long)]
|
|
|
|
store_path: Option<PathBuf>,
|
|
|
|
|
|
|
|
/// Do not open a web browser with the UI.
|
|
|
|
#[arg(long)]
|
|
|
|
no_browser: bool,
|
|
|
|
|
|
|
|
/// Disable desktop features (web browser, native file opening).
|
2023-08-23 17:16:38 +02:00
|
|
|
#[arg(long, env = "UPEND_NO_DESKTOP")]
|
2023-04-24 23:00:21 +02:00
|
|
|
no_desktop: bool,
|
|
|
|
|
|
|
|
/// Trust the vault, and open local executable files.
|
|
|
|
#[arg(long)]
|
|
|
|
trust_executables: bool,
|
|
|
|
|
|
|
|
/// Do not serve the web UI.
|
|
|
|
#[arg(long)]
|
|
|
|
no_ui: bool,
|
|
|
|
|
|
|
|
/// Do not run a database update on start.
|
|
|
|
#[arg(long)]
|
|
|
|
no_initial_update: bool,
|
|
|
|
|
2023-11-17 17:21:26 +01:00
|
|
|
/// Which mode to use for rescanning the vault.
|
|
|
|
#[arg(long)]
|
|
|
|
rescan_mode: Option<BlobMode>,
|
|
|
|
|
2023-04-24 23:00:21 +02:00
|
|
|
/// Clean up temporary files (e.g. previews) on start.
|
|
|
|
#[arg(long)]
|
|
|
|
clean: bool,
|
|
|
|
|
|
|
|
/// Delete and initialize database, if it exists already.
|
|
|
|
#[arg(long)]
|
|
|
|
reinitialize: bool,
|
|
|
|
|
|
|
|
/// Name of the vault.
|
2023-08-26 09:48:15 +02:00
|
|
|
#[arg(long, env = "UPEND_VAULT_NAME")]
|
2023-04-24 23:00:21 +02:00
|
|
|
vault_name: Option<String>,
|
|
|
|
|
|
|
|
/// Secret to use for authentication.
|
|
|
|
#[arg(long, env = "UPEND_SECRET")]
|
|
|
|
secret: Option<String>,
|
|
|
|
|
|
|
|
/// Allowed host/domain name the API can serve.
|
2023-08-23 22:00:34 +02:00
|
|
|
#[arg(long, env = "UPEND_ALLOW_HOST")]
|
2023-04-24 23:00:21 +02:00
|
|
|
allow_host: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2023-05-22 19:29:08 +02:00
|
|
|
#[actix_web::main]
|
|
|
|
async fn main() -> Result<()> {
|
2023-10-22 21:18:00 +02:00
|
|
|
let command = Cli::command().version(crate::common::get_version());
|
ci: switch to Earthly
Squashed commit of the following:
commit 06baa23fc82f1f723bbfb1ab69c97802d28efa19
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:10:19 2023 +0200
ci, fix: forgot push
commit 6494be49d282368dd7c4aa78f56ccb33acac3eaa
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 11:01:14 2023 +0200
fix, ci: docker tag arg
commit 38682ba930abfeec5bf7facffbd0b94f6d61af9e
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:54:45 2023 +0200
ci: parallelize push steps
commit 5eeab18aa0d3fa3a7ef88e71cb5e9b5be5e7b9df
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:52:37 2023 +0200
ci, fix: docker login
commit ce10d0d04a55282cb4d95136e695705f95f11a86
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:52 2023 +0200
ci: remove earthly verbose
commit ff9b84296868bd18f004bdfcc23832acadeac388
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:41:23 2023 +0200
ci, fix: typo
commit df80ee061006c5eaa9b729a69818fd8bd273865c
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:06:47 2023 +0200
ci, refactor: better step names
commit 80093f8964cfd4cfa9e8b5ce4b6854c5c48060e4
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:05:03 2023 +0200
ci, fix: earthly config for publish:appimage step
commit 650824df99495afcc3feb4fcaa3e336b691d3008
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 10:04:50 2023 +0200
ci, refactor: only explicitly copy AppImages in sign target
commit 3b53e2dc6475e55f64fa9d00a2f2d31f07a9d8bb
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 08:01:43 2023 +0200
ci: EARTHLY_VERBOSE=1
commit cec95ea29a4f207fe1b7790b70562b1eeabee195
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:10:09 2023 +0200
ci: earthly bootstrap after conf
commit 7afe653d575a8ff76488d8fe3a93bc5e679124e5
Author: Tomáš Mládek <t@mldk.cz>
Date: Wed Aug 23 07:04:08 2023 +0200
ci, fix: remove ssh_key secret
commit b549d891ed789743066d3c29e12b6b82fcab35a3
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 22:02:01 2023 +0200
ci, fix: missing gpg-agent
commit 47938c71474ec39b0d989742f9e1446b6df94bca
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 20:55:15 2023 +0200
ci, fix: unify earthly config
commit 7b89ea7ef4957a9925c4250c662bdd4d204b53dd
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 19:59:37 2023 +0200
ci: publishing docker, appimage, nightlies
commit f4f94d98644c7cc1506103538a30db241108b589
Author: Tomáš Mládek <t@mldk.cz>
Date: Tue Aug 22 18:19:00 2023 +0200
ci: add lint & test step
commit be180ed59b216cfc85953ac5841eb11933a3a000
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:13:03 2023 +0200
ci, wip: earthly integration
commit 39db638cbdaaf5a436de8d531da2f68f85264430
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 16:12:21 2023 +0200
ci: use `upend --version` for AppImage, move get_version.sh logic to cli
commit 5188336c7eca877c0174ffa655893b287bc97baa
Author: Tomáš Mládek <t@mldk.cz>
Date: Mon Aug 21 12:30:47 2023 +0200
ci: refix AppImage, switch to appimage-builder, build docker
commit 27f7941020bcf2e103d1e476038a6bc65f4153ea
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:55:03 2023 +0200
wip: remote woodpecker CI config for the time being
commit 53e775b85d2408060b034c2eff76e81b30b3dae5
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:59 2023 +0200
wip: delete .env
it's interpreted by Earthly and I'm not sure it's necessary anyway
commit 26bec328036c03dcb9a1917f9cf531bf7d10bb7d
Author: Tomáš Mládek <t@mldk.cz>
Date: Sat Aug 19 18:47:32 2023 +0200
wip: initial somewhat functional Earthfile
2023-08-23 12:13:24 +02:00
|
|
|
let args = Cli::from_arg_matches(&command.get_matches())?;
|
2023-04-24 23:00:21 +02:00
|
|
|
|
2022-08-07 12:13:12 +02:00
|
|
|
tracing_subscriber::fmt()
|
|
|
|
.with_env_filter(
|
|
|
|
EnvFilter::builder()
|
|
|
|
.with_default_directive(LevelFilter::INFO.into())
|
|
|
|
.from_env_lossy(),
|
|
|
|
)
|
|
|
|
.init();
|
2020-08-27 00:11:50 +02:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
match args.command {
|
2024-06-11 17:28:39 +02:00
|
|
|
Commands::Query {
|
|
|
|
url,
|
|
|
|
query,
|
|
|
|
format,
|
|
|
|
credentials,
|
|
|
|
} => {
|
|
|
|
let api_token = get_api_token(&url, credentials).await?;
|
2023-05-04 19:16:01 +02:00
|
|
|
let re = Regex::new(r#"@(="([^"]+)"|=([^ ]+))"#).unwrap();
|
|
|
|
|
|
|
|
let query = re
|
|
|
|
.replace_all(&query, |caps: &Captures| {
|
|
|
|
if let Some(filepath_match) = caps.get(2).or_else(|| caps.get(3)) {
|
|
|
|
let address = hash_path(filepath_match.as_str()).unwrap();
|
|
|
|
format!("@{}", address)
|
|
|
|
} else {
|
|
|
|
panic!("Error preprocessing query. Captures: {:?}", caps)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.to_string();
|
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
let api_url = url.join("/api/query")?;
|
|
|
|
|
2023-06-06 19:01:38 +02:00
|
|
|
debug!("Querying \"{}\": {}", api_url, query);
|
2023-06-06 19:15:02 +02:00
|
|
|
let response = REQWEST_ASYNC_CLIENT
|
|
|
|
.post(api_url)
|
2024-06-11 17:28:39 +02:00
|
|
|
.header("Authorization", format!("Bearer {}", api_token))
|
2023-06-06 19:15:02 +02:00
|
|
|
.body(query)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-06 19:01:38 +02:00
|
|
|
|
|
|
|
response.error_for_status_ref()?;
|
|
|
|
|
2023-06-06 19:15:02 +02:00
|
|
|
print_response_entries(response, format).await?;
|
2023-06-06 19:01:38 +02:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Commands::Get {
|
|
|
|
url,
|
|
|
|
entity,
|
|
|
|
attribute,
|
|
|
|
format,
|
2024-06-11 17:28:39 +02:00
|
|
|
credentials,
|
2023-06-06 19:01:38 +02:00
|
|
|
} => {
|
2023-06-06 20:20:31 +02:00
|
|
|
let response = if let Some(attribute) = attribute {
|
2024-06-11 17:28:39 +02:00
|
|
|
let api_token = get_api_token(&url, credentials).await?;
|
2023-06-06 20:20:31 +02:00
|
|
|
let api_url = url.join("/api/query")?;
|
2023-06-06 19:01:38 +02:00
|
|
|
|
2023-06-06 20:20:31 +02:00
|
|
|
let entity = match entity {
|
|
|
|
entity if entity.starts_with('=') => hash_path(&entity[1..])?.to_string(),
|
|
|
|
entity if entity.starts_with("http") => {
|
|
|
|
Address::Url(entity.parse()?).to_string()
|
|
|
|
}
|
|
|
|
_ => entity,
|
|
|
|
};
|
|
|
|
|
|
|
|
let query = format!("(matches @{} \"{}\" ?)", entity, attribute);
|
|
|
|
|
|
|
|
debug!("Querying \"{}\": {}", api_url, query);
|
|
|
|
REQWEST_ASYNC_CLIENT
|
|
|
|
.post(api_url)
|
2024-06-11 17:28:39 +02:00
|
|
|
.header("Authorization", format!("Bearer {}", api_token))
|
2023-06-06 20:20:31 +02:00
|
|
|
.body(query)
|
|
|
|
.send()
|
|
|
|
.await?
|
|
|
|
} else {
|
|
|
|
let entity = match entity {
|
|
|
|
entity if entity.starts_with('=') => hash_path(&entity[1..])?.to_string(),
|
|
|
|
_ => todo!("Only GETting blobs (files) is implemented."),
|
|
|
|
};
|
|
|
|
let api_url = url.join(&format!("/api/obj/{entity}"))?;
|
|
|
|
|
|
|
|
debug!("Getting object \"{}\" from {}", entity, api_url);
|
|
|
|
REQWEST_ASYNC_CLIENT.get(api_url).send().await?
|
2023-06-06 19:01:38 +02:00
|
|
|
};
|
|
|
|
|
2023-05-04 19:32:08 +02:00
|
|
|
response.error_for_status_ref()?;
|
|
|
|
|
2023-06-06 19:15:02 +02:00
|
|
|
print_response_entries(response, format).await?;
|
2023-05-04 19:32:08 +02:00
|
|
|
|
|
|
|
Ok(())
|
2023-04-25 19:27:31 +02:00
|
|
|
}
|
|
|
|
Commands::Insert {
|
|
|
|
url,
|
|
|
|
entity,
|
|
|
|
attribute,
|
|
|
|
value,
|
|
|
|
format: _,
|
2024-06-11 17:28:39 +02:00
|
|
|
credentials,
|
2023-04-25 19:27:31 +02:00
|
|
|
} => {
|
2024-06-11 17:28:39 +02:00
|
|
|
let api_token = get_api_token(&url, credentials).await?;
|
2023-04-25 19:27:31 +02:00
|
|
|
let api_url = url.join("/api/obj")?;
|
|
|
|
|
2023-05-04 17:39:05 +02:00
|
|
|
let entity = match entity {
|
2023-05-04 18:48:00 +02:00
|
|
|
entity if entity.starts_with('=') => hash_path(&entity[1..])?.to_string(),
|
2023-05-19 17:30:09 +02:00
|
|
|
entity if entity.starts_with("http") => Address::Url(entity.parse()?).to_string(),
|
2023-05-04 17:39:05 +02:00
|
|
|
_ => entity,
|
|
|
|
};
|
|
|
|
|
2023-05-04 18:54:17 +02:00
|
|
|
let value = EntryValue::guess_from(value);
|
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
let body = json!({
|
|
|
|
"entity": entity,
|
|
|
|
"attribute": attribute,
|
|
|
|
"value": value
|
|
|
|
});
|
2021-02-20 17:36:19 +01:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
debug!("Inserting {:?} at \"{}\"", body, api_url);
|
2024-06-11 17:28:39 +02:00
|
|
|
let response = REQWEST_ASYNC_CLIENT
|
|
|
|
.put(api_url)
|
|
|
|
.header("Authorization", format!("Bearer {}", api_token))
|
|
|
|
.json(&body)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2020-08-27 00:11:50 +02:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
match response.error_for_status_ref() {
|
|
|
|
Ok(_) => {
|
2023-06-06 19:01:38 +02:00
|
|
|
let data: Vec<String> = response.json().await?;
|
2023-04-25 19:27:31 +02:00
|
|
|
Ok(println!("{}", data[0]))
|
|
|
|
}
|
|
|
|
Err(err) => {
|
2023-06-06 19:01:38 +02:00
|
|
|
error!("{}", response.text().await?);
|
2023-04-25 19:33:57 +02:00
|
|
|
Err(err.into())
|
2023-04-25 19:27:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-11 17:28:39 +02:00
|
|
|
Commands::Authenticate { url, credentials } => match credentials {
|
|
|
|
Credentials::Token(_) => {
|
|
|
|
Err(anyhow!("Please specify a username and password for authentication in the format `username:password`."))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let api_token = get_api_token(&url, credentials).await?;
|
|
|
|
println!("{}", api_token);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
},
|
2023-04-25 19:27:31 +02:00
|
|
|
Commands::Address {
|
|
|
|
_type,
|
|
|
|
input,
|
|
|
|
format,
|
|
|
|
} => {
|
|
|
|
let address = match _type {
|
2023-05-04 18:48:00 +02:00
|
|
|
AddressType::File => hash_path(&input)?,
|
2023-04-25 19:27:31 +02:00
|
|
|
AddressType::Sha256sum => {
|
|
|
|
let digest = multibase::Base::Base16Lower.decode(input)?;
|
2023-06-29 15:17:06 +02:00
|
|
|
Address::Hash(UpMultihash::from_sha256(digest).unwrap())
|
2023-04-25 19:27:31 +02:00
|
|
|
}
|
|
|
|
};
|
2020-08-27 00:11:50 +02:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
match format {
|
|
|
|
OutputFormat::Json => Ok(println!("\"{}\"", address)),
|
|
|
|
OutputFormat::Tsv | OutputFormat::Raw => Ok(println!("{}", address)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Commands::Serve(args) => {
|
2023-06-25 15:29:52 +02:00
|
|
|
info!("Starting UpEnd {}...", common::build::PKG_VERSION);
|
2020-08-27 00:11:50 +02:00
|
|
|
|
2023-06-08 19:38:56 +02:00
|
|
|
let term_now = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
|
|
|
for sig in signal_hook::consts::TERM_SIGNALS {
|
|
|
|
signal_hook::flag::register_conditional_shutdown(*sig, 1, Arc::clone(&term_now))?;
|
|
|
|
signal_hook::flag::register(*sig, Arc::clone(&term_now))?;
|
|
|
|
}
|
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
let job_container = JobContainer::new();
|
2023-04-24 23:00:21 +02:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
let vault_path = args.directory;
|
2022-02-03 16:26:57 +01:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
let open_result = UpEndDatabase::open(&vault_path, args.reinitialize)
|
|
|
|
.expect("failed to open database!");
|
|
|
|
|
|
|
|
let upend = Arc::new(open_result.db);
|
|
|
|
let store = Arc::new(Box::new(
|
|
|
|
FsStore::from_path(args.store_path.unwrap_or_else(|| vault_path.clone())).unwrap(),
|
|
|
|
) as Box<dyn UpStore + Send + Sync>);
|
|
|
|
|
2024-01-26 22:41:43 +01:00
|
|
|
let webui_enabled = if args.no_ui {
|
|
|
|
false
|
2023-06-07 21:09:33 +02:00
|
|
|
} else {
|
2024-01-28 14:35:17 +01:00
|
|
|
let exists = WEBUI_PATH.exists();
|
2024-01-26 22:41:43 +01:00
|
|
|
if !exists {
|
2023-06-07 21:09:33 +02:00
|
|
|
warn!(
|
|
|
|
"Couldn't locate Web UI directory ({:?}), disabling...",
|
2024-03-30 16:45:04 +01:00
|
|
|
*WEBUI_PATH
|
2023-06-07 21:09:33 +02:00
|
|
|
);
|
|
|
|
}
|
2024-01-26 22:41:43 +01:00
|
|
|
exists
|
2023-06-07 21:09:33 +02:00
|
|
|
};
|
2022-10-22 12:50:52 +02:00
|
|
|
|
2024-01-26 22:41:43 +01:00
|
|
|
let browser_enabled = !args.no_desktop && webui_enabled && !args.no_browser;
|
2023-04-25 19:27:31 +02:00
|
|
|
|
|
|
|
let preview_path = upend.path.join("previews");
|
|
|
|
#[cfg(feature = "previews")]
|
|
|
|
let preview_store = Some(Arc::new(crate::previews::PreviewStore::new(
|
|
|
|
preview_path.clone(),
|
|
|
|
store.clone(),
|
|
|
|
)));
|
|
|
|
#[cfg(feature = "previews")]
|
2023-06-07 21:09:33 +02:00
|
|
|
let preview_thread_pool = Some(Arc::new(
|
2023-04-25 19:27:31 +02:00
|
|
|
rayon::ThreadPoolBuilder::new()
|
|
|
|
.num_threads(num_cpus::get() / 2)
|
|
|
|
.build()
|
|
|
|
.unwrap(),
|
|
|
|
));
|
|
|
|
|
|
|
|
if args.clean {
|
|
|
|
info!("Cleaning temporary directories...");
|
|
|
|
if preview_path.exists() {
|
|
|
|
std::fs::remove_dir_all(&preview_path).unwrap();
|
|
|
|
debug!("Removed {preview_path:?}");
|
|
|
|
} else {
|
|
|
|
debug!("No preview path exists, continuing...");
|
|
|
|
}
|
|
|
|
}
|
2021-12-27 11:58:01 +01:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
#[cfg(not(feature = "previews"))]
|
|
|
|
let preview_store = None;
|
|
|
|
#[cfg(not(feature = "previews"))]
|
2023-06-07 21:09:33 +02:00
|
|
|
let preview_thread_pool = None;
|
2023-04-25 19:27:31 +02:00
|
|
|
|
|
|
|
let mut bind: SocketAddr = args.bind.parse().expect("Incorrect bind format.");
|
|
|
|
|
|
|
|
let secret = args.secret.unwrap_or_else(|| {
|
|
|
|
warn!("No secret supplied, generating one at random.");
|
|
|
|
|
|
|
|
thread_rng()
|
|
|
|
.sample_iter(&rand::distributions::Alphanumeric)
|
|
|
|
.take(32)
|
|
|
|
.map(char::from)
|
|
|
|
.collect()
|
|
|
|
});
|
|
|
|
|
|
|
|
let state = routes::State {
|
|
|
|
upend: upend.clone(),
|
|
|
|
store,
|
|
|
|
job_container: job_container.clone(),
|
|
|
|
preview_store,
|
2023-06-07 21:09:33 +02:00
|
|
|
preview_thread_pool,
|
2023-04-25 19:27:31 +02:00
|
|
|
config: UpEndConfig {
|
|
|
|
vault_name: Some(args.vault_name.unwrap_or_else(|| {
|
|
|
|
vault_path
|
|
|
|
.iter()
|
|
|
|
.last()
|
|
|
|
.unwrap()
|
|
|
|
.to_string_lossy()
|
|
|
|
.into_owned()
|
|
|
|
})),
|
|
|
|
desktop_enabled: !args.no_desktop,
|
|
|
|
trust_executables: args.trust_executables,
|
|
|
|
secret,
|
|
|
|
},
|
2024-03-30 16:35:21 +01:00
|
|
|
public: Arc::new(Mutex::new(upend.connection()?.get_users()?.is_empty())),
|
2023-04-25 19:27:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// Start HTTP server
|
|
|
|
|
|
|
|
let mut cnt = 0;
|
|
|
|
let server = loop {
|
|
|
|
let state = state.clone();
|
|
|
|
let allowed_origins = args.allow_host.clone();
|
|
|
|
|
|
|
|
let server = HttpServer::new(move || {
|
2024-01-26 22:41:43 +01:00
|
|
|
serve::get_app(webui_enabled, allowed_origins.clone(), state.clone())
|
2023-04-25 19:27:31 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
let bind_result = server.bind(&bind);
|
|
|
|
if let Ok(server) = bind_result {
|
|
|
|
break server;
|
|
|
|
} else {
|
|
|
|
warn!("Failed to bind at {:?}, trying next port number...", bind);
|
|
|
|
bind.set_port(bind.port() + 1);
|
|
|
|
}
|
2021-12-27 11:58:01 +01:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
if cnt > 32 {
|
|
|
|
panic!("Couldn't start server.")
|
|
|
|
} else {
|
|
|
|
cnt += 1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-01-17 23:48:48 +01:00
|
|
|
if !args.no_initial_update && (!open_result.new || args.rescan_mode.is_some()) {
|
|
|
|
info!("Running update...");
|
|
|
|
block_background::<_, _, anyhow::Error>(move || {
|
|
|
|
let connection: upend_db::UpEndConnection = upend.connection()?;
|
|
|
|
|
2024-03-02 17:55:18 +01:00
|
|
|
let tree_mode = if let Some(rescan_mode) = args.rescan_mode {
|
|
|
|
connection.set_vault_options(upend_db::VaultOptions {
|
|
|
|
blob_mode: Some(rescan_mode.clone()),
|
|
|
|
})?;
|
|
|
|
rescan_mode
|
|
|
|
} else {
|
2024-01-17 23:48:48 +01:00
|
|
|
connection
|
|
|
|
.get_vault_options()
|
|
|
|
.unwrap()
|
|
|
|
.blob_mode
|
|
|
|
.unwrap_or_default()
|
2024-03-02 17:55:18 +01:00
|
|
|
};
|
2024-01-17 23:48:48 +01:00
|
|
|
|
|
|
|
let _ = state.store.update(
|
|
|
|
&upend,
|
|
|
|
job_container.clone(),
|
|
|
|
upend_db::stores::UpdateOptions {
|
|
|
|
initial: false,
|
|
|
|
tree_mode,
|
|
|
|
},
|
2024-03-31 00:31:32 +01:00
|
|
|
OperationContext::default(),
|
|
|
|
);
|
|
|
|
let _ = extractors::extract_all(
|
|
|
|
upend,
|
|
|
|
state.store,
|
|
|
|
job_container,
|
|
|
|
OperationContext::default(),
|
2024-01-17 23:48:48 +01:00
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
});
|
2023-04-25 19:27:31 +02:00
|
|
|
}
|
2020-08-30 22:11:32 +02:00
|
|
|
|
2023-04-25 19:27:31 +02:00
|
|
|
#[cfg(feature = "desktop")]
|
|
|
|
{
|
2024-01-26 22:41:43 +01:00
|
|
|
if browser_enabled {
|
2023-04-25 19:27:31 +02:00
|
|
|
let ui_result = webbrowser::open(&format!("http://localhost:{}", bind.port()));
|
|
|
|
if ui_result.is_err() {
|
|
|
|
warn!("Could not open UI in browser!");
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 20:09:44 +01:00
|
|
|
}
|
2023-04-25 19:27:31 +02:00
|
|
|
|
2023-05-22 19:29:08 +02:00
|
|
|
info!("Starting server at: {}", &bind);
|
|
|
|
server.run().await?;
|
|
|
|
Ok(())
|
2020-08-30 16:45:42 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-27 00:11:50 +02:00
|
|
|
}
|
2023-05-04 18:48:00 +02:00
|
|
|
|
2024-06-11 17:28:39 +02:00
|
|
|
async fn get_api_token(url: &Url, credentials: Credentials) -> Result<String> {
|
|
|
|
match credentials {
|
|
|
|
Credentials::Token(token) => Ok(token),
|
|
|
|
Credentials::Password(username, password) => {
|
|
|
|
debug!("Logging in as {}...", username);
|
|
|
|
let api_url = url.join("/api/auth/login?via=token").unwrap();
|
|
|
|
let body = json!({
|
|
|
|
"username": username,
|
|
|
|
"password": password
|
|
|
|
});
|
|
|
|
|
|
|
|
let response = REQWEST_ASYNC_CLIENT
|
|
|
|
.post(api_url)
|
|
|
|
.json(&body)
|
|
|
|
.send()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
response.error_for_status_ref()?;
|
|
|
|
|
|
|
|
let data: Value = response.json().await?;
|
|
|
|
data.get("key")
|
|
|
|
.and_then(|key| key.as_str())
|
|
|
|
.map(|key| key.to_string())
|
|
|
|
.ok_or_else(|| anyhow::anyhow!("No API token in response."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:20:31 +02:00
|
|
|
type Entries = HashMap<String, serde_json::Value>;
|
|
|
|
|
2023-06-06 19:15:02 +02:00
|
|
|
async fn print_response_entries(response: reqwest::Response, format: OutputFormat) -> Result<()> {
|
|
|
|
match format {
|
|
|
|
OutputFormat::Json | OutputFormat::Raw => println!("{}", response.text().await?),
|
|
|
|
OutputFormat::Tsv => {
|
2023-06-06 20:20:31 +02:00
|
|
|
let mut entries = if response.url().path().contains("/obj/") {
|
|
|
|
#[derive(serde::Deserialize)]
|
|
|
|
struct ObjResponse {
|
|
|
|
entries: Entries,
|
|
|
|
}
|
|
|
|
response.json::<ObjResponse>().await?.entries
|
|
|
|
} else {
|
|
|
|
response.json::<Entries>().await?
|
|
|
|
}
|
|
|
|
.into_iter()
|
|
|
|
.peekable();
|
2023-06-06 19:15:02 +02:00
|
|
|
|
|
|
|
if entries.peek().is_some() {
|
|
|
|
eprintln!("entity\tattribute\tvalue\ttimestamp\tprovenance");
|
|
|
|
entries.for_each(|(_, entry)| {
|
|
|
|
println!(
|
|
|
|
"{}\t{}\t{}\t{}\t{}",
|
2023-06-06 20:23:20 +02:00
|
|
|
entry
|
|
|
|
.get("entity")
|
|
|
|
.and_then(|e| e.as_str())
|
|
|
|
.unwrap_or("???"),
|
|
|
|
entry
|
|
|
|
.get("attribute")
|
|
|
|
.and_then(|a| a.as_str())
|
|
|
|
.unwrap_or("???"),
|
|
|
|
entry
|
|
|
|
.get("value")
|
|
|
|
.and_then(|v| v.get("c"))
|
|
|
|
.map(|c| format!("{c}"))
|
|
|
|
.unwrap_or("???".to_string()),
|
|
|
|
entry
|
|
|
|
.get("timestamp")
|
|
|
|
.and_then(|t| t.as_str())
|
|
|
|
.unwrap_or("???"),
|
|
|
|
entry
|
|
|
|
.get("provenance")
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
.unwrap_or("???"),
|
2023-06-06 19:15:02 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-05-04 18:48:00 +02:00
|
|
|
fn hash_path<P: AsRef<Path>>(filepath: P) -> Result<Address> {
|
|
|
|
let filepath = filepath.as_ref();
|
|
|
|
debug!("Hashing {:?}...", filepath);
|
|
|
|
let fbuffer = FileBuffer::open(filepath)?;
|
2023-06-29 14:29:38 +02:00
|
|
|
let hash = sha256hash(&fbuffer)?;
|
2023-05-04 18:48:00 +02:00
|
|
|
trace!("Finished hashing {:?}...", filepath);
|
2023-06-29 14:29:38 +02:00
|
|
|
Ok(Address::Hash(hash))
|
2023-05-04 18:48:00 +02:00
|
|
|
}
|