feat: add remote, with rss, cache, discord, and zulip
This commit is contained in:
parent
50ec63a474
commit
83baab68e0
32 changed files with 6615 additions and 40 deletions
131
remote/cache-service/src/db.rs
Normal file
131
remote/cache-service/src/db.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use anyhow::{Context, Result};
|
||||
use rusqlite::Connection;
|
||||
|
||||
use crate::types::{DoorStatus, HistoryEntry, StatusResponse};
|
||||
|
||||
pub fn init(path: &str) -> Result<Connection> {
|
||||
let conn = Connection::open(path).context("failed to open SQLite database")?;
|
||||
conn.execute_batch(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS current_state (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
status TEXT,
|
||||
timestamp INTEGER,
|
||||
last_seen INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS state_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
status TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL,
|
||||
recorded_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pi_info (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
data TEXT NOT NULL,
|
||||
fetched_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
INSERT OR IGNORE INTO current_state (id, status, timestamp, last_seen) VALUES (1, NULL, NULL, NULL);
|
||||
INSERT OR IGNORE INTO pi_info (id, data, fetched_at) VALUES (1, '{}', 0);
|
||||
",
|
||||
)
|
||||
.context("failed to initialize database schema")?;
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub fn get_status(conn: &Connection) -> Result<StatusResponse> {
|
||||
let (status, timestamp, last_seen) = conn.query_row(
|
||||
"SELECT status, timestamp, last_seen FROM current_state WHERE id = 1",
|
||||
[],
|
||||
|row| {
|
||||
Ok((
|
||||
row.get::<_, Option<String>>(0)?,
|
||||
row.get::<_, Option<u64>>(1)?,
|
||||
row.get::<_, Option<u64>>(2)?,
|
||||
))
|
||||
},
|
||||
)?;
|
||||
Ok(StatusResponse {
|
||||
status: status.unwrap_or_else(|| "offline".to_string()),
|
||||
timestamp,
|
||||
last_seen,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_state(conn: &Connection, status: DoorStatus, timestamp: u64, now: u64) -> Result<()> {
|
||||
let status_str = status.as_str();
|
||||
conn.execute(
|
||||
"UPDATE current_state SET status = ?1, timestamp = ?2, last_seen = ?3 WHERE id = 1",
|
||||
rusqlite::params![status_str, timestamp, now],
|
||||
)?;
|
||||
conn.execute(
|
||||
"INSERT INTO state_log (status, timestamp, recorded_at) VALUES (?1, ?2, ?3)",
|
||||
rusqlite::params![status_str, timestamp, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_last_seen(conn: &Connection, now: u64) -> Result<()> {
|
||||
conn.execute(
|
||||
"UPDATE current_state SET last_seen = ?1 WHERE id = 1",
|
||||
rusqlite::params![now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_offline(conn: &Connection, now: u64) -> Result<()> {
|
||||
conn.execute(
|
||||
"UPDATE current_state SET status = NULL WHERE id = 1",
|
||||
[],
|
||||
)?;
|
||||
conn.execute(
|
||||
"INSERT INTO state_log (status, timestamp, recorded_at) VALUES ('offline', ?1, ?1)",
|
||||
rusqlite::params![now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_current_status_str(conn: &Connection) -> Result<Option<String>> {
|
||||
let status = conn.query_row(
|
||||
"SELECT status FROM current_state WHERE id = 1",
|
||||
[],
|
||||
|row| row.get::<_, Option<String>>(0),
|
||||
)?;
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
pub fn get_history(conn: &Connection, limit: u32) -> Result<Vec<HistoryEntry>> {
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT status, timestamp, recorded_at FROM state_log ORDER BY id DESC LIMIT ?1",
|
||||
)?;
|
||||
let entries = stmt
|
||||
.query_map(rusqlite::params![limit], |row| {
|
||||
Ok(HistoryEntry {
|
||||
status: row.get(0)?,
|
||||
timestamp: row.get(1)?,
|
||||
recorded_at: row.get(2)?,
|
||||
})
|
||||
})?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
pub fn get_pi_info(conn: &Connection) -> Result<serde_json::Value> {
|
||||
let data: String = conn.query_row(
|
||||
"SELECT data FROM pi_info WHERE id = 1",
|
||||
[],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
Ok(serde_json::from_str(&data).unwrap_or(serde_json::json!({})))
|
||||
}
|
||||
|
||||
pub fn update_pi_info(conn: &Connection, data: &serde_json::Value, now: u64) -> Result<()> {
|
||||
let json = serde_json::to_string(data)?;
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO pi_info (id, data, fetched_at) VALUES (1, ?1, ?2)",
|
||||
rusqlite::params![json, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue