-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add a REST API rust backend based on Axum and OpenAPI
Use Axum to serve a REST API based on the statistics collected in the database. Use http-tower::ServeDir to serve static assets (the future web app) in the debug profile. Use rust_embed to serve the assets in release profile. Use Aide to generate an OpenAPI schema based on the Axum routes. This schema will then be used in the web app to parse and validate the REST API types. This allows declaring types in Rust, and then using them in typescript using the OpenAPI schema as the in-between converter.
- Loading branch information
Showing
25 changed files
with
2,084 additions
and
56 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
{ | ||
"openapi": "3.1.0", | ||
"info": { | ||
"title": "ddns Open API", | ||
"summary": "ddns Open API", | ||
"description": "ddns Open API", | ||
"version": "" | ||
}, | ||
"paths": { | ||
"/api/v1/domain_record_ip_changes": { | ||
"get": { | ||
"description": "List all recent domain record ip changes", | ||
"responses": { | ||
"default": { | ||
"description": "", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"oneOf": [ | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"GenericError" | ||
], | ||
"properties": { | ||
"GenericError": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
"example": { | ||
"GenericError": "generic error" | ||
} | ||
} | ||
} | ||
}, | ||
"200": { | ||
"description": "", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"$ref": "#/components/schemas/DomainRecordIpChanges" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"/docs/": { | ||
"get": { | ||
"description": "This documentation page.", | ||
"responses": { | ||
"default": { | ||
"description": "", | ||
"content": { | ||
"application/json": { | ||
"schema": { | ||
"oneOf": [ | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"GenericError" | ||
], | ||
"properties": { | ||
"GenericError": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
"example": { | ||
"GenericError": "generic error" | ||
} | ||
} | ||
} | ||
}, | ||
"200": { | ||
"description": "HTML content", | ||
"content": { | ||
"text/html": { | ||
"schema": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"components": { | ||
"schemas": { | ||
"DomainRecordIpChange": { | ||
"type": "object", | ||
"required": [ | ||
"attempt_date", | ||
"domain_record_id", | ||
"id", | ||
"name", | ||
"set_ip", | ||
"success" | ||
], | ||
"properties": { | ||
"attempt_date": { | ||
"type": "string", | ||
"format": "partial-date-time" | ||
}, | ||
"domain_record_id": { | ||
"type": "integer", | ||
"format": "int64" | ||
}, | ||
"id": { | ||
"type": "integer", | ||
"format": "int64" | ||
}, | ||
"name": { | ||
"type": "string" | ||
}, | ||
"set_ip": { | ||
"type": "string" | ||
}, | ||
"success": { | ||
"type": "boolean" | ||
} | ||
} | ||
}, | ||
"DomainRecordIpChanges": { | ||
"type": "object", | ||
"required": [ | ||
"changes" | ||
], | ||
"properties": { | ||
"changes": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/components/schemas/DomainRecordIpChange" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use crate::db::types::*; | ||
use color_eyre::eyre::Result; | ||
use diesel::prelude::*; | ||
use diesel::sqlite::SqliteConnection; | ||
|
||
use diesel::sql_types::Text; | ||
use schemars::JsonSchema; | ||
use serde::Serialize; | ||
|
||
#[derive(Queryable, Debug, Serialize, QueryableByName, JsonSchema)] | ||
pub struct DomainRecordIpChange { | ||
#[diesel(embed)] | ||
#[serde(flatten)] | ||
pub domain_record_update: DomainRecordUpdate, | ||
#[diesel(sql_type = Text)] | ||
pub name: String, | ||
} | ||
|
||
#[derive(Serialize, JsonSchema)] | ||
pub struct DomainRecordIpChanges { | ||
pub changes: Vec<DomainRecordIpChange>, | ||
} | ||
|
||
pub fn get_domain_record_ip_changes(conn: &mut SqliteConnection) -> Result<DomainRecordIpChanges> { | ||
// For each domain row, get previous ip and only return results | ||
// where the ip has changed. | ||
let changes = diesel::sql_query( | ||
" | ||
SELECT t.* FROM | ||
(SELECT u.*, r.*, | ||
lag(u.set_ip) over ( | ||
partition by domain_record_id | ||
order by u.attempt_date ASC | ||
) as prev_set_ip | ||
FROM domain_record_updates u | ||
INNER JOIN domain_records r | ||
ON u.domain_record_id=r.id | ||
WHERE u.success = true | ||
ORDER BY u.attempt_date DESC | ||
) t | ||
WHERE prev_set_ip IS NULL OR prev_set_ip != set_ip", | ||
) | ||
.load::<DomainRecordIpChange>(conn)?; | ||
let results = DomainRecordIpChanges { changes }; | ||
Ok(results) | ||
} |
Oops, something went wrong.