- [后端/PDF解析] 重构 MinerU PDF 解析流程:引入预签名两阶段直传机制,解决大文件 API 传输限制问题;支持轮询机制与本地 images 备用目录存储。 - [后端/同步与下载] 新增经典 ADS SCAN 扫描件 PDF 和 ADS_PDF 直接通道的下载逻辑;新增常用同步检索配置的持久化存储与去重管理 API。 - [后端/日志] 重构日志系统,支持控制台 pretty 输出与每日滚动文件日志(使用上海 +08:00 时区),引入 HTTP 路由请求链路追踪。 - [前端/引力图] 升级引用星系图 canvas 交互:支持平移拖拽与滚轮缩放,添加引力圈轨道装饰及未导入文献的半透明视觉区分。 - [前端/控制台] 统一重构为扁平高对比度浅色纯中文控制台样式;重新设计文献详情弹窗与状态进度条。 - [数据库] 新增 papers 表的 doctype 字段及 sync_queries 检索配置表。
69 lines
2.4 KiB
Rust
69 lines
2.4 KiB
Rust
use sha1::Sha1;
|
|
use hmac::{Hmac, Mac};
|
|
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
|
use reqwest::multipart;
|
|
|
|
type HmacSha1 = Hmac<Sha1>;
|
|
|
|
fn generate_token(ak: &str, sk: &str, bucket: &str, key: &str, scope_key: bool) -> String {
|
|
let deadline = chrono::Utc::now().timestamp() + 3600;
|
|
|
|
let scope = if scope_key {
|
|
format!("{}:{}", bucket, key)
|
|
} else {
|
|
bucket.to_string()
|
|
};
|
|
|
|
let policy = serde_json::json!({
|
|
"scope": scope,
|
|
"deadline": deadline
|
|
});
|
|
|
|
let policy_str = policy.to_string();
|
|
let encoded_policy = URL_SAFE_NO_PAD.encode(policy_str.as_bytes());
|
|
|
|
let mut mac = HmacSha1::new_from_slice(sk.as_bytes()).unwrap();
|
|
mac.update(encoded_policy.as_bytes());
|
|
let signature = mac.finalize().into_bytes();
|
|
let encoded_signature = URL_SAFE_NO_PAD.encode(&signature);
|
|
|
|
format!("{}:{}:{}", ak, encoded_signature, encoded_policy)
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let ak = "vf63aPF-QIFbyzULtHaSx9JgiVSS3zRuy0zmBACE".to_string();
|
|
let sk = "JlQvHevHSAgilNYaH0UxQoX68rb4m9VflpaXtYL1".to_string();
|
|
let bucket = "fmqi-img".to_string();
|
|
let domain = "http://qnimg.asfmq.cn".to_string();
|
|
|
|
let client = reqwest::Client::new();
|
|
let dummy_data = b"hello world qiniu test".to_vec();
|
|
let filename = "test_hello.txt";
|
|
let key = "astroresearch/test_hello.txt";
|
|
|
|
println!("Testing with scoped key token (bucket:key)...");
|
|
let token1 = generate_token(&ak, &sk, &bucket, key, true);
|
|
let form1 = multipart::Form::new()
|
|
.text("token", token1)
|
|
.text("key", key)
|
|
.part("file", multipart::Part::bytes(dummy_data.clone()).file_name(filename));
|
|
|
|
let res1 = client.post("https://up-z1.qiniup.com").multipart(form1).send().await?;
|
|
println!("Status (scoped key): {}", res1.status());
|
|
println!("Response: {}", res1.text().await?);
|
|
|
|
println!("\nTesting with bucket-only scoped token...");
|
|
let token2 = generate_token(&ak, &sk, &bucket, key, false);
|
|
let form2 = multipart::Form::new()
|
|
.text("token", token2)
|
|
.text("key", key)
|
|
.part("file", multipart::Part::bytes(dummy_data).file_name(filename));
|
|
|
|
let res2 = client.post("https://up-z1.qiniup.com").multipart(form2).send().await?;
|
|
println!("Status (bucket-only): {}", res2.status());
|
|
println!("Response: {}", res2.text().await?);
|
|
|
|
Ok(())
|
|
}
|