From 740f8a955de283a0c14673fdae76c2eb34e09b45 Mon Sep 17 00:00:00 2001 From: AleksDw Date: Sat, 31 May 2025 01:51:48 +0200 Subject: [PATCH 1/6] Rename EventEndpoints VolunteerId to UserId --- WebApp/Data/ApplicationDbContext.cs | 2 +- WebApp/Endpoints/AuthEndpoints.cs | 2 +- WebApp/Entities/EventRegistration.cs | 2 +- ...235051_RenameEventRegistration.Designer.cs | 622 ++++++++++++++++++ .../20250530235051_RenameEventRegistration.cs | 97 +++ .../ApplicationDbContextModelSnapshot.cs | 13 +- 6 files changed, 727 insertions(+), 11 deletions(-) create mode 100644 WebApp/Migrations/20250530235051_RenameEventRegistration.Designer.cs create mode 100644 WebApp/Migrations/20250530235051_RenameEventRegistration.cs diff --git a/WebApp/Data/ApplicationDbContext.cs b/WebApp/Data/ApplicationDbContext.cs index 2f4d686..1ca7d77 100644 --- a/WebApp/Data/ApplicationDbContext.cs +++ b/WebApp/Data/ApplicationDbContext.cs @@ -35,7 +35,7 @@ namespace WebApp.Data .HasKey(es => new { es.EventId, es.SkillId }); builder.Entity() - .HasKey(er => new { er.VolunteerId, er.EventId }); + .HasKey(er => new { er.UserId, er.EventId }); builder.Entity() .HasKey(ma => new { ma.Sender, ma.Recipient }); diff --git a/WebApp/Endpoints/AuthEndpoints.cs b/WebApp/Endpoints/AuthEndpoints.cs index 3668d6c..2363ce7 100644 --- a/WebApp/Endpoints/AuthEndpoints.cs +++ b/WebApp/Endpoints/AuthEndpoints.cs @@ -97,7 +97,7 @@ namespace WebApp.Endpoints if(!user.IsOrganisation) { var events = await context.EventRegistrations - .Where(er => er.VolunteerId == user.UserId) + .Where(er => er.UserId == user.UserId) .Select(er => er.Event.ToEventSummaryNoErDto()) .ToListAsync(); diff --git a/WebApp/Entities/EventRegistration.cs b/WebApp/Entities/EventRegistration.cs index 759bc6c..4ae6e2d 100644 --- a/WebApp/Entities/EventRegistration.cs +++ b/WebApp/Entities/EventRegistration.cs @@ -3,7 +3,7 @@ public class EventRegistration { public int EventId { get; set; } - public required int VolunteerId { get; set; } + public required int UserId { get; set; } public DateTime RegisteredAt { get; set; } = DateTime.UtcNow; public Event? Event { get; set; } public User? User { get; set; } diff --git a/WebApp/Migrations/20250530235051_RenameEventRegistration.Designer.cs b/WebApp/Migrations/20250530235051_RenameEventRegistration.Designer.cs new file mode 100644 index 0000000..c402472 --- /dev/null +++ b/WebApp/Migrations/20250530235051_RenameEventRegistration.Designer.cs @@ -0,0 +1,622 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WebApp.Data; + +#nullable disable + +namespace WebApp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250530235051_RenameEventRegistration")] + partial class RenameEventRegistration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("WebApp.Entities.Event", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("EventId")); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("EventDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("EventId"); + + b.HasIndex("OrganisationId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("WebApp.Entities.EventRegistration", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("EventId") + .HasColumnType("integer"); + + b.Property("RegisteredAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UserId", "EventId"); + + b.HasIndex("EventId"); + + b.ToTable("EventRegistrations"); + }); + + modelBuilder.Entity("WebApp.Entities.EventSkill", b => + { + b.Property("EventId") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("integer"); + + b.HasKey("EventId", "SkillId"); + + b.HasIndex("SkillId"); + + b.ToTable("EventSkills"); + }); + + modelBuilder.Entity("WebApp.Entities.Message", b => + { + b.Property("MessageId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("MessageId")); + + b.Property("Content") + .HasColumnType("text"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("IsMsgFromVolunteer") + .HasColumnType("boolean"); + + b.Property("IsoDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("integer"); + + b.Property("VolunteerId") + .HasColumnType("integer"); + + b.HasKey("MessageId"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("WebApp.Entities.MessageActivity", b => + { + b.Property("Sender") + .HasColumnType("integer"); + + b.Property("Recipient") + .HasColumnType("integer"); + + b.Property("RecipientLastActive") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Sender", "Recipient"); + + b.ToTable("MessagesActivities"); + }); + + modelBuilder.Entity("WebApp.Entities.Organisation", b => + { + b.Property("OrganisationId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("OrganisationId")); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("OrganisationId"); + + b.HasIndex("UserId"); + + b.ToTable("Organisations"); + }); + + modelBuilder.Entity("WebApp.Entities.Skill", b => + { + b.Property("SkillId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SkillId")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("SkillId"); + + b.ToTable("Skills"); + }); + + modelBuilder.Entity("WebApp.Entities.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TokenId")); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("TokenId"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("WebApp.Entities.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOrganisation") + .HasColumnType("boolean"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .HasColumnType("text"); + + b.HasKey("UserId"); + + b.ToTable("WebUsers"); + }); + + modelBuilder.Entity("WebApp.Entities.VolunteerSkill", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("integer"); + + b.HasKey("UserId", "SkillId"); + + b.HasIndex("SkillId"); + + b.ToTable("VolunteerSkills"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WebApp.Entities.Event", b => + { + b.HasOne("WebApp.Entities.Organisation", "Organisation") + .WithMany("Events") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("WebApp.Entities.EventRegistration", b => + { + b.HasOne("WebApp.Entities.Event", "Event") + .WithMany("EventRegistrations") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WebApp.Entities.User", "User") + .WithMany("EventRegistrations") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WebApp.Entities.EventSkill", b => + { + b.HasOne("WebApp.Entities.Event", "Event") + .WithMany("EventSkills") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WebApp.Entities.Skill", "Skill") + .WithMany("EventSkills") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("WebApp.Entities.Organisation", b => + { + b.HasOne("WebApp.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WebApp.Entities.Token", b => + { + b.HasOne("WebApp.Entities.User", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WebApp.Entities.VolunteerSkill", b => + { + b.HasOne("WebApp.Entities.Skill", "Skill") + .WithMany("VolunteerSkills") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WebApp.Entities.User", "User") + .WithMany("VolunteerSkills") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Skill"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WebApp.Entities.Event", b => + { + b.Navigation("EventRegistrations"); + + b.Navigation("EventSkills"); + }); + + modelBuilder.Entity("WebApp.Entities.Organisation", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("WebApp.Entities.Skill", b => + { + b.Navigation("EventSkills"); + + b.Navigation("VolunteerSkills"); + }); + + modelBuilder.Entity("WebApp.Entities.User", b => + { + b.Navigation("EventRegistrations"); + + b.Navigation("Tokens"); + + b.Navigation("VolunteerSkills"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WebApp/Migrations/20250530235051_RenameEventRegistration.cs b/WebApp/Migrations/20250530235051_RenameEventRegistration.cs new file mode 100644 index 0000000..7275a55 --- /dev/null +++ b/WebApp/Migrations/20250530235051_RenameEventRegistration.cs @@ -0,0 +1,97 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WebApp.Migrations +{ + /// + public partial class RenameEventRegistration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_EventRegistrations_WebUsers_UserId", + table: "EventRegistrations"); + + migrationBuilder.DropPrimaryKey( + name: "PK_EventRegistrations", + table: "EventRegistrations"); + + migrationBuilder.DropIndex( + name: "IX_EventRegistrations_UserId", + table: "EventRegistrations"); + + migrationBuilder.DropColumn( + name: "VolunteerId", + table: "EventRegistrations"); + + migrationBuilder.AlterColumn( + name: "UserId", + table: "EventRegistrations", + type: "integer", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "integer", + oldNullable: true); + + migrationBuilder.AddPrimaryKey( + name: "PK_EventRegistrations", + table: "EventRegistrations", + columns: new[] { "UserId", "EventId" }); + + migrationBuilder.AddForeignKey( + name: "FK_EventRegistrations_WebUsers_UserId", + table: "EventRegistrations", + column: "UserId", + principalTable: "WebUsers", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_EventRegistrations_WebUsers_UserId", + table: "EventRegistrations"); + + migrationBuilder.DropPrimaryKey( + name: "PK_EventRegistrations", + table: "EventRegistrations"); + + migrationBuilder.AlterColumn( + name: "UserId", + table: "EventRegistrations", + type: "integer", + nullable: true, + oldClrType: typeof(int), + oldType: "integer"); + + migrationBuilder.AddColumn( + name: "VolunteerId", + table: "EventRegistrations", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddPrimaryKey( + name: "PK_EventRegistrations", + table: "EventRegistrations", + columns: new[] { "VolunteerId", "EventId" }); + + migrationBuilder.CreateIndex( + name: "IX_EventRegistrations_UserId", + table: "EventRegistrations", + column: "UserId"); + + migrationBuilder.AddForeignKey( + name: "FK_EventRegistrations_WebUsers_UserId", + table: "EventRegistrations", + column: "UserId", + principalTable: "WebUsers", + principalColumn: "UserId"); + } + } +} diff --git a/WebApp/Migrations/ApplicationDbContextModelSnapshot.cs b/WebApp/Migrations/ApplicationDbContextModelSnapshot.cs index 428e810..af1c340 100644 --- a/WebApp/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/WebApp/Migrations/ApplicationDbContextModelSnapshot.cs @@ -252,7 +252,7 @@ namespace WebApp.Migrations modelBuilder.Entity("WebApp.Entities.EventRegistration", b => { - b.Property("VolunteerId") + b.Property("UserId") .HasColumnType("integer"); b.Property("EventId") @@ -261,15 +261,10 @@ namespace WebApp.Migrations b.Property("RegisteredAt") .HasColumnType("timestamp with time zone"); - b.Property("UserId") - .HasColumnType("integer"); - - b.HasKey("VolunteerId", "EventId"); + b.HasKey("UserId", "EventId"); b.HasIndex("EventId"); - b.HasIndex("UserId"); - b.ToTable("EventRegistrations"); }); @@ -524,7 +519,9 @@ namespace WebApp.Migrations b.HasOne("WebApp.Entities.User", "User") .WithMany("EventRegistrations") - .HasForeignKey("UserId"); + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.Navigation("Event"); From 42e468f28fddf95d194fc1ac44b4567e223f163a Mon Sep 17 00:00:00 2001 From: AleksDw Date: Sat, 31 May 2025 13:34:18 +0200 Subject: [PATCH 2/6] auth frontend --- WebApp/ts/auth.ts | 58 ++++++++++++++++++++++++++ WebApp/ts/login.ts | 38 +++++++++++++++++ WebApp/wwwroot/index.html | 9 ++-- WebApp/wwwroot/js/auth.js | 46 ++++++++++++++++++++ WebApp/wwwroot/js/eventView.js | 12 +++--- WebApp/wwwroot/js/generalUseHelpers.js | 2 +- WebApp/wwwroot/js/login.js | 42 +++++++++++++++++++ WebApp/wwwroot/login.html | 17 ++++++++ 8 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 WebApp/ts/auth.ts create mode 100644 WebApp/ts/login.ts create mode 100644 WebApp/wwwroot/js/auth.js create mode 100644 WebApp/wwwroot/js/login.js create mode 100644 WebApp/wwwroot/login.html diff --git a/WebApp/ts/auth.ts b/WebApp/ts/auth.ts new file mode 100644 index 0000000..2363c03 --- /dev/null +++ b/WebApp/ts/auth.ts @@ -0,0 +1,58 @@ +// /js/auth.ts + +function deleteCookie(name: string): void { + document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; +} + +function logoutUser(): void { + // Inform backend to remove cookie if necessary + fetch('/api/logout', { + method: 'POST', + credentials: 'include', + }).catch((err) => console.warn('Logout request failed:', err)); + + // Clear the auth cookie + deleteCookie('token'); + + // Redirect to login page + window.location.href = 'index.html'; +} + +function redirectToLogin(): void { + window.location.href = 'login.html'; +} + +function checkAuth(): boolean { + // Basic auth check via presence of token cookie + return document.cookie.includes('token='); +} + +function setupAuthUI(): void { + const joinNowBtn = document.getElementById('joinnow-btn'); + const signInBtn = document.getElementById('signin-btn'); + const logoutBtn = document.getElementById('logout-btn'); + + const isAuthenticated = checkAuth(); + + if (joinNowBtn) { + joinNowBtn.classList.toggle('d-none', isAuthenticated); + joinNowBtn.addEventListener('click', redirectToLogin); + } + + if (signInBtn) { + signInBtn.classList.toggle('d-none', isAuthenticated); + signInBtn.addEventListener('click', redirectToLogin); + } + + if (logoutBtn) { + logoutBtn.classList.toggle('d-none', !isAuthenticated); + logoutBtn.addEventListener('click', logoutUser); + } + + // Hide all auth buttons initially until DOM loads + const hiddenBeforeLoad = document.querySelectorAll('.hidden-before-load'); + hiddenBeforeLoad.forEach(el => el.classList.remove('hidden-before-load')); +} + +// Initialize on load +document.addEventListener('DOMContentLoaded', setupAuthUI); diff --git a/WebApp/ts/login.ts b/WebApp/ts/login.ts new file mode 100644 index 0000000..def56be --- /dev/null +++ b/WebApp/ts/login.ts @@ -0,0 +1,38 @@ +document.addEventListener("DOMContentLoaded", () => { + const form = document.getElementById("loginForm") as HTMLFormElement; + const message = document.getElementById("message") as HTMLParagraphElement; + + form.addEventListener("submit", async (e) => { + e.preventDefault(); + message.textContent = ""; + + const email = (document.getElementById("email") as HTMLInputElement).value; + const password = (document.getElementById("password") as HTMLInputElement).value; + + try { + const response = await fetch("/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + + const data = await response.json(); + + if (!response.ok) { + message.textContent = data.message || "Login failed."; + return; + } + + document.cookie = `token=${data.token}; path=/; SameSite=Lax; Secure`; + message.style.color = "green"; + message.textContent = "Login successful!"; + + window.location.href = "/index.html"; + } catch (error) { + message.textContent = "Something went wrong."; + console.error(error); + } + }); +}); \ No newline at end of file diff --git a/WebApp/wwwroot/index.html b/WebApp/wwwroot/index.html index f7b67dc..45971c4 100644 --- a/WebApp/wwwroot/index.html +++ b/WebApp/wwwroot/index.html @@ -56,21 +56,21 @@
+ + -->

Events

@@ -92,5 +92,6 @@ + \ No newline at end of file diff --git a/WebApp/wwwroot/js/auth.js b/WebApp/wwwroot/js/auth.js new file mode 100644 index 0000000..e8be80f --- /dev/null +++ b/WebApp/wwwroot/js/auth.js @@ -0,0 +1,46 @@ +"use strict"; +// /js/auth.ts +function deleteCookie(name) { + document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; +} +function logoutUser() { + // Inform backend to remove cookie if necessary + fetch('/api/logout', { + method: 'POST', + credentials: 'include', + }).catch((err) => console.warn('Logout request failed:', err)); + // Clear the auth cookie + deleteCookie('token'); + // Redirect to login page + window.location.href = 'index.html'; +} +function redirectToLogin() { + window.location.href = 'login.html'; +} +function checkAuth() { + // Basic auth check via presence of token cookie + return document.cookie.includes('token='); +} +function setupAuthUI() { + const joinNowBtn = document.getElementById('joinnow-btn'); + const signInBtn = document.getElementById('signin-btn'); + const logoutBtn = document.getElementById('logout-btn'); + const isAuthenticated = checkAuth(); + if (joinNowBtn) { + joinNowBtn.classList.toggle('d-none', isAuthenticated); + joinNowBtn.addEventListener('click', redirectToLogin); + } + if (signInBtn) { + signInBtn.classList.toggle('d-none', isAuthenticated); + signInBtn.addEventListener('click', redirectToLogin); + } + if (logoutBtn) { + logoutBtn.classList.toggle('d-none', !isAuthenticated); + logoutBtn.addEventListener('click', logoutUser); + } + // Hide all auth buttons initially until DOM loads + const hiddenBeforeLoad = document.querySelectorAll('.hidden-before-load'); + hiddenBeforeLoad.forEach(el => el.classList.remove('hidden-before-load')); +} +// Initialize on load +document.addEventListener('DOMContentLoaded', setupAuthUI); diff --git a/WebApp/wwwroot/js/eventView.js b/WebApp/wwwroot/js/eventView.js index 3b88e16..d5953cd 100644 --- a/WebApp/wwwroot/js/eventView.js +++ b/WebApp/wwwroot/js/eventView.js @@ -36,11 +36,11 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo } catch (err) { if (container !== null) - container.innerHTML = `

To wydarzenie nie istnieje! Powrót ->

`; + container.innerHTML = `

To wydarzenie nie istnieje! Powr�t ->

`; } if (thisEvent == null) { if (container !== null) - container.innerHTML = `

Błąd we wczytywaniu wydarzenia. Powrót ->

`; + container.innerHTML = `

B��d we wczytywaniu wydarzenia. Powr�t ->

`; } else { const titleText = document.getElementById("titleText"); @@ -56,13 +56,13 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo dateText.innerHTML = "When: " + newdateText + " " + newtimeText; //thisEvent.eventDate; organizerText.innerHTML = "Organized by: " + thisEvent.organisationName; if (org_id == thisEvent.organisationId) { - // Użytkownik jest organizacją, która - // stworzyła to wydarzenie + // U�ytkownik jest organizacj�, kt�ra + // stworzy�a to wydarzenie unhideElementById(document, "editBtn"); unhideElementById(document, "removeBtn"); } else if (org_id == -1) { - // Użytkownik jest wolontariuszem + // U�ytkownik jest wolontariuszem unhideElementById(document, "applyBtn"); } unhideElementById(document, "mainContainer"); @@ -78,7 +78,7 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo if (!confirmed) return; try { - // Wysyła żądanie DELETE do API + // Wysy�a ��danie DELETE do API const response = yield fetch(`/api/events/${eventId}`, { method: "DELETE" }); diff --git a/WebApp/wwwroot/js/generalUseHelpers.js b/WebApp/wwwroot/js/generalUseHelpers.js index dc3e607..137fd53 100644 --- a/WebApp/wwwroot/js/generalUseHelpers.js +++ b/WebApp/wwwroot/js/generalUseHelpers.js @@ -29,7 +29,7 @@ export function getMyAccount() { return __awaiter(this, void 0, void 0, function* () { const res = yield fetch("/api/auth/my_account"); if (!res.ok) { - throw Error("Użytkownik niezalogowany!"); + throw Error("U�ytkownik niezalogowany!"); } const data = yield res.json(); return data; diff --git a/WebApp/wwwroot/js/login.js b/WebApp/wwwroot/js/login.js new file mode 100644 index 0000000..96cca61 --- /dev/null +++ b/WebApp/wwwroot/js/login.js @@ -0,0 +1,42 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +document.addEventListener("DOMContentLoaded", () => { + const form = document.getElementById("loginForm"); + const message = document.getElementById("message"); + form.addEventListener("submit", (e) => __awaiter(void 0, void 0, void 0, function* () { + e.preventDefault(); + message.textContent = ""; + const email = document.getElementById("email").value; + const password = document.getElementById("password").value; + try { + const response = yield fetch("/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + const data = yield response.json(); + if (!response.ok) { + message.textContent = data.message || "Login failed."; + return; + } + document.cookie = `token=${data.token}; path=/; SameSite=Lax; Secure`; + message.style.color = "green"; + message.textContent = "Login successful!"; + window.location.href = "/index.html"; + } + catch (error) { + message.textContent = "Something went wrong."; + console.error(error); + } + })); +}); diff --git a/WebApp/wwwroot/login.html b/WebApp/wwwroot/login.html new file mode 100644 index 0000000..afb82fa --- /dev/null +++ b/WebApp/wwwroot/login.html @@ -0,0 +1,17 @@ + + + + + Login + + + +

Login

+
+

+

+ +

+
+ + From 5da58ee030355c3918f35a29ed1c1f2857397846 Mon Sep 17 00:00:00 2001 From: AleksDw Date: Sat, 31 May 2025 13:57:58 +0200 Subject: [PATCH 3/6] fix auth.ts --- WebApp/ts/auth.ts | 3 --- WebApp/wwwroot/js/auth.js | 3 --- WebApp/wwwroot/js/eventView.js | 12 ++++++------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/WebApp/ts/auth.ts b/WebApp/ts/auth.ts index 2363c03..9f91e0c 100644 --- a/WebApp/ts/auth.ts +++ b/WebApp/ts/auth.ts @@ -49,9 +49,6 @@ function setupAuthUI(): void { logoutBtn.addEventListener('click', logoutUser); } - // Hide all auth buttons initially until DOM loads - const hiddenBeforeLoad = document.querySelectorAll('.hidden-before-load'); - hiddenBeforeLoad.forEach(el => el.classList.remove('hidden-before-load')); } // Initialize on load diff --git a/WebApp/wwwroot/js/auth.js b/WebApp/wwwroot/js/auth.js index e8be80f..d217220 100644 --- a/WebApp/wwwroot/js/auth.js +++ b/WebApp/wwwroot/js/auth.js @@ -38,9 +38,6 @@ function setupAuthUI() { logoutBtn.classList.toggle('d-none', !isAuthenticated); logoutBtn.addEventListener('click', logoutUser); } - // Hide all auth buttons initially until DOM loads - const hiddenBeforeLoad = document.querySelectorAll('.hidden-before-load'); - hiddenBeforeLoad.forEach(el => el.classList.remove('hidden-before-load')); } // Initialize on load document.addEventListener('DOMContentLoaded', setupAuthUI); diff --git a/WebApp/wwwroot/js/eventView.js b/WebApp/wwwroot/js/eventView.js index d5953cd..3b88e16 100644 --- a/WebApp/wwwroot/js/eventView.js +++ b/WebApp/wwwroot/js/eventView.js @@ -36,11 +36,11 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo } catch (err) { if (container !== null) - container.innerHTML = `

To wydarzenie nie istnieje! Powr�t ->

`; + container.innerHTML = `

To wydarzenie nie istnieje! Powrót ->

`; } if (thisEvent == null) { if (container !== null) - container.innerHTML = `

B��d we wczytywaniu wydarzenia. Powr�t ->

`; + container.innerHTML = `

Błąd we wczytywaniu wydarzenia. Powrót ->

`; } else { const titleText = document.getElementById("titleText"); @@ -56,13 +56,13 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo dateText.innerHTML = "When: " + newdateText + " " + newtimeText; //thisEvent.eventDate; organizerText.innerHTML = "Organized by: " + thisEvent.organisationName; if (org_id == thisEvent.organisationId) { - // U�ytkownik jest organizacj�, kt�ra - // stworzy�a to wydarzenie + // Użytkownik jest organizacją, która + // stworzyła to wydarzenie unhideElementById(document, "editBtn"); unhideElementById(document, "removeBtn"); } else if (org_id == -1) { - // U�ytkownik jest wolontariuszem + // Użytkownik jest wolontariuszem unhideElementById(document, "applyBtn"); } unhideElementById(document, "mainContainer"); @@ -78,7 +78,7 @@ document.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, vo if (!confirmed) return; try { - // Wysy�a ��danie DELETE do API + // Wysyła żądanie DELETE do API const response = yield fetch(`/api/events/${eventId}`, { method: "DELETE" }); From b194819b6e3751be6feb8afbd04dbef3826ab94a Mon Sep 17 00:00:00 2001 From: AleksDw Date: Sat, 31 May 2025 14:21:02 +0200 Subject: [PATCH 4/6] Add login/logout in every page --- WebApp/wwwroot/create.html | 2 ++ WebApp/wwwroot/view.html | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/WebApp/wwwroot/create.html b/WebApp/wwwroot/create.html index 62ae1aa..7f55362 100644 --- a/WebApp/wwwroot/create.html +++ b/WebApp/wwwroot/create.html @@ -80,6 +80,8 @@ + + diff --git a/WebApp/wwwroot/view.html b/WebApp/wwwroot/view.html index 18bbbd7..ea1b551 100644 --- a/WebApp/wwwroot/view.html +++ b/WebApp/wwwroot/view.html @@ -61,7 +61,7 @@

Place: 127.0.0.1

When: now or never!

Description:

-


+


@@ -71,6 +71,8 @@ + + From 2a8fff39c947b3b6622f0c5aa5f2365ec914ff35 Mon Sep 17 00:00:00 2001 From: AleksDw Date: Sat, 31 May 2025 14:37:06 +0200 Subject: [PATCH 5/6] Fix logout so it deletes token in database --- WebApp/ts/auth.ts | 24 +++++++++++++----------- WebApp/wwwroot/js/auth.js | 33 +++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/WebApp/ts/auth.ts b/WebApp/ts/auth.ts index 9f91e0c..4e595ca 100644 --- a/WebApp/ts/auth.ts +++ b/WebApp/ts/auth.ts @@ -4,18 +4,17 @@ function deleteCookie(name: string): void { document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; } -function logoutUser(): void { - // Inform backend to remove cookie if necessary - fetch('/api/logout', { - method: 'POST', - credentials: 'include', - }).catch((err) => console.warn('Logout request failed:', err)); + async function logoutUser(): Promise { + await fetch("/api/auth/logout", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); - // Clear the auth cookie - deleteCookie('token'); + deleteCookie('token'); - // Redirect to login page - window.location.href = 'index.html'; + window.location.href = "/index.html"; } function redirectToLogin(): void { @@ -46,7 +45,10 @@ function setupAuthUI(): void { if (logoutBtn) { logoutBtn.classList.toggle('d-none', !isAuthenticated); - logoutBtn.addEventListener('click', logoutUser); + logoutBtn.addEventListener('click', (e) => { + e.preventDefault(); + logoutUser(); + }); } } diff --git a/WebApp/wwwroot/js/auth.js b/WebApp/wwwroot/js/auth.js index d217220..b494245 100644 --- a/WebApp/wwwroot/js/auth.js +++ b/WebApp/wwwroot/js/auth.js @@ -1,18 +1,28 @@ "use strict"; // /js/auth.ts +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; function deleteCookie(name) { document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`; } function logoutUser() { - // Inform backend to remove cookie if necessary - fetch('/api/logout', { - method: 'POST', - credentials: 'include', - }).catch((err) => console.warn('Logout request failed:', err)); - // Clear the auth cookie - deleteCookie('token'); - // Redirect to login page - window.location.href = 'index.html'; + return __awaiter(this, void 0, void 0, function* () { + yield fetch("/api/auth/logout", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + deleteCookie('token'); + window.location.href = "/index.html"; + }); } function redirectToLogin() { window.location.href = 'login.html'; @@ -36,7 +46,10 @@ function setupAuthUI() { } if (logoutBtn) { logoutBtn.classList.toggle('d-none', !isAuthenticated); - logoutBtn.addEventListener('click', logoutUser); + logoutBtn.addEventListener('click', (e) => { + e.preventDefault(); + logoutUser(); + }); } } // Initialize on load From 32027f7384372d5eea6e16bf62af535c654508e1 Mon Sep 17 00:00:00 2001 From: eee4 <41441600+eee4@users.noreply.github.com> Date: Sat, 31 May 2025 18:19:15 +0200 Subject: [PATCH 6/6] feat: add first volunteer skill endpoint (add_skill) along with dtos --- WebApp/DTOs/SingleSkillDto.cs | 8 ++++++ WebApp/DTOs/SkillSummaryDto.cs | 9 ++++++ WebApp/Endpoints/AuthEndpoints.cs | 37 +++++++++++++++++++++++++ WebApp/Mapping/SkillMapping.cs | 25 +++++++++++++++++ WebApp/Mapping/VolunteerSkillMapping.cs | 17 ++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 WebApp/DTOs/SingleSkillDto.cs create mode 100644 WebApp/DTOs/SkillSummaryDto.cs create mode 100644 WebApp/Mapping/SkillMapping.cs create mode 100644 WebApp/Mapping/VolunteerSkillMapping.cs diff --git a/WebApp/DTOs/SingleSkillDto.cs b/WebApp/DTOs/SingleSkillDto.cs new file mode 100644 index 0000000..a6d5d47 --- /dev/null +++ b/WebApp/DTOs/SingleSkillDto.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace WebApp.DTOs; + +public record class SingleSkillDto +( + [Required] int Skill +); diff --git a/WebApp/DTOs/SkillSummaryDto.cs b/WebApp/DTOs/SkillSummaryDto.cs new file mode 100644 index 0000000..597d39b --- /dev/null +++ b/WebApp/DTOs/SkillSummaryDto.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations; + +namespace WebApp.DTOs; + +public record class SkillSummaryDto +( + [Required] int SkillId, + [Required] string SkillName +); diff --git a/WebApp/Endpoints/AuthEndpoints.cs b/WebApp/Endpoints/AuthEndpoints.cs index 2363ce7..f4aecd2 100644 --- a/WebApp/Endpoints/AuthEndpoints.cs +++ b/WebApp/Endpoints/AuthEndpoints.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; +using System.Runtime.Intrinsics.Arm; using System.Security.Cryptography; using System.Text; using WebApp.Data; @@ -123,6 +124,42 @@ namespace WebApp.Endpoints }); + group.MapPost("/add_skill", async (SingleSkillDto dto, HttpContext httpContext, ApplicationDbContext context, GeneralUseHelpers guh) => + { + // Uzyskaj użytkownika z tokenu + Token? token = await guh.GetTokenFromHTTPContext(httpContext); + User? user = await guh.GetUserFromToken(token); + + // Tylko wolontariusze powinno móc dodawać swoje skille + if (user == null || user.IsOrganisation) { + return Results.Json(new { message = "Unauthorized" }, statusCode: 401); + } + + // Szukamy skilla w bazie o ID takim, jak w otrzymanym DTO + Skill? skill = await context.Skills.FindAsync(dto.Skill); + if (skill is null) + { + return Results.Json(new { message = "Skill not found" }, statusCode: 404); + } + + // Sprawdzamy, czy ten użytkownik nie ma już takiego skilla. Jeżeli ma, nie ma sensu dodawać go kilkukrotnie. + VolunteerSkill? vs = await context.VolunteerSkills.FirstOrDefaultAsync(v => v.UserId == user.UserId && v.SkillId == dto.Skill); + if (vs is null) + { + // Nie ma - zatem musimy dodać nowy VolunteerSkill do bazy + VolunteerSkill newVs = dto.ToVolunteerSkillEntity(user.UserId); + context.VolunteerSkills.Add(newVs); + await context.SaveChangesAsync(); + + } else + { + // Ma - (ta para UserId <-> SkillId już istnieje w bazie) użytkownik już ma ten skill + return Results.Json(new { message = "User already has this skill" }, statusCode: 400); + } + + return Results.Json(new { message = "Skill added successfully!" }, statusCode: 201); + }); + return group; } diff --git a/WebApp/Mapping/SkillMapping.cs b/WebApp/Mapping/SkillMapping.cs new file mode 100644 index 0000000..fb96056 --- /dev/null +++ b/WebApp/Mapping/SkillMapping.cs @@ -0,0 +1,25 @@ +using WebApp.DTOs; +using WebApp.Entities; + +namespace WebApp.Mapping +{ + public static class SkillMapping + { + public static Skill ToSkillEntity(this SingleSkillDto SSDto, string name) + { + return new Skill() + { + SkillId = SSDto.Skill, + Name = name + }; + } + + public static SkillSummaryDto ToSkillSummaryDto(this Skill s) + { + return new SkillSummaryDto( + s.SkillId, + s.Name + ); + } + } +} diff --git a/WebApp/Mapping/VolunteerSkillMapping.cs b/WebApp/Mapping/VolunteerSkillMapping.cs new file mode 100644 index 0000000..a92198a --- /dev/null +++ b/WebApp/Mapping/VolunteerSkillMapping.cs @@ -0,0 +1,17 @@ +using WebApp.DTOs; +using WebApp.Entities; + +namespace WebApp.Mapping +{ + public static class VolunteerSkillMapping + { + public static VolunteerSkill ToVolunteerSkillEntity(this SingleSkillDto SSDto, int uid) + { + return new VolunteerSkill() + { + UserId = uid, + SkillId = SSDto.Skill, + }; + } + } +}