headless_lms_server/controllers/main_frontend/
time.rs

1/*!
2Handlers for HTTP requests to `/api/v0/main-frontend/time`.
3*/
4
5use crate::prelude::*;
6use chrono::{SecondsFormat, Utc};
7
8/**
9GET `/api/v0/main-frontend/time` Returns the server's current UTC time as an RFC3339 timestamp string.
10
11Response body example:
12`"2026-02-18T12:34:56.789Z"`
13*/
14pub async fn get_current_time() -> ControllerResult<HttpResponse> {
15    let server_time = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
16    let token = skip_authorize();
17
18    token.authorized_ok(
19        HttpResponse::Ok()
20            .insert_header((
21                "Cache-Control",
22                "no-store, no-cache, must-revalidate, max-age=0",
23            ))
24            .insert_header(("Pragma", "no-cache"))
25            .insert_header(("Expires", "0"))
26            .json(server_time),
27    )
28}
29
30pub fn _add_routes(cfg: &mut ServiceConfig) {
31    cfg.route("", web::get().to(get_current_time));
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use actix_web::{App, test, web};
38
39    #[actix_web::test]
40    async fn returns_non_cacheable_rfc3339_json_string() {
41        let app = test::init_service(App::new().service(web::scope("/api/v0").service(
42            web::scope("/main-frontend").service(web::scope("/time").configure(_add_routes)),
43        )))
44        .await;
45
46        let req = test::TestRequest::with_uri("/api/v0/main-frontend/time").to_request();
47        let resp = test::call_service(&app, req).await;
48
49        assert!(resp.status().is_success());
50        assert_eq!(
51            resp.headers().get("Cache-Control").unwrap(),
52            "no-store, no-cache, must-revalidate, max-age=0"
53        );
54        assert_eq!(resp.headers().get("Pragma").unwrap(), "no-cache");
55        assert_eq!(resp.headers().get("Expires").unwrap(), "0");
56
57        let body: String = test::read_body_json(resp).await;
58        assert!(chrono::DateTime::parse_from_rfc3339(&body).is_ok());
59
60        let parsed = chrono::DateTime::parse_from_rfc3339(&body).unwrap();
61        let server_utc = parsed.with_timezone(&chrono::Utc);
62        let now = chrono::Utc::now();
63        let diff = (server_utc - now).abs();
64        assert!(
65            diff < chrono::Duration::seconds(5),
66            "server time should be close to now"
67        );
68    }
69}