nirvati_apps_core/
metadata.rs

1// SPDX-FileCopyrightText: 2024 The Nirvati Developers
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5use std::collections::{BTreeMap, HashMap};
6
7use serde::{Deserialize, Serialize};
8
9use crate::utils::true_default;
10use nirvati::utils::MultiLanguageItem;
11
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
13#[serde(untagged)]
14pub enum Dependency {
15    OneDependency(String),
16    AlternativeDependency(Vec<String>),
17}
18
19#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize)]
20pub struct SvcPorts {
21    pub udp: Vec<u16>,
22    pub tcp: Vec<u16>,
23}
24
25impl SvcPorts {
26    pub fn append(&mut self, other: SvcPorts) {
27        self.udp.extend(other.udp);
28        self.tcp.extend(other.tcp);
29    }
30}
31
32#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
33#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
34pub struct Permission {
35    pub id: String,
36    pub name: MultiLanguageItem,
37    pub description: MultiLanguageItem,
38    /// Other permissions this permission implies
39    /// May also contain permissions of other apps
40    pub includes: Vec<String>,
41    /// Secrets (+ keys) accessible with this permission
42    #[cfg_attr(feature = "graphql", graphql(skip))]
43    pub secrets: BTreeMap<String, Vec<String>>,
44    /// Makes this permission "invisible" (Hidden from the UI) if requested by the apps listed in this field
45    /// The * wildcard can be used to hide from all apps
46    pub hidden: Vec<String>,
47    /// Includes access to certain services and ports on these services
48    pub services: BTreeMap<String, SvcPorts>,
49}
50
51#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
52#[serde(rename_all = "snake_case")]
53#[cfg_attr(feature = "graphql", derive(async_graphql::Enum))]
54pub enum SettingType {
55    Enum,
56    String,
57    Bool,
58}
59
60#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
61#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
62pub struct SettingsDeclaration {
63    #[serde(rename = "type")]
64    pub setting_type: SettingType,
65    #[serde(default = "Vec::default")]
66    #[serde(skip_serializing_if = "Vec::<String>::is_empty")]
67    pub values: Vec<String>,
68    pub name: BTreeMap<String, String>,
69    pub description: BTreeMap<String, String>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub default: Option<String>,
72}
73
74#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
75#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
76#[cfg_attr(feature = "graphql", graphql(name = "VolumeDefinition"))]
77pub struct Volume {
78    pub minimum_size: u64,
79    pub recommended_size: u64,
80    pub name: MultiLanguageItem,
81    pub description: MultiLanguageItem,
82}
83
84#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
85pub enum Runtime {
86    AppYml,
87    Plugin(String),
88}
89
90#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
91#[cfg_attr(feature = "graphql", derive(async_graphql::Enum))]
92pub enum AppType {
93    App,
94    Library,
95    SystemLibrary,
96}
97
98#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
99#[serde(rename_all = "camelCase")]
100#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
101pub struct InputDeclaration {
102    pub label: MultiLanguageItem,
103    pub description: MultiLanguageItem,
104}
105
106#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
107#[serde(rename_all = "camelCase")]
108#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
109pub struct StorePlugin {
110    pub name: String,
111    pub icon: String,
112    pub description: String,
113    pub id: String,
114    pub inputs: BTreeMap<String, InputDeclaration>,
115}
116
117#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
118#[serde(rename_all = "camelCase")]
119#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
120pub struct UiMenuEntry {
121    pub name: MultiLanguageItem,
122    // Icon name, must be either a raw SVG or a name of a heroicons icon
123    pub icon: String,
124    pub path: String,
125}
126
127#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
128#[serde(rename_all = "camelCase")]
129#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
130pub struct UiModule {
131    pub menu_entries: Vec<UiMenuEntry>,
132}
133
134#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
135#[serde(rename_all = "camelCase")]
136#[cfg_attr(feature = "graphql", derive(async_graphql::SimpleObject))]
137pub struct Metadata {
138    /// The app id
139    pub id: String,
140    /// The name of the app
141    pub name: String,
142    /// The version of the app
143    #[cfg_attr(feature = "graphql", graphql(skip))]
144    pub version: semver::Version,
145    /// The version of the app to display
146    #[cfg_attr(feature = "graphql", graphql(name = "version"))]
147    pub display_version: String,
148    /// The category for the app
149    pub category: MultiLanguageItem,
150    /// A short tagline for the app
151    pub tagline: MultiLanguageItem,
152    /// Developer name -> their website
153    pub developers: BTreeMap<String, String>,
154    /// A description of the app
155    pub description: MultiLanguageItem,
156    #[serde(default)]
157    #[serde(skip_serializing_if = "Vec::is_empty")]
158    #[cfg_attr(feature = "graphql", graphql(skip))]
159    /// Dependencies the app requires
160    pub dependencies: Vec<Dependency>,
161    /// Permissions the app has
162    /// If a permission is from an app that is not listed in the dependencies, it is considered optional
163    #[cfg_attr(feature = "graphql", graphql(skip))]
164    pub has_permissions: Vec<String>,
165    /// Permissions this app exposes
166    pub exposes_permissions: Vec<Permission>,
167    /// App repository name -> repo URL
168    pub repos: BTreeMap<String, String>,
169    /// A support link for the app
170    pub support: String,
171    /// A list of promo images for the apps
172    #[serde(default)]
173    #[serde(skip_serializing_if = "Vec::is_empty")]
174    pub gallery: Vec<String>,
175    /// The URL to the app icon
176    pub icon: Option<String>,
177    /// The path the "Open" link on the dashboard should lead to
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub path: Option<String>,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    /// The app's default username
182    pub default_username: Option<String>,
183    /// The app's default password.
184    pub default_password: Option<String>,
185    /// For "virtual" apps, the service the app implements
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub implements: Option<String>,
188    #[serde(
189        default,
190        skip_serializing_if = "BTreeMap::<String, BTreeMap<String, String>>::is_empty"
191    )]
192    pub release_notes: BTreeMap<String, BTreeMap<String, String>>,
193    /// The SPDX identifier of the app license
194    pub license: String,
195    /// Available settings for this app, directly copied from settings.json
196    #[serde(
197        default,
198        skip_serializing_if = "BTreeMap::<String, SettingsDeclaration>::is_empty"
199    )]
200    pub settings: BTreeMap<String, SettingsDeclaration>,
201    /// Volumes this app exposes
202    pub volumes: HashMap<String, Volume>,
203    /// Ports this app uses
204    /// Before installing, app-manager clients need to check if any of these ports are already in use by other apps
205    /// If so, the user needs to be notified
206    pub ports: Vec<u16>,
207    /// Exported data shared with plugins (Map plugin -> data)
208    #[serde(skip_serializing_if = "Option::is_none")]
209    #[cfg_attr(feature = "graphql", graphql(skip))]
210    pub exported_data: Option<HashMap<String, HashMap<String, serde_json::Value>>>,
211    /// The runtime this app uses
212    #[cfg_attr(feature = "graphql", graphql(skip))]
213    pub runtime: Runtime,
214    /// The type of this app (app/library) - Library is anything that does not have a frontend
215    pub r#type: AppType,
216    #[serde(default)]
217    pub store_plugins: Vec<StorePlugin>,
218    #[serde(default)]
219    pub ui_module: Option<UiModule>,
220    /// Whether the app supports ingress
221    /// This is declared by app-manager based on whether an app's ingress vec is empty,
222    /// setting it during generation has no effect
223    #[serde(default = "true_default")]
224    pub supports_ingress: bool,
225    /// Whether to allow all cluster-internal ingress to this app
226    #[cfg_attr(feature = "graphql", graphql(skip))]
227    pub allow_all_internal_ingress: bool,
228    #[cfg_attr(feature = "graphql", graphql(skip))]
229    pub allow_ingress_from_ns: Vec<String>,
230    /// Whether this app can be protected
231    /// Please note: If the app type is not "App", this field is ignored and will be treated as if it were false
232    #[cfg_attr(feature = "graphql", graphql(skip))]
233    pub can_be_protected: bool,
234}