diff options
Diffstat (limited to 'Timeline/Services')
-rw-r--r-- | Timeline/Services/Clock.cs | 2 | ||||
-rw-r--r-- | Timeline/Services/TimelineService.cs | 51 | ||||
-rw-r--r-- | Timeline/Services/UserDeleteService.cs | 69 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 46 | ||||
-rw-r--r-- | Timeline/Services/UserTokenManager.cs | 3 |
5 files changed, 120 insertions, 51 deletions
diff --git a/Timeline/Services/Clock.cs b/Timeline/Services/Clock.cs index 040f9304..4395edcd 100644 --- a/Timeline/Services/Clock.cs +++ b/Timeline/Services/Clock.cs @@ -23,7 +23,7 @@ namespace Timeline.Services public DateTime GetCurrentTime()
{
- return DateTime.Now;
+ return DateTime.UtcNow;
}
}
}
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs index eafb0088..0070fe3e 100644 --- a/Timeline/Services/TimelineService.cs +++ b/Timeline/Services/TimelineService.cs @@ -220,6 +220,12 @@ namespace Timeline.Services Task DeletePost(string timelineName, long postId);
/// <summary>
+ /// Delete all posts of the given user. Used when delete a user.
+ /// </summary>
+ /// <param name="userId">The id of the user.</param>
+ Task DeleteAllPostsOfUser(long userId);
+
+ /// <summary>
/// Change member of timeline.
/// </summary>
/// <param name="timelineName">The name of the timeline.</param>
@@ -413,9 +419,7 @@ namespace Timeline.Services private async Task<TimelinePost> MapTimelinePostFromEntity(TimelinePostEntity entity, string timelineName)
{
-
-
- var author = await _userService.GetUserById(entity.AuthorId);
+ User? author = entity.AuthorId.HasValue ? await _userService.GetUserById(entity.AuthorId.Value) : null;
ITimelinePostContent? content = null;
@@ -561,11 +565,13 @@ namespace Timeline.Services public async Task<List<TimelinePost>> GetPosts(string timelineName, DateTime? modifiedSince = null, bool includeDeleted = false)
{
+ modifiedSince = modifiedSince?.MyToUtc();
+
if (timelineName == null)
throw new ArgumentNullException(nameof(timelineName));
var timelineId = await FindTimelineId(timelineName);
- var query = _database.TimelinePosts.OrderBy(p => p.Time).Where(p => p.TimelineId == timelineId);
+ IQueryable<TimelinePostEntity> query = _database.TimelinePosts.Where(p => p.TimelineId == timelineId);
if (!includeDeleted)
{
@@ -574,9 +580,11 @@ namespace Timeline.Services if (modifiedSince.HasValue)
{
- query = query.Where(p => p.LastUpdated >= modifiedSince);
+ query = query.Include(p => p.Author).Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince));
}
+ query = query.OrderBy(p => p.Time);
+
var postEntities = await query.ToListAsync();
var posts = new List<TimelinePost>();
@@ -659,6 +667,8 @@ namespace Timeline.Services public async Task<TimelinePost> CreateTextPost(string timelineName, long authorId, string text, DateTime? time)
{
+ time = time?.MyToUtc();
+
if (timelineName == null)
throw new ArgumentNullException(nameof(timelineName));
if (text == null)
@@ -700,6 +710,8 @@ namespace Timeline.Services public async Task<TimelinePost> CreateImagePost(string timelineName, long authorId, byte[] data, DateTime? time)
{
+ time = time?.MyToUtc();
+
if (timelineName == null)
throw new ArgumentNullException(nameof(timelineName));
if (data == null)
@@ -778,6 +790,35 @@ namespace Timeline.Services }
}
+ public async Task DeleteAllPostsOfUser(long userId)
+ {
+ var posts = await _database.TimelinePosts.Where(p => p.AuthorId == userId).ToListAsync();
+
+ var now = _clock.GetCurrentTime();
+
+ var dataTags = new List<string>();
+
+ foreach (var post in posts)
+ {
+ if (post.Content != null)
+ {
+ if (post.ContentType == TimelinePostContentTypes.Image)
+ {
+ dataTags.Add(post.Content);
+ }
+ post.Content = null;
+ }
+ post.LastUpdated = now;
+ }
+
+ await _database.SaveChangesAsync();
+
+ foreach (var dataTag in dataTags)
+ {
+ await _dataManager.FreeEntry(dataTag);
+ }
+ }
+
public async Task ChangeProperty(string timelineName, TimelineChangePropertyRequest newProperties)
{
if (timelineName == null)
diff --git a/Timeline/Services/UserDeleteService.cs b/Timeline/Services/UserDeleteService.cs new file mode 100644 index 00000000..845de573 --- /dev/null +++ b/Timeline/Services/UserDeleteService.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Helpers;
+using Timeline.Models.Validation;
+using static Timeline.Resources.Services.UserService;
+
+namespace Timeline.Services
+{
+ public interface IUserDeleteService
+ {
+ /// <summary>
+ /// Delete a user of given username.
+ /// </summary>
+ /// <param name="username">Username of the user to delete. Can't be null.</param>
+ /// <returns>True if user is deleted, false if user not exist.</returns>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
+ Task<bool> DeleteUser(string username);
+ }
+
+ public class UserDeleteService : IUserDeleteService
+ {
+ private readonly ILogger<UserDeleteService> _logger;
+
+ private readonly DatabaseContext _databaseContext;
+
+ private readonly ITimelineService _timelineService;
+
+ private readonly UsernameValidator _usernameValidator = new UsernameValidator();
+
+ public UserDeleteService(ILogger<UserDeleteService> logger, DatabaseContext databaseContext, ITimelineService timelineService)
+ {
+ _logger = logger;
+ _databaseContext = databaseContext;
+ _timelineService = timelineService;
+ }
+
+ public async Task<bool> DeleteUser(string username)
+ {
+ if (username == null)
+ throw new ArgumentNullException(nameof(username));
+
+ if (!_usernameValidator.Validate(username, out var message))
+ {
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), nameof(username));
+ }
+
+ var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
+ if (user == null)
+ return false;
+
+ await _timelineService.DeleteAllPostsOfUser(user.Id);
+
+ _databaseContext.Users.Remove(user);
+
+ await _databaseContext.SaveChangesAsync();
+ _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", user.Id), ("Username", user.Username)));
+
+ return true;
+ }
+
+ }
+}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index 4e56c86a..d9b3da26 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -127,22 +127,6 @@ namespace Timeline.Services Task<User> ModifyUser(string username, User? info);
/// <summary>
- /// Delete a user of given id.
- /// </summary>
- /// <param name="id">Id of the user to delete.</param>
- /// <returns>True if user is deleted, false if user not exist.</returns>
- Task<bool> DeleteUser(long id);
-
- /// <summary>
- /// Delete a user of given username.
- /// </summary>
- /// <param name="username">Username of the user to delete. Can't be null.</param>
- /// <returns>True if user is deleted, false if user not exist.</returns>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- Task<bool> DeleteUser(string username);
-
- /// <summary>
/// Try to change a user's password with old password.
/// </summary>
/// <param name="id">The id of user to change password of.</param>
@@ -348,7 +332,7 @@ namespace Timeline.Services {
if (info != null)
{
- DateTimeOffset now = _clock.GetCurrentTime();
+ var now = _clock.GetCurrentTime();
bool updateLastModified = false;
var username = info.Username;
@@ -428,34 +412,6 @@ namespace Timeline.Services return CreateUserFromEntity(entity);
}
- public async Task<bool> DeleteUser(long id)
- {
- var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
- if (user == null)
- return false;
-
- _databaseContext.Users.Remove(user);
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", id), ("Username", user.Username)));
- return true;
- }
-
- public async Task<bool> DeleteUser(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- CheckUsernameFormat(username, nameof(username));
-
- var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
- if (user == null)
- return false;
-
- _databaseContext.Users.Remove(user);
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", user.Id), ("Username", username)));
- return true;
- }
-
public async Task ChangePassword(long id, string oldPassword, string newPassword)
{
if (oldPassword == null)
diff --git a/Timeline/Services/UserTokenManager.cs b/Timeline/Services/UserTokenManager.cs index a016ff96..813dae67 100644 --- a/Timeline/Services/UserTokenManager.cs +++ b/Timeline/Services/UserTokenManager.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
+using Timeline.Helpers;
using Timeline.Models;
using Timeline.Services.Exceptions;
@@ -57,6 +58,8 @@ namespace Timeline.Services public async Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null)
{
+ expireAt = expireAt?.MyToUtc();
+
if (username == null)
throw new ArgumentNullException(nameof(username));
if (password == null)
|