using Invercargill.DataStructures; using Json; namespace Spry.Users { public class Session : GLib.Object { // Identity public string id { get; set; } public string user_id { get; set; } // Timing public DateTime created_at { get; set; } public DateTime expires_at { get; set; } // Optional tracking public string? ip_address { get; set; } public string? user_agent { get; set; } public Session() { id = ""; user_id = ""; created_at = new DateTime.now_utc(); expires_at = new DateTime.now_utc(); } public bool is_expired() { return expires_at.compare(new DateTime.now_utc()) <= 0; } public static Session from_json(Json.Object obj) { var session = new Session(); // Required string fields - use has_member and null coalescing for safety session.id = obj.has_member("id") ? (obj.get_string_member("id") ?? "") : ""; session.user_id = obj.has_member("user_id") ? (obj.get_string_member("user_id") ?? "") : ""; // created_at - check member exists and value is not null/empty if (obj.has_member("created_at")) { var created_str = obj.get_string_member("created_at"); if (created_str != null && created_str.length > 0) { session.created_at = new DateTime.from_iso8601(created_str, new TimeZone.utc()); } } // expires_at - check member exists and value is not null/empty if (obj.has_member("expires_at")) { var expires_str = obj.get_string_member("expires_at"); if (expires_str != null && expires_str.length > 0) { session.expires_at = new DateTime.from_iso8601(expires_str, new TimeZone.utc()); } } // ip_address (optional) - check member exists and is not null if (obj.has_member("ip_address")) { var member = obj.get_member("ip_address"); if (member != null && member.get_node_type() == Json.NodeType.VALUE) { session.ip_address = obj.get_string_member("ip_address"); } } // user_agent (optional) - check member exists and is not null if (obj.has_member("user_agent")) { var member = obj.get_member("user_agent"); if (member != null && member.get_node_type() == Json.NodeType.VALUE) { session.user_agent = obj.get_string_member("user_agent"); } } return session; } public Json.Object to_json() { var obj = new Json.Object(); // Use null coalescing to ensure we never pass null to set_string_member obj.set_string_member("id", id ?? ""); obj.set_string_member("user_id", user_id ?? ""); obj.set_string_member("created_at", created_at != null ? created_at.format_iso8601() : new DateTime.now_utc().format_iso8601()); obj.set_string_member("expires_at", expires_at != null ? expires_at.format_iso8601() : new DateTime.now_utc().format_iso8601()); // ip_address (optional) if (ip_address != null) { obj.set_string_member("ip_address", (!)ip_address); } else { obj.set_null_member("ip_address"); } // user_agent (optional) if (user_agent != null) { obj.set_string_member("user_agent", (!)user_agent); } else { obj.set_null_member("user_agent"); } return obj; } } }