//! `GET /api/version` — what this binary is and how long it has been running. //! //! Unauthenticated (ruling 18), like the other monitoring endpoints. The three //! strings are build options, so nothing here reads the running configuration. const std = @import("std"); const http_util = @import("../http_util.zig"); const server = @import("../server.zig"); const version = @import("../../version.zig"); pub const Body = struct { version: []const u8, zig_version: []const u8, /// Seconds since the process started. Zero until `started_unix` is wired, /// and never negative: a clock stepped backwards must not report a /// process that started in the future. uptime_seconds: u64, }; pub fn handle( state: *server.WebState, io: std.Io, request: *http_util.Request, ) http_util.HandlerError!void { const now = std.Io.Clock.real.now(io).toSeconds(); return http_util.respondJson(request, .ok, body(state.version, state.started_unix, now), &.{}); } pub fn body(version_string: []const u8, started_unix: i64, now_unix: i64) Body { return .{ .version = if (version_string.len == 0) version.string else version_string, .zig_version = version.zig_version_string, .uptime_seconds = uptime(started_unix, now_unix), }; } fn uptime(started_unix: i64, now_unix: i64) u64 { if (started_unix <= 0 or now_unix <= started_unix) return 0; return @intCast(now_unix - started_unix); } const testing = std.testing; test "the body carries the build strings and the elapsed time" { const out = body("", 1_000, 1_060); try testing.expectEqualStrings(version.string, out.version); try testing.expectEqualStrings(version.zig_version_string, out.zig_version); try testing.expectEqual(@as(u64, 60), out.uptime_seconds); } test "the state's version string wins over the compiled-in one" { try testing.expectEqualStrings("9.9.9", body("9.9.9", 0, 0).version); } test "an unset start time and a clock that stepped back both read as zero uptime" { try testing.expectEqual(@as(u64, 0), body("", 0, 5_000).uptime_seconds); try testing.expectEqual(@as(u64, 0), body("", 5_000, 4_000).uptime_seconds); } test "the body serializes with snake_case field names" { var buffer: [256]u8 = undefined; var writer: std.Io.Writer = .fixed(&buffer); try std.json.Stringify.value(body("1.2.3", 10, 20), .{}, &writer); const text = writer.buffered(); try testing.expect(std.mem.containsAtLeast(u8, text, 1, "\"zig_version\":")); try testing.expect(std.mem.containsAtLeast(u8, text, 1, "\"uptime_seconds\":10")); }