diff --git a/.gitignore b/.gitignore
index 223a8e2..da7a856 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@
/backend/API/obj
/backend/API/appsettings.Development.json
/backend/API/appsettings.json
-/backend/API/bin/Debug/net8.0
+*bin
diff --git a/backend/API/API.csproj b/backend/API/API.csproj
index 9daa180..1e9549b 100644
--- a/backend/API/API.csproj
+++ b/backend/API/API.csproj
@@ -7,6 +7,17 @@
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/backend/API/BusinessLogic/UserLogic.cs b/backend/API/BusinessLogic/UserLogic.cs
new file mode 100644
index 0000000..07fb887
--- /dev/null
+++ b/backend/API/BusinessLogic/UserLogic.cs
@@ -0,0 +1,236 @@
+using API.DBAccess;
+using API.Models.UserModels;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.IdentityModel.Tokens;
+using Org.BouncyCastle.Asn1.Ocsp;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace API.BusinessLogic
+{
+ public class UserLogic
+ {
+ private readonly DbAccess _dbAccess;
+ private readonly IConfiguration _configuration;
+
+ public UserLogic(IConfiguration configuration, DbAccess dbAccess)
+ {
+ _dbAccess = dbAccess;
+ _configuration = configuration;
+ }
+
+ public async Task GetUser(int userId)
+ {
+ User user = await _dbAccess.ReadUser(userId);
+
+ if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
+ return new OkObjectResult(new { user.Id, user.UserName, user.Email });
+ }
+
+ public async Task RegisterUser(CreateUserDTO userDTO)
+ {
+ if (!EmailCheck(userDTO.Email))
+ {
+ return new ConflictObjectResult(new { message = "Invalid email address" });
+ }
+
+ if (!PasswordSecurity(userDTO.Password))
+ {
+ return new ConflictObjectResult(new { message = "Password is not up to the security standard" });
+ }
+
+ var users = await _dbAccess.ReadAllUsers();
+
+ foreach (var item in users)
+ {
+ if (item.UserName == userDTO.UserName)
+ {
+ return new ConflictObjectResult(new { message = "Username is already in use." });
+ }
+
+ if (item.Email == userDTO.Email)
+ {
+ return new ConflictObjectResult(new { message = "Email is already in use." });
+ }
+ }
+
+ string salt = Guid.NewGuid().ToString();
+ string hashedPassword = ComputeHash(userDTO.Password, SHA256.Create(), salt);
+
+ User user = new User
+ {
+ UserName = userDTO.UserName,
+ Email = userDTO.Email,
+ Password = hashedPassword,
+ Salt = salt,
+ };
+
+ return await _dbAccess.CreateUser(user);
+ }
+
+ public async Task Login(LoginDTO loginDTO)
+ {
+ var user = await _dbAccess.ReadUserForLogin(loginDTO.EmailUsr);
+
+ if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
+
+ string hashedPassword = ComputeHash(loginDTO.Password, SHA256.Create(), user.Salt);
+
+ if (user.Password == hashedPassword)
+ {
+ var token = GenerateJwtToken(user);
+
+ user = await UpdateRefreshToken(user);
+
+ return new OkObjectResult(new { token, user.UserName, user.Id, refreshToken = user.RefreshToken });
+ }
+
+ return new ConflictObjectResult(new { message = "Invalid password" });
+ }
+
+ public async Task EditProfile(UpdateUserDTO userDTO, int userId)
+ {
+ var profile = await _dbAccess.ReadUser(userId);
+ var users = await _dbAccess.ReadAllUsers();
+
+ if (profile == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
+
+ if (!EmailCheck(userDTO.Email))
+ {
+ return new ConflictObjectResult(new { message = "Invalid email address" });
+ }
+
+ foreach (var item in users)
+ {
+ if (item.UserName == userDTO.UserName)
+ {
+ return new ConflictObjectResult(new { message = "Username is already in use." });
+ }
+
+ if (item.Email == userDTO.Email)
+ {
+ return new ConflictObjectResult(new { message = "Email is already in use." });
+ }
+ }
+
+ if (userDTO.Email == "" || userDTO.Email == null)
+ {
+ return new ConflictObjectResult(new { message = "Please enter an email" });
+ }
+
+ if (userDTO.UserName == "" || userDTO.UserName == null)
+ {
+ return new ConflictObjectResult(new { message = "Please enter an userName" });
+ }
+
+ profile.Email = userDTO.Email;
+ profile.UserName = userDTO.UserName;
+
+ return await _dbAccess.UpdateUser(profile);
+ }
+
+ public async Task ChangePassword(ChangePasswordDTO passwordDTO, int userId)
+ {
+ var user = await _dbAccess.ReadUser(userId);
+
+ if (user == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
+
+ string hashedPassword = ComputeHash(passwordDTO.OldPassword, SHA256.Create(), user.Salt);
+
+ if (user.Password != hashedPassword)
+ {
+ return new ConflictObjectResult(new { message = "Old password is incorrect" });
+
+ }
+
+ if (!PasswordSecurity(passwordDTO.NewPassword))
+ {
+ return new ConflictObjectResult(new { message = "New password is not up to the security standard" });
+ }
+
+ string hashedNewPassword = ComputeHash(passwordDTO.NewPassword, SHA256.Create(), user.Salt);
+ user.Password = hashedNewPassword;
+
+ return await _dbAccess.UpdatePassword(user);
+ }
+
+ public async Task DeleteUser(int userId)
+ {
+ var user = await _dbAccess.ReadUserForDelete(userId);
+
+ if (user != null) { return await _dbAccess.DeleteUser(user); }
+
+ return new ConflictObjectResult(new { message = "Invalid user" });
+ }
+
+ public async Task RefreshToken(string refreshToken)
+ {
+ User user = await _dbAccess.ReadUserByRefreshToken(refreshToken);
+ if (user == null) { return new ConflictObjectResult(new { message = "Could not match refreshtoken" }); }
+ user = await UpdateRefreshToken(user);
+ string jwtToken = GenerateJwtToken(user);
+ return new OkObjectResult(new { token = jwtToken, refreshToken = user.RefreshToken });
+ }
+
+ private bool PasswordSecurity(string password)
+ {
+ var hasMinimum8Chars = new Regex(@".{8,}");
+
+ return hasMinimum8Chars.IsMatch(password);
+ }
+
+ private bool EmailCheck(string email)
+ {
+ return new Regex(@".+@.+\..+").IsMatch(email);
+ }
+
+ private static string ComputeHash(string input, HashAlgorithm algorithm, string salt)
+ {
+ Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
+ Byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
+
+ // Combine salt and input bytes
+ Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length];
+ saltBytes.CopyTo(saltedInput, 0);
+ inputBytes.CopyTo(saltedInput, saltBytes.Length);
+
+ Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
+
+ return BitConverter.ToString(hashedBytes);
+ }
+
+ private string GenerateJwtToken(User user)
+ {
+ var claims = new[]
+ {
+ new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
+ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
+ new Claim(ClaimTypes.Name, user.UserName)
+ };
+
+ var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes
+ (_configuration["JwtSettings:Key"]));
+ var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
+
+ var token = new JwtSecurityToken(
+ _configuration["JwtSettings:Issuer"],
+ _configuration["JwtSettings:Audience"],
+ claims,
+ expires: DateTime.Now.AddHours(1),
+ signingCredentials: creds);
+
+ return new JwtSecurityTokenHandler().WriteToken(token);
+ }
+
+ private async Task UpdateRefreshToken(User user)
+ {
+ user.RefreshToken = Guid.NewGuid().ToString();
+ user.RefreshTokenExpireAt = DateTime.Now.AddDays(30);
+ await _dbAccess.UpdateUser(user);
+ return user;
+ }
+ }
+}
diff --git a/backend/API/Controllers/UserController.cs b/backend/API/Controllers/UserController.cs
new file mode 100644
index 0000000..30ecf89
--- /dev/null
+++ b/backend/API/Controllers/UserController.cs
@@ -0,0 +1,78 @@
+using API.BusinessLogic;
+using API.Models.UserModels;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.Security.Claims;
+
+namespace API.Controllers
+{
+ [ApiController]
+ [Route("api/[controller]")]
+ public class UserController : Controller
+ {
+ private readonly UserLogic _userLogic;
+
+ public UserController(UserLogic userLogic)
+ {
+ _userLogic = userLogic;
+ }
+
+ [Authorize]
+ [HttpGet("get")]
+ public async Task ReadUser()
+ {
+ var claims = HttpContext.User.Claims;
+ string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ int userId = Convert.ToInt32(userIdString);
+ return await _userLogic.GetUser(userId);
+ }
+
+ [HttpPost("login")]
+ public async Task Login([FromBody] LoginDTO loginDTO)
+ {
+ return await _userLogic.Login(loginDTO);
+ }
+
+ [HttpPost("create")]
+ public async Task CreateUser([FromBody] CreateUserDTO userDTO)
+ {
+ return await _userLogic.RegisterUser(userDTO);
+ }
+
+ [Authorize]
+ [HttpPut("change-password")]
+ public async Task ChangePassword([FromBody] ChangePasswordDTO passwordDTO)
+ {
+ var claims = HttpContext.User.Claims;
+ string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ int userId = Convert.ToInt32(userIdString);
+ return await _userLogic.ChangePassword(passwordDTO, userId);
+ }
+
+ [Authorize]
+ [HttpPut("update")]
+ public async Task UpdateUser([FromBody] UpdateUserDTO userDTO)
+ {
+ var claims = HttpContext.User.Claims;
+ string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ int userId = Convert.ToInt32(userIdString);
+ return await _userLogic.EditProfile(userDTO, userId);
+ }
+
+ [Authorize]
+ [HttpDelete("delete")]
+ public async Task DeleteUser()
+ {
+ var claims = HttpContext.User.Claims;
+ string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
+ int userId = Convert.ToInt32(userIdString);
+ return await _userLogic.DeleteUser(userId);
+ }
+
+ [HttpPost("refreshtoken/{refreshToken}")]
+ public async Task RefreashToken(string refreshToken)
+ {
+ return await _userLogic.RefreshToken(refreshToken);
+ }
+ }
+}
diff --git a/backend/API/Controllers/WeatherForecastController.cs b/backend/API/Controllers/WeatherForecastController.cs
deleted file mode 100644
index aaf3a93..0000000
--- a/backend/API/Controllers/WeatherForecastController.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-namespace API.Controllers
-{
- [ApiController]
- [Route("[controller]")]
- public class WeatherForecastController : ControllerBase
- {
- private static readonly string[] Summaries = new[]
- {
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
- };
-
- private readonly ILogger _logger;
-
- public WeatherForecastController(ILogger logger)
- {
- _logger = logger;
- }
-
- [HttpGet(Name = "GetWeatherForecast")]
- public IEnumerable Get()
- {
- return Enumerable.Range(1, 5).Select(index => new WeatherForecast
- {
- Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- TemperatureC = Random.Shared.Next(-20, 55),
- Summary = Summaries[Random.Shared.Next(Summaries.Length)]
- })
- .ToArray();
- }
- }
-}
diff --git a/backend/API/DBAccess/DBContext.cs b/backend/API/DBAccess/DBContext.cs
new file mode 100644
index 0000000..e7fffff
--- /dev/null
+++ b/backend/API/DBAccess/DBContext.cs
@@ -0,0 +1,12 @@
+using API.Models.UserModels;
+using Microsoft.EntityFrameworkCore;
+
+namespace API.DBAccess
+{
+ public class DBContext : DbContext
+ {
+ public DbSet Users { get; set; }
+
+ public DBContext(DbContextOptions options) : base(options) { }
+ }
+}
diff --git a/backend/API/DBAccess/DbAccess.cs b/backend/API/DBAccess/DbAccess.cs
new file mode 100644
index 0000000..f994c5a
--- /dev/null
+++ b/backend/API/DBAccess/DbAccess.cs
@@ -0,0 +1,93 @@
+using API.Models.UserModels;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace API.DBAccess
+{
+ public class DbAccess
+ {
+ private readonly DBContext _context;
+
+ public DbAccess(DBContext context)
+ {
+ _context = context;
+ }
+
+ public async Task ReadUser(int userId)
+ {
+ return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
+ }
+
+ public async Task> ReadAllUsers()
+ {
+ return await _context.Users.ToListAsync();
+ }
+
+ public async Task ReadUserByRefreshToken(string refreshToken)
+ {
+ return await _context.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);
+ }
+
+ public async Task ReadUserForDelete(int userId)
+ {
+ return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
+ }
+
+ public async Task ReadUserForLogin(string emailOrUsername)
+ {
+ if (emailOrUsername.Contains("@"))
+ {
+ return await _context.Users.FirstOrDefaultAsync(u => u.Email == emailOrUsername);
+ }
+ else
+ {
+ return await _context.Users.FirstOrDefaultAsync(u => u.UserName == emailOrUsername);
+ }
+ }
+
+ public async Task CreateUser(User user)
+ {
+ _context.Users.Add(user);
+
+ bool saved = await _context.SaveChangesAsync() == 1;
+
+ if (saved) { return new OkObjectResult(true); }
+
+ return new ConflictObjectResult(new { message = "Could not save to database" });
+
+ }
+
+ public async Task UpdateUser(User user)
+ {
+ _context.Entry(user).State = EntityState.Modified;
+
+ bool saved = await _context.SaveChangesAsync() == 1;
+
+ if (saved) { return new OkObjectResult(user); }
+
+ return new ConflictObjectResult(new { message = "Could not save to database" });
+
+ }
+
+ public async Task UpdatePassword(User user)
+ {
+ _context.Entry(user).State = EntityState.Modified;
+
+ bool saved = await _context.SaveChangesAsync() == 1;
+
+ if (saved) { return new OkObjectResult(user); }
+
+ return new ConflictObjectResult(new { message = "Could not save to database" });
+ }
+
+ public async Task DeleteUser(User user)
+ {
+ _context.Users.Remove(user);
+ bool saved = await _context.SaveChangesAsync() >= 0;
+
+ if (saved) { return new OkObjectResult(saved); }
+
+ return new ConflictObjectResult(new { message = "Could not save to database" });
+ }
+ }
+}
diff --git a/backend/API/Migrations/20250423074254_init.Designer.cs b/backend/API/Migrations/20250423074254_init.Designer.cs
new file mode 100644
index 0000000..9c85172
--- /dev/null
+++ b/backend/API/Migrations/20250423074254_init.Designer.cs
@@ -0,0 +1,59 @@
+//
+using System;
+using API.DBAccess;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace API.Migrations
+{
+ [DbContext(typeof(DBContext))]
+ [Migration("20250423074254_init")]
+ partial class init
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("API.Models.UserModels.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("RefreshToken")
+ .HasColumnType("longtext");
+
+ b.Property("RefreshTokenExpireAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Salt")
+ .HasColumnType("longtext");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/API/Migrations/20250423074254_init.cs b/backend/API/Migrations/20250423074254_init.cs
new file mode 100644
index 0000000..0580713
--- /dev/null
+++ b/backend/API/Migrations/20250423074254_init.cs
@@ -0,0 +1,45 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using MySql.EntityFrameworkCore.Metadata;
+
+#nullable disable
+
+namespace API.Migrations
+{
+ ///
+ public partial class init : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterDatabase()
+ .Annotation("MySQL:Charset", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "Users",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
+ UserName = table.Column(type: "longtext", nullable: false),
+ Password = table.Column(type: "longtext", nullable: false),
+ Email = table.Column(type: "longtext", nullable: false),
+ Salt = table.Column(type: "longtext", nullable: true),
+ RefreshToken = table.Column(type: "longtext", nullable: true),
+ RefreshTokenExpireAt = table.Column(type: "datetime(6)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Users", x => x.Id);
+ })
+ .Annotation("MySQL:Charset", "utf8mb4");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Users");
+ }
+ }
+}
diff --git a/backend/API/Migrations/DBContextModelSnapshot.cs b/backend/API/Migrations/DBContextModelSnapshot.cs
new file mode 100644
index 0000000..71ff77e
--- /dev/null
+++ b/backend/API/Migrations/DBContextModelSnapshot.cs
@@ -0,0 +1,56 @@
+//
+using System;
+using API.DBAccess;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace API.Migrations
+{
+ [DbContext(typeof(DBContext))]
+ partial class DBContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.10")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("API.Models.UserModels.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("RefreshToken")
+ .HasColumnType("longtext");
+
+ b.Property("RefreshTokenExpireAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Salt")
+ .HasColumnType("longtext");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/API/Models/UserModels/ChangePasswordDTO.cs b/backend/API/Models/UserModels/ChangePasswordDTO.cs
new file mode 100644
index 0000000..59063ed
--- /dev/null
+++ b/backend/API/Models/UserModels/ChangePasswordDTO.cs
@@ -0,0 +1,9 @@
+namespace API.Models.UserModels
+{
+ public class ChangePasswordDTO
+ {
+ public string OldPassword { get; set; }
+
+ public string NewPassword { get; set; }
+ }
+}
diff --git a/backend/API/Models/UserModels/CreateUserDTO.cs b/backend/API/Models/UserModels/CreateUserDTO.cs
new file mode 100644
index 0000000..c674c21
--- /dev/null
+++ b/backend/API/Models/UserModels/CreateUserDTO.cs
@@ -0,0 +1,11 @@
+namespace API.Models.UserModels
+{
+ public class CreateUserDTO
+ {
+ public string UserName { get; set; }
+
+ public string Password { get; set; }
+
+ public string Email { get; set; }
+ }
+}
diff --git a/backend/API/Models/UserModels/LoginDTO.cs b/backend/API/Models/UserModels/LoginDTO.cs
new file mode 100644
index 0000000..721cda3
--- /dev/null
+++ b/backend/API/Models/UserModels/LoginDTO.cs
@@ -0,0 +1,9 @@
+namespace API.Models.UserModels
+{
+ public class LoginDTO
+ {
+ public string EmailUsr { get; set; }
+
+ public string Password { get; set; }
+ }
+}
diff --git a/backend/API/Models/UserModels/UpdateUserDTO.cs b/backend/API/Models/UserModels/UpdateUserDTO.cs
new file mode 100644
index 0000000..6fdae36
--- /dev/null
+++ b/backend/API/Models/UserModels/UpdateUserDTO.cs
@@ -0,0 +1,9 @@
+namespace API.Models.UserModels
+{
+ public class UpdateUserDTO
+ {
+ public string UserName { get; set; }
+
+ public string Email { get; set; }
+ }
+}
diff --git a/backend/API/Models/UserModels/User.cs b/backend/API/Models/UserModels/User.cs
new file mode 100644
index 0000000..107646a
--- /dev/null
+++ b/backend/API/Models/UserModels/User.cs
@@ -0,0 +1,19 @@
+namespace API.Models.UserModels
+{
+ public class User
+ {
+ public int Id { get; set; }
+
+ public string UserName { get; set; }
+
+ public string Password { get; set; }
+
+ public string Email { get; set; }
+
+ public string? Salt { get; set; }
+
+ public string? RefreshToken { get; set; }
+
+ public DateTime RefreshTokenExpireAt { get; set; }
+ }
+}
diff --git a/backend/API/Program.cs b/backend/API/Program.cs
index 48863a6..58ea8e3 100644
--- a/backend/API/Program.cs
+++ b/backend/API/Program.cs
@@ -1,25 +1,34 @@
-var builder = WebApplication.CreateBuilder(args);
+using API;
+using Microsoft.AspNetCore;
+using API.DBAccess;
+using Microsoft.EntityFrameworkCore;
-// Add services to the container.
-builder.Services.AddControllers();
-// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
-builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
-
-var app = builder.Build();
-
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
+internal class Program
{
- app.UseSwagger();
- app.UseSwaggerUI();
-}
+ private static void Main(string[] args)
+ {
+ var app = CreateWebHostBuilder(args).Build();
-app.UseHttpsRedirection();
+ RunMigrations(app);
-app.UseAuthorization();
+ app.Run();
+ }
-app.MapControllers();
+ // Calls the startup class and creates the webinterface
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseUrls("http://0.0.0.0:5000")
+ .UseStartup();
-app.Run();
+ public static async void RunMigrations(IWebHost app)
+ {
+ await using var scope = app.Services.CreateAsyncScope();
+ await using var db = scope.ServiceProvider.GetService();
+
+ if (db != null)
+ {
+ await db.Database.MigrateAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/API/Startup.cs b/backend/API/Startup.cs
new file mode 100644
index 0000000..c0d4f97
--- /dev/null
+++ b/backend/API/Startup.cs
@@ -0,0 +1,122 @@
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.OpenApi.Models;
+using System.Text;
+using API.DBAccess;
+using API.BusinessLogic;
+
+namespace API
+{
+ public class Startup
+ {
+ public IConfiguration _configuration { get; }
+
+ public Startup(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Sets the connectionstring to the database so dbcontext can find it
+ services.AddDbContext(options =>
+ options.UseMySQL(_configuration.GetConnectionString("Database")));
+
+ services.AddScoped();
+ services.AddScoped();
+
+ services.AddControllers();
+
+ services.AddAuthentication(x =>
+ {
+ x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(x =>
+ {
+ x.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidIssuer = _configuration["JwtSettings:Issuer"],
+ ValidAudience = _configuration["JwtSettings:Audience"],
+ IssuerSigningKey = new SymmetricSecurityKey
+ (
+ Encoding.UTF8.GetBytes(_configuration["JwtSettings:Key"])
+ ),
+ ValidateIssuer = true,
+ ValidateAudience = true,
+ ValidateLifetime = true,
+ ValidateIssuerSigningKey = true
+ };
+ });
+
+
+ services.AddCors(options =>
+ {
+ options.AddPolicy("AllowAll", builder =>
+ {
+ builder.AllowAnyOrigin()
+ .AllowAnyMethod().AllowAnyHeader();
+ });
+ });
+
+ services.AddSwaggerGen(c =>
+ {
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
+
+ // Configure Swagger to use Bearer token authentication
+ c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ Description = "JWT Authorization header using the Bearer scheme",
+ Type = SecuritySchemeType.Http,
+ Scheme = "bearer"
+ });
+
+ c.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ new string[] { }
+ }
+ });
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
+ });
+
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseCors("AllowAll");
+
+ app.UseRouting();
+
+ app.UseAuthentication();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/API/WeatherForecast.cs b/backend/API/WeatherForecast.cs
deleted file mode 100644
index 10c41a8..0000000
--- a/backend/API/WeatherForecast.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace API
-{
- public class WeatherForecast
- {
- public DateOnly Date { get; set; }
-
- public int TemperatureC { get; set; }
-
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-
- public string? Summary { get; set; }
- }
-}