work on API docs

This commit is contained in:
beckerinj
2023-04-06 01:03:01 -04:00
parent 1dffdbddc2
commit 8de8d2df9a
13 changed files with 459 additions and 61 deletions

View File

@@ -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 <JWT>`

236
docsite/docs/api/build.mdx Normal file
View File

@@ -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/<build_id>` | |
| get build | Text | |
| get build | Text | |
| get build | Text | |
| get build | Text | |
| get build | Text | |
| get build | Text | |
```mdx-code-block
<Divider />
```
## 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<Build>
```
```mdx-code-block
<Divider />
```
## get build
`GET /api/build/<build_id>`
### response body
```
Build
```
```mdx-code-block
<Divider />
```
## get build action state
`GET /api/build/<build_id>/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
<Divider />
```
## get build versions
`GET /api/build/<build_id>/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
<Divider />
```
## create build
`POST /api/build/create`
### request body
```json
{
name: string,
}
```
### response body
```
Build
```
```mdx-code-block
<Divider />
```
## create full build
`POST /api/build/create_full`
### request body
```
Build
```
### response body
```
Build
```
```mdx-code-block
<Divider />
```
## copy build
`POST /api/build/<build_id>/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
<Divider />
```
## delete build
`DELETE /api/build/<build_id>/delete`
### response body
```json
Build // the deleted build
```
```mdx-code-block
<Divider />
```
## update build
`PATCH /api/build/update`
### request body
```
Build
```
### response body
```
Build
```
```mdx-code-block
<Divider />
```
## build (action)
`POST /api/build/<build_id>/build`
### response body
```
Update
```
```mdx-code-block
<Divider />
```
## 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
<Divider />
```
## get allowed docker organizations
`GET /api/build/docker_organizations`
### response body
```json
string[] // the names of the allowed docker organizations
```

View File

@@ -0,0 +1,11 @@
---
slug: /api
---
this section documents the rest and websocket api
```mdx-code-block
import DocCardList from '@theme/DocCardList';
<DocCardList />
```

113
docsite/docs/api/login.mdx Normal file
View File

@@ -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
<Divider />
```
## 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
<Divider />
```
## 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
`<JWT token as string>`
:::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
<Divider />
```
## 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
`<JWT token as string>`
```mdx-code-block
<Divider />
```
## 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
`<JWT token as string>`

View File

View File

View File

@@ -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",

View File

@@ -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"],
},
],
};

View File

@@ -0,0 +1,15 @@
import React from "react";
export default function Divider() {
return (
<div
style={{
opacity: 0.7,
backgroundColor: "rgb(175, 175, 175)",
height: "3px",
width: "100%",
margin: "100px 0px"
}}
/>
);
}

View File

@@ -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<Build> {
pub async fn create_build(&self, name: &str) -> anyhow::Result<Build> {
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}"
))
}

View File

@@ -297,34 +297,34 @@ impl MonitorClient {
}
}
async fn _patch_string<B: Serialize>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<String> {
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<B: Serialize>(
// &self,
// endpoint: &str,
// body: impl Into<Option<B>>,
// ) -> anyhow::Result<String> {
// 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<B: Serialize, R: DeserializeOwned>(
&self,
@@ -355,34 +355,34 @@ impl MonitorClient {
}
}
async fn _delete_string<B: Serialize>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<String> {
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<B: Serialize>(
// &self,
// endpoint: &str,
// body: impl Into<Option<B>>,
// ) -> anyhow::Result<String> {
// 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 {

View File

@@ -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<Update> {
.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("");