diff options
Diffstat (limited to 'Backend')
-rw-r--r-- | Backend/Api/Api/Api.csproj | 1 | ||||
-rw-r--r-- | Backend/Api/Api/Controllers/PostController.cs | 53 | ||||
-rw-r--r-- | Backend/Api/Api/Interfaces/IPostService.cs | 9 | ||||
-rw-r--r-- | Backend/Api/Api/Models/Post.cs | 36 | ||||
-rw-r--r-- | Backend/Api/Api/Program.cs | 14 | ||||
-rw-r--r-- | Backend/Api/Api/Services/PostService.cs | 182 |
6 files changed, 278 insertions, 17 deletions
diff --git a/Backend/Api/Api/Api.csproj b/Backend/Api/Api/Api.csproj index 80898fd..24c41b7 100644 --- a/Backend/Api/Api/Api.csproj +++ b/Backend/Api/Api/Api.csproj @@ -13,6 +13,7 @@ <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.10" /> <PackageReference Include="MongoDB.Driver" Version="2.18.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> + <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.5" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.24.0" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="MailKit" Version="3.4.2" /> diff --git a/Backend/Api/Api/Controllers/PostController.cs b/Backend/Api/Api/Controllers/PostController.cs index a61ee2e..27823bc 100644 --- a/Backend/Api/Api/Controllers/PostController.cs +++ b/Backend/Api/Api/Controllers/PostController.cs @@ -14,10 +14,12 @@ namespace Api.Controllers { private readonly IPostService _postService; private readonly IFileService _fileService; - public PostController(IPostService postService, IFileService fileService) + private readonly IUserService _userService; + public PostController(IPostService postService, IFileService fileService,IUserService userService) { _postService = postService; _fileService = fileService; + _userService = userService; } [HttpPost("add")] @@ -46,7 +48,8 @@ namespace Api.Controllers [Authorize(Roles = "User")] public async Task<ActionResult<PostSend>> getPostByid(string id) { - var res = await _postService.getPostById(id); + var userid = await _userService.UserIdFromJwt(); + var res = await _postService.getPostById(id,userid); if (res != null) { return Ok(res); @@ -62,10 +65,56 @@ namespace Api.Controllers if (f == null || !System.IO.File.Exists(f.path)) return BadRequest("Slika ne postoji"); return File(System.IO.File.ReadAllBytes(f.path), "image/*", Path.GetFileName(f.path)); + } + [HttpPost("posts/{id}/addrating")] + [Authorize(Roles = "User")] + public async Task<ActionResult> addRating([FromBody] RatingReceive rating,string id) + { + var userid = await _userService.UserIdFromJwt(); + if (await _postService.AddOrReplaceRating(rating, userid)) + return Ok(); + return BadRequest(); + } + [HttpDelete("posts/{id}/removerating")] + [Authorize(Roles = "User")] + public async Task<ActionResult> removeRating(string id) + { + var userid = await _userService.UserIdFromJwt(); + if (await _postService.RemoveRating(id,userid)) + return Ok(); + return BadRequest(); } + [HttpPost("posts/{id}/addcomment")] + [Authorize(Roles = "User")] + public async Task<ActionResult> addComment([FromBody] CommentReceive cmnt,string id) + { + var userid = await _userService.UserIdFromJwt(); + if (await _postService.AddComment(cmnt,userid,id)) + return Ok(); + return BadRequest(); + } + + [HttpGet("posts/{id}/listcomments")] + [Authorize(Roles = "User")] + public async Task<ActionResult<List<CommentSend>>> listComments(string id) + { + var ret = await _postService.ListComments(id); + if(ret != null) + return Ok(ret); + return BadRequest(); + } + [HttpDelete("posts/{id}/removecomment/{cmntid}")] + [Authorize(Roles = "User")] + public async Task<ActionResult> removeRating(string id,string cmntid) + { + var userid = await _userService.UserIdFromJwt(); + if (await _postService.DeleteComments(id,cmntid,userid)) + return Ok(); + return BadRequest(); + } } } diff --git a/Backend/Api/Api/Interfaces/IPostService.cs b/Backend/Api/Api/Interfaces/IPostService.cs index 29a824a..daeee92 100644 --- a/Backend/Api/Api/Interfaces/IPostService.cs +++ b/Backend/Api/Api/Interfaces/IPostService.cs @@ -6,7 +6,14 @@ namespace Api.Interfaces { Task<PostSend> addPost(PostReceive post); Task<List<PostSend>> getAllPosts(); - Task<PostSend> getPostById(string id); + Task<PostSend> getPostById(string id,string userid); Task<PostSend> postToPostSend(Post post); + Task<Boolean> AddOrReplaceRating(RatingReceive rating, string userid); + Task<Boolean> RemoveRating(string postid, string userid); + Task<Boolean> AddComment(CommentReceive cmnt, string userid, string postid); + Task<List<CommentSend>> ListComments(string postid); + Task<List<CommentSend>> CascadeComments(string parentid, Post p); + Task<Boolean> DeleteComments(string postid, string cmntid,string userid); + Task CascadeDeleteComments(string cmntid, Post p); } }
\ No newline at end of file diff --git a/Backend/Api/Api/Models/Post.cs b/Backend/Api/Api/Models/Post.cs index ee84e0f..c832d23 100644 --- a/Backend/Api/Api/Models/Post.cs +++ b/Backend/Api/Api/Models/Post.cs @@ -20,9 +20,7 @@ namespace Api.Models } public class PostReceive { - [BsonId] - [BsonRepresentation(BsonType.ObjectId)] - public string _id { get; set; } + public string? _id { get; set; } public string locationId { get; set; } public string description { get; set; } public List<IFormFile> images { get; set; } @@ -31,15 +29,13 @@ namespace Api.Models } public class PostSend { - [BsonId] - [BsonRepresentation(BsonType.ObjectId)] public string _id { get; set; } public string ownerId { get; set; } public Location location { get; set; } public string description { get; set; } public int views { get; set; } - public float ratings { get; set; } - public List<Comment> comments { get; set; } + public double ratings { get; set; } + public List<CommentSend> comments { get; set; } public List<File> images { get; set; } } public class Rating @@ -49,9 +45,33 @@ namespace Api.Models } public class Comment { + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + public string _id { get; set; } + public string userId { get; set; } + public string comment { get; set; } + public string parentId { get; set; } + public DateTime timestamp { get; set; } + } + + public class RatingReceive + { + public int rating { get; set; } + public string postId { get; set; } + } + public class CommentSend + { + public string _id { get; set; } public string userId { get; set; } public string comment { get; set; } - public Comment parent { get; set; } + public string? parentId { get; set; } public DateTime timestamp { get; set; } + public string username { get; set; } + public List<CommentSend> replies { get; set; } + } + public class CommentReceive + { + public string comment { get; set; } + public string parentId { get; set; } } } diff --git a/Backend/Api/Api/Program.cs b/Backend/Api/Api/Program.cs index 16b0241..7d6b03e 100644 --- a/Backend/Api/Api/Program.cs +++ b/Backend/Api/Api/Program.cs @@ -4,7 +4,9 @@ using Api.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; using MongoDB.Driver; +using Swashbuckle.AspNetCore.Filters; using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -50,7 +52,17 @@ builder.Services.AddAuthentication( builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(options => { + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Description = "Standard Authorization header using the Bearer scheme (\"bearer {token}\")", + In = ParameterLocation.Header, + Name = "Authorization", + Type = SecuritySchemeType.ApiKey + }); + + options.OperationFilter<SecurityRequirementsOperationFilter>(); +}); builder.Services.AddCors(options => { diff --git a/Backend/Api/Api/Services/PostService.cs b/Backend/Api/Api/Services/PostService.cs index e9a56d2..78167bd 100644 --- a/Backend/Api/Api/Services/PostService.cs +++ b/Backend/Api/Api/Services/PostService.cs @@ -2,6 +2,8 @@ using Api.Interfaces; using Api.Models; using MongoDB.Driver; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson; namespace Api.Services { @@ -12,10 +14,12 @@ namespace Api.Services private readonly IHttpContextAccessor _httpContext; private readonly IFileService _fileService; private readonly ILocationService _locationService; + private readonly IMongoCollection<User> _users; public PostService(IDatabaseConnection settings, IMongoClient mongoClient, IHttpContextAccessor httpContext, IFileService fileService,ILocationService locationService) { var database = mongoClient.GetDatabase(settings.DatabaseName); _posts = database.GetCollection<Post>(settings.PostCollectionName); + _users = database.GetCollection<User>(settings.UserCollectionName); _httpContext = httpContext; _fileService = fileService; _locationService = locationService; @@ -25,7 +29,7 @@ namespace Api.Services { Post p = new Post(); p._id = ""; - p.ownerId = _httpContext.HttpContext.User.FindFirstValue("id").ToString(); + p.ownerId = _httpContext.HttpContext.User.FindFirstValue("id"); p.locationId = post.locationId; p.description = post.description; @@ -77,8 +81,16 @@ namespace Api.Services p.description = post.description; p.location = await _locationService.getById(post.locationId); p.images = post.images; - p.views = 1;//Default values todo - p.ratings = 1; + p.views = post.views.Count(); + if (post.ratings.Count() > 0) + { + List<int> ratings = new List<int>(); + foreach (var r in post.ratings) + ratings.Add(r.rating); + p.ratings = ratings.Average(); + } + else + p.ratings = 0; p.comments = null; @@ -96,12 +108,172 @@ namespace Api.Services return temp; } - public async Task<PostSend> getPostById(string id) + public async Task<PostSend> getPostById(string id,string userid) { Post p = await _posts.Find(post => post._id == id).FirstOrDefaultAsync(); + if (p != null) + { + if (!p.views.Any(x => x == userid)) + { + p.views.Add(userid); + await _posts.ReplaceOneAsync(x => x._id == id, p); + } + } return await postToPostSend(p); + } + + public async Task<Boolean> AddOrReplaceRating(RatingReceive rating,string userid) + { + Post p = await _posts.Find(post => post._id == rating.postId).FirstOrDefaultAsync(); + if (p != null) + { + if (p.ownerId == userid) + return false; + if(!p.ratings.Any(x => x.userId == userid)) + { + Rating r = new Rating(); + r.rating = rating.rating; + r.userId = userid; + p.ratings.Add(r); + await _posts.ReplaceOneAsync(x => x._id == p._id, p); + } + else + { + var r = p.ratings.Find(x => x.userId == userid); + p.ratings.Remove(r); + r.rating = rating.rating; + p.ratings.Add(r); + await _posts.ReplaceOneAsync(x => x._id == p._id, p); + } + return true; + } + return false; + } + public async Task<Boolean> RemoveRating(string postid, string userid) + { + Post p = await _posts.Find(post => post._id == postid).FirstOrDefaultAsync(); + if (p != null) + { + if (p.ratings.Any(x => x.userId == userid)) + { + var r = p.ratings.Find(x => x.userId == userid); + p.ratings.Remove(r); + await _posts.ReplaceOneAsync(x => x._id == postid, p); + return true; + } + } + return false; + } + public async Task<Boolean> AddComment(CommentReceive cmnt,string userid,string postid) + { + Post p = await _posts.Find(post => post._id == postid).FirstOrDefaultAsync(); + if (p != null) + { + Comment c= new Comment(); + c.parentId = cmnt.parentId; + c.userId = userid; + c.comment = cmnt.comment; + c.timestamp = DateTime.Now.ToUniversalTime(); + c._id = ObjectId.GenerateNewId().ToString(); + p.comments.Add(c); + await _posts.ReplaceOneAsync(x => x._id == postid, p); + return true; + } + return false; + } + public async Task<List<CommentSend>> ListComments(string postid) + { + Post p = await _posts.Find(post => post._id == postid).FirstOrDefaultAsync(); + if (p != null) + { + List<Comment> lista = new List<Comment>(); + lista = p.comments.FindAll(x => x.parentId == null || x.parentId == ""); + if (lista.Count() > 0) + { + List<CommentSend> tosend = new List<CommentSend>(); + foreach(var comment in lista) + { + CommentSend c = new CommentSend(); + c.userId = comment.userId; + c._id = comment._id; + c.parentId = comment.parentId; + c.comment = comment.comment; + c.timestamp = comment.timestamp; + + var user = await _users.Find(x => x._id == comment.userId).FirstOrDefaultAsync(); + if (user != null) + c.username = user.username; + else c.username = "Deleted user"; + + c.replies = await CascadeComments(comment._id, p); + + tosend.Add(c); + } + return tosend; + } + } + return null; + } + public async Task<List<CommentSend>> CascadeComments(string parentid,Post p) + { + List<Comment> lista = new List<Comment>(); + lista = p.comments.FindAll(x => x.parentId == parentid); + if (lista.Count()>0) + { + List<CommentSend> replies = new List<CommentSend>(); + foreach (var comment in lista) + { + CommentSend c = new CommentSend(); + c.userId = comment.userId; + c._id = comment._id; + c.parentId = comment.parentId; + c.comment = comment.comment; + c.timestamp = comment.timestamp; + + var user= await _users.Find(x => x._id == comment.userId).FirstOrDefaultAsync(); + if (user != null) + c.username = user.username; + else c.username = "Deleted user"; + + c.replies = await CascadeComments(comment._id, p); + + replies.Add(c); + } + return replies; + } + return null; + } + public async Task<Boolean> DeleteComments(string postid,string cmntid,string userid) + { + Post p = await _posts.Find(post => post._id == postid).FirstOrDefaultAsync(); + if (p != null) + { + var com = p.comments.Find(x => x._id == cmntid); + if (com != null && com.userId == userid) + { + var comment = p.comments.Find(x => x._id == cmntid); + p.comments.Remove(comment); + await _posts.ReplaceOneAsync(x => x._id == p._id, p); + await CascadeDeleteComments(cmntid, p); + return true; + } + } + return false; + } + public async Task CascadeDeleteComments(string cmntid,Post p) + { + List<Comment> lista = new List<Comment>(); + lista = p.comments.FindAll(x => x.parentId == cmntid); + if (lista.Count() > 0) + { + foreach (var comment in lista) + { + p.comments.Remove(comment); + await _posts.ReplaceOneAsync(x => x._id == p._id, p); + await CascadeDeleteComments(comment._id, p); + } + } } - //(TODO) ADD Delete and update } } |