Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion DEV_QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ npm run dev
### 4. Seed test users (new terminal)

```bash
./scripts/seed-test-users.sh
./scripts/seed.sh
```

Creates 3 users (password for all: `Password123!`):
Expand Down Expand Up @@ -105,3 +105,5 @@ podman machine stop # if using podman
| Gateway | http://localhost:5050/swagger |
| Account Service | http://localhost:5001/swagger |
| Donation Service | http://localhost:5100 |
| Video Catalogue | http://localhost:5002/swagger |
| Chat Service | http://localhost:5004/swagger |
112 changes: 112 additions & 0 deletions Glense.Server/Controllers/ChatProxyController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using Microsoft.AspNetCore.Mvc;
using System.Text;

namespace Glense.Server.Controllers;

[ApiController]
[Route("api")]
public class ChatProxyController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<ChatProxyController> _logger;

public ChatProxyController(IHttpClientFactory httpClientFactory, ILogger<ChatProxyController> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}

[HttpGet("chats")]
public Task<IActionResult> GetChats([FromQuery] Guid? cursor, [FromQuery] int pageSize = 50)
{
var qs = $"?pageSize={pageSize}" + (cursor.HasValue ? $"&cursor={cursor}" : "");
return ProxyGet($"/api/chats{qs}");
}

[HttpPost("chats")]
public Task<IActionResult> CreateChat() => ProxyPost("/api/chats");

[HttpGet("chats/{chatId:guid}")]
public Task<IActionResult> GetChat(Guid chatId) => ProxyGet($"/api/chats/{chatId}");

[HttpDelete("chats/{chatId:guid}")]
public Task<IActionResult> DeleteChat(Guid chatId) => ProxyDelete($"/api/chats/{chatId}");

[HttpGet("chats/{chatId:guid}/messages")]
public Task<IActionResult> GetMessages(Guid chatId, [FromQuery] Guid? cursor, [FromQuery] int pageSize = 50)
{
var qs = $"?pageSize={pageSize}" + (cursor.HasValue ? $"&cursor={cursor}" : "");
return ProxyGet($"/api/chats/{chatId}/messages{qs}");
}

[HttpPost("chats/{chatId:guid}/messages")]
public Task<IActionResult> PostMessage(Guid chatId) => ProxyPost($"/api/chats/{chatId}/messages");

[HttpGet("messages/{messageId:guid}")]
public Task<IActionResult> GetMessage(Guid messageId) => ProxyGet($"/api/messages/{messageId}");

[HttpDelete("messages/{messageId:guid}")]
public Task<IActionResult> DeleteMessage(Guid messageId) => ProxyDelete($"/api/messages/{messageId}");

