summary refs log tree commit diff
path: root/MxApiExtensions/Controllers/SyncController.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MxApiExtensions/Controllers/SyncController.cs')
-rw-r--r--MxApiExtensions/Controllers/SyncController.cs111
1 files changed, 111 insertions, 0 deletions
diff --git a/MxApiExtensions/Controllers/SyncController.cs b/MxApiExtensions/Controllers/SyncController.cs
new file mode 100644
index 0000000..e2b724f
--- /dev/null
+++ b/MxApiExtensions/Controllers/SyncController.cs
@@ -0,0 +1,111 @@
+using System.Net.Http.Headers;
+using Microsoft.AspNetCore.Mvc;
+
+namespace MxApiExtensions.Controllers;
+
+[ApiController]
+[Route("/")]
+public class SyncController : ControllerBase {
+    private readonly ILogger<SyncController> _logger;
+    private readonly CacheConfiguration _config;
+    private readonly Auth _auth;
+
+    public SyncController(ILogger<SyncController> logger, CacheConfiguration config, Auth auth) {
+        _logger = logger;
+        _config = config;
+        _auth = auth;
+    }
+
+    [HttpGet("/_matrix/client/v3/sync")]
+    public async Task Sync([FromQuery] string? since, [FromQuery] string? access_token) {
+        try {
+            access_token ??= _auth.GetToken();
+            var mxid = _auth.GetUserId();
+            var cacheFile = GetFilePath(mxid, since);
+
+            if (!await TrySendCached(cacheFile)) {
+                using var hc = new HttpClient();
+                hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access_token);
+                hc.Timeout = TimeSpan.FromMinutes(10);
+                //remove access_token from query string
+                Request.QueryString = new QueryString(
+                    Request.QueryString.Value
+                        .Replace("&access_token", "access_token")
+                        .Replace($"access_token={access_token}", "")
+                );
+
+                var resp = hc.GetAsync($"{_config.Homeserver}{Request.Path}{Request.QueryString}").Result;
+                // var resp = await hs._httpClient.GetAsync($"/_matrix/client/v3/sync?since={since}");
+
+                if (resp.Content is null) {
+                    throw new MatrixException() {
+                        ErrorCode = "M_UNKNOWN",
+                        Error = "No content in response"
+                    };
+                }
+
+                Response.StatusCode = (int)resp.StatusCode;
+                Response.ContentType = "application/json";
+                await Response.StartAsync();
+                await using var stream = await resp.Content.ReadAsStreamAsync();
+                await using var target = System.IO.File.OpenWrite(cacheFile);
+                byte[] buffer = new byte[1];
+
+                int bytesRead;
+                while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) {
+                    await Response.Body.WriteAsync(buffer.AsMemory(0, bytesRead));
+                    target.Write(buffer, 0, bytesRead);
+                }
+
+                await target.FlushAsync();
+                await Response.CompleteAsync();
+            }
+        }
+        catch (MatrixException e) {
+            Response.StatusCode = StatusCodes.Status500InternalServerError;
+            Response.ContentType = "application/json";
+
+            await Response.WriteAsJsonAsync(e.GetAsJson());
+            await Response.CompleteAsync();
+        }
+        catch (Exception e) {
+            Response.StatusCode = StatusCodes.Status500InternalServerError;
+            Response.ContentType = "text/plain";
+
+            await Response.WriteAsync(e.ToString());
+            await Response.CompleteAsync();
+        }
+    }
+
+    private async Task<bool> TrySendCached(string cacheFile) {
+        if (System.IO.File.Exists(cacheFile)) {
+            Response.StatusCode = 200;
+            Response.ContentType = "application/json";
+            await Response.StartAsync();
+            await using var stream = System.IO.File.OpenRead(cacheFile);
+            await stream.CopyToAsync(Response.Body);
+            await Response.CompleteAsync();
+            return true;
+        }
+
+        return false;
+    }
+
+#region Cache management
+
+    public string GetFilePath(string mxid, string since) {
+        var cacheDir = Path.Join("cache", mxid);
+        Directory.CreateDirectory(cacheDir);
+        var cacheFile = Path.Join(cacheDir, $"sync-{since}.json");
+        if (!Path.GetFullPath(cacheFile).StartsWith(Path.GetFullPath(cacheDir))) {
+            throw new MatrixException() {
+                ErrorCode = "M_UNKNOWN",
+                Error = "[Rory&::MxSyncCache] Cache file path is not in cache directory"
+            };
+        }
+
+        return cacheFile;
+    }
+
+#endregion
+}