Merge branch 'Prothotype-login-page' into MyBranchKarol

This commit is contained in:
2025-05-31 18:24:59 +02:00
19 changed files with 1044 additions and 17 deletions

View File

@@ -0,0 +1,8 @@
using System.ComponentModel.DataAnnotations;
namespace WebApp.DTOs;
public record class SingleSkillDto
(
[Required] int Skill
);

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace WebApp.DTOs;
public record class SkillSummaryDto
(
[Required] int SkillId,
[Required] string SkillName
);

View File

@@ -35,7 +35,7 @@ namespace WebApp.Data
.HasKey(es => new { es.EventId, es.SkillId });
builder.Entity<EventRegistration>()
.HasKey(er => new { er.VolunteerId, er.EventId });
.HasKey(er => new { er.UserId, er.EventId });
builder.Entity<MessageActivity>()
.HasKey(ma => new { ma.Sender, ma.Recipient });

View File

@@ -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;
@@ -97,7 +98,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();
@@ -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;
}

View File

@@ -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; }

View File

@@ -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
);
}
}
}

View File

@@ -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,
};
}
}
}

View File

@@ -0,0 +1,622 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("WebApp.Entities.Event", b =>
{
b.Property<int>("EventId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("EventId"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<DateTime>("EventDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Location")
.IsRequired()
.HasColumnType("text");
b.Property<int>("OrganisationId")
.HasColumnType("integer");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("text");
b.HasKey("EventId");
b.HasIndex("OrganisationId");
b.ToTable("Events");
});
modelBuilder.Entity("WebApp.Entities.EventRegistration", b =>
{
b.Property<int>("UserId")
.HasColumnType("integer");
b.Property<int>("EventId")
.HasColumnType("integer");
b.Property<DateTime>("RegisteredAt")
.HasColumnType("timestamp with time zone");
b.HasKey("UserId", "EventId");
b.HasIndex("EventId");
b.ToTable("EventRegistrations");
});
modelBuilder.Entity("WebApp.Entities.EventSkill", b =>
{
b.Property<int>("EventId")
.HasColumnType("integer");
b.Property<int>("SkillId")
.HasColumnType("integer");
b.HasKey("EventId", "SkillId");
b.HasIndex("SkillId");
b.ToTable("EventSkills");
});
modelBuilder.Entity("WebApp.Entities.Message", b =>
{
b.Property<int>("MessageId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("MessageId"));
b.Property<string>("Content")
.HasColumnType("text");
b.Property<int>("EventType")
.HasColumnType("integer");
b.Property<bool>("IsMsgFromVolunteer")
.HasColumnType("boolean");
b.Property<DateTime>("IsoDate")
.HasColumnType("timestamp with time zone");
b.Property<int>("OrganizationId")
.HasColumnType("integer");
b.Property<int>("VolunteerId")
.HasColumnType("integer");
b.HasKey("MessageId");
b.ToTable("Messages");
});
modelBuilder.Entity("WebApp.Entities.MessageActivity", b =>
{
b.Property<int>("Sender")
.HasColumnType("integer");
b.Property<int>("Recipient")
.HasColumnType("integer");
b.Property<DateTime>("RecipientLastActive")
.HasColumnType("timestamp with time zone");
b.HasKey("Sender", "Recipient");
b.ToTable("MessagesActivities");
});
modelBuilder.Entity("WebApp.Entities.Organisation", b =>
{
b.Property<int>("OrganisationId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("OrganisationId"));
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("UserId")
.HasColumnType("integer");
b.Property<string>("Website")
.HasColumnType("text");
b.HasKey("OrganisationId");
b.HasIndex("UserId");
b.ToTable("Organisations");
});
modelBuilder.Entity("WebApp.Entities.Skill", b =>
{
b.Property<int>("SkillId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SkillId"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("SkillId");
b.ToTable("Skills");
});
modelBuilder.Entity("WebApp.Entities.Token", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("TokenId"));
b.Property<int>("UserId")
.HasColumnType("integer");
b.Property<DateTime>("ValidUntil")
.HasColumnType("timestamp with time zone");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("TokenId");
b.HasIndex("UserId");
b.ToTable("Tokens");
});
modelBuilder.Entity("WebApp.Entities.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsOrganisation")
.HasColumnType("boolean");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Password")
.HasColumnType("text");
b.HasKey("UserId");
b.ToTable("WebUsers");
});
modelBuilder.Entity("WebApp.Entities.VolunteerSkill", b =>
{
b.Property<int>("UserId")
.HasColumnType("integer");
b.Property<int>("SkillId")
.HasColumnType("integer");
b.HasKey("UserId", "SkillId");
b.HasIndex("SkillId");
b.ToTable("VolunteerSkills");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", 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
}
}
}

