Compare commits

...

10 Commits

Author SHA1 Message Date
45755c498c add content-type to JSON response 2025-03-17 22:10:43 +01:00
452c292570 adjust Traefik header for legacy Traefik pre-v2
Our modern infrastructure is not yet ready for this, apparently.
2025-03-17 21:52:57 +01:00
64ccd0a51b add CORS 2025-03-05 23:17:15 +01:00
2b03c60ef2 scripts/open_aerie: fix comment; wrong creds should get 403 2025-01-20 21:56:00 +01:00
c962a53bbe simplify code 2025-01-20 21:55:31 +01:00
fab52f7f80 panic on start if env vars are not set 2025-01-20 21:55:22 +01:00
6cab009b3a test script failes on purpose 2024-06-29 00:45:21 +02:00
41c111569d add build / deploy instructions to readme 2024-06-29 00:41:16 +02:00
baf9d87556 build docker container with git clone to enable automation 2024-06-29 00:14:38 +02:00
b4938c904e check if payload secrets match env variable secrets 2024-06-28 23:03:58 +02:00
6 changed files with 95 additions and 13 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target /target
.idea

View File

@ -1,7 +1,9 @@
FROM rust:1.75.0 as builder FROM rust:1.75.0 as builder
WORKDIR /usr/src/chaospott-status WORKDIR /usr/src/chaospott-status
COPY . . # git clone anstatt copy, damit Dockerfile in extra repository liegen kann. Danke @a3x
# git ist im image enthalten, da rust image hiervon abstammt https://hub.docker.com/layers/library/buildpack-deps/bookworm-scm/images/sha256-25f20fd3e3c8be1e9626c246986beb400ccfe19b0ab13d57127399927801d499?context=explore
RUN git clone https://git.chaospott.de/Chaospott/chaospott-status.git .
# use musl to create a truly static binary https://bxbrenden.github.io/ # use musl to create a truly static binary https://bxbrenden.github.io/
RUN rustup component add rust-std-x86_64-unknown-linux-musl RUN rustup component add rust-std-x86_64-unknown-linux-musl

View File

@ -16,6 +16,19 @@ To start the app, just `cargo run --release` as usual.
Find scripts for testing in [`scripts/`](scripts/). Find scripts for testing in [`scripts/`](scripts/).
## Build / Deploy
While building the Docker Container, the sources will be cloned from this repository.
Set the environment variables to set the update secrets.
```shell
export consumer_key=foo
export consumer_secret=bar
docker compose up
```
## Need help? ## Need help?
Ask chfkch, starblue, m0veax, CyReVolt or your favourite Rustacean. 🦀 Ask chfkch, starblue, m0veax, CyReVolt or your favourite Rustacean. 🦀

24
docker-compose.yaml Normal file
View File

@ -0,0 +1,24 @@
version: '3'
services:
spaceapi-v2:
build: .
container_name: spaceapi-v2
restart: always
labels:
- traefik.frontend.rule=Host:status-v2.chaospott.de
- traefik.port=3000
- traefik.frontend.passHostHeader=true
- traefik.enable=true
- traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin:*
# NOTE: This is for Traefik v2, apparently.
- traefik.http.middlewares.cors.headers.accesscontrolallowmethods=GET,OPTIONS,PUT
- traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://chaospott.de
- traefik.http.middlewares.cors.headers.accesscontrolmaxage=100
- traefik.http.middlewares.cors.headers.addvaryheader=true
networks:
- extern
networks:
extern:
external:
name: web

View File

@ -1,4 +1,16 @@
#!/bin/sh
## starte server mit env vars passend zum ersten aufruf
## TODO das muss noch gescripted werden
# should return 201 if env vars are set like this payload states
curl -XPOST \ curl -XPOST \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
--data '{"consumer_key": "","consumer_secret":"","aerie":true }' \ --data '{"consumer_key": "test123","consumer_secret":"123test","aerie":true }' \
http://localhost:3000/api/update http://localhost:3000/api/update -vvv
#should return 403
curl -XPOST \
-H "Content-Type: application/json" \
--data '{"consumer_key": "foo","consumer_secret":"bar","aerie":true }' \
http://localhost:3000/api/update -vvv

