@@ -5,20 +5,14 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: rust:bullseye
|
image: rust:latest
|
||||||
|
|
||||||
# Service containers to run with `container-job`
|
|
||||||
services:
|
services:
|
||||||
# Label used to access the service container
|
|
||||||
db:
|
db:
|
||||||
# Docker Hub image
|
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
# Provide the password for postgres
|
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: dev
|
POSTGRES_USER: dev
|
||||||
POSTGRES_PASSWORD: dev
|
POSTGRES_PASSWORD: dev
|
||||||
POSTGRES_DB: iot
|
POSTGRES_DB: iot
|
||||||
# Set health checks to wait until postgres has started
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
@@ -43,14 +37,22 @@ jobs:
|
|||||||
- name: Log in to Docker Registry
|
- name: Log in to Docker Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
registry: gitea.maiertobi.de # Replace with your Docker registry URL
|
registry: gitea.maiertobi.de
|
||||||
username: tobimai
|
username: tobimai
|
||||||
password: ${{ secrets.docker_registry_key }}
|
password: ${{ secrets.docker_registry_key }}
|
||||||
|
- name: Extract Version from Cargo.toml
|
||||||
|
id: cargo_version
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep '^version' Cargo.toml | head -n 1 | cut -d ' ' -f 3 | tr -d '"')
|
||||||
|
echo "VERSION=$VERSION" >> $GITEA_ENV
|
||||||
|
- name: Test
|
||||||
|
run: echo $VERSION
|
||||||
- name: Build and Push Docker Image
|
- name: Build and Push Docker Image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: gitea.maiertobi.de/tobimai/iot # Replace with your Docker image's details
|
tags: gitea.maiertobi.de/tobimai/iot:$VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: rust:latest
|
image: rust:latest
|
||||||
|
|
||||||
# Service containers to run with `container-job`
|
|
||||||
services:
|
services:
|
||||||
# Label used to access the service container
|
|
||||||
db:
|
db:
|
||||||
# Docker Hub image
|
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
# Provide the password for postgres
|
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: dev
|
POSTGRES_USER: dev
|
||||||
POSTGRES_PASSWORD: dev
|
POSTGRES_PASSWORD: dev
|
||||||
POSTGRES_DB: iot
|
POSTGRES_DB: iot
|
||||||
# Set health checks to wait until postgres has started
|
|
||||||
options: >-
|
options: >-
|
||||||
--health-cmd pg_isready
|
--health-cmd pg_isready
|
||||||
--health-interval 10s
|
--health-interval 10s
|
||||||
|
|||||||
@@ -17,3 +17,7 @@ sqlx-cli = "0.8"
|
|||||||
strum = { version = "0.26.3", features = ["derive"] }
|
strum = { version = "0.26.3", features = ["derive"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = { version = "1", features = ["fs", "rt-multi-thread"] }
|
tokio = { version = "1", features = ["fs", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
pedantic = "warn"
|
||||||
@@ -41,7 +41,7 @@ impl Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub async fn init_from_pool(pool: Pool<Postgres>) -> Database {
|
pub fn init_from_pool(pool: Pool<Postgres>) -> Database {
|
||||||
Database { conn_pool: pool }
|
Database { conn_pool: pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ impl Database {
|
|||||||
pub async fn init_db(&self) {
|
pub async fn init_db(&self) {
|
||||||
info!("Checking if required tables exist");
|
info!("Checking if required tables exist");
|
||||||
match migrate!().run(&self.conn_pool).await {
|
match migrate!().run(&self.conn_pool).await {
|
||||||
Ok(_) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error when running migrations {}", e);
|
error!("Error when running migrations {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@@ -142,7 +142,7 @@ impl Database {
|
|||||||
|
|
||||||
pub async fn add_value(
|
pub async fn add_value(
|
||||||
&self,
|
&self,
|
||||||
msg: &web::Json<ValueMessageFromDevice>,
|
msg: &ValueMessageFromDevice,
|
||||||
device_id: &MacAddress,
|
device_id: &MacAddress,
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<(), DatabaseError> {
|
||||||
info!("Adding value to DB");
|
info!("Adding value to DB");
|
||||||
@@ -201,8 +201,8 @@ mod tests {
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
async fn add_device(pool: PgPool) {
|
async fn add_device_and_display_name(pool: PgPool) {
|
||||||
let db = Database::init_from_pool(pool).await;
|
let db = Database::init_from_pool(pool);
|
||||||
|
|
||||||
let test_device = Device {
|
let test_device = Device {
|
||||||
display_name: Some("Waterlevel daheim".to_owned()),
|
display_name: Some("Waterlevel daheim".to_owned()),
|
||||||
@@ -211,7 +211,7 @@ mod tests {
|
|||||||
db.add_device(&MacAddress::from([0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F]))
|
db.add_device(&MacAddress::from([0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F]))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.add_display_name(
|
db.update_display_name(
|
||||||
&MacAddress::from([0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F]),
|
&MacAddress::from([0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F]),
|
||||||
"Waterlevel daheim",
|
"Waterlevel daheim",
|
||||||
)
|
)
|
||||||
@@ -221,4 +221,27 @@ mod tests {
|
|||||||
let devices = db.get_devices().await.unwrap();
|
let devices = db.get_devices().await.unwrap();
|
||||||
assert_eq!(test_device, devices[0]);
|
assert_eq!(test_device, devices[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn add_value(pool: PgPool) {
|
||||||
|
let db = Database::init_from_pool(pool);
|
||||||
|
let device_id = MacAddress::from([0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F]);
|
||||||
|
|
||||||
|
db.add_device(&device_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let msg = ValueMessageFromDevice{
|
||||||
|
active_errors: 0,
|
||||||
|
value: 112.0,
|
||||||
|
value_id: 1
|
||||||
|
};
|
||||||
|
db.add_value(&msg, &device_id).await.unwrap();
|
||||||
|
|
||||||
|
let values = db.get_values_for_id(&device_id).await.unwrap();
|
||||||
|
|
||||||
|
assert!((values[0].value - msg.value).abs() < 1e-5);
|
||||||
|
assert_eq!(values[0].value_id, msg.value_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ async fn receive_telemetry(
|
|||||||
};
|
};
|
||||||
let mac_converted = MacAddress::from(mac_converted);
|
let mac_converted = MacAddress::from(mac_converted);
|
||||||
match data.db.create_device_if_not_exists(&mac_converted).await {
|
match data.db.create_device_if_not_exists(&mac_converted).await {
|
||||||
Ok(_) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error creating new device: {}", e);
|
error!("Error creating new device: {}", e);
|
||||||
return HttpResponse::InternalServerError();
|
return HttpResponse::InternalServerError();
|
||||||
@@ -29,7 +29,7 @@ async fn receive_telemetry(
|
|||||||
.add_telemetry(&telemetry_message, &mac_converted)
|
.add_telemetry(&telemetry_message, &mac_converted)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => HttpResponse::Created(),
|
Ok(()) => HttpResponse::Created(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("adding Telemetry message to DB failed \n{}", e);
|
error!("adding Telemetry message to DB failed \n{}", e);
|
||||||
HttpResponse::InternalServerError()
|
HttpResponse::InternalServerError()
|
||||||
@@ -66,7 +66,7 @@ async fn receive_value(
|
|||||||
};
|
};
|
||||||
let mac_converted = MacAddress::from(mac_converted);
|
let mac_converted = MacAddress::from(mac_converted);
|
||||||
match data.db.create_device_if_not_exists(&mac_converted).await {
|
match data.db.create_device_if_not_exists(&mac_converted).await {
|
||||||
Ok(_) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error creating new device: {}", e);
|
error!("Error creating new device: {}", e);
|
||||||
return HttpResponse::InternalServerError();
|
return HttpResponse::InternalServerError();
|
||||||
@@ -74,7 +74,7 @@ async fn receive_value(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match data.db.add_value(&value_message, &mac_converted).await {
|
match data.db.add_value(&value_message, &mac_converted).await {
|
||||||
Ok(_) => HttpResponse::Created(),
|
Ok(()) => HttpResponse::Created(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("adding Telemetry message to DB failed \n{}", e);
|
error!("adding Telemetry message to DB failed \n{}", e);
|
||||||
HttpResponse::InternalServerError()
|
HttpResponse::InternalServerError()
|
||||||
@@ -122,13 +122,10 @@ async fn set_display_name(data: web::Data<AppState>, device_id: web::Path<String
|
|||||||
return HttpResponse::InternalServerError().finish();
|
return HttpResponse::InternalServerError().finish();
|
||||||
};
|
};
|
||||||
let mac_converted = MacAddress::from(mac_converted);
|
let mac_converted = MacAddress::from(mac_converted);
|
||||||
match db.update_display_name(&mac_converted, &display_name).await {
|
if let Ok(()) = db.update_display_name(&mac_converted, &display_name).await { HttpResponse::Ok().finish() } else {
|
||||||
Ok(_) => HttpResponse::Ok().finish(),
|
|
||||||
Err(_) => {
|
|
||||||
error!("Failed to update/set displayName for device");
|
error!("Failed to update/set displayName for device");
|
||||||
HttpResponse::InternalServerError().finish()
|
HttpResponse::InternalServerError().finish()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async fn upload_firmware(
|
|||||||
body: web::Bytes,
|
body: web::Bytes,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let (device, config, version) = path.into_inner();
|
let (device, config, version) = path.into_inner();
|
||||||
let version = version.replace(".", "-");
|
let version = version.replace('.', "-");
|
||||||
|
|
||||||
let firmware_root_path = &data.firmwares_path;
|
let firmware_root_path = &data.firmwares_path;
|
||||||
|
|
||||||
@@ -34,8 +34,7 @@ async fn upload_firmware(
|
|||||||
debug!("{x:?}");
|
debug!("{x:?}");
|
||||||
|
|
||||||
HttpResponse::Ok().body(format!(
|
HttpResponse::Ok().body(format!(
|
||||||
"Firmware version {} uploaded successfully",
|
"Firmware version {version} uploaded successfully"
|
||||||
version
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ async fn serve_firmware(
|
|||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let (product, config, version) = path.into_inner();
|
let (product, config, version) = path.into_inner();
|
||||||
let version = version.replace(".", "-").replace("_", "-");
|
let version = version.replace(['.', '_'], "-");
|
||||||
let fw_root_path = &data.firmwares_path;
|
let fw_root_path = &data.firmwares_path;
|
||||||
let file_path = PathBuf::from(format!("{product}/firmware_{config}_{version}.bin"));
|
let file_path = PathBuf::from(format!("{product}/firmware_{config}_{version}.bin"));
|
||||||
let file_path = fw_root_path.join(&file_path);
|
let file_path = fw_root_path.join(&file_path);
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ pub struct TelemetryMessageFromDevice {
|
|||||||
pub software_version: i32,
|
pub software_version: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Serialize)]
|
#[derive(Deserialize, Debug, Serialize, PartialEq)]
|
||||||
pub struct ValueMessageFromDevice {
|
pub struct ValueMessageFromDevice {
|
||||||
pub value: f64,
|
pub value: f64,
|
||||||
pub value_id: i32,
|
pub value_id: i32,
|
||||||
pub active_errors: i32,
|
pub active_errors: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Serialize)]
|
#[derive(Deserialize, Debug, Serialize, PartialEq)]
|
||||||
pub struct ValueMessage {
|
pub struct ValueMessage {
|
||||||
pub value: f64,
|
pub value: f64,
|
||||||
pub value_id: i32,
|
pub value_id: i32,
|
||||||
|
|||||||
@@ -63,15 +63,15 @@ pub fn get_files(
|
|||||||
.ok_or(GetFilesError::Filename)?
|
.ok_or(GetFilesError::Filename)?
|
||||||
.to_str()
|
.to_str()
|
||||||
.ok_or(GetFilesError::Filename)?
|
.ok_or(GetFilesError::Filename)?
|
||||||
.split("_")
|
.split('_')
|
||||||
.collect();
|
.collect();
|
||||||
let version = split_name[2]
|
let version = split_name[2]
|
||||||
.strip_suffix(".bin")
|
.strip_suffix(".bin")
|
||||||
.ok_or(GetFilesError::Extension)?
|
.ok_or(GetFilesError::Extension)?
|
||||||
.replace("-", ".");
|
.replace('-', ".");
|
||||||
let board_config = BoardConfig::from_str(split_name[1])?;
|
let board_config = BoardConfig::from_str(split_name[1])?;
|
||||||
let board_type = BoardType::from_str(&product_name).unwrap();
|
let board_type = BoardType::from_str(&product_name).unwrap();
|
||||||
let version_replaced = version.replace(".", "_");
|
let version_replaced = version.replace('.', "_");
|
||||||
let fw_url =
|
let fw_url =
|
||||||
format!("{hostname}/firmware/{board_type}/{board_config}/{version_replaced}.bin");
|
format!("{hostname}/firmware/{board_type}/{board_config}/{version_replaced}.bin");
|
||||||
let cfg = OTAConfiguration {
|
let cfg = OTAConfiguration {
|
||||||
@@ -82,7 +82,7 @@ pub fn get_files(
|
|||||||
};
|
};
|
||||||
configs.push(cfg);
|
configs.push(cfg);
|
||||||
} else if path.is_dir() {
|
} else if path.is_dir() {
|
||||||
println!("Directory: {:?}", path);
|
println!("Directory: {path:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user