From a55e7a74ab3c9b8b67f48d850208a69f6512d203 Mon Sep 17 00:00:00 2001
From: adam <git@adamrgrey.com>
Date: Thu, 22 May 2025 16:54:10 -0400
Subject: [PATCH] backgroundservice instead of IHostedService - now I can *see*
 my g.d. errors!

---
 ConsoleService.cs                             |  8 +--
 Program.cs                                    |  8 +--
 .../Controllers/api/AccountsController.cs     | 52 +++++++++++++++++++
 WebInterface/Views/Accounts/Details.cshtml    | 18 ++++---
 wwwroot/js/site.js                            | 30 ++++++++++-
 5 files changed, 95 insertions(+), 21 deletions(-)
 create mode 100644 WebInterface/Controllers/api/AccountsController.cs

diff --git a/ConsoleService.cs b/ConsoleService.cs
index f962c8f..158b90d 100644
--- a/ConsoleService.cs
+++ b/ConsoleService.cs
@@ -8,7 +8,7 @@ namespace vassago
     using vassago.ProtocolInterfaces.DiscordInterface;
     using System.Runtime.CompilerServices;
 
-    internal class ConsoleService : IHostedService
+    internal class ConsoleService : BackgroundService
     {
         public ConsoleService(IConfiguration aspConfig)
         {
@@ -25,7 +25,7 @@ namespace vassago
         IEnumerable<string> DiscordTokens { get; }
         IEnumerable<TwitchConfig> TwitchConfigs { get; }
 
-        public async Task StartAsync(CancellationToken cancellationToken)
+        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
         {
             var initTasks = new List<Task>();
             var dbc = new ChattingContext();
@@ -50,9 +50,5 @@ namespace vassago
             Task.WaitAll(initTasks.ToArray(), cancellationToken);
         }
 
-        public Task StopAsync(CancellationToken cancellationToken)
-        {
-            return null;
-        }
     }
 }
diff --git a/Program.cs b/Program.cs
index c932e84..1d066ef 100644
--- a/Program.cs
+++ b/Program.cs
@@ -25,12 +25,6 @@ builder.Services.AddSwaggerGen();
 
 var app = builder.Build();
 
-// Configure the HTTP request pipeline.
-if (!app.Environment.IsDevelopment())
-{
-    app.UseExceptionHandler("/Home/Error");
-}
-
 app.UseStaticFiles();
 app.UseRouting();
 app.UseAuthorization();
@@ -46,7 +40,7 @@ app.UseSwaggerUI(c =>
 });
 
 //app.UseExceptionHandler();
-app.UseStatusCodePages();
+//app.UseStatusCodePages();
 
 if (app.Environment.IsDevelopment())
 {
diff --git a/WebInterface/Controllers/api/AccountsController.cs b/WebInterface/Controllers/api/AccountsController.cs
new file mode 100644
index 0000000..828c2f0
--- /dev/null
+++ b/WebInterface/Controllers/api/AccountsController.cs
@@ -0,0 +1,52 @@
+using System.Diagnostics;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using vassago.Models;
+using vassago.ProtocolInterfaces.DiscordInterface;
+
+namespace vassago.Controllers.api;
+
+[Route("api/[controller]")]
+[ApiController]
+public class AccountsController : ControllerBase
+{
+    private readonly ILogger<AccountsController> _logger;
+
+    public AccountsController(ILogger<AccountsController> logger)
+    {
+        _logger = logger;
+    }
+
+    //microsoft: "you can't have multiple [FromBody]. The reason for this rule is some bullshti about storage buffers."
+    //cool story, bro. nobody gives a fuck, look at the boilerplate you've necessitated.
+    public class extraSpecialObjectReadGlorifiedTupleFor_UnlinkUser
+    {
+        public Guid acc_guid;
+    }
+    [HttpPatch]
+    [Route("UnlinkUser")]
+    [Produces("application/json")]
+    public IActionResult UnlinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_UnlinkUser req)
+    {
+        var acc_guid = req.acc_guid;
+        var accFromDb = Rememberer.SearchAccount(acc => acc.Id == acc_guid);
+        if (accFromDb == null)
+        {
+            var err = $"attempt to unlink user for acc {acc_guid}, not found";
+            _logger.LogError(err);
+            return NotFound(err);
+        }
+        var userFromDb = Rememberer.SearchUser(c => c.Id == accFromDb.IsUser.Id);
+        if (userFromDb == null)
+        {
+            var err = $"attempt to unlink user for {acc_guid}, doesn't have a user";
+            _logger.LogError(err);
+            return NotFound(err);
+        }
+
+        accFromDb.IsUser = null;
+
+        Rememberer.RememberAccount(accFromDb);
+        return Ok(accFromDb);
+    }
+}
diff --git a/WebInterface/Views/Accounts/Details.cshtml b/WebInterface/Views/Accounts/Details.cshtml
index 4a18080..c5a29ba 100644
--- a/WebInterface/Views/Accounts/Details.cshtml
+++ b/WebInterface/Views/Accounts/Details.cshtml
@@ -8,19 +8,23 @@
 <a href="/">home</a>/@Html.Raw(ViewData["breadcrumbs"])
 <table class="table">
     <tbody>
