Add project files.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
using CalendarReminder.Api.Data;
|
||||
using CalendarReminder.Api.Models;
|
||||
using CalendarReminder.Api.Services;
|
||||
using CalendarReminder.Domain.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CalendarReminder.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly TokenService _tokenService;
|
||||
|
||||
public AuthController(AppDbContext db, TokenService tokenService)
|
||||
{
|
||||
_db = db;
|
||||
_tokenService = tokenService;
|
||||
}
|
||||
|
||||
// POST api/auth/register
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register(RegisterRequest req)
|
||||
{
|
||||
if (await _db.Users.AnyAsync(u => u.Email == req.Email))
|
||||
return BadRequest("Email je už registrovaný.");
|
||||
|
||||
if (await _db.Users.AnyAsync(u => u.Username == req.Username))
|
||||
return BadRequest("Používateľské meno je obsadené.");
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Username = req.Username,
|
||||
Email = req.Email,
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.Password)
|
||||
};
|
||||
|
||||
_db.Users.Add(user);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
var token = _tokenService.GenerateToken(user);
|
||||
return Ok(new AuthResponse(token, user.Username, user.Email));
|
||||
}
|
||||
|
||||
// POST api/auth/login
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login(LoginRequest req)
|
||||
{
|
||||
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == req.Email);
|
||||
if (user is null || !BCrypt.Net.BCrypt.Verify(req.Password, user.PasswordHash))
|
||||
return Unauthorized("Nesprávny email alebo heslo.");
|
||||
|
||||
var token = _tokenService.GenerateToken(user);
|
||||
return Ok(new AuthResponse(token, user.Username, user.Email));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
using CalendarReminder.Api.Data;
|
||||
using CalendarReminder.Api.Services;
|
||||
using CalendarReminder.Domain.Entities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace CalendarReminder.Api.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class RemindersController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly EmailService _email;
|
||||
|
||||
public RemindersController(AppDbContext db, EmailService email)
|
||||
{
|
||||
_db = db;
|
||||
_email = email;
|
||||
}
|
||||
|
||||
private int UserId => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
||||
|
||||
// GET api/reminders — moje + zdieľané so mnou
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll()
|
||||
{
|
||||
var myId = UserId;
|
||||
|
||||
var reminders = await _db.Reminders
|
||||
.Include(r => r.User)
|
||||
.Include(r => r.Shares).ThenInclude(s => s.SharedWithUser)
|
||||
.Where(r => r.UserId == myId || r.Shares.Any(s => s.SharedWithUserId == myId))
|
||||
.OrderBy(r => r.ReminderDate)
|
||||
.Select(r => new
|
||||
{
|
||||
r.Id, r.Title, r.Description, r.ReminderDate,
|
||||
r.IsCompleted, r.NotificationSent, r.UserId, r.CreatedAt,
|
||||
Owner = r.User!.Username,
|
||||
IsOwner = r.UserId == myId,
|
||||
SharedWith = r.Shares.Select(s => new { s.SharedWithUser!.Id, s.SharedWithUser.Username })
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(reminders);
|
||||
}
|
||||
|
||||
// GET api/reminders/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var myId = UserId;
|
||||
var r = await _db.Reminders
|
||||
.Include(r => r.Shares)
|
||||
.FirstOrDefaultAsync(r => r.Id == id &&
|
||||
(r.UserId == myId || r.Shares.Any(s => s.SharedWithUserId == myId)));
|
||||
return r is null ? NotFound() : Ok(r);
|
||||
}
|
||||
|
||||
// POST api/reminders
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] CreateReminderRequest req)
|
||||
{
|
||||
var reminder = new Reminder
|
||||
{
|
||||
Title = req.Title,
|
||||
Description = req.Description,
|
||||
ReminderDate = req.ReminderDate,
|
||||
IsCompleted = false,
|
||||
NotificationSent = false,
|
||||
UserId = UserId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
_db.Reminders.Add(reminder);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
// Zdieľaj s vybranými používateľmi
|
||||
foreach (var uid in req.ShareWithUserIds ?? [])
|
||||
{
|
||||
_db.ReminderShares.Add(new ReminderShare { ReminderId = reminder.Id, SharedWithUserId = uid });
|
||||
}
|
||||
if (req.ShareWithUserIds?.Count > 0)
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetById), new { id = reminder.Id }, reminder);
|
||||
}
|
||||
|
||||
// PUT api/reminders/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] UpdateReminderRequest req)
|
||||
{
|
||||
var reminder = await _db.Reminders
|
||||
.Include(r => r.Shares)
|
||||
.FirstOrDefaultAsync(r => r.Id == id && r.UserId == UserId);
|
||||
if (reminder is null) return NotFound();
|
||||
|
||||
bool dateChanged = reminder.ReminderDate != req.ReminderDate;
|
||||
reminder.Title = req.Title;
|
||||
reminder.Description = req.Description;
|
||||
reminder.ReminderDate = req.ReminderDate;
|
||||
reminder.IsCompleted = req.IsCompleted;
|
||||
if (dateChanged) reminder.NotificationSent = false;
|
||||
|
||||
// Aktualizuj zdieľanie
|
||||
_db.ReminderShares.RemoveRange(reminder.Shares);
|
||||
foreach (var uid in req.ShareWithUserIds ?? [])
|
||||
_db.ReminderShares.Add(new ReminderShare { ReminderId = id, SharedWithUserId = uid });
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(reminder);
|
||||
}
|
||||
|
||||
// DELETE api/reminders/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var reminder = await _db.Reminders
|
||||
.FirstOrDefaultAsync(r => r.Id == id && r.UserId == UserId);
|
||||
if (reminder is null) return NotFound();
|
||||
_db.Reminders.Remove(reminder);
|
||||
await _db.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST api/reminders/5/send-email
|
||||
[HttpPost("{id}/send-email")]
|
||||
public async Task<IActionResult> SendEmail(int id)
|
||||
{
|
||||
var myId = UserId;
|
||||
var reminder = await _db.Reminders
|
||||
.Include(r => r.User)
|
||||
.Include(r => r.Shares).ThenInclude(s => s.SharedWithUser)
|
||||
.FirstOrDefaultAsync(r => r.Id == id && r.UserId == myId);
|
||||
|
||||
if (reminder is null) return NotFound();
|
||||
if (!reminder.Shares.Any()) return BadRequest("Pripomienka nie je zdieľaná so žiadnym používateľom.");
|
||||
|
||||
var date = reminder.ReminderDate.ToLocalTime();
|
||||
var dateStr = date.ToString("dd.MM.yyyy");
|
||||
var timeStr = date.ToString("HH:mm");
|
||||
|
||||
var body = $"""
|
||||
<div style="font-family:Segoe UI,sans-serif;max-width:520px;margin:auto;background:#161b22;color:#e1e4e8;border-radius:10px;overflow:hidden">
|
||||
<div style="background:#1f6feb;padding:1.2rem 1.5rem">
|
||||
<h2 style="margin:0;color:#fff;font-size:1.1rem">📅 Calendar Reminder</h2>
|
||||
</div>
|
||||
<div style="padding:1.5rem">
|
||||
<h3 style="margin:0 0 1rem;color:#58a6ff;font-size:1.2rem">{reminder.Title}</h3>
|
||||
<table style="width:100%;border-collapse:collapse">
|
||||
<tr>
|
||||
<td style="padding:0.4rem 0;color:#8b949e;width:80px">Dátum</td>
|
||||
<td style="padding:0.4rem 0;font-weight:600">{dateStr}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0.4rem 0;color:#8b949e">Čas</td>
|
||||
<td style="padding:0.4rem 0;font-weight:600">{timeStr}</td>
|
||||
</tr>
|
||||
{(reminder.Description != null ? $"""
|
||||
<tr>
|
||||
<td style="padding:0.4rem 0;color:#8b949e;vertical-align:top">Popis</td>
|
||||
<td style="padding:0.4rem 0">{reminder.Description}</td>
|
||||
</tr>
|
||||
""" : "")}
|
||||
<tr>
|
||||
<td style="padding:0.4rem 0;color:#8b949e">Od</td>
|
||||
<td style="padding:0.4rem 0">{reminder.User!.Username}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="padding:0.75rem 1.5rem;border-top:1px solid #30363d;font-size:0.78rem;color:#6e7681">
|
||||
Calendar Reminder — automatická správa
|
||||
</div>
|
||||
</div>
|
||||
""";
|
||||
|
||||
var tasks = reminder.Shares
|
||||
.Where(s => s.SharedWithUser != null)
|
||||
.Select(s => _email.SendAsync(
|
||||
s.SharedWithUser!.Email,
|
||||
s.SharedWithUser.Username,
|
||||
$"📅 Pripomienka: {reminder.Title} — {dateStr} {timeStr}",
|
||||
body));
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
return Ok(new { sent = reminder.Shares.Count });
|
||||
}
|
||||
}
|
||||
|
||||
public record CreateReminderRequest(
|
||||
string Title,
|
||||
string? Description,
|
||||
DateTime ReminderDate,
|
||||
List<int>? ShareWithUserIds);
|
||||
|
||||
public record UpdateReminderRequest(
|
||||
string Title,
|
||||
string? Description,
|
||||
DateTime ReminderDate,
|
||||
bool IsCompleted,
|
||||
List<int>? ShareWithUserIds);
|
||||
@@ -0,0 +1,34 @@
|
||||
using CalendarReminder.Api.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CalendarReminder.Api.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public UsersController(AppDbContext db) => _db = db;
|
||||
|
||||
// GET api/users?search=jan
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Search([FromQuery] string? search)
|
||||
{
|
||||
var myId = int.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)!.Value);
|
||||
|
||||
var query = _db.Users.Where(u => u.Id != myId);
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
query = query.Where(u => u.Username.Contains(search) || u.Email.Contains(search));
|
||||
|
||||
var users = await query
|
||||
.Select(u => new { u.Id, u.Username, u.Email })
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(users);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user