From f097892fd99fe313fedb80361a94f4012070da73 Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Wed, 28 Aug 2024 07:49:12 -0400 Subject: [PATCH] More Documentation and Badges for Projects --- crates/core/src/repository/config.rs | 109 ----------- crates/core/src/repository/mod.rs | 2 +- docs/.gitignore | 3 +- docs/docs/.vitepress/config.ts | 173 +++++++++++------- docs/docs/compiling.md | 27 --- docs/docs/knowledge/ArtifactTypes.md | 7 - docs/docs/knowledge/userpermissions.md | 25 --- docs/docs/repositoryTypes/index.md | 0 docs/docs/repositoryTypes/maven/configs.md | 16 ++ docs/docs/repositoryTypes/maven/index.md | 9 +- .../docs/repositoryTypes/maven/nitroDeploy.md | 0 docs/docs/repositoryTypes/maven/standard.md | 29 +++ docs/docs/repositoryTypes/npm/configs.md | 1 + docs/docs/repositoryTypes/npm/index.md | 1 + nitro_repo/src/app/badge.rs | 96 +++++++++- nitro_repo/src/app/mod.rs | 10 +- nitro_repo/src/error/mod.rs | 5 +- nitro_repo/src/repository/maven/configs.rs | 138 ++++++++++++++ nitro_repo/src/repository/maven/hosted.rs | 57 ++---- nitro_repo/src/repository/maven/mod.rs | 76 +------- nitro_repo/src/repository/npm/login/mod.rs | 6 +- .../src/repository/npm/login/web_login.rs | 9 +- nitro_repo/src/repository/npm/mod.rs | 2 +- nitro_repo/src/repository/repo_http.rs | 7 +- 24 files changed, 438 insertions(+), 370 deletions(-) delete mode 100644 docs/docs/compiling.md delete mode 100644 docs/docs/knowledge/ArtifactTypes.md delete mode 100644 docs/docs/knowledge/userpermissions.md create mode 100644 docs/docs/repositoryTypes/index.md create mode 100644 docs/docs/repositoryTypes/maven/configs.md rename nitro_repo/src/repository/maven/nitro_deploy.md => docs/docs/repositoryTypes/maven/nitroDeploy.md (100%) create mode 100644 docs/docs/repositoryTypes/npm/configs.md create mode 100644 docs/docs/repositoryTypes/npm/index.md create mode 100644 nitro_repo/src/repository/maven/configs.rs diff --git a/crates/core/src/repository/config.rs b/crates/core/src/repository/config.rs index e10438f4..55a55c3a 100644 --- a/crates/core/src/repository/config.rs +++ b/crates/core/src/repository/config.rs @@ -11,7 +11,6 @@ use uuid::Uuid; use crate::database::repository::DBRepositoryConfig; pub mod project; -use super::Policy; pub mod repository_page; #[derive(Debug, Error)] pub enum RepositoryConfigError { @@ -87,111 +86,3 @@ pub async fn get_repository_config_or_default< .map(|x| x.unwrap_or_default()) } pub type DynRepositoryConfigType = Box; -#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] -#[serde(default)] - -pub struct SecurityConfig { - #[schemars(title = "Require Auth Token for Push")] - /// If the repository requires an auth token to be used - pub must_use_auth_token_for_push: bool, -} -#[derive(Debug, Clone, Copy, Default)] -pub struct SecurityConfigType; -impl RepositoryConfigType for SecurityConfigType { - fn get_type(&self) -> &'static str { - "security" - } - fn get_description(&self) -> ConfigDescription { - ConfigDescription { - name: "Security", - description: Some("Security settings for the repository"), - documentation_link: None, - ..Default::default() - } - } - - fn validate_config(&self, config: Value) -> Result<(), RepositoryConfigError> { - let _config: SecurityConfig = serde_json::from_value(config)?; - Ok(()) - } - - fn default(&self) -> Result { - Ok(serde_json::to_value(SecurityConfig::default())?) - } - - fn schema(&self) -> Option { - Some(schema_for!(SecurityConfig)) - } - - fn get_type_static() -> &'static str - where - Self: Sized, - { - "security" - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct PushRulesConfig { - #[schemars(title = "Push Policy")] - /// The push policy. Rather it allows snapshots, stages, or both - pub push_policy: Policy, - /// If yanking is allowed - #[schemars(title = "Yanking Allowed")] - pub yanking_allowed: bool, - /// If overwriting is allowed - #[schemars(title = "Allow Overwrite")] - pub allow_overwrite: bool, - /// If a project exists the user must be a member of the project to push. - #[schemars(title = "Project Members can only push")] - pub must_be_project_member: bool, - #[schemars(title = "Require Nitro Deploy")] - pub require_nitro_deploy: bool, -} -impl Default for PushRulesConfig { - fn default() -> Self { - Self { - push_policy: Default::default(), - yanking_allowed: true, - allow_overwrite: true, - must_be_project_member: Default::default(), - require_nitro_deploy: false, - } - } -} -#[derive(Debug, Clone, Copy, Default)] - -pub struct PushRulesConfigType; -impl RepositoryConfigType for PushRulesConfigType { - fn get_type(&self) -> &'static str { - "push_rules" - } - fn get_description(&self) -> ConfigDescription { - ConfigDescription { - name: "Push Rules", - description: Some("Rules for pushing to the repository"), - documentation_link: None, - ..Default::default() - } - } - fn validate_config(&self, config: Value) -> Result<(), RepositoryConfigError> { - let _config: PushRulesConfig = serde_json::from_value(config)?; - Ok(()) - } - - fn default(&self) -> Result { - Ok(serde_json::to_value(PushRulesConfig::default())?) - } - - fn schema(&self) -> Option { - Some(schema_for!(PushRulesConfig)) - } - - fn get_type_static() -> &'static str - where - Self: Sized, - { - "push_rules" - } -} diff --git a/crates/core/src/repository/mod.rs b/crates/core/src/repository/mod.rs index 9cd1bb97..e3dc8908 100644 --- a/crates/core/src/repository/mod.rs +++ b/crates/core/src/repository/mod.rs @@ -41,9 +41,9 @@ pub enum Visibility { Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default, EnumIter, JsonSchema, EnumIs, )] pub enum Policy { - #[default] Release, Snapshot, + #[default] Mixed, } #[derive(Debug, Error)] diff --git a/docs/.gitignore b/docs/.gitignore index 2e9eb0d4..420101c1 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,2 +1,3 @@ /node_modules -/.vitepress/dist +/docs/.vitepress/dist +/docs/.vitepress/cache diff --git a/docs/docs/.vitepress/config.ts b/docs/docs/.vitepress/config.ts index 0562b2e5..dd39c90c 100644 --- a/docs/docs/.vitepress/config.ts +++ b/docs/docs/.vitepress/config.ts @@ -1,84 +1,115 @@ -import {defineConfig} from 'vitepress' +import { defineConfig } from "vitepress"; export default defineConfig({ - lang: 'en-US', - title: 'Nitro_Repo', - description: 'A Fast Artifact Manager', - lastUpdated: true, - themeConfig: { - nav: [ - {text: 'Home', link: '/', activeMatch: '^/$'}, - { - text: 'System Admin', - link: '/sysAdmin/', - activeMatch: '^/sysAdmin/' - }, - { - text: 'Knowledge Base', - link: '/knowledge/', - activeMatch: '^/knowledge/' - }, - { - text: 'Release Notes', - link: 'https://github.com/wyatt-herkamp/nitro_repo/releases' - } - ], - socialLinks: [ - {icon: 'github', link: 'https://github.com/wyatt-herkamp/nitro_repo'} - ], - sidebar: { - '/': generalInfo(), - '/sysAdmin/': sysAdminBar(), - '/knowledge/': knowledgeBaseBar() - }, + lang: "en-US", + title: "Nitro_Repo", + description: "A Fast Artifact Manager", + lastUpdated: true, + themeConfig: { + nav: [ + { text: "Home", link: "/", activeMatch: "^/$" }, + { + text: "System Admin", + link: "/sysAdmin/", + activeMatch: "^/sysAdmin/", + }, + { + text: "Knowledge Base", + link: "/knowledge/", + activeMatch: "^/knowledge/", + }, + { + text: "Repository Types", + link: "/repositoryTypes/", + activeMatch: "^/repositoryTypes/", + }, + { + text: "Release Notes", + link: "https://github.com/wyatt-herkamp/nitro_repo/releases", + }, + ], + socialLinks: [ + { icon: "github", link: "https://github.com/wyatt-herkamp/nitro_repo" }, + ], + sidebar: { + "/": generalInfo(), + "/sysAdmin/": sysAdminBar(), + "/knowledge/": knowledgeBaseBar(), + "/repositoryTypes/": repositoryTypesBar(), }, - -}) + }, +}); function generalInfo() { - return [ - { - text: 'Nitro Repo', - items: [ - {text: 'What is Nitro Repo?', link: '/'}, - {text: 'Features', link: '/features'}, - {text: 'Compiling', link: '/compiling'}, - {text: 'Contributing', link: '/contributing'}, - ] - } - ] + return [ + { + text: "Nitro Repo", + items: [ + { text: "What is Nitro Repo?", link: "/" }, + { text: "Features", link: "/features" }, + { text: "Contributing", link: "/contributing" }, + ], + }, + ]; } function knowledgeBaseBar() { - return [ - { - text: 'User Management', - items: [ - {text: 'User Permissions', link: '/knowledge/userpermissions'}, - ] - }, { - text: 'Repositories', - items: [ - {text: 'Artifact Types', link: '/knowledge/ArtifactTypes'} - ] - }, { - text: 'Other', - items: [ - {text: 'Internal Workings', link: '/knowledge/InternalWorkings'} - ] - }, - - ] + return [ + { + text: "Other", + items: [ + { text: "Internal Workings", link: "/knowledge/InternalWorkings" }, + ], + }, + ]; } function sysAdminBar() { - return [ - { - text: 'Installing', - items: [ - {text: 'Prepping your System', link: '/sysAdmin/'}, + return [ + { + text: "Installing", + items: [{ text: "Prepping your System", link: "/sysAdmin/" }], + }, + ]; +} - ] - } - ] +function repositoryTypesBar() { + return [ + { + text: "Maven", + link: "/repositoryTypes/maven", + items: [ + { + text: "Maven Standard", + link: "/repositoryTypes/maven/standard", + }, + { + text: "Nitro Deploy", + link: "/repositoryTypes/maven/nitroDeploy", + }, + { + text: "Configs", + link: "/repositoryTypes/maven/configs", + }, + ], + }, + { + text: "NPM", + link: "/repositoryTypes/npm", + items: [ + { + text: "NPM Standard", + link: "/repositoryTypes/npm/standard", + }, + { + text: "Configs", + link: "/repositoryTypes/npm/configs", + }, + { + text: "Common Issues", + link: "/repositoryTypes/npm/errors", + }, + ], + }, + ]; } diff --git a/docs/docs/compiling.md b/docs/docs/compiling.md deleted file mode 100644 index ba9dafe4..00000000 --- a/docs/docs/compiling.md +++ /dev/null @@ -1,27 +0,0 @@ -## Windows Pre Compile Tasks -1. Build the OpenSSL devel library through https://github.com/microsoft/vcpkg#quick-start-windows -2. Setup the MySQL-devel library -3. Recommended to setup [NVM](https://dev.to/skaytech/how-to-install-node-version-manager-nvm-for-windows-10-4nbi) currently we use the latest Node and -4. Setup [Rust](https://rustup.rs/#) -## Linux Pre Compile Tasks -1. Recommended to setup [NVM](https://github.com/nvm-sh/nvm#installing-and-updating) currently we use the latest Node and NPM -2. Setup [Rust](https://rustup.rs/#) -## Compiling -1. Pull latest code from https://github.com/wherkamp/nitro_repo.git `git clone https://github.com/wherkamp/nitro_repo.git` -### Compiling for Use -1. On Linux run the the `build.sh` file. `nitro_repo.tar.gz` file will be produced. -### Frontend Setup -1. Inside the site directory -2. Run `npm install` -3. Run `npm run build` -### Compiling Application -1. Inside the nitro_repo directory -2. run `cargo build --features ssl --release` for full version. Removing `--release` is recommended if you are doing development. `--features ssl` adds ssl support. You can remove this if you do not want the web server to use ssl - -### After -1. After that you should have a nitro_repo executable either in your target/debug/ or target/release depending if you had a release tag set -## Common Issues -### Mysql Linkage Issue -1. You are honestly going to have to look this one up. Depending on your system you have to specific paths to the right files -### OpenSSL linkage issues. -1. Really only a Windows issue but please ensure vcpkg was setup correctly diff --git a/docs/docs/knowledge/ArtifactTypes.md b/docs/docs/knowledge/ArtifactTypes.md deleted file mode 100644 index 69b0950a..00000000 --- a/docs/docs/knowledge/ArtifactTypes.md +++ /dev/null @@ -1,7 +0,0 @@ -# Artifact Types - - -| Artifact Name | Supports Project Pages/Badges | Supports Manual Upload | Notes | -|--|--|--|--| -| Maven | Yes | Yes | N/A | -| NPM | Yes | No | Project Pages and Repositories pages currently. Do not provide instructions for how to add registry or depend | \ No newline at end of file diff --git a/docs/docs/knowledge/userpermissions.md b/docs/docs/knowledge/userpermissions.md deleted file mode 100644 index 4efcfbed..00000000 --- a/docs/docs/knowledge/userpermissions.md +++ /dev/null @@ -1,25 +0,0 @@ -# Available Permissions for Users - -1. `Admin` Overrides everything -2. `User Manager`. Can Manage User -3. `Repository Manager`. Creates Repos, Create Storages, and modify settings -4. `Deployer` Can have specified repositories they can access -5. `Viewer`Can have specified repositories they can access. Only matters if repository is set to private. If the deployer permission exists the reader permission is present too. - - -## Deployer Special Properties - -- `*` is a wild card -- `{}` Will be parsed as json -- `()` are just meant as variables for example -- Available Repository classifiers. `type` Meaning the repository type. Such as `NPM` or `Maven`. `policy` Reference if its `Snapshot`, `Mixed`, `Release`. - -### Examples - -1. `*/*` can deploy to all repositories -2. `(storage_name)/*` Can deploy to all repositories in a specific storage -3. `(storage_name)/(repository)` -4. `(storage_name)/{"type": "npm"}` can deploy to all npm repos -5. `(storage_name)/{"policy": "Snapshot"}` can deploy to all Snapshot. Policy can be combined with type. - -Any combination of the rules can be combined so `*/{"policy":"Release", "type": "maven"}` So this will say any storage. But only maven policies set to Release diff --git a/docs/docs/repositoryTypes/index.md b/docs/docs/repositoryTypes/index.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/docs/repositoryTypes/maven/configs.md b/docs/docs/repositoryTypes/maven/configs.md new file mode 100644 index 00000000..f2cf9e35 --- /dev/null +++ b/docs/docs/repositoryTypes/maven/configs.md @@ -0,0 +1,16 @@ +# Maven Repository + + +## Maven Push Rules - Options - Hosted Only + +- `push_policy`: The Policy for pushing artifacts to the repository. This can be one of the following: + - `Release`: Only accepts releases. Denies snapshots. + - `Snapshot`: Only accepts snapshots. Denies releases. + - `Mixed`: Accepts both releases and snapshots. +- `yanking_allowed`: Whether or not yanking is allowed. This is a boolean value. +- `allow_overwrite`: Whether or not overwriting artifacts is allowed. This is a boolean value. +- `must_be_project_member`: Whether or not the user must be a member of the project to push artifacts. This is a boolean value. When using standard maven deploy. This feature may not work as expected. +- `require_nitro_deploy`: If true standard maven deploy will not work. This is a boolean value. +- `must_use_auth_token_for_push`: If true the user must use an auth token to push artifacts. This is a boolean value. When using standard maven deploy. You can put your auth token in the password field and the username field can be anything. + + diff --git a/docs/docs/repositoryTypes/maven/index.md b/docs/docs/repositoryTypes/maven/index.md index c1845f91..1f2eac31 100644 --- a/docs/docs/repositoryTypes/maven/index.md +++ b/docs/docs/repositoryTypes/maven/index.md @@ -1 +1,8 @@ -# Maven Repository \ No newline at end of file +# Maven Repository + +Maven Repositories have two modes. + +- **Hosted** - The repository is hosted on the server and is used to store artifacts. This is used to create a regular Maven Repository. +- **Proxy** - The repository is a proxy to another repository. This is used to cache artifacts from another repository. + + diff --git a/nitro_repo/src/repository/maven/nitro_deploy.md b/docs/docs/repositoryTypes/maven/nitroDeploy.md similarity index 100% rename from nitro_repo/src/repository/maven/nitro_deploy.md rename to docs/docs/repositoryTypes/maven/nitroDeploy.md diff --git a/docs/docs/repositoryTypes/maven/standard.md b/docs/docs/repositoryTypes/maven/standard.md index 30c10120..a1b2c30d 100644 --- a/docs/docs/repositoryTypes/maven/standard.md +++ b/docs/docs/repositoryTypes/maven/standard.md @@ -1,3 +1,32 @@ # Documentation for the Maven Repository Format This is the layout for Maven Repositories and how they work. Use this document to help you improve nitro_repo or even make your own repository + +## Deploying + +Maven Deploying is done by sending a PUT request to the location of the file. + +[PUT] `{BASE_PATH}/repositories/{storage}/{repository}/{fileName}` + +File Name being the path of the location. + +The groupId is turned into a path so `com.example` would be `com/example` and then they add the artifactId and version to the end. +So if you have a file that is `com.example:example:1.0.0` the path would be `com/example/example/1.0.0` + +### Authentication + +Maven uses the basic authentication method. This is done by sending the header `Authorization: Basic base64("{username}:{password}")` + + +## Downloading + +Maven downloading is done by sending a GET request to the location of the file. + +[GET] `{BASE_PATH}/repositories/{storage}/{repository}/{fileName}` + +### Authentication + +If the repository is private then the repository should send a WWW-Authenticate header and will let the client know that it needs to authenticate. + +It will use the same basic authentication method as deploying. + diff --git a/docs/docs/repositoryTypes/npm/configs.md b/docs/docs/repositoryTypes/npm/configs.md new file mode 100644 index 00000000..faa85065 --- /dev/null +++ b/docs/docs/repositoryTypes/npm/configs.md @@ -0,0 +1 @@ +# NPM Configs \ No newline at end of file diff --git a/docs/docs/repositoryTypes/npm/index.md b/docs/docs/repositoryTypes/npm/index.md new file mode 100644 index 00000000..b0db903d --- /dev/null +++ b/docs/docs/repositoryTypes/npm/index.md @@ -0,0 +1 @@ +# NPM \ No newline at end of file diff --git a/nitro_repo/src/app/badge.rs b/nitro_repo/src/app/badge.rs index 2ddba709..8818fe66 100644 --- a/nitro_repo/src/app/badge.rs +++ b/nitro_repo/src/app/badge.rs @@ -5,7 +5,10 @@ use axum::{ }; use http::StatusCode; use nr_core::{ - database::repository::DBRepositoryConfig, + database::{ + project::{DBProject, ProjectDBType}, + repository::DBRepositoryConfig, + }, repository::config::{ project::{BadgeSettings, ProjectConfig, ProjectConfigType}, RepositoryConfigType, @@ -18,7 +21,10 @@ use crate::{error::InternalError, repository::Repository}; use super::NitroRepo; #[derive(OpenApi)] -#[openapi(paths(repository_badge), components(schemas()))] +#[openapi( + paths(repository_badge, project_badge, supports_badges), + components(schemas()) +)] pub struct BadgeRoutes; pub fn badge_routes() -> axum::Router { axum::Router::new() @@ -26,6 +32,10 @@ pub fn badge_routes() -> axum::Router { "/:storage/:repository", axum::routing::get(repository_badge), ) + .route( + "/:storage/:repository/project/:project", + axum::routing::get(project_badge), + ) .route( "/:storage/:repository/supports", axum::routing::get(supports_badges), @@ -137,6 +147,88 @@ async fn repository_badge( .unwrap()) } +#[derive(Deserialize)] +struct ProjectBadgeRequest { + pub storage: String, + pub repository: String, + pub project: String, +} +#[utoipa::path( + get, + path = "/{storage}/{repository}/project/{project}", + responses( + (status = 200, description = "Generates the Repository Badge", body = String) + ) +)] +async fn project_badge( + Path(ProjectBadgeRequest { + storage, + repository, + project, + }): Path, + State(site): State, +) -> Result { + let Some(repo) = site + .get_repository_from_names((storage, repository)) + .await + .map_err(InternalError::from)? + else { + return Ok(Response::builder() + .status(http::StatusCode::NOT_FOUND) + .body("Repository not found".into()) + .unwrap()); + }; + if !repo + .config_types() + .contains(&ProjectConfigType::get_type_static()) + { + return Ok(Response::builder() + .status(http::StatusCode::BAD_REQUEST) + .body("This Repository does not support badges".into()) + .unwrap()); + } + + let badge_settings = DBRepositoryConfig::::get_config( + repo.id(), + ProjectConfigType::get_type_static(), + site.as_ref(), + ) + .await + .map_err(InternalError::from)? + .map(|c| c.value.0.badge_settings) + .unwrap_or_default(); + let Some(project) = DBProject::find_by_project_key(&project, repo.id(), site.as_ref()) + .await + .map_err(InternalError::from)? + else { + return Ok(Response::builder() + .status(http::StatusCode::NOT_FOUND) + .body("Project not found".into()) + .unwrap()); + }; + let latest_release = project + .latest_release + .as_deref() + .or(project.latest_pre_release.as_deref()) + .unwrap_or("No Release"); + + let badge = match generate_badge(&badge_settings, &project.name, &latest_release) { + Ok(ok) => ok, + Err(err) => { + return Ok(Response::builder() + .status(http::StatusCode::INTERNAL_SERVER_ERROR) + .body(format!("Error generating badge: {}", err).into()) + .unwrap()); + } + }; + + Ok(Response::builder() + .header(http::header::CONTENT_TYPE, "image/svg+xml") + .status(StatusCode::OK) + .body(badge.svg().into()) + .unwrap()) +} + fn generate_badge( settings: &BadgeSettings, label: &str, diff --git a/nitro_repo/src/app/mod.rs b/nitro_repo/src/app/mod.rs index cec6b90f..cac60679 100644 --- a/nitro_repo/src/app/mod.rs +++ b/nitro_repo/src/app/mod.rs @@ -17,8 +17,7 @@ use nr_core::{ user::user_utils, }, repository::config::{ - project::ProjectConfigType, repository_page::RepositoryPageType, PushRulesConfigType, - RepositoryConfigType, SecurityConfigType, + project::ProjectConfigType, repository_page::RepositoryPageType, RepositoryConfigType, }, }; use nr_storage::{DynStorage, Storage, StorageConfig, StorageFactory, STORAGE_FACTORIES}; @@ -36,7 +35,9 @@ use utoipa::ToSchema; use uuid::Uuid; pub mod open_api; use crate::repository::{ - maven::{MavenRepositoryConfigType, MavenRepositoryType}, + maven::{ + MavenPushRules, MavenPushRulesConfigType, MavenRepositoryConfigType, MavenRepositoryType, + }, npm::{NPMRegistryConfigType, NpmRegistryType}, DynRepository, RepositoryType, StagingConfig, }; @@ -344,11 +345,10 @@ impl NitroRepo { pub type NitroRepoState = State; pub static REPOSITORY_CONFIG_TYPES: &'static [&dyn RepositoryConfigType] = &[ - &PushRulesConfigType, - &SecurityConfigType, &ProjectConfigType, &RepositoryPageType, &MavenRepositoryConfigType, + &MavenPushRulesConfigType, &NPMRegistryConfigType, ]; pub static REPOSITORY_TYPES: &'static [&dyn RepositoryType] = diff --git a/nitro_repo/src/error/mod.rs b/nitro_repo/src/error/mod.rs index 734f0eb7..16323643 100644 --- a/nitro_repo/src/error/mod.rs +++ b/nitro_repo/src/error/mod.rs @@ -33,8 +33,11 @@ impl IntoResponse for SQLXError { .unwrap() } } - +/// Allows creating a response from an error pub trait IntoErrorResponse: Error + Send + Sync { + /// Converts the error into a response + /// + /// It must be of type of Box to allow for dynamic dispatch fn into_response_boxed(self: Box) -> axum::response::Response; } impl IntoErrorResponse for sqlx::Error { diff --git a/nitro_repo/src/repository/maven/configs.rs b/nitro_repo/src/repository/maven/configs.rs new file mode 100644 index 00000000..edebc3b1 --- /dev/null +++ b/nitro_repo/src/repository/maven/configs.rs @@ -0,0 +1,138 @@ +use nr_core::repository::{ + config::{ConfigDescription, RepositoryConfigError, RepositoryConfigType}, + Policy, +}; +use schemars::{schema_for, JsonSchema, Schema}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use super::proxy::MavenProxyConfig; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(tag = "type", content = "config")] +pub enum MavenRepositoryConfig { + Hosted, + Proxy(MavenProxyConfig), +} +impl MavenRepositoryConfig { + pub fn is_same_type(&self, other: &MavenRepositoryConfig) -> bool { + match (self, other) { + (MavenRepositoryConfig::Hosted, MavenRepositoryConfig::Hosted) => true, + (MavenRepositoryConfig::Proxy(_), MavenRepositoryConfig::Proxy(_)) => true, + _ => false, + } + } +} +#[derive(Debug, Clone, Default)] +pub struct MavenRepositoryConfigType; +impl RepositoryConfigType for MavenRepositoryConfigType { + fn get_type(&self) -> &'static str { + "maven" + } + + fn get_type_static() -> &'static str + where + Self: Sized, + { + "maven" + } + fn schema(&self) -> Option { + Some(schema_for!(MavenRepositoryConfig)) + } + fn validate_config(&self, config: Value) -> Result<(), RepositoryConfigError> { + let config: MavenRepositoryConfig = serde_json::from_value(config)?; + Ok(()) + } + fn validate_change(&self, old: Value, new: Value) -> Result<(), RepositoryConfigError> { + let new: MavenRepositoryConfig = serde_json::from_value(new)?; + let old: MavenRepositoryConfig = serde_json::from_value(old)?; + if !old.is_same_type(&new) { + return Err(RepositoryConfigError::InvalidChange( + "maven", + "Cannot change the type of Maven Repository", + )); + } + Ok(()) + } + fn default(&self) -> Result { + let config = MavenRepositoryConfig::Hosted; + Ok(serde_json::to_value(config).unwrap()) + } + fn get_description(&self) -> ConfigDescription { + ConfigDescription { + name: "Maven Repository Config", + description: Some("Handles the type of Maven Repository"), + documentation_link: None, + ..Default::default() + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(default)] +pub struct MavenPushRules { + #[schemars(title = "Push Policy")] + /// The push policy. Rather it allows snapshots, stages, or both + pub push_policy: Policy, + /// If yanking is allowed + #[schemars(title = "Yanking Allowed")] + pub yanking_allowed: bool, + /// If overwriting is allowed + #[schemars(title = "Allow Overwrite")] + pub allow_overwrite: bool, + /// If a project exists the user must be a member of the project to push. + #[schemars(title = "Project Members can only push")] + pub must_be_project_member: bool, + #[schemars(title = "Require Nitro Deploy")] + pub require_nitro_deploy: bool, + #[schemars(title = "Require Auth Token for Push")] + /// If the repository requires an auth token to be used + pub must_use_auth_token_for_push: bool, +} +impl Default for MavenPushRules { + fn default() -> Self { + Self { + push_policy: Default::default(), + yanking_allowed: true, + allow_overwrite: true, + must_be_project_member: Default::default(), + require_nitro_deploy: false, + must_use_auth_token_for_push: false, + } + } +} +#[derive(Debug, Clone, Copy, Default)] + +pub struct MavenPushRulesConfigType; +impl RepositoryConfigType for MavenPushRulesConfigType { + fn get_type(&self) -> &'static str { + Self::get_type_static() + } + + fn get_type_static() -> &'static str + where + Self: Sized, + { + "maven_push_rules" + } + fn get_description(&self) -> ConfigDescription { + ConfigDescription { + name: "Push Rules", + description: Some("Rules for pushing to a maven repository"), + documentation_link: None, + ..Default::default() + } + } + fn validate_config(&self, config: Value) -> Result<(), RepositoryConfigError> { + let _config: MavenPushRules = serde_json::from_value(config)?; + Ok(()) + } + + fn default(&self) -> Result { + Ok(serde_json::to_value(MavenPushRules::default())?) + } + + fn schema(&self) -> Option { + Some(schema_for!(MavenPushRules)) + } +} diff --git a/nitro_repo/src/repository/maven/hosted.rs b/nitro_repo/src/repository/maven/hosted.rs index 355ac4e9..d25ef461 100644 --- a/nitro_repo/src/repository/maven/hosted.rs +++ b/nitro_repo/src/repository/maven/hosted.rs @@ -15,8 +15,7 @@ use nr_core::{ get_repository_config_or_default, project::{ProjectConfig, ProjectConfigType}, repository_page::RepositoryPageType, - PushRulesConfig, PushRulesConfigType, RepositoryConfigType, SecurityConfig, - SecurityConfigType, + RepositoryConfigType, }, project::ProjectResolution, Visibility, @@ -32,20 +31,20 @@ use uuid::Uuid; use crate::{ app::NitroRepo, repository::{ - maven::MavenRepositoryConfigType, Repository, RepositoryFactoryError, - RepositoryHandlerError, + maven::{configs::MavenPushRulesConfigType, MavenRepositoryConfigType}, + utils::RepositoryExt, + Repository, RepositoryFactoryError, RepositoryHandlerError, }, }; -use super::{utils::MavenRepositoryExt, RepoResponse, RepositoryRequest}; +use super::{configs::MavenPushRules, utils::MavenRepositoryExt, RepoResponse, RepositoryRequest}; #[derive(derive_more::Debug)] pub struct MavenHostedInner { pub id: Uuid, pub name: String, pub active: AtomicBool, pub visibility: RwLock, - pub push_rules: RwLock, - pub security: RwLock, + pub push_rules: RwLock, pub project: RwLock, #[debug(skip)] pub storage: DynStorage, @@ -56,6 +55,7 @@ impl MavenHostedInner {} #[derive(Debug, Clone, Deref)] pub struct MavenHosted(Arc); impl MavenRepositoryExt for MavenHosted {} +impl RepositoryExt for MavenHosted {} impl MavenHosted { #[instrument] pub async fn standard_maven_deploy( @@ -104,18 +104,11 @@ impl MavenHosted { storage: DynStorage, site: NitroRepo, ) -> Result { - let security_db = get_repository_config_or_default::( - repository.id, - site.as_ref(), - ) + let push_rules_db = get_repository_config_or_default::< + MavenPushRulesConfigType, + MavenPushRules, + >(repository.id, site.as_ref()) .await?; - debug!("Loaded Security Config: {:?}", security_db); - let push_rules_db = - get_repository_config_or_default::( - repository.id, - site.as_ref(), - ) - .await?; debug!("Loaded Push Rules Config: {:?}", push_rules_db); let project_db = get_repository_config_or_default::( @@ -131,7 +124,6 @@ impl MavenHosted { active: active, visibility: RwLock::new(repository.visibility), push_rules: RwLock::new(push_rules_db.value.0), - security: RwLock::new(security_db.value.0), project: RwLock::new(project_db.value.0), storage, site, @@ -172,8 +164,7 @@ impl Repository for MavenHosted { fn config_types(&self) -> Vec<&str> { vec![ RepositoryPageType::get_type_static(), - PushRulesConfigType::get_type_static(), - SecurityConfigType::get_type_static(), + MavenPushRulesConfigType::get_type_static(), ProjectConfigType::get_type_static(), MavenRepositoryConfigType::get_type_static(), ] @@ -188,17 +179,12 @@ impl Repository for MavenHosted { }; self.0.active.store(is_active, atomic::Ordering::Relaxed); - let push_rules_db = - get_repository_config_or_default::( - self.id, - self.site.as_ref(), - ) - .await?; - let security_db = get_repository_config_or_default::( - self.id, - self.site.as_ref(), - ) + let push_rules_db = get_repository_config_or_default::< + MavenPushRulesConfigType, + MavenPushRules, + >(self.id, self.site.as_ref()) .await?; + let project_config_db = get_repository_config_or_default::( self.id, @@ -210,10 +196,7 @@ impl Repository for MavenHosted { let mut push_rules = self.push_rules.write(); *push_rules = push_rules_db.value.0; } - { - let mut security = self.security.write(); - *security = security_db.value.0; - } + { let mut project_config = self.project.write(); *project_config = project_config_db.value.0; @@ -262,8 +245,8 @@ impl Repository for MavenHosted { ) -> Result { info!("Handling PUT Request for Repository: {}", self.id); { - let security = self.security.read(); - if security.must_use_auth_token_for_push && !request.authentication.has_auth_token() { + let push_rules = self.push_rules.read(); + if push_rules.must_use_auth_token_for_push && !request.authentication.has_auth_token() { info!("Repository requires an auth token for push"); return Ok(RepoResponse::require_auth_token()); } diff --git a/nitro_repo/src/repository/maven/mod.rs b/nitro_repo/src/repository/maven/mod.rs index 9c857aec..c67a4f3a 100644 --- a/nitro_repo/src/repository/maven/mod.rs +++ b/nitro_repo/src/repository/maven/mod.rs @@ -3,91 +3,28 @@ use crate::app::NitroRepo; use ::http::status::StatusCode; use ahash::HashMap; use axum::response::IntoResponse; +pub use configs::*; use futures::future::BoxFuture; use hosted::MavenHosted; use nr_core::{ builder_error, database::repository::{DBRepository, DBRepositoryConfig}, repository::{ - config::{ - project::ProjectConfigType, ConfigDescription, PushRulesConfigType, - RepositoryConfigError, RepositoryConfigType, SecurityConfigType, - }, + config::{project::ProjectConfigType, RepositoryConfigType}, project::ReleaseType, }, storage::StoragePath, }; use nr_macros::DynRepositoryHandler; use nr_storage::DynStorage; -use proxy::{MavenProxy, MavenProxyConfig}; -use schemars::{schema_for, JsonSchema}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - +use proxy::MavenProxy; +mod configs; use super::{DynRepository, Repository, RepositoryFactoryError, RepositoryType}; pub mod hosted; pub mod nitro_deploy; pub mod proxy; pub mod utils; -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(tag = "type", content = "config")] -pub enum MavenRepositoryConfig { - Hosted, - Proxy(MavenProxyConfig), -} -impl MavenRepositoryConfig { - pub fn is_same_type(&self, other: &MavenRepositoryConfig) -> bool { - match (self, other) { - (MavenRepositoryConfig::Hosted, MavenRepositoryConfig::Hosted) => true, - (MavenRepositoryConfig::Proxy(_), MavenRepositoryConfig::Proxy(_)) => true, - _ => false, - } - } -} -#[derive(Debug, Clone, Default)] -pub struct MavenRepositoryConfigType; -impl RepositoryConfigType for MavenRepositoryConfigType { - fn get_type(&self) -> &'static str { - "maven" - } - fn get_type_static() -> &'static str - where - Self: Sized, - { - "maven" - } - fn schema(&self) -> Option { - Some(schema_for!(MavenRepositoryConfig)) - } - fn validate_config(&self, config: Value) -> Result<(), RepositoryConfigError> { - let config: MavenRepositoryConfig = serde_json::from_value(config)?; - Ok(()) - } - fn validate_change(&self, old: Value, new: Value) -> Result<(), RepositoryConfigError> { - let new: MavenRepositoryConfig = serde_json::from_value(new)?; - let old: MavenRepositoryConfig = serde_json::from_value(old)?; - if !old.is_same_type(&new) { - return Err(RepositoryConfigError::InvalidChange( - "maven", - "Cannot change the type of Maven Repository", - )); - } - Ok(()) - } - fn default(&self) -> Result { - let config = MavenRepositoryConfig::Hosted; - Ok(serde_json::to_value(config).unwrap()) - } - fn get_description(&self) -> ConfigDescription { - ConfigDescription { - name: "Maven Repository Config", - description: Some("Handles the type of Maven Repository"), - documentation_link: None, - ..Default::default() - } - } -} #[derive(Debug, Default)] pub struct MavenRepositoryType; @@ -98,8 +35,7 @@ impl RepositoryType for MavenRepositoryType { fn config_types(&self) -> Vec<&str> { vec![ - PushRulesConfigType::get_type_static(), - SecurityConfigType::get_type_static(), + MavenPushRulesConfigType::get_type_static(), ProjectConfigType::get_type_static(), ] } @@ -109,7 +45,7 @@ impl RepositoryType for MavenRepositoryType { type_name: "maven", name: "Maven", description: "A Maven Repository", - documentation_url: Some("https://maven.apache.org/"), + documentation_url: Some("https://nitro-repo.kingtux.dev/repositoryTypes/maven/"), is_stable: true, required_configs: vec![MavenRepositoryConfigType::get_type_static()], } diff --git a/nitro_repo/src/repository/npm/login/mod.rs b/nitro_repo/src/repository/npm/login/mod.rs index 8d8e2b28..5afe3fe3 100644 --- a/nitro_repo/src/repository/npm/login/mod.rs +++ b/nitro_repo/src/repository/npm/login/mod.rs @@ -7,11 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::repository::RepoResponse; pub mod couch_db; pub mod web_login; -#[derive(Debug, Serialize, Deserialize)] -pub struct NewLoginResponse { - pub done_url: String, - pub login_url: String, -} + #[derive(Debug, From)] pub enum LoginResponse { ValidCouchDBLogin(CouchDBLoginResponse), diff --git a/nitro_repo/src/repository/npm/login/web_login.rs b/nitro_repo/src/repository/npm/login/web_login.rs index ecf1ca4a..f8fce12d 100644 --- a/nitro_repo/src/repository/npm/login/web_login.rs +++ b/nitro_repo/src/repository/npm/login/web_login.rs @@ -1,12 +1,19 @@ +use serde::{Deserialize, Serialize}; + use crate::repository::{ npm::utils::NpmRegistryExt, RepoResponse, RepositoryHandlerError, RepositoryRequest, }; use super::LoginResponse; - +#[derive(Debug, Serialize, Deserialize)] +pub struct WebLoginResponse { + pub done_url: String, + pub login_url: String, +} pub async fn perform_login( repository: &impl NpmRegistryExt, request: RepositoryRequest, ) -> Result { + // TODO: Implement Web Login return Ok(LoginResponse::UnsupportedLogin.into()); } diff --git a/nitro_repo/src/repository/npm/mod.rs b/nitro_repo/src/repository/npm/mod.rs index 14738699..a1ab76d2 100644 --- a/nitro_repo/src/repository/npm/mod.rs +++ b/nitro_repo/src/repository/npm/mod.rs @@ -103,7 +103,7 @@ impl RepositoryType for NpmRegistryType { type_name: "npm", name: "NPM", description: "A NPM Registry", - documentation_url: None, + documentation_url: Some("https://nitro-repo.kingtux.dev/repositoryTypes/npm/"), is_stable: true, required_configs: vec![NPMRegistryConfigType::get_type_static()], } diff --git a/nitro_repo/src/repository/repo_http.rs b/nitro_repo/src/repository/repo_http.rs index a2b86cc6..c97f00f9 100644 --- a/nitro_repo/src/repository/repo_http.rs +++ b/nitro_repo/src/repository/repo_http.rs @@ -1,4 +1,4 @@ -use std::{any::type_name, error::Error}; +use std::error::Error; use crate::{ app::{ @@ -26,7 +26,6 @@ use http_body_util::BodyExt; use nr_core::storage::{InvalidStoragePath, StoragePath}; use nr_storage::{StorageFile, StorageFileMeta, StorageFileReader}; use serde::Deserialize; -use serde_json::Value; use tracing::{debug, error, instrument, span, Level}; mod header; mod repo_auth; @@ -49,10 +48,6 @@ impl RepositoryRequestBody { self, ) -> Result { let body = self.body_as_bytes().await?; - if body.is_empty() { - let message = format!("Body is empty. Expected JSON for {}", type_name::()); - return Err(BadRequestErrors::Other(message).into()); - } serde_json::from_slice(&body).map_err(RepositoryHandlerError::from) } #[cfg(debug_assertions)]