From 79ee297e615ae84fd83ba661a3b70a5467c1f7a8 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Thu, 26 Feb 2026 18:45:15 -0800 Subject: [PATCH] Implement user login and registration; update database connection management to be per use not a singleton by adjusting service lifetimes in DI. --- WebServer/Components/Pages/Home.razor | 124 ++++++++++++++++------ WebServer/Components/Pages/Home.razor.css | 83 +++++++++++---- WebServer/Components/_Imports.razor | 1 + WebServer/Controllers/Database.cs | 73 +++++++------ WebServer/Program.cs | 2 +- WebServer/WebServer.csproj | 1 + 6 files changed, 194 insertions(+), 90 deletions(-) diff --git a/WebServer/Components/Pages/Home.razor b/WebServer/Components/Pages/Home.razor index bef96989..71c325ed 100644 --- a/WebServer/Components/Pages/Home.razor +++ b/WebServer/Components/Pages/Home.razor @@ -3,46 +3,70 @@ Home -
- - HOME - - - S&P Chart - - - Connect - - - About - -
-
- - - @foreach (stockPredictionPair cur in predictions){ -
-

Symbol: @cur.Symbol


- @if (cur.Movement == -1){ -

Sell

- } else if (cur.Movement == 1){ -

Buy

- } else{ -

Hold

- } + + @if (Session == null){ +
+

LOGIN

+
+
username
+ +
+
+
password
+ +
+
+ + +
+
+ @loginError +
+
+ + + }else{ +
+ UserName: @Session.UserName
} + + +
+
+ + +
+
+ @foreach (stockPredictionPair cur in predictions){ +
+

Symbol: @cur.Symbol


+ @if (cur.Movement == -1){ +

Sell

+ } else if (cur.Movement == 1){ +

Buy

+ } else{ +

Hold

+ } +
+ } +
+
@code { - string button1Text = "Train AI"; - string button2Text = "Predict AI"; + // User Stuff + loginSession? Session = null; + // Login Stuff + string userName = ""; + string passWord = ""; + string loginError = ""; List predictions = new List(){ new stockPredictionPair(){ Symbol = "AAPL" }, @@ -51,6 +75,36 @@ new stockPredictionPair(){ Symbol = "FUN" } }; + async Task LoginSession(){ + string dbPrefix = $"[{userName.ToLower()}]:"; // Set the DB prefix for the get and set + string passwordhash = dbDriver.Get( dbPrefix + "password" ); // Pull the password hash + if (BCrypt.Verify( passWord, passwordhash )){ // If the password is valid + Session = new loginSession(){ + UserName = userName.ToLower() + }; + }else{ + loginError = "wrong password"; + } + } + + async Task registerSession(){ + string dbPrefix = $"[{userName.ToLower()}]:"; + string passwordhash = dbDriver.Get( dbPrefix + "password" ); + if (string.IsNullOrEmpty(passwordhash)){ + dbDriver.Set( dbPrefix + "password", BCrypt.HashPassword( passWord, BCrypt.GenerateSalt() ) ); + Session = new loginSession(){ + UserName = userName.ToLower() + }; + }else{ + loginError = "account is taken"; + } + } + + // AI Stuff + + string button1Text = "Train AI"; + string button2Text = "Predict AI"; + async Task pullandtrain(){ button1Text = "Do not refresh the page. The data is refreshing."; await Task.Delay(1); @@ -76,9 +130,15 @@ await Task.Delay(1); } - class stockPredictionPair{ - public string Symbol = ""; - public int Movement = 0; + // Data Types + + class stockPredictionPair { + public string Symbol { get; set; } = ""; + public int Movement { get; set; } = 0; + } + + class loginSession { + public string UserName { get; set; } = ""; } } \ No newline at end of file diff --git a/WebServer/Components/Pages/Home.razor.css b/WebServer/Components/Pages/Home.razor.css index 9d8e445d..54a0f148 100644 --- a/WebServer/Components/Pages/Home.razor.css +++ b/WebServer/Components/Pages/Home.razor.css @@ -1,25 +1,62 @@ -.card-holder { - display: flex; - float: left; - flex-direction: column; - background-color: red; - width: 300px; - height: calc(100vh - 3.5rem); -} - -.card { - background-color: blue; - margin: 5px; - height: 45px; - color: white; - align-items: center; - display: flex; - justify-content: center; -} - .main-area { - float: left; - background-color: aqua; - height: calc(100vh - 3.5rem); - width: calc(100vw - 300px); + display: grid; + gap: 20px; + padding: 20px; + grid-auto-columns: auto; + grid-auto-flow: column; + grid-template-columns: max-content; + overflow: scroll; +} + +.gridFrame { + background: #eee; + border-radius: 12px; + padding: 1.5rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: 0.5s ease-in-out; + height: fit-content; + display: grid; + grid-row-gap: 10px; + border: 1px solid black; +} + +.gridFrame h2 { + text-align: center; + margin: 0; + padding: 0; + border: 0; +} + +.gridFrame:hover { + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4); +} + +.loginRow { + width: 300px; + height: 40px; + padding-bottom: 10px; +} + +.loginRow input { + width: calc(100% - 6px); + border: 1px solid black; + padding: 1px 2px; +} + + .loginRow input:focus { + border-color: brown; + } + +.loginRow button { + width: calc(50% - 15px); + background-color: darkblue; + border: 0; + margin: 0; + padding: 0; + color: white; + height: 30px; +} + +.loginRow button:last-of-type{ + float: right; } \ No newline at end of file diff --git a/WebServer/Components/_Imports.razor b/WebServer/Components/_Imports.razor index c8c4e4eb..22b16ca2 100644 --- a/WebServer/Components/_Imports.razor +++ b/WebServer/Components/_Imports.razor @@ -10,6 +10,7 @@ @using PythonInterop @using WebServer @using WebServer.Components +@using BCrypt.Net; @inject DbDriver dbDriver @inject AIModule aiModule diff --git a/WebServer/Controllers/Database.cs b/WebServer/Controllers/Database.cs index a97a7fcd..196d9deb 100644 --- a/WebServer/Controllers/Database.cs +++ b/WebServer/Controllers/Database.cs @@ -2,52 +2,67 @@ using Microsoft.Data.Sqlite; namespace DataBase { - public class DbDriver : IDisposable { + public class DbDriver { - static SqliteConnection? singletonConnector = null; + static bool Initilized = false; public DbDriver() { // Load in the datastore if not already loaded - if (singletonConnector == null) { - singletonConnector = new SqliteConnection("Data Source=test.db"); - singletonConnector.Open(); - - // Create the key value store if not exist - using var command = singletonConnector.CreateCommand(); - command.CommandText = """ - CREATE TABLE IF NOT EXISTS KeyValuePair ( - key TEXT NOT NULL, - value TEXT, - PRIMARY KEY(key) - ); - """; - command.ExecuteReader(); + if (!Initilized) { + // Open a connection with auto dispose when done + using (SqliteConnection sqlite = new SqliteConnection("Data Source=test.db")) { + // Open the connection + sqlite.Open(); + // Create the key value store if not exist + using var command = sqlite.CreateCommand(); + command.CommandText = """ + CREATE TABLE IF NOT EXISTS KeyValuePair ( + key TEXT NOT NULL, + value TEXT, + PRIMARY KEY(key) + ); + """; + // Run the command + command.ExecuteReader(); + } } } // Return Values from keys public string Get(string Key) { - if (singletonConnector != null) { - using var command = singletonConnector.CreateCommand(); + // Open a connection with auto dispose when done + using (SqliteConnection sqlite = new SqliteConnection("Data Source=test.db")) { + // Open the connection + sqlite.Open(); + // Create the key value store if not exist + using var command = sqlite.CreateCommand(); command.CommandText = """ SELECT value FROM KeyValuePair Where key = $key; """; + // Add the parameter to prevent sql injection command.Parameters.AddWithValue("$key", Key); - command.ExecuteReader(); + // Run the command using var reader = command.ExecuteReader(); + // Read off the result if exists if (reader.Read()) { + // Return the results return reader.GetString(0); } + // Return something if nothing existed + return ""; } - return ""; } // Set a key value pair in the store public void Set(string Key, string Value) { - if (singletonConnector != null) { - using var command = singletonConnector.CreateCommand(); + // Open a connection with auto dispose when done + using (SqliteConnection sqlite = new SqliteConnection("Data Source=test.db")) { + // Open the connection + sqlite.Open(); + // Create the key value store if not exist + using var command = sqlite.CreateCommand(); command.CommandText = """ INSERT INTO KeyValuePair (key,value) @@ -57,24 +72,14 @@ namespace DataBase { DO UPDATE SET value = excluded.value; """; + // Add the parameters to prevent sql injection command.Parameters.AddWithValue("$key", Key); command.Parameters.AddWithValue("$value", Value); + // Process the command command.ExecuteReader(); } } - // Deconstructor - ~DbDriver() { - Dispose(); - } - - // Deconstructor - public void Dispose() { - if (singletonConnector != null) { - singletonConnector.Close(); - singletonConnector = null; - } - } } } \ No newline at end of file diff --git a/WebServer/Program.cs b/WebServer/Program.cs index da72e72f..3448ff19 100644 --- a/WebServer/Program.cs +++ b/WebServer/Program.cs @@ -19,7 +19,7 @@ if (args.Contains("Pull-Stock-Data")) { builder.Services.AddRazorComponents().AddInteractiveServerComponents(); // Insert the DB driver for Dependency Injection - builder.Services.AddSingleton(); + builder.Services.AddScoped(); // Insert the python modlue for Dependency Injection builder.Services.AddSingleton(interopModule); diff --git a/WebServer/WebServer.csproj b/WebServer/WebServer.csproj index 74369535..5695abb4 100644 --- a/WebServer/WebServer.csproj +++ b/WebServer/WebServer.csproj @@ -7,6 +7,7 @@ +