private async Task<IActionResult> ProxyGet(string path)
{
var client = _httpClientFactory.CreateClient("ChatService");
try
{
var req = new HttpRequestMessage(HttpMethod.Get, path);
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private async Task<IActionResult> ProxyPost(string path)
{
var client = _httpClientFactory.CreateClient("ChatService");
try
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var req = new HttpRequestMessage(HttpMethod.Post, path)
{
Content = new StringContent(body, Encoding.UTF8, "application/json")
};
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private async Task<IActionResult> ProxyDelete(string path)
{
var client = _httpClientFactory.CreateClient("ChatService");
try
{
var req = new HttpRequestMessage(HttpMethod.Delete, path);
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private void ForwardHeaders(HttpRequestMessage req)
{
if (Request.Headers.TryGetValue("Authorization", out var auth))
req.Headers.Add("Authorization", auth.ToString());
}

private static async Task<ContentResult> ToResult(HttpResponseMessage resp)
{
var content = await resp.Content.ReadAsStringAsync();
return new ContentResult { StatusCode = (int)resp.StatusCode, Content = content, ContentType = "application/json" };
}

private IActionResult ServiceUnavailable(Exception ex, string path)
{
_logger.LogError(ex, "Error proxying to Chat Service: {Path}", path);
return StatusCode(502, new { message = "Chat service unavailable" });
}
}
155 changes: 155 additions & 0 deletions Glense.Server/Controllers/VideoProxyController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using Microsoft.AspNetCore.Mvc;
using System.Text;

namespace Glense.Server.Controllers;

[ApiController]
[Route("api")]
public class VideoProxyController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<VideoProxyController> _logger;

public VideoProxyController(IHttpClientFactory httpClientFactory, ILogger<VideoProxyController> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}

[HttpGet("videos")]
public Task<IActionResult> ListVideos() => ProxyGet("/api/videos");

[HttpGet("videos/{id:guid}")]
public Task<IActionResult> GetVideo(Guid id) => ProxyGet($"/api/videos/{id}");

[HttpGet("videos/{id:guid}/comments")]
public Task<IActionResult> GetComments(Guid id) => ProxyGet($"/api/videos/{id}/comments");

[HttpPost("videos/{id:guid}/comments")]
public Task<IActionResult> PostComment(Guid id) => ProxyPost($"/api/videos/{id}/comments");

[HttpDelete("videos/{videoId:guid}/comments/{commentId:guid}")]
public Task<IActionResult> DeleteComment(Guid videoId, Guid commentId) => ProxyDelete($"/api/videos/{videoId}/comments/{commentId}");

[HttpPost("videos/upload")]
public Task<IActionResult> UploadVideo() => ProxyUpload("/api/videos/upload");

[HttpGet("videos/{id:guid}/stream")]
public Task<IActionResult> StreamVideo(Guid id) => ProxyGet($"/api/videos/{id}/stream");

[HttpPost("subscriptions")]
public Task<IActionResult> Subscribe() => ProxyPost("/api/subscriptions");

[HttpDelete("subscriptions")]
public Task<IActionResult> Unsubscribe() => ProxyDelete("/api/subscriptions");

[HttpPost("videolikes")]
public Task<IActionResult> Like() => ProxyPost("/api/videolikes");

[HttpPost("playlists")]
public Task<IActionResult> CreatePlaylist() => ProxyPost("/api/playlists");

[HttpGet("playlists")]
public Task<IActionResult> ListPlaylists() => ProxyGet("/api/playlists");

[HttpGet("playlists/{id:guid}")]
public Task<IActionResult> GetPlaylist(Guid id) => ProxyGet($"/api/playlists/{id}");

[HttpPost("playlistvideos")]
public Task<IActionResult> AddToPlaylist() => ProxyPost("/api/playlistvideos");

[HttpDelete("playlistvideos")]
public Task<IActionResult> RemoveFromPlaylist() => ProxyDelete("/api/playlistvideos");

[HttpGet("playlistvideos/{playlistId:guid}")]
public Task<IActionResult> ListPlaylistVideos(Guid playlistId) => ProxyGet($"/api/playlistvideos/{playlistId}");

private async Task<IActionResult> ProxyGet(string path)
{
var client = _httpClientFactory.CreateClient("VideoService");
try
{
var req = new HttpRequestMessage(HttpMethod.Get, path);
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private async Task<IActionResult> ProxyPost(string path)
{
var client = _httpClientFactory.CreateClient("VideoService");
try
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var req = new HttpRequestMessage(HttpMethod.Post, path)
{
Content = new StringContent(body, Encoding.UTF8, "application/json")
};
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private async Task<IActionResult> ProxyDelete(string path)
{
var client = _httpClientFactory.CreateClient("VideoService");
try
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var req = new HttpRequestMessage(HttpMethod.Delete, path);
if (!string.IsNullOrEmpty(body))
req.Content = new StringContent(body, Encoding.UTF8, "application/json");
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private async Task<IActionResult> ProxyUpload(string path)
{
var client = _httpClientFactory.CreateClient("VideoService");
try
{
var content = new StreamContent(Request.Body);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(Request.ContentType ?? "application/octet-stream");
var req = new HttpRequestMessage(HttpMethod.Post, path) { Content = content };
ForwardHeaders(req);
var resp = await client.SendAsync(req);
return await ToResult(resp);
}
catch (Exception ex) { return ServiceUnavailable(ex, path); }
}

private void ForwardHeaders(HttpRequestMessage req)
{
if (Request.Headers.TryGetValue("Authorization", out var auth))
req.Headers.Add("Authorization", auth.ToString());
if (Request.Headers.TryGetValue("X-Uploader-Id", out var uid))
req.Headers.Add("X-Uploader-Id", uid.ToString());
if (Request.Headers.TryGetValue("X-User-Id", out var userId))
req.Headers.Add("X-User-Id", userId.ToString());
if (Request.Headers.TryGetValue("X-Creator-Id", out var cid))
req.Headers.Add("X-Creator-Id", cid.ToString());
if (Request.Headers.TryGetValue("X-Username", out var uname))
req.Headers.Add("X-Username", uname.ToString());
}

private static async Task<ContentResult> ToResult(HttpResponseMessage resp)
{
var content = await resp.Content.ReadAsStringAsync();
return new ContentResult { StatusCode = (int)resp.StatusCode, Content = content, ContentType = "application/json" };
}

private IActionResult ServiceUnavailable(Exception ex, string path)
{
_logger.LogError(ex, "Error proxying to Video Service: {Path}", path);
return StatusCode(502, new { message = "Video service unavailable" });
}
}
10 changes: 9 additions & 1 deletion Glense.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,22 @@
client.BaseAddress = new Uri(serviceUrl);
});

// Video Catalogue Service (for future use)
// Video Catalogue Service
builder.Services.AddHttpClient("VideoService", client =>
{
var serviceUrl = Environment.GetEnvironmentVariable("VIDEO_SERVICE_URL")
?? "http://localhost:5002";
client.BaseAddress = new Uri(serviceUrl);
});

// Chat Service
builder.Services.AddHttpClient("ChatService", client =>
{
var serviceUrl = Environment.GetEnvironmentVariable("CHAT_SERVICE_URL")
?? "http://localhost:5004";
client.BaseAddress = new Uri(serviceUrl);
});

var app = builder.Build();

// Serve static files from wwwroot (for SPA frontend)
Expand Down
Loading
Loading