SqlSessionRepository.vala 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using Invercargill;
  2. using Invercargill.DataStructures;
  3. using InvercargillSql;
  4. namespace Spry.Authentication {
  5. /**
  6. * SQL implementation of SessionRepository using InvercargillSql.
  7. */
  8. public class SqlSessionRepository : Object, SessionRepository {
  9. private Connection _connection;
  10. // =========================================================================
  11. // Constructor
  12. // =========================================================================
  13. public SqlSessionRepository(Connection connection) {
  14. _connection = connection;
  15. }
  16. // =========================================================================
  17. // Retrieval Operations
  18. // =========================================================================
  19. public async Session? get_by_id(string id) throws Error {
  20. var sql = "SELECT * FROM sessions WHERE id = :id";
  21. var results = yield _connection.create_command(sql)
  22. .with_parameter("id", id)
  23. .execute_query_async();
  24. var row = results.first_or_default();
  25. if (row == null) {
  26. return null;
  27. }
  28. return session_from_properties(row);
  29. }
  30. public async Vector<Session> get_by_user_id(string user_id) throws Error {
  31. var sql = """
  32. SELECT * FROM sessions
  33. WHERE user_id = :user_id
  34. ORDER BY created_at DESC
  35. """;
  36. var results = yield _connection.create_command(sql)
  37. .with_parameter("user_id", user_id)
  38. .execute_query_async();
  39. var sessions = new Vector<Session>();
  40. foreach (var row in results) {
  41. sessions.add(session_from_properties(row));
  42. }
  43. return sessions;
  44. }
  45. // =========================================================================
  46. // Mutation Operations
  47. // =========================================================================
  48. public async Session create(string id, string user_id, DateTime expires_at) throws Error {
  49. var now = new DateTime.now_utc();
  50. var sql = """
  51. INSERT INTO sessions (id, user_id, created_at, expires_at, last_accessed_at)
  52. VALUES (:id, :user_id, :created_at, :expires_at, :last_accessed_at)
  53. """;
  54. yield _connection.create_command(sql)
  55. .with_parameter("id", id)
  56. .with_parameter("user_id", user_id)
  57. .with_parameter("created_at", now.format_iso8601())
  58. .with_parameter("expires_at", expires_at.format_iso8601())
  59. .with_parameter("last_accessed_at", now.format_iso8601())
  60. .execute_non_query_async();
  61. var session = new Session();
  62. session.id = id;
  63. session.user_id = user_id;
  64. session.created_at = now;
  65. session.expires_at = expires_at;
  66. return session;
  67. }
  68. public async void update(Session session) throws Error {
  69. var sql = """
  70. UPDATE sessions SET
  71. expires_at = :expires_at
  72. WHERE id = :id
  73. """;
  74. yield _connection.create_command(sql)
  75. .with_parameter("id", session.id)
  76. .with_parameter("expires_at", session.expires_at.format_iso8601())
  77. .execute_non_query_async();
  78. }
  79. public async void delete(string id) throws Error {
  80. var sql = "DELETE FROM sessions WHERE id = :id";
  81. yield _connection.create_command(sql)
  82. .with_parameter("id", id)
  83. .execute_non_query_async();
  84. }
  85. public async void delete_by_user_id(string user_id) throws Error {
  86. var sql = "DELETE FROM sessions WHERE user_id = :user_id";
  87. yield _connection.create_command(sql)
  88. .with_parameter("user_id", user_id)
  89. .execute_non_query_async();
  90. }
  91. // =========================================================================
  92. // Cleanup Operations
  93. // =========================================================================
  94. public async void delete_expired() throws Error {
  95. var now = new DateTime.now_utc();
  96. var sql = "DELETE FROM sessions WHERE expires_at < :now";
  97. yield _connection.create_command(sql)
  98. .with_parameter("now", now.format_iso8601())
  99. .execute_non_query_async();
  100. }
  101. // =========================================================================
  102. // Private Helpers
  103. // =========================================================================
  104. private Session session_from_properties(Properties props) {
  105. var session = new Session();
  106. // Required fields
  107. session.id = get_string_or_empty(props, "id");
  108. session.user_id = get_string_or_empty(props, "user_id");
  109. // created_at
  110. var created_str = get_string_or_empty(props, "created_at");
  111. if (created_str.length > 0) {
  112. session.created_at = new DateTime.from_iso8601(created_str, new TimeZone.utc());
  113. }
  114. // expires_at
  115. var expires_str = get_string_or_empty(props, "expires_at");
  116. if (expires_str.length > 0) {
  117. session.expires_at = new DateTime.from_iso8601(expires_str, new TimeZone.utc());
  118. }
  119. // ip_address (nullable)
  120. var ip_address = get_string_or_null(props, "ip_address");
  121. session.ip_address = ip_address;
  122. // user_agent (nullable)
  123. var user_agent = get_string_or_null(props, "user_agent");
  124. session.user_agent = user_agent;
  125. return session;
  126. }
  127. private string get_string_or_empty(Properties props, string key) {
  128. if (!props.has(key)) {
  129. return "";
  130. }
  131. var elem = props.get(key);
  132. if (elem == null) {
  133. return "";
  134. }
  135. var str = elem.as<string>();
  136. return str ?? "";
  137. }
  138. private string? get_string_or_null(Properties props, string key) {
  139. if (!props.has(key)) {
  140. return null;
  141. }
  142. var elem = props.get(key);
  143. if (elem == null) {
  144. return null;
  145. }
  146. return elem.as<string>();
  147. }
  148. }
  149. }