View File

@@ -0,0 +1,97 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace WebApp.Migrations
{
/// <inheritdoc />
public partial class RenameEventRegistration : Migration
{
/// <inheritdoc />
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<int>(
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);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_EventRegistrations_WebUsers_UserId",
table: "EventRegistrations");
migrationBuilder.DropPrimaryKey(
name: "PK_EventRegistrations",
table: "EventRegistrations");
migrationBuilder.AlterColumn<int>(
name: "UserId",
table: "EventRegistrations",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AddColumn<int>(
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");
}
}
}

View File

@@ -252,7 +252,7 @@ namespace WebApp.Migrations
modelBuilder.Entity("WebApp.Entities.EventRegistration", b =>
{
b.Property<int>("VolunteerId")
b.Property<int>("UserId")
.HasColumnType("integer");
b.Property<int>("EventId")
@@ -261,15 +261,10 @@ namespace WebApp.Migrations
b.Property<DateTime>("RegisteredAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("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");

57
WebApp/ts/auth.ts Normal file
View File

@@ -0,0 +1,57 @@
// /js/auth.ts
function deleteCookie(name: string): void {
document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
}
async function logoutUser(): Promise<void> {
await fetch("/api/auth/logout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
deleteCookie('token');
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', (e) => {
e.preventDefault();
logoutUser();
});
}
}
// Initialize on load
document.addEventListener('DOMContentLoaded', setupAuthUI);

38
WebApp/ts/login.ts Normal file
View File

@@ -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);
}
});
});

View File

@@ -80,6 +80,8 @@
<script type="module" src="/js/eventCreate.js"></script>
<script type="module" src="/js/generalUseHelpers.js"></script>
<script type="module" src="/js/auth.js"></script>
</body>

View File

@@ -70,7 +70,7 @@
<h2 class="eventsText">Events</h2>
<span class="position-absolute end-0 translate-middle-y me-4" style="margin-top: 20px;">
<button class="btn btn-link" onclick="">
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#2898BD"><path d="M440-120v-240h80v80h320v80H520v80h-80Zm-320-80v-80h240v80H120Zm160-160v-80H120v-80h160v-80h80v240h-80Zm160-80v-80h400v80H440Zm160-160v-240h80v80h160v80H680v80h-80Zm-480-80v-80h400v80H120Z" /></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#2898BD"><path d="M440-120v-240h80v80h320v80H520v80h-80ZŁm-320-80v-80h240v80H120Zm160-160v-80H120v-80h160v-80h80v240h-80Zm160-80v-80h400v80H440Zm160-160v-240h80v80h160v80H680v80h-80Zm-480-80v-80h400v80H120Z" /></svg>
</button>
<button class="btn btn-link" id="list-sort-btn" onclick=""><svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#2898BD"><path d="M320-440v-287L217-624l-57-56 200-200 200 200-57 56-103-103v287h-80ZM600-80 400-280l57-56 103 103v-287h80v287l103-103 57 56L600-80Z" /></svg></button>
</span>
@@ -92,5 +92,6 @@
<a href="/create.html" class="button-add mt-xl-auto rounded-5 align-content-center center-text hidden-before-load" id="addnewevent-btn">
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg>
</a>
<script type="module" src="/js/auth.js"></script>
</body>
</html>

56
WebApp/wwwroot/js/auth.js Normal file
View File

@@ -0,0 +1,56 @@
"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() {
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';
}
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', (e) => {
e.preventDefault();
logoutUser();
});
}
}
// Initialize on load
document.addEventListener('DOMContentLoaded', setupAuthUI);

View File

@@ -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<EFBFBD>ytkownik niezalogowany!");
}
const data = yield res.json();
return data;

View File

@@ -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);
}
}));
});

17
WebApp/wwwroot/login.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Login</title>
<script src="/js/login.js" defer></script>
</head>
<body>
<h2>Login</h2>
<form id="loginForm">
<label>Email: <input type="email" id="email" required /></label><br /><br />
<label>Password: <input type="password" id="password" required /></label><br /><br />
<button type="submit">Login</button>
<p id="message" style="color: red;"></p>
</form>
</body>
</html>

View File

@@ -71,6 +71,8 @@
<script type="module" src="/js/eventView.js"></script>
<script type="module" src="/js/generalUseHelpers.js"></script>
<script type="module" src="/js/auth.js"></script>
</body>