+        <tr>
+            <th>Id</th>
+            <td>@Model.Id</td>
+        </tr>
         <tr>
             <th scope="row">belongs to user</th>
             <td>@Model.IsUser.DisplayName</td>
-            <td><button alt="to do" disabled>separate</button></2td>
+            <td><button onclick="unlinkAccountUser(() => { window.location.reload(); })">separate</button></2td>
         </tr>
         <tr>
             <th scope="row">Seen in channel</th>
             <td class="account @Model.SeenInChannel.Protocol"><div class="protocol-icon">&nbsp;</div>@Model.SeenInChannel.LineageSummary<a href="/Channels/Details/@Model.SeenInChannel.Id">@Model.SeenInChannel.DisplayName</a></td>
         </tr>
         <tr>
-            <th scope="row">Permission Tags</th>
+            <th scope="row">UACs tied to account</th>
             <td>
-                <div id="tagsTree"></div>
+                <div id="uacsTree"></div>
             </td>
         </tr>
     </tbody>
@@ -44,10 +48,10 @@
             return userNow;
         }
 
-        function getTagsTree() {
+        function getUacsTree() {
         @{
             var sb = new StringBuilder();
-            sb.Append("[{text: \"permission tags\", \"expanded\":true, nodes: [");
+            sb.Append("[{text: \"UACs\", \"expanded\":true, nodes: [");
             var first = true;
             for (int i = 0; i < 1; i++)
             {
@@ -62,7 +66,7 @@
             var tree = @Html.Raw(sb.ToString());
             return tree;
         }
-        $('#tagsTree').bstreeview({ data: getTagsTree() });
-        document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser(), '/api/Users/') } });
+        $('#uacsTree').bstreeview({ data: getUacsTree() });
+        //document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser(), '/api/Users/') } });
     </script>
 }
diff --git a/wwwroot/js/site.js b/wwwroot/js/site.js
index 2f7e360..8286404 100644
--- a/wwwroot/js/site.js
+++ b/wwwroot/js/site.js
@@ -75,7 +75,7 @@ function deleteModel(model, deprecated_apiUrl)
   .catch(error => {
     console.error('Error:', error);
   });
-  }
+}
 function linkUAC_Channel(channel_guid, callback)
 {
 
@@ -246,3 +246,31 @@ function unlinkUAC_Channel(user_guid, callback)
     console.error('Error:', error);
   });
 }
+//give me account, we'll tear it off from user.
+function unlinkAccountUser(callback)
+{
+  var components = window.location.pathname.split('/');
+  var id=components[3];
+  let model={"acc_guid": id};
+  fetch(apiUrl + "Accounts/UnlinkUser/", {
+    method: 'PATCH',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    body: JSON.stringify(model),
+  })
+  .then(response => {
+    if (!response.ok) {
+      throw new Error('Network response was not "ok". which is not ok.');
+    }
+    return response.json();
+  })
+  .then(returnedSuccessdata => {
+    // perhaps a success callback
+    console.log('returnedSuccessdata:', returnedSuccessdata);
+    if(callback !== null) { callback(); }
+  })
+  .catch(error => {
+    console.error('Error:', error);
+  });
+}