Compare commits

...

2 commits

Author SHA1 Message Date
9436e2afe9 notify a share 2025-05-22 21:07:49 +00:00
08f27b3644 creating empty share works 2025-05-22 17:34:44 +00:00
7 changed files with 1222 additions and 1 deletions

3
.vscode/launch.json vendored
View file

@ -20,6 +20,9 @@
}
},
"args": [],
"env": {
"RUST_LOG": "shrupl=debug",
},
"cwd": "${workspaceFolder}"
},
// {

1014
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
env_logger = "0.11.8"
log = "0.4.27"
serde = { version = "1.0.219", features = ["derive"] }
ureq = { version = "3.0.11", features = ["json"] }

37
doc/notes.md Normal file
View file

@ -0,0 +1,37 @@
outline of sharry uploading
1. POST to "new" route
- uri: https://sharry.yavook.de/api/v2/alias/upload/new
- hdr: `Sharry-Alias: $alias_id`
- res.status == 200
- res_json.success == true
- res_json.message == "Share created."
- $share_id := res_json.id
1. POST to "tus" route
- uri: https://sharry.yavook.de/api/v2/alias/upload/$share_id/files/tus
- hdr: `Sharry-Alias: $alias_id`
- hdr: `Sharry-File-Length: $file_bytes`
- hdr: `Sharry-File-Name: $file_name`
- hdr: `Sharry-File-Type: $file_mimetype`
- hdr: `Tus-Resumable: 1.0.0`
- hdr: `Upload-Length: $file_bytes`
- res.status == 201
- $patch_uri := res_hdr.Location
1. (multi) PATCH to "$patch_uri"
- hdr: `Sharry-Alias`, `Sharry-File-Length`, `Sharry-File-Name`, `Sharry-File-Type`, `Tus-Resumable`
- hdr: `Upload-Offset: 10485760 * (n-1)` for nth chunk
- body: up to 10 MiB of binary data
- res.status == 204
- better use res_hdr.Upload-Offset
1. (opt) POST to "notify" route
- uri: https://sharry.yavook.de/api/v2/alias/mail/notify/$share_id
- hdr: `Sharry-Alias`
- res.status == 200
- res_json.success, res_json.message
hints
- https://stackoverflow.com/questions/59586787/rust-how-to-do-http-put-of-large-files

View file

@ -1,3 +1,30 @@
mod sharry;
use log::info;
use sharry::{
Alias,
api::{NewShareRequest, URI},
};
use std::time::Duration;
use ureq::Agent;
fn main() {
println!("Hello, world!");
env_logger::init();
let agent: Agent = Agent::config_builder()
.timeout_global(Some(Duration::from_secs(5)))
.build()
.into();
let alias = Alias::new(
URI::new("sharry.yavook.de"),
"G7RYoWME1W7-pcgipemJcr8-39FcMd92gBu-RgufeHc51z6",
);
let share = alias.create_share(&agent, &NewShareRequest::new(Some("foo"), "", 10));
let share = share.unwrap();
info!("share: {:?}", share);
share.notify(&agent).unwrap();
}

64
src/sharry/api.rs Normal file
View file

@ -0,0 +1,64 @@
use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct URI {
protocol: String,
base_url: String,
}
impl URI {
pub fn new_protocol(base_url: &str, protocol: &str) -> Self {
Self {
protocol: protocol.into(),
base_url: base_url.into(),
}
}
pub fn new(base_url: &str) -> Self {
Self::new_protocol(base_url, "https")
}
pub fn get_endpoint(&self, endpoint: &str) -> String {
let uri = format!("{}://{}/api/v2/{}", self.protocol, self.base_url, endpoint);
debug!("uri: {:?}", uri);
uri
}
}
#[derive(Serialize)]
#[allow(non_snake_case)]
pub struct NewShareRequest<'t> {
name: Option<&'t str>,
validity: u32,
description: &'t str,
maxViews: u32,
password: Option<&'t str>,
}
impl<'t> NewShareRequest<'t> {
pub fn new(name: Option<&'t str>, description: &'t str, max_views: u32) -> Self {
Self {
name: name,
validity: 0,
description: description,
maxViews: max_views,
password: None,
}
}
}
#[derive(Deserialize, Debug)]
pub struct NewShareResponse {
pub success: bool,
pub message: String,
pub id: String,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
pub struct NotifyShareResponse {
pub success: bool,
pub message: String,
}

72
src/sharry/mod.rs Normal file
View file

@ -0,0 +1,72 @@
pub mod api;
use api::{NewShareRequest, NewShareResponse, NotifyShareResponse, URI};
use log::debug;
use ureq::{self, RequestBuilder};
#[derive(Debug)]
pub struct Alias {
uri: URI,
id: String,
}
impl Alias {
pub fn new(uri: URI, id: &str) -> Self {
Self { uri, id: id.into() }
}
fn add_headers<B>(&self, req: RequestBuilder<B>) -> RequestBuilder<B> {
req.header("Sharry-Alias", &self.id)
}
pub fn create_share(
&self,
http: &ureq::Agent,
data: &NewShareRequest,
) -> Result<Share, ureq::Error> {
let endpoint = self.uri.get_endpoint("alias/upload/new");
let res = self
.add_headers(http.post(endpoint))
.send_json(data)?
.body_mut()
.read_json::<NewShareResponse>()?;
debug!("response: {:?}", res);
assert!(res.success);
assert_eq!(res.message, "Share created.");
Ok(Share {
alias: self,
id: res.id,
})
}
}
#[derive(Debug)]
pub struct Share<'t> {
alias: &'t Alias,
id: String,
}
impl<'t> Share<'t> {
pub fn add_file(&self) {}
pub fn notify(&self, http: &ureq::Agent) -> Result<(), ureq::Error> {
let endpoint = self
.alias
.uri
.get_endpoint(&format!("alias/mail/notify/{}", &self.id));
let res = self
.alias
.add_headers(http.post(endpoint))
.send_empty()?
.body_mut()
.read_json::<NotifyShareResponse>()?;
debug!("response: {:?}", res);
Ok(())
}
}