Ignore:
Timestamp:
09/18/22 18:09:53 (2 years ago)
Author:
Danilo <danilo.najkov@…>
Branches:
master
Parents:
49b0bbd
Message:

vip functionallity + menu fields + alergens filtering + google/fb login + email queueing

Location:
resTools_backend/backend
Files:
7 added
19 edited

Legend:

Unmodified
Added
Removed
  • resTools_backend/backend/Controllers/MenuController.cs

    r49b0bbd r13f1472  
    3535        return Ok();
    3636    }
     37
     38    [HttpPost("{id}/upload")]
     39    public async Task<IActionResult> UploadImage(int id, [FromForm] IFormFile file)
     40    {
     41        await _menuService.UploadImage(id, file);
     42        return Ok();
     43    }
    3744}
  • resTools_backend/backend/Controllers/ReviewsController.cs

    r49b0bbd r13f1472  
    1212    {
    1313        private readonly IReviewService _reviewService = null;
     14        private readonly IUserService _userService = null;
    1415
    15         public ReviewsController(IReviewService reviewService)
     16        public ReviewsController(IReviewService reviewService, IUserService userService)
    1617        {
    1718            _reviewService = reviewService;
     19            _userService = userService;
    1820        }
    1921
     
    3941                userId = (int)this.HttpContext.Items["User"];
    4042            }
    41             catch (Exception ex) { return null; }
     43            catch (Exception ex) {
     44                var user = await _userService.GetByEmail((string)this.HttpContext.Items["User"]);
     45                if (user == null)
     46                {
     47                    return null;
     48                }
     49                userId = user.Id;
     50            }
    4251            await _reviewService.AddReview(req, userId);
    4352            return Ok();
  • resTools_backend/backend/Controllers/UsersController.cs

    r49b0bbd r13f1472  
    3939        {
    4040            userId = (int)this.HttpContext.Items["User"];
    41         }catch (Exception ex){ return null; }
     41        }
     42        catch (Exception ex)
     43        {
     44            var usr = await _userService.GetByEmail((string)this.HttpContext.Items["User"]);
     45            if(usr == null)
     46            {
     47                return null;
     48            }
     49            userId = usr.Id;
     50        }
    4251        User user = await _userService.GetById(userId);
    43         return new AuthenticateResponse() { Email=user.Email, Id = user.Id, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed};
     52        return new AuthenticateResponse() { Email=user.Email, Id = user.Id, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed, isVip = user.IsVip};
    4453    }
    4554
     
    93102        return response;
    94103    }
     104
     105    [Authorize]
     106    [HttpGet()]
     107    public async Task<List<UserResponse>> GetUsers()
     108    {
     109        return await _userService.GetUsers();
     110    }
     111
     112    [Authorize]
     113    [HttpPost("{id}/vip")]
     114    public async Task UpdateVip(int id, bool newStatus)
     115    {
     116        await _userService.UpdateVipStatus(id,newStatus);
     117    }
    95118}
  • resTools_backend/backend/DTOs/AuthenticateResponse.cs

    r49b0bbd r13f1472  
    1717    [JsonProperty]
    1818    public bool IsConfirmed { get; set; }
     19    [JsonProperty]
     20    public bool isVip { get; set; }
    1921}
  • resTools_backend/backend/DTOs/CreateMenuItemRequest.cs

    r49b0bbd r13f1472  
    1111        [JsonProperty]
    1212        public int Price { get; set; }
     13        [JsonProperty]
     14        public bool IsVipOnly { get; set; }
     15        [JsonProperty]
     16        public string Alergens { get; set; }
    1317    }
    1418}
  • resTools_backend/backend/DTOs/CreateUserRequest.cs

    r49b0bbd r13f1472  
    1010        [JsonProperty]
    1111        public string Password { get; set; }
     12
     13        [JsonProperty]
     14        public bool IsConfirmed { get; set; }
    1215    }
    1316}
  • resTools_backend/backend/DTOs/MenuItemResponse.cs

    r49b0bbd r13f1472  
    1313        [JsonProperty]
    1414        public int Price { get; set; }
     15        [JsonProperty]
     16        public bool IsVipOnly { get; set; }
     17        [JsonProperty]
     18        public string Alergens { get; set; }
     19        [JsonProperty]
     20        public string Image { get; set; }
    1521    }
    1622}
  • resTools_backend/backend/Data/DataContext.cs

    r49b0bbd r13f1472  
    9494        }
    9595
     96        private DbSet<QueueItem> queueItems;
     97        public DbSet<QueueItem> QueueItems
     98        {
     99            get
     100            {
     101                if (queueItems == null)
     102                {
     103                    queueItems = Set<QueueItem>();
     104                }
     105
     106                return queueItems;
     107            }
     108        }
     109
    96110
    97111        protected override void OnModelCreating(ModelBuilder modelBuilder)
     
    152166            modelBuilder.Entity<ToDoItem>()
    153167            .HasOne(p => p.LinkedReview);
     168
     169            modelBuilder.Entity<QueueItem>().Property(x => x.Id).IsRequired().ValueGeneratedOnAdd();
    154170        }
    155171    }
  • resTools_backend/backend/Email/EmailSender.cs

    r49b0bbd r13f1472  
    1 using SendGrid;
     1using backend.Data;
     2using backend.Entities;
     3using SendGrid;
    24using SendGrid.Helpers.Mail;
    35
     
    911public class EmailSender : IEmailSender
    1012{
     13
     14    private readonly DataContext _context = null;
     15    public EmailSender(DataContext context)
     16    {
     17        _context = context;
     18    }
     19
    1120    public async Task SendEmailAsync(string subject, string message, string toEmail)
    1221    {
    13         var client = new SendGridClient("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");
    14         var msg = new SendGridMessage()
     22
     23        _context.QueueItems.Add(new QueueItem()
    1524        {
    16             From = new EmailAddress("danilo.najkov@students.finki.ukim.mk", "Danilo"),
    1725            Subject = subject,
    18             PlainTextContent = message,
    19             HtmlContent = message
    20         };
    21         msg.AddTo(new EmailAddress(toEmail));
    22         msg.SetClickTracking(false, false);
    23         var response = await client.SendEmailAsync(msg);
     26            Message = message,
     27            Reciptient = toEmail,
     28            CreatedAt = DateTime.UtcNow,
     29            Retries = 0
     30        });
     31        await _context.SaveChangesAsync();
    2432    }
    2533}
  • resTools_backend/backend/Entities/MenuItem.cs

    r49b0bbd r13f1472  
    77        public string Description { get; set; }
    88        public int Price { get; set; }
     9        public byte[] Image { get; set; }
     10        public bool IsVipOnly { get; set; }
     11        public string Alergens { get; set; }
    912        public virtual Restaurant Restaurant { get; set; }
    1013    }
  • resTools_backend/backend/Entities/User.cs

    r49b0bbd r13f1472  
    1010    public bool IsAdmin { get; set; }
    1111    public bool IsConfirmed { get; set; }
     12    public bool IsVip { get; set; }
    1213    public string? ConfirmationURL { get; set; }
    1314    public DateTime? ConfirmationValidTo { get; set; }
  • resTools_backend/backend/Helpers/JwtMiddleware.cs

    r49b0bbd r13f1472  
    66using backend.Services;
    77using backend.Helpers;
     8using Google.Apis.Auth;
     9using System.Text.Json;
     10using Newtonsoft.Json;
    811
    912public class JwtMiddleware
     
    1114    private readonly RequestDelegate _next;
    1215    private readonly AppSettings _appSettings;
     16    private static readonly HttpClient client = new HttpClient();
     17
    1318
    1419    public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
     
    2227        var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
    2328
    24         if (token != null)
    25             attachUserToContext(context, userService, token);
     29        if (token != null && token != "null")
     30            await attachUserToContext(context, userService, token);
    2631
    2732        await _next(context);
    2833    }
    2934
    30     private void attachUserToContext(HttpContext context, IUserService userService, string token)
     35    private async Task attachUserToContext(HttpContext context, IUserService userService, string token)
    3136    {
    3237        try
    3338        {
     39            var fbResult = await this.ValidateFacebookToken(token);
     40            if (fbResult != null)
     41            {
     42                context.Items["User"] = fbResult;
     43                return;
     44            }
     45
    3446            var tokenHandler = new JwtSecurityTokenHandler();
    3547            var key = System.Text.Encoding.ASCII.GetBytes(_appSettings.Secret);
     
    4759
    4860            context.Items["User"] = userId;
     61
    4962        }
    5063        catch
    5164        {
    52             // do nothing if jwt validation fails
     65            try
     66            {
     67                var result = await GoogleJsonWebSignature.ValidateAsync(token);
     68                context.Items["User"] = result.Email;
     69            }
     70            catch
     71            {
     72                // do nothing
     73            }
    5374        }
    5475    }
     76
     77    private async Task<string> ValidateFacebookToken(string token)
     78    {
     79        try
     80        {
     81            var stringTask = await client.GetStringAsync("https://graph.facebook.com/me?fields=email&access_token=" + token);
     82            var obj = JsonConvert.DeserializeObject<FacebookResult>(stringTask);
     83            return obj.email;
     84        }
     85        catch
     86        {
     87            return null;
     88        }
     89    }
     90
     91    internal class FacebookResult
     92    {
     93        public string email { get; set; }
     94        public string id { get; set; }
     95    }
    5596}
  • resTools_backend/backend/Migrations/DataContextModelSnapshot.cs

    r49b0bbd r13f1472  
    3131                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
    3232
     33                    b.Property<string>("Alergens")
     34                        .IsRequired()
     35                        .HasColumnType("text");
     36
    3337                    b.Property<string>("Description")
    3438                        .IsRequired()
    3539                        .HasColumnType("text");
    3640
     41                    b.Property<byte[]>("Image")
     42                        .IsRequired()
     43                        .HasColumnType("bytea");
     44
     45                    b.Property<bool>("IsVipOnly")
     46                        .HasColumnType("boolean");
     47
    3748                    b.Property<int>("Price")
    3849                        .HasColumnType("integer");
     
    5061
    5162                    b.ToTable("MenuItems");
     63                });
     64
     65            modelBuilder.Entity("backend.Entities.QueueItem", b =>
     66                {
     67                    b.Property<int>("Id")
     68                        .ValueGeneratedOnAdd()
     69                        .HasColumnType("integer");
     70
     71                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
     72
     73                    b.Property<DateTime>("CreatedAt")
     74                        .HasColumnType("timestamp with time zone");
     75
     76                    b.Property<string>("Message")
     77                        .IsRequired()
     78                        .HasColumnType("text");
     79
     80                    b.Property<string>("Reciptient")
     81                        .IsRequired()
     82                        .HasColumnType("text");
     83
     84                    b.Property<int>("Retries")
     85                        .HasColumnType("integer");
     86
     87                    b.Property<string>("Subject")
     88                        .IsRequired()
     89                        .HasColumnType("text");
     90
     91                    b.HasKey("Id");
     92
     93                    b.ToTable("QueueItems");
    5294                });
    5395
     
    225267
    226268                    b.Property<bool>("IsConfirmed")
     269                        .HasColumnType("boolean");
     270
     271                    b.Property<bool>("IsVip")
    227272                        .HasColumnType("boolean");
    228273
  • resTools_backend/backend/Program.cs

    r49b0bbd r13f1472  
    22using backend.Email;
    33using backend.Helpers;
     4using backend.Jobs;
    45using backend.Services;
    56using Microsoft.EntityFrameworkCore;
    67using Microsoft.OpenApi.Models;
     8using Quartz;
    79using WebApi.Helpers;
    810
     
    5658builder.Services.AddDbContext<DataContext>(p => p.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
    5759
     60builder.Services.AddQuartz(q =>
     61{
     62    q.UseMicrosoftDependencyInjectionScopedJobFactory();
     63    var jobKey = new JobKey("QueueJob");
     64    q.AddJob<QueueJob>(opts => opts.WithIdentity(jobKey));
     65
     66    q.AddTrigger(opts => opts
     67        .ForJob(jobKey)
     68        .WithIdentity("QueueJob-trigger")
     69        .WithCronSchedule("0 0/1 * * * ?"));
     70
     71});
     72
     73IServiceCollection serviceCollection = builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
     74
    5875var app = builder.Build();
    5976
  • resTools_backend/backend/Services/MenuService.cs

    r49b0bbd r13f1472  
    1010        public Task AddMenu(CreateMenuItemRequest menu);
    1111        public Task RemoveMenu(int id);
     12        public Task UploadImage(int id, IFormFile file);
    1213    }
    1314    public class MenuService : IMenuService
     
    2930                Title = menu.Title,
    3031                Description = menu.Description,
    31                 Price = menu.Price
    32             });
     32                Price = menu.Price,
     33                Alergens = menu.Alergens,
     34                IsVipOnly = menu.IsVipOnly,
     35                Image = Array.Empty<byte>()
     36            }) ;
    3337            _context.Restoraunts.Update(res);
    3438            await _context.SaveChangesAsync();
     
    4246            await _context.SaveChangesAsync();
    4347        }
     48
     49        public async Task UploadImage(int id, IFormFile file)
     50        {
     51            using (var memoryStream = new MemoryStream())
     52            {
     53                await file.CopyToAsync(memoryStream);
     54                var menuItem = await _context.MenuItems.FindAsync(id);
     55                menuItem.Image = memoryStream.ToArray();
     56                _context.MenuItems.Update(menuItem);
     57                _context.SaveChanges();
     58            }
     59        }
    4460    }
    4561}
  • resTools_backend/backend/Services/RestaurantService.cs

    r49b0bbd r13f1472  
    4545                        Title = x.Title,
    4646                        Description = x.Description,
    47                         Price = x.Price
     47                        Price = x.Price,
     48                        Alergens = x.Alergens,
     49                        Image = String.Format("data:image/png;base64,{0}", Convert.ToBase64String(x.Image)),
     50                        IsVipOnly = x.IsVipOnly
    4851                    }).ToList(),
    4952                    Reviews = x.Reviews.Select(x => new ReviewResponse()
     
    5558                        Title = x.Title,
    5659                        Username = x.User == null ? "Anonymous" : x.User.Email
    57                     }).ToList(),
    58                     AverageReview = x.Reviews.Count>0 ? x.Reviews.Select(x => x.Stars).Average() : 0
     60                    }).ToList()
    5961                })
    6062                .FirstOrDefaultAsync();
     63            var reviews = await _context.Reviews.ToListAsync();
     64            res.AverageReview = reviews.Select(x => x.Stars).Sum();
    6165            return res;
    6266        }
  • resTools_backend/backend/Services/ReviewService.cs

    r49b0bbd r13f1472  
    11using backend.Data;
    22using backend.DTOs;
     3using backend.Email;
    34using backend.Entities;
    45using Microsoft.EntityFrameworkCore;
     
    1516    {
    1617        private readonly DataContext _context = null;
     18        private readonly IEmailSender _emailSender;
    1719
    18         public ReviewService(DataContext context)
     20        public ReviewService(DataContext context, IEmailSender emailSender)
    1921        {
    2022            _context = context;
     23            _emailSender = emailSender;
     24
    2125        }
    2226
     
    3943            _context.Restoraunts.Update(res);
    4044            await _context.SaveChangesAsync();
     45
     46            if (review.Stars < 3)
     47            {
     48                var adminUser = await _context.Users.FirstOrDefaultAsync(x => x.IsAdmin);
     49                await _emailSender.SendEmailAsync("Добивте лоша оценка", $"<html><h1>{review.Title}</h1><p>{review.Description}</p></html>", adminUser.Email);
     50            }
    4151        }
    4252
  • resTools_backend/backend/Services/UserService.cs

    r49b0bbd r13f1472  
    2020    Task<AuthenticateResponse> Register(CreateUserRequest req, bool isFirst);
    2121    Task<User> GetById(int id);
     22    Task<User> GetByEmail(string email);
    2223    Task SendEmailConfirmation(string email);
    2324    Task SendPasswordReset(string email);
    2425    Task ConfirmEmail(User user, string checkValid);
    2526    Task ResetPassword(string checkValid, string password);
     27    Task<List<UserResponse>> GetUsers();
     28    Task UpdateVipStatus(int id, bool isVip);
     29
    2630}
    2731
     
    3943    }
    4044
     45    public async Task<User> GetByEmail(string email)
     46    {
     47        return await _context.Users.FirstOrDefaultAsync(x => x.Email == email);
     48    }
     49
    4150    public async Task<AuthenticateResponse> Authenticate(AuthenticateRequest model)
    4251    {
     
    4958        var token = generateJwtToken(user);
    5059
    51         return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed};
     60        return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed, isVip = user.IsVip};
    5261    }
    5362
     
    7584    public async Task<AuthenticateResponse> Register(CreateUserRequest req, bool isFirst)
    7685    {
    77         User user = new User() { Email = req.Email, Password = req.Password, IsAdmin = isFirst, IsConfirmed = false };
     86        var exists = await _context.Users.FirstOrDefaultAsync(x => x.Email == req.Email);
     87        if(exists != null && req.IsConfirmed)
     88        {
     89            return new AuthenticateResponse { Email = exists.Email, Id = exists.Id, IsAdmin = exists.IsAdmin, IsConfirmed = true };
     90        }
     91        User user = new User() { Email = req.Email, Password = req.Password, IsAdmin = isFirst, IsConfirmed = req.IsConfirmed, IsVip = false };
    7892        await _context.Users.AddAsync(user);
    7993        await _context.SaveChangesAsync();
    8094        var token = generateJwtToken(user);
    81         return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = false };
     95        return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = req.IsConfirmed, isVip = user.IsVip };
    8296    }
    8397
     
    125139    }
    126140
     141    public async Task<List<UserResponse>> GetUsers()
     142    {
     143        return await _context.Users.Select(x => new UserResponse()
     144        {
     145            Email = x.Email,
     146            Id = x.Id,
     147            IsVip = x.IsVip
     148        }).OrderBy(x => x.Id).ToListAsync();
     149    }
     150
     151    public async Task UpdateVipStatus(int id, bool isVip)
     152    {
     153        var user = await _context.Users.FindAsync(id);
     154        user.IsVip = isVip;
     155        _context.Users.Update(user);
     156        await _context.SaveChangesAsync();
     157    }
     158
    127159    private string generateJwtToken(User user)
    128160    {
  • resTools_backend/backend/backend.csproj

    r49b0bbd r13f1472  
    1212
    1313  <ItemGroup>
     14    <PackageReference Include="Google.Apis.Auth" Version="1.57.0" />
     15    <PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="6.0.9" />
    1416    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" />
    1517    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.3" />
     
    2224    <PackageReference Include="Npgsql" Version="6.0.3" />
    2325    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3" />
     26    <PackageReference Include="Quartz" Version="3.4.0" />
     27    <PackageReference Include="Quartz.Extensions.Hosting" Version="3.4.0" />
    2428    <PackageReference Include="SendGrid" Version="9.28.0" />
    2529    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
Note: See TracChangeset for help on using the changeset viewer.