From 8de8d2df9abd098fcbe02b5448c103f10fbb5aad Mon Sep 17 00:00:00 2001 From: beckerinj Date: Thu, 6 Apr 2023 01:03:01 -0400 Subject: [PATCH] work on API docs --- docsite/docs/api/authenticating-requests.md | 8 + docsite/docs/api/build.mdx | 236 ++++++++++++++++++++ docsite/docs/api/index.mdx | 11 + docsite/docs/api/login.mdx | 113 ++++++++++ docsite/docs/api/websocket.mdx | 0 docsite/docs/core-setup/authentication.md | 0 docsite/docs/core-setup/index.mdx | 0 docsite/docusaurus.config.js | 6 + docsite/sidebars.js | 9 + docsite/src/components/Divider.tsx | 15 ++ lib/monitor_client/src/build.rs | 6 +- lib/monitor_client/src/lib.rs | 112 +++++----- tests/src/tests.rs | 4 +- 13 files changed, 459 insertions(+), 61 deletions(-) create mode 100644 docsite/docs/api/authenticating-requests.md create mode 100644 docsite/docs/api/build.mdx create mode 100644 docsite/docs/api/index.mdx create mode 100644 docsite/docs/api/login.mdx create mode 100644 docsite/docs/api/websocket.mdx create mode 100644 docsite/docs/core-setup/authentication.md create mode 100644 docsite/docs/core-setup/index.mdx create mode 100644 docsite/src/components/Divider.tsx diff --git a/docsite/docs/api/authenticating-requests.md b/docsite/docs/api/authenticating-requests.md new file mode 100644 index 000000000..d4f444cf1 --- /dev/null +++ b/docsite/docs/api/authenticating-requests.md @@ -0,0 +1,8 @@ +# authenticating requests + +monitor uses the `JSON Web Token (JWT)` standard to authenticate all requests to subroutes under `/api`. +users can acquire a `JWT` using a [login method](/api/login). + +to authenticate requests, pass the `JWT` under the `Authorization` header: + +`Authorization: Bearer ` \ No newline at end of file diff --git a/docsite/docs/api/build.mdx b/docsite/docs/api/build.mdx new file mode 100644 index 000000000..ecc0f75b6 --- /dev/null +++ b/docsite/docs/api/build.mdx @@ -0,0 +1,236 @@ +import Divider from '@site/src/components/Divider'; + +# build | /build + +these routes relate to interacting with monitor `builds` + +| name | method | Description | +| ---- | ------ | ----------- | +| [list builds](/api/build#list-builds) | `GET /api/build/list` | | +| [get build](/api/build#get-build) | `GET /api/build/` | | +| get build | Text | | +| get build | Text | | +| get build | Text | | +| get build | Text | | +| get build | Text | | +| get build | Text | | + +```mdx-code-block + +``` + +## list builds +`GET /api/build/list` + +this method will return an array of builds the requesting user has a minimum of `Read` permissions on. + + +### response body +``` +Array +``` + +```mdx-code-block + +``` + +## get build +`GET /api/build/` + +### response body +``` +Build +``` + +```mdx-code-block + +``` + +## get build action state +`GET /api/build//action_state` + +this method returns the action state for the build, eg. whether the build is currently `building`. + + +### response body +```json +{ + building: boolean, + updating: boolean, +} +``` + +```mdx-code-block + +``` + +## get build versions +`GET /api/build//versions` + +paginated route for fetching the most recent available versions of this build. + +### query params +```json +page=number // optional, default is 0. pagination starting at page 0. +major=number // optional. filter by major version number +minor=number // optional. filter by minor version number +patch=number // optional. filter by patch version number +``` + +### response body +```json +[ + { + ts: rfc3339_timestamp, + version: { + major: number, + minor: number, + patch: number, + } + }, + ... +] +``` + +```mdx-code-block + +``` + +## create build +`POST /api/build/create` + + +### request body +```json +{ + name: string, +} +``` + +### response body +``` +Build +``` + +```mdx-code-block + +``` + +## create full build +`POST /api/build/create_full` + + +### request body +``` +Build +``` + +### response body +``` +Build +``` + +```mdx-code-block + +``` + +## copy build +`POST /api/build//copy` + +this method will create a copy of the build with a new _id and name, +with all the same configuration as the target build. + + +### request body +```json +{ + name: string, // the new name +} +``` + +### response body +```json +Build // the copied build +``` + +```mdx-code-block + +``` + +## delete build +`DELETE /api/build//delete` + +### response body +```json +Build // the deleted build +``` + +```mdx-code-block + +``` + +## update build +`PATCH /api/build/update` + +### request body +``` +Build +``` + +### response body +``` +Build +``` + +```mdx-code-block + +``` + +## build (action) +`POST /api/build//build` + +### response body +``` +Update +``` + +```mdx-code-block + +``` + +## get aws builder defaults +`GET /api/build/aws_builder_defaults` + +### response body +```json +{ + default_ami_name: string, + default_subnet_id: string, + default_key_pair_name: string, + default_region: string, + default_volume_gb: number, + default_instance_type: string, + default_security_group_ids: string[], + default_assign_public_ip: boolean, + available_ami_accounts: [ + { + ami_id: string, + github: string[], + docker: string[], + secrets: string[], + } + ], +} +``` + +```mdx-code-block + +``` + +## get allowed docker organizations +`GET /api/build/docker_organizations` + +### response body +```json +string[] // the names of the allowed docker organizations +``` \ No newline at end of file diff --git a/docsite/docs/api/index.mdx b/docsite/docs/api/index.mdx new file mode 100644 index 000000000..3922e708d --- /dev/null +++ b/docsite/docs/api/index.mdx @@ -0,0 +1,11 @@ +--- +slug: /api +--- + +this section documents the rest and websocket api + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; + + +``` \ No newline at end of file diff --git a/docsite/docs/api/login.mdx b/docsite/docs/api/login.mdx new file mode 100644 index 000000000..38a782319 --- /dev/null +++ b/docsite/docs/api/login.mdx @@ -0,0 +1,113 @@ +import Divider from '@site/src/components/Divider'; + +# login | /auth + +*routes for user login* + +monitor supports local login (username and password), Oauth2 login (github and google), +and secret login (username and API secret key). +each method must be explicitly enabled in your monitor core config, +otherwise the api won't be available. + +:::note +in order to login to an Oauth2 user's account programmatically, +you must generate an API secret and login using [/auth/secret/login](/api/login#login-using-api-secret) +::: + +```mdx-code-block + +``` + +## get login options +`GET /auth/options` + +this method is used to obtain the login options for monitor core + +### query params +none + +### request body +none + +### response body +```json +{ + local: boolean, + github: boolean, + google: boolean, +} +``` + +```mdx-code-block + +``` + +## create local user account +`POST /auth/local/create_user` + +this method will create a new local auth account with the provided **username** and **password**, +and return a `JWT` for the user to authenticate with. + +### query params +none + +### request body +```json +{ + username: string, + password: string, +} +``` + +### response body +`` + +:::caution +a user created with this method is, by default, `disabled`. a monitor admin must enable their account before they can access the API. +::: + +```mdx-code-block + +``` + +## login local user account +`POST /auth/local/login` + +this method will authenticate a local users credentials and return a JWT if login is successful. + +### query params +none + +### request body +```json +{ + username: string, + password: string, +} +``` + +### response body +`` + +```mdx-code-block + +``` + +## login using API secret +`POST /auth/secret/login` + +this method will authenticate a users account of any kind using an API secret generated using [/api/secret/create](/) + +### query params +none + +### request body +```json +{ + username: string, + secret: string, +} +``` + +### response body +`` \ No newline at end of file diff --git a/docsite/docs/api/websocket.mdx b/docsite/docs/api/websocket.mdx new file mode 100644 index 000000000..e69de29bb diff --git a/docsite/docs/core-setup/authentication.md b/docsite/docs/core-setup/authentication.md new file mode 100644 index 000000000..e69de29bb diff --git a/docsite/docs/core-setup/index.mdx b/docsite/docs/core-setup/index.mdx new file mode 100644 index 000000000..e69de29bb diff --git a/docsite/docusaurus.config.js b/docsite/docusaurus.config.js index 2b7055f20..d43ded241 100644 --- a/docsite/docusaurus.config.js +++ b/docsite/docusaurus.config.js @@ -59,6 +59,11 @@ const config = { ({ // Replace with your project's social card image: "img/monitor-lizard.png", + docs: { + sidebar: { + autoCollapseCategories: true, + } + }, navbar: { title: "monitor", logo: { @@ -71,6 +76,7 @@ const config = { sidebarId: "docs", position: "left", label: "docs", + }, { href: "https://github.com/mbecker20/monitor", diff --git a/docsite/sidebars.js b/docsite/sidebars.js index b9aacb3d3..bdc579e5f 100644 --- a/docsite/sidebars.js +++ b/docsite/sidebars.js @@ -61,6 +61,15 @@ const sidebars = { }, "permissioning", "file-paths", + { + type: "category", + label: "API", + link: { + type: "doc", + id: "api/index", + }, + items: ["api/authenticating-requests", "api/login", "api/build", "api/websocket"], + }, ], }; diff --git a/docsite/src/components/Divider.tsx b/docsite/src/components/Divider.tsx new file mode 100644 index 000000000..5397e09d6 --- /dev/null +++ b/docsite/src/components/Divider.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +export default function Divider() { + return ( +
+ ); +} diff --git a/lib/monitor_client/src/build.rs b/lib/monitor_client/src/build.rs index 04b8162be..619af15cd 100644 --- a/lib/monitor_client/src/build.rs +++ b/lib/monitor_client/src/build.rs @@ -44,14 +44,14 @@ impl MonitorClient { .context("failed at getting build versions") } - pub async fn create_build(&self, name: &str, server_id: &str) -> anyhow::Result { + pub async fn create_build(&self, name: &str) -> anyhow::Result { self.post( "/api/build/create", - json!({ "name": name, "server_id": server_id }), + json!({ "name": name }), ) .await .context(format!( - "failed at creating build with name {name} on server id {server_id}" + "failed at creating build with name {name}" )) } diff --git a/lib/monitor_client/src/lib.rs b/lib/monitor_client/src/lib.rs index 4f3e8bbd3..b92e09860 100644 --- a/lib/monitor_client/src/lib.rs +++ b/lib/monitor_client/src/lib.rs @@ -297,34 +297,34 @@ impl MonitorClient { } } - async fn _patch_string( - &self, - endpoint: &str, - body: impl Into>, - ) -> anyhow::Result { - let req = self - .http_client - .patch(format!("{}{endpoint}", self.url)) - .header("Authorization", format!("Bearer {}", self.token)); - let req = if let Some(body) = body.into() { - req.header("Content-Type", "application/json").json(&body) - } else { - req - }; - let res = req.send().await.context("failed to reach monitor api")?; - let status = res.status(); - if status == StatusCode::OK { - match res.text().await { - Ok(res) => Ok(res), - Err(e) => Err(anyhow!("{status}: {e:#?}")), - } - } else { - match res.text().await { - Ok(res) => Err(anyhow!("{status}: {res}")), - Err(e) => Err(anyhow!("{status}: {e:#?}")), - } - } - } + // async fn _patch_string( + // &self, + // endpoint: &str, + // body: impl Into>, + // ) -> anyhow::Result { + // let req = self + // .http_client + // .patch(format!("{}{endpoint}", self.url)) + // .header("Authorization", format!("Bearer {}", self.token)); + // let req = if let Some(body) = body.into() { + // req.header("Content-Type", "application/json").json(&body) + // } else { + // req + // }; + // let res = req.send().await.context("failed to reach monitor api")?; + // let status = res.status(); + // if status == StatusCode::OK { + // match res.text().await { + // Ok(res) => Ok(res), + // Err(e) => Err(anyhow!("{status}: {e:#?}")), + // } + // } else { + // match res.text().await { + // Ok(res) => Err(anyhow!("{status}: {res}")), + // Err(e) => Err(anyhow!("{status}: {e:#?}")), + // } + // } + // } async fn delete( &self, @@ -355,34 +355,34 @@ impl MonitorClient { } } - async fn _delete_string( - &self, - endpoint: &str, - body: impl Into>, - ) -> anyhow::Result { - let req = self - .http_client - .delete(format!("{}{endpoint}", self.url)) - .header("Authorization", format!("Bearer {}", self.token)); - let req = if let Some(body) = body.into() { - req.header("Content-Type", "application/json").json(&body) - } else { - req - }; - let res = req.send().await.context("failed to reach monitor api")?; - let status = res.status(); - if status == StatusCode::OK { - match res.text().await { - Ok(res) => Ok(res), - Err(e) => Err(anyhow!("{status}: {e:#?}")), - } - } else { - match res.text().await { - Ok(res) => Err(anyhow!("{status}: {res}")), - Err(e) => Err(anyhow!("{status}: {e:#?}")), - } - } - } + // async fn _delete_string( + // &self, + // endpoint: &str, + // body: impl Into>, + // ) -> anyhow::Result { + // let req = self + // .http_client + // .delete(format!("{}{endpoint}", self.url)) + // .header("Authorization", format!("Bearer {}", self.token)); + // let req = if let Some(body) = body.into() { + // req.header("Content-Type", "application/json").json(&body) + // } else { + // req + // }; + // let res = req.send().await.context("failed to reach monitor api")?; + // let status = res.status(); + // if status == StatusCode::OK { + // match res.text().await { + // Ok(res) => Ok(res), + // Err(e) => Err(anyhow!("{status}: {e:#?}")), + // } + // } else { + // match res.text().await { + // Ok(res) => Err(anyhow!("{status}: {res}")), + // Err(e) => Err(anyhow!("{status}: {e:#?}")), + // } + // } + // } } fn parse_url(url: &str) -> String { diff --git a/tests/src/tests.rs b/tests/src/tests.rs index e07fa8f05..c8258112c 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -35,7 +35,7 @@ pub async fn create_test_setup( let mut builds = monitor.list_builds(None).await?; let build = if builds.is_empty() { monitor - .create_build(&format!("{group_name}_build"), &server.id) + .create_build(&format!("{group_name}_build")) .await .context("failed at create build")? } else { @@ -98,7 +98,7 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result { .await .context("failed at list servers")?; let server = &servers.get(0).ok_or(anyhow!("no servers"))?.server; - let mut build = monitor.create_build("old_periphery", &server.id).await?; + let mut build = monitor.create_build("old_periphery").await?; println!("created build. updating..."); build.repo = Some("mbecker20/monitor".to_string()); // build.branch = Some("");