use std::{fs, path::Path};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct MaintenanceState {
pub secret: String,
pub message: Option<String>,
pub retry_at: Option<String>,
}
const STATE_PATH: &str = "storage/framework/down.json";
pub fn activate(secret: Option<String>, message: Option<String>, retry_after: Option<u64>) -> anyhow::Result<()> {
if Path::new(STATE_PATH).exists() {
anyhow::bail!("Application is already in maintenance mode. Run `rok up` first.");
}
let secret = secret.unwrap_or_else(|| generate_secret());
let retry_at = retry_after.map(|secs| {
let now = chrono::Utc::now() + chrono::Duration::seconds(secs as i64);
now.to_rfc3339()
});
let state = MaintenanceState {
secret,
message,
retry_at,
};
fs::create_dir_all("storage/framework")?;
let json = serde_json::to_string_pretty(&state)?;
fs::write(STATE_PATH, &json)?;
println!("Application is now in maintenance mode.");
println!("Secret bypass token: {}", state.secret);
if let Some(msg) = &state.message {
println!("Message: {msg}");
}
if let Some(retry) = &state.retry_at {
println!("Retry after: {retry}");
}
Ok(())
}
pub fn deactivate() -> anyhow::Result<()> {
if !Path::new(STATE_PATH).exists() {
anyhow::bail!("Application is not in maintenance mode.");
}
fs::remove_file(STATE_PATH)?;
println!("Maintenance mode disabled.");
Ok(())
}
pub fn status() -> anyhow::Result<Option<MaintenanceState>> {
if !Path::new(STATE_PATH).exists() {
println!("Application is not in maintenance mode.");
return Ok(None);
}
let json = fs::read_to_string(STATE_PATH)?;
let state: MaintenanceState = serde_json::from_str(&json)?;
println!("Application is in maintenance mode.");
println!(" Secret: {}", state.secret);
if let Some(msg) = &state.message {
println!(" Message: {msg}");
}
if let Some(retry) = &state.retry_at {
println!(" Retry after: {retry}");
}
Ok(Some(state))
}
fn generate_secret() -> String {
use rand::Rng;
const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
let mut rng = rand::thread_rng();
(0..32)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
}