use std::collections::{BTreeMap, HashSet};
#[cfg(feature = "graphql")]
use async_graphql::scalar;
use n5i::PodSecurityStandard;
pub use n5i::utils::MultiLanguageItem;
use serde::{Deserialize, Serialize};
use crate::utils::true_default;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Default)]
#[cfg_attr(feature = "graphql", derive(async_graphql::Enum))]
pub enum SaasCompatibility {
Compatible,
Incompatible,
#[default]
Unknown,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum Dependency {
OneDependency(String),
AlternativeDependency(HashSet<String>),
}
#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
pub struct SvcPorts {
pub udp: Vec<u16>,
pub tcp: Vec<u16>,
}
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct Permission {
pub id: String,
pub name: MultiLanguageItem,
pub description: MultiLanguageItem,
pub includes: Vec<String>,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub secrets: BTreeMap<String, Vec<String>>,
pub hidden: Vec<String>,
pub services: BTreeMap<String, SvcPorts>,
pub open_to_all_users: bool,
#[serde(default)]
pub volumes: Vec<VolumePermission>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct VolumePermission {
pub volume: String,
pub sub_paths: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(tag = "type")]
pub enum Setting {
Enum {
name: MultiLanguageItem,
description: MultiLanguageItem,
values: Vec<String>,
default: Option<String>,
#[serde(default)]
immutable: bool,
},
String {
name: MultiLanguageItem,
description: MultiLanguageItem,
default: Option<String>,
max_len: Option<usize>,
string_type: Option<StringType>,
#[serde(default = "true_default")]
required: bool,
placeholder: Option<MultiLanguageItem>,
#[serde(default)]
immutable: bool,
},
Bool {
name: MultiLanguageItem,
description: MultiLanguageItem,
default: bool,
#[serde(default)]
immutable: bool,
},
Int {
name: MultiLanguageItem,
description: MultiLanguageItem,
default: Option<i64>,
min: Option<i64>,
max: Option<i64>,
step_size: Option<i64>,
placeholder: Option<MultiLanguageItem>,
#[serde(default)]
immutable: bool,
},
Float {
name: MultiLanguageItem,
description: MultiLanguageItem,
default: Option<f64>,
min: Option<f64>,
max: Option<f64>,
step_size: Option<f64>,
placeholder: Option<MultiLanguageItem>,
#[serde(default)]
immutable: bool,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum StringType {
#[serde(rename = "password")]
Password,
#[serde(rename = "email")]
Email,
#[serde(rename = "url")]
Url,
#[serde(rename = "ip")]
Ip,
}
impl Setting {
pub fn default_value(&self) -> serde_json::Value {
match self {
Setting::Enum { default, .. } => match default {
Some(default) => serde_json::Value::String(default.clone()),
None => serde_json::Value::Null,
},
Setting::String { default, .. } => match default {
Some(default) => serde_json::Value::String(default.clone()),
None => serde_json::Value::Null,
},
Setting::Bool { default, .. } => serde_json::Value::Bool(*default),
Setting::Int { default, .. } => match default {
Some(default) => serde_json::Value::Number(serde_json::Number::from(*default)),
None => serde_json::Value::Null,
},
Setting::Float { default, .. } => match default {
Some(default) => {
serde_json::Value::Number(serde_json::Number::from_f64(*default).unwrap())
}
None => serde_json::Value::Null,
},
}
}
}
#[cfg(feature = "graphql")]
scalar!(Setting);
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "graphql", derive(async_graphql::Enum))]
pub enum VolumeAccessPolicy {
ReadWriteOnce,
#[default]
ReadWriteMany,
ReadWriteOncePod,
}
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
#[cfg_attr(feature = "graphql", graphql(name = "VolumeDefinition"))]
pub struct Volume {
pub minimum_size: u64,
pub recommended_size: u64,
pub name: MultiLanguageItem,
pub description: MultiLanguageItem,
#[serde(default)]
pub access_policy: VolumeAccessPolicy,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum Runtime {
#[serde(rename = "AppYml")]
Kubernetes,
Plugin(String),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct InputDeclaration {
pub label: MultiLanguageItem,
pub description: MultiLanguageItem,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct StorePlugin {
pub name: String,
pub icon: String,
pub description: String,
pub id: String,
pub inputs: BTreeMap<String, InputDeclaration>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct UiMenuEntry {
pub name: MultiLanguageItem,
pub icon: String,
pub path: String,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct UiModule {
pub menu_entries: Vec<UiMenuEntry>,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Settings(pub BTreeMap<String, Setting>);
#[cfg(feature = "graphql")]
scalar!(Settings);
impl Settings {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "graphql", derive(async_graphql::Enum))]
pub enum AppScope {
#[default]
User,
System,
}
impl AppScope {
pub fn get_ns(&self, user: &str, app_id: &str) -> String {
match self {
AppScope::User => format!("{user}-{app_id}"),
AppScope::System => app_id.to_string(),
}
}
}
impl TryFrom<i32> for AppScope {
type Error = String;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(AppScope::User),
1 => Ok(AppScope::System),
_ => Err(format!("Invalid AppScope value: {value}")),
}
}
}
impl From<AppScope> for i32 {
fn from(value: AppScope) -> Self {
match value {
AppScope::User => 0,
AppScope::System => 1,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AppComponent {
pub id: String,
pub name: MultiLanguageItem,
pub description: MultiLanguageItem,
pub icon: Option<String>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct IngressOption {
pub plugin_id: String,
pub id: String,
pub name: MultiLanguageItem,
pub can_choose_domain: bool,
pub domain_placeholder: MultiLanguageItem,
pub component: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
pub struct Metadata {
pub id: String,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub base_app: Option<String>,
pub name: MultiLanguageItem,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub version: semver::Version,
#[cfg_attr(feature = "graphql", graphql(name = "version"))]
pub display_version: String,
pub category: MultiLanguageItem,
pub tagline: MultiLanguageItem,
pub developers: BTreeMap<String, String>,
pub description: MultiLanguageItem,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
#[cfg_attr(feature = "graphql", graphql(skip))]
pub dependencies: Vec<Dependency>,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub base_permissions: HashSet<String>,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub has_permissions: HashSet<String>,
pub exposes_permissions: Vec<Permission>,
pub repos: BTreeMap<String, String>,
pub support: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub gallery: Vec<String>,
pub icon: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_username: Option<String>,
pub default_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub implements: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub release_notes: BTreeMap<String, MultiLanguageItem>,
pub license: String,
#[serde(default, skip_serializing_if = "Settings::is_empty")]
pub settings: Settings,
pub volumes: BTreeMap<String, Volume>,
pub ports: Vec<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "graphql", graphql(skip))]
pub exported_data: Option<BTreeMap<String, BTreeMap<String, serde_json::Value>>>,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub runtime: Runtime,
pub allowed_scopes: Vec<AppScope>,
pub default_scope: AppScope,
#[serde(default)]
pub store_plugins: Vec<StorePlugin>,
#[serde(default)]
pub ui_module: Option<UiModule>,
#[serde(default = "true_default")]
pub supports_ingress: bool,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub can_be_protected: bool,
#[serde(default)]
pub saas_compatibility: SaasCompatibility,
pub raw_ingress: bool,
pub post_install_redirect: Option<String>,
#[serde(default)]
#[cfg_attr(feature = "graphql", graphql(skip))]
pub components: Vec<AppComponent>,
#[cfg_attr(feature = "graphql", graphql(skip))]
pub preload_type: Option<PreloadType>,
pub requires_cluster_config: Option<bool>,
pub deprecated: Option<bool>,
#[serde(default)]
pub ingress_options: Vec<IngressOption>,
#[serde(default)]
pub supports_multi_instance: bool,
#[serde(default)]
pub pod_security_standard: PodSecurityStandard,
pub is_snapshot_compatible: Option<bool>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum PreloadType {
Nirvati,
MainVolumeFromSubdir(String),
MainVolumeFromRoot,
}