init commit over here

This commit is contained in:
Derek Holloway
2025-05-12 16:57:19 -07:00
parent 8e02c028a7
commit 838b3ab450
87 changed files with 6068 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Pages.Shared.MainLayout)">
<Authorizing>
<Authorizing></Authorizing>
</Authorizing>
<NotAuthorized>
<NotAuthroized></NotAuthroized>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(Pages.Shared.MainLayout)">
<PageDoesntExist></PageDoesntExist>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
+2
View File
@@ -0,0 +1,2 @@
/* This file is needed for compile for some reason */
/* Dont delete this file */
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Remove="App.razor.css" />
</ItemGroup>
<ItemGroup>
<Content Include="App.razor.css">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<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.DevServer" Version="9.0.3" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MistoxWebsite.Shared\MistoxWebsite.Shared.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,38 @@
@page "/About"
<PageTitle>About</PageTitle>
<style>
.about-frame {
margin-top: 25px;
max-width: 1200px;
padding: 10px;
}
</style>
<div class="big-frame background-border horizontal-center about-frame">
<p>Welcome to Mistox LLC. A project and hobby of Derek Holloway.</p>
<br />
<p>I am an indi-developer who has been making small projects since I was 13. I originally learned lua and spent 4 years mastering it. Then I moved onto C# which is my preferred language</p>
<p>My programming catalog consist of C#, Lua, SQL, C++, C, and JavaScript in the order of knowledge from best to passiable.</p>
<p>Im currently in college for computer sciences and should honestly be doing that instead of this but I find working on this website and hobby games to be way more enjoyable.</p>
<br />
<p>I would love to learn how to use Blender in order to make all the models for my games but with the amount of work ive already made for myself im going to hold off for now.</p>
<p>This website and everything on it are the long countless hours of my time and motivation to create something that I can be proud of and share that with the world.</p>
<p>So if you would like to support me as a small creator please feel free to leave a donation from on the store page. It would means a lot to me.</p>
<br />
<p>For the nerds out there, this website is a blazor webassembly app, hosted on an ubuntu webserver, with a mysql backend.</p>
<p>All the passwords are encrypted using bcrypt for your safety and all the data is only allowed through SSL.</p>
<p>After you make your account. All the data in the database is easily accessable through the account settings and</p>
<p>you can delete your account at any time. Including all your data with it so there is no risk.</p>
<p>I wont show ads and never will and I refuse to use trackers on this site.</p>
<br />
<br />
<p>If you have any questions, concerns, or would like to suggest a feature, bug-fix, or request to help. Please feel</p>
<p>free to reach out to me at <a href="mailto://derek@mistox.net">derek@mistox.net</a></p>
<a href='https://ko-fi.com/A0A3TSI2D' target='_blank'>
<img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi6.png?v=6' border='0' alt='Buy Me a Coffee at ko-fi.com' />
</a>
</div>
@@ -0,0 +1,43 @@
@page "/account/forgotpassword"
<PageTitle>Forgot</PageTitle>
<form class="center big-frame background-border">
<h3>Forgot Password</h3>
<div class="frame-item">
<input type="text" @bind="Email" @onkeyup="OnKeyDown" placeholder=" " />
<label>Email</label>
</div>
<div class="">
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="Send Code" @onclick="TrySendCode" />
</div>
</div>
</div>
<ul>
@if (!string.IsNullOrEmpty(Result)){
<li>@Result</li>
}
</ul>
</form>
@code {
public string Email{ get; set; } = "";
public string Result{ get; set; } = "";
public async Task OnKeyDown(KeyboardEventArgs e) {
if (e.Key == "Enter") {
await TrySendCode();
}
}
public async Task TrySendCode() {
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/account/sendresetpassword", new Account(){ Email = Email });
Result = await TestLogin.Content.ReadAsStringAsync();
}
}
@@ -0,0 +1,89 @@
@page "/account/login"
<PageTitle>Login</PageTitle>
<form class="center big-frame background-border">
<h3>Login</h3>
<div class="frame-item">
<input type="text" @bind="UserName" @onkeyup="OnKeyDown" placeholder=" " />
<label>UserName</label>
</div>
<div class="frame-item">
<input type="password" @bind="Password" @onkeyup="OnKeyDown" placeholder=" " />
<label>Password</label>
</div>
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="LOGIN" @onclick="TryLogin" />
</div>
<div class="frame-forgot">
<div class="sub-frame">
Stay Logged In
<input type="checkbox" @bind="StayLoggedIn" />
</div>
<div class="sub-frame">
<a href="/account/forgotpassword">Forgot Password</a>
</div>
</div>
</div>
<ul>
<h1 style="font-size: 15px;">@Loading</h1>
@foreach(string msg in ErrorMsgs ) {
<li>@msg</li>
}
</ul>
</form>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string? ReturnURL { get; set; }
string UserName { get; set; } = "";
string Password { get; set; } = "";
string Loading { get; set; } = "";
bool StayLoggedIn{ get; set; }
List<string> ErrorMsgs = new List<string>();
public async Task OnKeyDown(KeyboardEventArgs e){
if (e.Key == "Enter") {
await TryLogin();
}
}
public async Task TryLogin() {
Loading = "Waiting for login response from server";
ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL;
ErrorMsgs = new List<string>();
if(UserName != null ) {
if(Password != null ) {
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");
}
Loading = "";
base.StateHasChanged();
}
}
@@ -0,0 +1,116 @@
@page "/account/register"
<PageTitle>Register</PageTitle>
<form class="center big-frame background-border">
<h3>Register</h3>
<div class="frame-item">
<input type="text" placeholder=" " @bind="UserName" @onkeyup="OnKeyDown" />
<label>UserName</label>
</div>
<div class="frame-item">
<input type="email" placeholder=" " @bind="Email" @onkeyup="OnKeyDown" />
<label>Email</label>
</div>
<div class="frame-item">
<input type="password" placeholder=" " @bind="Password" @onkeyup="OnKeyDown" />
<label>Password</label>
</div>
<div class="">
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="REGISTER" @onclick="TryRegister" />
</div>
</div>
</div>
<ul>
<h1 style="font-size: 15px;">@Loading</h1>
@foreach(string msg in ErrorMsgs ) {
<li>@msg</li>
}
</ul>
</form>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string? ReturnURL { get; set; }
string Email { get; set; } = "";
string UserName { get; set; } = "";
string Password { get; set; } = "";
string Loading { get; set; } = "";
List<string> ErrorMsgs = new List<string>();
public async Task OnKeyDown(KeyboardEventArgs e){
if (e.Key == "Enter") {
if (string.IsNullOrEmpty(Email) || string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty( Password )) {
await Task.Delay( 100 );
}
await TryRegister();
}
}
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() {
Loading = "Waiting for a response from the server";
ReturnURL = string.IsNullOrEmpty(ReturnURL) ? "/" : ReturnURL;
ErrorMsgs = new List<string>();
if (Email != null){
if( CheckEmail( Email ) ) {
if(UserName != null ) {
if(Password != null ) {
if (Password.Length >= 6 ) {
HttpResponseMessage TestRegister = await Http.PostAsJsonAsync("api/account/register", new Account(){
UserName = UserName,
Email = Email,
PasswordHash = Password,
EmailVerified = false,
});
Account? user = await TestRegister.Content.ReadFromJsonAsync<Account>();
if ( string.IsNullOrEmpty(user?.Error) ) {
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");
}
Loading = "";
base.StateHasChanged();
}
}
@@ -0,0 +1,57 @@
@page "/account/resetpassword"
<PageTitle>Reset Password</PageTitle>
<form class="center big-frame background-border">
<h3>Reset Password</h3>
<div class="frame-item">
<input type="text" placeholder=" " @bind="NewPassword" @onkeyup="OnKeyDown" />
<label>New Password</label>
</div>
<div class="frame-item">
<input type="text" placeholder=" " @bind="RepeatPassword" @onkeyup="OnKeyDown" />
<label>Repeat New Password</label>
</div>
<div class="">
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="Reset" @onclick="TryChange" />
</div>
</div>
</div>
<ul>
@if (!string.IsNullOrEmpty(Result)){
<li>@Result</li>
}
</ul>
</form>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string UserName { get; set; } = "";
[Parameter]
[SupplyParameterFromQuery]
public string ResetPwd { get; set; } = "";
public string NewPassword{ get; set; } = "";
public string RepeatPassword{ get; set; } = "";
public string Result{ get; set; } = "";
public async Task OnKeyDown(KeyboardEventArgs e ) {
if (e.Key == "Enter") {
await TryChange();
}
}
protected async Task TryChange() {
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/account/resetpassword", new Account(){ UserName = UserName, PasswordHash = NewPassword, Error = ResetPwd });
string result = await TestLogin.Content.ReadAsStringAsync();
Result = result == "true" ? "Password changed successfully" : "Something is wrong";
}
}
@@ -0,0 +1,43 @@
@page "/account/verifyemail"
<PageTitle>Verifying Email</PageTitle>
<div class="center big-frame background-border">
<h3>@Result</h3>
<div class="">
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="Return To Login" @onclick="ReturnToLogin" />
</div>
</div>
</div>
</div>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string Guid { get; set; } = "";
[Parameter]
[SupplyParameterFromQuery]
public string UserName{ get; set; } = "";
public string Result{ get; set; } = "";
protected override async Task OnInitializedAsync() {
HttpResponseMessage Query = await Http.PostAsJsonAsync("api/account/verifyemail", new Account(){ UserName = UserName, PasswordHash = Guid });
bool Answer = await Query.Content.ReadFromJsonAsync<bool>();
if (Answer == true ) {
Result = "Verified Email Successfully";
} else {
Result = "Email was not able to be verified please resend email";
}
base.StateHasChanged();
}
public void ReturnToLogin(){
this.Nav.NavigateTo("/account/login");
}
}
@@ -0,0 +1,123 @@
<AuthorizeView>
<div class="subject-frame">
<div class="title-frame">
@if(context.User.FindFirst("EmailVerified")?.Value == "True" ) {
<h1>Email Is Verified Successfully</h1>
} else {
<button type="button" @onclick="SendVerifyEmail">Resend Verify Email</button>
}
</div>
<h3>@EmailSentResult</h3>
</div>
<div class="subject-frame">
<div class="title-frame">
<h1>Reset Your Password</h1>
</div>
<input type="password" placeholder="Current Password" @bind="CurPass">
<input type="password" placeholder="New Password" @bind="NewPass1">
<input type="password" placeholder="Repeat New Password" @bind="NewPass2">
<button type="button" @onclick="TestNewPass">Reset</button>
<h2>@PasswordErrorText</h2>
</div>
<div class="subject-frame">
<div class="title-frame">
<h1>Login Counter</h1>
</div>
<h2>This will lock the account after the specified number of failed logins</h2>
<h2>You will have to reset the password using your email aftwards</h2>
@if( _account != null && _account.EmailVerified && FailedLoginToggle ) {
<h3>Failed login attempts</h3><input type="number" @bind="MaxFailedLogin">
} else if (_account != null && !_account.EmailVerified) {
<h3>This feature cannot be enabled until you verify your email</h3>
}
<h3>Enabled</h3><input type="checkbox" @bind="FailedLoginToggle">
<button @onclick="SubmitLoginLock">Apply Login Counter Changes</button>
<h4>@LoginCounterResult</h4>
</div>
</AuthorizeView>
@code {
public MistoxWebsite.Shared.Account? _account = null;
public int MaxFailedLogin = 0;
public bool FailedLoginToggle = false;
public string LoginCounterResult = "";
public async Task SubmitLoginLock() {
if (_account != null ) {
_account.SiteData.FailedPasswordLock = FailedLoginToggle;
_account.SiteData.PasswordAttempts = MaxFailedLogin;
_account.PasswordHash = "";
HttpResponseMessage SendVerifyEmail = await Http.PostAsJsonAsync("api/account/toggleAccountLock", _account);
LoginCounterResult = await SendVerifyEmail.Content.ReadAsStringAsync();
}
}
protected override async Task OnInitializedAsync() {
HttpResponseMessage x = await Http.PostAsync("api/account/get", new StringContent(""));
string body = await x.Content.ReadAsStringAsync();
_account = JsonConvert.DeserializeObject<MistoxWebsite.Shared.Account>(body);
if (_account != null){
FailedLoginToggle = _account.SiteData.FailedPasswordLock;
MaxFailedLogin = _account.SiteData.PasswordAttempts;
}
}
/* Verify Email Code */
public string EmailSentResult = "";
public async Task SendVerifyEmail() {
if (_account != null){
HttpResponseMessage SendVerifyEmail = await Http.PostAsJsonAsync("api/account/sendverifyemail", new MistoxWebsite.Shared.Account(){ UserName = _account.UserName });
bool result = await SendVerifyEmail.Content.ReadFromJsonAsync<bool>();
if (result == true ) {
EmailSentResult = "Email Sent";
} else {
EmailSentResult = "Problem Sending Email";
}
}
}
/* New Password Code */
public string CurPass { get; set; } = "";
public string NewPass1 { get; set; } = "";
public string NewPass2 { get; set; } = "";
public string PasswordErrorText { get; set; } = "";
protected async Task TestNewPass() {
if ( string.IsNullOrEmpty(CurPass) ) {
PasswordErrorText = "The Current Password Is Required";
return;
}
if ( string.IsNullOrEmpty(NewPass1) ) {
PasswordErrorText = "The New Password Is Required";
return;
}
if ( string.IsNullOrEmpty(NewPass2) ) {
PasswordErrorText = "The New Repeated Password Is Required";
return;
}
if (NewPass1 != NewPass2 ) {
PasswordErrorText = "The New Passwords Dont Match";
return;
}
if (_account != null){
HttpResponseMessage TryChangePassword = await Http.PostAsJsonAsync("api/account/changepassword", new MistoxWebsite.Shared.Account(){ UserName = _account.UserName, PasswordHash = CurPass, Error = NewPass1 });
bool resultText = await TryChangePassword.Content.ReadFromJsonAsync<bool>();
if (resultText == true ) {
PasswordErrorText = "Password changed successfully";
} else {
PasswordErrorText = "Current password is wrong";
}
}
}
}
@@ -0,0 +1,15 @@
.subject-frame{
background-color: rgba(255,255,255,.1);
border-radius: 4px;
padding: 5px;
margin-bottom: 1.4rem;
}
.title-frame{
}
.title-frame h1 input{
padding-left: 1.4rem;
padding-top: 0.4rem;
}
@@ -0,0 +1,38 @@
@page "/account/manage"
@attribute [Authorize]
@using MistoxWebsite.Client.Pages.Account.Manage
<PageTitle>Manage</PageTitle>
<TitleBar Title="Manage" ButtonList=tbd ></TitleBar>
<div class="lower-frame">
@if (Visible == ShowingFrame.Account) {
<Account></Account>
} else if (Visible == ShowingFrame.Data) {
<WebsiteData></WebsiteData>
}
</div>
@code {
TitleBarDiv[] tbd = new TitleBarDiv[2];
protected override void OnInitialized() {
tbd[0] = new TitleBarDiv{ Name = "Data" };
tbd[0].onClicked += ( object? o, EventArgs e ) => { Visible = ShowingFrame.Data; base.StateHasChanged(); };
tbd[1] = new TitleBarDiv{ Name = "Account" };
tbd[1].onClicked += ( object? o, EventArgs e ) => { Visible = ShowingFrame.Account; base.StateHasChanged(); };
}
public ShowingFrame Visible = ShowingFrame.Account;
public enum ShowingFrame {
Account,
Data,
}
}
@@ -0,0 +1,6 @@
body {
}
.lower-frame{
padding: 1.4rem;
}
@@ -0,0 +1,10 @@
<AuthorizeView>
<div>
<h1>MistDataJSON</h1>
</div>
</AuthorizeView>
@code {
}
@@ -0,0 +1,183 @@
<AuthorizeView>
@if (showDeleteConfirm){
<div class="Blackout-Frame"></div>
<div class="Confirm-Frame">
<h2 class="Confirm-Title">Are you sure?</h2>
<h3 class="Confirm-Content">There is no way to recover your account when its deleted. All your purchases and stats will be lost forever.</h3>
<h3 class="Confirm-Password">Password</h3>
<input class="Confirm-Input" type="password" @bind="Password" placeholder=" " />
<div class="Confirm-Padding"></div>
<button class="delete-button" style="color: green;" @onclick="cancelDeleteAccount">Cancel</button>
<button class="delete-button" style="color: red;" @onclick="confirmDeleteAccount">Delete Account</button>
</div>
<h2 class="Delete-Error">@ErrorPlaceholder</h2>
}
<div>
<h1>Website Data</h1>
<span>We dont keep what we collect on you private. Have a look for yourself what is on the server.</span>
<hr/>
<div class="data-table">
<div class="padder">
<h1>User Data</h1>
@foreach(KeyValuePare cur in UserData ) {
<div class="data">
<div class="data-left">
<h1>@cur.Key</h1>
</div>
<div class="data-right">
<h2>@cur.Value</h2>
</div>
</div>
}
</div>
</div>
<div class="data-table">
<div class="padder">
<h1>Site Data</h1>
@foreach( KeyValuePare cur in SiteData ) {
<div class="data">
<div class="data-left">
<h1>@cur.Key</h1>
</div>
<div class="data-right">
<h2>@cur.Value</h2>
</div>
</div>
}
</div>
</div>
<div class="data-table">
<div class="padder">
<h1>Purchases</h1>
@foreach( Receipt cur in Statics.Owned ) {
<div class="data">
<div>
<div class="data-left">
<h1>Receipt ID</h1>
</div>
<div class="data-right">
<h2>@cur.ReceiptID</h2>
</div>
</div>
<div>
<div class="data-left">
<h1>Game</h1>
</div>
<div class="data-right">
<h2>@ProductName( cur.ProductID )</h2>
</div>
</div>
<div>
<div class="data-left">
<h1>Time</h1>
</div>
<div class="data-right">
<h2>@cur.Time</h2>
</div>
</div>
</div>
}
</div>
</div>
<div class="delete-frame">
<button class="delete-button" @onclick="showDeleteAccount">Delete Account</button>
</div>
</div>
</AuthorizeView>
@code {
bool showDeleteConfirm = false;
string Password = "";
string ErrorPlaceholder = "";
class KeyValuePare {
public string Key { get; set; } = "";
public string Value { get; set; } = "";
}
string ProductName(int ProductID ) {
foreach(Product cur in Statics.Products ) {
if( cur.ID == ProductID ) {
return cur.Name + " -> ( ID : " + ProductID + " )";
}
}
return "Unknown Product -> ( ID : " + ProductID + " )";
}
void showDeleteAccount() {
showDeleteConfirm = true;
}
void cancelDeleteAccount() {
showDeleteConfirm = false;
Password = "";
}
async Task confirmDeleteAccount() {
HttpResponseMessage Delete = await Http.PostAsJsonAsync( "api/account/delete", new MistoxWebsite.Shared.Account(){
ID = Statics.User.ID,
UserName = Statics.User.Email,
PasswordHash = Password
});
string result = await Delete.Content.ReadAsStringAsync();
bool status = result == "true" ? true : false;
if (status){
await Http.PostAsync("api/account/logout", new StringContent(""));
Nav.NavigateTo("/", true);
}
ErrorPlaceholder = status.ToString();
}
List<KeyValuePare> UserData = new List<KeyValuePare>();
List<KeyValuePare> SiteData = new List<KeyValuePare>();
protected override void OnInitialized() {
UserData.Add(new KeyValuePare {
Key = "ID",
Value = Statics.User.ID.ToString()
} );
UserData.Add( new KeyValuePare {
Key = "UserName",
Value = Statics.User.UserName
} );
UserData.Add( new KeyValuePare {
Key = "Email",
Value = Statics.User.Email
} );
UserData.Add( new KeyValuePare {
Key = "EmailVerified",
Value = Statics.User.EmailVerified ? "true" : "false"
} );
UserData.Add( new KeyValuePare {
Key = "PasswordHash",
Value = Statics.User.PasswordHash
} );
SiteData.Add( new KeyValuePare {
Key = "FailedPasswordLockEnabled",
Value = Statics.User.SiteData.FailedPasswordLock ? "true" : "false"
} );
SiteData.Add(new KeyValuePare {
Key = "MaxPasswordAttempts",
Value = Statics.User.SiteData.PasswordAttempts.ToString()
} );
SiteData.Add( new KeyValuePare {
Key = "CurrentPasswordAttempts",
Value = Statics.User.SiteData.CurrentPasswordAttempts.ToString()
} );
SiteData.Add( new KeyValuePare {
Key = "Role",
Value = Statics.User.SiteData.Role
} );
SiteData.Add( new KeyValuePare {
Key = "EmailToken",
Value = Statics.User.SiteData.EmailToken
} );
}
}
@@ -0,0 +1,115 @@
.data-table {
column-count: 1;
background-color: rgba(255,255,255,.1);
margin-bottom: 1.4rem;
border-radius: 5px;
}
.padder{
padding: 1rem;
}
.data{
float: left;
width: 100%;
border-bottom: solid 1px #808080;
}
.data:last-child{
border: none;
}
.data-left {
float: left;
width: 50%;
}
.data-right {
float: right;
width: 50%;
}
.data-right h2 {
font-size: 14px;
word-wrap: break-word;
}
.data-left h1 {
font-size: 14px;
}
.Confirm-Frame{
position: absolute;
width: 400px;
height: 200px;
background-color: #202020;
left: calc( 50% - 200px );
top: calc( 50% - 100px );
border-radius: 5px;
border-color: red;
border-width: 2px;
border-style: double;
}
.Blackout-Frame {
position: absolute;
width: 100%;
height: 100%;
background-color: #00000088;
top: 0px;
left: 0px;
}
.Confirm-Title {
width: 100%;
text-align: center;
margin: 10px 0;
}
.Confirm-Content {
width: 100%;
text-align: center;
font-size: 15px;
margin-top: 0;
}
.Confirm-Password {
margin: 0 5%;
float: left;
height: 24px;
}
.Confirm-Input {
width: 62%;
float: left;
}
.Confirm-Padding {
height: 18px;
width: 80px;
float: left;
}
.delete-button {
color: green;
padding: 5px 15px;
background-color: #fff;
border: none;
margin: 10px;
border-radius: 4px;
}
.delete-button :hvoer {
background-color: #aaa;
transition-duration: 1s;
}
.Delete-Error {
position: absolute;
font-size: 20px;
left: calc(50% - 200px);
top: calc(50% + 100px);
width: 400px;
text-align: center;
color: red;
}
@@ -0,0 +1,74 @@
@page "/admin/product/create"
@attribute [Authorize(Roles = "Admin")]
<h3>CreateProduct</h3>
<div>
<div>
<label for="Name">Name</label>
<input type="text" placeholder="Name" @bind-value=obj.Name />
</div>
<div>
<label for="Description">Description</label>
<input type="text" placeholder="Description" @bind-value=obj.Description />
</div>
<div>
<label for="Images">Images</label>
@foreach(string img in obj.Images ) {
<div>
<img src="@img" />
<input type="button" value="Remove" @onclick="() => { obj.Images.Remove(img); }" />
</div>
}
<InputFile OnChange="@OnFileUpload" multiple />
</div>
<div>
<label for="Cost">Cost</label>
<input type="number" placeholder="Cost" @bind-value=obj.Cost />
</div>
<div>
<label for="Url">ItemURL</label>
<input type="text" placeholder="Url" @bind-value=obj.URL />
</div>
<button type="submit" @onclick="Submit">Submit</button>
</div>
<div>
@if( !string.IsNullOrEmpty( Result ) ) {
<h2>Results : @Result</h2>
}
</div>
@code {
public string Result = "";
public Product obj = new Product();
async void OnFileUpload(InputFileChangeEventArgs e ) {
List<IBrowserFile> files = e.GetMultipleFiles( 10 ).ToList();
foreach(IBrowserFile cur in files){
// Open Network Stream and read to memory
Stream ns = cur.OpenReadStream(maxAllowedSize:500000000); // 500 Mb
MemoryStream ms = new MemoryStream();
await ns.CopyToAsync( ms );
byte[] data = ms.ToArray();
string base64Image = "data:" + cur.ContentType + ";base64, ";
base64Image += Convert.ToBase64String( data );
obj.Images.Add( base64Image );
StateHasChanged();
};
}
public async Task Submit() {
HttpResponseMessage response = await Http.PostAsJsonAsync( "api/product/create", obj );
Result = await response.Content.ReadAsStringAsync();
}
public void RemoveImage(int index) {
Console.WriteLine( index );
obj.Images.RemoveAt( index );
}
}
@@ -0,0 +1,80 @@
@page "/admin/product/edit"
@attribute [Authorize(Roles = "Admin")]
<h3>EditProduct</h3>
@if( product != null ) {
<div>
<span>Name</span>
<input type="text" value="@product.Name" />
</div>
<div>
<span>Description</span>
<input type="text" value="@product.Description" />
</div>
<div>
<span>Cost</span>
<input type="number" value="@product.Cost" />
</div>
<div>
<span>PurchaseURL</span>
<input type="text" value="@product.URL" />
</div>
<div>
@foreach( string img in product.Images ) {
<div>
<img src="@img" alt="not found" />
<input type="button" value="Remove" @onclick="() => { product.Images.Remove(img); }"></input>
</div>
}
<InputFile OnChange="@OnFileUpload" multiple />
</div>
<div>
<input type="button" value="Submit" @onclick="onSubmit"></input>
</div>
<div>
<span>@Result</span>
</div>
}else{
<h1>Product Doesnt Exist</h1>
}
@code {
[Parameter]
[SupplyParameterFromQuery]
public int ProductID { get; set; }
Product? product = null;
string Result = "";
protected override async Task OnInitializedAsync() {
HttpResponseMessage response = await Http.PostAsJsonAsync( "api/product/get", new Product(){ ID = ProductID } );
string Result = await response.Content.ReadAsStringAsync();
product = JsonConvert.DeserializeObject<Product>( Result );
}
async void OnFileUpload(InputFileChangeEventArgs e ) {
List<IBrowserFile> files = e.GetMultipleFiles( 10 ).ToList();
foreach(IBrowserFile cur in files){
// Open Network Stream and read to memory
Stream ns = cur.OpenReadStream(maxAllowedSize:500000000); // 500 Mb
MemoryStream ms = new MemoryStream();
await ns.CopyToAsync( ms );
byte[] data = ms.ToArray();
string base64Image = "data:" + cur.ContentType + ";base64, ";
base64Image += Convert.ToBase64String( data );
product?.Images.Add( base64Image );
StateHasChanged();
};
}
protected async void onSubmit() {
HttpResponseMessage TestLogin = await Http.PostAsJsonAsync("api/product/update", product);
Result = await TestLogin.Content.ReadAsStringAsync();
}
}
@@ -0,0 +1,11 @@
@page "/"
<PageTitle>Mistox</PageTitle>
<div style="padding: 10px;" >
</div>
@code{
}
@@ -0,0 +1,33 @@
@page "/project/downloads"
@attribute [Authorize]
<TitleBar Title="Downloads"></TitleBar>
<div class="Big-Div">
<div id="DirTree">
@if (output != null ) {
<ExplorerChild Title=@output.Path Children=output.Children PartialPath="\"></ExplorerChild>
}
</div>
<span>@ErrorTxt</span>
</div>
@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();
}
}
}
@@ -0,0 +1 @@

