rebuilderd/api/v1/
worker.rs1use crate::api::header;
2use crate::api::v1::util::auth;
3use crate::api::v1::util::pagination::PaginateDsl;
4use crate::config::Config;
5use crate::db::Pool;
6use crate::models::NewWorker;
7use crate::schema::workers;
8use crate::web;
9use actix_web::{HttpRequest, HttpResponse, Responder, delete, get, post};
10use chrono::Utc;
11use diesel::{OptionalExtension, QueryDsl, RunQueryDsl, SqliteExpressionMethods};
12use rebuilderd_common::api::WORKER_KEY_HEADER;
13use rebuilderd_common::api::v1::{Page, RegisterWorkerRequest, ResultPage};
14use rebuilderd_common::errors::{Context, Error, format_err};
15use std::net::IpAddr;
16
17#[diesel::dsl::auto_type]
18fn workers_base() -> _ {
19 workers::table.select((
20 workers::id,
21 workers::name,
22 workers::address,
23 workers::status,
24 workers::last_ping,
25 workers::online,
26 ))
27}
28
29#[get("")]
30pub async fn get_workers(
31 pool: web::Data<Pool>,
32 page: web::Query<Page>,
33) -> web::Result<impl Responder> {
34 let mut connection = pool.get().map_err(Error::from)?;
35
36 let records = workers_base()
37 .paginate(page.into_inner())
38 .load::<rebuilderd_common::api::v1::Worker>(connection.as_mut())
39 .map_err(Error::from)?;
40
41 let total = workers_base()
42 .count()
43 .get_result::<i64>(connection.as_mut())
44 .map_err(Error::from)?;
45
46 Ok(HttpResponse::Ok().json(ResultPage { total, records }))
47}
48
49#[post("")]
50pub async fn register_worker(
51 req: HttpRequest,
52 cfg: web::Data<Config>,
53 pool: web::Data<Pool>,
54 request: web::Json<RegisterWorkerRequest>,
55) -> web::Result<impl Responder> {
56 let mut connection = pool.get().map_err(Error::from)?;
57 if auth::signup(&cfg, &req).is_err() {
58 return Ok(HttpResponse::Forbidden().finish());
59 }
60
61 let key = header(&req, WORKER_KEY_HEADER).context("Failed to get worker key")?;
62 let ip = if let Some(real_ip_header) = &cfg.real_ip_header {
63 let ip = header(&req, real_ip_header).context("Failed to locate real ip header")?;
64 ip.parse::<IpAddr>()
65 .context("Can't parse real ip header as ip address")?
66 } else {
67 let ci = req
68 .peer_addr()
69 .ok_or_else(|| format_err!("Can't determine client ip"))?;
70 ci.ip()
71 };
72
73 let new_worker = NewWorker {
74 key: key.to_string(),
75 name: request.name.clone(),
76 address: ip.to_string(),
77 status: None,
78 last_ping: Utc::now().naive_utc(),
79 online: true,
80 };
81
82 new_worker.upsert(connection.as_mut())?;
83
84 Ok(HttpResponse::NoContent().finish())
85}
86
87#[get("/{id}")]
88pub async fn get_worker(pool: web::Data<Pool>, id: web::Path<i32>) -> web::Result<impl Responder> {
89 let mut connection = pool.get().map_err(Error::from)?;
90
91 if let Some(record) = workers_base()
92 .filter(workers::id.is(id.into_inner()))
93 .get_result::<rebuilderd_common::api::v1::Worker>(connection.as_mut())
94 .optional()
95 .map_err(Error::from)?
96 {
97 Ok(HttpResponse::Ok().json(record))
98 } else {
99 Ok(HttpResponse::NotFound().finish())
100 }
101}
102
103#[delete("/{id}")]
104pub async fn unregister_worker(
105 req: HttpRequest,
106 cfg: web::Data<Config>,
107 pool: web::Data<Pool>,
108 id: web::Path<i32>,
109) -> web::Result<impl Responder> {
110 let mut connection = pool.get().map_err(Error::from)?;
111 if auth::worker(&cfg, &req, connection.as_mut()).is_err() {
112 return Ok(HttpResponse::Forbidden().finish());
113 }
114
115 let unregistered_count = diesel::delete(workers::table)
116 .filter(workers::id.is(id.into_inner()))
117 .execute(connection.as_mut())
118 .map_err(Error::from)?;
119
120 if unregistered_count < 1 {
121 Ok(HttpResponse::NotFound().finish())
122 } else {
123 Ok(HttpResponse::NoContent().finish())
124 }
125}