starchart/db/db-core/src/lib.rs
Aravinth Manivannan e726f2234d
feat: use fixed value for an instance for DNS challenge
DESCRIPTION
    Randomly generated values for forge ownership verification through
    DNS TXT records was making development process complicated. Using
    starchart instance's hostname for the TXT record's value is secure
    enough for our use case.

    This patch gets rid of all the code necessary to implement random
    value challenges
2023-02-22 12:05:49 +05:30

272 lines
8.3 KiB
Rust

/*
* ForgeFlux StarChart - A federated software forge spider
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#![warn(missing_docs)]
//! # `Starchart` database operations
//!
//! Traits and datastructures used in Starchart to interact with database.
//!
//! To use an unsupported database with Starchart, traits present within this crate should be
//! implemented.
//!
//!
//! ## Organisation
//!
//! Database functionallity is divided accross various modules:
//!
//! - [errors](crate::auth): error data structures used in this crate
//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting
//! connection from pool
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use url::Url;
pub mod errors;
pub mod ops;
#[cfg(feature = "test")]
pub mod tests;
use dev::*;
pub use ops::GetConnection;
pub mod prelude {
//! useful imports for users working with a supported database
pub use super::errors::*;
pub use super::ops::*;
pub use super::*;
}
pub mod dev {
//! useful imports for supporting a new database
pub use super::prelude::*;
pub use async_trait::async_trait;
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// create a new forge on the database
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,
/// is this forge an import
pub import: bool,
}
/// 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 {
/// 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
pub html_link: String,
/// OPTIONAL: html link to the user's profile photo
pub profile_photo: Option<String>,
/// is this user an import
pub import: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// add new user to database
pub struct AddUser<'a> {
/// 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
pub html_link: &'a str,
/// OPTIONAL: html link to the user's profile photo
pub profile_photo: Option<&'a str>,
/// is this user an import
pub import: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// add new repository to database
pub struct AddRepository<'a> {
/// html link to the repository
pub html_link: &'a str,
/// repository topic tags
pub tags: Option<Vec<&'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
pub owner: &'a str,
/// repository description, if any
pub description: Option<&'a str>,
/// repository website, if any
pub website: Option<&'a str>,
/// is this repository an import
pub import: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// data representing a forge instance
pub struct Forge {
/// url of the forge
pub url: String,
/// type of the forge
pub forge_type: ForgeImplementation,
/// last crawl
pub last_crawl_on: Option<i64>,
/// is this forge an import
pub import: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// repository
pub struct Repository {
/// html link to the repository
pub html_url: String,
/// repository topic tags
pub tags: Option<Vec<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
pub username: String,
/// repository description, if any
pub description: Option<String>,
/// repository website, if any
pub website: Option<String>,
/// is this repository an import
pub import: bool,
}
#[async_trait]
/// Starchart's database requirements. To implement support for $Database, kindly implement this
/// trait.
pub trait SCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
/// ping DB
async fn ping(&self) -> bool;
/// create forge instance
async fn create_forge_instance(&self, f: &CreateForge) -> DBResult<()>;
/// get forge instance data
async fn get_forge(&self, url: &Url) -> DBResult<Forge>;
/// delete forge instance
async fn delete_forge_instance(&self, url: &Url) -> DBResult<()>;
/// check if a forge instance exists
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>;
/// Get all forges
async fn get_all_forges(&self, offset: u32, limit: u32) -> DBResult<Vec<Forge>>;
/// add new user to database
async fn add_user(&self, u: &AddUser) -> DBResult<()>;
/// get user data
async fn get_user(&self, username: &str, url: &Url) -> DBResult<User>;
/// check if an user exists. When url of a forge instance is provided, username search is
/// done only on that forge
async fn user_exists(&self, username: &str, url: Option<&Url>) -> DBResult<bool>;
/// delete user
async fn delete_user(&self, username: &str, url: &Url) -> DBResult<()>;
/// delete repository
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, url: &Url) -> DBResult<bool>;
/// Get all repositories
async fn get_all_repositories(&self, offset: u32, limit: u32) -> DBResult<Vec<Repository>>;
/// add new repository to database.
async fn create_repository(&self, r: &AddRepository) -> DBResult<()>;
}
/// Trait to clone SCDatabase
pub trait CloneSPDatabase {
/// clone DB
fn clone_db(&self) -> Box<dyn SCDatabase>;
}
impl<T> CloneSPDatabase for T
where
T: SCDatabase + Clone + 'static,
{
fn clone_db(&self) -> Box<dyn SCDatabase> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn SCDatabase> {
fn clone(&self) -> Self {
(**self).clone_db()
}
}
/// Forge type: Gitea, Sourcehut, GitLab, etc. Support is currently only available for Gitea
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ForgeImplementation {
/// [Gitea](https://gitea.io) softare forge
Gitea,
}
impl ForgeImplementation {
/// Convert [ForgeImplementation] to [str]
pub const fn to_str(&self) -> &'static str {
match self {
ForgeImplementation::Gitea => "gitea",
}
}
}
impl FromStr for ForgeImplementation {
type Err = DBError;
/// Convert [str] to [ForgeImplementation]
fn from_str(s: &str) -> DBResult<Self> {
const GITEA: &str = ForgeImplementation::Gitea.to_str();
let s = s.trim();
match s {
GITEA => Ok(Self::Gitea),
_ => Err(DBError::UnknownForgeType(s.to_owned())),
}
}
}