fix & chore: handle hostname/URL cleaning within trait implementation
SUMMARY Renamed function parameters from "hostname" to "url" wherever a url::Url is received db_core::get_hostname is renamed to db_core::clean_url, better describing its new implementation forge_core::get_hostname is renamed to forge_core::get_url to better describe its new implementation URL PROCESSING METHODS federate/federate-core Parses URL and returns only the hostname db/db-core Parses URL, cleans it by removing path and query parameters and returns the end result
This commit is contained in:
parent
29f5556586
commit
fe8bd2fb26
17 changed files with 273 additions and 232 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1018,6 +1018,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"db-core",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -60,25 +60,29 @@ pub mod dev {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// create a new forge on the database
|
||||
pub struct CreateForge<'a> {
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
pub hostname: &'a str,
|
||||
pub struct CreateForge {
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
pub url: Url,
|
||||
/// forge type: which software is the instance running?
|
||||
pub forge_type: ForgeImplementation,
|
||||
}
|
||||
|
||||
/// Get hostname from URL
|
||||
/// Utility function for uniform hostname format
|
||||
pub fn get_hostname(url: &Url) -> String {
|
||||
url.host().as_ref().unwrap().to_string()
|
||||
/// Get url from URL
|
||||
/// Utility function for uniform url format
|
||||
pub fn clean_url(url: &Url) -> String {
|
||||
let mut url = url.clone();
|
||||
url.set_path("");
|
||||
url.set_query(None);
|
||||
url.set_fragment(None);
|
||||
url.as_str().to_string()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// user data
|
||||
pub struct User {
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: String,
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub url: String,
|
||||
/// username of the user
|
||||
pub username: String,
|
||||
/// html link to the user profile
|
||||
|
@ -90,9 +94,9 @@ pub struct User {
|
|||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// add new user to database
|
||||
pub struct AddUser<'a> {
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: &'a str,
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub url: Url,
|
||||
/// username of the user
|
||||
pub username: &'a str,
|
||||
/// html link to the user profile
|
||||
|
@ -108,9 +112,9 @@ pub struct AddRepository<'a> {
|
|||
pub html_link: &'a str,
|
||||
/// repository topic tags
|
||||
pub tags: Option<Vec<&'a str>>,
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: &'a str,
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub url: Url,
|
||||
/// repository name
|
||||
pub name: &'a str,
|
||||
/// repository owner
|
||||
|
@ -124,8 +128,8 @@ pub struct AddRepository<'a> {
|
|||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// data representing a forge instance
|
||||
pub struct Forge {
|
||||
/// hostname of the forge
|
||||
pub hostname: String,
|
||||
/// url of the forge
|
||||
pub url: String,
|
||||
/// type of the forge
|
||||
pub forge_type: ForgeImplementation,
|
||||
/// last crawl
|
||||
|
@ -139,9 +143,9 @@ pub struct Repository {
|
|||
pub html_url: String,
|
||||
/// repository topic tags
|
||||
pub tags: Option<Vec<String>>,
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: String,
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub url: String,
|
||||
/// repository name
|
||||
pub name: String,
|
||||
/// repository owner
|
||||
|
@ -155,8 +159,8 @@ pub struct Repository {
|
|||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// represents a DNS challenge
|
||||
pub struct Challenge {
|
||||
/// hostname of the forge instance
|
||||
pub hostname: String,
|
||||
/// url of the forge instance
|
||||
pub url: String,
|
||||
/// key of TXT record
|
||||
pub key: String,
|
||||
/// value of TXT record
|
||||
|
@ -186,13 +190,13 @@ pub trait SCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
|
|||
async fn create_forge_instance(&self, f: &CreateForge) -> DBResult<()>;
|
||||
|
||||
/// get forge instance data
|
||||
async fn get_forge(&self, hostname: &str) -> DBResult<Forge>;
|
||||
async fn get_forge(&self, url: &Url) -> DBResult<Forge>;
|
||||
|
||||
/// delete forge instance
|
||||
async fn delete_forge_instance(&self, hostname: &str) -> DBResult<()>;
|
||||
async fn delete_forge_instance(&self, url: &Url) -> DBResult<()>;
|
||||
|
||||
/// check if a forge instance exists
|
||||
async fn forge_exists(&self, hostname: &str) -> DBResult<bool>;
|
||||
async fn forge_exists(&self, url: &Url) -> DBResult<bool>;
|
||||
|
||||
/// check if forge type exists
|
||||
async fn forge_type_exists(&self, forge_type: &ForgeImplementation) -> DBResult<bool>;
|
||||
|
@ -204,20 +208,20 @@ pub trait SCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
|
|||
async fn add_user(&self, u: &AddUser) -> DBResult<()>;
|
||||
|
||||
/// get user data
|
||||
async fn get_user(&self, username: &str, hostname: &str) -> DBResult<User>;
|
||||
async fn get_user(&self, username: &str, url: &Url) -> DBResult<User>;
|
||||
|
||||
/// check if an user exists. When hostname of a forge instace is provided, username search is
|
||||
/// check if an user exists. When url of a forge instace is provided, username search is
|
||||
/// done only on that forge
|
||||
async fn user_exists(&self, username: &str, hostname: Option<&str>) -> DBResult<bool>;
|
||||
async fn user_exists(&self, username: &str, url: Option<&Url>) -> DBResult<bool>;
|
||||
|
||||
/// delete user
|
||||
async fn delete_user(&self, username: &str, hostname: &str) -> DBResult<()>;
|
||||
async fn delete_user(&self, username: &str, url: &Url) -> DBResult<()>;
|
||||
|
||||
/// delete repository
|
||||
async fn delete_repository(&self, owner: &str, name: &str, hostname: &str) -> DBResult<()>;
|
||||
async fn delete_repository(&self, owner: &str, name: &str, url: &Url) -> DBResult<()>;
|
||||
|
||||
/// check if a repository exists.
|
||||
async fn repository_exists(&self, name: &str, owner: &str, hostname: &str) -> DBResult<bool>;
|
||||
async fn repository_exists(&self, name: &str, owner: &str, url: &Url) -> DBResult<bool>;
|
||||
|
||||
/// Get all repositories
|
||||
async fn get_all_repositories(&self, offset: u32, limit: u32) -> DBResult<Vec<Repository>>;
|
||||
|
|
|
@ -21,20 +21,20 @@ use crate::prelude::*;
|
|||
/// adding forge works
|
||||
pub async fn adding_forge_works<'a, T: SCDatabase>(
|
||||
db: &T,
|
||||
create_forge_msg: CreateForge<'a>,
|
||||
create_forge_msg: CreateForge,
|
||||
add_user_msg: AddUser<'a>,
|
||||
add_user_msg2: AddUser<'a>,
|
||||
add_repo_msg: AddRepository<'a>,
|
||||
) {
|
||||
let _ = db.delete_forge_instance(create_forge_msg.hostname).await;
|
||||
let _ = db.delete_forge_instance(&create_forge_msg.url).await;
|
||||
db.create_forge_instance(&create_forge_msg).await.unwrap();
|
||||
assert!(
|
||||
db.forge_exists(create_forge_msg.hostname).await.unwrap(),
|
||||
"forge creation failed, forge exinstance check failure"
|
||||
db.forge_exists(&create_forge_msg.url).await.unwrap(),
|
||||
"forge creation failed, forge existence check failure"
|
||||
);
|
||||
|
||||
{
|
||||
let forge = db.get_forge(create_forge_msg.hostname).await.unwrap();
|
||||
let forge = db.get_forge(&create_forge_msg.url).await.unwrap();
|
||||
let forges = db.get_all_forges(0, 10).await.unwrap();
|
||||
assert_eq!(forges.len(), 1);
|
||||
|
||||
|
@ -43,11 +43,11 @@ pub async fn adding_forge_works<'a, T: SCDatabase>(
|
|||
create_forge_msg.forge_type
|
||||
);
|
||||
assert_eq!(
|
||||
forges.get(0).as_ref().unwrap().hostname,
|
||||
create_forge_msg.hostname
|
||||
forges.get(0).as_ref().unwrap().url,
|
||||
crate::clean_url(&create_forge_msg.url)
|
||||
);
|
||||
|
||||
assert_eq!(forge.hostname, create_forge_msg.hostname);
|
||||
assert_eq!(forge.url, crate::clean_url(&create_forge_msg.url));
|
||||
assert_eq!(forge.forge_type, create_forge_msg.forge_type);
|
||||
}
|
||||
|
||||
|
@ -56,10 +56,10 @@ pub async fn adding_forge_works<'a, T: SCDatabase>(
|
|||
db.add_user(&add_user_msg2).await.unwrap();
|
||||
{
|
||||
let db_user = db
|
||||
.get_user(add_user_msg.username, add_user_msg.hostname)
|
||||
.get_user(add_user_msg.username, &add_user_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(db_user.hostname, add_user_msg.hostname);
|
||||
assert_eq!(db_user.url, crate::clean_url(&add_user_msg.url));
|
||||
assert_eq!(db_user.username, add_user_msg.username);
|
||||
assert_eq!(db_user.html_link, add_user_msg.html_link);
|
||||
assert_eq!(
|
||||
|
@ -70,7 +70,7 @@ pub async fn adding_forge_works<'a, T: SCDatabase>(
|
|||
// verify user exists
|
||||
assert!(db.user_exists(add_user_msg.username, None).await.unwrap());
|
||||
assert!(db
|
||||
.user_exists(add_user_msg.username, Some(add_user_msg.hostname))
|
||||
.user_exists(add_user_msg.username, Some(&add_user_msg.url))
|
||||
.await
|
||||
.unwrap());
|
||||
|
||||
|
@ -78,24 +78,24 @@ pub async fn adding_forge_works<'a, T: SCDatabase>(
|
|||
db.create_repository(&add_repo_msg).await.unwrap();
|
||||
// verify repo exists
|
||||
assert!(db
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, add_repo_msg.hostname)
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
// delete repository
|
||||
db.delete_repository(add_repo_msg.owner, add_repo_msg.name, add_repo_msg.hostname)
|
||||
db.delete_repository(add_repo_msg.owner, add_repo_msg.name, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!db
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, add_repo_msg.hostname)
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
|
||||
// delete user
|
||||
db.delete_user(add_user_msg.username, add_user_msg.hostname)
|
||||
db.delete_user(add_user_msg.username, &add_user_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!db
|
||||
.user_exists(add_user_msg.username, Some(add_user_msg.hostname))
|
||||
.user_exists(add_user_msg.username, Some(&add_user_msg.url))
|
||||
.await
|
||||
.unwrap());
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ path = "src/lib.rs"
|
|||
sqlx = { version = "0.5.11", features = [ "sqlite", "time", "offline", "runtime-actix-rustls" ] }
|
||||
db-core = {path = "../db-core"}
|
||||
async-trait = "0.1.51"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2"
|
||||
|
|
|
@ -22,6 +22,7 @@ use db_core::dev::*;
|
|||
use sqlx::sqlite::SqlitePool;
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
pub mod errors;
|
||||
#[cfg(test)]
|
||||
|
@ -112,26 +113,25 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// delete forge instance
|
||||
async fn delete_forge_instance(&self, hostname: &str) -> DBResult<()> {
|
||||
sqlx::query!(
|
||||
"DELETE FROM starchart_forges WHERE hostname = ($1)",
|
||||
hostname,
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||
async fn delete_forge_instance(&self, url: &Url) -> DBResult<()> {
|
||||
let url = db_core::clean_url(url);
|
||||
sqlx::query!("DELETE FROM starchart_forges WHERE hostname = ($1)", url,)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// create forge instance DB
|
||||
async fn create_forge_instance(&self, f: &CreateForge) -> DBResult<()> {
|
||||
let now = now_unix_time_stamp();
|
||||
let url = db_core::clean_url(&f.url);
|
||||
let forge_type = f.forge_type.to_str();
|
||||
sqlx::query!(
|
||||
"INSERT INTO
|
||||
starchart_forges (hostname, verified_on, forge_type )
|
||||
VALUES ($1, $2, (SELECT ID FROM starchart_forge_type WHERE name = $3))",
|
||||
f.hostname,
|
||||
url,
|
||||
now,
|
||||
forge_type,
|
||||
)
|
||||
|
@ -143,7 +143,8 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// get forge instance data
|
||||
async fn get_forge(&self, hostname: &str) -> DBResult<Forge> {
|
||||
async fn get_forge(&self, url: &Url) -> DBResult<Forge> {
|
||||
let url = db_core::clean_url(url);
|
||||
let f = sqlx::query_as!(
|
||||
InnerForge,
|
||||
"SELECT
|
||||
|
@ -159,7 +160,7 @@ impl SCDatabase for Database {
|
|||
WHERE
|
||||
hostname = $1;
|
||||
",
|
||||
hostname,
|
||||
url,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
|
@ -200,13 +201,11 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// check if a forge instance exists
|
||||
async fn forge_exists(&self, hostname: &str) -> DBResult<bool> {
|
||||
match sqlx::query!(
|
||||
"SELECT ID FROM starchart_forges WHERE hostname = $1",
|
||||
hostname
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
async fn forge_exists(&self, url: &Url) -> DBResult<bool> {
|
||||
let url = db_core::clean_url(url);
|
||||
match sqlx::query!("SELECT ID FROM starchart_forges WHERE hostname = $1", url)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(Error::RowNotFound) => Ok(false),
|
||||
|
@ -232,6 +231,7 @@ impl SCDatabase for Database {
|
|||
/// add new user to database
|
||||
async fn add_user(&self, u: &AddUser) -> DBResult<()> {
|
||||
let now = now_unix_time_stamp();
|
||||
let url = db_core::clean_url(&u.url);
|
||||
sqlx::query!(
|
||||
"INSERT INTO
|
||||
starchart_users (
|
||||
|
@ -240,7 +240,7 @@ impl SCDatabase for Database {
|
|||
)
|
||||
VALUES (
|
||||
(SELECT ID FROM starchart_forges WHERE hostname = $1), $2, $3, $4, $5, $6)",
|
||||
u.hostname,
|
||||
url,
|
||||
u.username,
|
||||
u.html_link,
|
||||
u.profile_photo,
|
||||
|
@ -255,46 +255,51 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// get user data
|
||||
async fn get_user(&self, username: &str, hostname: &str) -> DBResult<User> {
|
||||
async fn get_user(&self, username: &str, url: &Url) -> DBResult<User> {
|
||||
struct InnerUser {
|
||||
profile_photo_html_url: Option<String>,
|
||||
html_url: String,
|
||||
}
|
||||
|
||||
let url = db_core::clean_url(url);
|
||||
let res = sqlx::query_as!(
|
||||
InnerUser,
|
||||
"SELECT html_url, profile_photo_html_url FROM starchart_users WHERE username = $1 AND
|
||||
hostname_id = (SELECT ID FROM starchart_forges WHERE hostname = $2)",
|
||||
username,
|
||||
hostname,
|
||||
url,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||
Ok(User {
|
||||
username: username.into(),
|
||||
hostname: hostname.into(),
|
||||
url: url.into(),
|
||||
profile_photo: res.profile_photo_html_url,
|
||||
html_link: res.html_url,
|
||||
})
|
||||
}
|
||||
|
||||
/// check if an user exists. When hostname of a forge instace is provided, username search is
|
||||
/// check if an user exists. When url of a forge instace is provided, username search is
|
||||
/// done only on that forge
|
||||
async fn user_exists(&self, username: &str, hostname: Option<&str>) -> DBResult<bool> {
|
||||
match hostname {
|
||||
Some(hostname) => match sqlx::query!(
|
||||
"SELECT ID FROM starchart_users WHERE username = $1 AND
|
||||
async fn user_exists(&self, username: &str, url: Option<&Url>) -> DBResult<bool> {
|
||||
match url {
|
||||
Some(url) => {
|
||||
let url = db_core::clean_url(url);
|
||||
match sqlx::query!(
|
||||
"SELECT ID FROM starchart_users WHERE username = $1 AND
|
||||
hostname_id = (SELECT ID FROM starchart_forges WHERE hostname = $2)",
|
||||
username,
|
||||
hostname,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(Error::RowNotFound) => Ok(false),
|
||||
Err(e) => Err(DBError::DBError(Box::new(e).into())),
|
||||
},
|
||||
username,
|
||||
url,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(Error::RowNotFound) => Ok(false),
|
||||
Err(e) => Err(DBError::DBError(Box::new(e).into())),
|
||||
}
|
||||
}
|
||||
None => match sqlx::query!(
|
||||
"SELECT ID FROM starchart_users WHERE username = $1",
|
||||
username
|
||||
|
@ -310,7 +315,8 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// check if a repo exists.
|
||||
async fn repository_exists(&self, name: &str, owner: &str, hostname: &str) -> DBResult<bool> {
|
||||
async fn repository_exists(&self, name: &str, owner: &str, url: &Url) -> DBResult<bool> {
|
||||
let url = db_core::clean_url(url);
|
||||
match sqlx::query!(
|
||||
"SELECT ID FROM starchart_repositories
|
||||
WHERE
|
||||
|
@ -321,7 +327,7 @@ impl SCDatabase for Database {
|
|||
hostname_id = (SELECT ID FROM starchart_forges WHERE hostname = $3)",
|
||||
name,
|
||||
owner,
|
||||
hostname,
|
||||
url,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
|
@ -336,6 +342,7 @@ impl SCDatabase for Database {
|
|||
async fn create_repository(&self, r: &AddRepository) -> DBResult<()> {
|
||||
// unimplemented!()
|
||||
let now = now_unix_time_stamp();
|
||||
let url = db_core::clean_url(&r.url);
|
||||
sqlx::query!(
|
||||
"INSERT INTO
|
||||
starchart_repositories (
|
||||
|
@ -346,7 +353,7 @@ impl SCDatabase for Database {
|
|||
(SELECT ID FROM starchart_users WHERE username = $2),
|
||||
$3, $4, $5, $6, $7, $8
|
||||
);",
|
||||
r.hostname,
|
||||
url,
|
||||
r.owner,
|
||||
r.name,
|
||||
r.description,
|
||||
|
@ -389,12 +396,13 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// delete user
|
||||
async fn delete_user(&self, username: &str, hostname: &str) -> DBResult<()> {
|
||||
async fn delete_user(&self, username: &str, url: &Url) -> DBResult<()> {
|
||||
let url = db_core::clean_url(url);
|
||||
sqlx::query!(
|
||||
" DELETE FROM starchart_users WHERE username = $1 AND
|
||||
hostname_id = (SELECT ID FROM starchart_forges WHERE hostname = $2)",
|
||||
username,
|
||||
hostname
|
||||
url
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
|
@ -403,7 +411,8 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
/// delete repository
|
||||
async fn delete_repository(&self, owner: &str, name: &str, hostname: &str) -> DBResult<()> {
|
||||
async fn delete_repository(&self, owner: &str, name: &str, url: &Url) -> DBResult<()> {
|
||||
let url = db_core::clean_url(url);
|
||||
sqlx::query!(
|
||||
" DELETE FROM starchart_repositories
|
||||
WHERE
|
||||
|
@ -414,7 +423,7 @@ impl SCDatabase for Database {
|
|||
hostname_id = (SELECT ID FROM starchart_forges WHERE hostname = $3)",
|
||||
name,
|
||||
owner,
|
||||
hostname
|
||||
url
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
|
@ -437,14 +446,24 @@ impl SCDatabase for Database {
|
|||
}
|
||||
|
||||
async fn get_dns_challenge(&self, key: &str) -> DBResult<Challenge> {
|
||||
struct InnerChallenge {
|
||||
hostname: String,
|
||||
key: String,
|
||||
value: String,
|
||||
}
|
||||
let res = sqlx::query_as!(
|
||||
Challenge,
|
||||
InnerChallenge,
|
||||
"SELECT key, value, hostname FROM starchart_dns_challenges WHERE key = $1",
|
||||
key
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DBError::DBError(Box::new(e)))?;
|
||||
let res = Challenge {
|
||||
url: res.hostname,
|
||||
key: res.key,
|
||||
value: res.value,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
|
@ -463,7 +482,7 @@ impl SCDatabase for Database {
|
|||
"INSERT INTO
|
||||
starchart_dns_challenges (hostname, value, key, created )
|
||||
VALUES ($1, $2, $3, $4);",
|
||||
challenge.hostname,
|
||||
challenge.url,
|
||||
challenge.value,
|
||||
challenge.key,
|
||||
now,
|
||||
|
@ -480,8 +499,8 @@ impl SCDatabase for Database {
|
|||
struct InnerRepository {
|
||||
/// html link to the repository
|
||||
pub html_url: String,
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: String,
|
||||
/// repository name
|
||||
pub name: String,
|
||||
|
@ -554,7 +573,7 @@ LIMIT $1 OFFSET $2
|
|||
};
|
||||
res.push(Repository {
|
||||
html_url: repo.html_url,
|
||||
hostname: repo.hostname,
|
||||
url: repo.hostname,
|
||||
name: repo.name,
|
||||
username: repo.username,
|
||||
description: repo.description,
|
||||
|
@ -580,7 +599,7 @@ struct InnerForge {
|
|||
impl From<InnerForge> for Forge {
|
||||
fn from(f: InnerForge) -> Self {
|
||||
Self {
|
||||
hostname: f.hostname,
|
||||
url: f.hostname,
|
||||
last_crawl_on: f.last_crawl_on,
|
||||
forge_type: ForgeImplementation::from_str(&f.name).unwrap(),
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use db_core::tests::*;
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn everything_works() {
|
||||
const HOSTNAME: &str = "https://test-gitea.example.com";
|
||||
const URL: &str = "https://test-gitea.example.com";
|
||||
const HTML_PROFILE_URL: &str = "https://test-gitea.example.com/user1";
|
||||
const HTML_PROFILE_PHOTO_URL_2: &str = "https://test-gitea.example.com/profile-photo/user2";
|
||||
const USERNAME: &str = "user1";
|
||||
|
@ -35,33 +35,35 @@ async fn everything_works() {
|
|||
const HTML_REPO_URL: &str = "https://test-gitea.example.com/user1/starchart";
|
||||
const TAGS: [&str; 3] = ["test", "starchart", "spider"];
|
||||
|
||||
let hostname = Url::parse(HOSTNAME).unwrap();
|
||||
let hostname = get_hostname(&hostname);
|
||||
let url = Url::parse(URL).unwrap();
|
||||
|
||||
let create_forge_msg = CreateForge {
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
forge_type: ForgeImplementation::Gitea,
|
||||
};
|
||||
|
||||
let add_user_msg = AddUser {
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
html_link: HTML_PROFILE_URL,
|
||||
profile_photo: None,
|
||||
username: USERNAME,
|
||||
};
|
||||
|
||||
let add_user_msg_2 = AddUser {
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
html_link: HTML_PROFILE_PHOTO_URL_2,
|
||||
profile_photo: Some(HTML_PROFILE_PHOTO_URL_2),
|
||||
username: USERNAME2,
|
||||
};
|
||||
|
||||
let url = env::var("SQLITE_DATABASE_URL").expect("Set SQLITE_DATABASE_URL env var");
|
||||
let pool_options = SqlitePoolOptions::new().max_connections(2);
|
||||
let connection_options = ConnectionOptions::Fresh(Fresh { pool_options, url });
|
||||
let db = connection_options.connect().await.unwrap();
|
||||
db.migrate().await.unwrap();
|
||||
let db = {
|
||||
let url = env::var("SQLITE_DATABASE_URL").expect("Set SQLITE_DATABASE_URL env var");
|
||||
let pool_options = SqlitePoolOptions::new().max_connections(2);
|
||||
let connection_options = ConnectionOptions::Fresh(Fresh { pool_options, url });
|
||||
let db = connection_options.connect().await.unwrap();
|
||||
db.migrate().await.unwrap();
|
||||
db
|
||||
};
|
||||
|
||||
let add_repo_msg = AddRepository {
|
||||
html_link: HTML_REPO_URL,
|
||||
|
@ -70,7 +72,7 @@ async fn everything_works() {
|
|||
owner: USERNAME,
|
||||
website: None,
|
||||
description: None,
|
||||
hostname: &hostname,
|
||||
url,
|
||||
};
|
||||
|
||||
adding_forge_works(
|
||||
|
|
|
@ -20,6 +20,7 @@ use std::path::PathBuf;
|
|||
use std::result::Result;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use url::Url;
|
||||
|
||||
use db_core::prelude::*;
|
||||
|
||||
|
@ -37,16 +38,16 @@ pub trait Federate: Sync + Send {
|
|||
async fn rm_util(&self, path: &Path) -> Result<(), Self::Error>;
|
||||
|
||||
/// create forge instance
|
||||
async fn create_forge_instance(&self, f: &CreateForge<'_>) -> Result<(), Self::Error>;
|
||||
async fn create_forge_instance(&self, f: &CreateForge) -> Result<(), Self::Error>;
|
||||
|
||||
/// delete forge instance
|
||||
async fn delete_forge_instance(&self, hostname: &str) -> Result<(), Self::Error>;
|
||||
async fn delete_forge_instance(&self, url: &Url) -> Result<(), Self::Error>;
|
||||
|
||||
/// check if a forge instance exists
|
||||
async fn forge_exists(&self, hostname: &str) -> Result<bool, Self::Error>;
|
||||
async fn forge_exists(&self, url: &Url) -> Result<bool, Self::Error>;
|
||||
|
||||
/// check if an user exists.
|
||||
async fn user_exists(&self, username: &str, hostname: &str) -> Result<bool, Self::Error>;
|
||||
async fn user_exists(&self, username: &str, url: &Url) -> Result<bool, Self::Error>;
|
||||
|
||||
/// create user instance
|
||||
async fn create_user(&self, f: &AddUser<'_>) -> Result<(), Self::Error>;
|
||||
|
@ -59,20 +60,24 @@ pub trait Federate: Sync + Send {
|
|||
&self,
|
||||
name: &str,
|
||||
owner: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
) -> Result<bool, Self::Error>;
|
||||
|
||||
/// delete user
|
||||
async fn delete_user(&self, username: &str, hostname: &str) -> Result<(), Self::Error>;
|
||||
async fn delete_user(&self, username: &str, url: &Url) -> Result<(), Self::Error>;
|
||||
|
||||
/// delete repository
|
||||
async fn delete_repository(
|
||||
&self,
|
||||
owner: &str,
|
||||
name: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
/// publish results in tar ball
|
||||
async fn tar(&self) -> Result<PathBuf, Self::Error>;
|
||||
}
|
||||
|
||||
pub fn get_hostname(url: &Url) -> &str {
|
||||
url.host_str().unwrap()
|
||||
}
|
||||
|
|
|
@ -21,34 +21,34 @@ use crate::*;
|
|||
/// adding forge works
|
||||
pub async fn adding_forge_works<'a, T: Federate>(
|
||||
ff: &T,
|
||||
create_forge_msg: CreateForge<'a>,
|
||||
create_forge_msg: CreateForge,
|
||||
create_user_msg: AddUser<'a>,
|
||||
add_repo_msg: AddRepository<'a>,
|
||||
) {
|
||||
let _ = ff.delete_forge_instance(create_forge_msg.hostname).await;
|
||||
assert!(!ff.forge_exists(&create_forge_msg.hostname).await.unwrap());
|
||||
let _ = ff.delete_forge_instance(&create_forge_msg.url).await;
|
||||
assert!(!ff.forge_exists(&create_forge_msg.url).await.unwrap());
|
||||
ff.create_forge_instance(&create_forge_msg).await.unwrap();
|
||||
assert!(ff.forge_exists(&create_forge_msg.hostname).await.unwrap());
|
||||
assert!(ff.forge_exists(&create_forge_msg.url).await.unwrap());
|
||||
|
||||
// add user
|
||||
assert!(!ff
|
||||
.user_exists(&create_user_msg.username, &create_user_msg.hostname)
|
||||
.user_exists(&create_user_msg.username, &create_user_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
ff.create_user(&create_user_msg).await.unwrap();
|
||||
assert!(ff
|
||||
.user_exists(&create_user_msg.username, &create_user_msg.hostname)
|
||||
.user_exists(&create_user_msg.username, &create_user_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
|
||||
// add repository
|
||||
assert!(!ff
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, add_repo_msg.hostname)
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
ff.create_repository(&add_repo_msg).await.unwrap();
|
||||
assert!(ff
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, add_repo_msg.hostname)
|
||||
.repository_exists(add_repo_msg.name, add_repo_msg.owner, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap());
|
||||
|
||||
|
@ -56,17 +56,17 @@ pub async fn adding_forge_works<'a, T: Federate>(
|
|||
ff.tar().await.unwrap();
|
||||
|
||||
// delete repository
|
||||
ff.delete_repository(add_repo_msg.owner, add_repo_msg.name, add_repo_msg.hostname)
|
||||
ff.delete_repository(add_repo_msg.owner, add_repo_msg.name, &add_repo_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// delete user
|
||||
ff.delete_user(create_user_msg.username, create_user_msg.hostname)
|
||||
ff.delete_user(create_user_msg.username, &create_user_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// delete user
|
||||
ff.delete_forge_instance(create_forge_msg.hostname)
|
||||
ff.delete_forge_instance(&create_forge_msg.url)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ use std::path::{Path, PathBuf};
|
|||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
use tokio::fs;
|
||||
use url::Url;
|
||||
|
||||
use db_core::prelude::*;
|
||||
|
||||
|
@ -58,7 +59,8 @@ impl PccFederate {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
pub async fn get_instance_path(&self, hostname: &str, create_dirs: bool) -> FResult<PathBuf> {
|
||||
pub async fn get_instance_path(&self, url: &Url, create_dirs: bool) -> FResult<PathBuf> {
|
||||
let hostname = federate_core::get_hostname(url);
|
||||
let path = self.get_content_path(false).await?.join(hostname);
|
||||
if create_dirs {
|
||||
self.create_dir_if_not_exists(&path).await?;
|
||||
|
@ -69,13 +71,10 @@ impl PccFederate {
|
|||
pub async fn get_user_path(
|
||||
&self,
|
||||
username: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
create_dirs: bool,
|
||||
) -> FResult<PathBuf> {
|
||||
let path = self
|
||||
.get_instance_path(hostname, false)
|
||||
.await?
|
||||
.join(username);
|
||||
let path = self.get_instance_path(url, false).await?.join(username);
|
||||
if create_dirs {
|
||||
self.create_dir_if_not_exists(&path).await?;
|
||||
}
|
||||
|
@ -86,10 +85,10 @@ impl PccFederate {
|
|||
&self,
|
||||
name: &str,
|
||||
owner: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
create_dirs: bool,
|
||||
) -> FResult<PathBuf> {
|
||||
let path = self.get_user_path(owner, hostname, false).await?.join(name);
|
||||
let path = self.get_user_path(owner, url, false).await?.join(name);
|
||||
if create_dirs {
|
||||
self.create_dir_if_not_exists(&path).await?;
|
||||
}
|
||||
|
@ -129,21 +128,21 @@ impl Federate for PccFederate {
|
|||
}
|
||||
|
||||
/// create forge instance
|
||||
async fn create_forge_instance(&self, f: &CreateForge<'_>) -> FResult<()> {
|
||||
let path = self.get_instance_path(f.hostname, true).await?;
|
||||
async fn create_forge_instance(&self, f: &CreateForge) -> FResult<()> {
|
||||
let path = self.get_instance_path(&f.url, true).await?;
|
||||
self.write_util(f, &path.join(INSTANCE_INFO_FILE)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// delete forge instance
|
||||
async fn delete_forge_instance(&self, hostname: &str) -> FResult<()> {
|
||||
let path = self.get_instance_path(hostname, false).await?;
|
||||
async fn delete_forge_instance(&self, url: &Url) -> FResult<()> {
|
||||
let path = self.get_instance_path(&url, false).await?;
|
||||
self.rm_util(&path).await
|
||||
}
|
||||
|
||||
/// check if a forge instance exists
|
||||
async fn forge_exists(&self, hostname: &str) -> Result<bool, Self::Error> {
|
||||
let path = self.get_instance_path(hostname, false).await?;
|
||||
async fn forge_exists(&self, url: &Url) -> Result<bool, Self::Error> {
|
||||
let path = self.get_instance_path(url, false).await?;
|
||||
if path.exists() && path.is_dir() {
|
||||
let instance = path.join(INSTANCE_INFO_FILE);
|
||||
Ok(instance.exists() && instance.is_file())
|
||||
|
@ -153,8 +152,8 @@ impl Federate for PccFederate {
|
|||
}
|
||||
|
||||
/// check if an user exists.
|
||||
async fn user_exists(&self, username: &str, hostname: &str) -> Result<bool, Self::Error> {
|
||||
let path = self.get_user_path(username, hostname, false).await?;
|
||||
async fn user_exists(&self, username: &str, url: &Url) -> Result<bool, Self::Error> {
|
||||
let path = self.get_user_path(username, url, false).await?;
|
||||
if path.exists() && path.is_dir() {
|
||||
let user = path.join(USER_INFO_FILE);
|
||||
Ok(user.exists() && user.is_file())
|
||||
|
@ -165,14 +164,14 @@ impl Federate for PccFederate {
|
|||
|
||||
/// create user instance
|
||||
async fn create_user(&self, f: &AddUser<'_>) -> Result<(), Self::Error> {
|
||||
let path = self.get_user_path(f.username, f.hostname, true).await?;
|
||||
let path = self.get_user_path(f.username, &f.url, true).await?;
|
||||
self.write_util(f, &path.join(USER_INFO_FILE)).await
|
||||
}
|
||||
|
||||
/// add repository instance
|
||||
async fn create_repository(&self, f: &AddRepository<'_>) -> Result<(), Self::Error> {
|
||||
let path = self
|
||||
.get_repo_path(f.name, f.owner, f.hostname, true)
|
||||
.get_repo_path(f.name, f.owner, &f.url, true)
|
||||
.await?
|
||||
.join(REPO_INFO_FILE);
|
||||
let publiccode: schema::Repository = f.into();
|
||||
|
@ -184,9 +183,9 @@ impl Federate for PccFederate {
|
|||
&self,
|
||||
name: &str,
|
||||
owner: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
) -> Result<bool, Self::Error> {
|
||||
let path = self.get_repo_path(name, owner, hostname, false).await?;
|
||||
let path = self.get_repo_path(name, owner, url, false).await?;
|
||||
if path.exists() && path.is_dir() {
|
||||
let repo = path.join(REPO_INFO_FILE);
|
||||
Ok(repo.exists() && repo.is_file())
|
||||
|
@ -196,8 +195,8 @@ impl Federate for PccFederate {
|
|||
}
|
||||
|
||||
/// delete user
|
||||
async fn delete_user(&self, username: &str, hostname: &str) -> Result<(), Self::Error> {
|
||||
let path = self.get_user_path(username, hostname, false).await?;
|
||||
async fn delete_user(&self, username: &str, url: &Url) -> Result<(), Self::Error> {
|
||||
let path = self.get_user_path(username, url, false).await?;
|
||||
self.rm_util(&path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -207,9 +206,9 @@ impl Federate for PccFederate {
|
|||
&self,
|
||||
owner: &str,
|
||||
name: &str,
|
||||
hostname: &str,
|
||||
url: &Url,
|
||||
) -> Result<(), Self::Error> {
|
||||
let path = self.get_repo_path(name, owner, hostname, false).await?;
|
||||
let path = self.get_repo_path(name, owner, url, false).await?;
|
||||
self.rm_util(&path).await
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ use federate_core::tests;
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn everything_works() {
|
||||
const HOSTNAME: &str = "https://test-gitea.example.com";
|
||||
const URL: &str = "https://test-gitea.example.com";
|
||||
const HTML_PROFILE_URL: &str = "https://test-gitea.example.com/user1";
|
||||
const USERNAME: &str = "user1";
|
||||
|
||||
|
@ -32,16 +32,15 @@ async fn everything_works() {
|
|||
|
||||
let tmp_dir = Temp::new_dir().unwrap();
|
||||
|
||||
let hostname = Url::parse(HOSTNAME).unwrap();
|
||||
let hostname = get_hostname(&hostname);
|
||||
let url = Url::parse(URL).unwrap();
|
||||
|
||||
let create_forge_msg = CreateForge {
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
forge_type: ForgeImplementation::Gitea,
|
||||
};
|
||||
|
||||
let add_user_msg = AddUser {
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
html_link: HTML_PROFILE_URL,
|
||||
profile_photo: None,
|
||||
username: USERNAME,
|
||||
|
@ -54,7 +53,7 @@ async fn everything_works() {
|
|||
owner: USERNAME,
|
||||
website: None,
|
||||
description: None,
|
||||
hostname: &hostname,
|
||||
url: url.clone(),
|
||||
};
|
||||
|
||||
let pcc = PccFederate::new(tmp_dir.to_str().unwrap().to_string())
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/lib.rs"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.51"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
|
||||
[dependencies.db-core]
|
||||
path = "../../db/db-core"
|
||||
|
|
|
@ -20,6 +20,7 @@ use std::sync::Arc;
|
|||
|
||||
use async_trait::async_trait;
|
||||
use db_core::prelude::*;
|
||||
use url::Url;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::*;
|
||||
|
@ -33,10 +34,10 @@ pub mod dev {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct User<'a> {
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be derived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: &'a str,
|
||||
pub struct User {
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be derived from html_link also, but used to link to user's forge instance
|
||||
pub url: Url,
|
||||
/// username of the user
|
||||
pub username: Arc<String>,
|
||||
/// html link to the user profile
|
||||
|
@ -45,10 +46,10 @@ pub struct User<'a> {
|
|||
pub profile_photo: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a User<'a>> for AddUser<'a> {
|
||||
impl<'a> From<&'a User> for AddUser<'a> {
|
||||
fn from(u: &'a User) -> Self {
|
||||
Self {
|
||||
hostname: u.hostname,
|
||||
url: u.url.clone(),
|
||||
username: u.username.as_str(),
|
||||
html_link: &u.html_link,
|
||||
profile_photo: u.profile_photo.as_deref(),
|
||||
|
@ -58,25 +59,25 @@ impl<'a> From<&'a User<'a>> for AddUser<'a> {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
/// add new repository to database
|
||||
pub struct Repository<'a> {
|
||||
pub struct Repository {
|
||||
/// html link to the repository
|
||||
pub html_link: String,
|
||||
/// repository topic tags
|
||||
pub tags: Option<Vec<Arc<String>>>,
|
||||
/// hostname of the forge instance: with scheme but remove trailing slash
|
||||
/// hostname can be deras_ref().map(|p| p.as_str()),ived from html_link also, but used to link to user's forge instance
|
||||
pub hostname: &'a str,
|
||||
/// url of the forge instance: with scheme but remove trailing slash
|
||||
/// url can be deras_ref().map(|p| p.as_str()),ived from html_link also, but used to link to user's forge instance
|
||||
pub url: Url,
|
||||
/// repository name
|
||||
pub name: String,
|
||||
/// repository owner
|
||||
pub owner: Arc<User<'a>>,
|
||||
pub owner: Arc<User>,
|
||||
/// repository description, if any
|
||||
pub description: Option<String>,
|
||||
/// repository website, if any
|
||||
pub website: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Repository<'a>> for AddRepository<'a> {
|
||||
impl<'a> From<&'a Repository> for AddRepository<'a> {
|
||||
fn from(r: &'a Repository) -> Self {
|
||||
let tags = if let Some(rtags) = &r.tags {
|
||||
let mut tags = Vec::with_capacity(rtags.len());
|
||||
|
@ -88,7 +89,7 @@ impl<'a> From<&'a Repository<'a>> for AddRepository<'a> {
|
|||
None
|
||||
};
|
||||
Self {
|
||||
hostname: r.hostname,
|
||||
url: r.url.clone(),
|
||||
name: &r.name,
|
||||
description: r.description.as_deref(),
|
||||
owner: r.owner.username.as_str(),
|
||||
|
@ -99,21 +100,21 @@ impl<'a> From<&'a Repository<'a>> for AddRepository<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub type UserMap<'a> = HashMap<Arc<String>, Arc<User<'a>>>;
|
||||
pub type UserMap = HashMap<Arc<String>, Arc<User>>;
|
||||
pub type Tags = HashSet<Arc<String>>;
|
||||
pub type Repositories<'a> = Vec<Repository<'a>>;
|
||||
pub type Repositories = Vec<Repository>;
|
||||
|
||||
pub struct CrawlResp<'a> {
|
||||
pub repos: Repositories<'a>,
|
||||
pub struct CrawlResp {
|
||||
pub repos: Repositories,
|
||||
pub tags: Tags,
|
||||
pub users: UserMap<'a>,
|
||||
pub users: UserMap,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SCForge: std::marker::Send + std::marker::Sync + CloneSPForge {
|
||||
async fn is_forge(&self) -> bool;
|
||||
async fn crawl(&self, limit: u64, page: u64, rate_limit: u64) -> CrawlResp;
|
||||
fn get_hostname(&self) -> &str;
|
||||
fn get_url(&self) -> &Url;
|
||||
fn forge_type(&self) -> ForgeImplementation;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ use tokio::task::JoinHandle;
|
|||
use url::Url;
|
||||
|
||||
use db_core::ForgeImplementation;
|
||||
use db_core::SCDatabase;
|
||||
use forge_core::dev::*;
|
||||
use forge_core::Repository;
|
||||
|
||||
|
@ -37,24 +36,24 @@ const GITEA_IDENTIFIER: &str = "gitea";
|
|||
pub struct Gitea {
|
||||
pub instance_url: Url,
|
||||
pub client: Client,
|
||||
hostname: String,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
impl Gitea {
|
||||
pub fn new(instance_url: Url, client: Client) -> Self {
|
||||
let hostname = db_core::get_hostname(&instance_url);
|
||||
let url = Url::parse(&db_core::clean_url(&instance_url)).unwrap();
|
||||
|
||||
Self {
|
||||
instance_url,
|
||||
client,
|
||||
hostname,
|
||||
url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Gitea {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hostname == other.hostname && self.instance_url == other.instance_url
|
||||
self.url == other.url && self.instance_url == other.instance_url
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,8 +79,8 @@ impl SCForge for Gitea {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_hostname(&self) -> &str {
|
||||
&self.hostname
|
||||
fn get_url(&self) -> &Url {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn forge_type(&self) -> ForgeImplementation {
|
||||
|
@ -125,7 +124,7 @@ impl SCForge for Gitea {
|
|||
username,
|
||||
html_link: profile_url.to_string(),
|
||||
profile_photo: Some(u.avatar_url),
|
||||
hostname: &g.hostname,
|
||||
url: g.url.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -174,7 +173,7 @@ impl SCForge for Gitea {
|
|||
}
|
||||
|
||||
let frepo = Repository {
|
||||
hostname: &self.hostname,
|
||||
url: self.url.clone(),
|
||||
website: empty_is_none(&repo.website),
|
||||
name: repo.name,
|
||||
owner: user,
|
||||
|
|
|
@ -97,18 +97,17 @@ pub async fn add_submit(
|
|||
db: &BoxDB,
|
||||
) -> ServiceResult<TXTChallenge> {
|
||||
let url_hostname = Url::parse(&payload.hostname).unwrap();
|
||||
let hostname = get_hostname(&url_hostname);
|
||||
let key = TXTChallenge::get_challenge_txt_key(&ctx, &hostname);
|
||||
let key = TXTChallenge::get_challenge_txt_key(&ctx, &url_hostname);
|
||||
if db.dns_challenge_exists(&key).await? {
|
||||
let value = db.get_dns_challenge(&key).await?.value;
|
||||
Ok(TXTChallenge { key, value })
|
||||
} else {
|
||||
let challenge = TXTChallenge::new(ctx, &hostname);
|
||||
let challenge = TXTChallenge::new(ctx, &url_hostname);
|
||||
|
||||
let c = Challenge {
|
||||
key: challenge.key,
|
||||
value: challenge.value,
|
||||
hostname: url_hostname.to_string(),
|
||||
url: url_hostname.to_string(),
|
||||
};
|
||||
db.create_dns_challenge(&c).await?;
|
||||
|
||||
|
@ -180,8 +179,7 @@ mod tests {
|
|||
};
|
||||
|
||||
println!("{}", payload.hostname);
|
||||
|
||||
let hostname = get_hostname(&Url::parse(&payload.hostname).unwrap());
|
||||
let hostname = Url::parse(&payload.hostname).unwrap();
|
||||
let key = TXTChallenge::get_challenge_txt_key(&ctx, &hostname);
|
||||
|
||||
db.delete_dns_challenge(&key).await.unwrap();
|
||||
|
|
|
@ -87,7 +87,7 @@ pub async fn get_verify(
|
|||
let challenge = Challenge {
|
||||
key: payload.hostname,
|
||||
value: "".into(),
|
||||
hostname: "".into(),
|
||||
url: "".into(),
|
||||
};
|
||||
|
||||
PageError::new(VerifyChallenge::new(&ctx.settings, &challenge), e)
|
||||
|
@ -124,7 +124,7 @@ pub async fn submit_verify(
|
|||
let challenge = Challenge {
|
||||
key: payload.hostname.clone(),
|
||||
value: "".into(),
|
||||
hostname: "".into(),
|
||||
url: "".into(),
|
||||
};
|
||||
|
||||
PageError::new(VerifyChallenge::new(&ctx.settings, &challenge), e)
|
||||
|
@ -143,7 +143,8 @@ pub async fn submit_verify(
|
|||
let federate = federate.clone();
|
||||
let db = db.clone();
|
||||
let fut = async move {
|
||||
ctx.crawl(&value.hostname, &db, &federate).await;
|
||||
ctx.crawl(&Url::parse(&value.url).unwrap(), &db, &federate)
|
||||
.await;
|
||||
};
|
||||
|
||||
tokio::spawn(fut);
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::future::Future;
|
||||
|
||||
use log::info;
|
||||
use tokio::sync::oneshot::{error::TryRecvError, Receiver};
|
||||
use url::Url;
|
||||
|
||||
use db_core::prelude::*;
|
||||
|
@ -25,31 +28,32 @@ use gitea::Gitea;
|
|||
use crate::ctx::Ctx;
|
||||
use crate::db::BoxDB;
|
||||
use crate::federate::ArcFederate;
|
||||
use crate::ArcCtx;
|
||||
|
||||
impl Ctx {
|
||||
pub async fn crawl(&self, instance_url: &str, db: &BoxDB, federate: &ArcFederate) {
|
||||
let forge: Box<dyn SCForge> = Box::new(Gitea::new(
|
||||
Url::parse(instance_url).unwrap(),
|
||||
self.client.clone(),
|
||||
));
|
||||
pub async fn crawl(&self, instance_url: &Url, db: &BoxDB, federate: &ArcFederate) {
|
||||
info!("[crawl][{instance_url}] Init crawling");
|
||||
let forge: Box<dyn SCForge> =
|
||||
Box::new(Gitea::new(instance_url.clone(), self.client.clone()));
|
||||
if !forge.is_forge().await {
|
||||
unimplemented!("Forge type unimplemented");
|
||||
}
|
||||
|
||||
let mut page = 1;
|
||||
let hostname = forge.get_hostname();
|
||||
if !db.forge_exists(hostname).await.unwrap() {
|
||||
info!("[crawl][{hostname}] Creating forge");
|
||||
let msg = CreateForge {
|
||||
hostname,
|
||||
let url = forge.get_url();
|
||||
if !db.forge_exists(url).await.unwrap() {
|
||||
info!("[crawl][{url}] Creating forge");
|
||||
let mut msg = CreateForge {
|
||||
url: url.clone(),
|
||||
forge_type: forge.forge_type(),
|
||||
};
|
||||
|
||||
db.create_forge_instance(&msg).await.unwrap();
|
||||
} else {
|
||||
if !federate.forge_exists(hostname).await.unwrap() {
|
||||
let forge = db.get_forge(hostname).await.unwrap();
|
||||
if !federate.forge_exists(&url).await.unwrap() {
|
||||
let forge = db.get_forge(&url).await.unwrap();
|
||||
let msg = CreateForge {
|
||||
hostname,
|
||||
url: url.clone(),
|
||||
forge_type: forge.forge_type,
|
||||
};
|
||||
federate.create_forge_instance(&msg).await.unwrap();
|
||||
|
@ -57,7 +61,7 @@ impl Ctx {
|
|||
}
|
||||
|
||||
loop {
|
||||
info!("[crawl][{hostname}] Crawling. page: {page}");
|
||||
info!("[crawl][{url}] Crawling. page: {page}");
|
||||
let res = forge
|
||||
.crawl(
|
||||
self.settings.crawler.items_per_api_call,
|
||||
|
@ -66,23 +70,23 @@ impl Ctx {
|
|||
)
|
||||
.await;
|
||||
if res.repos.is_empty() {
|
||||
info!("[crawl][{hostname}] Finished crawling. pages: {}", page - 1);
|
||||
info!("[crawl][{url}] Finished crawling. pages: {}", page - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (username, u) in res.users.iter() {
|
||||
if !db
|
||||
.user_exists(username, Some(forge.get_hostname()))
|
||||
.user_exists(username, Some(forge.get_url()))
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
info!("[crawl][{hostname}] Creating user: {username}");
|
||||
info!("[crawl][{url}] Creating user: {username}");
|
||||
let msg = u.as_ref().into();
|
||||
db.add_user(&msg).await.unwrap();
|
||||
federate.create_user(&msg).await.unwrap();
|
||||
} else {
|
||||
if !federate
|
||||
.user_exists(username, forge.get_hostname())
|
||||
.user_exists(username, forge.get_url())
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
|
@ -94,17 +98,17 @@ impl Ctx {
|
|||
|
||||
for r in res.repos.iter() {
|
||||
if !db
|
||||
.repository_exists(&r.name, &r.owner.username, r.hostname)
|
||||
.repository_exists(&r.name, &r.owner.username, &r.url)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
info!("[crawl][{hostname}] Creating repository: {}", r.name);
|
||||
info!("[crawl][{url}] Creating repository: {}", r.name);
|
||||
let msg = r.into();
|
||||
db.create_repository(&msg).await.unwrap();
|
||||
federate.create_repository(&msg).await.unwrap();
|
||||
} else {
|
||||
if !federate
|
||||
.repository_exists(&r.name, &r.owner.username, r.hostname)
|
||||
.repository_exists(&r.name, &r.owner.username, &r.url)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
|
@ -116,7 +120,6 @@ impl Ctx {
|
|||
|
||||
page += 1;
|
||||
}
|
||||
federate.tar().await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,21 +136,19 @@ mod tests {
|
|||
#[actix_rt::test]
|
||||
async fn crawl_gitea() {
|
||||
let (db, ctx, federate, _tmp_dir) = sqlx_sqlite::get_ctx().await;
|
||||
ctx.crawl(GITEA_HOST, &db, &federate).await;
|
||||
let hostname = get_hostname(&Url::parse(GITEA_HOST).unwrap());
|
||||
assert!(db.forge_exists(&hostname).await.unwrap());
|
||||
assert!(db
|
||||
.user_exists(GITEA_USERNAME, Some(&hostname))
|
||||
.await
|
||||
.unwrap());
|
||||
let url = Url::parse(GITEA_HOST).unwrap();
|
||||
ctx.crawl(&url, &db, &federate).await;
|
||||
// let hostname = get_hostname(&Url::parse(GITEA_HOST).unwrap());
|
||||
assert!(db.forge_exists(&url).await.unwrap());
|
||||
assert!(db.user_exists(GITEA_USERNAME, Some(&url)).await.unwrap());
|
||||
assert!(db.user_exists(GITEA_USERNAME, None).await.unwrap());
|
||||
for i in 0..100 {
|
||||
let repo = format!("repository_{i}");
|
||||
assert!(db
|
||||
.repository_exists(&repo, GITEA_USERNAME, hostname.as_str())
|
||||
.repository_exists(&repo, GITEA_USERNAME, &url)
|
||||
.await
|
||||
.unwrap())
|
||||
}
|
||||
assert!(db.forge_exists(&hostname).await.unwrap());
|
||||
assert!(db.forge_exists(&url).await.unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ use trust_dns_resolver::{
|
|||
config::{ResolverConfig, ResolverOpts},
|
||||
AsyncResolver,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::utils::get_random;
|
||||
use crate::ArcCtx;
|
||||
|
@ -39,11 +40,15 @@ impl TXTChallenge {
|
|||
format!("starchart-{}", &ctx.settings.server.domain)
|
||||
}
|
||||
|
||||
pub fn get_challenge_txt_key(ctx: &ArcCtx, hostname: &str) -> String {
|
||||
format!("{}.{}", Self::get_challenge_txt_key_prefix(ctx), hostname)
|
||||
pub fn get_challenge_txt_key(ctx: &ArcCtx, hostname: &Url) -> String {
|
||||
format!(
|
||||
"{}.{}",
|
||||
Self::get_challenge_txt_key_prefix(ctx),
|
||||
hostname.host_str().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(ctx: &ArcCtx, hostname: &str) -> Self {
|
||||
pub fn new(ctx: &ArcCtx, hostname: &Url) -> Self {
|
||||
let key = Self::get_challenge_txt_key(ctx, hostname);
|
||||
let value = get_random(VALUES_LEN);
|
||||
Self { key, value }
|
||||
|
@ -62,7 +67,7 @@ impl TXTChallenge {
|
|||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::tests::sqlx_sqlite;
|
||||
pub const BASE_DOMAIN: &str = "forge.forgeflux.org";
|
||||
pub const BASE_DOMAIN: &str = "https://forge.forgeflux.org";
|
||||
pub const VALUE: &str = "ifthisvalueisretrievedbyforgefluxstarchartthenthetestshouldpass";
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -71,12 +76,17 @@ pub mod tests {
|
|||
|
||||
let (_db, ctx, _federate, _tmp_dir) = sqlx_sqlite::get_ctx().await;
|
||||
|
||||
let key = TXTChallenge::get_challenge_txt_key(&ctx, BASE_DOMAIN);
|
||||
let base_hostname = Url::parse(BASE_DOMAIN).unwrap();
|
||||
|
||||
let key = TXTChallenge::get_challenge_txt_key(&ctx, &base_hostname);
|
||||
let mut txt_challenge = TXTChallenge {
|
||||
value: VALUE.to_string(),
|
||||
key: key.clone(),
|
||||
};
|
||||
assert_eq!(TXTChallenge::get_challenge_txt_key(&ctx, BASE_DOMAIN), key,);
|
||||
assert_eq!(
|
||||
TXTChallenge::get_challenge_txt_key(&ctx, &base_hostname),
|
||||
key,
|
||||
);
|
||||
|
||||
assert!(
|
||||
txt_challenge.verify_txt().await.unwrap(),
|
||||
|
|
Loading…
Reference in a new issue