mirror of
https://github.com/Wessel/nhl-setgame.git
synced 2026-06-06 00:05:42 +02:00
chore(backend): Clean up Controllers
This commit is contained in:
@@ -5,73 +5,62 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using backend.Models;
|
||||
|
||||
public class UserLogin
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
namespace backend.Controllers;
|
||||
|
||||
public class UserLogin {
|
||||
public required string Username { get; set; }
|
||||
public required string Password { get; set; }
|
||||
}
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly GameContext _context;
|
||||
[Route("api/v1/[controller]")]
|
||||
public class AuthController(GameContext context) : ControllerBase {
|
||||
private readonly GameContext _context = context;
|
||||
|
||||
public static string CreateMD5(string input)
|
||||
{
|
||||
// Use input string to calculate MD5 hash
|
||||
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
|
||||
{
|
||||
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
|
||||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||||
[HttpPost("login")]
|
||||
public IActionResult Login([FromBody] UserLogin loginData) {
|
||||
var salt = Environment.GetEnvironmentVariable("MD5_SALT") ?? "";
|
||||
var passwordHash = CreateMD5(loginData.Password + salt);
|
||||
|
||||
return Convert.ToHexString(hashBytes); // .NET 5 +
|
||||
var user = _context
|
||||
.Users
|
||||
.Where(u => u.Username == loginData.Username && u.PasswordHash == passwordHash)
|
||||
.FirstOrDefault();
|
||||
|
||||
// Convert the byte array to hexadecimal string prior to .NET 5
|
||||
// StringBuilder sb = new System.Text.StringBuilder();
|
||||
// for (int i = 0; i < hashBytes.Length; i++)
|
||||
// {
|
||||
// sb.Append(hashBytes[i].ToString("X2"));
|
||||
// }
|
||||
// return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public AuthController(GameContext context) {
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public IActionResult Login([FromBody] UserLogin user) {
|
||||
var salt = Environment.GetEnvironmentVariable("MD5_SALT") ?? "";
|
||||
var passwordHash = CreateMD5(user.Password + salt);
|
||||
var dbUser = _context.Users.Where(u => u.Username == user.Username && u.PasswordHash == passwordHash).FirstOrDefault();
|
||||
if (dbUser != null) {
|
||||
var token = GenerateJwtToken(dbUser.Id.ToString());
|
||||
return Ok(new { token });
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
private string GenerateJwtToken(string userId)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWT_SECRET")));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: "wessel.gg",
|
||||
audience: "wessel.gg",
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(30),
|
||||
signingCredentials: creds);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
if (user != null) {
|
||||
var token = GenerateJwtToken(user.Id.ToString());
|
||||
|
||||
return Ok(new { token });
|
||||
}
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
private static string GenerateJwtToken(string userId) {
|
||||
var secret = Environment.GetEnvironmentVariable("JWT_SECRET") ?? "";
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new[] {
|
||||
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
signingCredentials: creds,
|
||||
issuer: Environment.GetEnvironmentVariable("JWT_ISSUER") ?? "",
|
||||
audience: Environment.GetEnvironmentVariable("JWT_AUDIENCE") ?? "",
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddHours(6)
|
||||
);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
public static string CreateMD5(string input) {
|
||||
byte[] inputBytes = Encoding.ASCII.GetBytes(input);
|
||||
byte[] hashBytes = System.Security.Cryptography.MD5.HashData(inputBytes);
|
||||
|
||||
return Convert.ToHexString(hashBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,178 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using backend.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Security.Claims;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace backend.Controllers
|
||||
{
|
||||
[Route("/api/v1/[controller]")]
|
||||
[ApiController]
|
||||
public class GamesController : ControllerBase {
|
||||
private readonly GameContext _context;
|
||||
namespace backend.Controllers;
|
||||
|
||||
public GamesController(GameContext context) {
|
||||
_context = context;
|
||||
[Route("/api/v1/[controller]")]
|
||||
[ApiController]
|
||||
public class GamesController(GameContext context) : ControllerBase {
|
||||
private readonly GameContext _context = context;
|
||||
|
||||
private long? ParseUserId() {
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
|
||||
if (long.TryParse(userIdClaim, out var userId)) {
|
||||
return userId;
|
||||
}
|
||||
|
||||
// GET: api/Games
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<IEnumerable<Game>>> GetGames() {
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!long.TryParse(userIdClaim, out var userId)) {
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
// return await _context.Games.ToListAsync();
|
||||
return await _context.Games.Where(g => g.UserId == userId).ToListAsync();
|
||||
// GET: api/Games
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<IEnumerable<Game>>> GetGames() {
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
// GET: api/Games/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Game>> GetGame(long id)
|
||||
{
|
||||
var game = await _context.Games.FindAsync(id);
|
||||
return await _context.Games
|
||||
.Where(g => g.UserId == user)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
if (game == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return game;
|
||||
// GET: api/Games/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Game>> GetGame(long id) {
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
// PUT: api/Games/5
|
||||
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
|
||||
[HttpPut("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PutGame(long id, Game game)
|
||||
{
|
||||
if (id != game.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
var game = await _context.Games
|
||||
.Where(g => g.Id == id && g.UserId == user)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
_context.Entry(game).State = EntityState.Modified;
|
||||
|
||||
try {
|
||||
await _context.SaveChangesAsync();
|
||||
} catch (DbUpdateConcurrencyException) {
|
||||
if (!GameExists(id)) {
|
||||
return NotFound();
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
if (game == null || game.UserId != user) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// POST: api/Games
|
||||
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Game>> PostGame() {
|
||||
|
||||
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
return game;
|
||||
}
|
||||
|
||||
if (!long.TryParse(userIdClaim, out var userId)) {
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var newGame = new Game {
|
||||
Deck = (
|
||||
from shape in Enum.GetValues(typeof(CardShape)).Cast<CardShape>()
|
||||
from color in Enum.GetValues(typeof(CardColor)).Cast<CardColor>()
|
||||
from count in Enum.GetValues(typeof(CardCount)).Cast<CardCount>()
|
||||
from shade in Enum.GetValues(typeof(CardShade)).Cast<CardShade>()
|
||||
select new Card {
|
||||
Shape = shape,
|
||||
Color = color,
|
||||
Count = count,
|
||||
Shade = shade
|
||||
}.ToUshort()).ToArray(),
|
||||
UserId = userId
|
||||
};
|
||||
|
||||
newGame.ShuffleDeck();
|
||||
newGame.DealHand();
|
||||
|
||||
_context.Games.Add(newGame);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetGame), new { id = newGame.Id }, newGame);
|
||||
// POST: api/Games
|
||||
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Game>> PostGame() {
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
// DELETE: api/Games/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> DeleteGame(long id) {
|
||||
var game = await _context.Games.FindAsync(id);
|
||||
if (game == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var newGame = new Game {
|
||||
UserId = (long)user,
|
||||
Deck = [..
|
||||
from shape in Enum.GetValues<CardShape>().Cast<CardShape>()
|
||||
from color in Enum.GetValues<CardColor>().Cast<CardColor>()
|
||||
from count in Enum.GetValues<CardCount>().Cast<CardCount>()
|
||||
from shade in Enum.GetValues<CardShade>().Cast<CardShade>()
|
||||
select new Card {
|
||||
Shape = shape,
|
||||
Color = color,
|
||||
Count = count,
|
||||
Shade = shade
|
||||
}.ToUshort()
|
||||
]
|
||||
};
|
||||
|
||||
_context.Games.Remove(game);
|
||||
await _context.SaveChangesAsync();
|
||||
newGame.ShuffleDeck();
|
||||
newGame.DealHand();
|
||||
|
||||
return NoContent();
|
||||
_context.Games.Add(newGame);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetGame), new { id = newGame.Id }, newGame);
|
||||
}
|
||||
|
||||
// DELETE: api/Games/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> DeleteGame(long id) {
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
private bool GameExists(long id) {
|
||||
return _context.Games.Any(e => e.Id == id);
|
||||
var game = await _context.Games
|
||||
.Where(g => g.Id == id && g.UserId == user)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (game == null) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]/{id}")]
|
||||
[Authorize]
|
||||
_context.Games.Remove(game);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("[action]/{id}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<SetCheckResult>> CheckSet(
|
||||
long id,
|
||||
[FromBody] ushort[] cardIndices
|
||||
) {
|
||||
var game = await _context.Games.FindAsync(id);
|
||||
if (game == null) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var res = game.IsSet(cardIndices);
|
||||
|
||||
if (res == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return res;
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("[action]/{id}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<int[]>>> SetsInHand(long id) {
|
||||
var game = await _context.Games.FindAsync(id);
|
||||
if (game == null) {
|
||||
return NotFound();
|
||||
}
|
||||
var game = await _context.Games
|
||||
.Where(g => g.Id == id && g.UserId == user)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var res = game.GetIndicesOfSet();
|
||||
|
||||
if (res == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
Console.WriteLine($"Found {res.Count} sets in hand");
|
||||
|
||||
return res;
|
||||
if (game == null) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var res = game.IsSet(cardIndices);
|
||||
|
||||
if (res == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("[action]/{id}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<int[]>>> SetsInHand(long id) {
|
||||
var user = ParseUserId();
|
||||
if (user == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var game = await _context.Games
|
||||
.Where(g => g.Id == id && g.UserId == user)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (game == null) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var res = game.GetIndicesOfSet();
|
||||
|
||||
if (res == null) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ using backend.Models;
|
||||
namespace backend.Migrations
|
||||
{
|
||||
[DbContext(typeof(GameContext))]
|
||||
[Migration("20250325111947_initial")]
|
||||
partial class initial
|
||||
[Migration("20250325145201_InitialDatabase")]
|
||||
partial class InitialDatabase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
@@ -102,13 +102,11 @@ namespace backend.Migrations
|
||||
|
||||
modelBuilder.Entity("backend.Models.Game", b =>
|
||||
{
|
||||
b.HasOne("backend.Models.User", "User")
|
||||
b.HasOne("backend.Models.User", null)
|
||||
.WithMany("Games")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("backend.Models.User", b =>
|
||||
@@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace backend.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class initial : Migration
|
||||
public partial class InitialDatabase : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
@@ -99,13 +99,11 @@ namespace backend.Migrations
|
||||
|
||||
modelBuilder.Entity("backend.Models.Game", b =>
|
||||
{
|
||||
b.HasOne("backend.Models.User", "User")
|
||||
b.HasOne("backend.Models.User", null)
|
||||
.WithMany("Games")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("backend.Models.User", b =>
|
||||
|
||||
@@ -18,8 +18,6 @@ public class Game {
|
||||
public ushort[]? Deck { get; set; }
|
||||
public ushort[] Found { get; set; } = Array.Empty<ushort>();
|
||||
|
||||
public User User { get; set; } = null!;
|
||||
|
||||
public void ShuffleDeck() {
|
||||
if (Deck == null) return;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ interface LoginResponse {
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthService {
|
||||
private API_URL = 'http://localhost:5224/api/Auth';
|
||||
private API_URL = 'http://localhost:5224/api/v1/Auth';
|
||||
private tokenKey = 'auth_token';
|
||||
private isAuthenticatedSubject = new BehaviorSubject<boolean>(this.hasToken());
|
||||
public redirectUrl: string | null = null;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Card, toCard } from '../../app/models/card';
|
||||
// todo: Rewrite to use angular http client instead of axios, supports always sending tokens
|
||||
import axios from 'axios';
|
||||
|
||||
axios.defaults.headers.common['Authorization'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImp0aSI6ImRmNThhYTU3LWZkNzItNGIzYS05OTNmLTY4NjAyNGMzYjdlNSIsImV4cCI6MTc0MjgxODEzOCwiaXNzIjoid2Vzc2VsLmdnIiwiYXVkIjoid2Vzc2VsLmdnIn0.hDf8qcxXeSFQhmgnMzBrH3ZJJMplwZ-1RQwNxeZo5ok';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
||||
export class GameService {
|
||||
private API_URL = 'http://localhost:5224/api/v1/Games';
|
||||
|
||||
public deck: Card[] = [];
|
||||
public hand: Card[] = [];
|
||||
public foundSets: Card[][] = [];
|
||||
@@ -20,8 +20,9 @@ export class GameService {
|
||||
public finishedAt?: Date;
|
||||
public selectedCards: Card[] = [];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
) {}
|
||||
|
||||
public initGame(gameId?: string): Promise<string> {
|
||||
this.deck = [];
|
||||
@@ -35,58 +36,113 @@ export class GameService {
|
||||
}
|
||||
|
||||
public async initializeExistingGame(gameId: string): Promise<string> {
|
||||
const req = await axios.get('http://localhost:5224/api/v1/Games/' + gameId);
|
||||
req.data.deck.forEach((card: number) => {
|
||||
this.deck.push(toCard(card));
|
||||
|
||||
});
|
||||
req.data.hand.forEach((card: number) => {
|
||||
try {
|
||||
const response = await lastValueFrom(this.http.get<any>(`${this.API_URL}/${gameId}`));
|
||||
|
||||
response.deck.forEach((card: number) => {
|
||||
this.deck.push(toCard(card));
|
||||
});
|
||||
|
||||
response.hand.forEach((card: number) => {
|
||||
this.hand.push(toCard(card));
|
||||
});
|
||||
this.startDate = new Date(req.data.startedAt);
|
||||
this.fails = req.data.fails;
|
||||
this.hints = req.data.hints;
|
||||
this.finishedAt = req.data.finishedAt;
|
||||
|
||||
this.startDate = new Date(response.startedAt);
|
||||
this.fails = response.fails;
|
||||
this.hints = response.hints;
|
||||
this.finishedAt = response.finishedAt ? new Date(response.finishedAt) : undefined;
|
||||
|
||||
this.foundSets = [];
|
||||
for (let i = 0; i < req.data.found.length; i += 3) {
|
||||
this.foundSets.push(req.data.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
for (let i = 0; i < response.found.length; i += 3) {
|
||||
this.foundSets.push(response.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
}
|
||||
|
||||
this.gameId = req.data.id;
|
||||
await this.updateSets();
|
||||
|
||||
return req.data.id;
|
||||
|
||||
this.gameId = response.id;
|
||||
this.updateSets();
|
||||
return response.id.toString();
|
||||
} catch (error) {
|
||||
console.error('Error initializing existing game', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeDeck(): Promise<string> {
|
||||
const res = await axios.post('http://localhost:5224/api/v1/Games', {});
|
||||
res.data.deck.forEach((card: number) => {
|
||||
try {
|
||||
const response = await lastValueFrom(this.http.post<any>(this.API_URL, {}));
|
||||
|
||||
response.deck.forEach((card: number) => {
|
||||
this.deck.push(toCard(card));
|
||||
});
|
||||
|
||||
res.data.hand.forEach((card: number) => {
|
||||
});
|
||||
|
||||
response.hand.forEach((card: number) => {
|
||||
this.hand.push(toCard(card));
|
||||
});
|
||||
|
||||
this.startDate = new Date(res.data.startedAt);
|
||||
this.fails = res.data.fails;
|
||||
this.hints = res.data.hints;
|
||||
this.gameId = res.data.id;
|
||||
this.finishedAt = res.data.finishedAt;
|
||||
|
||||
this.startDate = new Date(response.startedAt);
|
||||
this.fails = response.fails;
|
||||
this.hints = response.hints;
|
||||
this.gameId = response.id;
|
||||
this.finishedAt = response.finishedAt ? new Date(response.finishedAt) : undefined;
|
||||
|
||||
this.foundSets = [];
|
||||
for (let i = 0; i < res.data.newState.found.length; i += 3) {
|
||||
this.foundSets.push(res.data.newState.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
for (let i = 0; i < response.found.length; i += 3) {
|
||||
this.foundSets.push(response.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
}
|
||||
|
||||
this.updateSets();
|
||||
return response.id.toString();
|
||||
} catch (error) {
|
||||
console.error('Error initializing deck', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSets(): Promise<Card[][]> {
|
||||
try {
|
||||
const response = await lastValueFrom(
|
||||
this.http.get<number[][]>(`${this.API_URL}/SetsInHand/${this.gameId}`)
|
||||
);
|
||||
|
||||
const cards = response.map((set: number[]) =>
|
||||
set.map((card: number) => this.hand[card])
|
||||
);
|
||||
|
||||
this.possibleSets = cards;
|
||||
return cards;
|
||||
} catch (error) {
|
||||
console.error('Error updating sets', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async checkSet(cards: number[]): Promise<boolean> {
|
||||
try {
|
||||
const response = await lastValueFrom(
|
||||
this.http.post<any>(`${this.API_URL}/CheckSet/${this.gameId}`, cards)
|
||||
);
|
||||
|
||||
this.hand = response.newState.hand.map((card: number) => toCard(card));
|
||||
this.deck = response.newState.deck.map((card: number) => toCard(card));
|
||||
this.fails = response.newState.fails;
|
||||
this.hints = response.newState.hints;
|
||||
this.finishedAt = response.newState.finishedAt ? new Date(response.newState.finishedAt) : undefined;
|
||||
|
||||
this.foundSets = [];
|
||||
for (let i = 0; i < response.newState.found.length; i += 3) {
|
||||
this.foundSets.push(response.newState.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
}
|
||||
|
||||
await this.updateSets();
|
||||
|
||||
return res.data.id;
|
||||
return response.isSet;
|
||||
} catch (error) {
|
||||
console.error('Error checking set', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public selectCard(card: Card): void {
|
||||
const cardIndex = this.hand.indexOf(card);
|
||||
if (cardIndex === -1) return; // Card not found on the board
|
||||
if (cardIndex === -1) return; // Card not found in hand
|
||||
|
||||
if (this.selectedCards.includes(card)) {
|
||||
this.selectedCards = this.selectedCards.filter(c => c !== card);
|
||||
@@ -95,58 +151,29 @@ export class GameService {
|
||||
}
|
||||
|
||||
if (this.selectedCards.length === 3) {
|
||||
const [card1, card2, card3] = this.selectedCards as [Card, Card, Card]; // ✅ Explicitly cast to a tuple
|
||||
if (this.isSet([this.hand.indexOf(card1), this.hand.indexOf(card2), this.hand.indexOf(card3)])) {
|
||||
this.replaceSet();
|
||||
}
|
||||
const indices = this.selectedCards.map(c => this.hand.indexOf(c));
|
||||
this.checkSet(indices).then(isSet => {
|
||||
this.selectedCards = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async updateSets(): Promise<Card[][]> {
|
||||
const req = await axios.get(`http://localhost:5224/api/v1/Games/SetsInHand/${this.gameId}`);
|
||||
const cards = req.data
|
||||
.map((set: number[]) =>
|
||||
set.map((card: number) => this.hand[card])
|
||||
);
|
||||
|
||||
this.possibleSets = cards;
|
||||
|
||||
return cards;
|
||||
public async deleteGame(): Promise<void> {
|
||||
try {
|
||||
await lastValueFrom(this.http.delete<void>(`${this.API_URL}/${this.gameId}`));
|
||||
} catch (error) {
|
||||
console.error('Error deleting game', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private isSet(cards: number[]): boolean {
|
||||
this.selectedCards = [];
|
||||
|
||||
axios.post(`http://localhost:5224/api/v1/Games/CheckSet/${this.gameId}`, cards).then((response) => {
|
||||
this.hand = response.data.newState.hand.map((card: number) => toCard(card));
|
||||
this.deck = response.data.newState.deck.map((card: number) => toCard(card));
|
||||
this.fails = response.data.newState.fails;
|
||||
this.hints = response.data.newState.hints;
|
||||
this.finishedAt = response.data.newState.finishedAt;
|
||||
this.foundSets = [];
|
||||
for (let i = 0; i < response.data.newState.found.length; i += 3) {
|
||||
this.foundSets.push(response.data.newState.found.slice(i, i + 3).map((card: number) => toCard(card)));
|
||||
}
|
||||
|
||||
this.updateSets();
|
||||
|
||||
return response.data.isSet;
|
||||
});
|
||||
|
||||
return false;
|
||||
public async showHint(): Promise<void> {
|
||||
try {
|
||||
await lastValueFrom(this.http.post<void>(`${this.API_URL}/Hint/${this.gameId}`, {}));
|
||||
this.hints++;
|
||||
} catch (error) {
|
||||
console.error('Error showing hint', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private replaceSet(): void {
|
||||
this.hand.find((c, i) => {
|
||||
if (this.selectedCards.includes(c)) {
|
||||
this.hand[i] = this.deck.splice(0, 1)[0];
|
||||
}
|
||||
});
|
||||
this.selectedCards = [];
|
||||
}
|
||||
|
||||
public async deleteGame() {
|
||||
await axios.delete('http://localhost:5224/api/v1/Games/' + this.gameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,40 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
|
||||
// axios.defaults.headers.common['Authorization'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImp0aSI6ImRmNThhYTU3LWZkNzItNGIzYS05OTNmLTY4NjAyNGMzYjdlNSIsImV4cCI6MTc0MjgxODEzOCwiaXNzIjoid2Vzc2VsLmdnIiwiYXVkIjoid2Vzc2VsLmdnIn0.hDf8qcxXeSFQhmgnMzBrH3ZJJMplwZ-1RQwNxeZo5ok';
|
||||
axios.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserDataService {
|
||||
private GAMES_API_URL = 'http://localhost:5224/api/v1/Games';
|
||||
|
||||
constructor() {
|
||||
// this.login({ username: 'admin', password: 'password' });
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private authService: AuthService
|
||||
) {}
|
||||
|
||||
public async login(credentials: { username: string, password: string }): Promise<void> {
|
||||
try {
|
||||
await lastValueFrom(this.authService.login(credentials.username, credentials.password));
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async login(credentials: any) {
|
||||
const response = await axios.post('http://localhost:5224/api/Auth/login', credentials);
|
||||
localStorage.setItem('token', response.data.token);
|
||||
public async getGames(): Promise<any[]> {
|
||||
try {
|
||||
const data = await lastValueFrom(this.http.get<any[]>(this.GAMES_API_URL));
|
||||
|
||||
const sortedGames = data
|
||||
.sort((a: any, b: any) => new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime())
|
||||
.reverse();
|
||||
|
||||
return sortedGames;
|
||||
} catch (error) {
|
||||
console.error('Error getting games:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async getGames(): Promise<any> {
|
||||
const req = await axios.get('http://localhost:5224/api/v1/Games', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImp0aSI6ImRmNThhYTU3LWZkNzItNGIzYS05OTNmLTY4NjAyNGMzYjdlNSIsImV4cCI6MTc0MjgxODEzOCwiaXNzIjoid2Vzc2VsLmdnIiwiYXVkIjoid2Vzc2VsLmdnIn0.hDf8qcxXeSFQhmgnMzBrH3ZJJMplwZ-1RQwNxeZo5ok'
|
||||
}
|
||||
});
|
||||
const sortedGames = req.data
|
||||
.sort((a: any, b: any) => new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime())
|
||||
.reverse();
|
||||
return sortedGames;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user