Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
4b99f2028d | |||
e9e51d455b | |||
3252927df7 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1144,8 +1144,6 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "submissions-api"
|
name = "submissions-api"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
|
||||||
checksum = "72e67dbf479fc6a5e22514208d533534a2e0543eab7c6c1b8860ee3f6f0c6290"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
26
src/cmd/mod.rs
Normal file
26
src/cmd/mod.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use clap::{Args,Parser,Subcommand};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author,version,about,long_about=None)]
|
||||||
|
#[command(propagate_version=true)]
|
||||||
|
pub struct Cli{
|
||||||
|
#[command(subcommand)]
|
||||||
|
command:Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands{
|
||||||
|
Review(ReviewCommand),
|
||||||
|
UploadScripts(UploadScriptsCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct ReviewCommand{
|
||||||
|
#[arg(long)]
|
||||||
|
cookie:String,
|
||||||
|
}
|
||||||
|
#[derive(Args)]
|
||||||
|
struct UploadScriptsCommand{
|
||||||
|
#[arg(long)]
|
||||||
|
session_id:PathBuf,
|
||||||
|
}
|
260
src/main.rs
260
src/main.rs
@ -1,50 +1,16 @@
|
|||||||
use clap::{Args,Parser,Subcommand};
|
mod cmd;
|
||||||
|
|
||||||
|
use cmd::{Cli,Commands};
|
||||||
use futures::{StreamExt,TryStreamExt};
|
use futures::{StreamExt,TryStreamExt};
|
||||||
|
|
||||||
const READ_CONCURRENCY:usize=16;
|
|
||||||
const REMOTE_CONCURRENCY:usize=16;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(author,version,about,long_about=None)]
|
|
||||||
#[command(propagate_version=true)]
|
|
||||||
struct Cli{
|
|
||||||
#[command(subcommand)]
|
|
||||||
command:Commands,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum Commands{
|
|
||||||
Review(ReviewCommand),
|
|
||||||
UploadScripts(UploadScriptsCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args)]
|
|
||||||
struct ReviewCommand{
|
|
||||||
#[arg(long)]
|
|
||||||
session_id:String,
|
|
||||||
#[arg(long)]
|
|
||||||
api_url:String,
|
|
||||||
}
|
|
||||||
#[derive(Args)]
|
|
||||||
struct UploadScriptsCommand{
|
|
||||||
#[arg(long)]
|
|
||||||
session_id:String,
|
|
||||||
#[arg(long)]
|
|
||||||
api_url:String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main(){
|
async fn main(){
|
||||||
let cli=Cli::parse();
|
let cli=Cli::parse();
|
||||||
match cli.command{
|
match cli.command{
|
||||||
Commands::Review(command)=>review(ReviewConfig{
|
Commands::Review(command)=>review(ReviewConfig{
|
||||||
session_id:command.session_id,
|
cookie:command.cookie,
|
||||||
api_url:command.api_url,
|
|
||||||
}).await.unwrap(),
|
|
||||||
Commands::UploadScripts(command)=>upload_scripts(UploadConfig{
|
|
||||||
session_id:command.session_id,
|
|
||||||
api_url:command.api_url,
|
|
||||||
}).await.unwrap(),
|
}).await.unwrap(),
|
||||||
|
Commands::UploadScripts(command)=>upload_scripts(command.session_id).await.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,15 +51,14 @@ enum ReviewError{
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ReviewConfig{
|
struct ReviewConfig{
|
||||||
session_id:String,
|
cookie:String,
|
||||||
api_url:String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn review(config:ReviewConfig)->Result<(),ReviewError>{
|
async fn review(config:ReviewConfig)->Result<(),ReviewError>{
|
||||||
// download unreviewed policies
|
// download unreviewed policies
|
||||||
// review them
|
// review them
|
||||||
let cookie=submissions_api::Cookie::new(&config.session_id).map_err(ReviewError::Cookie)?;
|
let cookie=submissions_api::Cookie::new(&config.cookie).map_err(ReviewError::Cookie)?;
|
||||||
let api=submissions_api::external::Context::new(config.api_url,cookie).map_err(ReviewError::Reqwest)?;
|
let api=submissions_api::external::Context::new("http://localhost:8083".to_owned(),cookie).map_err(ReviewError::Reqwest)?;
|
||||||
|
|
||||||
let unreviewed_policies=api.get_script_policies(submissions_api::types::GetScriptPoliciesRequest{
|
let unreviewed_policies=api.get_script_policies(submissions_api::types::GetScriptPoliciesRequest{
|
||||||
Page:1,
|
Page:1,
|
||||||
@ -139,7 +104,9 @@ async fn review(config:ReviewConfig)->Result<(),ReviewError>{
|
|||||||
submissions_api::types::Policy::Allowed
|
submissions_api::types::Policy::Allowed
|
||||||
}else{
|
}else{
|
||||||
// compute hash
|
// compute hash
|
||||||
let hash=hash_source(source.as_str());
|
let mut hasher=siphasher::sip::SipHasher::new();
|
||||||
|
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
||||||
|
let hash=std::hash::Hasher::finish(&hasher);
|
||||||
|
|
||||||
// check if modified script already exists
|
// check if modified script already exists
|
||||||
let maybe_script_response=api.get_script_from_hash(submissions_api::types::HashRequest{
|
let maybe_script_response=api.get_script_from_hash(submissions_api::types::HashRequest{
|
||||||
@ -167,7 +134,7 @@ async fn review(config:ReviewConfig)->Result<(),ReviewError>{
|
|||||||
|
|
||||||
// update policy
|
// update policy
|
||||||
api.update_script_policy(submissions_api::types::UpdateScriptPolicyRequest{
|
api.update_script_policy(submissions_api::types::UpdateScriptPolicyRequest{
|
||||||
ID:unreviewed_policy.ID,
|
ScriptPolicyID:unreviewed_policy.ID,
|
||||||
FromScriptID:None,
|
FromScriptID:None,
|
||||||
ToScriptID:to_script_id,
|
ToScriptID:to_script_id,
|
||||||
Policy:Some(reviewed_policy),
|
Policy:Some(reviewed_policy),
|
||||||
@ -177,87 +144,6 @@ async fn review(config:ReviewConfig)->Result<(),ReviewError>{
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ScriptUploadError{
|
|
||||||
Cookie(submissions_api::CookieError),
|
|
||||||
Reqwest(submissions_api::ReqwestError),
|
|
||||||
AllowedSet(std::io::Error),
|
|
||||||
AllowedMap(GetMapError),
|
|
||||||
ReplaceMap(GetMapError),
|
|
||||||
BlockedSet(std::io::Error),
|
|
||||||
GOC(GOCError),
|
|
||||||
GOCPolicyReplace(GOCError),
|
|
||||||
GOCPolicyAllowed(GOCError),
|
|
||||||
GOCPolicyBlocked(GOCError),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_dir_stream(dir:tokio::fs::ReadDir)->impl futures::stream::Stream<Item=std::io::Result<tokio::fs::DirEntry>>{
|
|
||||||
futures::stream::unfold(dir,|mut dir|async{
|
|
||||||
match dir.next_entry().await{
|
|
||||||
Ok(Some(entry))=>Some((Ok(entry),dir)),
|
|
||||||
Ok(None)=>None, // End of directory
|
|
||||||
Err(e)=>Some((Err(e),dir)), // Error encountered
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_set_from_file(path:impl AsRef<std::path::Path>)->std::io::Result<std::collections::HashSet<String>>{
|
|
||||||
read_dir_stream(tokio::fs::read_dir(path).await?)
|
|
||||||
.map(|dir_entry|async{
|
|
||||||
tokio::fs::read_to_string(dir_entry?.path()).await
|
|
||||||
})
|
|
||||||
.buffer_unordered(READ_CONCURRENCY)
|
|
||||||
.try_collect().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_allowed_set()->std::io::Result<std::collections::HashSet<String>>{
|
|
||||||
get_set_from_file("scripts/allowed").await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_blocked_set()->std::io::Result<std::collections::HashSet<String>>{
|
|
||||||
get_set_from_file("scripts/blocked").await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum GetMapError{
|
|
||||||
IO(std::io::Error),
|
|
||||||
FileStem,
|
|
||||||
ToStr,
|
|
||||||
ParseInt(std::num::ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_allowed_map()->Result<std::collections::HashMap::<u32,String>,GetMapError>{
|
|
||||||
read_dir_stream(tokio::fs::read_dir("scripts/allowed").await.map_err(GetMapError::IO)?)
|
|
||||||
.map(|dir_entry|async{
|
|
||||||
let path=dir_entry.map_err(GetMapError::IO)?.path();
|
|
||||||
let id:u32=path
|
|
||||||
.file_stem().ok_or(GetMapError::FileStem)?
|
|
||||||
.to_str().ok_or(GetMapError::ToStr)?
|
|
||||||
.parse().map_err(GetMapError::ParseInt)?;
|
|
||||||
let source=tokio::fs::read_to_string(path).await.map_err(GetMapError::IO)?;
|
|
||||||
Ok((id,source))
|
|
||||||
})
|
|
||||||
.buffer_unordered(READ_CONCURRENCY)
|
|
||||||
.try_collect().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_replace_map()->Result<std::collections::HashMap::<String,u32>,GetMapError>{
|
|
||||||
read_dir_stream(tokio::fs::read_dir("scripts/replace").await.map_err(GetMapError::IO)?)
|
|
||||||
.map(|dir_entry|async{
|
|
||||||
let path=dir_entry.map_err(GetMapError::IO)?.path();
|
|
||||||
let id:u32=path
|
|
||||||
.file_stem().ok_or(GetMapError::FileStem)?
|
|
||||||
.to_str().ok_or(GetMapError::ToStr)?
|
|
||||||
.parse().map_err(GetMapError::ParseInt)?;
|
|
||||||
let source=tokio::fs::read_to_string(path).await.map_err(GetMapError::IO)?;
|
|
||||||
Ok((source,id))
|
|
||||||
})
|
|
||||||
.buffer_unordered(READ_CONCURRENCY)
|
|
||||||
.try_collect().await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_source(source:&str)->u64{
|
fn hash_source(source:&str)->u64{
|
||||||
let mut hasher=siphasher::sip::SipHasher::new();
|
let mut hasher=siphasher::sip::SipHasher::new();
|
||||||
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
std::hash::Hasher::write(&mut hasher,source.as_bytes());
|
||||||
@ -311,40 +197,41 @@ async fn check_or_create_script_poicy(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UploadConfig{
|
async fn do_policy(
|
||||||
session_id:String,
|
api:&submissions_api::external::Context,
|
||||||
api_url:String,
|
script_ids:&std::collections::HashMap<&str,submissions_api::types::ScriptID>,
|
||||||
|
source:&str,
|
||||||
|
to_script_id:submissions_api::types::ScriptID,
|
||||||
|
policy:submissions_api::types::Policy,
|
||||||
|
)->Result<(),GOCError>{
|
||||||
|
let hash=hash_format(hash_source(source));
|
||||||
|
check_or_create_script_poicy(api,hash.as_str(),submissions_api::types::CreateScriptPolicyRequest{
|
||||||
|
FromScriptID:script_ids[source],
|
||||||
|
ToScriptID:to_script_id,
|
||||||
|
Policy:policy,
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn upload_scripts(config:UploadConfig)->Result<(),ScriptUploadError>{
|
async fn upload_scripts(session_id:PathBuf)->Result<()>{
|
||||||
let cookie=submissions_api::Cookie::new(&config.session_id).map_err(ScriptUploadError::Cookie)?;
|
let cookie={
|
||||||
let api=&submissions_api::external::Context::new(config.api_url,cookie).map_err(ScriptUploadError::Reqwest)?;
|
let mut cookie=String::new();
|
||||||
|
std::fs::File::open(session_id)?.read_to_string(&mut cookie)?;
|
||||||
|
submissions_api::Cookie::new(&cookie)?
|
||||||
|
};
|
||||||
|
let api=&submissions_api::external::Context::new("http://localhost:8083".to_owned(),cookie)?;
|
||||||
|
|
||||||
// load all script files
|
let allowed_set=get_allowed_set()?;
|
||||||
let (
|
let allowed_map=get_allowed_map()?;
|
||||||
allowed_set_result,
|
let replace_map=get_replace_map()?;
|
||||||
allowed_map_result,
|
let blocked=get_blocked()?;
|
||||||
replace_map_result,
|
|
||||||
blocked_set_result,
|
|
||||||
)=tokio::join!(
|
|
||||||
get_allowed_set(),
|
|
||||||
get_allowed_map(),
|
|
||||||
get_replace_map(),
|
|
||||||
get_blocked_set(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let allowed_set=allowed_set_result.map_err(ScriptUploadError::AllowedSet)?;
|
|
||||||
let allowed_map=allowed_map_result.map_err(ScriptUploadError::AllowedMap)?;
|
|
||||||
let replace_map=replace_map_result.map_err(ScriptUploadError::ReplaceMap)?;
|
|
||||||
let blocked_set=blocked_set_result.map_err(ScriptUploadError::BlockedSet)?;
|
|
||||||
|
|
||||||
// create a unified deduplicated set of all scripts
|
// create a unified deduplicated set of all scripts
|
||||||
let script_set:std::collections::HashSet<&str>=allowed_set.iter()
|
let script_set:std::collections::HashSet<&str>=allowed_set.iter()
|
||||||
.map(String::as_str)
|
.map(|s|s.as_str())
|
||||||
.chain(
|
.chain(
|
||||||
replace_map.keys().map(String::as_str)
|
replace_map.keys().map(|s|s.as_str())
|
||||||
).chain(
|
).chain(
|
||||||
blocked_set.iter().map(String::as_str)
|
blocked.iter().map(|s|s.as_str())
|
||||||
).collect();
|
).collect();
|
||||||
|
|
||||||
// get or create every unique script
|
// get or create every unique script
|
||||||
@ -352,57 +239,48 @@ async fn upload_scripts(config:UploadConfig)->Result<(),ScriptUploadError>{
|
|||||||
futures::stream::iter(script_set)
|
futures::stream::iter(script_set)
|
||||||
.map(|source|async move{
|
.map(|source|async move{
|
||||||
let script_id=get_or_create_script(api,source).await?;
|
let script_id=get_or_create_script(api,source).await?;
|
||||||
Ok((source,script_id))
|
Ok::<_,GOCError>((source,script_id))
|
||||||
})
|
})
|
||||||
.buffer_unordered(REMOTE_CONCURRENCY)
|
.buffer_unordered(16)
|
||||||
.try_collect().await.map_err(ScriptUploadError::GOC)?;
|
.try_collect().await?;
|
||||||
|
|
||||||
// get or create policy for each script in each category
|
// get or create policy for each script in each category
|
||||||
//
|
//
|
||||||
// replace
|
// replace
|
||||||
let replace_fut=futures::stream::iter(replace_map.iter().map(Ok))
|
futures::stream::iter(replace_map.iter().map(Ok))
|
||||||
.try_for_each_concurrent(Some(REMOTE_CONCURRENCY),|(source,id)|async{
|
.try_for_each_concurrent(Some(16),|(source,id)|async{
|
||||||
check_or_create_script_poicy(
|
do_policy(
|
||||||
api,
|
api,
|
||||||
hash_format(hash_source(source)).as_str(),
|
&script_ids,
|
||||||
submissions_api::types::CreateScriptPolicyRequest{
|
source,
|
||||||
FromScriptID:script_ids[source.as_str()],
|
script_ids[allowed_map[id].as_str()],
|
||||||
ToScriptID:script_ids[allowed_map[id].as_str()],
|
submissions_api::types::Policy::Replace
|
||||||
Policy:submissions_api::types::Policy::Replace,
|
).await
|
||||||
}
|
}).await?;
|
||||||
).await.map_err(ScriptUploadError::GOCPolicyReplace)
|
|
||||||
});
|
|
||||||
|
|
||||||
// allowed
|
// allowed
|
||||||
let allowed_fut=futures::stream::iter(allowed_set.iter().map(Ok))
|
futures::stream::iter(allowed_set.iter().map(Ok))
|
||||||
.try_for_each_concurrent(Some(REMOTE_CONCURRENCY),|source|async{
|
.try_for_each_concurrent(Some(16),|source|async{
|
||||||
check_or_create_script_poicy(
|
do_policy(
|
||||||
api,
|
api,
|
||||||
hash_format(hash_source(source)).as_str(),
|
&script_ids,
|
||||||
submissions_api::types::CreateScriptPolicyRequest{
|
source,
|
||||||
FromScriptID:script_ids[source.as_str()],
|
script_ids[source.as_str()],
|
||||||
ToScriptID:script_ids[source.as_str()],
|
submissions_api::types::Policy::Allowed
|
||||||
Policy:submissions_api::types::Policy::Allowed,
|
).await
|
||||||
}
|
}).await?;
|
||||||
).await.map_err(ScriptUploadError::GOCPolicyAllowed)
|
|
||||||
});
|
|
||||||
|
|
||||||
// blocked
|
// blocked
|
||||||
let blocked_fut=futures::stream::iter(blocked_set.iter().map(Ok))
|
futures::stream::iter(blocked.iter().map(Ok))
|
||||||
.try_for_each_concurrent(Some(REMOTE_CONCURRENCY),|source|async{
|
.try_for_each_concurrent(Some(16),|source|async{
|
||||||
check_or_create_script_poicy(
|
do_policy(
|
||||||
api,
|
api,
|
||||||
hash_format(hash_source(source)).as_str(),
|
&script_ids,
|
||||||
submissions_api::types::CreateScriptPolicyRequest{
|
source,
|
||||||
FromScriptID:script_ids[source.as_str()],
|
script_ids[source.as_str()],
|
||||||
ToScriptID:script_ids[source.as_str()],
|
submissions_api::types::Policy::Blocked
|
||||||
Policy:submissions_api::types::Policy::Blocked,
|
).await
|
||||||
}
|
}).await?;
|
||||||
).await.map_err(ScriptUploadError::GOCPolicyBlocked)
|
|
||||||
});
|
|
||||||
|
|
||||||
// run futures
|
|
||||||
tokio::try_join!(replace_fut,allowed_fut,blocked_fut)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user