Compare commits

..

32 Commits

Author SHA1 Message Date
derek 64cc5066af Update Comment for angular sizes 2025-06-19 19:51:56 -07:00
derek ddb05c305c Update comment 2025-06-19 19:45:07 -07:00
derek b9cb93071d Turn off AOT as its the best option for produciton 2025-06-19 19:19:30 -07:00
derek ff91ff9109 Turn all optimizations back on for speed test 2025-06-19 18:56:57 -07:00
derek 57dca6974a For test with no Optimizations 2025-06-19 18:50:55 -07:00
derek d83ba35a84 Turn AOT back off 2025-06-19 18:45:57 -07:00
derek 7160515121 Try running AOT compile again 2025-06-19 18:19:31 -07:00
derek 66db26c74c Remove App.css 2025-06-19 18:13:16 -07:00
derek 4902f9c6ce Dont cut anything that is the app 2025-06-19 18:08:27 -07:00
derek 16a9c5ec5f Disable more trimmings 2025-06-19 18:03:48 -07:00
derek e1d3908e29 Turn off linking for the full Newtonsoft.Json 2025-06-19 17:58:18 -07:00
derek ea9d1fa24f Add more to newtonsoft ignores 2025-06-19 17:51:46 -07:00
derek 4f6fc16494 Turn linking back on with Newtonsoft not trimmed 2025-06-19 17:46:54 -07:00
derek 123f739b61 Turn off linking 2025-06-19 17:39:25 -07:00
derek 0cdcf9af71 Trim less 2025-06-19 17:31:50 -07:00
derek 59d6bb1ef1 Turn off AOT compile for blazor 2025-06-19 17:26:28 -07:00
derek 60d9b844fb Add comments to dockerfile 2025-06-19 17:09:41 -07:00
derek 1a08e99c6b . 2025-06-19 16:59:13 -07:00
derek 2e8893b240 add python to build container 2025-06-19 16:58:19 -07:00
derek d0af6ba73c update docker for wasm 2025-06-19 16:52:54 -07:00
derek 059c755d14 Update client to use forms 2025-06-19 16:49:43 -07:00
derek 34d3d047d2 fix namespacing issues 2025-06-19 16:49:34 -07:00
derek a3de3d573a Make api 'FormBody' -> cleaner & smaller dto's 2025-06-19 16:48:52 -07:00
derek bbab4972c3 Update usings for new shared folder 2025-06-19 16:48:16 -07:00
derek 07a1014889 Fix warnings by cleaning CSS 2025-06-19 16:47:23 -07:00
derek 2ce398a8d4 Organize imports file 2025-06-19 16:46:53 -07:00
derek fcf64f335d Remove non-essential website pages 2025-06-19 16:46:39 -07:00
derek c873f473c8 Delete Launch Settings 2025-06-19 16:46:18 -07:00
derek f110ad8d27 Modify GitIgnore 2025-06-19 16:46:06 -07:00
derek 412624a93c Split apart the DTO from the Database Objects 2025-06-19 10:40:26 -07:00
derek.holloway e88c7111e6 add lauch task for F5 2025-06-18 13:47:17 -07:00
derek.holloway 7c3ac165b7 Update .gitignore 2025-06-18 13:47:09 -07:00
47 changed files with 588 additions and 1365 deletions
+2 -2
View File
@@ -2,5 +2,5 @@
**/obj **/obj
.git .git
.env .env
.vscode data
data Properties
+25
View File
@@ -0,0 +1,25 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "dotnet: build",
"program": "${workspaceFolder}/src/MistoxWebsite.Server/bin/Debug/net9.0/MistoxWebsite.Server.dll",
"args":[],
"cwd": "${workspaceFolder}/src/MistoxWebsite.Server/",
"stopAtEntry": false,
"serverReadyAction":{
"action": "openExternally",
"killOnServerStop": false
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}
]
}
+7
View File
@@ -5,6 +5,13 @@ WORKDIR /src
# Copy all projects # Copy all projects
COPY ["src", "."] COPY ["src", "."]
# Needed for AOT Compiling wasm
RUN dotnet workload install wasm-tools
# Needed for compression of AOT files
RUN apt update
RUN apt install -y python3
# Restore the Server # Restore the Server
RUN dotnet restore 'MistoxWebsite.Server/MistoxWebsite.Server.csproj' RUN dotnet restore 'MistoxWebsite.Server/MistoxWebsite.Server.csproj'
+4 -1
View File
@@ -24,4 +24,7 @@ Store Catalog
Add to cart wraps text when screen is too small Add to cart wraps text when screen is too small
Program Program
Probably need to turn on cors at some point Probably need to turn on cors at some point
ProductController
No way to download products
-2
View File
@@ -1,2 +0,0 @@
/* This file is needed for compile for some reason */
/* Dont delete this file */
@@ -0,0 +1,4 @@
<linker>
<assembly fullname="Newtonsoft.Json" preserve="all" />
<assembly fullname="MistoxWebsite.Client" preserve="all" />
</linker>
@@ -5,27 +5,25 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<!-- Without Optimizations 3.92MB Initial Download / 302Kb home page load after -->
<!-- All Optimizations Without AOT 3.27MB Initial Download / 302Kb home page load after -->
<!-- All Optimizations With AOT 7.44MB Initial Download / 303Kb home page load after-->
<!-- Angular with ASPNET backend 0.49Kb Inital Download / 0.49Kb home page load after -->
<PublishTrimmed>true</PublishTrimmed> <PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode> <TrimMode>link</TrimMode>
<RunAOTCompilation>true</RunAOTCompilation> <RunAOTCompilation>false</RunAOTCompilation>
<InvariantGlobalization>true</InvariantGlobalization> <InvariantGlobalization>true</InvariantGlobalization>
<BlazorEnableCompression>true</BlazorEnableCompression> <BlazorEnableCompression>true</BlazorEnableCompression>
<BlazorWebAssemblyEnableLinking>true</BlazorWebAssemblyEnableLinking>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile> <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<StripSymbols>true</StripSymbols> <StripSymbols>true</StripSymbols>
</PropertyGroup> </PropertyGroup>
<!-- Retain important files on trimming -->
<ItemGroup> <ItemGroup>
<None Remove="App.razor.css" /> <TrimmerRootDescriptor Include="ILLink.Descriptors.xml" />
</ItemGroup>
<ItemGroup>
<Content Include="App.razor.css">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup> </ItemGroup>
<!-- Packages -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
@@ -33,6 +31,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<!-- Project References -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MistoxWebsite.Shared\MistoxWebsite.Shared.csproj" /> <ProjectReference Include="..\MistoxWebsite.Shared\MistoxWebsite.Shared.csproj" />
</ItemGroup> </ItemGroup>
@@ -34,10 +34,21 @@
} }
public async Task TrySendCode() { public async Task TrySendCode() {
Result = "Waiting on response from server"; MailAddress addr = new MailAddress(Email);
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/account/sendresetpassword", new Account(){ Email = Email }); if ( addr.Address != Email ){
Result = await TestLogin.Content.ReadAsStringAsync(); Result = "Enter a valid email address";
}else{
Result = "";
}
if (string.IsNullOrEmpty(Result)){
Dictionary<string, string> formData = new Dictionary<string, string>{
{ "Email", Email }
};
Result = "Waiting on response from server";
HttpResponseMessage TestLogin = await Http.PostAsync("api/account/sendresetpassword", new FormUrlEncodedContent(formData) );
Result = await TestLogin.Content.ReadAsStringAsync();
}
base.StateHasChanged();
} }
} }
@@ -54,34 +54,36 @@
Loading = "Waiting for login response from server"; Loading = "Waiting for login response from server";
ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL; ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL;
ErrorMsgs = new List<string>(); ErrorMsgs = new List<string>();
if( string.IsNullOrEmpty(UserName) ) {
if( !string.IsNullOrEmpty(UserName) ) {
if( !string.IsNullOrEmpty(Password) ) {
if (Password.Length >= 6 ) {
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/account/login", new MistoxWebsite.Shared.Account(){ UserName = UserName, PasswordHash = Password, EmailVerified = StayLoggedIn });
string result = await TestLogin.Content.ReadAsStringAsync();
Account? user = JsonConvert.DeserializeObject<Account>(result);
if (user == null ) {
ErrorMsgs.Add("No response from the server");
base.StateHasChanged();
return;
}
if ( string.IsNullOrEmpty(user.Error) ) {
ErrorMsgs.Add("Login Success");
Nav.NavigateTo("/", true);
} else {
ErrorMsgs.Add(user.Error);
}
Loading = "";
} else {
ErrorMsgs.Add("Password must be at least 6 Characters long");
}
} else {
ErrorMsgs.Add("The 'password' field is required");
}
} else{
ErrorMsgs.Add("The 'username' field is required"); ErrorMsgs.Add("The 'username' field is required");
} }
if( string.IsNullOrEmpty(Password) ) {
ErrorMsgs.Add("The 'password' field is required");
}
if (Password.Length < 6 ) {
ErrorMsgs.Add("Password must be at least 6 Characters long");
}
if (ErrorMsgs.Count == 0){
Dictionary<string, string> formData = new Dictionary<string, string>{
{ "UserName", UserName },
{ "PasswordHash", Password },
{ "StayLoggedIn", StayLoggedIn.ToString() }
};
HttpResponseMessage TestLogin = await Http.PostAsync("api/account/login", new FormUrlEncodedContent(formData) );
string result = await TestLogin.Content.ReadAsStringAsync();
Account? user = JsonConvert.DeserializeObject<Account>(result);
if (user == null ) {
ErrorMsgs.Add("No response from the server");
base.StateHasChanged();
return;
}
if ( string.IsNullOrEmpty(user.Error) ) {
ErrorMsgs.Add("Login Success");
Nav.NavigateTo(ReturnURL, true);
} else {
ErrorMsgs.Add(user.Error);
}
}
Loading = ""; Loading = "";
base.StateHasChanged(); base.StateHasChanged();
} }
@@ -52,63 +52,41 @@
} }
} }
public bool CheckEmail(string email ) {
int ATcount = 0;
int DOTcount = 0;
char[] cmail = email.ToArray();
foreach(char cur in cmail ) {
if(cur == '@' ) {
ATcount += 1;
}else if(cur == '.' ) {
DOTcount += 1;
}
}
if (ATcount == 1 && DOTcount >= 1 ) {
return true;
}
return false;
}
public async Task TryRegister() { public async Task TryRegister() {
Loading = "Waiting for a response from the server"; Loading = "Waiting for a response from the server";
ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL; ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL;
ErrorMsgs = new List<string>(); ErrorMsgs = new List<string>();
MailAddress addr = new MailAddress(Email);
if ( !string.IsNullOrEmpty(Email) ){ if ( addr.Address != Email ){
if( CheckEmail( Email ) ) { ErrorMsgs.Add("Please check your email address");
if( !string.IsNullOrEmpty(UserName) ) { }
if( !string.IsNullOrEmpty(Password) ) { if (Password.Length < 6 ) {
if (Password.Length >= 6 ) { ErrorMsgs.Add("Password must be at least 6 Characters long");
HttpResponseMessage TestRegister = await Http.PostAsJsonAsync("api/account/register", new Account(){ }
UserName = UserName, if( string.IsNullOrEmpty(UserName) ) {
Email = Email, ErrorMsgs.Add("The 'username' field is required");
PasswordHash = Password, }
EmailVerified = false, if( string.IsNullOrEmpty(Password) ) {
}); ErrorMsgs.Add("The 'password' field is required");
Account? user = await TestRegister.Content.ReadFromJsonAsync<Account>(); }
if ( string.IsNullOrEmpty(user?.Error) ) { if ( string.IsNullOrEmpty(Email) ){
ErrorMsgs.Add("Register Success");
Nav.NavigateTo("/", true);
} else {
ErrorMsgs.Add( user.Error );
}
Loading = "";
}else{
ErrorMsgs.Add("Password must be at least 6 Characters long");
}
}else{
ErrorMsgs.Add("The 'password' field is required");
}
}else{
ErrorMsgs.Add("The 'username' field is required");
}
}else{
ErrorMsgs.Add("Please check your email address");
}
}else{
ErrorMsgs.Add("The 'email' field is required"); ErrorMsgs.Add("The 'email' field is required");
} }
if (ErrorMsgs.Count == 0){
Dictionary<string, string> formData = new Dictionary<string, string>{
{ "Email", Email },
{ "UserName", UserName },
{ "PasswordHash", Password },
};
HttpResponseMessage TestRegister = await Http.PostAsync("api/account/register", new FormUrlEncodedContent(formData) );
Account? user = await TestRegister.Content.ReadFromJsonAsync<Account>();
if ( string.IsNullOrEmpty(user?.Error) ) {
ErrorMsgs.Add("Register Success");
Nav.NavigateTo(ReturnURL, true);
} else {
ErrorMsgs.Add( user.Error );
}
}
Loading = ""; Loading = "";
base.StateHasChanged(); base.StateHasChanged();
} }
@@ -38,7 +38,6 @@
[Parameter] [Parameter]
[SupplyParameterFromQuery] [SupplyParameterFromQuery]
public string ResetPwd { get; set; } = ""; public string ResetPwd { get; set; } = "";
public string NewPassword{ get; set; } = ""; public string NewPassword{ get; set; } = "";
public string RepeatPassword{ get; set; } = ""; public string RepeatPassword{ get; set; } = "";
@@ -52,24 +51,29 @@
protected async Task TryChange() { protected async Task TryChange() {
Result = "Waiting on response from server"; Result = "Waiting on response from server";
if (NewPassword.Length >= 6){ if (NewPassword != RepeatPassword){
if (NewPassword == RepeatPassword){ Result = "Passwords must match";
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/account/resetpassword", new Account(){ UserName = UserName, PasswordHash = NewPassword, Error = ResetPwd }); }
string result = await TestLogin.Content.ReadAsStringAsync(); if (NewPassword.Length < 6){
bool success = result == "true" ? true : false;
if (success){
Result = "Password changed successfully";
Thread.Sleep(2000);
Nav.NavigateTo("/", true);
}else{
Result = "Something is wrong";
}
}else{
Result = "Passwords must match";
}
}else{
Result = "Password must be at least 6 Characters long"; Result = "Password must be at least 6 Characters long";
} }
if (string.IsNullOrEmpty(Result)){
Dictionary<string, string> formData = new Dictionary<string, string>{
{ "UserName", UserName },
{ "NewPassword", NewPassword },
{ "ResetToken", ResetPwd },
};
HttpResponseMessage TestLogin = await Http.PostAsync("api/account/resetpassword", new FormUrlEncodedContent(formData) );
string result = await TestLogin.Content.ReadAsStringAsync();
bool success = result == "true" ? true : false;
if (success){
Result = "Password changed successfully";
Thread.Sleep(2000);
Nav.NavigateTo("/", true);
}else{
Result = "Something is wrong";
}
}
} }
} }
@@ -26,7 +26,11 @@
public string Result{ get; set; } = ""; public string Result{ get; set; } = "";
protected override async Task OnInitializedAsync() { protected override async Task OnInitializedAsync() {
HttpResponseMessage Query = await Http.PostAsJsonAsync("api/account/verifyemail", new Account(){ UserName = UserName, PasswordHash = Guid }); Dictionary<string, string> formData = new Dictionary<string, string>{
{ "UserName", UserName },
{ "EmailToken", Guid },
};
HttpResponseMessage Query = await Http.PostAsync("api/account/verifyemail", new FormUrlEncodedContent(formData) );
bool Answer = await Query.Content.ReadFromJsonAsync<bool>(); bool Answer = await Query.Content.ReadFromJsonAsync<bool>();
if (Answer == true ) { if (Answer == true ) {
Result = "Verified Email Successfully"; Result = "Verified Email Successfully";
@@ -41,7 +41,7 @@
@code { @code {
public MistoxWebsite.Shared.Account? _account = null; public MistoxWebsite.Shared.Database.Account? _account = null;
public int MaxFailedLogin = 0; public int MaxFailedLogin = 0;
public bool FailedLoginToggle = false; public bool FailedLoginToggle = false;
@@ -49,18 +49,19 @@
public async Task SubmitLoginLock() { public async Task SubmitLoginLock() {
if (_account != null ) { if (_account != null ) {
_account.SiteData.FailedPasswordLock = FailedLoginToggle; Dictionary<string, string> formData = new Dictionary<string, string>{
_account.SiteData.PasswordAttempts = MaxFailedLogin; { "UserName", _account.UserName },
_account.PasswordHash = ""; { "AccountLock", FailedLoginToggle.ToString() },
HttpResponseMessage SendVerifyEmail = await Http.PostAsJsonAsync("api/account/toggleAccountLock", _account); };
LoginCounterResult = await SendVerifyEmail.Content.ReadAsStringAsync(); HttpResponseMessage Query = await Http.PostAsync("api/account/toggleAccountLock", new FormUrlEncodedContent(formData) );
LoginCounterResult = await Query.Content.ReadAsStringAsync();
} }
} }
protected override async Task OnInitializedAsync() { protected override async Task OnInitializedAsync() {
HttpResponseMessage x = await Http.PostAsync("api/account/get", new StringContent("")); HttpResponseMessage x = await Http.PostAsync("api/account/get", new StringContent(""));
string body = await x.Content.ReadAsStringAsync(); string body = await x.Content.ReadAsStringAsync();
_account = JsonConvert.DeserializeObject<MistoxWebsite.Shared.Account>(body); _account = JsonConvert.DeserializeObject<MistoxWebsite.Shared.Database.Account>(body);
if (_account != null){ if (_account != null){
FailedLoginToggle = _account.SiteData.FailedPasswordLock; FailedLoginToggle = _account.SiteData.FailedPasswordLock;
MaxFailedLogin = _account.SiteData.PasswordAttempts; MaxFailedLogin = _account.SiteData.PasswordAttempts;
@@ -73,8 +74,11 @@
public async Task SendVerifyEmail() { public async Task SendVerifyEmail() {
if (_account != null){ if (_account != null){
HttpResponseMessage SendVerifyEmail = await Http.PostAsJsonAsync("api/account/sendverifyemail", new MistoxWebsite.Shared.Account(){ UserName = _account.UserName }); Dictionary<string, string> formData = new Dictionary<string, string>{
bool result = await SendVerifyEmail.Content.ReadFromJsonAsync<bool>(); { "UserName", _account.UserName },
};
HttpResponseMessage Query = await Http.PostAsync("api/account/sendverifyemail", new FormUrlEncodedContent(formData) );
bool result = await Query.Content.ReadFromJsonAsync<bool>();
if (result == true ) { if (result == true ) {
EmailSentResult = "Email Sent"; EmailSentResult = "Email Sent";
} else { } else {
@@ -108,8 +112,13 @@
return; return;
} }
if (_account != null){ if (_account != null){
HttpResponseMessage TryChangePassword = await Http.PostAsJsonAsync("api/account/changepassword", new MistoxWebsite.Shared.Account(){ UserName = _account.UserName, PasswordHash = CurPass, Error = NewPass1 }); Dictionary<string, string> formData = new Dictionary<string, string>{
bool resultText = await TryChangePassword.Content.ReadFromJsonAsync<bool>(); { "UserName", _account.UserName },
{ "OldPassword", CurPass },
{ "NewPassword", NewPass1 }
};
HttpResponseMessage Query = await Http.PostAsync("api/account/changepassword", new FormUrlEncodedContent(formData) );
bool resultText = await Query.Content.ReadFromJsonAsync<bool>();
if (resultText == true ) { if (resultText == true ) {
PasswordErrorText = "Password changed successfully"; PasswordErrorText = "Password changed successfully";
} else { } else {
@@ -5,10 +5,6 @@
margin-bottom: 1.4rem; margin-bottom: 1.4rem;
} }
.title-frame{
}
.title-frame h1 input{ .title-frame h1 input{
padding-left: 1.4rem; padding-left: 1.4rem;
padding-top: 0.4rem; padding-top: 0.4rem;
@@ -1,6 +1,3 @@
body { .lower-frame{
}
.lower-frame{
padding: 1.4rem; padding: 1.4rem;
} }
@@ -119,12 +119,12 @@
} }
async Task confirmDeleteAccount() { async Task confirmDeleteAccount() {
HttpResponseMessage Delete = await Http.PostAsJsonAsync( "api/account/delete", new MistoxWebsite.Shared.Account(){ Dictionary<string, string> formData = new Dictionary<string, string>{
ID = Statics.User.ID, { "UserName", Statics.User.UserName },
UserName = Statics.User.Email, { "Password", Password },
PasswordHash = Password };
}); HttpResponseMessage Query = await Http.PostAsync("api/account/delete", new FormUrlEncodedContent(formData) );
string result = await Delete.Content.ReadAsStringAsync(); string result = await Query.Content.ReadAsStringAsync();
bool status = result == "true" ? true : false; bool status = result == "true" ? true : false;
if (status){ if (status){
await Http.PostAsync("api/account/logout", new StringContent("")); await Http.PostAsync("api/account/logout", new StringContent(""));
@@ -6,28 +6,11 @@
<div class="Big-Div"> <div class="Big-Div">
<div id="DirTree"> <div id="DirTree">
@if (output != null ) {
<ExplorerChild Title=@output.Path Children=output.Children PartialPath="\"></ExplorerChild>
}
</div> </div>
<span>@ErrorTxt</span>
</div> </div>
@code{ @code{
public string ErrorTxt = "";
public DirObj? output = null;
protected override async void OnInitialized() {
try {
byte[] resultBody = await (await Http.PostAsync( "api/product/showdownloads", new StringContent("") )).Content.ReadAsByteArrayAsync();
string JsonData = Encoding.UTF8.GetString(resultBody);
output = JsonConvert.DeserializeObject<DirObj>( JsonData );
base.StateHasChanged();
} catch( Exception e ) {
ErrorTxt = "Error : " + e.ToString();
}
}
} }
@@ -1,283 +0,0 @@
@page "/resume/derek"
<div class="PopOutFrame">
<div class="Content" style="height: 170px !important;">
<div class="ImgFrame">
<img class="Picture" src="/img/ResumeFace.jpg" />
</div>
<div class="NameFrame">
<h1>Derek Holloway</h1>
<h1>Owner and sole developer</h1>
</div>
<div class="ContactFrame">
<h1>derek@mistox.net</h1>
</div>
</div>
<div class="PopOutHr"></div>
</div>
<div class="PopOutFrame">
<div class="Content">
<h1 style="margin: 0; margin-bottom: 5px;">Work Experience</h1>
<!-- NAVWAR Assistant Contract Tech. Rep. -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(1); }" @onmouseleave="()=>{ MouseLeave(1); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">NAVWAR Assistant Contract Tech. Rep.</h2>
<h3 class="jobSub">Redhorse corp. - San Diego, CA</h3>
<h3 class="jobSub">888-445-8010</h3>
<h3 class="jobSub">February 2022 - Today</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle1">
<div class="paddedcell">
<ul>
<li><span>The first line of support for end users</span></li>
<li><span>Fix or escalate issues as required to the correct authority for resolution</span></li>
<li><span>The ACTR performs routine office IT functions managing mechanical and printer supplies</span></li>
<li><span>Assisting with connectivity to and troubleshooting networked systems and Video Teleconferences</span></li>
<li><span>This includes setting up new accounts</span></li>
<li><span>Managing users accesses</span></li>
<li><span>Uses the building badging system to grant access to appropriate personnel Assist with transition to Office 365 and Navy Flank Speed as required.</span></li>
<li><span>Leading role on Junior level tasks/projects</span></li>
<li><span>Supports the customer performing moderately complex tasks on a routine basis.</span></li>
<br />
<li><span>Project manager for NAVWAR tech refresh</span></li>
<li><span>Replacing 4010+ computers within a 1 year time-frame</span></li>
<li><span>Manage warranties with HP Federal</span></li>
<li><span>Coordinating orders and returns for old leased hardware</span></li>
</ul>
</div>
</div>
</div>
<!-- NMCI Field Service -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(2); }" @onmouseleave="()=>{ MouseLeave(2); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">NMCI Field Service Technitian</h2>
<h3 class="jobSub">Super Systems Inc - San Diego, CA</h3>
<h3 class="jobSub">757-399-3000 - info@supersystemsinc.com</h3>
<h3 class="jobSub">June 2021 - Feburary 2022</h3>
<br />
<h3 class="jobSub">Ohm Systems, Inc - San Diego, CA</h3>
<h3 class="jobSub">215-675-2766 - info@ohmsysinc.com</h3>
<h3 class="jobSub">February 2021 - June 2021</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle2">
<div class="paddedcell">
<ul>
<li><span>Performed layer 1 network troubleshooting; windows netsh firewalls, 802.1x Authentication issues, and cable and port issues.</span></li>
<li><span>Performed hardware troubleshooting and replacements; replacing laptop motherboards, displays, cpus, ram, and peripherals.</span></li>
<li><span>Performed software troubleshooting using event viewer, task manager, and command prompt. By uninstalling and reinstalling or reconfiguring.</span></li>
<li><span>Worked with network printers, voip's, and video telecommunication devices.</span></li>
<li><span>Worked face to face with customers, managing my time between calls and work orders.</span></li>
</ul>
</div>
</div>
</div>
<!-- NMCI Help Desk -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(3); }" @onmouseleave="()=>{ MouseLeave(3); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">NMCI Help Desk Technitian</h2>
<h3 class="jobSub">Apex Systems - Coronado, CA</h3>
<h3 class="jobSub">619-757-1646</h3>
<h3 class="jobSub">September 2019 - September 2020</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle3">
<div class="paddedcell">
<ul>
<li><span>Performed remote troubleshooting: windows cmd, winrs, rdc, winrs. Fixing software issues</span></li>
<li><span>Performed administrative Tasks: create active directory accounts and verifying identity to unlock accounts.</span></li>
<li><span>Walked users through diagnosing network issues over the phone or escalated issue to field services</span></li>
<li><span>Fixed users email issues; server mappings, proxy email addresses, shared emails, and outlook related issues.</span></li>
<li><span>Moved users profiles from one domain to another and moved users files along with it.</span></li>
<li><span>Fixed account related issues; id to user mismatch and wrong display name.</span></li>
</ul>
</div>
</div>
</div>
<!-- Warehouse Reciever -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(4); }" @onmouseleave="()=>{ MouseLeave(4); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">Warehouse receiver</h2>
<h3 class="jobSub">Ababa Bolt - El Cajon, CA</h3>
<h3 class="jobSub">619-440-1781</h3>
<h3 class="jobSub">May 2019 - August 2019</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle4">
<div class="paddedcell">
<ul>
<li><span>Verified that all parts came in off the packing slip</span></li>
<li><span>Rejected parts that were damaged and sent back to manufacturer</span></li>
<li><span>Sort and add parts into the tracking system</span></li>
<li><span>Put parts away in appropriate areas</span></li>
</ul>
</div>
</div>
</div>
<!-- Warehouse packer -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(5); }" @onmouseleave="()=>{ MouseLeave(5); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">Warehouse packer</h2>
<h3 class="jobSub">Ababa Bolt - El Cajon, CA</h3>
<h3 class="jobSub">619-440-1781</h3>
<h3 class="jobSub">October 2017 - September 2018</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle5">
<div class="paddedcell">
<ul>
<li><span>Find which parts need more stock on shelves</span></li>
<li><span>Split out parts by count or weight</span></li>
<li><span>Verified and marked appropriate compliance such as RoHS</span></li>
<li><span>Operated forklifts and scissor lifts</span></li>
</ul>
</div>
</div>
</div>
<!-- California Army National Guard -->
<div class="capsule" @onmouseenter="()=>{ MouseEnter(6); }" @onmouseleave="()=>{ MouseLeave(6); }">
<div class="Skills" style="height: calc(100% - 40px);">
<div class="paddedcell">
<h2 class="jobTitle">Motor Vehicle Trasport Operator</h2>
<h3 class="jobSub">California Army National Guard</h3>
<h3 class="jobSub">760-607-8574</h3>
<h3 class="jobSub">September 2018 - Today</h3>
<h3 class="jobSub">Rank/Grade - Specialist / E-4</h3>
<span></span>
</div>
</div>
<div class="SkillsContent colored" style="height: calc(100% - 40px); @ContentStyle6">
<div class="paddedcell">
<ul>
<li><span>88M - Motor Transport Operator</span></li>
<li><span>Neccesarry to hold secret clearence and keep all cyber security certifications up to date.</span></li>
<li><span>June 2020 - Protected and defended the Los Alamitos police department and Six Flags Magic Mountain during the rios cause by BLM movement that caused rioting in the streets.</span></li>
<li><span>September 2020 - Worked with Cal-Fire in Chico, CA and Alderpoint, CA to cut fire lines to prevent the spread of fires during the <a href="https://en.wikipedia.org/wiki/August_Complex_fire">August Fire Complex</a> fires.</span></li>
</ul>
</div>
</div>
</div>
</div>
<div class="PopOutHr"></div>
</div>
<div class="PopOutFrame" @onmouseenter="()=>{ MouseEnter(7); }" @onmouseleave="()=>{ MouseLeave(7); }">
<div class="Content">
<div class="Skills">
<h1>Development</h1>
<ul>
<li class="LIPrime">C#</li>
<li>Mono</li>
<li>ASP.NET</li>
<li>ASP Core</li>
<li>Blazor Web Assembly</li>
<li>Windows Forms Apps</li>
<li class="LIPrime">C++</li>
<li>Arm Embedded</li>
<li>Raspberry Pi</li>
<li class="LIPrime">Database</li>
<li>MySql</li>
<li>MsSql</li>
<li>LINQ</li>
<li class="LIPrime">Game Engines</li>
<li>Godot</li>
<li>Unity 3D</li>
<li>Solar 2D</li>
<li class="LIPrime">Web Development</li>
<li>Front End</li>
<li>Back End</li>
<li>Interfaces</li>
<li>Square Payment API</li>
<li>Rest Clients</li>
</ul>
</div>
<div class="SkillsContent" style="@ContentStyle7">
<div class="paddedcell textSection">
<a href="https://github.com/reverseslayer/LUADNS-DDNS">LUADNS-DDNS : https://github.com/reverseslayer/LUADNS-DDNS</a><br />
<span>This is a service that I made for </span><a href="http://luadns.com">luadns.com</a><span>. This allows free ddns by simply changing the dns name servers to luadns nameservers and running this program as a service. This was made for linux but shoud easily run on windows.</span><br /><br />
</div>
<div class="paddedcell textSection">
<a href="https://github.com/reverseslayer/MistoxServer">Mistox-Server : https://github.com/reverseslayer/MistoxServer</a><br />
<span>This is a </span><a href="https://en.wikipedia.org/wiki/UDP_hole_punching">UDP Hole Punched</a><span> server; that allows clients to direct connect over the wan without opening ports. It works by having a dedicated TCP server that routes the UDP connections directly to each client. Based on this </span><a href="img/IdeaMap.jpg">Idea map.</a>
</div>
<div class="paddedcell textSection">
<a href="https://www.mistox.net">Mistox.net : https://mistox.net</a><br />
<span>Mistox.net is all done by me. The DNS is hosted by <a href="https://domains.google.com">domains.google.com</a> and pushed through <a href="http://luadns.com">luadns.com</a> name server so that I can run LUA-DDNS a service I made to reach other places that behind ddns. The webservers are hosted by <a href="https://www.vultr.com/">vultr.com</a> and the entire stack is built on <a href="https://dotnet.microsoft.com/en-us/apps/aspnet">asp.net</a>. The payment services are run through <a href="https://stripe.com/">stripe-payments</a>. I built this as a side project but its slowly becoming more and more something that I would like to be able to share things that I make; paid or free.</span>
</div>
</div>
</div>
<div class="PopOutHr" style="margin-bottom: 30px;"></div>
</div>
@code {
string ContentStyle1 = "";
string ContentStyle2 = "";
string ContentStyle3 = "";
string ContentStyle4 = "";
string ContentStyle5 = "";
string ContentStyle6 = "";
string ContentStyle7 = "";
void MouseEnter( int frameNumber ) {
if (frameNumber == 1){
ContentStyle1 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 2){
ContentStyle2 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 3){
ContentStyle3 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 4){
ContentStyle4 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 5){
ContentStyle5 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 6){
ContentStyle6 = "right: 0px;";
base.StateHasChanged();
}else if (frameNumber == 7){
ContentStyle7 = "right: 0px;";
base.StateHasChanged();
}
}
void MouseLeave( int frameNumber ){
if (frameNumber == 1){
ContentStyle1 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 2){
ContentStyle2 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 3){
ContentStyle3 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 4){
ContentStyle4 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 5){
ContentStyle5 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 6){
ContentStyle6 = "right: -700px;";
base.StateHasChanged();
}else if (frameNumber == 7){
ContentStyle7 = "right: -700px;";
base.StateHasChanged();
}
}
}
@@ -1,191 +0,0 @@
* {
--popout-shadow-color-left: #8c8c8c;
--popout-shadow-color-bottom: #595959;
--popout-background-color: #004262;
--frame-background-color: #005662;
--frame-title-color: #ffffff;
--job-title-color: #f35100;
--job-sub-color: #c85c00;
--text-color: #dddddd;
--link-color: #4cff00;
--link-visited-color: #73ac5b;
--skills-bg-color: #c85c00;
--skills-text-color: #000;
--skills-text-shadow-color: #f35100;
--skills-prime-background-color: #972500;
--skills-prime-text-color: #fff;
}
body {
position: relative;
}
h1 {
color: var(--frame-title-color);
}
span {
color: var(--text-color);
}
.PopOutFrame {
position: relative;
background-color: white;
width: 100%;
max-width: 1080px;
margin: 15px auto 0 auto;
content: "";
}
.PopOutFrame::before {
position: absolute;
width: 6px;
left: -6px;
margin-top: 6px;
background: var(--popout-shadow-color-left);
content: "";
display: block;
transform: skew(0deg, -61deg);
height: 100%;
}
.PopOutHr {
width: 100%;
float: left;
}
.PopOutHr::after {
position: absolute;
width: 100%;
margin-top: 0px;
bottom: -11px;
left: -3px;
background: var(--popout-shadow-color-bottom);
content: "";
display: block;
transform: skew(-31deg, 0deg);
height: 11px;
}
.Content{
position: relative;
padding: 15px;
overflow: hidden;
background-color: var(--popout-background-color);
}
.ImgFrame {
float: left;
}
.Picture{
width: 140px;
height: 140px;
}
.NameFrame {
float: left;
width: calc(100% - 510px);
margin-left: 20px;
}
.ContactFrame {
float: left;
width: 300px;
height: 140px;
}
.ContactFrame h1{
font-size: 30px;
}
.SkillsContent {
position: relative;
float: left;
height: 100%;
width: 60%;
transition-duration: 2s;
right: -700px;
}
.SkillsContent :link {
color: var(--link-color);
}
.SkillsContent :visited{
color: var(--link-visited-color) !important;
}
.paddedcell {
padding: 10px 5px;
padding-bottom: 0;
margin-bottom: 5px;
}
.colored {
background-color: var(--frame-background-color);
}
.paddedcell br{
display: block;
margin: 10px 0;
content: "";
}
.Skills {
float: left;
height: 100%;
width: 40%;
transition-duration: 2s;
}
.Skills h1{
margin: 5px;
}
.Skills ul{
font-size: 14px;
list-style: none;
margin: 0;
padding: 0 5px;
}
.Skills li {
float: left;
padding: 4px 6px;
margin: 0 4px 4px 0;
background-color: var(--skills-bg-color);
text-shadow: 0 1px 1px var(--skills-text-shadow-color);
color: var(--skills-text-color);
}
.LIPrime{
text-shadow: none;
background-color: var(--skills-prime-background-color) !important;
color: var(--skills-prime-text-color) !important;
clear: left;
}
.capsule {
margin-top: 15px;
overflow: hidden;
border: solid var(--frame-background-color);
border-radius: 5px;
}
.jobTitle {
margin: 0;
color: var(--job-title-color);
}
.jobSub {
margin: 0;
color: var(--job-sub-color);
font-size: 15px;
}
.textSection {
margin-top: 15px;
background-color: var(--frame-background-color);
border-radius: 5px;
}
@@ -1,38 +0,0 @@
<button type="button" class="collapsible" @onclick=OpenCollapseable>@Title</button>
<ul class="dropdown-content" style="display : @collapseStyle;">
@if (Children != null){
@foreach(var Cur in Children){
<li>
@if(Cur.Type == FileType.Directory) {
<ExplorerChild Title=@Cur.Path Children=Cur.Children PartialPath=@(PartialPath+Cur.Path) ></ExplorerChild>
} else {
<button type="button" @onclick=@((e)=>{ Download(Cur.Path); })>@Cur.Path</button>
}
</li>
}
}
</ul>
@code{
[Parameter]
public string Title{ get; set; } = "";
[Parameter]
public DirObj[]? Children{ get; set; }
[Parameter]
public string PartialPath{ get; set; } = "";
public string collapseStyle = "none";
void OpenCollapseable() {
if (collapseStyle == "block") {
collapseStyle = "none";
} else {
collapseStyle = "block";
}
}
void Download(string Path) {
Nav.NavigateTo( "api/product/download?Product=" + PartialPath + Path, true );
}
}
@@ -7,7 +7,7 @@
@code { @code {
protected override void OnInitialized() { protected override void OnInitialized() {
if (Statics.User.ID == -1 ) { if (Statics.User.ID == -1 ) {
Nav.NavigateTo( "/account/login?ReturnURL=Null" ); Nav.NavigateTo( "/account/login" );
} }
} }
} }
-164
View File
@@ -1,164 +0,0 @@
@page "/snake"
<div @onkeydown="OnKeyDown" tabindex="0" style="background-color: #333;" >
<h1 id="Score" style="width: 100%; text-align: center; color:#fff;">Score : 0</h1>
<div id="BODY" style="position: relative; margin-bottom: 5px; margin-left: 50%; right: 300px; background-color: #666; width: 600px; height: 600px;">
<!-- Game -->
@foreach(SnakePart part in SnakeParts ) {
<div style="position: absolute; left: @part.getX(); top: @part.getY(); width: 10px; height: 10px; background-color: #111; border: 0; padding: 0; margin: 0;"></div>
}
<div style="position: absolute; left: @Collectable.getX(); top: @Collectable.getY(); width: 10px; height: 10px; background-color: #f00; border: 0; padding: 0; margin: 0;"></div>
<!-- End Game -->
</div>
<div id="PauseScreen"style="position: relative; display: none; width: 500px; margin-left: 50%; right: 250px;">
<h2 style="text-align: center; color: #f00; ">Game Paused</h2>
</div>
<div style="margin: 0 40px; width: calc(100% - 80px); background-color: #777;">
<h3 style="text-align: center; font-size: 25px; color: #0f0; padding: 0; margin: 0; border: 0;">LEADERBOARD</h3>
</div>
<hr style="margin: 0px; width:calc(100% - 82px);" />
<div id="Scoreboard" style="margin: 0 40px; width: calc(100% - 80px); background-color: #777;"></div>
<h3 style="position: absolute; right: 10px; bottom: 3px; color: #fff;">Designed by Derek in California</h3>
</div>
@code {
struct SnakePart {
public int X{ get; set; }
public int Y{ get; set; }
public int Hue{ get; set; }
public string getX() {
return X * 10 + "px";
}
public string getY() {
return Y * 10 + "px";
}
}
enum Direction {
Up,
Down,
Left,
Right
}
List<SnakePart> SnakeParts = new List<SnakePart>(){
new SnakePart{ X = 5, Y = 10 }, // Tail
new SnakePart{ X = 6, Y = 10 },
new SnakePart{ X = 7, Y = 10 },
new SnakePart{ X = 8, Y = 10 } // Head
};
int Score = 0;
bool Paused = false;
Direction SnakeDirection = Direction.Right;
int FrameRate = 1000 / 10;
int HueRate = 5;
SnakePart Collectable = new SnakePart{ X = 15, Y = 15 };
void OnKeyDown(KeyboardEventArgs e) {
Console.WriteLine(e.Key);
if (e.Key.ToLower() == "w" ) {
SnakeDirection = Direction.Up;
}else if (e.Key.ToLower() == "a" ) {
SnakeDirection = Direction.Left;
}else if (e.Key.ToLower() == "s" ) {
SnakeDirection = Direction.Down;
}else if (e.Key.ToLower() == "d" ) {
SnakeDirection = Direction.Right;
}
}
bool CheckSelfHit() {
foreach(SnakePart cur in SnakeParts ) {
if (SnakeParts[SnakeParts.Count-1].X == cur.X ) {
if (SnakeParts[SnakeParts.Count-1].Y == cur.Y ) {
return false;
}
}
}
return true;
}
bool CheckBounds() {
if (SnakeParts[SnakeParts.Count-1].X > 0 && SnakeParts[SnakeParts.Count-1].X < 50 ) {
if (SnakeParts[SnakeParts.Count-1].Y > 0 && SnakeParts[SnakeParts.Count-1].Y < 50 ) {
return true;
}
}
return false;
}
bool CheckCollectibleHit() { // return true if hit
if (SnakeParts[SnakeParts.Count-1].X == Collectable.X ) {
if (SnakeParts[SnakeParts.Count-1].Y == Collectable.Y ) {
return true;
}
}
return false;
}
void ResetCollectable() {
Collectable.X = new Random().Next(1, 40);
Collectable.Y = new Random().Next(1, 40);
}
void Die() {
}
void Update() {
// Get Next Position
if( SnakeDirection == Direction.Up ) {
SnakeParts.Add( new SnakePart {
X = SnakeParts[SnakeParts.Count-1].X,
Y = SnakeParts[SnakeParts.Count-1].Y - 1,
Hue = SnakeParts[SnakeParts.Count-1].Hue + HueRate
});
}else if (SnakeDirection == Direction.Right ) {
SnakeParts.Add( new SnakePart {
X = SnakeParts[SnakeParts.Count-1].X + 1,
Y = SnakeParts[SnakeParts.Count-1].Y,
Hue = SnakeParts[SnakeParts.Count-1].Hue + HueRate
});
}else if (SnakeDirection == Direction.Down ) {
SnakeParts.Add( new SnakePart {
X = SnakeParts[SnakeParts.Count-1].X,
Y = SnakeParts[SnakeParts.Count-1].Y + 1,
Hue = SnakeParts[SnakeParts.Count-1].Hue + HueRate
});
}else if (SnakeDirection == Direction.Left ) {
SnakeParts.Add( new SnakePart {
X = SnakeParts[SnakeParts.Count-1].X - 1,
Y = SnakeParts[SnakeParts.Count-1].Y,
Hue = SnakeParts[SnakeParts.Count-1].Hue + HueRate
});
}
if (CheckSelfHit() && CheckBounds() ) {
if( CheckCollectibleHit() ) {
Score += 1;
ResetCollectable();
} else {
SnakeParts.RemoveAt( 0 );
}
} else {
Die();
}
StateHasChanged();
}
protected override void OnInitialized() {
ResetCollectable();
var timer = new System.Threading.Timer((e) => {
if (!Paused){
Update();
}
}, null, 0, FrameRate );
}
}
@@ -4,7 +4,7 @@
<h3 class="cart-title">Cart</h3> <h3 class="cart-title">Cart</h3>
<div class="Spacer"> <div class="Spacer">
@foreach( MistoxWebsite.Shared.Cart obj in Statics.Carts ) { @foreach( MistoxWebsite.Shared.Database.Cart obj in Statics.Carts ) {
<div class="cart-item"> <div class="cart-item">
<h1>@getItem(obj.ProductID)?.Name</h1> <h1>@getItem(obj.ProductID)?.Name</h1>
@foreach( string cur in getItem( obj.ProductID )?.Images ) { @foreach( string cur in getItem( obj.ProductID )?.Images ) {
@@ -43,7 +43,7 @@
protected override void OnInitialized() { protected override void OnInitialized() {
total = 0; total = 0;
foreach( MistoxWebsite.Shared.Cart obj in Statics.Carts ) { foreach( MistoxWebsite.Shared.Database.Cart obj in Statics.Carts ) {
foreach( Product item in Statics.Products ) { foreach( Product item in Statics.Products ) {
if( obj.ProductID == item.ID ) { if( obj.ProductID == item.ID ) {
total = total + (item.Cost / 100f); total = total + (item.Cost / 100f);
@@ -99,7 +99,7 @@
public bool isOwned(int ID ) { public bool isOwned(int ID ) {
if(Statics.Owned.Count > 0 ) { if(Statics.Owned.Count > 0 ) {
foreach( MistoxWebsite.Shared.Receipt cur in Statics.Owned ) { foreach( MistoxWebsite.Shared.Database.Receipt cur in Statics.Owned ) {
if( cur.ProductID == ID ) { if( cur.ProductID == ID ) {
return true; return true;
} }
@@ -109,7 +109,7 @@
} }
public bool isInCart(int id) { public bool isInCart(int id) {
foreach( MistoxWebsite.Shared.Cart cur in Statics.Carts ) { foreach( MistoxWebsite.Shared.Database.Cart cur in Statics.Carts ) {
if (cur.ProductID == id ) { if (cur.ProductID == id ) {
return true; return true;
} }
@@ -118,7 +118,7 @@
} }
public async void onAddToCart( int ID ) { public async void onAddToCart( int ID ) {
MistoxWebsite.Shared.Cart item = new MistoxWebsite.Shared.Cart { MistoxWebsite.Shared.Database.Cart item = new MistoxWebsite.Shared.Database.Cart {
ProductID = ID, ProductID = ID,
AccountID = Statics.User.ID AccountID = Statics.User.ID
}; };
@@ -1,6 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using MistoxWebsite.Shared; using MistoxWebsite.Shared.DTO.Session;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace MistoxWebsite.Client.AuthState { namespace MistoxWebsite.Client.AuthState {
+1 -1
View File
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
namespace MistoxWebsite.Client.Statics { namespace MistoxWebsite.Client.Statics {
public class Statics { public class Statics {
+13 -9
View File
@@ -1,24 +1,28 @@
@using System.Net.Http @using System.Net.Http
@using System.Net.Http.Json @using System.Net.Http.Json
@using System.Net.Mail
@using System.Security.Claims
@using System.Security.Cryptography
@using System.Text
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using MistoxWebsite.Client
@using Microsoft.AspNetCore.Authorization @using MistoxWebsite.Client
@using Microsoft.AspNetCore.Components.Authorization
@using System.Security.Claims
@using MistoxWebsite.Client.AuthState @using MistoxWebsite.Client.AuthState
@using MistoxWebsite.Client.Statics @using MistoxWebsite.Client.Statics
@using System.Security.Cryptography
@using System.Text
@using MistoxWebsite.Shared
@using Newtonsoft.Json
@using MistoxWebsite.Client.Pages.Shared @using MistoxWebsite.Client.Pages.Shared
@using MistoxWebsite.Shared.Database
@using MistoxWebsite.Shared.DTO.Account
@using MistoxWebsite.Shared.DTO.Session
@using Newtonsoft.Json
@inject IJSRuntime JS @inject IJSRuntime JS
@inject NavigationManager Nav @inject NavigationManager Nav
@@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Shared;
using System.Security.Claims; using System.Security.Claims;
using MistoxWebsite.Server.Services; using MistoxWebsite.Server.Services;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using Microsoft.AspNetCore.Authentication.Cookies; using MistoxWebsite.Shared.Database;
using MistoxWebsite.Shared.DTO.Session;
namespace MistoxWebsite.Server.Controllers { namespace MistoxWebsite.Server.Controllers {
[ApiController] [ApiController]
@@ -12,29 +13,27 @@ namespace MistoxWebsite.Server.Controllers {
DatabaseService _accountContext; DatabaseService _accountContext;
EmailService _emailContext; EmailService _emailContext;
public AuthenticationController( DatabaseService DatabaseContext, EmailService emailContext ) { public AuthenticationController(DatabaseService DatabaseContext, EmailService emailContext) {
_accountContext = DatabaseContext; _accountContext = DatabaseContext;
_emailContext = emailContext; _emailContext = emailContext;
} }
// In Account -> References UserName / PasswordHash [Route("api/account/login")]
// Out Account
[Route( "api/account/login" )]
[HttpPost] [HttpPost]
public async Task<ActionResult<Account>> Login( [FromBody] Account request ) { public async Task<ActionResult<Account>> Login([FromForm] string UserName, [FromForm] string PasswordHash, [FromForm] bool StayLoggedIn) {
try { try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); Account? test = await _accountContext.GetAccount(UserName.ToLower());
if( test != null ) { if (test != null) {
if( test.EmailVerified == true ) { if (test.EmailVerified == true) {
if( test.SiteData.FailedPasswordLock ) { if (test.SiteData.FailedPasswordLock) {
if( test.SiteData.CurrentPasswordAttempts >= test.SiteData.PasswordAttempts ) { if (test.SiteData.CurrentPasswordAttempts >= test.SiteData.PasswordAttempts) {
return new Account() { Error = "Too many failed password attempts. Please reset your password" }; return new Account() { Error = "Too many failed password attempts. Please reset your password" };
} }
} }
if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) { if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) {
test.SiteData.CurrentPasswordAttempts = 0; test.SiteData.CurrentPasswordAttempts = 0;
await _accountContext.SetAccount( test ); await _accountContext.SetAccount(test);
AccountClaims aClaims = await getClaims(test.ID); AccountClaims aClaims = await getClaims(test.ID);
List<Claim> claims = new List<Claim>() { List<Claim> claims = new List<Claim>() {
@@ -48,64 +47,306 @@ namespace MistoxWebsite.Server.Controllers {
await HttpContext.SignInAsync( await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal( new ClaimsIdentity( claims, "Auth" ) ), new ClaimsPrincipal(new ClaimsIdentity(claims, "Auth")),
new AuthenticationProperties { new AuthenticationProperties {
ExpiresUtc = DateTime.UtcNow.AddYears(30), // Add 30 years with sliding on ExpiresUtc = DateTime.UtcNow.AddYears(30), // Add 30 years with sliding on
IsPersistent = request.EmailVerified, // Is set from the StayLoggedIn IsPersistent = StayLoggedIn, // Is set from the StayLoggedIn
} }
); );
return test; return test;
} else { }
else {
test.SiteData.CurrentPasswordAttempts += 1; test.SiteData.CurrentPasswordAttempts += 1;
await _accountContext.SetAccount( test ); await _accountContext.SetAccount(test);
return new Account() { Error = "Wrong password" }; return new Account() { Error = "Wrong password" };
} }
}else{ }
await SendVerify(test); else {
await SendVerify(test.UserName);
return new Account() { Error = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" }; return new Account() { Error = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" };
} }
} }
return new Account() { Error = "User doesn't exist" }; return new Account() { Error = "User doesn't exist" };
} catch( Exception ex ) { } catch (Exception ex) {
return new Account() { Error = ex.Message }; return new Account() { Error = ex.Message };
} }
} }
// In Account -> References UserName / PasswordHash [Route("api/account/session")]
// Out Account
[Route( "api/account/session" )]
[HttpPost] [HttpPost]
public async Task<ActionResult<Account>> LoginSession( [FromBody] Account request ) { public async Task<ActionResult<Account>> LoginSession([FromBody] Account request) {
try { try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) { if (test != null) {
if( request.PasswordHash == test.PasswordHash ) { if (request.PasswordHash == test.PasswordHash) {
return test; return test;
} else { }
else {
test.SiteData.CurrentPasswordAttempts += 1; test.SiteData.CurrentPasswordAttempts += 1;
await _accountContext.SetAccount( test ); await _accountContext.SetAccount(test);
return new Account() { Error = "Wrong password" }; return new Account() { Error = "Wrong password" };
} }
} }
return new Account() { Error = "User doesn't exist" }; return new Account() { Error = "User doesn't exist" };
} catch( Exception ex ) { } catch (Exception ex) {
return new Account() { Error = ex.Message }; return new Account() { Error = ex.Message };
} }
} }
// In Account [Route("api/account/claims")]
// Out List<String>
[Route( "api/account/claims" )]
[HttpPost] [HttpPost]
public async Task<ActionResult<AccountClaims>> Claims( [FromBody] Account Account ) { public async Task<ActionResult<AccountClaims>> Claims([FromBody] Account Account) {
AccountClaims claims = await getClaims(Account.ID); AccountClaims claims = await getClaims(Account.ID);
return claims; return claims;
} }
async Task<AccountClaims> getClaims( int AccountID ) { [Route("api/account/register")]
[HttpPost]
public async Task<ActionResult<Account>> Register([FromForm] string Email, [FromForm] string UserName, [FromForm] string PasswordHash) {
try {
if (await _accountContext.GetAccount(UserName.ToLower()) == null) {
if (await _accountContext.GetAccount(Email.ToLower()) == null) {
Account? created = new Account() {
UserName = UserName.ToLower(),
Email = Email.ToLower(),
EmailVerified = false,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash),
};
await _accountContext.NewAccount(created);
created = await _accountContext.GetAccount(Email.ToLower());
if (created != null) {
AccountClaims aClaims = await getClaims(created.ID);
List<Claim> claims = new List<Claim>() {
new Claim(ClaimTypes.Name, aClaims.UserName),
new Claim(ClaimTypes.Email, aClaims.Email),
new Claim("emailverified", aClaims.EmailVerified),
new Claim(ClaimTypes.Role, aClaims.Role),
new Claim("LockAccount", aClaims.FailedPasswordLock)
};
await SendVerify(created.UserName);
return created;
}
return new Account() { Error = "Unknown Error" };
}
else {
return new Account() { Error = "Email is already in use" };
}
}
else {
return new Account() { Error = "UserName is taken" };
}
} catch (Exception ex) {
Console.WriteLine("Error: " + ex.Message);
return new Account() { Error = ex.Message };
}
}
[Route("api/account/changepassword")]
[HttpPost]
public async Task<ActionResult<bool>> ChangePassword([FromForm]string UserName, [FromForm]string OldPassword, [FromForm]string NewPassword) {
try {
Account? test = await _accountContext.GetAccount(UserName.ToLower());
if (test != null) {
if (BCrypt.Net.BCrypt.Verify(OldPassword, test.PasswordHash)) {
test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
test.SiteData.CurrentPasswordAttempts = 0;
await _accountContext.SetAccount(test);
return true;
}
}
return false;
} catch {
return false;
}
}
[Route("api/account/toggleAccountLock")]
[HttpPost]
public async Task<ActionResult<string>> ToggleAccountLock([FromForm]string UserName, [FromForm]bool AccountLock) {
try {
Account? test = await _accountContext.GetAccount(UserName);
if (test != null) {
test.SiteData.FailedPasswordLock = AccountLock;
test.SiteData.CurrentPasswordAttempts = 0;
await _accountContext.SetAccount(test);
return "Account Lock Status Updated";
}
return "Unknown Error Occurred";
} catch (Exception ex) {
return ex.Message;
}
}
[Route("api/account/get")]
[HttpPost]
public async Task<ActionResult<Account?>> Get() {
try {
if (User.Identity != null && User.Identity.IsAuthenticated) {
string? email = User.FindFirstValue(ClaimTypes.Email);
if (!string.IsNullOrEmpty(email)) {
Account? test = await _accountContext.GetAccount(email);
if (test != null) {
return test;
}
}
}
return Ok();
} catch {
return Ok();
}
}
[Route("api/account/logout")]
[HttpPost]
public async Task Logout() {
await HttpContext.SignOutAsync();
}
[Route("api/account/sendverifyemail")]
[HttpPost]
public async Task<ActionResult<string>> SendVerify([FromForm]string UserName) {
try {
string key = "v" + UserName;
// Stop from sending multiple emails quickly
if (_emailContext._SentEmails.ContainsKey(key)) {
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
return "Cannot sent another verify email until 5 minutes has elapsed ";
}
else {
_emailContext._SentEmails.Remove(key);
}
}
Account? test = await _accountContext.GetAccount(UserName.ToLower());
if (test != null) {
test.SiteData.EmailToken = Guid.NewGuid().ToString();
await _accountContext.SetAccount(test);
string EmailContents = EmailService.VerifyEmailEmail;
EmailContents = Substitue(EmailContents, "@UserName", UserName);
EmailContents = Substitue(EmailContents, "@UserName", UserName);
EmailContents = Substitue(EmailContents, "@VerifyPassword", test.SiteData.EmailToken);
string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
_emailContext._SentEmails.Add(key, DateTime.Now);
return result;
}
return "Account not found";
} catch (Exception) {
return "The connection couldn't be established to the email server";
}
}
[Route("api/account/verifyemail")]
[HttpPost]
public async Task<ActionResult<bool>> VerifyEmail([FromForm]string UserName, [FromForm]string EmailToken) {
try {
Account? test = await _accountContext.GetAccount(UserName.ToLower());
if (test != null) {
if (test.SiteData.EmailToken == EmailToken) {
test.SiteData.EmailToken = "";
test.EmailVerified = true;
await _accountContext.SetAccount(test);
return true;
}
}
return false;
} catch {
return false;
}
}
[Route("api/account/sendresetpassword")]
[HttpPost]
public async Task<ActionResult<string>> ResetPassword([FromForm] string Email) {
try {
string key = "p" + Email.ToLower();
// Stop from sending multiple emails quickly
if (_emailContext._SentEmails.ContainsKey(key)) {
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
return "Cannot sent another reset requests until 5 minutes has elapsed";
}
else {
_emailContext._SentEmails.Remove(key);
}
}
Account? test = await _accountContext.GetAccount(Email.ToLower());
if (test != null) {
test.SiteData.EmailToken = Guid.NewGuid().ToString();
await _accountContext.SetAccount(test);
string EmailContents = EmailService.ResetPasswordEmail;
EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
EmailContents = Substitue(EmailContents, "@ResetPassWord", test.SiteData.EmailToken);
string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
_emailContext._SentEmails.Add(key, DateTime.Now);
return result;
}
return "Account Not Found";
} catch (Exception) {
return "The connection couldn't be established to the email server";
}
}
[Route("api/account/resetpassword")]
[HttpPost]
public async Task<ActionResult<bool>> ResetPwdVerify([FromForm] string UserName, [FromForm] string NewPassword, [FromForm] string ResetToken) {
try {
Account? test = await _accountContext.GetAccount(UserName.ToLower());
if (test != null && !string.IsNullOrEmpty(test.SiteData.EmailToken)) {
if (test.SiteData.EmailToken == ResetToken) {
test.SiteData.CurrentPasswordAttempts = 0;
test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
await _accountContext.SetAccount(test);
return true;
}
}
return false;
} catch {
return false;
}
}
[Route("api/account/delete")]
[HttpPost]
public async Task<ActionResult<bool>> delete([FromForm]string UserName, [FromForm]string Password) {
try {
Account? test = await _accountContext.GetAccount(UserName.ToLower());
if (test != null) {
if (BCrypt.Net.BCrypt.Verify(Password, test.PasswordHash)) {
await _accountContext.DeleteAccount(test);
return true;
}
}
return false;
} catch {
return false;
}
}
// Helper Functions
string Substitue(string message, string subString, string Replacement) {
for (int i = 0; i < (message.Length - subString.Length); i++) {
if (message.Substring(i, subString.Length) == subString) {
string before = message.Substring(0, i);
string after = message.Substring(i + subString.Length);
return before + Replacement + after;
}
}
return message;
}
async Task<AccountClaims> getClaims(int AccountID) {
try { try {
Account? test = await _accountContext.GetAccountByID(AccountID); Account? test = await _accountContext.GetAccountByID(AccountID);
if( test != null ) { if (test != null) {
AccountClaims aClaims = new AccountClaims() { AccountClaims aClaims = new AccountClaims() {
UserName = test.UserName, UserName = test.UserName,
Email = test.Email, Email = test.Email,
@@ -121,265 +362,5 @@ namespace MistoxWebsite.Server.Controllers {
} }
} }
// In Account -> Full account
// Out Account
[Route( "api/account/register" )]
[HttpPost]
public async Task<ActionResult<Account>> Register( [FromBody] Account request ) {
try {
if( await _accountContext.GetAccount( request.UserName.ToLower() ) == null ) {
if( await _accountContext.GetAccount( request.Email.ToLower() ) == null ) {
Account? created = new Account(){
UserName = request.UserName.ToLower(),
Email = request.Email.ToLower(),
EmailVerified = false,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.PasswordHash),
};
await _accountContext.NewAccount( created );
created = await _accountContext.GetAccount( request.Email.ToLower() );
if( created != null ) {
AccountClaims aClaims = await getClaims(created.ID);
List<Claim> claims = new List<Claim>() {
new Claim(ClaimTypes.Name, aClaims.UserName),
new Claim(ClaimTypes.Email, aClaims.Email),
new Claim("emailverified", aClaims.EmailVerified),
new Claim(ClaimTypes.Role, aClaims.Role),
new Claim("LockAccount", aClaims.FailedPasswordLock)
};
await SendVerify(created);
return created;
}
return new Account() { Error = "Unknown Error" };
} else {
return new Account() { Error = "Email is already in use" };
}
} else {
return new Account() { Error = "UserName is taken" };
}
} catch( Exception ex ) {
Console.WriteLine("Error: " + ex.Message);
return new Account() { Error = ex.Message };
}
}
// In Account -> References UserName / PasswordHash( Current Password ) / Error( New Password )
// Out Bool
[Route( "api/account/changepassword" )]
[HttpPost]
public async Task<ActionResult<bool>> ChangePassword( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) {
test.PasswordHash = BCrypt.Net.BCrypt.HashPassword( request.Error );
test.SiteData.CurrentPasswordAttempts = 0;
await _accountContext.SetAccount( test );
return true;
}
}
return false;
} catch {
return false;
}
}
// In Account -> References UserName / SiteData FailedPasswordLock / SiteData.PasswordAttempts
// Out Error String
[Route( "api/account/toggleAccountLock" )]
[HttpPost]
public async Task<ActionResult<string>> ToggleAccountLock( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName);
if( test != null ) {
test.SiteData.FailedPasswordLock = request.SiteData.FailedPasswordLock;
test.SiteData.CurrentPasswordAttempts = 0;
test.SiteData.PasswordAttempts = request.SiteData.PasswordAttempts;
await _accountContext.SetAccount( test );
return "Account Lock Status Updated";
}
return "Unknown Error Occurred";
} catch( Exception ex ) {
return ex.Message;
}
}
// Out Account -> only if logged in
[Route( "api/account/get" )]
[HttpPost]
public async Task<ActionResult<Account?>> Get() {
try {
if( User.Identity != null && User.Identity.IsAuthenticated ) {
string? email = User.FindFirstValue(ClaimTypes.Email);
if( !string.IsNullOrEmpty( email ) ) {
Account? test = await _accountContext.GetAccount(email);
if( test != null ) {
return test;
}
}
}
return Ok();
} catch {
return Ok();
}
}
// In Null
// Out Null
[Route( "api/account/logout" )]
[HttpPost]
public async Task Logout() {
await HttpContext.SignOutAsync();
}
string Substitue( string message, string subString, string Replacement ) {
for( int i = 0; i < (message.Length - subString.Length); i++ ) {
if( message.Substring( i, subString.Length ) == subString ) {
string before = message.Substring( 0, i );
string after = message.Substring(i + subString.Length );
return before + Replacement + after;
}
}
return message;
}
// In Account -> References UserName
// Out Success bool
[Route( "api/account/sendverifyemail" )]
[HttpPost]
public async Task<ActionResult<string>> SendVerify( [FromBody] Account request ) {
try {
string key = "v" + request.UserName;
// Stop from sending multiple emails quickly
if ( _emailContext._SentEmails.ContainsKey(key) ){
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
if (PreviousSentTime.AddMinutes(5) > DateTime.Now){
return "Cannot sent another verify email until 5 minutes has elapsed ";
}else{
_emailContext._SentEmails.Remove(key);
}
}
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
test.SiteData.EmailToken = Guid.NewGuid().ToString();
await _accountContext.SetAccount( test );
string EmailContents = EmailService.VerifyEmailEmail;
EmailContents = Substitue( EmailContents, "@UserName", request.UserName );
EmailContents = Substitue( EmailContents, "@UserName", request.UserName );
EmailContents = Substitue( EmailContents, "@VerifyPassword", test.SiteData.EmailToken );
string result = _emailContext.Send( test.Email, EmailService.VerifyEmailSubject, EmailContents );
_emailContext._SentEmails.Add(key, DateTime.Now);
return result;
}
return "Account not found";
} catch (Exception) {
return "The connection couldn't be established to the email server";
}
}
// In Account -> References UserName / Password( EmailToken )
// Out Success bool
[Route( "api/account/verifyemail" )]
[HttpPost]
public async Task<ActionResult<bool>> VerifyEmail( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
if( test.SiteData.EmailToken == request.PasswordHash ) {
test.SiteData.EmailToken = "";
test.EmailVerified = true;
await _accountContext.SetAccount( test );
return true;
}
}
return false;
} catch {
return false;
}
}
// In Account -> References Email
// Out Success bool
[Route( "api/account/sendresetpassword" )]
[HttpPost]
public async Task<ActionResult<string>> ResetPassword( [FromBody] Account request ) {
try {
string key = "p" + request.Email.ToLower();
// Stop from sending multiple emails quickly
if ( _emailContext._SentEmails.ContainsKey(key) ){
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
if (PreviousSentTime.AddMinutes(5) > DateTime.Now){
return "Cannot sent another reset requests until 5 minutes has elapsed";
}else{
_emailContext._SentEmails.Remove(key);
}
}
Account? test = await _accountContext.GetAccount(request.Email.ToLower());
if( test != null ) {
test.SiteData.EmailToken = Guid.NewGuid().ToString();
await _accountContext.SetAccount( test );
string EmailContents = EmailService.ResetPasswordEmail;
EmailContents = Substitue( EmailContents, "@UserName", test.UserName );
EmailContents = Substitue( EmailContents, "@UserName", test.UserName );
EmailContents = Substitue( EmailContents, "@ResetPassWord", test.SiteData.EmailToken );
string result = _emailContext.Send( test.Email, EmailService.VerifyEmailSubject, EmailContents );
_emailContext._SentEmails.Add(key, DateTime.Now);
return result;
}
return "Account Not Found";
} catch (Exception) {
return "The connection couldn't be established to the email server";
}
}
// In Account -> References UserName / Password( NewPassword ) / Error( EmailToken )
// Out Success bool
[Route( "api/account/resetpassword" )]
[HttpPost]
public async Task<ActionResult<bool>> ResetPwdVerify( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null && !string.IsNullOrEmpty(test.SiteData.EmailToken) ) {
if( test.SiteData.EmailToken == request.Error ) {
test.SiteData.CurrentPasswordAttempts = 0;
test.PasswordHash = BCrypt.Net.BCrypt.HashPassword( request.PasswordHash );
await _accountContext.SetAccount( test );
return true;
}
}
return false;
} catch {
return false;
}
}
// In Account -> References UserName / Password( Password ) )
// Out Success bool
[Route( "api/account/delete" )]
[HttpPost]
public async Task<ActionResult<bool>> delete( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) {
await _accountContext.DeleteAccount( test );
return true;
}
}
return false;
} catch {
return false;
}
}
} }
} }
@@ -1,6 +1,6 @@
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using System.Security.Claims; using System.Security.Claims;
using MistoxWebsite.Shared; using MistoxWebsite.Shared.DTO.Session;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace MistoxWebsite.Server.Controllers { namespace MistoxWebsite.Server.Controllers {
@@ -1,12 +1,8 @@
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Server.Controllers.Payment; using MistoxWebsite.Server.Controllers.Payment;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using Newtonsoft.Json;
using Stripe; using Stripe;
using Stripe.Climate;
using Stripe.Tax;
namespace MistoxWebsite.Server.Controllers { namespace MistoxWebsite.Server.Controllers {
[ApiController] [ApiController]
@@ -24,7 +20,7 @@ namespace MistoxWebsite.Server.Controllers {
public async Task<string> GetPaymentKey( [FromQuery] string userID ) { public async Task<string> GetPaymentKey( [FromQuery] string userID ) {
string OrderNumber = Guid.NewGuid().ToString().Substring(0,10); string OrderNumber = Guid.NewGuid().ToString().Substring(0,10);
Shared.Account? acc = await _databaseService.GetAccount(userID); Shared.Database.Account? acc = await _databaseService.GetAccount(userID);
if (acc != null) { if (acc != null) {
List<Cart> cart = await _databaseService.GetCart(acc); List<Cart> cart = await _databaseService.GetCart(acc);
@@ -84,7 +80,7 @@ namespace MistoxWebsite.Server.Controllers {
} }
// Clear the cart // Clear the cart
Shared.Account account = new Shared.Account{ Shared.Database.Account account = new() {
ID = userID ID = userID
}; };
await _databaseService.ClearCart( account ); await _databaseService.ClearCart( account );
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
namespace MistoxWebsite.Server.Controllers.Payment { namespace MistoxWebsite.Server.Controllers.Payment {
@@ -1,9 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using MistoxWebsite.Server.Controllers.Payment; using MistoxWebsite.Server.Controllers.Payment;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using Stripe; using Stripe;
using Stripe.Tax; using Stripe.Tax;
@@ -17,7 +14,7 @@ namespace MistoxWebsite.Server.Controllers {
_databaseService = databaseService; _databaseService = databaseService;
} }
public async Task<(bool, string)> Purchase(string OrderNumber, Shared.Account user, List<Cart> cart) { public async Task<(bool, string)> Purchase(string OrderNumber, Shared.Database.Account user, List<Cart> cart) {
try { try {
// build Recipt and calculate Tax // build Recipt and calculate Tax
var options = new CalculationCreateOptions { var options = new CalculationCreateOptions {
@@ -34,7 +31,7 @@ namespace MistoxWebsite.Server.Controllers {
// Add items to receipt // Add items to receipt
int subtotal = 0; int subtotal = 0;
foreach (Cart items in cart) { foreach (Cart items in cart) {
Shared.Product? product = await _databaseService.GetProduct(items.ProductID); Shared.Database.Product? product = await _databaseService.GetProduct(items.ProductID);
if (product != null) { if (product != null) {
prods.Add(product.ID); prods.Add(product.ID);
if (product != null) { if (product != null) {
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Server.Services.DatabaseService; using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
@@ -128,74 +128,6 @@ namespace MistoxWebsite.Server.Controllers {
} }
} }
DirObj RecursiveBuild( DirObj DirObj, string workingPath, List<ReceiptProduct> purchased ) {
string[] files = Directory.GetFiles(workingPath);
string[] directories = Directory.GetDirectories(workingPath);
List<DirObj> building = new List<DirObj>();
// Get File Names
Parallel.For( 0, files.Length, ( i ) => {
string fileName = files[i].Substring(workingPath.Length, files[i].Length - (workingPath.Length));
building.Add( new DirObj {
Type = FileType.File,
Path = fileName
});
} );
// Get Path Names
Parallel.For( 0, directories.Length, ( i ) => {
foreach( ReceiptProduct cur in purchased ) {
string dirName = directories[i].Substring(workingPath.Length, directories[i].Length - (workingPath.Length));
if( contains( dirName, cur.product.URL ) ) {
DirObj dir = new DirObj {
Type = FileType.Directory,
Path = dirName,
};
building.Add( dir );
RecursiveBuild( dir, directories [i], purchased );
}
}
} );
DirObj.Children = building.ToArray();
return DirObj;
}
string _FolderRoot = "/home/downloads/";
[Route( "api/product/showdownloads" )]
[HttpPost]
public async Task<IActionResult> ShowDownloads() {
try {
if( User.Identity != null && User.Identity.IsAuthenticated ) {
List<Claim> userClaims = User.Claims.ToList();
int UserID = -1;
foreach( Claim claim in userClaims ) {
if( claim.Type == "ID" ) {
UserID = Convert.ToInt32( claim.Value );
break;
}
}
List<ReceiptProduct> purchased = await _databaseService.GetAllReceiptsJoinedToProduct( new Account{ ID = UserID } );
byte[] datapacket = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(RecursiveBuild(new DirObj {
Path = @"\",
Type = FileType.Directory,
}, _FolderRoot, purchased)));
return new FileContentResult( datapacket, "text/html" );
}
return Unauthorized();
} catch {
return NotFound();
}
}
bool contains( string outer, string inner ) { bool contains( string outer, string inner ) {
if ( outer.Length >= inner.Length ) { if ( outer.Length >= inner.Length ) {
for ( int i=0; i<outer.Length-inner.Length; i++ ) { for ( int i=0; i<outer.Length-inner.Length; i++ ) {
@@ -221,10 +153,10 @@ namespace MistoxWebsite.Server.Controllers {
if ( contains( Product, product.URL ) ) { if ( contains( Product, product.URL ) ) {
Receipt? receipt = await _databaseService.GetReceipt(user, product); Receipt? receipt = await _databaseService.GetReceipt(user, product);
if( receipt != null ) { if( receipt != null ) {
FileStream fileStream = new FileStream(_FolderRoot + Product, FileMode.Open, FileAccess.Read); //FileStream fileStream = new FileStream(_FolderRoot + Product, FileMode.Open, FileAccess.Read);
return new FileStreamResult( fileStream, "application/octet-stream" ) { //return new FileStreamResult( fileStream, "application/octet-stream" ) {
FileDownloadName = fileStream.Name // FileDownloadName = fileStream.Name
}; //};
} }
break; break;
} }
@@ -1,19 +0,0 @@
{
"profiles": {
"IIS Express": {
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:6003",
"sslPort": 6003
},
"ProjectName": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:6003",
"sslPort": 6003
}
}
}
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,4 +1,5 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MistoxWebsite.Shared.DTO.Account;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,6 +1,6 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MistoxWebsite.Shared.DTO.Session;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common; using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService { namespace MistoxWebsite.Server.Services.DatabaseService {
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -46,8 +46,8 @@ namespace MistoxWebsite.Server.Services.DatabaseService {
return receipts; return receipts;
} }
public async Task<List<ReceiptProduct>> GetAllReceiptsJoinedToProduct( Account account ) { public async Task<List<( Receipt, Product )>> GetAllReceiptsJoinedToProduct( Account account ) {
List<ReceiptProduct> join = new List<ReceiptProduct> (); List<( Receipt, Product )> join = new();
using( MySqlConnection connection = GetConnection() ) { using( MySqlConnection connection = GetConnection() ) {
connection.Open(); connection.Open();
string command = @" string command = @"
@@ -78,24 +78,25 @@ namespace MistoxWebsite.Server.Services.DatabaseService {
int _cost = !reader.IsDBNull( "Cost" ) ? reader.GetInt32("Cost") : 0; int _cost = !reader.IsDBNull( "Cost" ) ? reader.GetInt32("Cost") : 0;
string _url = !reader.IsDBNull( "URL" ) ? reader.GetString("URL") : "Something Random That Wont Ever Be In A URL"; string _url = !reader.IsDBNull( "URL" ) ? reader.GetString("URL") : "Something Random That Wont Ever Be In A URL";
join.Add( new ReceiptProduct() { Receipt r = new() {
receipt = new Receipt { AccountID = _accountid,
AccountID = _accountid, ProductID = _gameid,
ProductID = _gameid, ReceiptID = _receiptid,
ReceiptID = _receiptid, Time = _receiptdate,
Time = _receiptdate, TotalCost = _totalcost,
TotalCost = _totalcost, TaxAmount = _taxamount,
TaxAmount = _taxamount, LineItem = _lineitem
LineItem = _lineitem };
},
product = new Product() { Product p = new() {
ID = _id, ID = _id,
Cost = _cost, Cost = _cost,
Description = _desc, Description = _desc,
Name = _name, Name = _name,
URL = _url URL = _url
} };
} );
join.Add( (r, p) );
} }
} }
} }
@@ -1,4 +1,4 @@
using MistoxWebsite.Shared; using MistoxWebsite.Shared.Database;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
@@ -1,5 +1,3 @@
using System.Net.Mail;
namespace MistoxWebsite.Server.Services { namespace MistoxWebsite.Server.Services {
public partial class EmailService { public partial class EmailService {
@@ -1,5 +1,3 @@
using System.Net.Mail;
namespace MistoxWebsite.Server.Services { namespace MistoxWebsite.Server.Services {
public partial class EmailService { public partial class EmailService {
@@ -0,0 +1,20 @@
namespace MistoxWebsite.Shared.DTO.Account {
public class UserInventory {
public string Item { get; set; } = string.Empty;
public int Quantity { get; set; }
public string Stats { get; set; } = string.Empty;
}
public class PaymentObject {
public string CardNumber { get; set; } = string.Empty;
public long ExperationMonth { get; set; }
public long ExperationYear { get; set; }
public string CVC { get; set; } = string.Empty;
public string FullName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Zip { get; set; } = string.Empty;
public List<int> productIDs { get; set; } = new List<int>();
}
}
@@ -0,0 +1,21 @@
using MistoxWebsite.Shared.Database;
namespace MistoxWebsite.Shared.DTO.Session {
public class PageLoadObject {
public Database.Account? user { get; set; }
public AccountClaims? claims { get; set; }
public List<Receipt>? receipts { get; set; }
public List<Product>? products { get; set; }
public List<Cart>? Cart { get; set; }
}
public class AccountClaims {
public string UserName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string EmailVerified { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty;
public string FailedPasswordLock { get; set; } = string.Empty;
}
}
+25 -76
View File
@@ -1,27 +1,6 @@
using System.Diagnostics; // Reflections of SQL Database objects
// Reflections of SQL Database objects namespace MistoxWebsite.Shared.Database {
namespace MistoxWebsite.Shared {
public class PageLoadObject {
public Account? user { get; set; }
public AccountClaims? claims { get; set; }
public List<Receipt>? receipts { get; set; }
public List<Product>? products { get; set; }
public List<Cart>? Cart { get; set; }
}
public class DirObj {
public FileType? Type { get; set; }
public string Path { get; set; } = "";
public DirObj? [] Children { get; set; } = new DirObj?[0];
}
public enum FileType {
File,
Directory
}
public class Account { public class Account {
public int ID { get; set; } // PK public int ID { get; set; } // PK
@@ -33,25 +12,6 @@ namespace MistoxWebsite.Shared {
public string Error { get; set; } = ""; public string Error { get; set; } = "";
} }
public class Product {
public int ID { get; set; } // PK
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public int CurShowingIMG = 0;
public List<string> Images { get; set; } = new List<string>();
public int Cost { get; set; }
public string URL { get; set; } = "";
}
public class WebSiteData {
public int AccountID { get; set; } // PK
public bool FailedPasswordLock { get; set; } = false;
public int PasswordAttempts { get; set; } = 5;
public int CurrentPasswordAttempts { get; set; } = 0;
public string Role { get; set; } = "Generic";
public string EmailToken { get; set; } = "";
}
public class AccountInventory { public class AccountInventory {
public int AccountID { get; set; } // PK public int AccountID { get; set; } // PK
public int ProductID { get; set; } // PK public int ProductID { get; set; } // PK
@@ -60,25 +20,14 @@ namespace MistoxWebsite.Shared {
public string Stats { get; set; } = string.Empty; public string Stats { get; set; } = string.Empty;
} }
public class UserInventory { public class Product {
public string Item { get; set; } = string.Empty; public int ID { get; set; } // PK
public int Quantity { get; set; } public string Name { get; set; } = "";
public string Stats { get; set; } = string.Empty; public string Description { get; set; } = "";
} public int CurShowingIMG = 0;
public List<string> Images { get; set; } = new List<string>();
public class Receipt { public int Cost { get; set; }
public int AccountID { get; set; } // PK public string URL { get; set; } = "";
public int ProductID { get; set; } // PK
public string ReceiptID { get; set; } = string.Empty;
public int LineItem { get; set; }
public int TaxAmount { get; set; }
public int TotalCost { get; set; }
public DateTime Time { get; set; }
}
public class ReceiptProduct {
public Receipt receipt { get; set; } = new Receipt();
public Product product { get; set; } = new Product();
} }
public class Cart { public class Cart {
@@ -97,23 +46,23 @@ namespace MistoxWebsite.Shared {
public int Deaths { get; set; } public int Deaths { get; set; }
} }
public class AccountClaims { public class Receipt {
public string UserName { get; set; } = string.Empty; public int AccountID { get; set; } // PK
public string Email { get; set; } = string.Empty; public int ProductID { get; set; } // PK
public string EmailVerified { get; set; } = string.Empty; public string ReceiptID { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty; public int LineItem { get; set; }
public string FailedPasswordLock { get; set; } = string.Empty; public int TaxAmount { get; set; }
public int TotalCost { get; set; }
public DateTime Time { get; set; }
} }
public class PaymentObject { public class WebSiteData {
public string CardNumber { get; set; } = string.Empty; public int AccountID { get; set; } // PK
public long ExperationMonth { get; set; } public bool FailedPasswordLock { get; set; } = false;
public long ExperationYear { get; set; } public int PasswordAttempts { get; set; } = 5;
public string CVC { get; set; } = string.Empty; public int CurrentPasswordAttempts { get; set; } = 0;
public string FullName { get; set; } = string.Empty; public string Role { get; set; } = "Generic";
public string Email { get; set; } = string.Empty; public string EmailToken { get; set; } = "";
public string Zip { get; set; } = string.Empty;
public List<int> productIDs { get; set; } = new List<int>();
} }
} }