* [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer
@ 2025-11-12 10:24 Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH widget-toolkit 1/1] api viewer: add support for endpoints that are marked as unstable Shannon Sterz
` (8 more replies)
0 siblings, 9 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
this series adds a flag that marks certain api endpoints as unstable.
allowing for more fine grained control over when and what we declare to
be stable. it also adds an api viewer to pdm as a separate package that
is only suggested for now.
the code that dumps api definitions as json is also moved to proxmox-rs
and unified between pdm and pbs to reduce technical debt.
Follow Ups
----------
* currently the proxmox-docgen handles AllOf schemas that have
additional properties that are not object schema to always have
additoinal properties. otherwise, a panic would be caused in
proxmox-schema. the proper fix here is to adapt schema generation in
pve-api-types that is caused by some properties being generated as
string schemas that wrap a property string.
the correct way of handling those would be to either fix
proxmox-schema to also handle these types of schema without panicing
or to not generate these type of schema in the first place.
proxmox-widget-toolkit:
Shannon Sterz (1):
api viewer: add support for endpoints that are marked as unstable
src/api-viewer/APIViewer.js | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
proxmox:
Shannon Sterz (4):
router/api-macro: add unstable flag for ApiMethod
docgen: add docgen crate
docgen: add support for the new stable flag
docgen: add a stop-gap fix to allow generating schema for
pve-api-types
Cargo.toml | 1 +
proxmox-api-macro/src/api/method.rs | 9 +-
proxmox-docgen/Cargo.toml | 20 ++
proxmox-docgen/debian/changelog | 5 +
proxmox-docgen/debian/control | 36 +++
proxmox-docgen/debian/copyright | 18 ++
proxmox-docgen/debian/debcargo.toml | 7 +
proxmox-docgen/src/lib.rs | 339 ++++++++++++++++++++++++++++
proxmox-router/src/router.rs | 10 +
9 files changed, 444 insertions(+), 1 deletion(-)
create mode 100644 proxmox-docgen/Cargo.toml
create mode 100644 proxmox-docgen/debian/changelog
create mode 100644 proxmox-docgen/debian/control
create mode 100644 proxmox-docgen/debian/copyright
create mode 100644 proxmox-docgen/debian/debcargo.toml
create mode 100644 proxmox-docgen/src/lib.rs
proxmox-backup:
Shannon Sterz (1):
docgen: use proxmox-rs docgen crate
Cargo.toml | 3 +
docs/api-viewer/index.html | 2 +
src/bin/docgen.rs | 312 +------------------------------------
3 files changed, 10 insertions(+), 307 deletions(-)
proxmox-datacenter-manager:
Shannon Sterz (2):
docgen: switch to proxmox-rs docgen crate
api-viewer: add an api-viewer package
Cargo.toml | 2 +
Makefile | 1 +
debian/control | 13 +
...xmox-datacenter-manager-api-viewer.install | 1 +
docs/api-viewer/Makefile | 27 ++
docs/api-viewer/index.html | 16 +
server/Cargo.toml | 1 +
server/src/bin/docgen.rs | 308 +-----------------
8 files changed, 63 insertions(+), 306 deletions(-)
create mode 100644 debian/proxmox-datacenter-manager-api-viewer.install
create mode 100644 docs/api-viewer/Makefile
create mode 100644 docs/api-viewer/index.html
Summary over all repositories:
21 files changed, 533 insertions(+), 616 deletions(-)
--
Generated by git-murpp 0.8.1
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH widget-toolkit 1/1] api viewer: add support for endpoints that are marked as unstable
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 1/4] router/api-macro: add unstable flag for ApiMethod Shannon Sterz
` (7 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
display a warning when an endpoint is marked as unstable.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
src/api-viewer/APIViewer.js | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/api-viewer/APIViewer.js b/src/api-viewer/APIViewer.js
index 7f27e0d..1357741 100644
--- a/src/api-viewer/APIViewer.js
+++ b/src/api-viewer/APIViewer.js
@@ -210,7 +210,21 @@ Ext.onReady(function () {
usage += cliUsageRenderer(method, endpoint);
}
- let sections = [
+ let sections = [];
+
+ if (info.unstable) {
+ sections.push({
+ title: 'Unstable',
+ html: `<div class="proxmox-warning-row" style="padding: 10px;">
+ <i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
+ This API endpoint is marked as unstable. All information on this
+ page is subject to change, including input parameters, return values
+ and permissions.
+ </div>`,
+ });
+ }
+
+ sections.concat([
{
title: 'Description',
html: Ext.htmlEncode(info.description),
@@ -221,7 +235,7 @@ Ext.onReady(function () {
html: usage,
bodyPadding: 10,
},
- ];
+ ]);
if (info.parameters && info.parameters.properties) {
let pstore = Ext.create('Ext.data.Store', {
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH proxmox 1/4] router/api-macro: add unstable flag for ApiMethod
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH widget-toolkit 1/1] api viewer: add support for endpoints that are marked as unstable Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 2/4] docgen: add docgen crate Shannon Sterz
` (6 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
this allows marking certain api endpoints as unstable. an unstable api
endpoint gives no guarantees that it will stay compatible between
different versions. programmers that make use of an unstable endpoint,
have to expect that anything is subject to change.
docgen: add docgen crate
this crate allows products to easily dump their api definitions to a
`Value` that can be output as json for an api viewer.
meh
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
proxmox-api-macro/src/api/method.rs | 9 ++++++++-
proxmox-router/src/router.rs | 10 ++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/proxmox-api-macro/src/api/method.rs b/proxmox-api-macro/src/api/method.rs
index d3d17447..68969735 100644
--- a/proxmox-api-macro/src/api/method.rs
+++ b/proxmox-api-macro/src/api/method.rs
@@ -245,6 +245,12 @@ pub fn handle_method(mut attribs: JSONObject, func: syn::ItemFn) -> Result<Token
.transpose()?
.unwrap_or(false);
+ let unstable: bool = attribs
+ .remove("unstable")
+ .map(TryFrom::try_from)
+ .transpose()?
+ .unwrap_or(false);
+
if !attribs.is_empty() {
error!(
attribs.span(),
@@ -329,7 +335,8 @@ pub fn handle_method(mut attribs: JSONObject, func: syn::ItemFn) -> Result<Token
#returns_schema_setter
#access_setter
.reload_timezone(#reload_timezone)
- .protected(#protected);
+ .protected(#protected)
+ .unstable(#unstable);
#default_consts
diff --git a/proxmox-router/src/router.rs b/proxmox-router/src/router.rs
index 732ced8b..f4207d2a 100644
--- a/proxmox-router/src/router.rs
+++ b/proxmox-router/src/router.rs
@@ -789,6 +789,8 @@ pub struct ApiMethod {
/// The protected flag indicates that the provides function should be forwarded
/// to the daemon running in privileged mode.
pub protected: bool,
+ /// Whether this method is still experimental or already stable.
+ pub unstable: bool,
/// This flag indicates that the provided method may change the local timezone, so the server
/// should do a tzset afterwards
pub reload_timezone: bool,
@@ -820,6 +822,7 @@ impl ApiMethod {
handler,
returns: ReturnType::new(false, &NULL_SCHEMA),
protected: false,
+ unstable: false,
reload_timezone: false,
access: ApiAccess {
description: None,
@@ -838,6 +841,7 @@ impl ApiMethod {
handler: &DUMMY_HANDLER,
returns: ReturnType::new(false, &NULL_SCHEMA),
protected: false,
+ unstable: false,
reload_timezone: false,
access: ApiAccess {
description: None,
@@ -858,6 +862,12 @@ impl ApiMethod {
self
}
+ pub const fn unstable(mut self, unstable: bool) -> Self {
+ self.unstable = unstable;
+
+ self
+ }
+
pub const fn reload_timezone(mut self, reload_timezone: bool) -> Self {
self.reload_timezone = reload_timezone;
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH proxmox 2/4] docgen: add docgen crate
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH widget-toolkit 1/1] api viewer: add support for endpoints that are marked as unstable Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 1/4] router/api-macro: add unstable flag for ApiMethod Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 3/4] docgen: add support for the new stable flag Shannon Sterz
` (5 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
currently this is limited to dumping a `Router` into a `Value` which
can be serialized to JSON and interpreted by an api viewer.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
Cargo.toml | 1 +
proxmox-docgen/Cargo.toml | 20 ++
proxmox-docgen/debian/changelog | 5 +
proxmox-docgen/debian/control | 36 ++++
proxmox-docgen/debian/copyright | 18 ++
proxmox-docgen/debian/debcargo.toml | 7 +
proxmox-docgen/src/lib.rs | 322 ++++++++++++++++++++++++++++
7 files changed, 409 insertions(+)
create mode 100644 proxmox-docgen/Cargo.toml
create mode 100644 proxmox-docgen/debian/changelog
create mode 100644 proxmox-docgen/debian/control
create mode 100644 proxmox-docgen/debian/copyright
create mode 100644 proxmox-docgen/debian/debcargo.toml
create mode 100644 proxmox-docgen/src/lib.rs
diff --git a/Cargo.toml b/Cargo.toml
index c9d734e6..9a4cf1b1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ members = [
"proxmox-config-digest",
"proxmox-daemon",
"proxmox-dns-api",
+ "proxmox-docgen",
"proxmox-http",
"proxmox-http-error",
"proxmox-human-byte",
diff --git a/proxmox-docgen/Cargo.toml b/proxmox-docgen/Cargo.toml
new file mode 100644
index 00000000..84a515de
--- /dev/null
+++ b/proxmox-docgen/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "proxmox-docgen"
+description = "Library to easily create documentation from Rust sources for Proxmox projects."
+version = "1.0.0"
+
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+exclude.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+serde_json.workspace = true
+
+proxmox-router.workspace = true
+proxmox-schema = { workspace = true }
+
+
diff --git a/proxmox-docgen/debian/changelog b/proxmox-docgen/debian/changelog
new file mode 100644
index 00000000..79b7e602
--- /dev/null
+++ b/proxmox-docgen/debian/changelog
@@ -0,0 +1,5 @@
+rust-proxmox-docgen (1.0.0-1) trixie; urgency=medium
+
+ * Initial packaging
+
+ -- Proxmox Support Team <support@proxmox.com> Mon, 10 Nov 2025 10:44:26 +0200
diff --git a/proxmox-docgen/debian/control b/proxmox-docgen/debian/control
new file mode 100644
index 00000000..4110e9e0
--- /dev/null
+++ b/proxmox-docgen/debian/control
@@ -0,0 +1,36 @@
+Source: rust-proxmox-docgen
+Section: rust
+Priority: optional
+Build-Depends: debhelper-compat (= 13),
+ dh-sequence-cargo
+Build-Depends-Arch: cargo:native <!nocheck>,
+ rustc:native (>= 1.82) <!nocheck>,
+ libstd-rust-dev <!nocheck>,
+ librust-proxmox-router-3+default-dev (>= 3.2.2-~~) <!nocheck>,
+ librust-proxmox-schema-5+default-dev (>= 5.0.1-~~) <!nocheck>,
+ librust-serde-json-1+default-dev <!nocheck>
+Maintainer: Proxmox Support Team <support@proxmox.com>
+Standards-Version: 4.7.2
+Vcs-Git: git://git.proxmox.com/git/proxmox.git
+Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
+Homepage: https://proxmox.com
+X-Cargo-Crate: proxmox-docgen
+
+Package: librust-proxmox-docgen-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-proxmox-router-3+default-dev (>= 3.2.2-~~),
+ librust-proxmox-schema-5+default-dev (>= 5.0.1-~~),
+ librust-serde-json-1+default-dev
+Provides:
+ librust-proxmox-docgen+default-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1+default-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1.0-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1.0+default-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1.0.0-dev (= ${binary:Version}),
+ librust-proxmox-docgen-1.0.0+default-dev (= ${binary:Version})
+Description: Easily create documentation from Rust sources for Proxmox projects - Rust source code
+ Source code for Debianized Rust crate "proxmox-docgen"
diff --git a/proxmox-docgen/debian/copyright b/proxmox-docgen/debian/copyright
new file mode 100644
index 00000000..d6e3c304
--- /dev/null
+++ b/proxmox-docgen/debian/copyright
@@ -0,0 +1,18 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files:
+ *
+Copyright: 2025 Proxmox Server Solutions GmbH <support@proxmox.com>
+License: AGPL-3.0-or-later
+ 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/>.
diff --git a/proxmox-docgen/debian/debcargo.toml b/proxmox-docgen/debian/debcargo.toml
new file mode 100644
index 00000000..b7864cdb
--- /dev/null
+++ b/proxmox-docgen/debian/debcargo.toml
@@ -0,0 +1,7 @@
+overlay = "."
+crate_src_path = ".."
+maintainer = "Proxmox Support Team <support@proxmox.com>"
+
+[source]
+vcs_git = "git://git.proxmox.com/git/proxmox.git"
+vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
diff --git a/proxmox-docgen/src/lib.rs b/proxmox-docgen/src/lib.rs
new file mode 100644
index 00000000..b3ef1ff8
--- /dev/null
+++ b/proxmox-docgen/src/lib.rs
@@ -0,0 +1,322 @@
+use serde_json::{json, Value};
+
+use proxmox_router::{ApiAccess, ApiHandler, ApiMethod, Permission, Router, SubRoute};
+use proxmox_schema::format::get_property_string_type_text;
+use proxmox_schema::{ApiStringFormat, ObjectSchemaType, Schema};
+
+pub fn generate_api_tree(router: &Router, path: &str, privileges: &[(&str, u64)]) -> Value {
+ let mut data = dump_api_schema(router, path, privileges);
+ data["expanded"] = true.into();
+ data
+}
+
+fn dump_schema(schema: &Schema) -> Value {
+ let mut data;
+
+ match schema {
+ Schema::Null => {
+ data = json!({
+ "type": "null",
+ });
+ }
+ Schema::Boolean(boolean_schema) => {
+ data = json!({
+ "type": "boolean",
+ "description": boolean_schema.description,
+ });
+ if let Some(default) = boolean_schema.default {
+ data["default"] = default.into();
+ }
+ }
+ Schema::String(string_schema) => {
+ data = json!({
+ "type": "string",
+ "description": string_schema.description,
+ });
+ if let Some(default) = string_schema.default {
+ data["default"] = default.into();
+ }
+ if let Some(min_length) = string_schema.min_length {
+ data["minLength"] = min_length.into();
+ }
+ if let Some(max_length) = string_schema.max_length {
+ data["maxLength"] = max_length.into();
+ }
+ if let Some(type_text) = string_schema.type_text {
+ data["typetext"] = type_text.into();
+ }
+ match string_schema.format {
+ None | Some(ApiStringFormat::VerifyFn(_)) => { /* do nothing */ }
+ Some(ApiStringFormat::Pattern(const_regex)) => {
+ data["pattern"] = format!("/{}/", const_regex.regex_string).into();
+ }
+ Some(ApiStringFormat::Enum(variants)) => {
+ let variants: Vec<String> =
+ variants.iter().map(|e| e.value.to_string()).collect();
+ data["enum"] = serde_json::to_value(variants).unwrap();
+ }
+ Some(ApiStringFormat::PropertyString(subschema)) => {
+ match subschema {
+ Schema::Object(_) | Schema::Array(_) => {
+ data["format"] = dump_schema(subschema);
+ data["typetext"] = get_property_string_type_text(subschema).into();
+ }
+ _ => { /* do nothing - should not happen */ }
+ };
+ }
+ }
+ }
+ Schema::Integer(integer_schema) => {
+ data = json!({
+ "type": "integer",
+ "description": integer_schema.description,
+ });
+ if let Some(default) = integer_schema.default {
+ data["default"] = default.into();
+ }
+ if let Some(minimum) = integer_schema.minimum {
+ data["minimum"] = minimum.into();
+ }
+ if let Some(maximum) = integer_schema.maximum {
+ data["maximum"] = maximum.into();
+ }
+ }
+ Schema::Number(number_schema) => {
+ data = json!({
+ "type": "number",
+ "description": number_schema.description,
+ });
+ if let Some(default) = number_schema.default {
+ data["default"] = default.into();
+ }
+ if let Some(minimum) = number_schema.minimum {
+ data["minimum"] = minimum.into();
+ }
+ if let Some(maximum) = number_schema.maximum {
+ data["maximum"] = maximum.into();
+ }
+ }
+ Schema::Object(object_schema) => {
+ data = dump_property_schema(object_schema);
+ data["type"] = "object".into();
+ if let Some(default_key) = object_schema.default_key {
+ data["default_key"] = default_key.into();
+ }
+ }
+ Schema::Array(array_schema) => {
+ data = json!({
+ "type": "array",
+ "description": array_schema.description,
+ "items": dump_schema(array_schema.items),
+ });
+ if let Some(min_length) = array_schema.min_length {
+ data["minLength"] = min_length.into();
+ }
+ if let Some(max_length) = array_schema.min_length {
+ data["maxLength"] = max_length.into();
+ }
+ }
+ Schema::AllOf(alloff_schema) => {
+ data = dump_property_schema(alloff_schema);
+ data["type"] = "object".into();
+ }
+ Schema::OneOf(schema) => {
+ let mut type_schema = dump_schema(schema.type_schema());
+ if schema.type_property_entry.1 {
+ type_schema["optional"] = true.into();
+ }
+ data = json!({
+ "type": "object",
+ "description": schema.description,
+ "typeProperty": schema.type_property(),
+ "typeSchema": type_schema,
+ });
+ let mut variants = Vec::with_capacity(schema.list.len());
+ for (title, variant) in schema.list {
+ let mut entry = dump_schema(variant);
+ entry["title"] = (*title).into();
+ variants.push(entry);
+ }
+ data["oneOf"] = variants.into();
+ }
+ };
+
+ data
+}
+
+fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
+ let mut properties = json!({});
+
+ for (prop, optional, schema) in param.properties() {
+ let mut property = dump_schema(schema);
+ if *optional {
+ property["optional"] = 1.into();
+ }
+ properties[prop] = property;
+ }
+
+ let data = json!({
+ "description": param.description(),
+ "additionalProperties": param.additional_properties(),
+ "properties": properties,
+ });
+
+ data
+}
+
+fn dump_api_permission(permission: &Permission, privileges: &[(&str, u64)]) -> Value {
+ match permission {
+ Permission::Superuser => json!({ "user": "root@pam" }),
+ Permission::User(user) => json!({ "user": user }),
+ Permission::Anybody => json!({ "user": "all" }),
+ Permission::World => json!({ "user": "world" }),
+ Permission::UserParam(param) => json!({ "userParam": param }),
+ Permission::Group(group) => json!({ "group": group }),
+ Permission::WithParam(param, sub_permission) => {
+ json!({
+ "withParam": {
+ "name": param,
+ "permissions": dump_api_permission(sub_permission, privileges),
+ },
+ })
+ }
+ Permission::Privilege(name, value, partial) => {
+ let mut privs = Vec::new();
+ for (name, v) in privileges {
+ if (value & v) != 0 {
+ privs.push(name.to_string());
+ }
+ }
+
+ json!({
+ "check": {
+ "path": name,
+ "privs": privs,
+ "partial": partial,
+ }
+ })
+ }
+ Permission::And(list) => {
+ let list: Vec<Value> = list
+ .iter()
+ .map(|p| dump_api_permission(p, privileges))
+ .collect();
+ json!({ "and": list })
+ }
+ Permission::Or(list) => {
+ let list: Vec<Value> = list
+ .iter()
+ .map(|p| dump_api_permission(p, privileges))
+ .collect();
+ json!({ "or": list })
+ }
+ }
+}
+
+fn dump_api_method_schema(
+ method: &str,
+ api_method: &ApiMethod,
+ privileges: &[(&str, u64)],
+) -> Value {
+ let mut data = json!({
+ "description": api_method.parameters.description(),
+ });
+
+ data["parameters"] = dump_property_schema(&api_method.parameters);
+
+ let mut returns = dump_schema(api_method.returns.schema);
+ if api_method.returns.optional {
+ returns["optional"] = 1.into();
+ }
+ data["returns"] = returns;
+
+ match api_method.access {
+ ApiAccess {
+ description: None,
+ permission: Permission::Superuser,
+ } => {
+ // no need to output default
+ }
+ ApiAccess {
+ description,
+ permission,
+ } => {
+ let mut permissions = dump_api_permission(permission, privileges);
+ if let Some(description) = description {
+ permissions["description"] = description.into();
+ }
+ data["permissions"] = permissions;
+ }
+ }
+
+ let mut method = method;
+
+ if let ApiHandler::AsyncHttp(_) = api_method.handler {
+ method = if method == "POST" { "UPLOAD" } else { method };
+ method = if method == "GET" { "DOWNLOAD" } else { method };
+ }
+
+ data["method"] = method.into();
+
+ data
+}
+
+fn dump_api_schema(router: &Router, path: &str, privileges: &[(&str, u64)]) -> Value {
+ let mut data = json!({});
+
+ let mut info = json!({});
+ if let Some(api_method) = router.get {
+ info["GET"] = dump_api_method_schema("GET", api_method, privileges);
+ }
+ if let Some(api_method) = router.post {
+ info["POST"] = dump_api_method_schema("POST", api_method, privileges);
+ }
+ if let Some(api_method) = router.put {
+ info["PUT"] = dump_api_method_schema("PUT", api_method, privileges);
+ }
+ if let Some(api_method) = router.delete {
+ info["DELETE"] = dump_api_method_schema("DELETE", api_method, privileges);
+ }
+
+ data["info"] = info;
+
+ match &router.subroute {
+ None => {
+ data["leaf"] = 1.into();
+ }
+ Some(SubRoute::MatchAll { router, param_name }) => {
+ let sub_path = if path == "." {
+ format!("/{{{param_name}}}")
+ } else {
+ format!("{path}/{{{param_name}}}")
+ };
+ let mut child = dump_api_schema(router, &sub_path, privileges);
+ child["path"] = sub_path.into();
+ child["text"] = format!("{{{param_name}}}").into();
+
+ let children = vec![child];
+ data["children"] = children.into();
+ data["leaf"] = 0.into();
+ }
+ Some(SubRoute::Map(dirmap)) => {
+ let mut children = Vec::new();
+
+ for (key, sub_router) in dirmap.iter() {
+ let sub_path = if path == "." {
+ format!("/{key}")
+ } else {
+ format!("{path}/{key}")
+ };
+ let mut child = dump_api_schema(sub_router, &sub_path, privileges);
+ child["path"] = sub_path.into();
+ child["text"] = key.to_string().into();
+ children.push(child);
+ }
+
+ data["children"] = children.into();
+ data["leaf"] = 0.into();
+ }
+ }
+
+ data
+}
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH proxmox 3/4] docgen: add support for the new stable flag
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (2 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 2/4] docgen: add docgen crate Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types Shannon Sterz
` (4 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
proxmox-docgen/src/lib.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/proxmox-docgen/src/lib.rs b/proxmox-docgen/src/lib.rs
index b3ef1ff8..67d502d5 100644
--- a/proxmox-docgen/src/lib.rs
+++ b/proxmox-docgen/src/lib.rs
@@ -229,6 +229,7 @@ fn dump_api_method_schema(
returns["optional"] = 1.into();
}
data["returns"] = returns;
+ data["unstable"] = api_method.unstable.into();
match api_method.access {
ApiAccess {
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (3 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 3/4] docgen: add support for the new stable flag Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 16:09 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox-backup 1/1] docgen: use proxmox-rs docgen crate Shannon Sterz
` (3 subsequent siblings)
8 siblings, 1 reply; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
proxmox-docgen/src/lib.rs | 38 +++++++++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/proxmox-docgen/src/lib.rs b/proxmox-docgen/src/lib.rs
index 67d502d5..f3d53519 100644
--- a/proxmox-docgen/src/lib.rs
+++ b/proxmox-docgen/src/lib.rs
@@ -97,7 +97,7 @@ fn dump_schema(schema: &Schema) -> Value {
}
}
Schema::Object(object_schema) => {
- data = dump_property_schema(object_schema);
+ data = dump_property_schema(object_schema, false);
data["type"] = "object".into();
if let Some(default_key) = object_schema.default_key {
data["default_key"] = default_key.into();
@@ -117,7 +117,12 @@ fn dump_schema(schema: &Schema) -> Value {
}
}
Schema::AllOf(alloff_schema) => {
- data = dump_property_schema(alloff_schema);
+ let dirty_allof = alloff_schema
+ .list
+ .iter()
+ .any(|schema| schema.any_object().is_none());
+
+ data = dump_property_schema(alloff_schema, dirty_allof);
data["type"] = "object".into();
}
Schema::OneOf(schema) => {
@@ -144,7 +149,8 @@ fn dump_schema(schema: &Schema) -> Value {
data
}
-fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
+// TODO: remove `dirty_allof` again once pve-api-types generates proper `AllOf` schema.
+fn dump_property_schema(param: &dyn ObjectSchemaType, dirty_allof: bool) -> Value {
let mut properties = json!({});
for (prop, optional, schema) in param.properties() {
@@ -155,13 +161,23 @@ fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
properties[prop] = property;
}
- let data = json!({
- "description": param.description(),
- "additionalProperties": param.additional_properties(),
- "properties": properties,
- });
-
- data
+ if dirty_allof {
+ json!({
+ "description": param.description(),
+ // stop gap for now. pve-api-types generates certain properties as string schema that
+ // are really property strings (which wrap an object schema anyway). however,
+ // `additional_properties()` panics there, because it will expect *only* object
+ // schema.
+ "additionalProperties": true,
+ "properties": properties,
+ })
+ } else {
+ json!({
+ "description": param.description(),
+ "additionalProperties": param.additional_properties(),
+ "properties": properties,
+ })
+ }
}
fn dump_api_permission(permission: &Permission, privileges: &[(&str, u64)]) -> Value {
@@ -222,7 +238,7 @@ fn dump_api_method_schema(
"description": api_method.parameters.description(),
});
- data["parameters"] = dump_property_schema(&api_method.parameters);
+ data["parameters"] = dump_property_schema(&api_method.parameters, false);
let mut returns = dump_schema(api_method.returns.schema);
if api_method.returns.optional {
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH proxmox-backup 1/1] docgen: use proxmox-rs docgen crate
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (4 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 1/2] docgen: switch to " Shannon Sterz
` (2 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
this has the benefit of also handling the unstable flag correctly.
hence, the api viewer needs to add the css definitions to properly
render unstable endpoints.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
Cargo.toml | 3 +
docs/api-viewer/index.html | 2 +
src/bin/docgen.rs | 312 +------------------------------------
3 files changed, 10 insertions(+), 307 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 28fdbf54..fbd16ccb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,6 +62,7 @@ proxmox-borrow = "1"
proxmox-compression = "1.0.1"
proxmox-config-digest = "1"
proxmox-daemon = "1"
+proxmox-docgen = "1"
proxmox-fuse = "1"
proxmox-http = { version = "1.0.2", features = [ "client", "http-helpers", "websocket" ] } # see below
proxmox-human-byte = "1"
@@ -216,6 +217,7 @@ proxmox-base64.workspace = true
proxmox-compression.workspace = true
proxmox-config-digest.workspace = true
proxmox-daemon.workspace = true
+proxmox-docgen.workspace = true
proxmox-http = { workspace = true, features = [ "body", "client-trait", "proxmox-async", "rate-limited-stream" ] } # pbs-client doesn't use these
proxmox-human-byte.workspace = true
proxmox-io.workspace = true
@@ -275,6 +277,7 @@ proxmox-rrd-api-types.workspace = true
#proxmox-compression = { path = "../proxmox/proxmox-compression" }
#proxmox-config-digest = { path = "../proxmox/proxmox-config-digest" }
#proxmox-daemon = { path = "../proxmox/proxmox-daemon" }
+#proxmox-docgen = { path = "../proxmox/proxmox-docgen" }
#proxmox-http = { path = "../proxmox/proxmox-http" }
#proxmox-http-error = { path = "../proxmox/proxmox-http-error" }
#proxmox-human-byte = { path = "../proxmox/proxmox-human-byte" }
diff --git a/docs/api-viewer/index.html b/docs/api-viewer/index.html
index 72dae96a..28f5b3b7 100644
--- a/docs/api-viewer/index.html
+++ b/docs/api-viewer/index.html
@@ -6,6 +6,8 @@
<title>Proxmox Backup Server API Documentation</title>
<link rel="stylesheet" type="text/css" href="extjs/theme-crisp/resources/theme-crisp-all.css">
+ <link rel="stylesheet" type="text/css" href="/fontawesome/css/font-awesome.css" />
+ <link rel="stylesheet" type="text/css" href="/widgettoolkit/css/ext6-pmx.css" />
<link rel="stylesheet" type="text/css" media="(prefers-color-scheme: dark)" href="/widgettoolkit/themes/theme-proxmox-dark.css" />
<script type="text/javascript" src="extjs/ext-all.js"></script>
<script type="text/javascript" src="apidoc.js"></script>
diff --git a/src/bin/docgen.rs b/src/bin/docgen.rs
index 674be945..454f02ca 100644
--- a/src/bin/docgen.rs
+++ b/src/bin/docgen.rs
@@ -55,22 +55,22 @@ fn main() -> Result<(), Error> {
fn generate_api_tree() -> String {
let mut tree = Vec::new();
- let mut data = dump_api_schema(&api2::ROUTER, ".");
+ let mut data = proxmox_docgen::generate_api_tree(&api2::ROUTER, ".", PRIVILEGES);
data["path"] = "/".into();
// hack: add invisible space to sort as first entry
data["text"] = "​Management API (HTTP)".into();
- data["expanded"] = true.into();
-
tree.push(data);
- let mut data = dump_api_schema(&api2::backup::BACKUP_API_ROUTER, "/backup/_upgrade_");
+ let mut data = proxmox_docgen::generate_api_tree(&api2::backup::BACKUP_API_ROUTER, "/backup/_upgrade_", PRIVILEGES);
data["path"] = "/backup/_upgrade_".into();
data["text"] = "Backup API (HTTP/2)".into();
+ data["expanded"] = false.into();
tree.push(data);
- let mut data = dump_api_schema(&api2::reader::READER_API_ROUTER, "/reader/_upgrade_");
+ let mut data = proxmox_docgen::generate_api_tree(&api2::reader::READER_API_ROUTER, "/reader/_upgrade_", PRIVILEGES);
data["path"] = "/reader/_upgrade_".into();
data["text"] = "Restore API (HTTP/2)".into();
+ data["expanded"] = false.into();
tree.push(data);
format!(
@@ -78,305 +78,3 @@ fn generate_api_tree() -> String {
serde_json::to_string_pretty(&tree).unwrap()
)
}
-
-pub fn dump_schema(schema: &Schema) -> Value {
- let mut data;
-
- match schema {
- Schema::Null => {
- data = json!({
- "type": "null",
- });
- }
- Schema::Boolean(boolean_schema) => {
- data = json!({
- "type": "boolean",
- "description": boolean_schema.description,
- });
- if let Some(default) = boolean_schema.default {
- data["default"] = default.into();
- }
- }
- Schema::String(string_schema) => {
- data = json!({
- "type": "string",
- "description": string_schema.description,
- });
- if let Some(default) = string_schema.default {
- data["default"] = default.into();
- }
- if let Some(min_length) = string_schema.min_length {
- data["minLength"] = min_length.into();
- }
- if let Some(max_length) = string_schema.max_length {
- data["maxLength"] = max_length.into();
- }
- if let Some(type_text) = string_schema.type_text {
- data["typetext"] = type_text.into();
- }
- match string_schema.format {
- None | Some(ApiStringFormat::VerifyFn(_)) => { /* do nothing */ }
- Some(ApiStringFormat::Pattern(const_regex)) => {
- data["pattern"] = format!("/{}/", const_regex.regex_string).into();
- }
- Some(ApiStringFormat::Enum(variants)) => {
- let variants: Vec<String> =
- variants.iter().map(|e| e.value.to_string()).collect();
- data["enum"] = serde_json::to_value(variants).unwrap();
- }
- Some(ApiStringFormat::PropertyString(subschema)) => {
- match subschema {
- Schema::Object(_) | Schema::Array(_) => {
- data["format"] = dump_schema(subschema);
- data["typetext"] = get_property_string_type_text(subschema).into();
- }
- _ => { /* do nothing - should not happen */ }
- };
- }
- }
- // fixme: dump format
- }
- Schema::Integer(integer_schema) => {
- data = json!({
- "type": "integer",
- "description": integer_schema.description,
- });
- if let Some(default) = integer_schema.default {
- data["default"] = default.into();
- }
- if let Some(minimum) = integer_schema.minimum {
- data["minimum"] = minimum.into();
- }
- if let Some(maximum) = integer_schema.maximum {
- data["maximum"] = maximum.into();
- }
- }
- Schema::Number(number_schema) => {
- data = json!({
- "type": "number",
- "description": number_schema.description,
- });
- if let Some(default) = number_schema.default {
- data["default"] = default.into();
- }
- if let Some(minimum) = number_schema.minimum {
- data["minimum"] = minimum.into();
- }
- if let Some(maximum) = number_schema.maximum {
- data["maximum"] = maximum.into();
- }
- }
- Schema::Object(object_schema) => {
- data = dump_property_schema(object_schema);
- data["type"] = "object".into();
- if let Some(default_key) = object_schema.default_key {
- data["default_key"] = default_key.into();
- }
- }
- Schema::Array(array_schema) => {
- data = json!({
- "type": "array",
- "description": array_schema.description,
- "items": dump_schema(array_schema.items),
- });
- if let Some(min_length) = array_schema.min_length {
- data["minLength"] = min_length.into();
- }
- if let Some(max_length) = array_schema.min_length {
- data["maxLength"] = max_length.into();
- }
- }
- Schema::AllOf(alloff_schema) => {
- data = dump_property_schema(alloff_schema);
- data["type"] = "object".into();
- }
- Schema::OneOf(schema) => {
- let mut type_schema = dump_schema(schema.type_schema());
- if schema.type_property_entry.1 {
- type_schema["optional"] = true.into();
- }
- data = json!({
- "type": "object",
- "description": schema.description,
- "typeProperty": schema.type_property(),
- "typeSchema": type_schema,
- });
- let mut variants = Vec::with_capacity(schema.list.len());
- for (title, variant) in schema.list {
- let mut entry = dump_schema(variant);
- entry["title"] = (*title).into();
- variants.push(entry);
- }
- data["oneOf"] = variants.into();
- }
- };
-
- data
-}
-
-pub fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
- let mut properties = json!({});
-
- for (prop, optional, schema) in param.properties() {
- let mut property = dump_schema(schema);
- if *optional {
- property["optional"] = 1.into();
- }
- properties[prop] = property;
- }
-
- let data = json!({
- "description": param.description(),
- "additionalProperties": param.additional_properties(),
- "properties": properties,
- });
-
- data
-}
-
-fn dump_api_permission(permission: &Permission) -> Value {
- match permission {
- Permission::Superuser => json!({ "user": "root@pam" }),
- Permission::User(user) => json!({ "user": user }),
- Permission::Anybody => json!({ "user": "all" }),
- Permission::World => json!({ "user": "world" }),
- Permission::UserParam(param) => json!({ "userParam": param }),
- Permission::Group(group) => json!({ "group": group }),
- Permission::WithParam(param, sub_permission) => {
- json!({
- "withParam": {
- "name": param,
- "permissions": dump_api_permission(sub_permission),
- },
- })
- }
- Permission::Privilege(name, value, partial) => {
- let mut privs = Vec::new();
- for (name, v) in PRIVILEGES {
- if (value & v) != 0 {
- privs.push(name.to_string());
- }
- }
-
- json!({
- "check": {
- "path": name,
- "privs": privs,
- "partial": partial,
- }
- })
- }
- Permission::And(list) => {
- let list: Vec<Value> = list.iter().map(|p| dump_api_permission(p)).collect();
- json!({ "and": list })
- }
- Permission::Or(list) => {
- let list: Vec<Value> = list.iter().map(|p| dump_api_permission(p)).collect();
- json!({ "or": list })
- }
- }
-}
-
-fn dump_api_method_schema(method: &str, api_method: &ApiMethod) -> Value {
- let mut data = json!({
- "description": api_method.parameters.description(),
- });
-
- data["parameters"] = dump_property_schema(&api_method.parameters);
-
- let mut returns = dump_schema(api_method.returns.schema);
- if api_method.returns.optional {
- returns["optional"] = 1.into();
- }
- data["returns"] = returns;
-
- match api_method.access {
- ApiAccess {
- description: None,
- permission: Permission::Superuser,
- } => {
- // no need to output default
- }
- ApiAccess {
- description,
- permission,
- } => {
- let mut permissions = dump_api_permission(permission);
- if let Some(description) = description {
- permissions["description"] = description.into();
- }
- data["permissions"] = permissions;
- }
- }
-
- let mut method = method;
-
- if let ApiHandler::AsyncHttp(_) = api_method.handler {
- method = if method == "POST" { "UPLOAD" } else { method };
- method = if method == "GET" { "DOWNLOAD" } else { method };
- }
-
- data["method"] = method.into();
-
- data
-}
-
-pub fn dump_api_schema(router: &Router, path: &str) -> Value {
- let mut data = json!({});
-
- let mut info = json!({});
- if let Some(api_method) = router.get {
- info["GET"] = dump_api_method_schema("GET", api_method);
- }
- if let Some(api_method) = router.post {
- info["POST"] = dump_api_method_schema("POST", api_method);
- }
- if let Some(api_method) = router.put {
- info["PUT"] = dump_api_method_schema("PUT", api_method);
- }
- if let Some(api_method) = router.delete {
- info["DELETE"] = dump_api_method_schema("DELETE", api_method);
- }
-
- data["info"] = info;
-
- match &router.subroute {
- None => {
- data["leaf"] = 1.into();
- }
- Some(SubRoute::MatchAll { router, param_name }) => {
- let sub_path = if path == "." {
- format!("/{{{param_name}}}")
- } else {
- format!("{path}/{{{param_name}}}")
- };
- let mut child = dump_api_schema(router, &sub_path);
- child["path"] = sub_path.into();
- child["text"] = format!("{{{param_name}}}").into();
-
- let children = vec![child];
- data["children"] = children.into();
- data["leaf"] = 0.into();
- }
- Some(SubRoute::Map(dirmap)) => {
- let mut children = Vec::new();
-
- for (key, sub_router) in dirmap.iter() {
- let sub_path = if path == "." {
- format!("/{key}")
- } else {
- format!("{path}/{key}")
- };
- let mut child = dump_api_schema(sub_router, &sub_path);
- child["path"] = sub_path.into();
- child["text"] = key.to_string().into();
- children.push(child);
- }
-
- data["children"] = children.into();
- data["leaf"] = 0.into();
- }
- }
-
- data
-}
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 1/2] docgen: switch to proxmox-rs docgen crate
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (5 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox-backup 1/1] docgen: use proxmox-rs docgen crate Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 2/2] api-viewer: add an api-viewer package Shannon Sterz
2025-11-13 12:02 ` [pdm-devel] Superseded: Re: [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
and use pdm permissions not pbs ones.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
Cargo.toml | 2 +
server/Cargo.toml | 1 +
server/src/bin/docgen.rs | 308 +--------------------------------------
3 files changed, 5 insertions(+), 306 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 3252ccb..f9a8872 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -40,6 +40,7 @@ proxmox-auth-api = "1"
proxmox-base64 = "1"
proxmox-client = "1"
proxmox-daemon = "1"
+proxmox-docgen = "1"
proxmox-http = { version = "1", features = [ "client", "http-helpers", "websocket" ] } # see below
proxmox-human-byte = "1"
proxmox-io = "1.0.1" # tools and client use "tokio" feature
@@ -154,6 +155,7 @@ zstd = { version = "0.13" }
# proxmox-config-digest = { path = "../proxmox/proxmox-config-digest" }
# proxmox-daemon = { path = "../proxmox/proxmox-daemon" }
# proxmox-dns-api = { path = "../proxmox/proxmox-dns-api" }
+# proxmox-docgen = { path = "../proxmox/proxmox-docgen" }
# proxmox-http-error = { path = "../proxmox/proxmox-http-error" }
# proxmox-http = { path = "../proxmox/proxmox-http" }
# proxmox-human-byte = { path = "../proxmox/proxmox-human-byte" }
diff --git a/server/Cargo.toml b/server/Cargo.toml
index a215aaf..db66789 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -39,6 +39,7 @@ proxmox-async.workspace = true
proxmox-auth-api = { workspace = true, features = [ "api", "ticket", "pam-authenticator", "password-authenticator" ] }
proxmox-base64.workspace = true
proxmox-daemon.workspace = true
+proxmox-docgen.workspace = true
proxmox-http = { workspace = true, features = [ "client-trait", "proxmox-async" ] } # pbs-client doesn't use these
proxmox-lang.workspace = true
proxmox-ldap.workspace = true
diff --git a/server/src/bin/docgen.rs b/server/src/bin/docgen.rs
index cbf6fc0..2d9c296 100644
--- a/server/src/bin/docgen.rs
+++ b/server/src/bin/docgen.rs
@@ -6,7 +6,7 @@ use proxmox_schema::format::{dump_enum_properties, get_property_string_type_text
use proxmox_schema::{ApiStringFormat, ApiType, ObjectSchemaType, Schema};
use proxmox_section_config::{dump_section_config, typed::ApiSectionDataEntry};
-use pbs_api_types::PRIVILEGES;
+use pdm_api_types::PRIVILEGES;
use server::api;
@@ -45,12 +45,10 @@ fn main() -> Result<(), Error> {
fn generate_api_tree() -> String {
let mut tree = Vec::new();
- let mut data = dump_api_schema(&api::ROUTER, ".");
+ let mut data = proxmox_docgen::generate_api_tree(&api::ROUTER, ".", PRIVILEGES);
data["path"] = "/".into();
// hack: add invisible space to sort as first entry
data["text"] = "​Management API (HTTP)".into();
- data["expanded"] = true.into();
-
tree.push(data);
format!(
@@ -58,305 +56,3 @@ fn generate_api_tree() -> String {
serde_json::to_string_pretty(&tree).unwrap()
)
}
-
-pub fn dump_schema(schema: &Schema) -> Value {
- let mut data;
-
- match schema {
- Schema::Null => {
- data = json!({
- "type": "null",
- });
- }
- Schema::Boolean(boolean_schema) => {
- data = json!({
- "type": "boolean",
- "description": boolean_schema.description,
- });
- if let Some(default) = boolean_schema.default {
- data["default"] = default.into();
- }
- }
- Schema::String(string_schema) => {
- data = json!({
- "type": "string",
- "description": string_schema.description,
- });
- if let Some(default) = string_schema.default {
- data["default"] = default.into();
- }
- if let Some(min_length) = string_schema.min_length {
- data["minLength"] = min_length.into();
- }
- if let Some(max_length) = string_schema.max_length {
- data["maxLength"] = max_length.into();
- }
- if let Some(type_text) = string_schema.type_text {
- data["typetext"] = type_text.into();
- }
- match string_schema.format {
- None | Some(ApiStringFormat::VerifyFn(_)) => { /* do nothing */ }
- Some(ApiStringFormat::Pattern(const_regex)) => {
- data["pattern"] = format!("/{}/", const_regex.regex_string).into();
- }
- Some(ApiStringFormat::Enum(variants)) => {
- let variants: Vec<String> =
- variants.iter().map(|e| e.value.to_string()).collect();
- data["enum"] = serde_json::to_value(variants).unwrap();
- }
- Some(ApiStringFormat::PropertyString(subschema)) => {
- match subschema {
- Schema::Object(_) | Schema::Array(_) => {
- data["format"] = dump_schema(subschema);
- data["typetext"] = get_property_string_type_text(subschema).into();
- }
- _ => { /* do nothing - should not happen */ }
- };
- }
- }
- // fixme: dump format
- }
- Schema::Integer(integer_schema) => {
- data = json!({
- "type": "integer",
- "description": integer_schema.description,
- });
- if let Some(default) = integer_schema.default {
- data["default"] = default.into();
- }
- if let Some(minimum) = integer_schema.minimum {
- data["minimum"] = minimum.into();
- }
- if let Some(maximum) = integer_schema.maximum {
- data["maximum"] = maximum.into();
- }
- }
- Schema::Number(number_schema) => {
- data = json!({
- "type": "number",
- "description": number_schema.description,
- });
- if let Some(default) = number_schema.default {
- data["default"] = default.into();
- }
- if let Some(minimum) = number_schema.minimum {
- data["minimum"] = minimum.into();
- }
- if let Some(maximum) = number_schema.maximum {
- data["maximum"] = maximum.into();
- }
- }
- Schema::Object(object_schema) => {
- data = dump_property_schema(object_schema);
- data["type"] = "object".into();
- if let Some(default_key) = object_schema.default_key {
- data["default_key"] = default_key.into();
- }
- }
- Schema::Array(array_schema) => {
- data = json!({
- "type": "array",
- "description": array_schema.description,
- "items": dump_schema(array_schema.items),
- });
- if let Some(min_length) = array_schema.min_length {
- data["minLength"] = min_length.into();
- }
- if let Some(max_length) = array_schema.min_length {
- data["maxLength"] = max_length.into();
- }
- }
- Schema::AllOf(alloff_schema) => {
- data = dump_property_schema(alloff_schema);
- data["type"] = "object".into();
- }
- Schema::OneOf(schema) => {
- let mut type_schema = dump_schema(schema.type_schema());
- if schema.type_property_entry.1 {
- type_schema["optional"] = true.into();
- }
- data = json!({
- "type": "object",
- "description": schema.description,
- "typeProperty": schema.type_property(),
- "typeSchema": type_schema,
- });
- let mut variants = Vec::with_capacity(schema.list.len());
- for (title, variant) in schema.list {
- let mut entry = dump_schema(variant);
- entry["title"] = (*title).into();
- variants.push(entry);
- }
- data["oneOf"] = variants.into();
- }
- };
-
- data
-}
-
-pub fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
- let mut properties = json!({});
-
- for (prop, optional, schema) in param.properties() {
- let mut property = dump_schema(schema);
- if *optional {
- property["optional"] = 1.into();
- }
- properties[prop] = property;
- }
-
- let data = json!({
- "description": param.description(),
- "additionalProperties": param.additional_properties(),
- "properties": properties,
- });
-
- data
-}
-
-fn dump_api_permission(permission: &Permission) -> Value {
- match permission {
- Permission::Superuser => json!({ "user": "root@pam" }),
- Permission::User(user) => json!({ "user": user }),
- Permission::Anybody => json!({ "user": "all" }),
- Permission::World => json!({ "user": "world" }),
- Permission::UserParam(param) => json!({ "userParam": param }),
- Permission::Group(group) => json!({ "group": group }),
- Permission::WithParam(param, sub_permission) => {
- json!({
- "withParam": {
- "name": param,
- "permissions": dump_api_permission(sub_permission),
- },
- })
- }
- Permission::Privilege(name, value, partial) => {
- let mut privs = Vec::new();
- for (name, v) in PRIVILEGES {
- if (value & v) != 0 {
- privs.push(name.to_string());
- }
- }
-
- json!({
- "check": {
- "path": name,
- "privs": privs,
- "partial": partial,
- }
- })
- }
- Permission::And(list) => {
- let list: Vec<Value> = list.iter().map(|p| dump_api_permission(p)).collect();
- json!({ "and": list })
- }
- Permission::Or(list) => {
- let list: Vec<Value> = list.iter().map(|p| dump_api_permission(p)).collect();
- json!({ "or": list })
- }
- }
-}
-
-fn dump_api_method_schema(method: &str, api_method: &ApiMethod) -> Value {
- let mut data = json!({
- "description": api_method.parameters.description(),
- });
-
- data["parameters"] = dump_property_schema(&api_method.parameters);
-
- let mut returns = dump_schema(api_method.returns.schema);
- if api_method.returns.optional {
- returns["optional"] = 1.into();
- }
- data["returns"] = returns;
-
- match api_method.access {
- ApiAccess {
- description: None,
- permission: Permission::Superuser,
- } => {
- // no need to output default
- }
- ApiAccess {
- description,
- permission,
- } => {
- let mut permissions = dump_api_permission(permission);
- if let Some(description) = description {
- permissions["description"] = description.into();
- }
- data["permissions"] = permissions;
- }
- }
-
- let mut method = method;
-
- if let ApiHandler::AsyncHttp(_) = api_method.handler {
- method = if method == "POST" { "UPLOAD" } else { method };
- method = if method == "GET" { "DOWNLOAD" } else { method };
- }
-
- data["method"] = method.into();
-
- data
-}
-
-pub fn dump_api_schema(router: &Router, path: &str) -> Value {
- let mut data = json!({});
-
- let mut info = json!({});
- if let Some(api_method) = router.get {
- info["GET"] = dump_api_method_schema("GET", api_method);
- }
- if let Some(api_method) = router.post {
- info["POST"] = dump_api_method_schema("POST", api_method);
- }
- if let Some(api_method) = router.put {
- info["PUT"] = dump_api_method_schema("PUT", api_method);
- }
- if let Some(api_method) = router.delete {
- info["DELETE"] = dump_api_method_schema("DELETE", api_method);
- }
-
- data["info"] = info;
-
- match &router.subroute {
- None => {
- data["leaf"] = 1.into();
- }
- Some(SubRoute::MatchAll { router, param_name }) => {
- let sub_path = if path == "." {
- format!("/{{{}}}", param_name)
- } else {
- format!("{}/{{{}}}", path, param_name)
- };
- let mut child = dump_api_schema(router, &sub_path);
- child["path"] = sub_path.into();
- child["text"] = format!("{{{}}}", param_name).into();
-
- let children = vec![child];
- data["children"] = children.into();
- data["leaf"] = 0.into();
- }
- Some(SubRoute::Map(dirmap)) => {
- let mut children = Vec::new();
-
- for (key, sub_router) in dirmap.iter() {
- let sub_path = if path == "." {
- format!("/{}", key)
- } else {
- format!("{}/{}", path, key)
- };
- let mut child = dump_api_schema(sub_router, &sub_path);
- child["path"] = sub_path.into();
- child["text"] = key.to_string().into();
- children.push(child);
- }
-
- data["children"] = children.into();
- data["leaf"] = 0.into();
- }
- }
-
- data
-}
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 2/2] api-viewer: add an api-viewer package
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (6 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 1/2] docgen: switch to " Shannon Sterz
@ 2025-11-12 10:24 ` Shannon Sterz
2025-11-13 12:02 ` [pdm-devel] Superseded: Re: [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 10:24 UTC (permalink / raw)
To: pdm-devel
this can be installed as a suggested package and will then be served
at `https://$pdm-host::8443/docs/api-viewer/apidoc.js`.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
Makefile | 1 +
debian/control | 13 +++++++++
...xmox-datacenter-manager-api-viewer.install | 1 +
docs/api-viewer/Makefile | 27 +++++++++++++++++++
docs/api-viewer/index.html | 16 +++++++++++
5 files changed, 58 insertions(+)
create mode 100644 debian/proxmox-datacenter-manager-api-viewer.install
create mode 100644 docs/api-viewer/Makefile
create mode 100644 docs/api-viewer/index.html
diff --git a/Makefile b/Makefile
index b2f794a..ca721eb 100644
--- a/Makefile
+++ b/Makefile
@@ -82,6 +82,7 @@ install: $(COMPILED_BINS) $(SHELL_COMPLETION_FILES)
$(foreach i,$(ZSH_COMPLETIONS), \
install -m644 $(COMPLETION_DIR)/$(i) $(DESTDIR)$(ZSHCOMPDIR)/ ;)
make -C services install
+ make -C docs/api-viewer install
$(COMPILED_BINS): .do-cargo-build
.do-cargo-build:
diff --git a/debian/control b/debian/control
index 19b13c8..ebe0923 100644
--- a/debian/control
+++ b/debian/control
@@ -125,6 +125,7 @@ Build-Depends: cargo:native,
librust-zstd-0.13+default-dev,
libstd-rust-dev,
libsystemd-dev,
+ proxmox-widget-toolkit-dev,
rustc:native,
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.6.1
@@ -143,6 +144,7 @@ Depends: proxmox-mini-journalreader,
${misc:Depends},
${shlibs:Depends},
Recommends: proxmox-datacenter-manager-client, proxmox-datacenter-manager-ui,
+Suggests: proxmox-datacenter-manager-api-viewer,
Description: Manage multiple Proxmox VE cluster and other Proxmox projects
This package provides the API daemons of the Proxmox Datacenter Manager (PDM)
which allows one to add multiple Proxmox VE and Proxmox Backup Server
@@ -155,3 +157,14 @@ Depends: ${misc:Depends}, ${shlibs:Depends},
Description: CLI Client for the Proxmox Datacenter Manager
This package provides the CLI client that can interface with a Proxmox
Datacenter Manager (PDM) instance.
+
+Package: proxmox-datacenter-manager-api-viewer
+Architecture: any
+Multi-Arch: allowed
+Depends: fonts-font-awesome,
+ libjs-extjs (>= 7~),
+ proxmox-widget-toolkit,
+ ${misc:Depends},
+ ${shlibs:Depends},
+Recommends: proxmox-datacenter-manager,
+Description: API Viewer for the Proxmox Datacenter Manager.
diff --git a/debian/proxmox-datacenter-manager-api-viewer.install b/debian/proxmox-datacenter-manager-api-viewer.install
new file mode 100644
index 0000000..0e4d9df
--- /dev/null
+++ b/debian/proxmox-datacenter-manager-api-viewer.install
@@ -0,0 +1 @@
+usr/share/doc/proxmox-datacenter-manager/html
diff --git a/docs/api-viewer/Makefile b/docs/api-viewer/Makefile
new file mode 100644
index 0000000..c64bfbb
--- /dev/null
+++ b/docs/api-viewer/Makefile
@@ -0,0 +1,27 @@
+DOCDIR = /usr/share/doc/proxmox-datacenter-manager
+
+API_VIEWER_SOURCES= \
+ index.html \
+ apidoc.js
+
+API_VIEWER_FILES := \
+ apidata.js \
+ /usr/share/javascript/proxmox-widget-toolkit-dev/APIViewer.js
+
+ifeq ($(BUILD_MODE), release)
+COMPILEDIR := ../../target/release
+else
+COMPILEDIR := ../../target/debug
+endif
+
+.PHONY: install
+install: ${API_VIEWER_SOURCES}
+ install -dm 0755 $(DESTDIR)$(DOCDIR)/html/api-viewer
+ install -m 0644 ${API_VIEWER_SOURCES} $(DESTDIR)$(DOCDIR)/html/api-viewer
+
+apidata.js: ${COMPILEDIR}/docgen
+ ${COMPILEDIR}/docgen apidata.js >$@
+
+apidoc.js: ${API_VIEWER_FILES}
+ cat ${API_VIEWER_FILES} >$@.tmp
+ mv $@.tmp $@
diff --git a/docs/api-viewer/index.html b/docs/api-viewer/index.html
new file mode 100644
index 0000000..2bf97c5
--- /dev/null
+++ b/docs/api-viewer/index.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+ <title>Proxmox Datacenter Manager API Documentation</title>
+
+ <link rel="stylesheet" type="text/css" href="/extjs/theme-crisp/resources/theme-crisp-all.css">
+ <link rel="stylesheet" type="text/css" href="/proxmox-extjs-widget-toolkit/css/ext6-pmx.css" />
+ <link rel="stylesheet" type="text/css" media="(prefers-color-scheme: dark)" href="/proxmox-extjs-widget-toolkit/themes/theme-proxmox-dark.css" />
+ <link rel="stylesheet" type="text/css" href="/fontawesome/css/font-awesome.css" />
+ <script type="text/javascript" src="/extjs/ext-all.js"></script>
+ <script type="text/javascript" src="apidoc.js"></script>
+</head>
+<body></body>
+</html>
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types Shannon Sterz
@ 2025-11-12 16:09 ` Shannon Sterz
0 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-12 16:09 UTC (permalink / raw)
To: Shannon Sterz; +Cc: pdm-devel
note that Wolfgang is already working on a fix for this, so this commit
can just be dropped. since it's just about dropping this commit, i'll
refrain from respin this series just for that change. hope that's ok, if
not let me know.
On Wed Nov 12, 2025 at 11:24 AM CET, Shannon Sterz wrote:
> Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
> ---
> proxmox-docgen/src/lib.rs | 38 +++++++++++++++++++++++++++-----------
> 1 file changed, 27 insertions(+), 11 deletions(-)
>
> diff --git a/proxmox-docgen/src/lib.rs b/proxmox-docgen/src/lib.rs
> index 67d502d5..f3d53519 100644
> --- a/proxmox-docgen/src/lib.rs
> +++ b/proxmox-docgen/src/lib.rs
> @@ -97,7 +97,7 @@ fn dump_schema(schema: &Schema) -> Value {
> }
> }
> Schema::Object(object_schema) => {
> - data = dump_property_schema(object_schema);
> + data = dump_property_schema(object_schema, false);
> data["type"] = "object".into();
> if let Some(default_key) = object_schema.default_key {
> data["default_key"] = default_key.into();
> @@ -117,7 +117,12 @@ fn dump_schema(schema: &Schema) -> Value {
> }
> }
> Schema::AllOf(alloff_schema) => {
> - data = dump_property_schema(alloff_schema);
> + let dirty_allof = alloff_schema
> + .list
> + .iter()
> + .any(|schema| schema.any_object().is_none());
> +
> + data = dump_property_schema(alloff_schema, dirty_allof);
> data["type"] = "object".into();
> }
> Schema::OneOf(schema) => {
> @@ -144,7 +149,8 @@ fn dump_schema(schema: &Schema) -> Value {
> data
> }
>
> -fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
> +// TODO: remove `dirty_allof` again once pve-api-types generates proper `AllOf` schema.
> +fn dump_property_schema(param: &dyn ObjectSchemaType, dirty_allof: bool) -> Value {
> let mut properties = json!({});
>
> for (prop, optional, schema) in param.properties() {
> @@ -155,13 +161,23 @@ fn dump_property_schema(param: &dyn ObjectSchemaType) -> Value {
> properties[prop] = property;
> }
>
> - let data = json!({
> - "description": param.description(),
> - "additionalProperties": param.additional_properties(),
> - "properties": properties,
> - });
> -
> - data
> + if dirty_allof {
> + json!({
> + "description": param.description(),
> + // stop gap for now. pve-api-types generates certain properties as string schema that
> + // are really property strings (which wrap an object schema anyway). however,
> + // `additional_properties()` panics there, because it will expect *only* object
> + // schema.
> + "additionalProperties": true,
> + "properties": properties,
> + })
> + } else {
> + json!({
> + "description": param.description(),
> + "additionalProperties": param.additional_properties(),
> + "properties": properties,
> + })
> + }
> }
>
> fn dump_api_permission(permission: &Permission, privileges: &[(&str, u64)]) -> Value {
> @@ -222,7 +238,7 @@ fn dump_api_method_schema(
> "description": api_method.parameters.description(),
> });
>
> - data["parameters"] = dump_property_schema(&api_method.parameters);
> + data["parameters"] = dump_property_schema(&api_method.parameters, false);
>
> let mut returns = dump_schema(api_method.returns.schema);
> if api_method.returns.optional {
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pdm-devel] Superseded: Re: [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
` (7 preceding siblings ...)
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 2/2] api-viewer: add an api-viewer package Shannon Sterz
@ 2025-11-13 12:02 ` Shannon Sterz
8 siblings, 0 replies; 11+ messages in thread
From: Shannon Sterz @ 2025-11-13 12:02 UTC (permalink / raw)
To: Shannon Sterz; +Cc: pdm-devel
Superseded-by: https://lore.proxmox.com/pdm-devel/20251113120021.331639-1-s.sterz@proxmox.com/
On Wed Nov 12, 2025 at 11:24 AM CET, Shannon Sterz wrote:
> this series adds a flag that marks certain api endpoints as unstable.
> allowing for more fine grained control over when and what we declare to
> be stable. it also adds an api viewer to pdm as a separate package that
> is only suggested for now.
>
> the code that dumps api definitions as json is also moved to proxmox-rs
> and unified between pdm and pbs to reduce technical debt.
>
> Follow Ups
> ----------
>
> * currently the proxmox-docgen handles AllOf schemas that have
> additional properties that are not object schema to always have
> additoinal properties. otherwise, a panic would be caused in
> proxmox-schema. the proper fix here is to adapt schema generation in
> pve-api-types that is caused by some properties being generated as
> string schemas that wrap a property string.
>
> the correct way of handling those would be to either fix
> proxmox-schema to also handle these types of schema without panicing
> or to not generate these type of schema in the first place.
>
> proxmox-widget-toolkit:
>
> Shannon Sterz (1):
> api viewer: add support for endpoints that are marked as unstable
>
> src/api-viewer/APIViewer.js | 18 ++++++++++++++++--
> 1 file changed, 16 insertions(+), 2 deletions(-)
>
>
> proxmox:
>
> Shannon Sterz (4):
> router/api-macro: add unstable flag for ApiMethod
> docgen: add docgen crate
> docgen: add support for the new stable flag
> docgen: add a stop-gap fix to allow generating schema for
> pve-api-types
>
> Cargo.toml | 1 +
> proxmox-api-macro/src/api/method.rs | 9 +-
> proxmox-docgen/Cargo.toml | 20 ++
> proxmox-docgen/debian/changelog | 5 +
> proxmox-docgen/debian/control | 36 +++
> proxmox-docgen/debian/copyright | 18 ++
> proxmox-docgen/debian/debcargo.toml | 7 +
> proxmox-docgen/src/lib.rs | 339 ++++++++++++++++++++++++++++
> proxmox-router/src/router.rs | 10 +
> 9 files changed, 444 insertions(+), 1 deletion(-)
> create mode 100644 proxmox-docgen/Cargo.toml
> create mode 100644 proxmox-docgen/debian/changelog
> create mode 100644 proxmox-docgen/debian/control
> create mode 100644 proxmox-docgen/debian/copyright
> create mode 100644 proxmox-docgen/debian/debcargo.toml
> create mode 100644 proxmox-docgen/src/lib.rs
>
>
> proxmox-backup:
>
> Shannon Sterz (1):
> docgen: use proxmox-rs docgen crate
>
> Cargo.toml | 3 +
> docs/api-viewer/index.html | 2 +
> src/bin/docgen.rs | 312 +------------------------------------
> 3 files changed, 10 insertions(+), 307 deletions(-)
>
>
> proxmox-datacenter-manager:
>
> Shannon Sterz (2):
> docgen: switch to proxmox-rs docgen crate
> api-viewer: add an api-viewer package
>
> Cargo.toml | 2 +
> Makefile | 1 +
> debian/control | 13 +
> ...xmox-datacenter-manager-api-viewer.install | 1 +
> docs/api-viewer/Makefile | 27 ++
> docs/api-viewer/index.html | 16 +
> server/Cargo.toml | 1 +
> server/src/bin/docgen.rs | 308 +-----------------
> 8 files changed, 63 insertions(+), 306 deletions(-)
> create mode 100644 debian/proxmox-datacenter-manager-api-viewer.install
> create mode 100644 docs/api-viewer/Makefile
> create mode 100644 docs/api-viewer/index.html
>
>
> Summary over all repositories:
> 21 files changed, 533 insertions(+), 616 deletions(-)
>
> --
> Generated by git-murpp 0.8.1
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-11-13 12:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-12 10:24 [pdm-devel] [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH widget-toolkit 1/1] api viewer: add support for endpoints that are marked as unstable Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 1/4] router/api-macro: add unstable flag for ApiMethod Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 2/4] docgen: add docgen crate Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 3/4] docgen: add support for the new stable flag Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox 4/4] docgen: add a stop-gap fix to allow generating schema for pve-api-types Shannon Sterz
2025-11-12 16:09 ` Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH proxmox-backup 1/1] docgen: use proxmox-rs docgen crate Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 1/2] docgen: switch to " Shannon Sterz
2025-11-12 10:24 ` [pdm-devel] [PATCH datacenter-manager 2/2] api-viewer: add an api-viewer package Shannon Sterz
2025-11-13 12:02 ` [pdm-devel] Superseded: Re: [PATCH datacenter-manager/proxmox{, -backup}/widget-toolkit 0/8] unstable flag and pdm api viewer Shannon Sterz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox