Implement user login and registration; update database connection management to be per use not a singleton by adjusting service lifetimes in DI.
This commit is contained in:
@@ -3,25 +3,43 @@
|
|||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
||||||
<div class="card-holder">
|
|
||||||
<a class="card">
|
|
||||||
HOME
|
|
||||||
</a>
|
|
||||||
<a class="card">
|
|
||||||
S&P Chart
|
|
||||||
</a>
|
|
||||||
<a class="card">
|
|
||||||
Connect
|
|
||||||
</a>
|
|
||||||
<a class="card">
|
|
||||||
About
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="main-area">
|
<div class="main-area">
|
||||||
|
|
||||||
|
<!-- Login Frame -->
|
||||||
|
@if (Session == null){
|
||||||
|
<div class="gridFrame">
|
||||||
|
<div><h2>LOGIN</h2></div>
|
||||||
|
<div class="loginRow">
|
||||||
|
<div><span>username</span></div>
|
||||||
|
<input @bind-value="userName" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="loginRow">
|
||||||
|
<div><span>password</span></div>
|
||||||
|
<input @bind-value="passWord" type="password" />
|
||||||
|
</div>
|
||||||
|
<div class="loginRow">
|
||||||
|
<button @onclick="LoginSession">Login</button>
|
||||||
|
<button @onclick="registerSession">Register</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span style="color: red;">@loginError</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User Frame -->
|
||||||
|
}else{
|
||||||
|
<div class="gridFrame">
|
||||||
|
<span>UserName: @Session.UserName</span><br />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- AI Frame -->
|
||||||
|
<div class="gridFrame">
|
||||||
|
<div>
|
||||||
<button @onclick="pullandtrain">@button1Text</button>
|
<button @onclick="pullandtrain">@button1Text</button>
|
||||||
<button @onclick="predict">@button2Text</button>
|
<button @onclick="predict">@button2Text</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
@foreach (stockPredictionPair cur in predictions){
|
@foreach (stockPredictionPair cur in predictions){
|
||||||
<div>
|
<div>
|
||||||
<h1>Symbol: @cur.Symbol</h1><br />
|
<h1>Symbol: @cur.Symbol</h1><br />
|
||||||
@@ -34,15 +52,21 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
string button1Text = "Train AI";
|
// User Stuff
|
||||||
string button2Text = "Predict AI";
|
loginSession? Session = null;
|
||||||
|
|
||||||
|
// Login Stuff
|
||||||
|
string userName = "";
|
||||||
|
string passWord = "";
|
||||||
|
string loginError = "";
|
||||||
|
|
||||||
List<stockPredictionPair> predictions = new List<stockPredictionPair>(){
|
List<stockPredictionPair> predictions = new List<stockPredictionPair>(){
|
||||||
new stockPredictionPair(){ Symbol = "AAPL" },
|
new stockPredictionPair(){ Symbol = "AAPL" },
|
||||||
@@ -51,6 +75,36 @@
|
|||||||
new stockPredictionPair(){ Symbol = "FUN" }
|
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(){
|
async Task pullandtrain(){
|
||||||
button1Text = "Do not refresh the page. The data is refreshing.";
|
button1Text = "Do not refresh the page. The data is refreshing.";
|
||||||
await Task.Delay(1);
|
await Task.Delay(1);
|
||||||
@@ -76,9 +130,15 @@
|
|||||||
await Task.Delay(1);
|
await Task.Delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class stockPredictionPair{
|
// Data Types
|
||||||
public string Symbol = "";
|
|
||||||
public int Movement = 0;
|
class stockPredictionPair {
|
||||||
|
public string Symbol { get; set; } = "";
|
||||||
|
public int Movement { get; set; } = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class loginSession {
|
||||||
|
public string UserName { get; set; } = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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 {
|
.main-area {
|
||||||
float: left;
|
display: grid;
|
||||||
background-color: aqua;
|
gap: 20px;
|
||||||
height: calc(100vh - 3.5rem);
|
padding: 20px;
|
||||||
width: calc(100vw - 300px);
|
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;
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
@using PythonInterop
|
@using PythonInterop
|
||||||
@using WebServer
|
@using WebServer
|
||||||
@using WebServer.Components
|
@using WebServer.Components
|
||||||
|
@using BCrypt.Net;
|
||||||
|
|
||||||
@inject DbDriver dbDriver
|
@inject DbDriver dbDriver
|
||||||
@inject AIModule aiModule
|
@inject AIModule aiModule
|
||||||
|
|||||||
@@ -2,18 +2,19 @@ using Microsoft.Data.Sqlite;
|
|||||||
|
|
||||||
namespace DataBase {
|
namespace DataBase {
|
||||||
|
|
||||||
public class DbDriver : IDisposable {
|
public class DbDriver {
|
||||||
|
|
||||||
static SqliteConnection? singletonConnector = null;
|
static bool Initilized = false;
|
||||||
|
|
||||||
public DbDriver() {
|
public DbDriver() {
|
||||||
// Load in the datastore if not already loaded
|
// Load in the datastore if not already loaded
|
||||||
if (singletonConnector == null) {
|
if (!Initilized) {
|
||||||
singletonConnector = new SqliteConnection("Data Source=test.db");
|
// Open a connection with auto dispose when done
|
||||||
singletonConnector.Open();
|
using (SqliteConnection sqlite = new SqliteConnection("Data Source=test.db")) {
|
||||||
|
// Open the connection
|
||||||
|
sqlite.Open();
|
||||||
// Create the key value store if not exist
|
// Create the key value store if not exist
|
||||||
using var command = singletonConnector.CreateCommand();
|
using var command = sqlite.CreateCommand();
|
||||||
command.CommandText = """
|
command.CommandText = """
|
||||||
CREATE TABLE IF NOT EXISTS KeyValuePair (
|
CREATE TABLE IF NOT EXISTS KeyValuePair (
|
||||||
key TEXT NOT NULL,
|
key TEXT NOT NULL,
|
||||||
@@ -21,33 +22,47 @@ namespace DataBase {
|
|||||||
PRIMARY KEY(key)
|
PRIMARY KEY(key)
|
||||||
);
|
);
|
||||||
""";
|
""";
|
||||||
|
// Run the command
|
||||||
command.ExecuteReader();
|
command.ExecuteReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return Values from keys
|
// Return Values from keys
|
||||||
public string Get(string Key) {
|
public string Get(string Key) {
|
||||||
if (singletonConnector != null) {
|
// Open a connection with auto dispose when done
|
||||||
using var command = singletonConnector.CreateCommand();
|
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 = """
|
command.CommandText = """
|
||||||
SELECT value
|
SELECT value
|
||||||
FROM KeyValuePair
|
FROM KeyValuePair
|
||||||
Where key = $key;
|
Where key = $key;
|
||||||
""";
|
""";
|
||||||
|
// Add the parameter to prevent sql injection
|
||||||
command.Parameters.AddWithValue("$key", Key);
|
command.Parameters.AddWithValue("$key", Key);
|
||||||
command.ExecuteReader();
|
// Run the command
|
||||||
using var reader = command.ExecuteReader();
|
using var reader = command.ExecuteReader();
|
||||||
|
// Read off the result if exists
|
||||||
if (reader.Read()) {
|
if (reader.Read()) {
|
||||||
|
// Return the results
|
||||||
return reader.GetString(0);
|
return reader.GetString(0);
|
||||||
}
|
}
|
||||||
}
|
// Return something if nothing existed
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set a key value pair in the store
|
// Set a key value pair in the store
|
||||||
public void Set(string Key, string Value) {
|
public void Set(string Key, string Value) {
|
||||||
if (singletonConnector != null) {
|
// Open a connection with auto dispose when done
|
||||||
using var command = singletonConnector.CreateCommand();
|
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 = """
|
command.CommandText = """
|
||||||
INSERT INTO KeyValuePair
|
INSERT INTO KeyValuePair
|
||||||
(key,value)
|
(key,value)
|
||||||
@@ -57,24 +72,14 @@ namespace DataBase {
|
|||||||
DO UPDATE
|
DO UPDATE
|
||||||
SET value = excluded.value;
|
SET value = excluded.value;
|
||||||
""";
|
""";
|
||||||
|
// Add the parameters to prevent sql injection
|
||||||
command.Parameters.AddWithValue("$key", Key);
|
command.Parameters.AddWithValue("$key", Key);
|
||||||
command.Parameters.AddWithValue("$value", Value);
|
command.Parameters.AddWithValue("$value", Value);
|
||||||
|
// Process the command
|
||||||
command.ExecuteReader();
|
command.ExecuteReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deconstructor
|
|
||||||
~DbDriver() {
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deconstructor
|
|
||||||
public void Dispose() {
|
|
||||||
if (singletonConnector != null) {
|
|
||||||
singletonConnector.Close();
|
|
||||||
singletonConnector = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ if (args.Contains("Pull-Stock-Data")) {
|
|||||||
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
||||||
|
|
||||||
// Insert the DB driver for Dependency Injection
|
// Insert the DB driver for Dependency Injection
|
||||||
builder.Services.AddSingleton<DbDriver>();
|
builder.Services.AddScoped<DbDriver>();
|
||||||
|
|
||||||
// Insert the python modlue for Dependency Injection
|
// Insert the python modlue for Dependency Injection
|
||||||
builder.Services.AddSingleton(interopModule);
|
builder.Services.AddSingleton(interopModule);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BCrypt.Net-Next" Version="4.1.0" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
|
||||||
<PackageReference Include="pythonnet" Version="3.0.5" />
|
<PackageReference Include="pythonnet" Version="3.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user