use crate::api::header;
use crate::api::v1::util::auth;
use crate::api::v1::util::pagination::PaginateDsl;
use crate::config::Config;
use crate::db::Pool;
use crate::models::NewWorker;
use crate::schema::workers;
use crate::web;
use actix_web::{HttpRequest, HttpResponse, Responder, delete, get, post};
use chrono::Utc;
use diesel::{OptionalExtension, QueryDsl, RunQueryDsl, SqliteExpressionMethods};
use rebuilderd_common::api::WORKER_KEY_HEADER;
use rebuilderd_common::api::v1::{Page, RegisterWorkerRequest, ResultPage};
use rebuilderd_common::errors::{Context, Error, format_err};
use std::net::IpAddr;
#[diesel::dsl::auto_type]
fn workers_base() -> _ {
workers::table.select((
workers::id,
workers::name,
workers::address,
workers::status,
workers::last_ping,
workers::online,
))
}
#[get("")]
pub async fn get_workers(
pool: web::Data<Pool>,
page: web::Query<Page>,
) -> web::Result<impl Responder> {
let mut connection = pool.get().map_err(Error::from)?;
let records = workers_base()
.paginate(page.into_inner())
.load::<rebuilderd_common::api::v1::Worker>(connection.as_mut())
.map_err(Error::from)?;
let total = workers_base()
.count()
.get_result::<i64>(connection.as_mut())
.map_err(Error::from)?;
Ok(HttpResponse::Ok().json(ResultPage { total, records }))
}
#[post("")]
pub async fn register_worker(
req: HttpRequest,
cfg: web::Data<Config>,
pool: web::Data<Pool>,
request: web::Json<RegisterWorkerRequest>,
) -> web::Result<impl Responder> {
let mut connection = pool.get().map_err(Error::from)?;
if auth::signup(&cfg, &req).is_err() {
return Ok(HttpResponse::Forbidden().finish());
}
let key = header(&req, WORKER_KEY_HEADER).context("Failed to get worker key")?;
let ip = if let Some(real_ip_header) = &cfg.real_ip_header {
let ip = header(&req, real_ip_header).context("Failed to locate real ip header")?;
ip.parse::<IpAddr>()
.context("Can't parse real ip header as ip address")?
} else {
let ci = req
.peer_addr()
.ok_or_else(|| format_err!("Can't determine client ip"))?;
ci.ip()
};
let new_worker = NewWorker {
key: key.to_string(),
name: request.name.clone(),
address: ip.to_string(),
status: None,
last_ping: Utc::now().naive_utc(),
online: true,
};
new_worker.upsert(connection.as_mut())?;
Ok(HttpResponse::NoContent().finish())
}
#[get("/{id}")]
pub async fn get_worker(pool: web::Data<Pool>, id: web::Path<i32>) -> web::Result<impl Responder> {
let mut connection = pool.get().map_err(Error::from)?;
if let Some(record) = workers_base()
.filter(workers::id.is(id.into_inner()))
.get_result::<rebuilderd_common::api::v1::Worker>(connection.as_mut())
.optional()
.map_err(Error::from)?
{
Ok(HttpResponse::Ok().json(record))
} else {
Ok(HttpResponse::NotFound().finish())
}
}
#[delete("/{id}")]
pub async fn unregister_worker(
req: HttpRequest,
cfg: web::Data<Config>,
pool: web::Data<Pool>,
id: web::Path<i32>,
) -> web::Result<impl Responder> {
let mut connection = pool.get().map_err(Error::from)?;
if auth::worker(&cfg, &req, connection.as_mut()).is_err() {
return Ok(HttpResponse::Forbidden().finish());
}
let unregistered_count = diesel::delete(workers::table)
.filter(workers::id.is(id.into_inner()))
.execute(connection.as_mut())
.map_err(Error::from)?;
if unregistered_count < 1 {
Ok(HttpResponse::NotFound().finish())
} else {
Ok(HttpResponse::NoContent().finish())
}
}