summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGalen Guyer <galen@galenguyer.com>2022-05-22 01:37:57 -0400
committerGalen Guyer <galen@galenguyer.com>2022-05-22 01:37:57 -0400
commit2145e15274776bcdadd86d9f011251fbe7cc85ff (patch)
tree958fcb2bc2a801de13a92ba216b044b3a219e0ad
parent3613656a396b42d70259e9ff325ab9fcf0b55dd1 (diff)
api kinda works to add records and stuff
-rw-r--r--migrations/2022-04-09-create-schema.sql3
-rw-r--r--src/db/mod.rs1
-rw-r--r--src/db/models.rs13
-rw-r--r--src/db/records.rs33
-rw-r--r--src/db/strings.rs7
-rw-r--r--src/extractors.rs2
-rw-r--r--src/main.rs8
-rw-r--r--src/routes/v1/mod.rs1
-rw-r--r--src/routes/v1/records.rs81
-rw-r--r--src/routes/v1/repsonse.rs2
-rw-r--r--src/routes/v1/requests.rs9
-rw-r--r--src/routes/v1/zones.rs40
12 files changed, 159 insertions, 41 deletions
diff --git a/migrations/2022-04-09-create-schema.sql b/migrations/2022-04-09-create-schema.sql
index 786710d..89bfa3f 100644
--- a/migrations/2022-04-09-create-schema.sql
+++ b/migrations/2022-04-09-create-schema.sql
@@ -22,8 +22,9 @@ CREATE TABLE IF NOT EXISTS zones (
);
CREATE TABLE IF NOT EXISTS records (
- id SERIAL PRIMARY KEY,
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
zone_id varchar(255) NOT NULL,
+ name varchar(255) NOT NULL,
type varchar(16) NOT NULL,
content TEXT NOT NULL,
ttl INTEGER NOT NULL,
diff --git a/src/db/mod.rs b/src/db/mod.rs
index fc37f5e..1cc94cd 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1,3 +1,4 @@
+pub mod records;
pub mod users;
pub mod zones;
diff --git a/src/db/models.rs b/src/db/models.rs
index 6af7e15..d9591d8 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -29,11 +29,14 @@ pub struct Zone {
#[derive(Serialize, Deserialize, FromRow, Debug)]
pub struct Record {
- pub id: u64,
+ pub id: Uuid,
pub zone_id: String,
- pub r#type: String,
+ pub name: String,
+ #[serde(rename = "type")]
+ #[sqlx(rename = "type")]
+ pub record_type: String,
pub content: String,
- pub ttl: u32,
+ pub ttl: i32,
pub created_at: DateTime<Utc>,
- pub modified_at: DateTime<Utc>,
-} \ No newline at end of file
+ pub modified_at: DateTime<Utc>,
+}
diff --git a/src/db/records.rs b/src/db/records.rs
new file mode 100644
index 0000000..d92e489
--- /dev/null
+++ b/src/db/records.rs
@@ -0,0 +1,33 @@
+use crate::db::models::Record;
+use crate::db::strings;
+use sqlx::types::Uuid;
+use sqlx::{Pool, Postgres};
+
+pub async fn create_record(
+ pool: &Pool<Postgres>,
+ zone_id: &str,
+ name: &str,
+ record_type: &str,
+ content: &str,
+ ttl: u32,
+) -> Result<Record, sqlx::Error> {
+ let mut transaction = pool.begin().await?;
+ let zone = sqlx::query_as::<_, Record>(&strings::CREATE_RECORD)
+ .bind(zone_id)
+ .bind(name)
+ .bind(record_type)
+ .bind(content)
+ .bind(ttl)
+ .fetch_one(&mut transaction)
+ .await?;
+ transaction.commit().await?;
+ Ok(zone)
+}
+
+pub async fn get_records(pool: &Pool<Postgres>, zone_id: &str) -> Result<Vec<Record>, sqlx::Error> {
+ let records = sqlx::query_as::<_, Record>(&strings::GET_RECORDS)
+ .bind(zone_id)
+ .fetch_all(pool)
+ .await?;
+ Ok(records)
+}
diff --git a/src/db/strings.rs b/src/db/strings.rs
index ebaa8c7..aa0249a 100644
--- a/src/db/strings.rs
+++ b/src/db/strings.rs
@@ -24,4 +24,11 @@ lazy_static! {
SELECT id,owner_uuid,created_at,modified_at
FROM zones WHERE id = $1
";
+ pub(crate) static ref CREATE_RECORD: &'static str = r"
+ INSERT INTO records(zone_id,name,type,content,ttl) VALUES ($1, $2, $3, $4, $5) RETURNING *
+ ";
+ pub(crate) static ref GET_RECORDS: &'static str = r"
+ SELECT id,zone_id,name,type,content,ttl,created_at,modified_at
+ FROM records WHERE zone_id = $1
+ ";
}
diff --git a/src/extractors.rs b/src/extractors.rs
index 30e8235..dd294b6 100644
--- a/src/extractors.rs
+++ b/src/extractors.rs
@@ -70,7 +70,7 @@ where
let token = Token {
iss: claims.get("iss").unwrap().to_string(),
- sub: Uuid::parse_str(&claims.get("sub").unwrap().to_string()).unwrap(),
+ sub: Uuid::parse_str(claims.get("sub").unwrap()).unwrap(),
iat: claims.get("iat").unwrap().parse().unwrap(),
exp: claims.get("exp").unwrap().parse().unwrap(),
dn: claims.get("dn").unwrap().to_string(),
diff --git a/src/main.rs b/src/main.rs
index d8d88a6..01a70b9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -60,8 +60,12 @@ async fn main() {
"/zones",
Router::new()
.route("/", get(routes::v1::zones::list_zones))
- .route("/:id", post(routes::v1::zones::create_zone))
- .route("/:id", get(routes::v1::zones::get_zone)),
+ .route(
+ "/:zone_id",
+ get(routes::v1::records::get_records)
+ .post(routes::v1::zones::create_zone)
+ .put(routes::v1::records::create_record),
+ ),
),
),
)
diff --git a/src/routes/v1/mod.rs b/src/routes/v1/mod.rs
index 9e3453c..f561d02 100644
--- a/src/routes/v1/mod.rs
+++ b/src/routes/v1/mod.rs
@@ -1,3 +1,4 @@
+pub mod records;
pub mod users;
pub mod zones;
diff --git a/src/routes/v1/records.rs b/src/routes/v1/records.rs
new file mode 100644
index 0000000..6c71749
--- /dev/null
+++ b/src/routes/v1/records.rs
@@ -0,0 +1,81 @@
+use crate::db;
+use crate::extractors::{Json, Jwt};
+use crate::routes::v1::{requests, zones};
+use axum::extract::Path;
+use axum::http::StatusCode;
+use axum::response::IntoResponse;
+use axum::Extension;
+use serde_json::json;
+use sqlx::{Pool, Postgres};
+use std::sync::Arc;
+
+pub async fn get_records(
+ Path(id): Path<String>,
+ Jwt(user): Jwt,
+ Extension(pool): Extension<Arc<Pool<Postgres>>>,
+) -> impl IntoResponse {
+ let domain = zones::ensure_trailing_dot(&id);
+
+ let zone = db::zones::get_zone(&pool, &domain).await;
+ if zone.is_err() {
+ return (
+ StatusCode::NOT_FOUND,
+ Json(json!({ "error": format!("Zone {domain} not found") })),
+ );
+ }
+ let zone = zone.unwrap();
+
+ if zone.owner_uuid != user.sub {
+ return (
+ StatusCode::FORBIDDEN,
+ Json(json!({ "error": "You do have permissions to access this zone" })),
+ );
+ }
+
+ let records = db::records::get_records(&pool, &zone.id).await.unwrap();
+
+ (StatusCode::OK, Json(json!(records)))
+}
+
+pub async fn create_record(
+ Path(zone_id): Path<String>,
+ Jwt(user): Jwt,
+ Json(data): Json<requests::Record>,
+ Extension(pool): Extension<Arc<Pool<Postgres>>>,
+) -> impl IntoResponse {
+ let zone = db::zones::get_zone(&pool, &zone_id).await;
+
+ if zone.is_err() {
+ return (
+ StatusCode::NOT_FOUND,
+ Json(json!({"error": "Zone not found"})),
+ );
+ }
+ let zone = zone.unwrap();
+
+ if zone.owner_uuid != user.sub {
+ return (
+ StatusCode::FORBIDDEN,
+ Json(json!({"error": "You do not have permissions to access this zone"})),
+ );
+ }
+
+ let record = db::records::create_record(
+ &pool,
+ &zone.id,
+ &data.name,
+ &data.record_type,
+ &data.content,
+ data.ttl,
+ )
+ .await;
+ if record.is_err() {
+ return (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(json!({"error": record.unwrap_err().to_string()})),
+ );
+ }
+ let record = record.unwrap();
+
+ (StatusCode::OK, Json(json!(record)))
+}
diff --git a/src/routes/v1/repsonse.rs b/src/routes/v1/repsonse.rs
index 73bd35d..cb35a41 100644
--- a/src/routes/v1/repsonse.rs
+++ b/src/routes/v1/repsonse.rs
@@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct ErrorMessage {
pub error: String,
- pub reason: String,
+ pub reason: Option<String>,
}
diff --git a/src/routes/v1/requests.rs b/src/routes/v1/requests.rs
index 4677928..aba7c00 100644
--- a/src/routes/v1/requests.rs
+++ b/src/routes/v1/requests.rs
@@ -13,3 +13,12 @@ pub struct Login {
pub password: String,
pub totp_code: Option<String>,
}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Record {
+ pub name: String,
+ #[serde(rename = "type")]
+ pub record_type: String,
+ pub content: String,
+ pub ttl: u32,
+}
diff --git a/src/routes/v1/zones.rs b/src/routes/v1/zones.rs
index 9867162..2bff4d8 100644
--- a/src/routes/v1/zones.rs
+++ b/src/routes/v1/zones.rs
@@ -11,8 +11,12 @@ use sqlx::{Error, Pool, Postgres};
use std::sync::Arc;
lazy_static! {
- static ref NAMESERVERS: Vec<String> =
- Vec::from([String::from("ns1.fdns.dev."), String::from("ns2.fdns.dev."), String::from("ns3.fdns.dev."), String::from("ns4.fdns.dev.")]);
+ static ref NAMESERVERS: Vec<String> = Vec::from([
+ String::from("ns1.fdns.dev."),
+ String::from("ns2.fdns.dev."),
+ String::from("ns3.fdns.dev."),
+ String::from("ns4.fdns.dev.")
+ ]);
}
pub async fn list_zones(
@@ -30,11 +34,11 @@ pub async fn list_zones(
}
pub async fn create_zone(
- Path(id): Path<String>,
+ Path(zone_id): Path<String>,
Jwt(user): Jwt,
Extension(pool): Extension<Arc<Pool<Postgres>>>,
) -> impl IntoResponse {
- let domain = ensure_trailing_dot(&id);
+ let domain = ensure_trailing_dot(&zone_id);
let domain = addr::parse_domain_name(&domain).unwrap();
if !domain.has_known_suffix() {
return (
@@ -72,33 +76,7 @@ pub async fn create_zone(
}
}
-pub async fn get_zone(
- Path(id): Path<String>,
- Jwt(user): Jwt,
- Extension(pool): Extension<Arc<Pool<Postgres>>>,
-) -> impl IntoResponse {
- let domain = ensure_trailing_dot(&id);
-
- let zone = db::zones::get_zone(&pool, &domain).await;
- if zone.is_err() {
- return (
- StatusCode::NOT_FOUND,
- Json(json!({ "error": format!("Zone {domain} not found") })),
- );
- }
- let zone = zone.unwrap();
-
- if zone.owner_uuid != user.sub {
- return (
- StatusCode::FORBIDDEN,
- Json(json!({ "error": "You do have permissions to view this zone" })),
- );
- }
-
- (StatusCode::OK, Json(json!(zone)))
-}
-
-fn ensure_trailing_dot(domain: &str) -> String {
+pub(crate) fn ensure_trailing_dot(domain: &str) -> String {
if domain.ends_with('.') {
return domain.to_string();
}