This commit is contained in:
+285
@@ -0,0 +1,285 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
using BoredCareers.Services;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
using BoredCareers.Entities;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
[ApiController]
|
||||
[Route("api/account/[controller]")]
|
||||
public class AuthenticationController : MistoxControllerBase {
|
||||
|
||||
EmailService _emailContext;
|
||||
|
||||
public AuthenticationController(DatabaseService db, EmailService emailContext) : base(db) {
|
||||
_emailContext = emailContext;
|
||||
}
|
||||
|
||||
[Route("login")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Account>> Login([FromForm] string UserName, [FromForm] string PasswordHash, [FromForm] bool StayLoggedIn) {
|
||||
try {
|
||||
Account? test = await _databaseService.GetAccount(UserName.ToLower());
|
||||
if (test != null) {
|
||||
if (test.EmailVerified == true) {
|
||||
if (test.FailedPasswordLock) {
|
||||
if (test.CurrentPasswordAttempts >= test.PasswordAttempts) {
|
||||
return new Account() { Error = "Too many failed password attempts. Please reset your password" };
|
||||
}
|
||||
}
|
||||
if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) {
|
||||
test.CurrentPasswordAttempts = 0;
|
||||
await _databaseService.SetAccount(test);
|
||||
|
||||
List<Claim> claims = new List<Claim>() {
|
||||
new Claim("ID", test.ID.ToString())
|
||||
};
|
||||
|
||||
await HttpContext.SignInAsync(
|
||||
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, "Auth")),
|
||||
new AuthenticationProperties {
|
||||
ExpiresUtc = DateTime.UtcNow.AddYears(30), // Add 30 years with sliding on
|
||||
IsPersistent = StayLoggedIn, // Is set from the StayLoggedIn
|
||||
}
|
||||
);
|
||||
return test;
|
||||
}
|
||||
else {
|
||||
test.CurrentPasswordAttempts += 1;
|
||||
await _databaseService.SetAccount(test);
|
||||
return new Account() { Error = "Wrong password" };
|
||||
}
|
||||
}
|
||||
else {
|
||||
await SendVerify(test.UserName);
|
||||
return new Account() { Error = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" };
|
||||
}
|
||||
}
|
||||
return new Account() { Error = "User doesn't exist" };
|
||||
} catch (Exception ex) {
|
||||
return new Account() { Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[Route("register")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Account>> Register([FromForm] string Email, [FromForm] string UserName, [FromForm] string PasswordHash) {
|
||||
try {
|
||||
if (await _databaseService.GetAccount(UserName.ToLower()) == null) {
|
||||
if (await _databaseService.GetAccount(Email.ToLower()) == null) {
|
||||
Account? created = new Account() {
|
||||
UserName = UserName.ToLower(),
|
||||
Email = Email.ToLower(),
|
||||
EmailVerified = false,
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash),
|
||||
};
|
||||
await _databaseService.SetAccount(created);
|
||||
created = await _databaseService.GetAccount(Email.ToLower());
|
||||
if (created != null) {
|
||||
await SendVerify(created.UserName);
|
||||
return created;
|
||||
}
|
||||
return new Account() { Error = "Unknown Error" };
|
||||
}
|
||||
else {
|
||||
return new Account() { Error = "Email is already in use" };
|
||||
}
|
||||
}
|
||||
else {
|
||||
return new Account() { Error = "UserName is taken" };
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine("Error: " + ex.Message);
|
||||
return new Account() { Error = ex.Message };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Route("changepassword")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Account user = await getLoggedInUser();
|
||||
if (BCrypt.Net.BCrypt.Verify(OldPassword, user.PasswordHash)) {
|
||||
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
|
||||
user.CurrentPasswordAttempts = 0;
|
||||
await _databaseService.SetAccount(user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("toggleaccountlock")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<string>> ToggleAccountLock([FromForm] bool AccountLock) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Account user = await getLoggedInUser();
|
||||
user.FailedPasswordLock = AccountLock;
|
||||
user.CurrentPasswordAttempts = 0;
|
||||
await _databaseService.SetAccount(user);
|
||||
return "Account Lock Status Updated";
|
||||
}
|
||||
return "Unknown Error Occurred";
|
||||
} catch (Exception ex) {
|
||||
return ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("get")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Account?>> Get() {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
return await getLoggedInUser();
|
||||
}
|
||||
return Ok();
|
||||
} catch {
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
[Route("logout")]
|
||||
[HttpPost]
|
||||
public async Task Logout() {
|
||||
await HttpContext.SignOutAsync();
|
||||
}
|
||||
|
||||
[Route("sendverifyemail")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<string>> SendVerify([FromForm] string UserName) {
|
||||
try {
|
||||
string key = "v" + UserName;
|
||||
// Stop from sending multiple emails quickly
|
||||
if (_emailContext._SentEmails.ContainsKey(key)) {
|
||||
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
|
||||
if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
|
||||
return "Cannot sent another verify email until 5 minutes has elapsed ";
|
||||
}
|
||||
else {
|
||||
_emailContext._SentEmails.Remove(key);
|
||||
}
|
||||
}
|
||||
Account? test = await _databaseService.GetAccount(UserName.ToLower());
|
||||
if (test != null) {
|
||||
test.EmailToken = Guid.NewGuid().ToString();
|
||||
await _databaseService.SetAccount(test);
|
||||
|
||||
string EmailContents = EmailService.VerifyEmailEmail;
|
||||
EmailContents = Substitue(EmailContents, "@UserName", UserName);
|
||||
EmailContents = Substitue(EmailContents, "@UserName", UserName);
|
||||
EmailContents = Substitue(EmailContents, "@VerifyPassword", test.EmailToken);
|
||||
|
||||
string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
|
||||
_emailContext._SentEmails.Add(key, DateTime.Now);
|
||||
return result;
|
||||
}
|
||||
return "Account not found";
|
||||
} catch (Exception) {
|
||||
return "The connection couldn't be established to the email server";
|
||||
}
|
||||
}
|
||||
|
||||
[Route("verifyemail")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> VerifyEmail([FromForm] string UserName, [FromForm] string EmailToken) {
|
||||
try {
|
||||
Account? test = await _databaseService.GetAccount(UserName.ToLower());
|
||||
if (test != null) {
|
||||
if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == EmailToken) {
|
||||
test.EmailToken = "";
|
||||
test.EmailVerified = true;
|
||||
await _databaseService.SetAccount(test);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("sendresetpassword")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<string>> ResetPassword([FromForm] string Email) {
|
||||
try {
|
||||
string key = "p" + Email.ToLower();
|
||||
// Stop from sending multiple emails quickly
|
||||
if (_emailContext._SentEmails.ContainsKey(key)) {
|
||||
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
|
||||
if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
|
||||
return "Cannot sent another reset requests until 5 minutes has elapsed";
|
||||
}
|
||||
else {
|
||||
_emailContext._SentEmails.Remove(key);
|
||||
}
|
||||
}
|
||||
Account? test = await _databaseService.GetAccount(Email.ToLower());
|
||||
if (test != null) {
|
||||
test.EmailToken = Guid.NewGuid().ToString();
|
||||
await _databaseService.SetAccount(test);
|
||||
|
||||
string EmailContents = EmailService.ResetPasswordEmail;
|
||||
EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
|
||||
EmailContents = Substitue(EmailContents, "@UserName", test.UserName);
|
||||
EmailContents = Substitue(EmailContents, "@ResetPassWord", test.EmailToken);
|
||||
|
||||
string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents);
|
||||
_emailContext._SentEmails.Add(key, DateTime.Now);
|
||||
return result;
|
||||
}
|
||||
return "Account Not Found";
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine("EmailService Error: " + e.ToString());
|
||||
return "The connection couldn't be established to the email server";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Route("resetpassword")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> ResetPwdVerify([FromForm] string UserName, [FromForm] string NewPassword, [FromForm] string ResetToken) {
|
||||
try {
|
||||
Account? test = await _databaseService.GetAccount(UserName.ToLower());
|
||||
if (test != null && !string.IsNullOrEmpty(test.EmailToken)) {
|
||||
if (!string.IsNullOrEmpty(test.EmailToken) && test.EmailToken == ResetToken) {
|
||||
test.CurrentPasswordAttempts = 0;
|
||||
test.EmailToken = "";
|
||||
test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
|
||||
await _databaseService.SetAccount(test);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("delete")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> delete([FromForm] string Password) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Account user = await getLoggedInUser();
|
||||
if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) {
|
||||
await _databaseService.DeleteAccount(user.ID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BoredCareers.Entities;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
[ApiController]
|
||||
[Route("api/cart/[controller]")]
|
||||
public class CartController : MistoxControllerBase {
|
||||
|
||||
CartController(DatabaseService db) : base(db) { }
|
||||
|
||||
[Route("get")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Cart[]>> GetCart() {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
return Ok(await _databaseService.GetCart(getLoggedInUserID()));
|
||||
}
|
||||
return StatusCode(500);
|
||||
} catch {
|
||||
return StatusCode(500);
|
||||
}
|
||||
}
|
||||
|
||||
[Route("add")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddCart([FromBody] Cart cart) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
cart.AccountID = getLoggedInUserID();
|
||||
await _databaseService.AddToCart(cart);
|
||||
return Ok();
|
||||
}
|
||||
return StatusCode(500);
|
||||
} catch {
|
||||
return StatusCode(500);
|
||||
}
|
||||
}
|
||||
|
||||
[Route("remove")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> RemoveCart([FromBody] Cart cart) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
cart.AccountID = getLoggedInUserID();
|
||||
await _databaseService.RemoveFromCart(cart);
|
||||
return Ok();
|
||||
}
|
||||
return StatusCode(500);
|
||||
} catch {
|
||||
return StatusCode(500);
|
||||
}
|
||||
}
|
||||
|
||||
[Route("clear")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ClearCart() {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
await _databaseService.ClearCart(getLoggedInUserID());
|
||||
return Ok();
|
||||
}
|
||||
return StatusCode(500);
|
||||
} catch {
|
||||
return StatusCode(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BoredCareers.Entities;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
|
||||
public class MistoxControllerBase : ControllerBase {
|
||||
|
||||
public DatabaseService _databaseService;
|
||||
|
||||
public MistoxControllerBase(DatabaseService databaseService) {
|
||||
_databaseService = databaseService;
|
||||
}
|
||||
|
||||
public bool isLoggedIn() {
|
||||
if (User.Identity != null && User.Identity.IsAuthenticated) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLoggedInUserID() {
|
||||
return Convert.ToInt32(User.FindFirst("ID")?.Value);
|
||||
}
|
||||
|
||||
public async Task<Account> getLoggedInUser() {
|
||||
try {
|
||||
Account? test = await _databaseService.GetAccount(getLoggedInUserID());
|
||||
if (test != null) {
|
||||
return test;
|
||||
}
|
||||
return new Account();
|
||||
} catch {
|
||||
return new Account();
|
||||
}
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BoredCareers.Controllers.Payment;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
using BoredCareers.Entities;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
[ApiController]
|
||||
[Route("api/payment/[controller]")]
|
||||
public class PaymentController : MistoxControllerBase {
|
||||
|
||||
IPayment _paymentService;
|
||||
|
||||
public PaymentController(DatabaseService db) : base(db) {
|
||||
if (IPayment._PaymentType == PaymentType.StripeIntent) {
|
||||
_paymentService = new StripeIntent(_databaseService);
|
||||
} else {
|
||||
// Fallback
|
||||
_paymentService = new StripeIntent(_databaseService);
|
||||
}
|
||||
// Add new payment plugins here
|
||||
}
|
||||
|
||||
[Route("getcheckouttoken")]
|
||||
[HttpPost]
|
||||
public async Task<string> GetCheckoutToken() {
|
||||
string OrderNumber = Guid.NewGuid().ToString().Substring(0, 10);
|
||||
if (isLoggedIn()) {
|
||||
Cart[] carts = await _databaseService.GetCart(getLoggedInUserID());
|
||||
(bool, string) PaymentResponse = await _paymentService.TryGetCheckoutToken(OrderNumber, getLoggedInUserID(), carts);
|
||||
if (PaymentResponse.Item1) {
|
||||
// Returns client secret
|
||||
return PaymentResponse.Item2;
|
||||
} else {
|
||||
Console.WriteLine("An error has occured in the payment plugin\n\n");
|
||||
Console.WriteLine(PaymentResponse.Item2);
|
||||
Console.WriteLine("\n");
|
||||
return "An error has occured in the payment plugin";
|
||||
}
|
||||
}
|
||||
return "You must be logged in";
|
||||
}
|
||||
|
||||
[Route("getpublickey")]
|
||||
[HttpPost]
|
||||
public IActionResult GetPublicKey() {
|
||||
try {
|
||||
return Ok(IPayment._PublicKey);
|
||||
} catch (Exception ex) {
|
||||
return NotFound(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[Route("response")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> paymentWebhook() {
|
||||
try {
|
||||
string body = await new StreamReader(Request.Body).ReadToEndAsync();
|
||||
await _paymentService.ValidatePurchase(body, Request.Headers["Stripe-Signature"].ToString());
|
||||
return Ok();
|
||||
} catch (Exception ex) {
|
||||
return NotFound(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using BoredCareers.Entities;
|
||||
|
||||
namespace BoredCareers.Controllers.Payment {
|
||||
|
||||
public interface IPayment {
|
||||
|
||||
public static PaymentType _PaymentType;
|
||||
public static string _EndpointSecret = "";
|
||||
public static string _PublicKey = "";
|
||||
|
||||
public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID, Cart[] cart);
|
||||
public Task ValidatePurchase(string WebHookData, string Headers);
|
||||
|
||||
}
|
||||
|
||||
public enum PaymentType {
|
||||
StripeIntent
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using BoredCareers.Controllers.Payment;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
using BoredCareers.Entities;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
|
||||
public class StripeIntent : IPayment {
|
||||
|
||||
DatabaseService _databaseService;
|
||||
|
||||
public StripeIntent(DatabaseService databaseService) {
|
||||
_databaseService = databaseService;
|
||||
}
|
||||
|
||||
public async Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID, Cart[] cart) {
|
||||
try {
|
||||
// build Recipt and calculate Tax
|
||||
var options = new Stripe.Tax.CalculationCreateOptions {
|
||||
Currency = "usd",
|
||||
CustomerDetails = new Stripe.Tax.CalculationCustomerDetailsOptions {
|
||||
AddressSource = "billing",
|
||||
},
|
||||
Expand = new List<string>() { "line_items" },
|
||||
LineItems = new List<Stripe.Tax.CalculationLineItemOptions>()
|
||||
};
|
||||
|
||||
List<int> prods = new List<int>();
|
||||
|
||||
// Add items to receipt
|
||||
int subtotal = 0;
|
||||
foreach (Cart items in cart) {
|
||||
Product? product = await _databaseService.GetProduct(items.ProductID);
|
||||
if (product != null) {
|
||||
prods.Add(product.ID);
|
||||
if (product != null) {
|
||||
subtotal += product.Cost;
|
||||
options.LineItems.Add(new Stripe.Tax.CalculationLineItemOptions {
|
||||
Amount = product.Cost,
|
||||
TaxCode = "txcd_10201000", // Tax code for downloadable digital games
|
||||
Quantity = 1,
|
||||
Reference = product.Name,
|
||||
TaxBehavior = "exclusive"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var service = new Stripe.Tax.CalculationService();
|
||||
Stripe.Tax.Calculation result = service.Create(options);
|
||||
|
||||
string csv = "";
|
||||
foreach (int cur in prods) {
|
||||
csv = csv + cur + ",";
|
||||
}
|
||||
|
||||
// Crate Payment Intent
|
||||
Stripe.PaymentIntentCreateOptions paymentIntent = new Stripe.PaymentIntentCreateOptions() {
|
||||
Amount = result.AmountTotal,
|
||||
Currency = "usd",
|
||||
Metadata = new Dictionary<string, string> {
|
||||
{ "ordernumber", OrderNumber },
|
||||
{ "user", userID.ToString() },
|
||||
{ "products", csv },
|
||||
{ "subtotal", subtotal.ToString() },
|
||||
{ "total", result.AmountTotal.ToString() }
|
||||
},
|
||||
StatementDescriptor = "Mistox.Net #" + OrderNumber
|
||||
};
|
||||
|
||||
Stripe.PaymentIntentService intentService = new Stripe.PaymentIntentService();
|
||||
Stripe.PaymentIntent x = await intentService.CreateAsync(paymentIntent);
|
||||
|
||||
return (true, x.ClientSecret);
|
||||
} catch (Exception e) {
|
||||
return (false, e.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task ValidatePurchase(string WebHookData, string Headers) {
|
||||
Stripe.Event e = Stripe.EventUtility.ConstructEvent( WebHookData, Headers, IPayment._EndpointSecret );
|
||||
if (e.Type == "payment_intent.succeeded") {
|
||||
// Extract Data from payment confirm
|
||||
Stripe.PaymentIntent intent = (Stripe.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
|
||||
await _databaseService.ClearCart(userID);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+143
@@ -0,0 +1,143 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
using BoredCareers.Entities;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
[ApiController]
|
||||
[Route("api/product/[controller]")]
|
||||
public class ProductController : MistoxControllerBase {
|
||||
|
||||
public ProductController(DatabaseService db) : base(db) { }
|
||||
|
||||
[Route("set")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> CreateProduct([FromForm] Product obj, [FromForm] IFormFile[] images) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Account user = await getLoggedInUser();
|
||||
if (user.Role == "Admin") {
|
||||
List<ProductImage> building = new List<ProductImage>();
|
||||
foreach (var file in images) {
|
||||
using (var stream = new MemoryStream()) {
|
||||
await file.CopyToAsync(stream);
|
||||
var bytes = stream.ToArray();
|
||||
|
||||
// Convert to your image model or whatever your logic is
|
||||
ProductImage img = new ProductImage { Image = bytes, Name = file.FileName };
|
||||
building.Add(img);
|
||||
}
|
||||
}
|
||||
obj.Images = building.ToArray();
|
||||
await _databaseService.SetProduct(obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("get")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Product>> GetProduct([FromForm] int productID) {
|
||||
try {
|
||||
Product? product = await _databaseService.GetProduct(productID);
|
||||
if (product != null) {
|
||||
return product;
|
||||
}
|
||||
else {
|
||||
return NotFound();
|
||||
}
|
||||
} catch {
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[Route("getall")]
|
||||
[HttpPost]
|
||||
public async Task<Product[]> GetAllProducts() {
|
||||
try {
|
||||
return await _databaseService.GetAllProducts();
|
||||
} catch {
|
||||
return Array.Empty<Product>();
|
||||
}
|
||||
}
|
||||
|
||||
[Route("delete")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<bool>> DeleteProduct([FromForm] int productID) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Account user = await getLoggedInUser();
|
||||
if (user.Role == "Admin") {
|
||||
await _databaseService.DeleteProduct(productID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Route("getimage")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> GetProductImage([FromForm] int ProductID, [FromForm] int ImageID) {
|
||||
try {
|
||||
ProductImage? img = await _databaseService.GetImage(ProductID, ImageID);
|
||||
if (img != null) {
|
||||
return File(img.Image, "Image/*");
|
||||
}
|
||||
else {
|
||||
return NotFound();
|
||||
}
|
||||
} catch {
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[Route("getowned")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Receipt[]>> GetOwnedProduct() {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Receipt[] returned = await _databaseService.GetAllReceipts(getLoggedInUserID());
|
||||
return returned;
|
||||
}
|
||||
return new Receipt[0];
|
||||
} catch {
|
||||
return new Receipt[0];
|
||||
}
|
||||
}
|
||||
|
||||
[Route("download")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> Download([FromQuery] string Product) {
|
||||
try {
|
||||
if (isLoggedIn()) {
|
||||
Product[] games = await _databaseService.GetAllProducts();
|
||||
foreach (Product product in games) {
|
||||
if (contains(Product, product.URL)) {
|
||||
Receipt? receipt = await _databaseService.GetReceipt(getLoggedInUserID(), product.ID);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
// Reflections of SQL Database objects
|
||||
|
||||
namespace BoredCareers.Entities {
|
||||
|
||||
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 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 string Error { get; set; } = "";
|
||||
}
|
||||
|
||||
public class Product {
|
||||
public int ID { get; set; } // PK
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public ProductImage[] Images { get; set; } = [];
|
||||
public int Cost { get; set; }
|
||||
public string URL { get; set; } = "";
|
||||
}
|
||||
|
||||
public class ProductImage {
|
||||
public int ImageID { get; set; } // PK
|
||||
public int ProductID { get; set; } // PK
|
||||
public byte[] Image { get; set; } = Array.Empty<byte>();
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
public class ProductInventory {
|
||||
public int AccountID { get; set; } // PK
|
||||
public int ProductID { get; set; } // PK
|
||||
public string Key { get; set; } = string.Empty; // PK
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class Cart {
|
||||
public int ID { get; set; } // PK
|
||||
public int AccountID { get; set; }
|
||||
public int ProductID { get; set; }
|
||||
}
|
||||
|
||||
public class Receipt {
|
||||
public int AccountID { get; set; } // PK
|
||||
public int ProductID { get; set; } // PK
|
||||
public string ReceiptID { get; set; } = string.Empty; // PK
|
||||
public int LineItem { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
public int TaxAmount { get; set; }
|
||||
public int TotalCost { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+120
@@ -0,0 +1,120 @@
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using BoredCareers.Controllers.Payment;
|
||||
using BoredCareers.Services;
|
||||
using BoredCareers.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 ///////
|
||||
////////////////////////////////
|
||||
|
||||
// Address
|
||||
string? _dbserver = Environment.GetEnvironmentVariable("MySQLServer");
|
||||
string dbserver = !string.IsNullOrEmpty(_dbserver) ? _dbserver : "localhost";
|
||||
|
||||
// Database
|
||||
string? _dbdatabase = Environment.GetEnvironmentVariable("MySQLDatabase");
|
||||
string dbdatabase = !string.IsNullOrEmpty(_dbdatabase) ? _dbdatabase : "mistox";
|
||||
|
||||
// UserName
|
||||
string? _dbuser = Environment.GetEnvironmentVariable("MySQLUser");
|
||||
string dbUser = !string.IsNullOrEmpty(_dbuser) ? _dbuser : "root";
|
||||
|
||||
// Password
|
||||
string? _dbpass = Environment.GetEnvironmentVariable("MySQLPass");
|
||||
string dbPass = !string.IsNullOrEmpty(_dbpass) ? _dbpass : "oasv34$8gpv023dd";
|
||||
|
||||
// Create the database serivice
|
||||
DatabaseService databaseService = new DatabaseService(connectionString: "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;");
|
||||
builder.Services.Add( new ServiceDescriptor( typeof( DatabaseService ), databaseService ) );
|
||||
|
||||
////////////////////////////////
|
||||
///////// Email Service ////////
|
||||
////////////////////////////////
|
||||
|
||||
// Address
|
||||
string? _eServer = Environment.GetEnvironmentVariable("EmailServer");
|
||||
string EmailServer = !string.IsNullOrEmpty(_eServer) ? _eServer : "mail.mistox.com";
|
||||
|
||||
// Port
|
||||
string? _ePort = Environment.GetEnvironmentVariable("EmailPort");
|
||||
int EmailPort = !string.IsNullOrEmpty(_ePort) ? Convert.ToInt32(_ePort) : 587;
|
||||
|
||||
// User
|
||||
string? _eAddress = Environment.GetEnvironmentVariable("EmailAddress");
|
||||
string EmailAddress = !string.IsNullOrEmpty(_eAddress) ? _eAddress : "no-reply@mistox.com";
|
||||
|
||||
// Password
|
||||
string? _ePassword = Environment.GetEnvironmentVariable("EmailPassword");
|
||||
string EmailPassword = !string.IsNullOrEmpty(_ePassword) ? _ePassword : "";
|
||||
|
||||
// Create the email service
|
||||
EmailService Emailservice = new EmailService( EmailServer, EmailPort, EmailAddress, EmailPassword );
|
||||
builder.Services.Add( new ServiceDescriptor( typeof( EmailService ), Emailservice ));
|
||||
|
||||
////////////////////////////////
|
||||
/////// Payment Service ////////
|
||||
////////////////////////////////
|
||||
|
||||
// Payment service name -> must be name of PaymentType enum
|
||||
string? PaymentService = Environment.GetEnvironmentVariable("PaymentService");
|
||||
IPayment._PaymentType = (PaymentType)Enum.Parse(typeof(PaymentType), PaymentService, true);
|
||||
|
||||
if (IPayment._PaymentType == PaymentType.StripeIntent) {
|
||||
// Get PublicKey
|
||||
string? StripePublicKey = Environment.GetEnvironmentVariable("StripePublicKey");
|
||||
IPayment._PublicKey = string.IsNullOrEmpty(StripePublicKey) ? "" : StripePublicKey;
|
||||
// Get PrivateKey
|
||||
string? StripeAPIKey = Environment.GetEnvironmentVariable("StripeApiKey");
|
||||
StripeConfiguration.ApiKey = StripeAPIKey;
|
||||
// Get Endpoint secret
|
||||
string? StripeEndpointKey = Environment.GetEnvironmentVariable("StripeEndpointSecret");
|
||||
IPayment._EndpointSecret = string.IsNullOrEmpty(StripeEndpointKey) ? "" : StripeEndpointKey;
|
||||
}
|
||||
|
||||
// Authentication Service
|
||||
builder.Services.AddAuthentication( options => {
|
||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
} ).AddCookie(options => {
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||
options.Cookie.SameSite = SameSiteMode.Strict;
|
||||
options.LoginPath = "/account/login";
|
||||
options.LogoutPath = "/account/logout";
|
||||
options.SlidingExpiration = true;
|
||||
});
|
||||
|
||||
builder.Services.AddCors( o => o.AddDefaultPolicy( builder => {
|
||||
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); // No CORS
|
||||
} ) );
|
||||
|
||||
// Pages Service
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddRazorPages();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if( !app.Environment.IsDevelopment() ) {
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseCors();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.MapControllers();
|
||||
|
||||
app.MapFallbackToFile("index.html");
|
||||
|
||||
app.Run();
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
<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.2.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.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
|
||||
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,
|
||||
CurrentPasswordAttempts = _curpasswordattempts,
|
||||
PasswordAttempts = _passwordattempts,
|
||||
EmailToken = _emailtoken,
|
||||
FailedPasswordLock = _failedpasswordlock,
|
||||
Role = _role,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
public async Task<Account?> GetAccount( int ID ) {
|
||||
Account? account = null;
|
||||
using( MySqlConnection connection = GetConnection() ) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
SELECT *
|
||||
FROM Account
|
||||
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,
|
||||
CurrentPasswordAttempts = _passwordattempts,
|
||||
PasswordAttempts = _passwordattempts,
|
||||
EmailToken = _emailtoken,
|
||||
FailedPasswordLock = _failedpasswordlock,
|
||||
Role = _role,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
public async Task SetAccount( Account Profile ) {
|
||||
using( MySqlConnection connection = GetConnection() ) {
|
||||
connection.Open();
|
||||
|
||||
string command = @"
|
||||
INSERT INTO Account
|
||||
(ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken)
|
||||
VALUES
|
||||
(@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken);
|
||||
ON DUPLICATE KEY UPDATE
|
||||
UserName = @UserName,
|
||||
Email = @Email,
|
||||
EmailVerified = @EmailVerified,
|
||||
PasswordHash = @PasswordHash,
|
||||
FailedPasswordLock = @FailedPasswordLock,
|
||||
PasswordAttempts = @PasswordAttempts,
|
||||
CurrentPasswordAttempts = @CurrentPasswordAttempts,
|
||||
Role = @Role,
|
||||
EmailToken = @EmailToken;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand( command , connection);
|
||||
cmd.Parameters.AddWithValue("@ID", Profile.ID);
|
||||
cmd.Parameters.AddWithValue("@UserName", Profile.UserName);
|
||||
cmd.Parameters.AddWithValue("@Email", Profile.Email);
|
||||
cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified);
|
||||
cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash);
|
||||
cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock);
|
||||
cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts);
|
||||
cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts);
|
||||
cmd.Parameters.AddWithValue("@Role", Profile.Role);
|
||||
cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAccount( int ID ) {
|
||||
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;
|
||||
";
|
||||
cmd = new MySqlCommand( command, connection );
|
||||
cmd.Parameters.AddWithValue("@ID", ID);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.Services.DatabaseService {
|
||||
public partial class DatabaseService {
|
||||
|
||||
public async Task<Cart[]> GetCart( int accountID ) {
|
||||
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", accountID);
|
||||
|
||||
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.ToArray();
|
||||
}
|
||||
|
||||
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( int accountID ) {
|
||||
using( MySqlConnection connection = GetConnection() ) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
DELETE FROM Cart
|
||||
WHERE AccountID = @AccountID;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand( command , connection);
|
||||
cmd.Parameters.AddWithValue("@AccountID", accountID);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace BoredCareers.Services.DatabaseService {
|
||||
public partial class DatabaseService {
|
||||
public string ConnectionString {
|
||||
get; set;
|
||||
}
|
||||
public DatabaseService( string connectionString ) {
|
||||
ConnectionString = connectionString;
|
||||
}
|
||||
MySqlConnection GetConnection() {
|
||||
return new MySqlConnection( ConnectionString );
|
||||
}
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.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");
|
||||
int _cost = reader.GetInt32("Cost");
|
||||
string _url = reader.GetString("URL");
|
||||
|
||||
ProductImage[] images = await GetAllImages(_id);
|
||||
|
||||
items = new Product() {
|
||||
ID = _id,
|
||||
Name = _name,
|
||||
Images = images,
|
||||
Description = _description,
|
||||
Cost = _cost,
|
||||
URL = _url
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<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");
|
||||
int _cost = reader.GetInt32("Cost");
|
||||
string _url = reader.GetString("URL");
|
||||
|
||||
ProductImage[] images = await GetAllImages(_id);
|
||||
|
||||
items.Add(new Product() {
|
||||
ID = _id,
|
||||
Name = _name,
|
||||
Images = images,
|
||||
Description = _description,
|
||||
Cost = _cost,
|
||||
URL = _url
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return items.ToArray();
|
||||
}
|
||||
|
||||
public async Task SetProduct(Product Item) {
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
INSERT INTO Product
|
||||
(ID,Name,Description,Cost,URL)
|
||||
VALUES
|
||||
(@ID,@Name,@Description,@Cost,@URL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
Name = @Name,
|
||||
Description = @Description,
|
||||
Cost = @Cost,
|
||||
URL = @URL
|
||||
WHERE ID = @ID;
|
||||
|
||||
SELECT ID FROM Product
|
||||
WHERE Name = @Name;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ID", Item.ID);
|
||||
cmd.Parameters.AddWithValue("@Name", Item.Name);
|
||||
cmd.Parameters.AddWithValue("@Description", Item.Description);
|
||||
cmd.Parameters.AddWithValue("@Cost", Item.Cost);
|
||||
cmd.Parameters.AddWithValue("@URL", Item.URL);
|
||||
|
||||
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||
while (await reader.ReadAsync()) {
|
||||
if (reader == null) {
|
||||
break;
|
||||
}
|
||||
Item.ID = reader.GetInt32("ID");
|
||||
}
|
||||
}
|
||||
|
||||
await AddAllImages(Item);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteProduct(int ID) {
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
|
||||
await DeleteAllImages(ID);
|
||||
|
||||
connection.Open();
|
||||
string command = @"
|
||||
DELETE FROM Product
|
||||
WHERE ID = @ID;
|
||||
";
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ID", ID);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.Services.DatabaseService {
|
||||
public partial class DatabaseService {
|
||||
|
||||
public async Task<ProductImage?> GetImage(int ProductID, int ImageID) {
|
||||
ProductImage? item = null;
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
SELECT * FROM ProductImage
|
||||
WHERE ProductID = @ProductID AND ImageID = @ImageID;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ProductID", ProductID);
|
||||
cmd.Parameters.AddWithValue("@ImageID", ImageID);
|
||||
|
||||
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||
while (await reader.ReadAsync()) {
|
||||
if (reader == null) {
|
||||
break;
|
||||
}
|
||||
int _ImageID = reader.GetInt32("ImageID");
|
||||
int _ProductID = reader.GetInt32("ProductID");
|
||||
byte[] _Image = (byte[])reader["Image"];
|
||||
string _Name = reader.GetString("Name");
|
||||
|
||||
item = new ProductImage() {
|
||||
ImageID = _ImageID,
|
||||
ProductID = _ProductID,
|
||||
Image = _Image,
|
||||
Name = _Name
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public async Task<ProductImage[]> GetAllImages(int ProductID) {
|
||||
List<ProductImage> items = new List<ProductImage>();
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
SELECT * FROM ProductImage
|
||||
WHERE ProductID = @ProductID;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ProductID", ProductID);
|
||||
|
||||
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||
while (await reader.ReadAsync()) {
|
||||
if (reader == null) {
|
||||
break;
|
||||
}
|
||||
int _ImageID = reader.GetInt32("ImageID");
|
||||
int _ProductID = reader.GetInt32("ProductID");
|
||||
string _Name = reader.GetString("Name");
|
||||
|
||||
items.Add(new ProductImage() {
|
||||
ImageID = _ImageID,
|
||||
ProductID = _ProductID,
|
||||
Name = _Name
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return items.ToArray();
|
||||
}
|
||||
|
||||
public async Task AddAllImages(Product Item) {
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
connection.Open();
|
||||
foreach (ProductImage cur in Item.Images) {
|
||||
if (cur.Image != null) {
|
||||
string command = @"
|
||||
INSERT INTO ProductImage
|
||||
(ProductID, Image, Name)
|
||||
VALUES
|
||||
(@ProductID, @Image, @Name);
|
||||
";
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ProductID", Item.ID);
|
||||
cmd.Parameters.AddWithValue("@Image", cur.Image );
|
||||
cmd.Parameters.AddWithValue("@Name", cur.Name );
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAllImages(int ItemID) {
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
DELETE FROM ProductImage
|
||||
WHERE ProductID = @ProductID;
|
||||
";
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@ProductID", ItemID);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.Services.DatabaseService {
|
||||
public partial class DatabaseService {
|
||||
|
||||
public async Task<ProductInventory[]> GetAllProductInventory( int accountID, int productID ) {
|
||||
List<ProductInventory> list = new List<ProductInventory>();
|
||||
using( MySqlConnection connection = GetConnection() ) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
SELECT * FROM ProductInventory
|
||||
WHERE AccountID = @AccountID AND ProductID = @ProductID;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@AccountID", accountID);
|
||||
cmd.Parameters.AddWithValue("@ProductID", productID);
|
||||
|
||||
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
|
||||
while( await reader.ReadAsync() ) {
|
||||
if( reader == null ) {
|
||||
break;
|
||||
}
|
||||
|
||||
string _Key = reader.GetString("Key");
|
||||
string _Value = reader.GetString("Value");
|
||||
|
||||
list.Add( new ProductInventory() {
|
||||
AccountID = accountID,
|
||||
ProductID = productID,
|
||||
Key = _Key,
|
||||
Value = _Value
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public async Task<ProductInventory> GetProductInventory( int accountID, int productID, string Key ) {
|
||||
ProductInventory item = new ProductInventory();
|
||||
using( MySqlConnection connection = GetConnection() ) {
|
||||
connection.Open();
|
||||
string command = @"
|
||||
SELECT * FROM ProductInventory
|
||||
WHERE AccountID = @AccountID AND ProductID = @ProductID AND Key = @Key;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@AccountID", accountID);
|
||||
cmd.Parameters.AddWithValue("@ProductID", productID);
|
||||
cmd.Parameters.AddWithValue("@Key", Key);
|
||||
|
||||
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||
while (await reader.ReadAsync()) {
|
||||
if (reader == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
string _Key = reader.GetString("Key");
|
||||
string _Value = reader.GetString("Value");
|
||||
|
||||
item = new ProductInventory() {
|
||||
AccountID = accountID,
|
||||
ProductID = productID,
|
||||
Key = _Key,
|
||||
Value = _Value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
async Task SetProductInventory(ProductInventory item) {
|
||||
using (MySqlConnection connection = GetConnection()) {
|
||||
string command = @"
|
||||
INSERT INTO ProductInventory
|
||||
(AccountID, ProductID, `Key`, `Value`)
|
||||
Values
|
||||
(@AccountID, @ProductID, @Key, @Value)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`Value` = @Value;
|
||||
";
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||
cmd.Parameters.AddWithValue("@AccountID", item.AccountID);
|
||||
cmd.Parameters.AddWithValue("@ProductID", item.ProductID);
|
||||
cmd.Parameters.AddWithValue("@Key", item.Key);
|
||||
cmd.Parameters.AddWithValue("@Value", item.Value ?? (object)DBNull.Value);
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
using BoredCareers.Entities;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace BoredCareers.Services.DatabaseService {
|
||||
public partial class DatabaseService {
|
||||
|
||||
public async Task<Receipt[]> GetAllReceipts( int accountID ) {
|
||||
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", accountID);
|
||||
|
||||
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.ToArray();
|
||||
}
|
||||
|
||||
public async Task<( Receipt, Product )[]> GetAllReceiptsJoinedToProduct( int accountID ) {
|
||||
List<( Receipt, Product )> join = new();
|
||||
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", accountID);
|
||||
|
||||
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";
|
||||
|
||||
Receipt r = new() {
|
||||
AccountID = _accountid,
|
||||
ProductID = _gameid,
|
||||
ReceiptID = _receiptid,
|
||||
Time = _receiptdate,
|
||||
TotalCost = _totalcost,
|
||||
TaxAmount = _taxamount,
|
||||
LineItem = _lineitem
|
||||
};
|
||||
|
||||
Product p = new() {
|
||||
ID = _id,
|
||||
Cost = _cost,
|
||||
Description = _desc,
|
||||
Name = _name,
|
||||
URL = _url
|
||||
};
|
||||
|
||||
join.Add( (r, p) );
|
||||
}
|
||||
}
|
||||
}
|
||||
return join.ToArray();
|
||||
}
|
||||
|
||||
public async Task<Receipt?> GetReceipt( int accountID, int gameID ) {
|
||||
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", accountID);
|
||||
cmd.Parameters.AddWithValue("@ProductID", gameID);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace BoredCareers.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace BoredCareers.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>
|
||||
";
|
||||
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace BoredCareers.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>
|
||||
";
|
||||
|
||||
}
|
||||
}
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", ".\Server.csproj", "{76F2B6C1-FF9A-4BD8-AB7A-7456E8122C44}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9E4D64F9-2F56-4AC5-85CE-51EFEE1513C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9E4D64F9-2F56-4AC5-85CE-51EFEE1513C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9E4D64F9-2F56-4AC5-85CE-51EFEE1513C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9E4D64F9-2F56-4AC5-85CE-51EFEE1513C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{76F2B6C1-FF9A-4BD8-AB7A-7456E8122C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{76F2B6C1-FF9A-4BD8-AB7A-7456E8122C44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{76F2B6C1-FF9A-4BD8-AB7A-7456E8122C44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{76F2B6C1-FF9A-4BD8-AB7A-7456E8122C44}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{19C67017-8C26-439B-95B3-FE346D1AC7D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{19C67017-8C26-439B-95B3-FE346D1AC7D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{19C67017-8C26-439B-95B3-FE346D1AC7D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{19C67017-8C26-439B-95B3-FE346D1AC7D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B413876B-4048-47F1-B8B8-B974DF5E9E2A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Executable
+179
@@ -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>
|
||||
Executable
+192
@@ -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>
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 308 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Reference in New Issue
Block a user