@@ -0,0 +1,29 @@
@page "/project/mist"
<PageTitle>Project-Mist</PageTitle>
<style>
.mist-frame {
margin-top: 25px;
max-width: 1000px;
padding: 10px;
}
</style>
<div class="big-frame background-border horizontal-center mist-frame">
<p><strong>What is the game</strong></p>
<p style="padding-left: 30px;">Project-Mist is a survival game. Kind of like a battle royal in a sense but, think of it backwards. And no I know what your thinking. Its not the first person to die wins. No instead its a never ending survival game where you can free roam and build structures. The catch is, the person who has the highest stats [i.e A combination of kills, survival time] has a marker placed on their forhead.</p>
<p><strong>How will the game play</strong></p>
<p style="padding-left: 30px;">When you join the game you will be able to customize your character. There you can set a default loadout for your player. This will be the spawn weapon and gear. After that you will drop into the map with other players to fend for your life. The kill-leader will be marked loosely on the mini-map. You can choose to go after the kill leader or you can choose to loot first. The choice is yours. But be aware that if you survive long enough you will become the new kill leader.</p>
<p><strong>Current Idea Board *SUBJECT TO CHANGE*</strong></p>
<p>Survival Game<br />look at item to pick up 'e' for third person and click for third [No nearby]<br />normal weapons with bullet drop bullet travel time<br />snipers but rare [Maybe special]</p>
<p>Abilities selectable at spawn<br /> a max 20 credit slider where you can spend them on traits<br /> Stamina -&gt; run for longer distances<br /> Strength -&gt; carry more weight<br /> Vitality -&gt; Have more base health<br /> Stealth -&gt; Approximate location on map is bigger</p>
<p>More weight slows player some<br />Backpacks -&gt; Add slots but not weight</p>
<p>Oddball style game<br /> Map that shows the relitive area of the top player</p>
<p>spawn with classes<br /> 2 mags<br /> no attachments<br /> unlock guns with experience</p>
<p>no health regen<br />final hit headshots = 20 credits<br />final hit bodyshots = 10 credits</p>
<p>classes require credits to spawn with better stuff<br />inventory and credits are transferrable between servers and sessions<br />combat loggging - if leave in combat start from scratch<br />one dynamicly roaming entity of the night ( Impossible to kill, when near heart starts pumping and vinegrette )<br /> goes after people possible to get away<br />Dyanmic day and night cycle<br />Dynamic weather ( rain, fog, thunder, lightning )<br />floods that cause roaring rivers to fill that cannot be swam<br />Fires that char trees(no leaves), regrows in 3ish days<br />Master leaderboard in the main menu of top players per rank<br />Ranked lobby ( Disabled until player base )<br />small towns around a main centralized area( ie city or temple )<br />large servers<br />random spawned skin boxes that require credits to open<br />purchasable skins<br />bullet penatration on certain materials<br />bullet reflection on certain materials<br />No kill leader until you get at least 2 kills minimum</p>
<p>Tournament mode<br /> all players spawn at the same time<br /> hold oddball for 30mins total</p>
<p>server quits introducing people into game after 5 hrs. (last man standing mode)<br /> last person in server wins<br /> on death quit to new server<br /> everyone becomes oddball<br /> less players alive equal less oddball area</p>
</div>
@@ -0,0 +1,13 @@
.Big-Div {
position: relative;
left: calc(50% - 400px);
max-width: 800px;
}
@media (max-width: 1150px) {
.Big-Div {
position: relative;
left: calc(50% - 200px);
max-width: 400px;
}
}
@@ -0,0 +1,283 @@
@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();
}
}
}
@@ -0,0 +1,191 @@
* {
--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;
}
@@ -0,0 +1,5 @@
<PageTitle>Checking Session</PageTitle>
@code {
}
@@ -0,0 +1,38 @@
<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 );
}
}
@@ -0,0 +1,19 @@
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<article class="content px-4">
@Body
</article>
</main>
</div>
@code{
}
@@ -0,0 +1,88 @@
article{
padding: 0 !important;
}
.page {
position: relative;
display: flex;
flex-direction: column;
background-color: var(--Mistox-Black);
}
body{
background-color: var(--Mistox-Black);
}
main {
flex: 1;
background-color: var(--Mistox-Black);
color: var(--Mistox-White);
}
.sidebar {
border-right: var(--Mistox-Background) 2px solid;
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 45px;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
display: none;
}
.top-row.auth {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
min-width: 250px;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
}
@@ -0,0 +1,96 @@
<div class="top-row">
<div class="top-gradient">
<a class="navbar-brand" href="">
<img src="/img/MistoxLogo.png" />
</a>
</div>
<button title="DropDownButtonMobile" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon">MENU</span>
</button>
</div>
<div class="bottom-row">
<style>
@@media (max-width: 641px) {
main{
min-height: @MainHeight;
flex: none !important;
}
}
</style>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span>Home</span>
</NavLink>
</div>
<div class="nav-item">
<NavLink class="nav-link" href="/project/mist" Match="NavLinkMatch.All">
<span>Project-Mist</span>
</NavLink>
</div>
<AuthorizeView>
<Authorized>
<div class="nav-item">
<NavLink class="nav-link" href="store/catalog">
<span>Store</span>
</NavLink>
</div>
</Authorized>
</AuthorizeView>
<div class="nav-item">
<NavLink class="nav-link" href="/about" Match="NavLinkMatch.All">
<span>About</span>
</NavLink>
</div>
<div class="nav-login">
<AuthorizeView>
<Authorized>
<input class="nav-login-button" type="button" value="@context.User.FindFirst(ClaimTypes.Name)?.Value" @onclick="ManageProf"> </input>
<input class="nav-login-button" type="button" value="Logout" @onclick="ClearSession" />
</Authorized>
<NotAuthorized>
<input class="nav-login-button" type="button" value="Login" @onclick="NavToLogin" />
<input class="nav-login-button" type="button" value="Register" @onclick="NavToRegister" />
</NotAuthorized>
</AuthorizeView>
</div>
</nav>
</div>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private string? MainHeight => collapseNavMenu ? "calc(100vh - 56px)" : "calc(100vh - 279.2px)";
private void ToggleNavMenu(){
collapseNavMenu = !collapseNavMenu;
}
private void ManageProf() {
Nav.NavigateTo("/account/manage");
}
async void ClearSession() {
await Http.PostAsync("api/account/logout", new StringContent(""));
Nav.NavigateTo("/", true);
}
void NavToLogin() {
Nav.NavigateTo("/account/login");
}
void NavToRegister() {
Nav.NavigateTo("/account/register");
}
}
@@ -0,0 +1,132 @@
.navbar-toggler {
background-color: var(--Mistox-Dark);
color: var(--Mistox-White);
width: 100%;
height: 40px;
border: none;
font-size: 20px;
transition-duration: 0.5s;
}
.navbar-toggler:hover {
background-color: #410a04;
}
.top-row {
background-color: var(--Mistox-Offset);
height: 200px;
}
.bottom-row {
height: calc(100% - 200px);
background: linear-gradient(0deg, var(--Mistox-Dark), var(--Mistox-Offset) );
}
.navbar-brand img {
width: 200px;
height: 200px;
padding: 0 25px;
}
.oi {
width: 2rem;
font-size: 1.1rem;
color: var(--Mistox-White);
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
text-decoration: none;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep a {
color: var(--Mistox-White);
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
transition-duration: 0.5s;
text-decoration: none;
padding-left: 20px;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.25);
color: var(--Mistox-White);
}
.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: var(--Mistox-White);
}
.nav-login {
position: relative;
bottom: 10px;
left: 10px;
width: calc(100% - 20px);
padding-top: 10px;
}
.collapse {
display: none;
}
.nav-login-button {
width: calc(50% - 2.5px);
background-color: transparent;
border-radius: 5px;
border-color: transparent;
color: var(--Mistox-White);
transition-duration: 0.5s;
padding: 5px 0;
}
.nav-login-button:hover {
background-color: #FFFFFF50;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.collapse {
/* Never collapse the sidebar for wide screens */
display: block !important;
}
.nav-login {
position: absolute;
}
}
@media (max-width: 640px){
.navbar-brand img {
position: relative !important;
padding-left: calc(50% - 80px) !important;
height: 160px;
width: 160px;
}
.top-gradient {
background: linear-gradient(0deg, var(--Mistox-Dark), var(--Mistox-Offset) );
}
.bottom-row {
background: var(--Mistox-Medium);
}
}
@@ -0,0 +1,13 @@
@if(Statics.User.ID != -1 ) {
<PageDoesntExist>
</PageDoesntExist>
}
@code {
protected override void OnInitialized() {
if (Statics.User.ID == -1 ) {
Nav.NavigateTo( "/account/login?ReturnURL=Null" );
}
}
}
@@ -0,0 +1,7 @@
<PageTitle>Not found</PageTitle>
<h3>Nothing at this page</h3>
@code {
}
@@ -0,0 +1,19 @@
<div class="title-div">
<div>
<h1>@Title</h1>
</div>
@foreach(TitleBarDiv cur in ButtonList ) {
<button type="button" @onclick="()=>{ cur.invokeClicked(); base.StateHasChanged(); }">
<img src="@cur.Image" />
<span>@cur.Name</span>
</button>
}
</div>
@code {
[Parameter]
public string Title { get; set; } = string.Empty;
[Parameter]
public TitleBarDiv[] ButtonList { get; set; } = {};
}
@@ -0,0 +1,43 @@
.title-div {
width: 100%;
height: 45px;
background-color: #16212c;
font-size: 15px;
}
.title-div div {
float: left;
}
.title-div h1 {
font-size: 30px;
padding: 5px;
padding-left: 1rem;
margin: 0;
}
.title-div img{
height: 20px;
}
.title-div span {
position: relative;
top: calc(50% - 15px);
}
.title-div button {
padding: 5px;
margin: 5px;
margin-right: 10px;
float: right;
width: 120px;
border: none;
background-color: #16212c;
color: #d7d7d7;
border-radius: 4px;
transition-duration: 0.5s;
}
.title-div button:hover {
background-color: rgba(255,255,255,0.2);
}
+164
View File
@@ -0,0 +1,164 @@
@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 );
}
}
@@ -0,0 +1,82 @@
@page "/store/cart"
@attribute [Authorize]
<h3 class="cart-title">Cart</h3>
<div class="Spacer">
@foreach( MistoxWebsite.Shared.Cart obj in Statics.Carts ) {
<div class="cart-item">
<h1>@getItem(obj.ProductID)?.Name</h1>
@foreach( string cur in getItem( obj.ProductID )?.Images ) {
<img src="@cur" alt="Not found">
}
<h2>$@((Convert.ToSingle( getItem( obj.ProductID )?.Cost ) / 100f).ToString( "0.00" )) USD</h2>
<button @onclick="() => removeFromCart( obj.ProductID )"><span>Remove From Cart</span></button>
</div>
}
</div>
<div class="cart-total">
@if( Statics.Carts.Count > 0 ) {
<h3>Total : $@total.ToString( "0.00" )</h3>
<button @onclick="() => Chekout()">Checkout</button>
} else {
<h3>The cart is empty</h3>
}
</div>
@code {
[Parameter] public Func<bool>? ShouldReRender { get; set; }
[Parameter] public Catalog? parent { get; set; } = null;
float total = 0;
Product? getItem(int productID ) {
foreach(Product cur in Statics.Products ) {
if (cur.ID == productID ) {
return cur;
}
}
return null;
}
protected override void OnInitialized() {
total = 0;
foreach( MistoxWebsite.Shared.Cart obj in Statics.Carts ) {
foreach( Product item in Statics.Products ) {
if( obj.ProductID == item.ID ) {
total = total + (item.Cost / 100f);
break;
}
}
}
base.StateHasChanged();
}
public void RecalcTotal() {
OnInitialized();
}
public async void removeFromCart(int ID) {
for(int i=0; i<=Statics.Carts.Count; i++ ) {
if(ID == Statics.Carts[i].ProductID ) {
await Http.PostAsJsonAsync( "api/cart/remove", Statics.Carts [i] );
Statics.Carts.RemoveAt(i);
total = 0;
OnInitialized();
break;
}
}
if (parent != null ) {
parent?.Refresh();
} else {
base.StateHasChanged();
}
}
public void Chekout() {
Nav.NavigateTo("/store/payment/checkout");
}
}
@@ -0,0 +1,70 @@
.Spacer{
padding: 10px;
}
.cart-title{
width: 100%;
text-align: center;
}
.cart-item {
background-color: #222;
border: 1px solid #f00;
border-radius: 4px;
}
.cart-item h1 {
text-align: center;
padding-top: 5px;
}
.cart-item img {
width: calc(100% - 10px);
margin: 5px;
}
.cart-item h2 {
float: left;
width: 50%;
text-align: center;
}
.cart-item button{
width: calc( 50% - 40px);
height: 28px;
margin: 20px;
background-color: #ff0000;
border: none;
border-radius: 4px;
transition-duration: 0.5s;
}
.cart-item button:hover {
background-color: #500;
color: #fff;
}
.cart-total{
width: 100%;
}
.cart-total h3 {
width: 100%;
text-align: center;
}
.cart-total button {
width: 200px;
height: 50px;
margin-left: calc( 50% - 100px );
margin-bottom: 30px;
background-color: lawngreen;
border: none;
border-radius: 4px;
transition-duration: 0.5s;
}
.cart-total button:hover {
background-color: #040;
color: #fff;
}
@@ -0,0 +1,139 @@
@page "/store/catalog"
@attribute [Authorize]
<PageTitle>Catalog</PageTitle>
<TitleBar Title="Catalog" ButtonList=tbd></TitleBar>
<div class="gameCard-Grid">
@foreach(Product obj in Statics.Products ) {
<div class="gameCard">
<div style="position: relative;">
@if( obj.Images.Count > 1 ) {
<button class="gameCard-Prev" @onclick="() => Previous(obj)"></button>
}
@if( obj.Images.Count > 0 ) {
<img class="gameCard-Img" src="@obj.Images[obj.CurShowingIMG]" alt="Not found">
}
@if( obj.Images.Count > 1 ) {
<button class="gameCard-Next" @onclick="() => Next(obj)"></button>
}
</div>
<h1 class ="gameCard-Name">@obj.Name</h1>
<h2 class="gameCard-Desc">@obj.Description</h2>
<h2 class="gameCard-Price">$@((Convert.ToSingle(obj.Cost)/100f).ToString("0.00"))</h2>
@if ( isOwned(obj.ID) ) {
<button class="gameCard-Button" @onclick="() => { Download(); }">Owned</button>
} else {
@if( isInCart( obj.ID ) ) {
<button class="gameCard-Button" disabled="">Already In Cart</button>
} else {
<button class="gameCard-Button" @onclick="() => onAddToCart( obj.ID )">Add To Cart</button>
}
}
<AuthorizeView Roles="Admin">
<button style="width: calc(100% - 10px); margin: 5px;" @onclick="() => gotoEdit(obj.ID )">
Edit
</button>
</AuthorizeView>
</div>
}
<AuthorizeView Roles="Admin">
<button @onclick="gotoNew">New</button>
</AuthorizeView>
</div>
<div class="@CartCssClass">
<Cart @ref="thisCart" ShouldReRender="ShouldRender" parent="this"></Cart>
</div>
@code {
Cart? thisCart;
bool CartOpen = false;
string? CartCssClass => CartOpen ? "cartopen" : "cartclosed";
TitleBarDiv[] tbd = new TitleBarDiv[2];
protected override void OnInitialized() {
tbd [0] = new TitleBarDiv { Name = "Cart", Image="/img/cart.png" };
tbd [0].onClicked += async ( object? o, EventArgs e ) => {
V2 dim = await JS.InvokeAsync<V2>("getWindowSize");
Console.WriteLine("Screen width : " + dim.width);
if (dim.width <= 640 ) {
Nav.NavigateTo( "/store/cart" );
}
CartOpen = !CartOpen;
base.StateHasChanged();
};
tbd[1] = new TitleBarDiv { Name = "Downloads" };
tbd[1].onClicked += (object? o, EventArgs e) => {
Nav.NavigateTo("/project/downloads");
};
}
public void Download() {
Nav.NavigateTo( "/project/downloads" );
}
public void Refresh() {
base.StateHasChanged();
}
public void Previous( Product obj ) {
obj.CurShowingIMG--;
if (obj.CurShowingIMG < 0 ) {
obj.CurShowingIMG = obj.Images.Count - 1;
}
}
public void Next( Product obj ) {
obj.CurShowingIMG++;
if (obj.CurShowingIMG > obj.Images.Count - 1 ) {
obj.CurShowingIMG = 0;
}
}
public bool isOwned(int ID ) {
if(Statics.Owned.Count > 0 ) {
foreach( MistoxWebsite.Shared.Receipt cur in Statics.Owned ) {
if( cur.ProductID == ID ) {
return true;
}
}
}
return false;
}
public bool isInCart(int id) {
foreach( MistoxWebsite.Shared.Cart cur in Statics.Carts ) {
if (cur.ProductID == id ) {
return true;
}
}
return false;
}
public async void onAddToCart( int ID ) {
MistoxWebsite.Shared.Cart item = new MistoxWebsite.Shared.Cart {
ProductID = ID,
AccountID = Statics.User.ID
};
await Http.PostAsJsonAsync( "api/cart/add", item );
Statics.Carts.Add(item);
base.StateHasChanged();
thisCart?.RecalcTotal();
}
void gotoNew() {
Nav.NavigateTo( "/admin/product/create" );
}
void gotoEdit(int objId ) {
Nav.NavigateTo( "/admin/product/edit?ProductID=" + objId );
}
class V2 {
public int width{ get; set; }
public int height{ get; set; }
}
}
@@ -0,0 +1,123 @@
.gameCard {
position: relative;
background-color: var(--Secondary-Button-Color);
float: left;
box-sizing: border-box;
margin: 0;
padding: 0;
width: 100%;
border-radius: 10px;
break-inside: avoid;
margin-bottom: 2rem;
}
.gameCard-Name {
width: 100%;
text-align: left;
font-size: 25px;
padding: 5px 0 0 5px;
background-color: rgba(0,0,0,.1);
}
.gameCard-Grid {
column-count: 4;
column-gap: 2rem;
padding-top: 20px;
width: calc(100% - 40px);
margin-left: 20px;
}
@media (max-width: 1400px) {
.gameCard-Grid {
column-count: 3;
padding-top: 20px;
width: calc(100% - 40px);
margin-left: 20px;
}
}
@media (max-width: 1100px) {
.gameCard-Grid {
column-count: 2;
padding-top: 20px;
width: calc(100% - 40px);
margin-left: 20px;
}
}
@media (max-width: 900px) {
.gameCard-Grid {
column-count: 1;
padding-top: 20px;
width: calc(100% - 40px);
margin-left: 20px;
}
}
.gameCard-Img {
width: 100%;
border-radius: 10px 10px 0 0;
}
.gameCard-Next,
.gameCard-Prev {
background-color: transparent;
color: #fff;
padding: 16px;
margin-top: -22px;
font-size: 18px;
font-weight: bold;
border: none;
transition: background-color 0.6s ease;
}
.gameCard-Next:hover,
.gameCard-Prev:hover {
background-color: rgba(0, 0, 0, 0.5);
}
.gameCard-Prev {
position: absolute;
top: 50%;
}
.gameCard-Next {
position: absolute;
top: 50%;
right: 0;
}
.gameCard-Desc {
font-size: 13px;
margin: 5px;
color: #ccc;
}
.gameCard-Price {
width: calc(50% - 10px);
float: left;
margin: 5px;
text-align: center;
margin-bottom: 10px;
}
.gameCard-Button {
width: 40%;
margin: 5px 5%;
height: 38.4px;
}
.cartopen {
position: absolute;
background: #2c2946AA;
right: 10px;
top: 55px;
width: 400px;
border-radius: 5px;
backdrop-filter: blur(3px);
border: 1px solid #00f;
}
.cartclosed {
display: none;
}
@@ -0,0 +1,36 @@
@page "/store/payment/checkout"
@attribute [Authorize]
@if( loaded ) {
<iframe id="PaymentFrame" class="PaymentFrame" src="@PaymentIFrame"></iframe>
}
@code {
public string PaymentIFrame { get; set; } = "";
public bool loaded = false;
protected override async Task OnInitializedAsync() {
if (Statics.Carts.Count == 0 ) {
Nav.NavigateTo("/store/cart");
}
AuthenticationState user = await Auth.GetAuthenticationStateAsync();
string email = string.Empty;
foreach(Claim cur in user.User.Claims ) {
if (cur.Type == ClaimTypes.Email ) {
email = cur.Value;
break;
}
}
string buildingURL = "https://mistox.net/PaymentFrame.html?userID=" + email;
PaymentIFrame = buildingURL;
loaded = true;
await base.OnInitializedAsync();
}
protected override async void OnAfterRender( bool firstRender ) {
if( firstRender ) {
await JS.InvokeVoidAsync( "PaymentLoaded" );
}
base.OnAfterRender( firstRender );
}
}
@@ -0,0 +1,7 @@
.PaymentFrame{
position: relative;
left: 50px;
width: calc(100% - 100px);
margin-top: 50px;
background-color: #808080;
}
@@ -0,0 +1,25 @@
@page "/store/payment/success"
<h3>Payment Success</h3>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string? ReceiptID { get; set; }
/*
protected override async Task OnInitializedAsync() {
string resultBody = await (await Http.PostAsync( "/api/stripe/getreceipt", new StringContent(ReceiptID) )).Content.ReadAsStringAsync();
Receipt receipt = JsonConvert.DeserializeObject<Receipt>( resultBody );
if (receipt.Payment.Status == "succeeded" ) {
// Add the product passed to the database
}
Console.WriteLine( "test" );
}
*/
}
@@ -0,0 +1,71 @@
@page "/store/receipt"
<h3>Receipt</h3>
<div>
<div id="AppName">
<h1>Mistox</h1>
<h2>https://mistox.net</h2>
<h2>For Support or questions please email</h2>
<h2>derek@mistox.net</h2>
</div>
<hr />
<div id="PaymentDetails">
<h2>Payment Method: @PayMethod</h2>
<h3>Card Used: @Last4</h3>
</div>
<hr />
<div>
<h1>@ItemTitle:</h1><h2>$@ItemCost</h2>
<hr />
<h1>Subtotal:</h1><h2>$@ItemCost</h2>
<h1>Amount Charged:</h1><h2>$@ItemCost</h2>
</div>
<hr />
<div>
<h1>Receipt ID: @ReceiptID</h1>
<h1>Purchase Time: @PurchaseDateTime</h1>
<h1>Customer: @CustomerID</h1>
</div>
<hr />
<div>
<h1>Thank you</h1>
</div>
</div>
@code {
[Parameter]
[SupplyParameterFromQuery]
public string ReceiptID { get; set; } = "";
public string PurchaseDateTime{ get; set; } = "";
public string CustomerID { get; set; } = "";
public string ItemTitle { get; set; } = "";
public string ItemCost { get; set; } = "";
public string PayMethod { get; set; } = "";
public string Last4 { get; set; } = "";
protected override void OnInitialized() {
/*
string resultBody = await (await Http.PostAsync( "/api/stripe/getreceipt", new StringContent(ReceiptID) )).Content.ReadAsStringAsync();
ReceiptResponse receipt = JsonConvert.DeserializeObject<ReceiptResponse>( resultBody );
if (receipt != null ) {
if( receipt.Succeeded == "succeeded" ) {
PurchaseDateTime = receipt.PurchaseDateTime;
CustomerID = receipt.CustomerID;
ItemTitle = receipt.ItemTitle;
ItemCost = receipt.ItemCost;
PayMethod = receipt.PayMethod;
Last4 = receipt.Last4;
} else {
Nav.NavigateTo( "/" );
}
} else {
Nav.NavigateTo( "/?ReceiptNotFound" );
}
*/
}
}
+23
View File
@@ -0,0 +1,23 @@
using MistoxWebsite.Client;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Components.Authorization;
using MistoxWebsite.Client.AuthState;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>( "#app" );
builder.RootComponents.Add<HeadOutlet>( "head::after" );
builder.Services.AddScoped(
sp => new HttpClient {
BaseAddress = new Uri(
builder.HostEnvironment.BaseAddress
)
}
);
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>();
await builder.Build().RunAsync();
@@ -0,0 +1,49 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using MistoxWebsite.Shared;
using Newtonsoft.Json;
namespace MistoxWebsite.Client.AuthState {
public class AuthStateProvider : AuthenticationStateProvider {
private readonly HttpClient _httpClient;
public AuthStateProvider( HttpClient httpClient ) {
_httpClient = httpClient;
}
// Called initially on website load
public override async Task<AuthenticationState> GetAuthenticationStateAsync() {
HttpResponseMessage test = await _httpClient.PostAsync("api/pageload", new StringContent("") );
if( test.IsSuccessStatusCode ) {
string outtest = await test.Content.ReadAsStringAsync();
PageLoadObject? t = JsonConvert.DeserializeObject<PageLoadObject>(outtest);
if( t != null && t.products != null && t.receipts != null && t.user != null && t.Cart != null && t.claims != null ) {
Statics.Statics.Products = t.products;
Statics.Statics.Owned = t.receipts;
Statics.Statics.User = t.user;
Statics.Statics.Carts = t.Cart;
List<Claim> claims = new List<Claim>() {
new Claim(ClaimTypes.Name, t.claims.UserName),
new Claim(ClaimTypes.Email, t.claims.Email),
new Claim("emailverified", t.claims.EmailVerified),
new Claim(ClaimTypes.Role, t.claims.Role),
new Claim("LockAccount", t.claims.FailedPasswordLock),
};
AuthenticationState temp1 =
new AuthenticationState(
new ClaimsPrincipal(
new ClaimsIdentity(claims, "serverAuth"
)
)
);
return temp1;
}
}
AuthenticationState temp2 = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
return temp2;
}
}
}
@@ -0,0 +1,21 @@
using MistoxWebsite.Shared;
namespace MistoxWebsite.Client.Statics {
public class Statics {
public static Account User = new Account(){ ID=-1 };
public static List<Product> Products = new List<Product>();
public static List<Receipt> Owned = new List<Receipt>();
public static List<Cart> Carts = new List<Cart>();
}
public class TitleBarDiv {
public string Name { get; set; } = string.Empty;
public string Image { get; set; } = string.Empty;
public event EventHandler? onClicked = null;
public void invokeClicked() {
onClicked?.Invoke( new object(), new EventArgs() );
}
}
}
+26
View File
@@ -0,0 +1,26 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using MistoxWebsite.Client
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using System.Security.Claims
@using MistoxWebsite.Client.AuthState
@using MistoxWebsite.Client.Statics
@using System.Security.Cryptography
@using System.Text
@using MistoxWebsite.Shared
@using Newtonsoft.Json
@using MistoxWebsite.Client.Pages.Shared
@inject IJSRuntime JS
@inject NavigationManager Nav
@inject HttpClient Http
@inject AuthenticationStateProvider Auth
@@ -0,0 +1,387 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Shared;
using System.Security.Claims;
using MistoxWebsite.Server.Services;
using MistoxWebsite.Server.Services.DatabaseService;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace MistoxWebsite.Server.Controllers {
[ApiController]
public class AuthenticationController : ControllerBase {
DatabaseService _accountContext;
EmailService _emailContext;
public AuthenticationController( DatabaseService DatabaseContext, EmailService emailContext ) {
_accountContext = DatabaseContext;
_emailContext = emailContext;
}
// In Account -> References UserName / PasswordHash
// Out Account
[Route( "api/account/login" )]
[HttpPost]
public async Task<ActionResult<Account>> Login( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
if( test.EmailVerified == true ) {
if( test.SiteData.FailedPasswordLock ) {
if( test.SiteData.CurrentPasswordAttempts >= test.SiteData.PasswordAttempts ) {
return new Account() { Error = "Too many failed password attempts. Please reset your password" };
}
}
if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) {
test.SiteData.CurrentPasswordAttempts = 0;
await _accountContext.SetAccount( test );
AccountClaims aClaims = await getClaims(test.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),
new Claim("ID", test.ID.ToString())
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal( new ClaimsIdentity( claims, "serverAuth" ) ),
new AuthenticationProperties {
AllowRefresh = true,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddDays( 32 ),
IsPersistent = true,
}
);
return test;
} else {
test.SiteData.CurrentPasswordAttempts += 1;
await _accountContext.SetAccount( test );
return new Account() { Error = "Wrong password" };
}
}else{
await SendVerify(test);
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" };
} catch( Exception ex ) {
return new Account() { Error = ex.Message };
}
}
// In Account -> References UserName / PasswordHash
// Out Account
[Route( "api/account/session" )]
[HttpPost]
public async Task<ActionResult<Account>> LoginSession( [FromBody] Account request ) {
try {
Account? test = await _accountContext.GetAccount(request.UserName.ToLower());
if( test != null ) {
if( request.PasswordHash == test.PasswordHash ) {
return test;
} else {
test.SiteData.CurrentPasswordAttempts += 1;
await _accountContext.SetAccount( test );
return new Account() { Error = "Wrong password" };
}
}
return new Account() { Error = "User doesn't exist" };
} catch( Exception ex ) {
return new Account() { Error = ex.Message };
}
}
// In Account
// Out List<String>
[Route( "api/account/claims" )]
[HttpPost]
public async Task<ActionResult<AccountClaims>> Claims( [FromBody] Account Account ) {
AccountClaims claims = await getClaims(Account.ID);
return claims;
}
async Task<AccountClaims> getClaims( int AccountID ) {
try {
Account? test = await _accountContext.GetAccountByID(AccountID);
if( test != null ) {
AccountClaims aClaims = new AccountClaims() {
UserName = test.UserName,
Email = test.Email,
Role = test.SiteData.Role
};
aClaims.EmailVerified = test.EmailVerified ? "1" : "0";
aClaims.FailedPasswordLock = test.SiteData.FailedPasswordLock ? "1" : "0";
return aClaims;
}
return new AccountClaims();
} catch {
return new AccountClaims();
}
}
// 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.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 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;
}
}
}
}
@@ -0,0 +1,41 @@
using MistoxWebsite.Server.Services.DatabaseService;
using System.Security.Claims;
using MistoxWebsite.Shared;
using Microsoft.AspNetCore.Mvc;
namespace MistoxWebsite.Server.Controllers {
[ApiController]
public class PageLoad : ControllerBase {
DatabaseService _databaseService;
public PageLoad( DatabaseService context ) {
_databaseService = context;
}
[Route( "api/pageload" )]
[HttpPost]
public async Task<ActionResult<PageLoadObject>> onPageLoad() {
try {
if( User.Identity != null && User.Identity.IsAuthenticated ) {
string? id = User.FindFirstValue( "ID" );
if( !string.IsNullOrEmpty( id ) ) {
PageLoadObject test = await _databaseService.getPageLoadObject(int.Parse(id));
if (test.user != null){
test.Cart = await _databaseService.GetCart( test.user );
if( test != null ) {
return test;
}
}
}
}
return NotFound();
} catch (Exception e) {
Console.WriteLine(e.ToString());
return NotFound();
}
}
}
}
@@ -0,0 +1,165 @@
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Shared;
using Newtonsoft.Json;
using Stripe;
using Stripe.Climate;
using Stripe.Tax;
namespace MistoxWebsite.Server.Controllers {
[ApiController]
public class PaymentController : ControllerBase {
DatabaseService _databaseService;
public PaymentController( DatabaseService databaseService ) {
_databaseService = databaseService;
}
// Charges
[Route( "api/getCheckoutToken" )]
[HttpPost]
public async Task<string> GetPaymentKey( [FromQuery] string userID ) {
// Stored Variables
string OrderNumber = Guid.NewGuid().ToString().Substring(0,10);
int subtotal = 0;
// Get the user purchasing the items
string UserID = "";
Shared.Account? acc = await _databaseService.GetAccount(userID);
if( acc != null ) {
UserID = acc.ID.ToString();
List<Cart> cart = await _databaseService.GetCart( acc );
// build Recipt and calculate Tax
var options = new CalculationCreateOptions{
Currency = "usd",
CustomerDetails = new CalculationCustomerDetailsOptions{
AddressSource = "billing",
},
Expand = new List<string>(){ "line_items" },
LineItems = new List<CalculationLineItemOptions>()
};
List<int> prods = new List<int>();
// Add items to receipt
foreach( Cart items in cart ) {
Shared.Product? product = await _databaseService.GetProduct( items.ProductID );
if (product != null){
prods.Add(product.ID);
if( product != null ) {
subtotal += product.Cost;
options.LineItems.Add( new CalculationLineItemOptions {
Amount = product.Cost,
TaxCode = "txcd_10201000", // Tax code for downloadable digital games
Quantity = 1,
Reference = product.Name,
TaxBehavior = "exclusive"
} );
}
}
}
var service = new CalculationService();
Calculation result = service.Create( options );
string csv = "";
foreach (int cur in prods ) {
csv = csv + cur + ",";
}
// Crate Payment Intent
PaymentIntentCreateOptions paymentIntent = new PaymentIntentCreateOptions(){
Amount = result.AmountTotal,
Currency = "usd",
Metadata = new Dictionary<string, string> {
{ "ordernumber", OrderNumber },
{ "user", UserID },
{ "products", csv },
{ "subtotal", subtotal.ToString() },
{ "total", result.AmountTotal.ToString() }
},
StatementDescriptor = "Mistox.Net #" + OrderNumber
};
PaymentIntentService intentService = new PaymentIntentService();
PaymentIntent x = await intentService.CreateAsync( paymentIntent );
return x.ClientSecret;
}
return "0";
}
[Route( "/api/payment/response" )]
[HttpPost]
public async Task<IActionResult> paymentWebhook() {
try {
const string endpointSecret = "whsec_HCO7uv2BPIPmUPOiSg9tfwLZul8usCGG";
string body = await new StreamReader(Request.Body).ReadToEndAsync();
Event e = EventUtility.ConstructEvent( body, Request.Headers["Stripe-Signature"], endpointSecret );
if( e.Type == "payment_intent.succeeded" ) {
// Extract Data from payment confirm
PaymentIntent intent = (PaymentIntent)e.Data.Object;
string orderNumber = "";
int userID = 0;
List<int> productIDs = new List<int>();
int subtotal = 0;
int total = 0;
KeyValuePair<string, string>[] y = intent.Metadata.ToArray();
foreach( KeyValuePair<string, string> cur in y ) {
string val = cur.Key;
if( val == "ordernumber" ) {
orderNumber = cur.Value;
} else if( val == "user" ) {
userID = int.Parse( cur.Value );
} else if( val == "products" ) {
string[] products = cur.Value.Split(',');
foreach( string product in products ) {
if ( !string.IsNullOrEmpty(product) ) {
productIDs.Add( Convert.ToInt32( product ) );
}
}
} else if( val == "subtotal" ) {
subtotal = int.Parse( cur.Value );
} else if( val == "total" ) {
total = int.Parse( cur.Value );
}
}
// Clear the cart
Shared.Account account = new Shared.Account{
ID = userID
};
await _databaseService.ClearCart( account );
// Add data to misox receipt
for( int i = 0; i < productIDs.Count; i++ ) {
int product = productIDs[i];
await _databaseService.NewReceipt( new Receipt {
AccountID = userID,
ProductID = product,
ReceiptID = orderNumber,
Time = DateTime.Now,
TaxAmount = total - subtotal,
TotalCost = total,
LineItem = i
} );
}
} else {
Console.WriteLine( "Unhandled event type: {0}", e.Type );
}
return Ok();
} catch( Exception ex ) {
return Content(ex.ToString());
}
}
}
}
@@ -0,0 +1,261 @@
using Microsoft.AspNetCore.Mvc;
using MistoxWebsite.Server.Services.DatabaseService;
using MistoxWebsite.Shared;
using Newtonsoft.Json;
using System.Security.Claims;
using System.Text;
namespace MistoxWebsite.Server.Controllers {
[ApiController]
public class ProductController : ControllerBase {
DatabaseService _databaseService;
public static List<Product> CatalogItems = new List<Product>();
public ProductController( DatabaseService databaseService ) {
_databaseService = databaseService;
}
[Route( "api/cart/get" )]
[HttpPost]
public async Task<List<Cart>> GetCart( [FromBody] Account acc ) {
try {
List<Cart> cart = await _databaseService.GetCart( acc );
return cart;
} catch {
return new List<Cart>();
}
}
[Route( "api/cart/add" )]
[HttpPost]
public async Task AddCart( [FromBody] Cart cart ) {
try {
await _databaseService.AddToCart( cart );
}catch {
}
}
[Route( "api/cart/remove" )]
[HttpPost]
public async Task RemoveCart( [FromBody] Cart cart ) {
try {
await _databaseService.RemoveFromCart( cart );
} catch {
}
}
[Route( "api/cart/clear" )]
[HttpPost]
public async Task ClearCart( [FromBody] Account acc ) {
try {
await _databaseService.ClearCart( acc );
} catch {
}
}
[Route( "api/product/create" )]
[HttpPost]
public async Task<ActionResult<string>> CreateProduct( [FromBody] Product obj ) {
try {
await _databaseService.NewProduct( obj );
await UpdateStore();
return "Success";
} catch {
return "Failed";
}
}
[Route( "api/product/update" )]
[HttpPost]
public async Task<ActionResult<string>> UpdateProduct( [FromBody] Product obj ) {
try {
await _databaseService.UpdateProduct( obj );
await UpdateStore();
return "Success";
} catch {
return "Failed";
}
}
[Route( "api/product/get" )]
[HttpPost]
public ActionResult<Product> GetProduct( [FromBody] Product product ) {
try {
foreach( Product? prod in CatalogItems ) {
if( product.ID == prod.ID ) {
return prod;
}
}
product.ID = -1;
return product;
} catch {
return new Product();
}
}
[Route( "api/product/getall" )]
[HttpPost]
public ActionResult<List<Product>> GetAllProducts() {
try {
return CatalogItems;
} catch {
return new List<Product>();
}
}
[Route( "api/product/getowned" )]
[HttpPost]
public async Task<ActionResult<List<Receipt>>> GetOwnedProduct() {
try {
if( User.Identity != null && User.Identity.IsAuthenticated ) {
string? email = User.FindFirstValue(ClaimTypes.Email);
if( !string.IsNullOrEmpty( email ) ) {
Account? test = await _databaseService.GetAccount(email);
if( test != null ) {
List<Receipt> returned = await _databaseService.GetAllReceipts(test);
return returned;
}
}
}
return new List<Receipt>();
} catch {
return new List<Receipt>();
}
}
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 ) {
if ( outer.Length >= inner.Length ) {
for ( int i=0; i<outer.Length-inner.Length; i++ ) {
if ( outer.Substring(i, inner.Length) == inner ) {
return true;
}
}
}
return false;
}
[Route( "api/product/download" )]
[HttpGet]
public async Task<ActionResult> Download( [FromQuery] string Product ) {
try {
if( User.Identity != null && User.Identity.IsAuthenticated ) {
string? email = User.FindFirstValue(ClaimTypes.Email);
if( !string.IsNullOrEmpty( email ) ) {
Account? user = await _databaseService.GetAccount(email);
if (user != null){
List<Product>? games = await _databaseService.GetAllProducts();
foreach( Product product in games ) {
if ( contains( Product, product.URL ) ) {
Receipt? receipt = await _databaseService.GetReceipt(user, product);
if( receipt != null ) {
FileStream fileStream = new FileStream(_FolderRoot + Product, FileMode.Open, FileAccess.Read);
return new FileStreamResult( fileStream, "application/octet-stream" ) {
FileDownloadName = fileStream.Name
};
}
break;
}
}
}
}
return Unauthorized();
}
return Unauthorized();
} catch {
return NotFound();
}
}
[Route( "api/product/hotreload" )]
[HttpPost] // Not implimented in admin panel
public async Task UpdateStore() {
await HotReload( _databaseService );
}
public static async Task HotReload( DatabaseService ds ) {
CatalogItems = new List<Product>();
try {
CatalogItems = await ds.GetAllProducts();
} catch {
CatalogItems.Add( new Product() { ID = 0, Name = "offline prod1", Cost = 100, Description = "offline desc" } );
CatalogItems.Add( new Product() { ID = 1, Name = "offline prod2", Cost = 100, Description = "offline desc" } );
CatalogItems.Add( new Product() { ID = 2, Name = "offline prod3", Cost = 100, Description = "offline desc" } );
};
}
}
}
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.3.0" />
<PackageReference Include="MySql.Data" Version="9.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Stripe.net" Version="48.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MistoxWebsite.Client\MistoxWebsite.Client.csproj" />
<ProjectReference Include="..\MistoxWebsite.Shared\MistoxWebsite.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\css\bootstrap\bootstrap.min.css" />
<None Include="wwwroot\css\bootstrap\bootstrap.min.css.map" />
<None Include="wwwroot\css\open-iconic\FONT-LICENSE" />
<None Include="wwwroot\css\open-iconic\font\css\open-iconic-bootstrap.min.css" />
<None Include="wwwroot\css\open-iconic\font\fonts\open-iconic.eot" />
<None Include="wwwroot\css\open-iconic\font\fonts\open-iconic.otf" />
<None Include="wwwroot\css\open-iconic\font\fonts\open-iconic.svg" />
<None Include="wwwroot\css\open-iconic\font\fonts\open-iconic.ttf" />
<None Include="wwwroot\css\open-iconic\font\fonts\open-iconic.woff" />
<None Include="wwwroot\css\open-iconic\ICON-LICENSE" />
<None Include="wwwroot\css\open-iconic\README.md" />
</ItemGroup>
</Project>
+75
View File
@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using MistoxWebsite.Server.Controllers;
using MistoxWebsite.Server.Services;
using MistoxWebsite.Server.Services.DatabaseService;
using Stripe;
var builder = WebApplication.CreateBuilder(args);
// Disable null warnings becuse string.IsNullOrEmpty checks for NULL or Empty
#pragma warning disable CS8600
#pragma warning disable CS8604
// Database Service
string? _dbserver = Environment.GetEnvironmentVariable("MySQLServer");
string dbserver = !string.IsNullOrEmpty(_dbserver) ? _dbserver : "localhost";
string? _dbuser = Environment.GetEnvironmentVariable("MySQLUser");
string dbUser = !string.IsNullOrEmpty(_dbuser) ? _dbuser : "root";
string? _dbdatabase = Environment.GetEnvironmentVariable("MySQLDatabase");
string dbdatabase = !string.IsNullOrEmpty(_dbdatabase) ? _dbdatabase : "mistox";
string? _dbpass = Environment.GetEnvironmentVariable("MySQLPass");
string dbPass = !string.IsNullOrEmpty(_dbpass) ? _dbpass : "oasv34$8gpv023dd";
string connStr = "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;";
DatabaseService databaseService = new DatabaseService( connectionString: connStr );
await ProductController.HotReload( databaseService );
builder.Services.Add( new ServiceDescriptor( typeof( DatabaseService ), databaseService ) );
// Email Service
string? _eServer = Environment.GetEnvironmentVariable("EmailServer");
string EmailServer = !string.IsNullOrEmpty(_eServer) ? _eServer : "gmail.com";
string? _ePort = Environment.GetEnvironmentVariable("EmailPort");
int EmailPort = !string.IsNullOrEmpty(_ePort) ? Convert.ToInt32(_ePort) : 587;
string? _eAddress = Environment.GetEnvironmentVariable("EmailAddress");
string EmailAddress = !string.IsNullOrEmpty(_eAddress) ? _eAddress : "no-reply@mistox.com";
string? _ePassword = Environment.GetEnvironmentVariable("EmailPassword");
string EmailPassword = !string.IsNullOrEmpty(_ePassword) ? _ePassword : "";
EmailService Emailservice = new EmailService( EmailServer, EmailPort, EmailAddress, EmailPassword );
builder.Services.Add( new ServiceDescriptor( typeof( EmailService ), Emailservice ));
// Payment Service
string? StripeKey = Environment.GetEnvironmentVariable("StripeKey");
StripeConfiguration.ApiKey = StripeKey;
// Authentication Service
builder.Services.AddAuthentication( options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
} ).AddCookie();
builder.Services.AddCors( o => o.AddDefaultPolicy( builder => {
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
} ) );
// Pages Service
builder.Services.AddControllers();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if( app.Environment.IsDevelopment() ) {
app.UseWebAssemblyDebugging();
} else {
app.UseHsts();
}
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseCors();
app.UseAuthentication();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
@@ -0,0 +1,19 @@
{
"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
}
}
}
@@ -0,0 +1,194 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<Account?> GetAccount( string UserNameOrEmail ) {
Account? account = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT *
FROM Account
Left Join WebsiteData
On Account.ID = WebsiteData.AccountID
WHERE UserName = @UorE OR Email = @UorE;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@UorE", UserNameOrEmail);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
string _username = reader.GetString("UserName");
string _email = reader.GetString("Email");
bool _emailVerified = reader.GetBoolean("EmailVerified");
string _passwordhash = reader.GetString("PasswordHash");
bool _failedpasswordlock = reader.GetBoolean( "FailedPasswordLock" );
int _passwordattempts = reader.GetInt32( "PasswordAttempts" );
int _curpasswordattempts = reader.GetInt32( "CurrentPasswordAttempts" );
string _role = reader.GetString( "Role" );
string _emailtoken = reader.GetString( "EmailToken" );
account = new Account() {
ID = _id,
UserName = _username,
Email = _email,
EmailVerified = _emailVerified,
PasswordHash = _passwordhash,
SiteData = new WebSiteData() {
AccountID = _id,
CurrentPasswordAttempts = _curpasswordattempts,
PasswordAttempts = _passwordattempts,
EmailToken = _emailtoken,
FailedPasswordLock = _failedpasswordlock,
Role = _role,
}
};
}
}
}
return account;
}
public async Task<Account?> GetAccountByID( int ID ) {
Account? account = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT *
FROM Account
Left Join WebsiteData
On Account.ID = WebsiteData.AccountID
WHERE ID = @ID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@ID", ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
string _username = reader.GetString("UserName");
string _email = reader.GetString("Email");
bool _emailVerified = reader.GetBoolean("EmailVerified");
string _passwordhash = reader.GetString("PasswordHash");
bool _failedpasswordlock = reader.GetBoolean( "FailedPasswordLock" );
int _passwordattempts = reader.GetInt32( "PasswordAttempts" );
int _curpasswordattempts = reader.GetInt32( "CurrentPasswordAttempts" );
string _role = reader.GetString( "Role" );
string _emailtoken = reader.GetString( "EmailToken" );
account = new Account() {
ID = _id,
UserName = _username,
Email = _email,
EmailVerified = _emailVerified,
PasswordHash = _passwordhash,
SiteData = new WebSiteData() {
AccountID = _id,
CurrentPasswordAttempts = _passwordattempts,
PasswordAttempts = _passwordattempts,
EmailToken = _emailtoken,
FailedPasswordLock = _failedpasswordlock,
Role = _role,
}
};
}
}
}
return account;
}
public async Task SetAccount( Account Update ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
UPDATE Account SET
UserName = @UserName,
Email = @Email,
EmailVerified = @EmailVerified,
PasswordHash = @PasswordHash
WHERE ID = @ID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@UserName", Update.UserName);
cmd.Parameters.AddWithValue("@Email", Update.Email);
cmd.Parameters.AddWithValue("@EmailVerified", Update.EmailVerified);
cmd.Parameters.AddWithValue("@PasswordHash", Update.PasswordHash);
cmd.Parameters.AddWithValue("@ID", Update.ID);
await cmd.ExecuteNonQueryAsync();
await UpdateWebsiteData( Update, Update.SiteData );
}
}
public async Task NewAccount( Account Profile ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
int EmailVer = Profile.EmailVerified ? 1 : 0;
string command = @"
INSERT INTO Account
(UserName,Email,EmailVerified,PasswordHash)
VALUES
(@UserName,@Email,@EmailVerified,@PasswordHash);
SELECT ID FROM Account
WHERE UserName = @UserName;
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@UserName", Profile.UserName);
cmd.Parameters.AddWithValue("@Email", Profile.Email);
cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified);
cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
Profile.ID = _id;
}
}
await NewWebsiteData( Profile, Profile.SiteData );
}
}
public async Task DeleteAccount( Account Profile ) {
using( MySqlConnection connection = GetConnection() ) {
MySqlCommand cmd;
connection.Open();
string command = @"
DELETE FROM Account WHERE ID = @ID;
DELETE FROM AccountInventory WHERE AccountID = @ID;
DELETE FROM ProjectMistData WHERE AccountID = @ID;
DELETE FROM Cart WHERE AccountID = @ID;
DELETE FROM WebsiteData WHERE AccountID = @ID;
";
cmd = new MySqlCommand( command, connection );
cmd.Parameters.AddWithValue("@ID", Profile.ID);
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -0,0 +1,116 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
// Account inventory needs to know whether there is already an object with the specified PK before making a new item
// If item exists already update the one that already exists
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<List<UserInventory>> GetInventory( Account account, Product product ) {
List<UserInventory> list = new List<UserInventory>();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM AccountInventory
WHERE AccountID = @AccountID AND ProductID = @ProductID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
cmd.Parameters.AddWithValue("@ProductID", product.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
string _item = reader.GetString("Item");
int _quantity = reader.GetInt32("Quantity");
string _stats = reader.GetString("Stats");
list.Add( new UserInventory() {
Item = _item,
Quantity = _quantity,
Stats = _stats
} );
}
}
}
return list;
}
async Task UpdateInventory( MySqlConnection connection, AccountInventory item ) {
string command = @"
UPDATE AccountInventory
SET AccountID = @AccountID,
ProductID = @ProductID,
Item = @Item,
Quantity = @Quantity,
Stats = @Stats
WHERE (AccountID = @AccountID AND ProductID = @ProductID AND Item = @Item);
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", item.AccountID);
cmd.Parameters.AddWithValue("@ProductID", item.ProductID);
cmd.Parameters.AddWithValue("@Item", item.Item);
cmd.Parameters.AddWithValue("@Quantity", item.Quantity);
cmd.Parameters.AddWithValue("@Stats", item.Stats);
await cmd.ExecuteNonQueryAsync();
}
async Task NewInventory( MySqlConnection connection, AccountInventory item ) {
string command = @"
INSERT INTO AccountInventory (AccountID, ProductID, Item, Quantity, Stats)
VALUES
(@AccountID, @ProductID, @Item, @Quantity, @Stats);
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", item.AccountID);
cmd.Parameters.AddWithValue("@ProductID", item.ProductID);
cmd.Parameters.AddWithValue("@Item", item.Item);
cmd.Parameters.AddWithValue("@Quantity", item.Quantity);
cmd.Parameters.AddWithValue("@Stats", item.Stats);
await cmd.ExecuteNonQueryAsync();
}
// Test to see if reader read does what its supposed to
// Not fully implimented
public async Task SetInventory( Account account, Product game, List<UserInventory> Item ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
foreach( UserInventory item in Item ) {
bool exists = false;
MySqlCommand cmd = new MySqlCommand("SELECT * FROM AccountInventory WHERE AccountID = '" + account.ID + "' AND ProductID = '" + game.ID + "' AND Item = '" + item.Item.ToLower() + "'", connection);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
exists = reader.HasRows;
}
if( exists ) {
await UpdateInventory( connection, new AccountInventory() {
AccountID = account.ID,
ProductID = game.ID,
Item = item.Item,
Quantity = item.Quantity,
Stats = item.Stats
} );
} else {
await NewInventory( connection, new AccountInventory() {
AccountID = account.ID,
ProductID = game.ID,
Item = item.Item,
Quantity = item.Quantity,
Stats = item.Stats
} );
}
}
}
}
}
}
@@ -0,0 +1,82 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<List<Cart>> GetCart( Account account ) {
List<Cart> list = new List<Cart>();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM Cart
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
int _accountid = reader.GetInt32("AccountID");
int _productid = reader.GetInt32("ProductID");
list.Add( new Cart() {
ID = _id,
AccountID = _accountid,
ProductID = _productid
} );
}
}
}
return list;
}
public async Task AddToCart( Cart item ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
INSERT INTO Cart
(AccountID, ProductID)
VALUES
(@AccountID, @ProductID);
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", item.AccountID);
cmd.Parameters.AddWithValue("@ProductID", item.ProductID);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task RemoveFromCart( Cart item ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = "DELETE FROM Cart WHERE AccountID=" + item.AccountID + " AND ProductID=" + item.ProductID + ";";
MySqlCommand cmd = new MySqlCommand( command , connection);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task ClearCart( Account account ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
DELETE FROM Cart
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -0,0 +1,15 @@
using MySql.Data.MySqlClient;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public string ConnectionString {
get; set;
}
public DatabaseService( string connectionString ) {
ConnectionString = connectionString;
}
MySqlConnection GetConnection() {
return new MySqlConnection( ConnectionString );
}
}
}
@@ -0,0 +1,122 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<PageLoadObject> getPageLoadObject( int AccountID ) {
PageLoadObject account = new PageLoadObject();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM Account
INNER JOIN WebsiteData
ON Account.ID = WebsiteData.AccountID
WHERE ID = @AccountID
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", AccountID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32(0);
string _username = reader.GetString(1);
string _email = reader.GetString(2);
bool _emailVerified = reader.GetBoolean(3);
string _passwordhash = reader.GetString(4);
bool _failedPasswordLock = reader.GetBoolean(6);
int _passwordAttempts = reader.GetInt32(7);
int _currentPasswordAttempts = reader.GetInt32(8);
string _role = reader.GetString(9);
string _emailToken = reader.GetString(10);
account.claims = new AccountClaims() {
Email = _email,
EmailVerified = _emailVerified.ToString(),
FailedPasswordLock = _failedPasswordLock.ToString(),
Role = _role,
UserName = _username,
};
account.user = new Account() {
ID = _id,
UserName = _username,
Email = _email,
EmailVerified = _emailVerified,
PasswordHash = _passwordhash,
SiteData = new WebSiteData() {
AccountID = _id,
CurrentPasswordAttempts = _currentPasswordAttempts,
PasswordAttempts = _passwordAttempts,
EmailToken = _emailToken,
FailedPasswordLock = _failedPasswordLock,
Role = _role,
}
};
}
}
account.products = new List<Product>();
account.receipts = new List<Receipt>();
command = @"
SELECT * FROM Product
LEFT JOIN Receipt
ON ID = Receipt.ProductID
WHERE AccountID is Null or AccountID = @AccountID;
";
MySqlCommand cmd2 = new MySqlCommand(command, connection);
cmd2.Parameters.AddWithValue("@AccountID", AccountID);
using( DbDataReader reader = await cmd2.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _productID = !await reader.IsDBNullAsync(0) ? reader.GetInt32(0) : -1;
string _gameName = !await reader.IsDBNullAsync(1) ? reader.GetString(1) : "";
string _gameDesc = !await reader.IsDBNullAsync(2) ? reader.GetString(2) : "";
string _gameImg = !await reader.IsDBNullAsync(3) ? reader.GetString(3) : "";
int _gameCost = !await reader.IsDBNullAsync(4) ? reader.GetInt32(4) : 37707;
string _gameURL = !await reader.IsDBNullAsync(5) ? reader.IsDBNull(5) ? "" : reader.GetString(5) : "Something not common";
int _receiptAccountID = !await reader.IsDBNullAsync(6) ? reader.IsDBNull(6) ? -1 : reader.GetInt32(6) : -1;
string _receiptID = !await reader.IsDBNullAsync(8) ? reader.IsDBNull(8) ? "" : reader.GetString(8) : "";
DateTime _receiptTime = !await reader.IsDBNullAsync(10) ? reader.GetDateTime(10) : DateTime.Now;
string[] _imageList = _gameImg.Split('|', StringSplitOptions.RemoveEmptyEntries);
account.products.Add( new Product {
ID = _productID,
Cost = _gameCost,
Description = _gameDesc,
Name = _gameName,
URL = _gameURL,
Images = _imageList.ToList()
} );
if( _receiptAccountID != -1 ) {
account.receipts.Add( new Receipt {
AccountID = _receiptAccountID,
ProductID = _productID,
ReceiptID = _receiptID,
Time = _receiptTime
} );
}
}
}
}
return account;
}
}
}
@@ -0,0 +1,140 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<Product?> GetProduct( int ID ) {
Product? items = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM Product
WHERE ID = @ID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@ID", ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
string _name = reader.GetString("Name");
string _description = reader.GetString("Description");
string _images = reader.GetString("Images");
int _cost = reader.GetInt32("Cost");
string _url = reader.GetString("URL");
string[] _imageList = _images.Split('|', StringSplitOptions.RemoveEmptyEntries);
items = new Product() {
ID = _id,
Name = _name,
Description = _description,
Cost = _cost,
Images = _imageList.ToList(),
URL = _url
};
}
}
}
return items;
}
public async Task<List<Product>> GetAllProducts() {
List<Product> items = new List<Product>();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
MySqlCommand cmd = new MySqlCommand("SELECT * FROM Product", connection);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
string _name = reader.GetString("Name");
string _description = reader.GetString("Description");
string _images = reader.GetString("Images");
int _cost = reader.GetInt32("Cost");
string _url = reader.GetString("URL");
string[] _imageList = _images.Split('|', StringSplitOptions.RemoveEmptyEntries);
items.Add( new Product() {
ID = _id,
Name = _name,
Description = _description,
Cost = _cost,
Images = _imageList.ToList(),
URL = _url
} );
}
}
}
return items;
}
public async Task NewProduct( Product Item ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string buildingImages = "";
foreach( string cur in Item.Images ) {
buildingImages = buildingImages + "|" + cur;
}
string command = @"
INSERT INTO Product
(Name, Description, Images, Cost, URL)
VALUES
(@Name, @Description, @Images, @Cost, @URL);
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@Name", Item.Name);
cmd.Parameters.AddWithValue("@Description", Item.Description);
cmd.Parameters.AddWithValue("@Images", buildingImages);
cmd.Parameters.AddWithValue("@Cost", Item.Cost);
cmd.Parameters.AddWithValue("@URL", Item.URL);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task UpdateProduct( Product Item ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string buildingImages = "";
foreach( string cur in Item.Images ) {
buildingImages = buildingImages + "|" + cur;
}
string command = @"UPDATE Product SET
Name = @Name,
Description = @Description,
Images = @Images,
Cost = @Cost,
URL = @URL
WHERE ID = @ID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@Name", Item.Name);
cmd.Parameters.AddWithValue("@Description", Item.Description);
cmd.Parameters.AddWithValue("@Images", Item.Images);
cmd.Parameters.AddWithValue("@Cost", Item.Cost);
cmd.Parameters.AddWithValue("@URL", Item.URL);
cmd.Parameters.AddWithValue("@ID", Item.ID);
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -0,0 +1,71 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<ProjectMistData?> GetProjectMistData( int ID ) {
ProjectMistData? items = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM ProjectMistData
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("AccountID");
items = new ProjectMistData() {
AccountID = _id,
};
}
}
}
return items;
}
public async Task NewProjectMistData( ProjectMistData data ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
INSERT INTO ProjectMistData
(AccountID)
VALUES
(@AccountID);
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", data.AccountID);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task UpdateProjectMistData( ProjectMistData data ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
UPDATE ProjectMistData SET
AccountID = @AccountID
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", data.AccountID);
await cmd.ExecuteReaderAsync();
}
}
}
}
@@ -0,0 +1,170 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<List<Receipt>> GetAllReceipts( Account account ) {
List<Receipt> receipts = new List<Receipt> ();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM Receipt
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _accountid = reader.GetInt32("AccountID");
int _gameid = reader.GetInt32("ProductID");
string _receiptid = reader.GetString("ReceiptID");
int _lineitem = reader.GetInt32("LineItem");
DateTime _receiptdate = reader.GetDateTime("Time");
int _taxamount = reader.GetInt32("TaxAmount");
int _totalcost = reader.GetInt32("TotalCost");
receipts.Add( new Receipt() {
AccountID = _accountid,
ProductID = _gameid,
ReceiptID = _receiptid,
Time = _receiptdate,
TotalCost = _totalcost,
TaxAmount = _taxamount,
LineItem = _lineitem
} );
}
}
}
return receipts;
}
public async Task<List<ReceiptProduct>> GetAllReceiptsJoinedToProduct( Account account ) {
List<ReceiptProduct> join = new List<ReceiptProduct> ();
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM Receipt
LEFT JOIN Product
ON Receipt.ProductID = Product.ID
WHERE AccountID = @AccountID
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _accountid = !reader.IsDBNull( "AccountID" ) ? reader.GetInt32("AccountID") : -1;
int _gameid = !reader.IsDBNull( "ProductID" ) ? reader.GetInt32("ProductID") : 0;
string _receiptid = !reader.IsDBNull( "ReceiptID" ) ? reader.GetString("ReceiptID") : "";
int _lineitem = !reader.IsDBNull( "LineItem" ) ? reader.GetInt32("LineItem") : 0;
DateTime _receiptdate = !reader.IsDBNull( "Time" ) ? reader.GetDateTime("Time") : DateTime.Now;
int _taxamount = !reader.IsDBNull( "TaxAmount" ) ? reader.GetInt32("TaxAmount") : 0;
int _totalcost = !reader.IsDBNull( "TotalCost" ) ? reader.GetInt32("TotalCost") : 0;
int _id = !reader.IsDBNull( "ID" ) ? reader.GetInt32("ID") : 0;
string _name = !reader.IsDBNull( "Name" ) ? reader.GetString("Name") : "";
string _desc = !reader.IsDBNull( "Description" ) ? reader.GetString("Description") : "";
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";
join.Add( new ReceiptProduct() {
receipt = new Receipt {
AccountID = _accountid,
ProductID = _gameid,
ReceiptID = _receiptid,
Time = _receiptdate,
TotalCost = _totalcost,
TaxAmount = _taxamount,
LineItem = _lineitem
},
product = new Product() {
ID = _id,
Cost = _cost,
Description = _desc,
Name = _name,
URL = _url
}
} );
}
}
}
return join;
}
public async Task<Receipt?> GetReceipt( Account account, Product game ) {
Receipt? receipt = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROMReceipt
WHERE AccountID = @AccountID AND ProductID = @ProductID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
cmd.Parameters.AddWithValue("@ProductID", game.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _accountid = reader.GetInt32("AccountID");
int _gameid = reader.GetInt32("ProductID");
string _receiptid = reader.GetString("ReceiptID");
int _lineitem = reader.GetInt32("LineItem");
DateTime _receiptdate = reader.GetDateTime("Time");
int _taxamount = reader.GetInt32("TaxAmount");
int _totalcost = reader.GetInt32("TotalCost");
receipt = new Receipt() {
AccountID = _accountid,
ProductID = _gameid,
ReceiptID = _receiptid,
Time = _receiptdate,
TotalCost = _totalcost,
TaxAmount = _taxamount,
LineItem = _lineitem
};
}
}
}
return receipt;
}
public async Task NewReceipt( Receipt receipt ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
INSERT INTO Receipt
(AccountID, ProductID, ReceiptID, LineItem, TaxAmount, TotalCost, Time)
VALUES
(@AccountID, @ProductID, @ReceiptID, @LineItem, @TaxAmount, @TotalCost, @Time)
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", receipt.AccountID);
cmd.Parameters.AddWithValue("@ProductID", receipt.ProductID);
cmd.Parameters.AddWithValue("@ReceiptID", receipt.ReceiptID);
cmd.Parameters.AddWithValue("@LineItem", receipt.LineItem);
cmd.Parameters.AddWithValue("@TaxAmount", receipt.TaxAmount);
cmd.Parameters.AddWithValue("@TotalCost", receipt.TotalCost);
cmd.Parameters.AddWithValue("@Time", receipt.Time); // Just incase i need this in the future | receipt.Time.ToString( "yyyy-MM-dd hh:mm:ss" )
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -0,0 +1,115 @@
using MistoxWebsite.Shared;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace MistoxWebsite.Server.Services.DatabaseService {
public partial class DatabaseService {
public async Task<WebSiteData?> GetWebsiteData( Account account ) {
WebSiteData? webSiteData = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT * FROM WebsiteData
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = 0;
bool _failedpasswordlock = false;
int _passwordattempts = 5;
int _curpasswordattempts = 0;
string _role = "";
string _emailtoken = "";
if( !reader.IsDBNull( "AccountID" ) ) {
_id = reader.GetInt32( "AccountID" );
}
if( !reader.IsDBNull( "FailedPasswordLock" ) ) {
_failedpasswordlock = reader.GetBoolean( "FailedPasswordLock" );
}
if( !reader.IsDBNull( "PasswordAttempts" ) ) {
_passwordattempts = reader.GetInt32( "PasswordAttempts" );
}
if( !reader.IsDBNull( "CurrentPasswordAttempts" ) ) {
_curpasswordattempts = reader.GetInt32( "CurrentPasswordAttempts" );
}
if( !reader.IsDBNull( "Role" ) ) {
_role = reader.GetString( "Role" );
}
if( !reader.IsDBNull( "EmailToken" ) ) {
_emailtoken = reader.GetString( "EmailToken" );
}
webSiteData = new WebSiteData() {
AccountID = _id,
FailedPasswordLock = _failedpasswordlock,
CurrentPasswordAttempts = _curpasswordattempts,
PasswordAttempts = _passwordattempts,
EmailToken = _emailtoken,
Role = _role,
};
}
}
}
return webSiteData;
}
public async Task NewWebsiteData( Account account, WebSiteData data ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
INSERT INTO WebsiteData
(AccountID, FailedPasswordLock, PasswordAttempts, CurrentPasswordAttempts, Role, EmailToken)
VALUES
(@AccountID, @FailedPasswordLock, @PasswordAttempts, @CurrentPasswordAttempts, @Role, @EmailToken);
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
cmd.Parameters.AddWithValue("@FailedPasswordLock", data.FailedPasswordLock);
cmd.Parameters.AddWithValue("@PasswordAttempts", data.PasswordAttempts);
cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", data.CurrentPasswordAttempts);
cmd.Parameters.AddWithValue("@Role", data.Role);
cmd.Parameters.AddWithValue("@EmailToken", data.EmailToken);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task UpdateWebsiteData( Account account, WebSiteData data ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
UPDATE WebsiteData SET
FailedPasswordLock = @FailedPasswordLock,
PasswordAttempts = @PasswordAttempts,
CurrentPasswordAttempts = @CurrentPasswordAttempts,
Role = @Role,
EmailToken = @EmailToken
WHERE AccountID = @AccountID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@AccountID", account.ID);
cmd.Parameters.AddWithValue("@FailedPasswordLock", data.FailedPasswordLock);
cmd.Parameters.AddWithValue("@PasswordAttempts", data.PasswordAttempts);
cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", data.CurrentPasswordAttempts);
cmd.Parameters.AddWithValue("@Role", data.Role);
cmd.Parameters.AddWithValue("@EmailToken", data.EmailToken);
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -0,0 +1,42 @@
using System.Net.Mail;
namespace MistoxWebsite.Server.Services {
public partial class EmailService {
public Dictionary<string, DateTime> _SentEmails = new Dictionary<string, DateTime>();
public string EmailServer = "";
public string EmailAddress = "";
public string EmailPassword = "";
public int EmailPort;
public EmailService( string _EmailServer, int _EmailPort, string _EmailAddress, string _EmailPassword ) {
EmailServer = _EmailServer;
EmailPort = _EmailPort;
EmailAddress = _EmailAddress;
EmailPassword = _EmailPassword;
}
public string Send( string Destination, string Subject, string Body ) {
using (SmtpClient client = new SmtpClient( EmailServer, EmailPort )){
client.EnableSsl = true;
client.Credentials = new System.Net.NetworkCredential( EmailAddress, EmailPassword );
try {
MailMessage msg = new MailMessage(){
IsBodyHtml = true,
Subject = Subject,
Body = Body
};
msg.From = new MailAddress( EmailAddress, "no-reply" );
msg.To.Add( new MailAddress( Destination ) );
client.Send( msg );
return "Success";
} catch( Exception e ) {
return "An Error Has Occurred Sending Email : " + e.ToString();
}
}
}
}
}
@@ -0,0 +1,53 @@
using System.Net.Mail;
namespace MistoxWebsite.Server.Services {
public partial class EmailService {
// @UserName
// @ResetPassWord
// https://mistox.com/account/resetpassword?UserName=@UserName&ResetPwd=@ResetPassWord
public static string ResetPasswordSubject = "Password Reset Request";
public static string ResetPasswordEmail = @"
<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Password Reset</title>
</head>
<body style=""font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0;"">
<table role=""presentation"" style=""width: 100%; background-color: #f4f4f4; padding: 20px 0;"">
<tr>
<td>
<table role=""presentation"" style=""max-width: 600px; width: 100%; background-color: #ffffff; margin: 0 auto; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"">
<tr>
<td style=""padding: 20px; text-align: center; background-color: #4CAF50; color: #ffffff; border-top-left-radius: 8px; border-top-right-radius: 8px;"">
<h2>Password Reset Request</h2>
</td>
</tr>
<tr>
<td style=""padding: 20px; text-align: left; font-size: 16px; color: #333333;"">
<p>Hi @UserName,</p>
<p>We received a request to reset your password. You can reset your password by clicking the button below:</p>
<p style=""text-align: center;"">
<a href=""https://mistox.com/account/resetpassword?UserName=@UserName&ResetPwd=@ResetPassWord"" style=""background-color: #4CAF50; color: #ffffff; text-decoration: none; padding: 15px 25px; font-size: 16px; border-radius: 5px; display: inline-block;"">Reset Password</a>
</p>
<p>If you didn't request a password reset, you can safely ignore this email.</p>
<p>Best regards</p>
</td>
</tr>
<tr>
<td style=""padding: 10px; text-align: center; background-color: #f4f4f4; color: #888888; font-size: 12px; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;"">
<p>If you have any questions, feel free to <a href=""mailto:webmaster@mistox.com"" style=""color: #4CAF50; text-decoration: none;"">contact support</a>.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
";
}
}
@@ -0,0 +1,54 @@
using System.Net.Mail;
namespace MistoxWebsite.Server.Services {
public partial class EmailService {
// @UserName
// @VerifyPassword
// https://mistox.com/api/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword
public static string VerifyEmailSubject = "Verify Your Email Address";
public static string VerifyEmailEmail = @"
<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Verify Your Email</title>
</head>
<body style=""font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0;"">
<table role=""presentation"" style=""width: 100%; background-color: #f4f4f4; padding: 20px 0;"">
<tr>
<td>
<table role=""presentation"" style=""max-width: 600px; width: 100%; background-color: #ffffff; margin: 0 auto; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"">
<tr>
<td style=""padding: 20px; text-align: center; background-color: #4CAF50; color: #ffffff; border-top-left-radius: 8px; border-top-right-radius: 8px;"">
<h2>Verify Email Request</h2>
</td>
</tr>
<tr>
<td style=""padding: 20px; text-align: left; font-size: 16px; color: #333333;"">
<p>Hi @UserName,</p>
<p>Thank you for making an account with us:</p>
<p>In order to start using your account we need to verify your email address by clicking the link below:</p>
<p style=""text-align: center;"">
<a href=""https://mistox.com/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword"" style=""background-color: #4CAF50; color: #ffffff; text-decoration: none; padding: 15px 25px; font-size: 16px; border-radius: 5px; display: inline-block;"">Verify Email</a>
</p>
<p>If you didn't create an account please ignore this email.</p>
<p>Best regards</p>
</td>
</tr>
<tr>
<td style=""padding: 10px; text-align: center; background-color: #f4f4f4; color: #888888; font-size: 12px; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;"">
<p>If you have any questions, feel free to <a href=""mailto:webmaster@mistox.com"" style=""color: #4CAF50; text-decoration: none;"">contact support</a>.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
";
}
}
@@ -0,0 +1,179 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Stripe-Payments</title>
<script src="https://js.stripe.com/v3/"></script>
<style>
#submit {
width: 200px;
height: 40px;
margin-left: calc(50% - 100px);
margin-top: 30px;
color: #fff;
background-color: #393;
font-size: 16px;
text-decoration: none;
text-transform: uppercase;
overflow: hidden;
transition: .5s;
letter-spacing: 4px;
border: 1px solid #8F7CEC;
}
#submit:hover {
background: #353;
color: #fff;
border-radius: 5px;
border-color: #353;
}
</style>
</head>
<body>
<form id="payment-form">
<div id="link-authentication-element">
<!--Stripe.js injects the Link Authentication Element-->
</div>
<div id="payment-element">
<!--Stripe.js injects the Payment Element-->
</div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay now</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
<script>
// This is your test publishable API key.
const stripe = Stripe('pk_live_51LBODxCozZzTNCNhFdVbzm93F1N3Kk5sEiOyUYeU8GlqxF8AkS6h1JOkIqmFJ1hBmkBCEEa8cfBCY7RotHlweS7g00UzyxkUnO');
let elements;
initialize();
checkStatus();
document.querySelector("#payment-form").addEventListener("submit", handleSubmit);
let emailAddress = '';
// Fetches a payment intent and captures the client secret
async function initialize() {
const response = await fetch("/api/getCheckoutToken?userID=" + new URL(window.location.href).searchParams.get("userID"), {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: ""
});
const clientSecret = await response.text();
const appearance = {
theme: 'night',
};
elements = stripe.elements({ appearance, clientSecret });
const linkAuthenticationElement = elements.create("linkAuthentication");
linkAuthenticationElement.mount("#link-authentication-element");
linkAuthenticationElement.on('change', (event) => {
emailAddress = event.value.email;
});
const paymentElementOptions = {
layout: "tabs",
};
const paymentElement = elements.create("payment", paymentElementOptions);
paymentElement.mount("#payment-element");
}
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "https://mistox.net/store/payment/success",
receipt_email: emailAddress,
},
});
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occurred.");
}
setLoading(false);
}
// Fetches the payment intent status after payment submission
async function checkStatus() {
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret);
switch (paymentIntent.status) {
case "succeeded":
showMessage("Payment succeeded!");
break;
case "processing":
showMessage("Your payment is processing.");
break;
case "requires_payment_method":
showMessage("Your payment was not successful, please try again.");
break;
default:
showMessage("Something went wrong.");
break;
}
}
// ------- UI helpers -------
function showMessage(messageText) {
const messageContainer = document.querySelector("#payment-message");
messageContainer.classList.remove("hidden");
messageContainer.textContent = messageText;
setTimeout(function () {
messageContainer.classList.add("hidden");
messageText.textContent = "";
}, 4000);
}
// Show a spinner on payment submission
function setLoading(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("#submit").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("#submit").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
}
</script>
</body>
</html>
+192
View File
@@ -0,0 +1,192 @@
<html>
<head>
<title>HTML_Snake</title>
</head>
<body onkeydown='return keyDown(event)'; 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;">
<script>
var snake = [];
var score = 0;
var posX = 12;
var posY = 15;
var colX, colY;
var saves = "";
var paused = false;
var direction = 2;
var wasCollected = false;
var body = document.getElementById("BODY");
function set(name,value) {
var expires = "";
var date = new Date();
date.setTime(date.getTime() + (9999*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function get(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function hsl2rgb(h,s,l) {
let a=s*Math.min(l,1-l);
let f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1);
return [f(0),f(8),f(4)];
}
var degree = 0;
function newTail(x, y){
degree += 5;
if(degree > 359){
degree = 0;
}
var color = hsl2rgb(degree, .6, .5);
var r = Math.floor(color[0] * 255).toString(16);
var g = Math.floor(color[1] * 255).toString(16);
var b = Math.floor(color[2] * 255).toString(16);
var nX = (10*x)-10;
var nY = (10*y)-10;
var id = x + "," + y;
if (x == colX){
if (y == colY){
var item = document.getElementById("collectable");
item.parentNode.removeChild(item);
wasCollected = true;
newCollectable();
score += 1;
document.getElementById("Score").innerHTML = "Score : " + score;
}
}
body.innerHTML = body.innerHTML + "<div id='" + id + "'; style='position: absolute; left: " + nX + "px; top:" + nY + "px; width: 10px; height: 10px; background-color: #" + r+g+b + "; border: 0; padding: 0; margin: 0;'></div>";
return id;
}
function randInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function newCollectable(){
colX = randInt(1, 59);
colY = randInt(1, 59);
body.innerHTML = body.innerHTML + "<div id='collectable'; style='position: absolute; left: " + (colX*10-10) + "px; top:" + (colY*10-10) + "px; width: 10px; height: 10px; background-color: #f00; border: 0; padding: 0; margin: 0;'></div>";
}
function isBound(x, y){
if (x > 0 && x < 61){
if (y > 0 && y < 61){
return true;
}
}
return false;
}
function die(){
set("data", saves + "|" + score );
location.reload();
}
function update(){
if (paused == false){
if (direction == 1){
posY -= 1;
}else if(direction == 2){
posX += 1;
}else if(direction == 3){
posY += 1;
}else if(direction == 4){
posX -= 1;
}
if (isBound(posX, posY)){
function func(item, index, arr){
var x = item.split(",")[0];
var y = item.split(",")[1];
if(posX == x){
if(posY == y){
die();
}
}
}
snake.forEach(func);
snake.push(newTail(posX, posY));
if(wasCollected == false){
var rem = snake.shift();
var remObj = document.getElementById(rem);
remObj.parentNode.removeChild(remObj);
}else{
wasCollected = false;
}
}else{
die();
}
}
}
function keyDown(event){
if (event.key == "w"){
direction = 1;
}else if(event.key == "d"){
direction = 2;
}else if(event.key == "s"){
direction = 3;
}else if(event.key == "a"){
direction = 4;
}else if(event.key == "p"){
if (paused == true){
paused = false;
document.getElementById("PauseScreen").style.display = "none";
}else{
paused = true;
document.getElementById("PauseScreen").style.display = "";
}
}
}
function loadLeaderboard(){
if (get("data") != null){
saves = get("data");
function func(item, index, arr){
document.getElementById("Scoreboard").innerHTML += '<div><h2 id="' + item + '"; style="text-align: center; font-size: 20px; padding: 0; margin: 0; border: 0;">' + item.toString() + '</h2></div>';
}
saves.split("|").forEach(func);
}
}
function start(){
newCollectable();
snake.push(newTail(10, 15));
snake.push(newTail(11, 15));
snake.push(newTail(12, 15));
setTimeout(loadLeaderboard, 100);
setInterval(update, 100);
}
start();
</script>
</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>
</body>
</html>
@@ -0,0 +1,169 @@
:root {
--Mistox-Dark: #2C0703;
--Mistox-Medium: #890620;
--Mistox-Light: #B6465F;
--Mistox-Bright: #FC440F;
--Mistox-Offset: #443B75;
--Mistox-Background: #320000;
--Mistox-White: #FFF;
--Mistox-Black: #000;
}
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
main {
background-color: #000000;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23ff0000' fill-opacity='0.2' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
/* CSS used for the Account Activity Pages */
.center {
position: relative;
left: 50%;
top: 50vh;
transform: translateY(-50%) translateX(-50%);
}
.horizontal-center {
position: relative;
left: 50%;
transform: translateX(-50%);
}
.vertical-center {
position: relative;
top: 50vh;
transform: translateY(-50%);
}
.background-border {
border: var(--Mistox-Background) 2px solid;
border-radius: 6px;
}
.big-frame {
background-color: var(--Mistox-Black);
padding: 4px;
max-width: 400px;
color: var(--Mistox-White);
transition-duration: 1s;
}
.big-frame h3{
margin: 15px 0 30px 0;
color: var(--Mistox-White);
text-align: center;
}
.big-frame .frame-item label{
position: relative;
padding: 10px 0;
font-size: 16px;
color: var(--Mistox-White);
pointer-events: none;
transition: .5s;
top: -70px;
left: 20px;
}
.big-frame .frame-item input:autofill,
.big-frame .frame-item input:-webkit-autofill,
.big-frame .frame-item input {
position: relative;
width: calc(100% - 40px);
margin: 0 20px;
padding: 10px 0;
font-size: 15px;
color: var(--Mistox-White);
margin-bottom: 30px;
border: none;
border-bottom: 1px solid var(--Mistox-White);
outline: none;
background: transparent;
}
.big-frame .frame-item input:focus ~ label,
.big-frame .frame-item input:not(:placeholder-shown) ~ label {
top: -95px;
left: 10px;
color: var(--Mistox-Light);
font-size: 12px;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-around;
}
.sub-frame {
text-align: center;
padding: 1px 0;
}
.submit{
position: relative;
padding: 10px 20px;
color: var(--Mistox-Black);
background-color: var(--Mistox-Light);
font-size: 16px;
text-decoration: none;
text-transform: uppercase;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.5s ease;
letter-spacing: 4px;
border: 1px solid var(--Mistox-Light);
margin: auto;
border-radius: 5px;
}
.submit:hover {
background-color: var(--Mistox-Light);
color: var(--Mistox-White);
box-shadow: 4px 3px 6px var(--Mistox-Dark);
}
.submit:active {
transform: translate( 4px, 2px );
background-color: var(--Mistox-Dark);
border: none;
color: var(--Mistox-White);
box-shadow: none;
}
ul {
list-style: none;
color: var(--Mistox-Bright);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>MistoxNet</title>
<base href="/" />
<link href="MistoxWebsite.Client.styles.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet">
<script src="js/screenwidth.js"></script>
<script>
PaymentLoaded = async function () {
var iframe;
while (iframe == null) {
await new Promise(r => setTimeout(r, 50));
iframe = document.getElementById("PaymentFrame");
}
window.onresize = function () {
iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 50 + 'px';
}
window.onchange = function () {
iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 50 + 'px';
}
iframe.onload = function () {
iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 50 + 'px';
}
for (var i = 0; i < 2000; i++) {
await new Promise(r => setTimeout(r, 100));
iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 50 + 'px';
}
}
</script>
</head>
<body style="border: 0; padding: 0; margin: 0;">
<div id="app"></div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>
@@ -0,0 +1,6 @@
window.getWindowSize = function() {
return {
width: window.innerWidth,
height: window.innerHeight
};
};
+119
View File
@@ -0,0 +1,119 @@
using System.Diagnostics;
// Reflections of SQL Database objects
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 int ID { get; set; } // PK
public string UserName { get; set; } = "";
public string Email { get; set; } = "";
public bool EmailVerified { get; set; } = false;
public string PasswordHash { get; set; } = "";
public WebSiteData SiteData { get; set; } = new WebSiteData();
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 int AccountID { get; set; } // PK
public int ProductID { get; set; } // PK
public string Item { get; set; } = string.Empty; // PK
public int Quantity { get; set; }
public string Stats { get; set; } = string.Empty;
}
public class UserInventory {
public string Item { get; set; } = string.Empty;
public int Quantity { get; set; }
public string Stats { get; set; } = string.Empty;
}
public class Receipt {
public int AccountID { get; set; } // PK
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 int ID { get; set; }
public int AccountID { get; set; }
public int ProductID { get; set; }
}
public class ProjectMistData {
public int AccountID { get; set; } // PK
public int Credits { get; set; }
public int OddballTimer { get; set; }
public string SessionToken { get; set; } = "";
public int SessionID { get; set; }
public int Kills { get; set; }
public int Deaths { 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;
}
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,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>