View File

@ -1,8 +1,11 @@
use std::fs::File; use std::env;
use std::fs::{read_to_string, File};
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path;
use axum::{ use axum::{
http::StatusCode, body::Body,
http::{Response, StatusCode},
routing::{get, post}, routing::{get, post},
Json, Router, Json, Router,
}; };
@ -16,6 +19,13 @@ const STATUS_FILE: &str = "status.json";
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
if env::var("consumer_key").is_err() {
panic!("env var consumer_key must be set");
}
if env::var("consumer_secret").is_err() {
panic!("env var consumer_secret must be set");
}
let app = Router::new() let app = Router::new()
.route("/status.json", get(root)) .route("/status.json", get(root))
.route("/api/update", post(the_doors)); .route("/api/update", post(the_doors));
@ -36,6 +46,15 @@ fn init_status() -> Status {
status::status(sensors, state) status::status(sensors, state)
} }
// check given secret
// https://www.youtube.com/watch?v=aHKWVLH-ibY
fn auth(p: &TheDoors) -> bool {
let consumer_secret = env::var("consumer_secret").unwrap();
let consumer_key = env::var("consumer_key").unwrap();
p.consumer_secret == consumer_secret && p.consumer_key == consumer_key
}
// Write status to file and return JSON string. // Write status to file and return JSON string.
fn write_status(s: Status) -> String { fn write_status(s: Status) -> String {
let s = serde_json::to_string(&s).unwrap(); let s = serde_json::to_string(&s).unwrap();
@ -46,12 +65,17 @@ fn write_status(s: Status) -> String {
// Just output the current file. We assume it to be consistent. // Just output the current file. We assume it to be consistent.
// It may cease to or not yet exist. Then create an initial status and persist. // It may cease to or not yet exist. Then create an initial status and persist.
async fn root() -> String { async fn root() -> Response<Body> {
if std::path::Path::new(STATUS_FILE).exists() { let res = if std::path::Path::new(STATUS_FILE).exists() {
return std::fs::read_to_string(STATUS_FILE).unwrap_or(String::from("KAPOTT")); read_to_string(STATUS_FILE).unwrap_or(String::from("KAPOTT"))
} } else {
let s = init_status(); write_status(init_status())
write_status(s) };
Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(Body::from(res))
.unwrap()
} }
// Input type for the API: Both fields are optional. // Input type for the API: Both fields are optional.
@ -59,13 +83,19 @@ async fn root() -> String {
struct TheDoors { struct TheDoors {
aerie: Option<bool>, aerie: Option<bool>,
cellar: Option<bool>, cellar: Option<bool>,
consumer_key: String,
consumer_secret: String,
} }
// The door can see through your soul. // The door can see through your soul.
// https://www.youtube.com/watch?v=bDQDp00oTP4 // https://www.youtube.com/watch?v=bDQDp00oTP4
async fn the_doors(Json(payload): Json<TheDoors>) -> StatusCode { async fn the_doors(Json(payload): Json<TheDoors>) -> StatusCode {
let status: Status = if std::path::Path::new(STATUS_FILE).exists() { if !auth(&payload) {
let contents = std::fs::read_to_string(STATUS_FILE).expect("FCKAFD"); return StatusCode::FORBIDDEN;
}
let status: Status = if Path::new(STATUS_FILE).exists() {
let contents = read_to_string(STATUS_FILE).expect("FCKAFD");
serde_json::from_str(&contents).unwrap_or_else(|_| init_status()) serde_json::from_str(&contents).unwrap_or_else(|_| init_status())
} else { } else {
init_status() init_status()