aboutsummaryrefslogtreecommitdiff
path: root/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline')
-rw-r--r--Timeline/Auth/Attribute.cs21
-rw-r--r--Timeline/Auth/MyAuthenticationHandler.cs100
-rw-r--r--Timeline/Auth/PrincipalExtensions.cs13
-rw-r--r--Timeline/ClientApp/.babelrc27
-rw-r--r--Timeline/ClientApp/.editorconfig14
-rw-r--r--Timeline/ClientApp/.eslintignore6
-rw-r--r--Timeline/ClientApp/.eslintrc.js47
-rw-r--r--Timeline/ClientApp/.gitattributes1
-rw-r--r--Timeline/ClientApp/.gitignore32
-rw-r--r--Timeline/ClientApp/.vscode/extensions.json9
-rw-r--r--Timeline/ClientApp/.vscode/preview.yml10
-rw-r--r--Timeline/ClientApp/.vscode/settings.json6
-rw-r--r--Timeline/ClientApp/.yarnrc.yml5
-rw-r--r--Timeline/ClientApp/LICENSE21
-rw-r--r--Timeline/ClientApp/package.json111
-rw-r--r--Timeline/ClientApp/postcss.config.js10
-rw-r--r--Timeline/ClientApp/public/android-chrome-192x192.pngbin8519 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/android-chrome-512x512.pngbin23619 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/apple-touch-icon.pngbin2088 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/browserconfig.xml9
-rw-r--r--Timeline/ClientApp/public/favicon-16x16.pngbin682 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/favicon-32x32.pngbin821 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/favicon.icobin15086 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/mstile-144x144.pngbin2259 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/mstile-150x150.pngbin2240 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/mstile-310x150.pngbin2482 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/mstile-310x310.pngbin4937 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/mstile-70x70.pngbin1561 -> 0 bytes
-rw-r--r--Timeline/ClientApp/public/safari-pinned-tab.svg25
-rw-r--r--Timeline/ClientApp/public/site.webmanifest22
-rw-r--r--Timeline/ClientApp/sandbox.config.json11
-rw-r--r--Timeline/ClientApp/src/app/App.tsx84
-rw-r--r--Timeline/ClientApp/src/app/common.ts44
-rw-r--r--Timeline/ClientApp/src/app/http/common.ts161
-rw-r--r--Timeline/ClientApp/src/app/http/timeline.ts544
-rw-r--r--Timeline/ClientApp/src/app/http/token.ts72
-rw-r--r--Timeline/ClientApp/src/app/http/user.ts134
-rw-r--r--Timeline/ClientApp/src/app/i18n.ts79
-rw-r--r--Timeline/ClientApp/src/app/index.ejs29
-rw-r--r--Timeline/ClientApp/src/app/index.sass66
-rw-r--r--Timeline/ClientApp/src/app/index.tsx15
-rw-r--r--Timeline/ClientApp/src/app/locales/en/translation.ts202
-rw-r--r--Timeline/ClientApp/src/app/locales/scheme.ts182
-rw-r--r--Timeline/ClientApp/src/app/locales/zh/translation.ts195
-rw-r--r--Timeline/ClientApp/src/app/service-worker.tsx113
-rw-r--r--Timeline/ClientApp/src/app/services/DataHub.ts225
-rw-r--r--Timeline/ClientApp/src/app/services/alert.ts61
-rw-r--r--Timeline/ClientApp/src/app/services/common.ts23
-rw-r--r--Timeline/ClientApp/src/app/services/timeline.ts702
-rw-r--r--Timeline/ClientApp/src/app/services/user.ts393
-rw-r--r--Timeline/ClientApp/src/app/tsconfig.json13
-rw-r--r--Timeline/ClientApp/src/app/typings.d.ts24
-rw-r--r--Timeline/ClientApp/src/app/utilities/rxjs.ts14
-rw-r--r--Timeline/ClientApp/src/app/utilities/url.ts52
-rw-r--r--Timeline/ClientApp/src/app/views/about/about.sass4
-rw-r--r--Timeline/ClientApp/src/app/views/about/author-avatar.pngbin12038 -> 0 bytes
-rw-r--r--Timeline/ClientApp/src/app/views/about/github.pngbin4268 -> 0 bytes
-rw-r--r--Timeline/ClientApp/src/app/views/about/index.tsx164
-rw-r--r--Timeline/ClientApp/src/app/views/admin/Admin.tsx75
-rw-r--r--Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx460
-rw-r--r--Timeline/ClientApp/src/app/views/common/AppBar.tsx64
-rw-r--r--Timeline/ClientApp/src/app/views/common/BlobImage.tsx27
-rw-r--r--Timeline/ClientApp/src/app/views/common/ImageCropper.tsx306
-rw-r--r--Timeline/ClientApp/src/app/views/common/LoadingButton.tsx29
-rw-r--r--Timeline/ClientApp/src/app/views/common/LoadingPage.tsx12
-rw-r--r--Timeline/ClientApp/src/app/views/common/OperationDialog.tsx364
-rw-r--r--Timeline/ClientApp/src/app/views/common/SearchInput.tsx63
-rw-r--r--Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx26
-rw-r--r--Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx26
-rw-r--r--Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx101
-rw-r--r--Timeline/ClientApp/src/app/views/common/alert/alert.sass15
-rw-r--r--Timeline/ClientApp/src/app/views/common/common.sass33
-rw-r--r--Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx101
-rw-r--r--Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx60
-rw-r--r--Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx61
-rw-r--r--Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx73
-rw-r--r--Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx53
-rw-r--r--Timeline/ClientApp/src/app/views/home/home.sass13
-rw-r--r--Timeline/ClientApp/src/app/views/home/index.tsx99
-rw-r--r--Timeline/ClientApp/src/app/views/login/index.tsx151
-rw-r--r--Timeline/ClientApp/src/app/views/login/login.sass2
-rw-r--r--Timeline/ClientApp/src/app/views/settings/index.tsx209
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx23
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx26
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx58
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx84
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx172
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx211
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx185
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx243
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx241
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx72
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx21
-rw-r--r--Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass146
-rw-r--r--Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx55
-rw-r--r--Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx85
-rw-r--r--Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx20
-rw-r--r--Timeline/ClientApp/src/app/views/timeline/index.tsx37
-rw-r--r--Timeline/ClientApp/src/app/views/timeline/timeline.sass0
-rw-r--r--Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx302
-rw-r--r--Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx28
-rw-r--r--Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx80
-rw-r--r--Timeline/ClientApp/src/app/views/user/UserPageUI.tsx18
-rw-r--r--Timeline/ClientApp/src/app/views/user/index.tsx72
-rw-r--r--Timeline/ClientApp/src/app/views/user/user.sass7
-rw-r--r--Timeline/ClientApp/src/sw/sw.ts28
-rw-r--r--Timeline/ClientApp/src/sw/tsconfig.json12
-rw-r--r--Timeline/ClientApp/src/tsconfig.json23
-rw-r--r--Timeline/ClientApp/webpack.common.js86
-rw-r--r--Timeline/ClientApp/webpack.config.dev.js52
-rw-r--r--Timeline/ClientApp/webpack.config.prod.js53
-rw-r--r--Timeline/Configs/ApplicationConfiguration.cs13
-rw-r--r--Timeline/Configs/JwtConfiguration.cs14
-rw-r--r--Timeline/Controllers/ControllerAuthExtensions.cs40
-rw-r--r--Timeline/Controllers/Testing/TestingAuthController.cs32
-rw-r--r--Timeline/Controllers/TimelineController.cs491
-rw-r--r--Timeline/Controllers/TokenController.cs142
-rw-r--r--Timeline/Controllers/UserAvatarController.cs174
-rw-r--r--Timeline/Controllers/UserController.cs195
-rw-r--r--Timeline/Entities/DataEntity.cs23
-rw-r--r--Timeline/Entities/DatabaseContext.cs34
-rw-r--r--Timeline/Entities/JwtTokenEntity.cs17
-rw-r--r--Timeline/Entities/TimelineEntity.cs58
-rw-r--r--Timeline/Entities/TimelineMemberEntity.cs24
-rw-r--r--Timeline/Entities/TimelinePostEntity.cs43
-rw-r--r--Timeline/Entities/UserAvatarEntity.cs29
-rw-r--r--Timeline/Entities/UserEntity.cs56
-rw-r--r--Timeline/Entities/UtcDateAnnotation.cs44
-rw-r--r--Timeline/Filters/Header.cs63
-rw-r--r--Timeline/Filters/Timeline.cs32
-rw-r--r--Timeline/Formatters/BytesInputFormatter.cs79
-rw-r--r--Timeline/Formatters/StringInputFormatter.cs26
-rw-r--r--Timeline/GlobalSuppressions.cs14
-rw-r--r--Timeline/Helpers/DataCacheHelper.cs125
-rw-r--r--Timeline/Helpers/DateTimeExtensions.cs14
-rw-r--r--Timeline/Helpers/InvalidModelResponseFactory.cs25
-rw-r--r--Timeline/Helpers/LanguageHelper.cs12
-rw-r--r--Timeline/Helpers/Log.cs22
-rw-r--r--Timeline/Migrations/20200105150407_Initialize.Designer.cs266
-rw-r--r--Timeline/Migrations/20200105150407_Initialize.cs217
-rw-r--r--Timeline/Migrations/20200131100517_RefactorUser.Designer.cs240
-rw-r--r--Timeline/Migrations/20200131100517_RefactorUser.cs128
-rw-r--r--Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs257
-rw-r--r--Timeline/Migrations/20200221064341_AddJwtToken.cs45
-rw-r--r--Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs265
-rw-r--r--Timeline/Migrations/20200229103848_AddPostLocalId.cs42
-rw-r--r--Timeline/Migrations/20200306110049_AddDataTable.Designer.cs290
-rw-r--r--Timeline/Migrations/20200306110049_AddDataTable.cs87
-rw-r--r--Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs290
-rw-r--r--Timeline/Migrations/20200306111553_DropUserDetails.cs17
-rw-r--r--Timeline/Migrations/20200312112552_AddImagePost.Designer.cs299
-rw-r--r--Timeline/Migrations/20200312112552_AddImagePost.cs38
-rw-r--r--Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs306
-rw-r--r--Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs50
-rw-r--r--Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs314
-rw-r--r--Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs57
-rw-r--r--Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs321
-rw-r--r--Timeline/Migrations/20200808071611_UserAddUniqueId.cs55
-rw-r--r--Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs339
-rw-r--r--Timeline/Migrations/20200810155908_AddTimesToUser.cs67
-rw-r--r--Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs337
-rw-r--r--Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs78
-rw-r--r--Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs337
-rw-r--r--Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs17
-rw-r--r--Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs341
-rw-r--r--Timeline/Migrations/20200826164553_TimelineAddTitle.cs22
-rw-r--r--Timeline/Migrations/DatabaseContextModelSnapshot.cs339
-rw-r--r--Timeline/MockClientApp/index.html10
-rw-r--r--Timeline/Models/ByteData.cs33
-rw-r--r--Timeline/Models/Converters/JsonDateTimeConverter.cs23
-rw-r--r--Timeline/Models/Converters/MyDateTimeConverter.cs51
-rw-r--r--Timeline/Models/Http/ActionContextAccessorExtensions.cs14
-rw-r--r--Timeline/Models/Http/Common.cs120
-rw-r--r--Timeline/Models/Http/ErrorResponse.cs261
-rw-r--r--Timeline/Models/Http/Timeline.cs219
-rw-r--r--Timeline/Models/Http/TimelineController.cs93
-rw-r--r--Timeline/Models/Http/TokenController.cs62
-rw-r--r--Timeline/Models/Http/UserController.cs93
-rw-r--r--Timeline/Models/Http/UserInfo.cs90
-rw-r--r--Timeline/Models/Timeline.cs98
-rw-r--r--Timeline/Models/User.cs21
-rw-r--r--Timeline/Models/Validation/GeneralTimelineNameValidator.cs33
-rw-r--r--Timeline/Models/Validation/NameValidator.cs42
-rw-r--r--Timeline/Models/Validation/NicknameValidator.cs25
-rw-r--r--Timeline/Models/Validation/TimelineNameValidator.cs19
-rw-r--r--Timeline/Models/Validation/UsernameValidator.cs19
-rw-r--r--Timeline/Models/Validation/Validator.cs127
-rw-r--r--Timeline/Program.cs43
-rw-r--r--Timeline/Properties/launchSettings.json27
-rw-r--r--Timeline/Resources/Authentication/AuthHandler.Designer.cs99
-rw-r--r--Timeline/Resources/Authentication/AuthHandler.resx132
-rw-r--r--Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs81
-rw-r--r--Timeline/Resources/Controllers/ControllerAuthExtensions.resx126
-rw-r--r--Timeline/Resources/Controllers/TimelineController.Designer.cs81
-rw-r--r--Timeline/Resources/Controllers/TimelineController.resx126
-rw-r--r--Timeline/Resources/Controllers/TokenController.Designer.cs153
-rw-r--r--Timeline/Resources/Controllers/TokenController.resx150
-rw-r--r--Timeline/Resources/Controllers/UserAvatarController.Designer.cs144
-rw-r--r--Timeline/Resources/Controllers/UserAvatarController.resx147
-rw-r--r--Timeline/Resources/Controllers/UserController.Designer.cs117
-rw-r--r--Timeline/Resources/Controllers/UserController.resx138
-rw-r--r--Timeline/Resources/Entities.Designer.cs72
-rw-r--r--Timeline/Resources/Entities.resx123
-rw-r--r--Timeline/Resources/Filters.Designer.cs90
-rw-r--r--Timeline/Resources/Filters.resx129
-rw-r--r--Timeline/Resources/Helper/DataCacheHelper.Designer.cs90
-rw-r--r--Timeline/Resources/Helper/DataCacheHelper.resx129
-rw-r--r--Timeline/Resources/Messages.Designer.cs396
-rw-r--r--Timeline/Resources/Messages.resx231
-rw-r--r--Timeline/Resources/Models/Http/Common.Designer.cs99
-rw-r--r--Timeline/Resources/Models/Http/Common.resx132
-rw-r--r--Timeline/Resources/Models/Http/Exception.Designer.cs81
-rw-r--r--Timeline/Resources/Models/Http/Exception.resx126
-rw-r--r--Timeline/Resources/Models/Validation/NameValidator.Designer.cs99
-rw-r--r--Timeline/Resources/Models/Validation/NameValidator.resx132
-rw-r--r--Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs72
-rw-r--r--Timeline/Resources/Models/Validation/NicknameValidator.resx123
-rw-r--r--Timeline/Resources/Models/Validation/Validator.Designer.cs108
-rw-r--r--Timeline/Resources/Models/Validation/Validator.resx135
-rw-r--r--Timeline/Resources/Services/DataManager.Designer.cs72
-rw-r--r--Timeline/Resources/Services/DataManager.resx123
-rw-r--r--Timeline/Resources/Services/Exception.Designer.cs234
-rw-r--r--Timeline/Resources/Services/Exception.resx177
-rw-r--r--Timeline/Resources/Services/Exceptions.Designer.cs189
-rw-r--r--Timeline/Resources/Services/Exceptions.resx142
-rw-r--r--Timeline/Resources/Services/TimelineService.Designer.cs144
-rw-r--r--Timeline/Resources/Services/TimelineService.resx147
-rw-r--r--Timeline/Resources/Services/UserAvatarService.Designer.cs108
-rw-r--r--Timeline/Resources/Services/UserAvatarService.resx135
-rw-r--r--Timeline/Resources/Services/UserService.Designer.cs162
-rw-r--r--Timeline/Resources/Services/UserService.resx153
-rw-r--r--Timeline/Resources/Services/UserTokenService.Designer.cs72
-rw-r--r--Timeline/Resources/Services/UserTokenService.resx123
-rw-r--r--Timeline/Routes/ApiRoutePrefixConvention.cs46
-rw-r--r--Timeline/Routes/UnknownEndpointMiddleware.cs39
-rw-r--r--Timeline/Services/BadPasswordException.cs27
-rw-r--r--Timeline/Services/Clock.cs29
-rw-r--r--Timeline/Services/DataManager.cs122
-rw-r--r--Timeline/Services/DatabaseBackupService.cs35
-rw-r--r--Timeline/Services/DatabaseCorruptedException.cs15
-rw-r--r--Timeline/Services/ETagGenerator.cs45
-rw-r--r--Timeline/Services/EntityNames.cs14
-rw-r--r--Timeline/Services/Exceptions/EntityAlreadyExistError.cs63
-rw-r--r--Timeline/Services/Exceptions/EntityNotExistError.cs55
-rw-r--r--Timeline/Services/Exceptions/ExceptionMessageHelper.cs13
-rw-r--r--Timeline/Services/Exceptions/ImageException.cs57
-rw-r--r--Timeline/Services/Exceptions/TimelineNotExistException.cs21
-rw-r--r--Timeline/Services/Exceptions/TimelinePostNoDataException.cs15
-rw-r--r--Timeline/Services/Exceptions/TimelinePostNotExistException.cs33
-rw-r--r--Timeline/Services/Exceptions/UserNotExistException.cs40
-rw-r--r--Timeline/Services/ImageValidator.cs54
-rw-r--r--Timeline/Services/JwtUserTokenBadFormatException.cs48
-rw-r--r--Timeline/Services/PasswordBadFormatException.cs27
-rw-r--r--Timeline/Services/PasswordService.cs224
-rw-r--r--Timeline/Services/PathProvider.cs42
-rw-r--r--Timeline/Services/TimelineService.cs1166
-rw-r--r--Timeline/Services/UserAvatarService.cs265
-rw-r--r--Timeline/Services/UserDeleteService.cs69
-rw-r--r--Timeline/Services/UserRoleConvert.cs43
-rw-r--r--Timeline/Services/UserService.cs437
-rw-r--r--Timeline/Services/UserTokenException.cs68
-rw-r--r--Timeline/Services/UserTokenManager.cs97
-rw-r--r--Timeline/Services/UserTokenService.cs149
-rw-r--r--Timeline/Startup.cs187
-rw-r--r--Timeline/Swagger/ApiConvention.cs15
-rw-r--r--Timeline/Swagger/ByteDataRequestOperationProcessor.cs27
-rw-r--r--Timeline/Swagger/DefaultDescriptionOperationProcessor.cs39
-rw-r--r--Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs55
-rw-r--r--Timeline/Timeline.csproj289
-rw-r--r--Timeline/appsettings.Development.json9
-rw-r--r--Timeline/appsettings.json11
-rw-r--r--Timeline/default-avatar.pngbin26442 -> 0 bytes
-rw-r--r--Timeline/packages.lock.json1563
273 files changed, 0 insertions, 29178 deletions
diff --git a/Timeline/Auth/Attribute.cs b/Timeline/Auth/Attribute.cs
deleted file mode 100644
index 86d0109b..00000000
--- a/Timeline/Auth/Attribute.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Timeline.Entities;
-
-namespace Timeline.Auth
-{
- public class AdminAuthorizeAttribute : AuthorizeAttribute
- {
- public AdminAuthorizeAttribute()
- {
- Roles = UserRoles.Admin;
- }
- }
-
- public class UserAuthorizeAttribute : AuthorizeAttribute
- {
- public UserAuthorizeAttribute()
- {
- Roles = UserRoles.User;
- }
- }
-}
diff --git a/Timeline/Auth/MyAuthenticationHandler.cs b/Timeline/Auth/MyAuthenticationHandler.cs
deleted file mode 100644
index 3c97c329..00000000
--- a/Timeline/Auth/MyAuthenticationHandler.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Timeline.Services;
-using static Timeline.Resources.Authentication.AuthHandler;
-
-namespace Timeline.Auth
-{
- public static class AuthenticationConstants
- {
- public const string Scheme = "Bearer";
- public const string DisplayName = "My Jwt Auth Scheme";
- }
-
- public class MyAuthenticationOptions : AuthenticationSchemeOptions
- {
- /// <summary>
- /// The query param key to search for token. If null then query params are not searched for token. Default to <c>"token"</c>.
- /// </summary>
- public string TokenQueryParamKey { get; set; } = "token";
- }
-
- public class MyAuthenticationHandler : AuthenticationHandler<MyAuthenticationOptions>
- {
- private readonly ILogger<MyAuthenticationHandler> _logger;
- private readonly IUserTokenManager _userTokenManager;
-
- public MyAuthenticationHandler(IOptionsMonitor<MyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager)
- : base(options, logger, encoder, clock)
- {
- _logger = logger.CreateLogger<MyAuthenticationHandler>();
- _userTokenManager = userTokenManager;
- }
-
- // return null if no token is found
- private string? ExtractToken()
- {
- // check the authorization header
- string header = Request.Headers[HeaderNames.Authorization];
- if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
- {
- var token = header.Substring("Bearer ".Length).Trim();
- _logger.LogInformation(LogTokenFoundInHeader, token);
- return token;
- }
-
- // check the query params
- var paramQueryKey = Options.TokenQueryParamKey;
- if (!string.IsNullOrEmpty(paramQueryKey))
- {
- string token = Request.Query[paramQueryKey];
- if (!string.IsNullOrEmpty(token))
- {
- _logger.LogInformation(LogTokenFoundInQuery, paramQueryKey, token);
- return token;
- }
- }
-
- // not found anywhere then return null
- return null;
- }
-
- protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
- {
- var token = ExtractToken();
- if (string.IsNullOrEmpty(token))
- {
- _logger.LogInformation(LogTokenNotFound);
- return AuthenticateResult.NoResult();
- }
-
- try
- {
- var userInfo = await _userTokenManager.VerifyToken(token);
-
- var identity = new ClaimsIdentity(AuthenticationConstants.Scheme);
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id!.Value.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64));
- identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String));
- identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator!.Value).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String)));
-
- var principal = new ClaimsPrincipal();
- principal.AddIdentity(identity);
-
- return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthenticationConstants.Scheme));
- }
- catch (Exception e) when (!(e is ArgumentException))
- {
- _logger.LogInformation(e, LogTokenValidationFail);
- return AuthenticateResult.Fail(e);
- }
- }
- }
-}
diff --git a/Timeline/Auth/PrincipalExtensions.cs b/Timeline/Auth/PrincipalExtensions.cs
deleted file mode 100644
index ad7a887f..00000000
--- a/Timeline/Auth/PrincipalExtensions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Security.Principal;
-using Timeline.Entities;
-
-namespace Timeline.Auth
-{
- internal static class PrincipalExtensions
- {
- internal static bool IsAdministrator(this IPrincipal principal)
- {
- return principal.IsInRole(UserRoles.Admin);
- }
- }
-}
diff --git a/Timeline/ClientApp/.babelrc b/Timeline/ClientApp/.babelrc
deleted file mode 100644
index 092f2f73..00000000
--- a/Timeline/ClientApp/.babelrc
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "presets": [
- "@babel/env",
- "@babel/preset-react"
- ],
- "plugins": [
- "@babel/plugin-syntax-dynamic-import",
- "@babel/plugin-proposal-class-properties",
- "@babel/plugin-proposal-optional-chaining",
- "@babel/plugin-proposal-nullish-coalescing-operator",
- [
- "@babel/plugin-proposal-decorators",
- {
- "decoratorsBeforeExport": true
- }
- ],
- [
- "babel-plugin-transform-builtin-extend",
- {
- "globals": [
- "Error",
- "Array"
- ]
- }
- ]
- ]
-} \ No newline at end of file
diff --git a/Timeline/ClientApp/.editorconfig b/Timeline/ClientApp/.editorconfig
deleted file mode 100644
index 779719e0..00000000
--- a/Timeline/ClientApp/.editorconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-root = true
-end_of_line = lf
-
-[*.ts]
-tab_width = 2
-
-[*.tsx]
-tab_width = 2
-
-[*.css]
-tab_width = 2
-
-[*.sass]
-tab_width = 2
diff --git a/Timeline/ClientApp/.eslintignore b/Timeline/ClientApp/.eslintignore
deleted file mode 100644
index f29f7466..00000000
--- a/Timeline/ClientApp/.eslintignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.yarn
-node_modules
-dist
-webpack.*.js
-.eslintrc.js
-postcss.config.js
diff --git a/Timeline/ClientApp/.eslintrc.js b/Timeline/ClientApp/.eslintrc.js
deleted file mode 100644
index 900489ed..00000000
--- a/Timeline/ClientApp/.eslintrc.js
+++ /dev/null
@@ -1,47 +0,0 @@
-module.exports = {
- env: {
- browser: true,
- es2020: true,
- },
- extends: [
- "eslint:recommended",
- "plugin:react/recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:@typescript-eslint/recommended-requiring-type-checking",
- "plugin:prettier/recommended",
- "prettier/react",
- "prettier/@typescript-eslint",
- "plugin:react-hooks/recommended",
- ],
- globals: {
- Atomics: "readonly",
- SharedArrayBuffer: "readonly",
- },
- parser: "@typescript-eslint/parser",
- parserOptions: {
- project: ["./src/app/tsconfig.json", "./src/sw/tsconfig.json"],
- ecmaFeatures: {
- jsx: true,
- },
- sourceType: "module",
- },
- plugins: ["react", "@typescript-eslint", "react-hooks"],
- settings: {
- react: {
- version: "detect",
- },
- },
- rules: {
- "react/prop-types": "off",
- "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
- "@typescript-eslint/explicit-function-return-type": [
- "warn",
- {
- allowExpressions: true,
- allowTypedFunctionExpressions: true,
- allowHigherOrderFunctions: true,
- },
- ],
- },
-};
diff --git a/Timeline/ClientApp/.gitattributes b/Timeline/ClientApp/.gitattributes
deleted file mode 100644
index c1aa21ac..00000000
--- a/Timeline/ClientApp/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-.yarn/** linguist-vendored \ No newline at end of file
diff --git a/Timeline/ClientApp/.gitignore b/Timeline/ClientApp/.gitignore
deleted file mode 100644
index 1de0b58f..00000000
--- a/Timeline/ClientApp/.gitignore
+++ /dev/null
@@ -1,32 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-
-.yarn/*
-!.yarn/cache
-!.yarn/releases
-!.yarn/plugins
-!.yarn/sdks
-!.yarn/versions
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-.vscode/launch.json
-
-/dist \ No newline at end of file
diff --git a/Timeline/ClientApp/.vscode/extensions.json b/Timeline/ClientApp/.vscode/extensions.json
deleted file mode 100644
index be640996..00000000
--- a/Timeline/ClientApp/.vscode/extensions.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "recommendations": [
- "dbaeumer.vscode-eslint",
- "esbenp.prettier-vscode",
- "arcanis.vscode-zipfs",
- "syler.sass-indented",
- "editorconfig.editorconfig"
- ]
-}
diff --git a/Timeline/ClientApp/.vscode/preview.yml b/Timeline/ClientApp/.vscode/preview.yml
deleted file mode 100644
index eb5e452c..00000000
--- a/Timeline/ClientApp/.vscode/preview.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-# .vscode/preview.yml
-autoOpen: false # 打开工作空间时是否自动开启所有应用的预览
-apps:
- - port: 3000 # 应用的端口
- run: yarn start:mock # 应用的启动命令
- root: . # 应用的启动目录
- name: timeline # 应用名称
- description: Timeline App # 应用描述
- autoOpen: false # 打开工作空间时是否自动开启预览(优先级高于根级 autoOpen)
diff --git a/Timeline/ClientApp/.vscode/settings.json b/Timeline/ClientApp/.vscode/settings.json
deleted file mode 100644
index 3db658ba..00000000
--- a/Timeline/ClientApp/.vscode/settings.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "eslint.nodePath": ".yarn/sdks",
- "prettier.prettierPath": ".yarn/sdks/prettier/index.js",
- "typescript.tsdk": ".yarn/sdks/typescript/lib",
- "typescript.enablePromptUseWorkspaceTsdk": true
-}
diff --git a/Timeline/ClientApp/.yarnrc.yml b/Timeline/ClientApp/.yarnrc.yml
deleted file mode 100644
index 2e4e43a7..00000000
--- a/Timeline/ClientApp/.yarnrc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-plugins:
- - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
- spec: "@yarnpkg/plugin-interactive-tools"
-
-yarnPath: .yarn/releases/yarn-2.1.1.cjs
diff --git a/Timeline/ClientApp/LICENSE b/Timeline/ClientApp/LICENSE
deleted file mode 100644
index 238cd2d9..00000000
--- a/Timeline/ClientApp/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 杨宇千
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/Timeline/ClientApp/package.json b/Timeline/ClientApp/package.json
deleted file mode 100644
index 65c5cbe2..00000000
--- a/Timeline/ClientApp/package.json
+++ /dev/null
@@ -1,111 +0,0 @@
-{
- "name": "timeline",
- "version": "0.1.0",
- "private": true,
- "homepage": "https://crupest.xyz",
- "keywords": [],
- "description": "Timeline app.",
- "dependencies": {
- "axios": "^0.21.0",
- "bootstrap": "^4.5.3",
- "bootstrap-icons": "^1.0.0",
- "classnames": "^2.2.6",
- "clsx": "^1.1.1",
- "core-js": "^3.6.5",
- "i18next": "^19.8.3",
- "i18next-browser-languagedetector": "^6.0.1",
- "localforage": "^1.9.0",
- "lodash": "^4.17.20",
- "pepjs": "^0.5.2",
- "react": "^17.0.1",
- "react-bootstrap": "^1.4.0",
- "react-dom": "^17.0.1",
- "react-hot-loader": "^4.13.0",
- "react-i18next": "^11.7.3",
- "react-inlinesvg": "^2.1.1",
- "react-responsive": "^8.1.0",
- "react-router": "^5.2.0",
- "react-router-bootstrap": "^0.25.0",
- "react-router-dom": "^5.2.0",
- "regenerator-runtime": "^0.13.7",
- "rxjs": "^6.6.3",
- "workbox-precaching": "^5.1.4",
- "workbox-routing": "^5.1.4",
- "workbox-strategies": "^5.1.4",
- "workbox-window": "^5.1.4",
- "xregexp": "^4.3.0"
- },
- "scripts": {
- "start": "webpack-dev-server --config ./webpack.config.dev.js",
- "build": "webpack --config ./webpack.config.prod.js",
- "lint": "eslint src/ --ext .js --ext .jsx --ext .ts --ext .tsx"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- },
- "devDependencies": {
- "@babel/core": "^7.12.3",
- "@babel/plugin-proposal-class-properties": "^7.12.1",
- "@babel/plugin-proposal-decorators": "^7.12.1",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
- "@babel/plugin-proposal-optional-chaining": "^7.12.1",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/preset-env": "^7.12.1",
- "@babel/preset-react": "^7.12.1",
- "@babel/preset-typescript": "^7.12.1",
- "@hot-loader/react-dom": "^17.0.0",
- "@types/classnames": "^2.2.10",
- "@types/lodash": "^4.14.162",
- "@types/node": "^14.14.5",
- "@types/react": "^16.9.53",
- "@types/react-dom": "^16.9.8",
- "@types/react-responsive": "^8.0.2",
- "@types/react-router": "^5.1.8",
- "@types/react-router-bootstrap": "^0.24.5",
- "@types/react-router-dom": "^5.1.6",
- "@types/webpack-env": "^1.15.3",
- "@types/xregexp": "^4.3.0",
- "@typescript-eslint/eslint-plugin": "^4.6.0",
- "@typescript-eslint/parser": "^4.6.0",
- "@yarnpkg/pnpify": "^2.3.3",
- "babel-loader": "^8.1.0",
- "babel-plugin-transform-builtin-extend": "^1.1.2",
- "clean-webpack-plugin": "^3.0.0",
- "copy-webpack-plugin": "^6.2.1",
- "css-loader": "^5.0.0",
- "eslint": "^7.12.1",
- "eslint-config-prettier": "^6.14.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-react": "^7.21.5",
- "eslint-plugin-react-hooks": "^4.2.0",
- "file-loader": "^6.1.1",
- "html-webpack-plugin": "^4.5.0",
- "http-server": "^0.12.3",
- "mini-css-extract-plugin": "^1.2.0",
- "postcss": "^8.1.4",
- "postcss-loader": "^4.0.4",
- "postcss-preset-env": "^6.7.0",
- "prettier": "^2.1.2",
- "sass": "^1.27.0",
- "sass-loader": "^10.0.4",
- "style-loader": "^2.0.0",
- "ts-loader": "^8.0.7",
- "typescript": "^4.0.5",
- "url-loader": "^4.1.1",
- "webpack": "^5.2.0",
- "webpack-chain": "^6.5.1",
- "webpack-cli": "^4.1.0",
- "webpack-dev-server": "^3.11.0",
- "workbox-webpack-plugin": "^5.1.4"
- }
-}
diff --git a/Timeline/ClientApp/postcss.config.js b/Timeline/ClientApp/postcss.config.js
deleted file mode 100644
index 74ee8155..00000000
--- a/Timeline/ClientApp/postcss.config.js
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = {
- plugins: [
- [
- "postcss-preset-env",
- {
- // Options
- },
- ],
- ],
-};
diff --git a/Timeline/ClientApp/public/android-chrome-192x192.png b/Timeline/ClientApp/public/android-chrome-192x192.png
deleted file mode 100644
index da9b6b81..00000000
--- a/Timeline/ClientApp/public/android-chrome-192x192.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/android-chrome-512x512.png b/Timeline/ClientApp/public/android-chrome-512x512.png
deleted file mode 100644
index fa84e055..00000000
--- a/Timeline/ClientApp/public/android-chrome-512x512.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/apple-touch-icon.png b/Timeline/ClientApp/public/apple-touch-icon.png
deleted file mode 100644
index d5a3fb45..00000000
--- a/Timeline/ClientApp/public/apple-touch-icon.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/browserconfig.xml b/Timeline/ClientApp/public/browserconfig.xml
deleted file mode 100644
index f2c89409..00000000
--- a/Timeline/ClientApp/public/browserconfig.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
- <msapplication>
- <tile>
- <square150x150logo src="/mstile-150x150.png"/>
- <TileColor>#2d89ef</TileColor>
- </tile>
- </msapplication>
-</browserconfig>
diff --git a/Timeline/ClientApp/public/favicon-16x16.png b/Timeline/ClientApp/public/favicon-16x16.png
deleted file mode 100644
index 6c978995..00000000
--- a/Timeline/ClientApp/public/favicon-16x16.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/favicon-32x32.png b/Timeline/ClientApp/public/favicon-32x32.png
deleted file mode 100644
index bbde902f..00000000
--- a/Timeline/ClientApp/public/favicon-32x32.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/favicon.ico b/Timeline/ClientApp/public/favicon.ico
deleted file mode 100644
index d4cd3db6..00000000
--- a/Timeline/ClientApp/public/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/mstile-144x144.png b/Timeline/ClientApp/public/mstile-144x144.png
deleted file mode 100644
index 61eaaf43..00000000
--- a/Timeline/ClientApp/public/mstile-144x144.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/mstile-150x150.png b/Timeline/ClientApp/public/mstile-150x150.png
deleted file mode 100644
index 85fa83ee..00000000
--- a/Timeline/ClientApp/public/mstile-150x150.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/mstile-310x150.png b/Timeline/ClientApp/public/mstile-310x150.png
deleted file mode 100644
index 41889953..00000000
--- a/Timeline/ClientApp/public/mstile-310x150.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/mstile-310x310.png b/Timeline/ClientApp/public/mstile-310x310.png
deleted file mode 100644
index cddce02e..00000000
--- a/Timeline/ClientApp/public/mstile-310x310.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/mstile-70x70.png b/Timeline/ClientApp/public/mstile-70x70.png
deleted file mode 100644
index 52f59d43..00000000
--- a/Timeline/ClientApp/public/mstile-70x70.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/public/safari-pinned-tab.svg b/Timeline/ClientApp/public/safari-pinned-tab.svg
deleted file mode 100644
index e91f046a..00000000
--- a/Timeline/ClientApp/public/safari-pinned-tab.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
- "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
- width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
- preserveAspectRatio="xMidYMid meet">
-<metadata>
-Created by potrace 1.11, written by Peter Selinger 2001-2013
-</metadata>
-<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
-fill="#000000" stroke="none">
-<path d="M3080 6207 l0 -794 -57 -13 c-148 -33 -321 -102 -503 -202 -250 -136
--519 -398 -687 -669 -122 -196 -224 -463 -264 -694 -24 -139 -32 -418 -16
--560 72 -648 485 -1236 1069 -1523 68 -33 139 -67 158 -75 51 -22 219 -74 263
--82 l37 -7 0 -794 0 -794 420 0 420 0 0 794 0 794 38 7 c128 23 423 146 573
-238 503 309 846 851 916 1447 10 83 10 355 1 441 -15 127 -49 283 -89 399 -38
-111 -36 105 -108 252 -151 310 -366 554 -673 765 -62 43 -260 149 -306 164
--20 6 -41 15 -47 19 -15 11 -183 66 -248 81 l-57 14 0 792 0 793 -420 0 -420
-0 0 -793z m600 -1601 c14 -3 39 -9 55 -12 17 -2 48 -10 70 -16 22 -6 42 -12
-45 -13 21 -4 117 -45 170 -74 277 -145 492 -417 565 -716 24 -100 26 -116 31
--235 12 -327 -107 -619 -344 -847 -342 -329 -846 -405 -1272 -192 -293 147
--488 387 -584 719 -24 81 -31 151 -31 300 1 234 65 432 203 630 57 82 177 201
-263 263 137 97 323 173 474 193 28 3 52 8 54 10 6 5 274 -3 301 -10z"/>
-</g>
-</svg>
diff --git a/Timeline/ClientApp/public/site.webmanifest b/Timeline/ClientApp/public/site.webmanifest
deleted file mode 100644
index 74f0901a..00000000
--- a/Timeline/ClientApp/public/site.webmanifest
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/web-manifest",
-
- "name": "Timeline",
- "short_name": "Timeline",
- "description": "Record your life in Timeline! Created by crupest.",
- "icons": [
- {
- "src": "/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/Timeline/ClientApp/sandbox.config.json b/Timeline/ClientApp/sandbox.config.json
deleted file mode 100644
index 42b540ce..00000000
--- a/Timeline/ClientApp/sandbox.config.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "infiniteLoopProtection": true,
- "hardReloadOnChange": false,
- "view": "browser",
- "container": {
- "port": 3000,
- "startScript": "start:mock"
- },
- "port": 3000,
- "startScript": "start:mock"
-}
diff --git a/Timeline/ClientApp/src/app/App.tsx b/Timeline/ClientApp/src/app/App.tsx
deleted file mode 100644
index b68eddb6..00000000
--- a/Timeline/ClientApp/src/app/App.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from "react";
-import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
-import { hot } from "react-hot-loader/root";
-
-import AppBar from "./views/common/AppBar";
-import LoadingPage from "./views/common/LoadingPage";
-import Home from "./views/home";
-import Login from "./views/login";
-import Settings from "./views/settings";
-import About from "./views/about";
-import User from "./views/user";
-import TimelinePage from "./views/timeline";
-import AlertHost from "./views/common/alert/AlertHost";
-
-import { dataStorage } from "./services/common";
-import { userService, useRawUser } from "./services/user";
-
-const NoMatch: React.FC = () => {
- return (
- <>
- <AppBar />
- <div style={{ height: 56 }} />
- <div>Ah-oh, 404!</div>
- </>
- );
-};
-
-const LazyAdmin = React.lazy(
- () => import(/* webpackChunkName: "admin" */ "./views/admin/Admin")
-);
-
-const App: React.FC = () => {
- const [loading, setLoading] = React.useState<boolean>(true);
-
- const user = useRawUser();
-
- React.useEffect(() => {
- void userService.checkLoginState();
- void dataStorage.ready().then(() => setLoading(false));
- }, []);
-
- if (user === undefined || loading) {
- return <LoadingPage />;
- } else {
- return (
- <React.Suspense fallback={<LoadingPage />}>
- <Router>
- <AppBar />
- <Switch>
- <Route exact path="/">
- <Home />
- </Route>
- <Route exact path="/login">
- <Login />
- </Route>
- <Route path="/settings">
- <Settings />
- </Route>
- <Route path="/about">
- <About />
- </Route>
- <Route path="/timelines/:name">
- <TimelinePage />
- </Route>
- <Route path="/users/:username">
- <User />
- </Route>
- {user && user.administrator && (
- <Route path="/admin">
- <LazyAdmin user={user} />
- </Route>
- )}
- <Route>
- <NoMatch />
- </Route>
- </Switch>
- <AlertHost />
- </Router>
- </React.Suspense>
- );
- }
-};
-
-export default hot(App);
diff --git a/Timeline/ClientApp/src/app/common.ts b/Timeline/ClientApp/src/app/common.ts
deleted file mode 100644
index 0a2d345f..00000000
--- a/Timeline/ClientApp/src/app/common.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from "react";
-import { Observable, Subject } from "rxjs";
-
-// This error is thrown when ui goes wrong with bad logic.
-// Such as a variable should not be null, but it does.
-// This error should never occur. If it does, it indicates there is some logic bug in codes.
-export class UiLogicError extends Error {}
-
-export function useEventEmiiter(): [() => Observable<null>, () => void] {
- const ref = React.useRef<Subject<null> | null>(null);
-
- return React.useMemo(() => {
- const getter = (): Subject<null> => {
- if (ref.current == null) {
- ref.current = new Subject<null>();
- }
- return ref.current;
- };
- const trigger = (): void => {
- getter().next(null);
- };
- return [getter, trigger];
- }, []);
-}
-
-export function useValueEventEmiiter<T>(): [
- () => Observable<T>,
- (value: T) => void
-] {
- const ref = React.useRef<Subject<T> | null>(null);
-
- return React.useMemo(() => {
- const getter = (): Subject<T> => {
- if (ref.current == null) {
- ref.current = new Subject<T>();
- }
- return ref.current;
- };
- const trigger = (value: T): void => {
- getter().next(value);
- };
- return [getter, trigger];
- }, []);
-}
diff --git a/Timeline/ClientApp/src/app/http/common.ts b/Timeline/ClientApp/src/app/http/common.ts
deleted file mode 100644
index 54203d1a..00000000
--- a/Timeline/ClientApp/src/app/http/common.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-import { AxiosError, AxiosResponse } from "axios";
-
-export const apiBaseUrl = "/api";
-
-export function base64(blob: Blob): Promise<string> {
- return new Promise<string>((resolve) => {
- const reader = new FileReader();
- reader.onload = function () {
- resolve((reader.result as string).replace(/^data:.+;base64,/, ""));
- };
- reader.readAsDataURL(blob);
- });
-}
-
-export function extractStatusCode(error: AxiosError): number | null {
- if (error.isAxiosError) {
- const code = error?.response?.status;
- if (typeof code === "number") {
- return code;
- }
- }
- return null;
-}
-
-export interface CommonErrorResponse {
- code: number;
- message: string;
-}
-
-export function extractErrorCode(
- error: AxiosError<CommonErrorResponse>
-): number | null {
- if (error.isAxiosError) {
- const code = error.response?.data?.code;
- if (typeof code === "number") {
- return code;
- }
- }
- return null;
-}
-
-export class HttpNetworkError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export class HttpForbiddenError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export class NotModified {}
-
-export interface BlobWithEtag {
- data: Blob;
- etag: string;
-}
-
-export function extractResponseData<T>(res: AxiosResponse<T>): T {
- return res.data;
-}
-
-export function catchIfStatusCodeIs<
- TResult,
- TErrorHandlerResult extends TResult | PromiseLike<TResult> | null | undefined
->(
- statusCode: number,
- errorHandler: (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult
-): (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult {
- return (error: AxiosError<CommonErrorResponse>) => {
- if (extractStatusCode(error) == statusCode) {
- return errorHandler(error);
- } else {
- throw error;
- }
- };
-}
-
-export function convertToIfStatusCodeIs<NewError>(
- statusCode: number,
- newErrorType: {
- new (innerError: AxiosError): NewError;
- }
-): (error: AxiosError<CommonErrorResponse>) => never {
- return catchIfStatusCodeIs(statusCode, (error) => {
- throw new newErrorType(error);
- });
-}
-
-export function catchIfErrorCodeIs<
- TResult,
- TErrorHandlerResult extends TResult | PromiseLike<TResult> | null | undefined
->(
- errorCode: number,
- errorHandler: (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult
-): (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult {
- return (error: AxiosError<CommonErrorResponse>) => {
- if (extractErrorCode(error) == errorCode) {
- return errorHandler(error);
- } else {
- throw error;
- }
- };
-}
-export function convertToIfErrorCodeIs<NewError>(
- errorCode: number,
- newErrorType: {
- new (innerError: AxiosError): NewError;
- }
-): (error: AxiosError<CommonErrorResponse>) => never {
- return catchIfErrorCodeIs(errorCode, (error) => {
- throw new newErrorType(error);
- });
-}
-
-export function convertToNetworkError(
- error: AxiosError<CommonErrorResponse>
-): never {
- if (error.isAxiosError && error.response == null) {
- throw new HttpNetworkError(error);
- } else {
- throw error;
- }
-}
-
-export function convertToForbiddenError(
- error: AxiosError<CommonErrorResponse>
-): never {
- if (
- error.isAxiosError &&
- error.response != null &&
- (error.response.status == 401 || error.response.status == 403)
- ) {
- throw new HttpForbiddenError(error);
- } else {
- throw error;
- }
-}
-
-export function convertToNotModified(
- error: AxiosError<CommonErrorResponse>
-): NotModified {
- if (
- error.isAxiosError &&
- error.response != null &&
- error.response.status == 304
- ) {
- return new NotModified();
- } else {
- throw error;
- }
-}
-
-export function convertToBlobWithEtag(res: AxiosResponse<Blob>): BlobWithEtag {
- return {
- data: res.data,
- etag: (res.headers as Record<"etag", string>)["etag"],
- };
-}
diff --git a/Timeline/ClientApp/src/app/http/timeline.ts b/Timeline/ClientApp/src/app/http/timeline.ts
deleted file mode 100644
index eb7d5065..00000000
--- a/Timeline/ClientApp/src/app/http/timeline.ts
+++ /dev/null
@@ -1,544 +0,0 @@
-import axios, { AxiosError } from "axios";
-
-import { updateQueryString, applyQueryParameters } from "../utilities/url";
-
-import {
- apiBaseUrl,
- extractResponseData,
- convertToNetworkError,
- base64,
- convertToIfStatusCodeIs,
- convertToIfErrorCodeIs,
- BlobWithEtag,
- NotModified,
- convertToNotModified,
- convertToForbiddenError,
- convertToBlobWithEtag,
-} from "./common";
-import { HttpUser } from "./user";
-
-export const kTimelineVisibilities = ["Public", "Register", "Private"] as const;
-
-export type TimelineVisibility = typeof kTimelineVisibilities[number];
-
-export interface HttpTimelineInfo {
- uniqueId: string;
- name: string;
- description: string;
- owner: HttpUser;
- visibility: TimelineVisibility;
- lastModified: Date;
- members: HttpUser[];
-}
-
-export interface HttpTimelineListQuery {
- visibility?: TimelineVisibility;
- relate?: string;
- relateType?: "own" | "join";
-}
-
-export interface HttpTimelinePostRequest {
- name: string;
-}
-
-export interface HttpTimelinePostTextContent {
- type: "text";
- text: string;
-}
-
-export interface HttpTimelinePostImageContent {
- type: "image";
-}
-
-export type HttpTimelinePostContent =
- | HttpTimelinePostTextContent
- | HttpTimelinePostImageContent;
-
-export interface HttpTimelinePostInfo {
- id: number;
- content: HttpTimelinePostContent;
- time: Date;
- lastUpdated: Date;
- author: HttpUser;
- deleted: false;
-}
-
-export interface HttpTimelineDeletedPostInfo {
- id: number;
- time: Date;
- lastUpdated: Date;
- author?: HttpUser;
- deleted: true;
-}
-
-export type HttpTimelineGenericPostInfo =
- | HttpTimelinePostInfo
- | HttpTimelineDeletedPostInfo;
-
-export interface HttpTimelinePostPostRequestTextContent {
- type: "text";
- text: string;
-}
-
-export interface HttpTimelinePostPostRequestImageContent {
- type: "image";
- data: Blob;
-}
-
-export type HttpTimelinePostPostRequestContent =
- | HttpTimelinePostPostRequestTextContent
- | HttpTimelinePostPostRequestImageContent;
-
-export interface HttpTimelinePostPostRequest {
- content: HttpTimelinePostPostRequestContent;
- time?: Date;
-}
-
-export interface HttpTimelinePatchRequest {
- visibility?: TimelineVisibility;
- description?: string;
-}
-
-export class HttpTimelineNotExistError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export class HttpTimelinePostNotExistError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export class HttpTimelineNameConflictError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-//-------------------- begin: internal model --------------------
-
-interface RawTimelineInfo {
- uniqueId: string;
- name: string;
- description: string;
- owner: HttpUser;
- visibility: TimelineVisibility;
- lastModified: string;
- members: HttpUser[];
-}
-
-interface RawTimelinePostTextContent {
- type: "text";
- text: string;
-}
-
-interface RawTimelinePostImageContent {
- type: "image";
- url: string;
-}
-
-type RawTimelinePostContent =
- | RawTimelinePostTextContent
- | RawTimelinePostImageContent;
-
-interface RawTimelinePostInfo {
- id: number;
- content: RawTimelinePostContent;
- time: string;
- lastUpdated: string;
- author: HttpUser;
- deleted: false;
-}
-
-interface RawTimelineDeletedPostInfo {
- id: number;
- time: string;
- lastUpdated: string;
- author: HttpUser;
- deleted: true;
-}
-
-type RawTimelineGenericPostInfo =
- | RawTimelinePostInfo
- | RawTimelineDeletedPostInfo;
-
-interface RawTimelinePostPostRequestTextContent {
- type: "text";
- text: string;
-}
-
-interface RawTimelinePostPostRequestImageContent {
- type: "image";
- data: string;
-}
-
-type RawTimelinePostPostRequestContent =
- | RawTimelinePostPostRequestTextContent
- | RawTimelinePostPostRequestImageContent;
-
-interface RawTimelinePostPostRequest {
- content: RawTimelinePostPostRequestContent;
- time?: string;
-}
-
-//-------------------- end: internal model --------------------
-
-function processRawTimelineInfo(raw: RawTimelineInfo): HttpTimelineInfo {
- return {
- ...raw,
- lastModified: new Date(raw.lastModified),
- };
-}
-
-function processRawTimelinePostInfo(
- raw: RawTimelinePostInfo
-): HttpTimelinePostInfo;
-function processRawTimelinePostInfo(
- raw: RawTimelineGenericPostInfo
-): HttpTimelineGenericPostInfo;
-function processRawTimelinePostInfo(
- raw: RawTimelineGenericPostInfo
-): HttpTimelineGenericPostInfo {
- return {
- ...raw,
- time: new Date(raw.time),
- lastUpdated: new Date(raw.lastUpdated),
- };
-}
-
-export interface IHttpTimelineClient {
- listTimeline(query: HttpTimelineListQuery): Promise<HttpTimelineInfo[]>;
- getTimeline(timelineName: string): Promise<HttpTimelineInfo>;
- getTimeline(
- timelineName: string,
- query: {
- checkUniqueId?: string;
- }
- ): Promise<HttpTimelineInfo>;
- getTimeline(
- timelineName: string,
- query: {
- checkUniqueId?: string;
- ifModifiedSince: Date;
- }
- ): Promise<HttpTimelineInfo | NotModified>;
- postTimeline(
- req: HttpTimelinePostRequest,
- token: string
- ): Promise<HttpTimelineInfo>;
- patchTimeline(
- timelineName: string,
- req: HttpTimelinePatchRequest,
- token: string
- ): Promise<HttpTimelineInfo>;
- deleteTimeline(timelineName: string, token: string): Promise<void>;
- memberPut(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void>;
- memberDelete(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void>;
- listPost(
- timelineName: string,
- token?: string
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
- query: {
- modifiedSince?: Date;
- includeDeleted?: false;
- }
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
- query: {
- modifiedSince?: Date;
- includeDeleted: true;
- }
- ): Promise<HttpTimelineGenericPostInfo[]>;
- getPostData(
- timelineName: string,
- postId: number,
- token?: string
- ): Promise<BlobWithEtag>;
- getPostData(
- timelineName: string,
- postId: number,
- token: string | undefined,
- etag: string
- ): Promise<BlobWithEtag | NotModified>;
- postPost(
- timelineName: string,
- req: HttpTimelinePostPostRequest,
- token: string
- ): Promise<HttpTimelinePostInfo>;
- deletePost(
- timelineName: string,
- postId: number,
- token: string
- ): Promise<void>;
-}
-
-export class HttpTimelineClient implements IHttpTimelineClient {
- listTimeline(query: HttpTimelineListQuery): Promise<HttpTimelineInfo[]> {
- return axios
- .get<RawTimelineInfo[]>(
- applyQueryParameters(`${apiBaseUrl}/timelines`, query)
- )
- .then(extractResponseData)
- .then((list) => list.map(processRawTimelineInfo))
- .catch(convertToNetworkError);
- }
-
- getTimeline(timelineName: string): Promise<HttpTimelineInfo>;
- getTimeline(
- timelineName: string,
- query: {
- checkUniqueId?: string;
- }
- ): Promise<HttpTimelineInfo>;
- getTimeline(
- timelineName: string,
- query: {
- checkUniqueId?: string;
- ifModifiedSince: Date;
- }
- ): Promise<HttpTimelineInfo | NotModified>;
- getTimeline(
- timelineName: string,
- query?: {
- checkUniqueId?: string;
- ifModifiedSince?: Date;
- }
- ): Promise<HttpTimelineInfo | NotModified> {
- return axios
- .get<RawTimelineInfo>(
- applyQueryParameters(`${apiBaseUrl}/timelines/${timelineName}`, query)
- )
- .then((res) => {
- if (res.status === 304) {
- return new NotModified();
- } else {
- return processRawTimelineInfo(res.data);
- }
- })
- .catch(convertToIfStatusCodeIs(404, HttpTimelineNotExistError))
- .catch(convertToNetworkError);
- }
-
- postTimeline(
- req: HttpTimelinePostRequest,
- token: string
- ): Promise<HttpTimelineInfo> {
- return axios
- .post<RawTimelineInfo>(`${apiBaseUrl}/timelines?token=${token}`, req)
- .then(extractResponseData)
- .then(processRawTimelineInfo)
- .catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError))
- .catch(convertToNetworkError);
- }
-
- patchTimeline(
- timelineName: string,
- req: HttpTimelinePatchRequest,
- token: string
- ): Promise<HttpTimelineInfo> {
- return axios
- .patch<RawTimelineInfo>(
- `${apiBaseUrl}/timelines/${timelineName}?token=${token}`,
- req
- )
- .then(extractResponseData)
- .then(processRawTimelineInfo)
- .catch(convertToNetworkError);
- }
-
- deleteTimeline(timelineName: string, token: string): Promise<void> {
- return axios
- .delete(`${apiBaseUrl}/timelines/${timelineName}?token=${token}`)
- .catch(convertToNetworkError)
- .then();
- }
-
- memberPut(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void> {
- return axios
- .put(
- `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}`
- )
- .catch(convertToNetworkError)
- .then();
- }
-
- memberDelete(
- timelineName: string,
- username: string,
- token: string
- ): Promise<void> {
- return axios
- .delete(
- `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}`
- )
- .catch(convertToNetworkError)
- .then();
- }
-
- listPost(
- timelineName: string,
- token?: string
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
- query: {
- modifiedSince?: Date;
- includeDeleted?: false;
- }
- ): Promise<HttpTimelinePostInfo[]>;
- listPost(
- timelineName: string,
- token: string | undefined,
- query: {
- modifiedSince?: Date;
- includeDeleted: true;
- }
- ): Promise<HttpTimelineGenericPostInfo[]>;
- listPost(
- timelineName: string,
- token?: string,
- query?: {
- modifiedSince?: Date;
- includeDeleted?: boolean;
- }
- ): Promise<HttpTimelineGenericPostInfo[]> {
- let url = `${apiBaseUrl}/timelines/${timelineName}/posts`;
- url = updateQueryString("token", token, url);
- if (query != null) {
- if (query.modifiedSince != null) {
- url = updateQueryString(
- "modifiedSince",
- query.modifiedSince.toISOString(),
- url
- );
- }
- if (query.includeDeleted != null) {
- url = updateQueryString(
- "includeDeleted",
- query.includeDeleted ? "true" : "false",
- url
- );
- }
- }
-
- return axios
- .get<RawTimelineGenericPostInfo[]>(url)
- .then(extractResponseData)
- .catch(convertToIfStatusCodeIs(404, HttpTimelineNotExistError))
- .catch(convertToForbiddenError)
- .catch(convertToNetworkError)
- .then((rawPosts) =>
- rawPosts.map((raw) => processRawTimelinePostInfo(raw))
- );
- }
-
- getPostData(
- timelineName: string,
- postId: number,
- token: string
- ): Promise<BlobWithEtag>;
- getPostData(
- timelineName: string,
- postId: number,
- token?: string,
- etag?: string
- ): Promise<BlobWithEtag | NotModified> {
- const headers =
- etag != null
- ? {
- "If-None-Match": etag,
- }
- : undefined;
-
- let url = `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`;
- url = updateQueryString("token", token, url);
-
- return axios
- .get(url, {
- responseType: "blob",
- headers,
- })
- .then(convertToBlobWithEtag)
- .catch(convertToNotModified)
- .catch(convertToIfStatusCodeIs(404, HttpTimelinePostNotExistError))
- .catch(convertToNetworkError);
- }
-
- async postPost(
- timelineName: string,
- req: HttpTimelinePostPostRequest,
- token: string
- ): Promise<HttpTimelinePostInfo> {
- let content: RawTimelinePostPostRequestContent;
- if (req.content.type === "image") {
- const base64Data = await base64(req.content.data);
- content = {
- ...req.content,
- data: base64Data,
- } as RawTimelinePostPostRequestImageContent;
- } else {
- content = req.content;
- }
- const rawReq: RawTimelinePostPostRequest = {
- content,
- };
- if (req.time != null) {
- rawReq.time = req.time.toISOString();
- }
- return await axios
- .post<RawTimelinePostInfo>(
- `${apiBaseUrl}/timelines/${timelineName}/posts?token=${token}`,
- rawReq
- )
- .then(extractResponseData)
- .catch(convertToNetworkError)
- .then((rawPost) => processRawTimelinePostInfo(rawPost));
- }
-
- deletePost(
- timelineName: string,
- postId: number,
- token: string
- ): Promise<void> {
- return axios
- .delete(
- `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}?token=${token}`
- )
- .catch(convertToNetworkError)
- .then();
- }
-}
-
-let client: IHttpTimelineClient = new HttpTimelineClient();
-
-export function getHttpTimelineClient(): IHttpTimelineClient {
- return client;
-}
-
-export function setHttpTimelineClient(
- newClient: IHttpTimelineClient
-): IHttpTimelineClient {
- const old = client;
- client = newClient;
- return old;
-}
diff --git a/Timeline/ClientApp/src/app/http/token.ts b/Timeline/ClientApp/src/app/http/token.ts
deleted file mode 100644
index ae0cf3f6..00000000
--- a/Timeline/ClientApp/src/app/http/token.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import axios, { AxiosError } from "axios";
-
-import {
- apiBaseUrl,
- convertToNetworkError,
- convertToIfErrorCodeIs,
- extractResponseData,
-} from "./common";
-import { HttpUser } from "./user";
-
-export interface HttpCreateTokenRequest {
- username: string;
- password: string;
- expire: number;
-}
-
-export interface HttpCreateTokenResponse {
- token: string;
- user: HttpUser;
-}
-
-export interface HttpVerifyTokenRequest {
- token: string;
-}
-
-export interface HttpVerifyTokenResponse {
- user: HttpUser;
-}
-
-export class HttpCreateTokenBadCredentialError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export interface IHttpTokenClient {
- create(req: HttpCreateTokenRequest): Promise<HttpCreateTokenResponse>;
- verify(req: HttpVerifyTokenRequest): Promise<HttpVerifyTokenResponse>;
-}
-
-export class HttpTokenClient implements IHttpTokenClient {
- create(req: HttpCreateTokenRequest): Promise<HttpCreateTokenResponse> {
- return axios
- .post<HttpCreateTokenResponse>(`${apiBaseUrl}/token/create`, req)
- .then(extractResponseData)
- .catch(
- convertToIfErrorCodeIs(11010101, HttpCreateTokenBadCredentialError)
- )
- .catch(convertToNetworkError);
- }
-
- verify(req: HttpVerifyTokenRequest): Promise<HttpVerifyTokenResponse> {
- return axios
- .post<HttpVerifyTokenResponse>(`${apiBaseUrl}/token/verify`, req)
- .then(extractResponseData)
- .catch(convertToNetworkError);
- }
-}
-
-let client: IHttpTokenClient = new HttpTokenClient();
-
-export function getHttpTokenClient(): IHttpTokenClient {
- return client;
-}
-
-export function setHttpTokenClient(
- newClient: IHttpTokenClient
-): IHttpTokenClient {
- const old = client;
- client = newClient;
- return old;
-}
diff --git a/Timeline/ClientApp/src/app/http/user.ts b/Timeline/ClientApp/src/app/http/user.ts
deleted file mode 100644
index a0a02cce..00000000
--- a/Timeline/ClientApp/src/app/http/user.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import axios, { AxiosError } from "axios";
-
-import {
- apiBaseUrl,
- convertToNetworkError,
- extractResponseData,
- convertToIfStatusCodeIs,
- convertToIfErrorCodeIs,
- NotModified,
- BlobWithEtag,
- convertToBlobWithEtag,
- convertToNotModified,
-} from "./common";
-
-export interface HttpUser {
- uniqueId: string;
- username: string;
- administrator: boolean;
- nickname: string;
-}
-
-export interface HttpUserPatchRequest {
- nickname?: string;
-}
-
-export interface HttpChangePasswordRequest {
- oldPassword: string;
- newPassword: string;
-}
-
-export class HttpUserNotExistError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export class HttpChangePasswordBadCredentialError extends Error {
- constructor(public innerError?: AxiosError) {
- super();
- }
-}
-
-export interface IHttpUserClient {
- get(username: string): Promise<HttpUser>;
- patch(
- username: string,
- req: HttpUserPatchRequest,
- token: string
- ): Promise<HttpUser>;
- getAvatar(username: string): Promise<BlobWithEtag>;
- getAvatar(
- username: string,
- etag: string
- ): Promise<BlobWithEtag | NotModified>;
- putAvatar(username: string, data: Blob, token: string): Promise<void>;
- changePassword(req: HttpChangePasswordRequest, token: string): Promise<void>;
-}
-
-export class HttpUserClient implements IHttpUserClient {
- get(username: string): Promise<HttpUser> {
- return axios
- .get<HttpUser>(`${apiBaseUrl}/users/${username}`)
- .then(extractResponseData)
- .catch(convertToIfStatusCodeIs(404, HttpUserNotExistError))
- .catch(convertToNetworkError);
- }
-
- patch(
- username: string,
- req: HttpUserPatchRequest,
- token: string
- ): Promise<HttpUser> {
- return axios
- .patch<HttpUser>(`${apiBaseUrl}/users/${username}?token=${token}`, req)
- .then(extractResponseData)
- .catch(convertToNetworkError);
- }
-
- getAvatar(username: string): Promise<BlobWithEtag>;
- getAvatar(
- username: string,
- etag?: string
- ): Promise<BlobWithEtag | NotModified> {
- const headers =
- etag != null
- ? {
- "If-None-Match": etag,
- }
- : undefined;
-
- return axios
- .get(`${apiBaseUrl}/users/${username}/avatar`, {
- responseType: "blob",
- headers,
- })
- .then(convertToBlobWithEtag)
- .catch(convertToNotModified)
- .catch(convertToIfStatusCodeIs(404, HttpUserNotExistError))
- .catch(convertToNetworkError);
- }
-
- putAvatar(username: string, data: Blob, token: string): Promise<void> {
- return axios
- .put(`${apiBaseUrl}/users/${username}/avatar?token=${token}`, data, {
- headers: {
- "Content-Type": data.type,
- },
- })
- .catch(convertToNetworkError)
- .then();
- }
-
- changePassword(req: HttpChangePasswordRequest, token: string): Promise<void> {
- return axios
- .post(`${apiBaseUrl}/userop/changepassword?token=${token}`, req)
- .catch(
- convertToIfErrorCodeIs(11020201, HttpChangePasswordBadCredentialError)
- )
- .catch(convertToNetworkError)
- .then();
- }
-}
-
-let client: IHttpUserClient = new HttpUserClient();
-
-export function getHttpUserClient(): IHttpUserClient {
- return client;
-}
-
-export function setHttpUserClient(newClient: IHttpUserClient): IHttpUserClient {
- const old = client;
- client = newClient;
- return old;
-}
diff --git a/Timeline/ClientApp/src/app/i18n.ts b/Timeline/ClientApp/src/app/i18n.ts
deleted file mode 100644
index cdced7bf..00000000
--- a/Timeline/ClientApp/src/app/i18n.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import i18n, { BackendModule, ResourceKey } from "i18next";
-import LanguageDetector from "i18next-browser-languagedetector";
-import { initReactI18next } from "react-i18next";
-
-const backend: BackendModule = {
- type: "backend",
- async read(language, namespace, callback) {
- function error(message: string): void {
- callback(new Error(message), false);
- }
-
- function success(result: ResourceKey): void {
- callback(null, result);
- }
-
- if (namespace !== "translation") {
- error("Namespace must be 'translation'.");
- }
-
- if (language === "en") {
- const res = (
- await import(
- /* webpackChunkName: "locales-en" */ "./locales/en/translation"
- )
- ).default;
- success(res);
- } else if (language === "zh-cn" || language === "zh") {
- const res = (
- await import(
- /* webpackChunkName: "locales-zh" */ "./locales/zh/translation"
- )
- ).default;
- success(res);
- } else {
- error(`Language ${language} is not supported.`);
- }
- },
- init() {}, // eslint-disable-line @typescript-eslint/no-empty-function
- create() {}, // eslint-disable-line @typescript-eslint/no-empty-function
-};
-
-export const i18nPromise = i18n
- .use(LanguageDetector)
- .use(backend)
- .use(initReactI18next) // bind react-i18next to the instance
- .init({
- fallbackLng: false,
- lowerCaseLng: true,
-
- debug: process.env.NODE_ENV === "development",
-
- interpolation: {
- escapeValue: false, // not needed for react!!
- },
-
- // react i18next special options (optional)
- // override if needed - omit if ok with defaults
- /*
- react: {
- bindI18n: 'languageChanged',
- bindI18nStore: '',
- transEmptyNodeValue: '',
- transSupportBasicHtmlNodes: true,
- transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'],
- useSuspense: true,
- }
- */
- });
-
-if (module.hot) {
- module.hot.accept(
- ["./locales/en/translation", "./locales/zh/translation"],
- () => {
- void i18n.reloadResources();
- }
- );
-}
-
-export default i18n;
diff --git a/Timeline/ClientApp/src/app/index.ejs b/Timeline/ClientApp/src/app/index.ejs
deleted file mode 100644
index 49306786..00000000
--- a/Timeline/ClientApp/src/app/index.ejs
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="utf-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-
- <link rel="icon" href="/favicon.ico" />
- <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
- <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
- <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
- <link rel="manifest" href="/site.webmanifest" />
- <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
- <meta name="msapplication-TileColor" content="#2d89ef" />
- <meta name="theme-color" content="#ffffff" />
-
- <title><%= htmlWebpackPlugin.options.title %></title>
- </head>
- <body>
- <noscript>
- <strong>
- We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
- properly without JavaScript enabled. Please enable it to continue.
- </strong>
- </noscript>
- <div id="app"></div>
- <!-- built files will be auto injected -->
- </body>
-</html>
diff --git a/Timeline/ClientApp/src/app/index.sass b/Timeline/ClientApp/src/app/index.sass
deleted file mode 100644
index 08e03bac..00000000
--- a/Timeline/ClientApp/src/app/index.sass
+++ /dev/null
@@ -1,66 +0,0 @@
-@import '~bootstrap/scss/bootstrap'
-
-@import './views/common/common'
-@import './views/common/alert/alert'
-@import './views/home/home'
-@import './views/about/about'
-@import './views/login/login'
-@import './views/timeline-common/timeline-common'
-@import './views/timeline/timeline'
-@import './views/user/user'
-
-body
- margin: 0
-
-small
- line-height: 1.2
-
-.flex-fix-length
- flex-grow: 0
- flex-shrink: 0
-
-.position-lt
- left: 0
- top: 0
-
-.avatar
- width: 60px
- &.large
- width: 100px
- &.small
- width: 40px
-
-.mt-appbar
- margin-top: 56px
-
-.icon-button
- font-size: 1.4em
- &.large
- font-size: 1.6em
-
-.cursor-pointer
- cursor: pointer
-
-textarea
- resize: none
-
-.white-space-no-wrap
- white-space: nowrap
-
-.cru-card
- @extend .shadow
- @extend .border
- @extend .border-primary
- @extend .rounded
- @extend .bg-light
-
-.full-viewport-center-child
- position: fixed
- width: 100vw
- height: 100vh
- display: flex
- justify-content: center
- align-items: center
-
-.text-orange
- color: $orange
diff --git a/Timeline/ClientApp/src/app/index.tsx b/Timeline/ClientApp/src/app/index.tsx
deleted file mode 100644
index 00a75a4a..00000000
--- a/Timeline/ClientApp/src/app/index.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import "regenerator-runtime";
-import "core-js/modules/es.promise";
-import "core-js/modules/es.array.iterator";
-import "pepjs";
-
-import React from "react";
-import ReactDOM from "react-dom";
-
-import "./index.sass";
-
-import "./i18n";
-
-import App from "./App";
-
-ReactDOM.render(<App />, document.getElementById("app"));
diff --git a/Timeline/ClientApp/src/app/locales/en/translation.ts b/Timeline/ClientApp/src/app/locales/en/translation.ts
deleted file mode 100644
index c7f33d1e..00000000
--- a/Timeline/ClientApp/src/app/locales/en/translation.ts
+++ /dev/null
@@ -1,202 +0,0 @@
-import TranslationResource from "../scheme";
-
-const translation: TranslationResource = {
- welcome: "Welcome!",
- search: "Search",
- loadFailReload: "Load failed, click <1>here</1> to reload.",
- serviceWorker: {
- availableOffline:
- "Timeline is now cached in your computer and you can use it offline. 🎉🎉🎉",
- upgradePrompt: "App is getting a new version!",
- upgradeNow: "Update Now",
- upgradeSuccess:
- "Congratulations! App update succeeded! Still you can use it offline. 🎉🎉🎉",
- externalActivatedPrompt:
- "A new version of app is activated. Please refresh the page. Or it may be broken.",
- reloadNow: "Refresh Now",
- },
- nav: {
- settings: "Settings",
- login: "Login",
- about: "About",
- },
- chooseImage: "Choose a image",
- loadImageError: "Failed to load image.",
- home: {
- go: "Go!",
- allTimeline: "All Timelines",
- joinTimeline: "Joined Timelines",
- ownTimeline: "Owned Timelines",
- offlinePrompt:
- "Oh oh, it seems you are offline. Here list some timelines cached locally. You can view them or click <1>here</1> to refresh.",
- createButton: "Create Timeline",
- createDialog: {
- title: "Create Timeline!",
- name: "Name",
- nameFormat:
- "Name must consist of only letter including non-English letter, digit, hyphen(-) and underline(_) and be no longer than 26.",
- badFormat: "Bad format.",
- noEmpty: "Empty is not allowed.",
- tooLong: "Too long.",
- },
- },
- operationDialog: {
- retry: "Retry",
- nextStep: "Next",
- previousStep: "Previous",
- confirm: "Confirm",
- cancel: "Cancel",
- ok: "OK!",
- processing: "Processing...",
- success: "Success!",
- error: "An error occured.",
- },
- timeline: {
- messageCantSee: "Sorry, you are not allowed to see this timeline.😅",
- userNotExist: "The user does not exist!",
- timelineNotExist: "The timeline does not exist!",
- manage: "Manage",
- memberButton: "Member",
- send: "Send",
- deletePostFailed: "Failed to delete post.",
- sendPostFailed: "Failed to send post.",
- visibility: {
- public: "public to everyone",
- register: "only registed people can see",
- private: "only members can see",
- },
- visibilityTooltip: {
- public:
- "Everyone including those without accounts can see content of the timeline.",
- register:
- "Only those who have an account and logined can see content of the timeline.",
- private: "Only members of this timeline can see content of the timeline.",
- },
- dialogChangeProperty: {
- title: "Change Timeline Properties",
- visibility: "Visibility",
- description: "Description",
- },
- member: {
- alreadyMember: "The user is already a member.",
- add: "Add",
- remove: "Remove",
- },
- manageItem: {
- nickname: "Nickname",
- avatar: "Avatar",
- property: "Timeline Property",
- member: "Timeline Member",
- delete: "Delete Timeline",
- },
- deleteDialog: {
- title: "Delete Timeline",
- inputPrompt:
- "This is a dangerous action. If you are sure to delete timeline<1>{{name}}</1>, please input its name below and click confirm button.",
- notMatch: "Name does not match.",
- },
- postSyncState: {
- syncing: "Syncing",
- synced: "Synced",
- offline: "Offline",
- },
- post: {
- deleteDialog: {
- title: "Confirm Delete",
- prompt:
- "Are you sure to delete the post? This operation is not recoverable.",
- },
- },
- },
- user: {
- username: "username",
- password: "password",
- login: "login",
- rememberMe: "Remember Me",
- welcomeBack: "Welcome back!",
- verifyTokenFailed: "User login info is expired. Please login again!",
- verifyTokenFailedNetwork:
- "Verifying user login info failed. Please check your network and refresh page!",
- },
- login: {
- emptyUsername: "Username can't be empty.",
- emptyPassword: "Password can't be empty.",
- badCredential: "Username or password is invalid.",
- alreadyLogin: "Already login! Redirect to home page in 3s!",
- },
- userPage: {
- dialogChangeNickname: {
- title: "Change Nickname",
- inputLabel: "New nickname",
- },
- dialogChangeAvatar: {
- title: "Change Avatar",
- previewImgAlt: "preview",
- prompt: {
- select: "Please select a picture.",
- crop: "Please crop the picture.",
- processingCrop: "Cropping picture...",
- uploading: "Uploading...",
- preview: "Please preview avatar",
- },
- upload: "upload",
- },
- },
- settings: {
- subheaders: {
- account: "Account",
- customization: "Customization",
- },
- languagePrimary: "Choose display language.",
- languageSecondary:
- "You language preference will be saved locally. Next time you visit this page, last language option will be used.",
- changePassword: "Change account's password.",
- logout: "Log out this account.",
- gotoSelf:
- "Click here to go to timeline of myself to change nickname and avatar.",
- dialogChangePassword: {
- title: "Change Password",
- prompt:
- "You are changing your password. You need to input the correct old password. After change, you need to login again and all old login will be invalid.",
- inputOldPassword: "Old password",
- inputNewPassword: "New password",
- inputRetypeNewPassword: "Retype new password",
- errorEmptyOldPassword: "Old password can't be empty.",
- errorEmptyNewPassword: "New password can't be empty.",
- errorRetypeNotMatch: "Password retyped does not match.",
- },
- dialogConfirmLogout: {
- title: "Confirm Logout",
- prompt:
- "Are you sure to log out? All cached data in the browser will be deleted.",
- },
- },
- about: {
- author: {
- title: "Site Developer",
- fullname: "Fullname: ",
- nickname: "Nickname: ",
- introduction: "Introduction: ",
- introductionContent: "A programmer coding based on coincidence",
- links: "Links: ",
- },
- site: {
- title: "Site Information",
- content:
- "The name of this site is <1>Timeline</1>, which is a Web App with <3>timeline</3> as its core concept. Its frontend and backend are both developed by <5>me</5>, and open source on GitHub. It is relatively easy to deploy it on your own server, which is also one of my goals. Welcome to comment anything in GitHub repository.",
- repo: "GitHub Repo",
- },
- credits: {
- title: "Credits",
- content:
- "Timeline is works standing on shoulders of gaints. Special appreciation for many open source projects listed below or not. Related licenses could be found in GitHub repository.",
- frontend: "Frontend: ",
- backend: "Backend: ",
- },
- },
- admin: {
- title: "admin",
- },
-};
-
-export default translation;
diff --git a/Timeline/ClientApp/src/app/locales/scheme.ts b/Timeline/ClientApp/src/app/locales/scheme.ts
deleted file mode 100644
index 9e3534ac..00000000
--- a/Timeline/ClientApp/src/app/locales/scheme.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-export default interface TranslationResource {
- welcome: string;
- search: string;
- chooseImage: string;
- loadImageError: string;
- loadFailReload: string;
- serviceWorker: {
- availableOffline: string;
- upgradePrompt: string;
- upgradeNow: string;
- upgradeSuccess: string;
- externalActivatedPrompt: string;
- reloadNow: string;
- };
- nav: {
- settings: string;
- login: string;
- about: string;
- };
- home: {
- go: string;
- allTimeline: string;
- joinTimeline: string;
- ownTimeline: string;
- offlinePrompt: string;
- createButton: string;
- createDialog: {
- title: string;
- name: string;
- nameFormat: string;
- badFormat: string;
- noEmpty: string;
- tooLong: string;
- };
- };
- operationDialog: {
- retry: string;
- nextStep: string;
- previousStep: string;
- confirm: string;
- cancel: string;
- ok: string;
- processing: string;
- success: string;
- error: string;
- };
- timeline: {
- messageCantSee: string;
- userNotExist: string;
- timelineNotExist: string;
- manage: string;
- memberButton: string;
- send: string;
- deletePostFailed: string;
- sendPostFailed: string;
- visibility: {
- public: string;
- register: string;
- private: string;
- };
- visibilityTooltip: {
- public: string;
- register: string;
- private: string;
- };
- dialogChangeProperty: {
- title: string;
- visibility: string;
- description: string;
- };
- member: {
- alreadyMember: string;
- add: string;
- remove: string;
- };
- manageItem: {
- nickname: string;
- avatar: string;
- property: string;
- member: string;
- delete: string;
- };
- deleteDialog: {
- title: string;
- inputPrompt: string;
- notMatch: string;
- };
- postSyncState: {
- syncing: string;
- synced: string;
- offline: string;
- };
- post: {
- deleteDialog: {
- title: string;
- prompt: string;
- };
- };
- };
- user: {
- username: string;
- password: string;
- login: string;
- rememberMe: string;
- welcomeBack: string;
- verifyTokenFailed: string;
- verifyTokenFailedNetwork: string;
- };
- login: {
- emptyUsername: string;
- emptyPassword: string;
- badCredential: string;
- alreadyLogin: string;
- };
- userPage: {
- dialogChangeNickname: {
- title: string;
- inputLabel: string;
- };
- dialogChangeAvatar: {
- title: string;
- previewImgAlt: string;
- prompt: {
- select: string;
- crop: string;
- processingCrop: string;
- preview: string;
- uploading: string;
- };
- upload: string;
- };
- };
- settings: {
- subheaders: {
- account: string;
- customization: string;
- };
- languagePrimary: string;
- languageSecondary: string;
- changePassword: string;
- logout: string;
- gotoSelf: string;
- dialogChangePassword: {
- title: string;
- prompt: string;
- inputOldPassword: string;
- inputNewPassword: string;
- inputRetypeNewPassword: string;
- errorEmptyOldPassword: string;
- errorEmptyNewPassword: string;
- errorRetypeNotMatch: string;
- };
- dialogConfirmLogout: {
- title: string;
- prompt: string;
- };
- };
- about: {
- author: {
- title: string;
- fullname: string;
- nickname: string;
- introduction: string;
- introductionContent: string;
- links: string;
- };
- site: {
- title: string;
- content: string;
- repo: string;
- };
- credits: {
- title: string;
- content: string;
- frontend: string;
- backend: string;
- };
- };
- admin: {
- title: string;
- };
-}
diff --git a/Timeline/ClientApp/src/app/locales/zh/translation.ts b/Timeline/ClientApp/src/app/locales/zh/translation.ts
deleted file mode 100644
index df316366..00000000
--- a/Timeline/ClientApp/src/app/locales/zh/translation.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-import TranslationResource from "../scheme";
-
-const translation: TranslationResource = {
- welcome: "欢迎!",
- search: "搜索",
- loadFailReload: "加载失败,<1>点击</1>重试。",
- serviceWorker: {
- availableOffline: "Timeline 已经缓存在本地,你可以离线使用它。🎉🎉🎉",
- upgradePrompt: "App 有新版本!",
- upgradeNow: "现在升级",
- upgradeSuccess: "App 升级成功,当然,你仍可以离线使用它。 🎉🎉🎉",
- externalActivatedPrompt:
- "一个新的 App 版本已经激活,请刷新页面使用,否则页面可能会出现故障。",
- reloadNow: "立刻刷新",
- },
- nav: {
- settings: "设置",
- login: "登陆",
- about: "关于",
- },
- chooseImage: "选择一个图片",
- loadImageError: "加载图片失败",
- home: {
- go: "冲!",
- allTimeline: "所有的时间线",
- joinTimeline: "加入的时间线",
- ownTimeline: "拥有的时间线",
- offlinePrompt:
- "你好像处于离线状态。以下是一些缓存在本地的时间线。你可以查看它们或者<1>点击</1>重新获取在线信息。",
- createButton: "创建时间线",
- createDialog: {
- title: "创建时间线!",
- name: "名字",
- nameFormat:
- "名字只能由字母、汉字、数字、下划线(_)和连字符(-)构成,且长度不能超过26.",
- badFormat: "格式错误",
- noEmpty: "不能为空",
- tooLong: "太长了",
- },
- },
- operationDialog: {
- retry: "重试",
- nextStep: "下一步",
- previousStep: "上一步",
- confirm: "确定",
- cancel: "取消",
- ok: "好的!",
- processing: "处理中...",
- success: "成功!",
- error: "出错啦!",
- },
- timeline: {
- messageCantSee: "不好意思,你没有权限查看这个时间线。😅",
- userNotExist: "该用户不存在!",
- timelineNotExist: "该时间线不存在!",
- manage: "管理",
- memberButton: "成员",
- send: "发送",
- deletePostFailed: "删除消息失败。",
- sendPostFailed: "发送消息失败。",
- visibility: {
- public: "对所有人公开",
- register: "仅注册可见",
- private: "仅成员可见",
- },
- visibilityTooltip: {
- public: "所有人都可以看到这个时间线的内容,包括没有注册的人。",
- register: "只有拥有本网站的账号且登陆了的人才能看到这个时间线的内容。",
- private: "只有这个时间线的成员可以看到这个时间线的内容。",
- },
- dialogChangeProperty: {
- title: "修改时间线属性",
- visibility: "可见性",
- description: "描述",
- },
- member: {
- alreadyMember: "该用户已经是一个成员。",
- add: "添加",
- remove: "移除",
- },
- manageItem: {
- nickname: "昵称",
- avatar: "头像",
- property: "时间线属性",
- member: "时间线成员",
- delete: "删除时间线",
- },
- deleteDialog: {
- title: "删除时间线",
- inputPrompt:
- "这是一个危险的操作。如果您确认要删除时间线<1>{{name}}</1>,请在下面输入它的名字并点击确认。",
- notMatch: "名字不匹配",
- },
- postSyncState: {
- syncing: "同步中",
- synced: "同步成功",
- offline: "离线",
- },
- post: {
- deleteDialog: {
- title: "确认删除",
- prompt: "确定删除这个消息?这个操作不可撤销。",
- },
- },
- },
- user: {
- username: "用户名",
- password: "密码",
- login: "登录",
- rememberMe: "记住我",
- welcomeBack: "欢迎回来!",
- verifyTokenFailed: "用户登录信息已过期,请重新登陆!",
- verifyTokenFailedNetwork:
- "验证用户登录信息失败,请检查网络连接并刷新页面!",
- },
- login: {
- emptyUsername: "用户名不能为空。",
- emptyPassword: "密码不能为空。",
- badCredential: "用户名或密码错误。",
- alreadyLogin: "已经登陆,三秒后导航到首页!",
- },
- userPage: {
- dialogChangeNickname: {
- title: "更改昵称",
- inputLabel: "新昵称",
- },
- dialogChangeAvatar: {
- title: "修改头像",
- previewImgAlt: "预览",
- prompt: {
- select: "请选择一个图片",
- crop: "请裁剪图片",
- processingCrop: "正在裁剪图片",
- uploading: "正在上传",
- preview: "请预览图片",
- },
- upload: "上传",
- },
- },
- settings: {
- subheaders: {
- account: "账户",
- customization: "个性化",
- },
- languagePrimary: "选择显示的语言。",
- languageSecondary:
- "您的语言偏好将会存储在本地,下次浏览时将自动使用上次保存的语言选项。",
- changePassword: "更改账号的密码。",
- logout: "注销此账号。",
- gotoSelf: "点击前往个人时间线修改昵称和头像!",
- dialogChangePassword: {
- title: "修改密码",
- prompt:
- "您正在修改密码,您需要输入正确的旧密码。成功修改后您需要重新登陆,而且以前所有的登录都会失效。",
- inputOldPassword: "旧密码",
- inputNewPassword: "新密码",
- inputRetypeNewPassword: "再次输入新密码",
- errorEmptyOldPassword: "旧密码不能为空。",
- errorEmptyNewPassword: "新密码不能为空",
- errorRetypeNotMatch: "两次输入的密码不一致",
- },
- dialogConfirmLogout: {
- title: "确定注销",
- prompt: "您确定注销此账号?这将删除所有已经缓存在浏览器的数据。",
- },
- },
- about: {
- author: {
- title: "网站作者",
- fullname: "姓名:",
- nickname: "昵称:",
- introduction: "简介:",
- introductionContent: "一个基于巧合编程的代码爱好者。",
- links: "链接:",
- },
- site: {
- title: "网站信息",
- content:
- "这个网站的名字叫 <1>Timeline</1>,是一个以<3>时间线</3>为核心概念的 Web App . 它的前端和后端都是由<5>我</5>开发,并且在 GitHub 上开源。大家可以相对轻松的把它们部署在自己的服务器上,这也是我的目标之一。欢迎大家前往 GitHub 仓库提出任何意见。",
- repo: "GitHub 仓库",
- },
- credits: {
- title: "鸣谢",
- content:
- "Timeline 是站在巨人肩膀上的作品,感谢以下列出的和其他未列出的许多开源项目,相关 License 请在 GitHub 仓库中查看。",
- frontend: "前端:",
- backend: "后端:",
- },
- },
- admin: {
- title: "管理",
- },
-};
-
-export default translation;
diff --git a/Timeline/ClientApp/src/app/service-worker.tsx b/Timeline/ClientApp/src/app/service-worker.tsx
deleted file mode 100644
index 3be54bc1..00000000
--- a/Timeline/ClientApp/src/app/service-worker.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { Button } from "react-bootstrap";
-
-import { pushAlert } from "./services/alert";
-
-if ("serviceWorker" in navigator) {
- let isThisTriggerUpgrade = false;
-
- const upgradeSuccessLocalStorageKey = "TIMELINE_UPGRADE_SUCCESS";
-
- if (window.localStorage.getItem(upgradeSuccessLocalStorageKey)) {
- pushAlert({
- message: {
- type: "i18n",
- key: "serviceWorker.upgradeSuccess",
- },
- type: "success",
- });
- window.localStorage.removeItem(upgradeSuccessLocalStorageKey);
- }
-
- void import("workbox-window").then(({ Workbox, messageSW }) => {
- const wb = new Workbox("/sw.js");
- let registration: ServiceWorkerRegistration | undefined;
-
- // externalactivated is not usable but I still use its name.
- wb.addEventListener("controlling", () => {
- const upgradeReload = (): void => {
- window.localStorage.setItem(upgradeSuccessLocalStorageKey, "true");
- window.location.reload();
- };
-
- if (isThisTriggerUpgrade) {
- upgradeReload();
- } else {
- const Message: React.FC = () => {
- const { t } = useTranslation();
- return (
- <>
- {t("serviceWorker.externalActivatedPrompt")}
- <Button
- variant="outline-success"
- size="sm"
- onClick={upgradeReload}
- >
- {t("serviceWorker.reloadNow")}
- </Button>
- </>
- );
- };
-
- pushAlert({
- message: Message,
- dismissTime: "never",
- type: "warning",
- });
- }
- });
-
- wb.addEventListener("activated", (event) => {
- if (!event.isUpdate) {
- pushAlert({
- message: {
- type: "i18n",
- key: "serviceWorker.availableOffline",
- },
- type: "success",
- });
- }
- });
-
- const showSkipWaitingPrompt = (): void => {
- const upgrade = (): void => {
- isThisTriggerUpgrade = true;
- if (registration && registration.waiting) {
- // Send a message to the waiting service worker,
- // instructing it to activate.
- // Note: for this to work, you have to add a message
- // listener in your service worker. See below.
- void messageSW(registration.waiting, { type: "SKIP_WAITING" });
- }
- };
-
- const UpgradeMessage: React.FC = () => {
- const { t } = useTranslation();
- return (
- <>
- {t("serviceWorker.upgradePrompt")}
- <Button variant="outline-success" size="sm" onClick={upgrade}>
- {t("serviceWorker.upgradeNow")}
- </Button>
- </>
- );
- };
-
- pushAlert({
- message: UpgradeMessage,
- dismissTime: "never",
- type: "success",
- });
- };
-
- // Add an event listener to detect when the registered
- // service worker has installed but is waiting to activate.
- wb.addEventListener("waiting", showSkipWaitingPrompt);
- wb.addEventListener("externalwaiting", showSkipWaitingPrompt);
-
- void wb.register().then((reg) => {
- registration = reg;
- });
- });
-}
diff --git a/Timeline/ClientApp/src/app/services/DataHub.ts b/Timeline/ClientApp/src/app/services/DataHub.ts
deleted file mode 100644
index 93a9b41f..00000000
--- a/Timeline/ClientApp/src/app/services/DataHub.ts
+++ /dev/null
@@ -1,225 +0,0 @@
-import { pull } from "lodash";
-import { Observable, BehaviorSubject, combineLatest } from "rxjs";
-import { map } from "rxjs/operators";
-
-export type Subscriber<TData> = (data: TData) => void;
-
-export type WithSyncStatus<T> = T & { syncing: boolean };
-
-export class DataLine<TData> {
- private _current: TData | undefined = undefined;
-
- private _syncPromise: Promise<void> | null = null;
- private _syncingSubject = new BehaviorSubject<boolean>(false);
-
- private _observers: Subscriber<TData>[] = [];
-
- constructor(
- private config: {
- sync: () => Promise<void>;
- destroyable?: (value: TData | undefined) => boolean;
- disableInitSync?: boolean;
- }
- ) {
- if (config.disableInitSync !== true) {
- setImmediate(() => void this.sync());
- }
- }
-
- private subscribe(subscriber: Subscriber<TData>): void {
- this._observers.push(subscriber);
- if (this._current !== undefined) {
- subscriber(this._current);
- }
- }
-
- private unsubscribe(subscriber: Subscriber<TData>): void {
- if (!this._observers.includes(subscriber)) return;
- pull(this._observers, subscriber);
- }
-
- getObservable(): Observable<TData> {
- return new Observable<TData>((observer) => {
- const f = (data: TData): void => {
- observer.next(data);
- };
- this.subscribe(f);
-
- return () => {
- this.unsubscribe(f);
- };
- });
- }
-
- getSyncStatusObservable(): Observable<boolean> {
- return this._syncingSubject.asObservable();
- }
-
- getDataWithSyncStatusObservable(): Observable<WithSyncStatus<TData>> {
- return combineLatest([
- this.getObservable(),
- this.getSyncStatusObservable(),
- ]).pipe(
- map(([data, syncing]) => ({
- ...data,
- syncing,
- }))
- );
- }
-
- get value(): TData | undefined {
- return this._current;
- }
-
- next(value: TData): void {
- this._current = value;
- this._observers.forEach((observer) => observer(value));
- }
-
- get isSyncing(): boolean {
- return this._syncPromise != null;
- }
-
- sync(): Promise<void> {
- if (this._syncPromise == null) {
- this._syncingSubject.next(true);
- this._syncPromise = this.config.sync().then(() => {
- this._syncingSubject.next(false);
- this._syncPromise = null;
- });
- }
-
- return this._syncPromise;
- }
-
- syncWithAction(
- syncAction: (line: DataLine<TData>) => Promise<void>
- ): Promise<void> {
- if (this._syncPromise == null) {
- this._syncingSubject.next(true);
- this._syncPromise = syncAction(this).then(() => {
- this._syncingSubject.next(false);
- this._syncPromise = null;
- });
- }
-
- return this._syncPromise;
- }
-
- get destroyable(): boolean {
- const customDestroyable = this.config?.destroyable;
-
- return (
- this._observers.length === 0 &&
- !this.isSyncing &&
- (customDestroyable != null ? customDestroyable(this._current) : true)
- );
- }
-}
-
-export class DataHub<TKey, TData> {
- private sync: (key: TKey, line: DataLine<TData>) => Promise<void>;
- private keyToString: (key: TKey) => string;
- private destroyable?: (key: TKey, value: TData | undefined) => boolean;
-
- private readonly subscriptionLineMap = new Map<string, DataLine<TData>>();
-
- private cleanTimerId = 0;
-
- // setup is called after creating line and if it returns a function as destroyer, then when the line is destroyed the destroyer will be called.
- constructor(config: {
- sync: (key: TKey, line: DataLine<TData>) => Promise<void>;
- keyToString?: (key: TKey) => string;
- destroyable?: (key: TKey, value: TData | undefined) => boolean;
- }) {
- this.sync = config.sync;
- this.keyToString =
- config.keyToString ??
- ((value): string => {
- if (typeof value === "string") return value;
- else
- throw new Error(
- "Default keyToString function only pass string value."
- );
- });
-
- this.destroyable = config.destroyable;
- }
-
- private cleanLines(): void {
- const toDelete: string[] = [];
- for (const [key, line] of this.subscriptionLineMap.entries()) {
- if (line.destroyable) {
- toDelete.push(key);
- }
- }
-
- if (toDelete.length === 0) return;
-
- for (const key of toDelete) {
- this.subscriptionLineMap.delete(key);
- }
-
- if (this.subscriptionLineMap.size === 0) {
- window.clearInterval(this.cleanTimerId);
- this.cleanTimerId = 0;
- }
- }
-
- private createLine(key: TKey, disableInitSync = false): DataLine<TData> {
- const keyString = this.keyToString(key);
- const { destroyable } = this;
- const newLine: DataLine<TData> = new DataLine<TData>({
- sync: () => this.sync(key, newLine),
- destroyable:
- destroyable != null ? (value) => destroyable(key, value) : undefined,
- disableInitSync: disableInitSync,
- });
- this.subscriptionLineMap.set(keyString, newLine);
- if (this.subscriptionLineMap.size === 1) {
- this.cleanTimerId = window.setInterval(this.cleanLines.bind(this), 20000);
- }
- return newLine;
- }
-
- getObservable(key: TKey): Observable<TData> {
- return this.getLineOrCreate(key).getObservable();
- }
-
- getSyncStatusObservable(key: TKey): Observable<boolean> {
- return this.getLineOrCreate(key).getSyncStatusObservable();
- }
-
- getDataWithSyncStatusObservable(
- key: TKey
- ): Observable<WithSyncStatus<TData>> {
- return this.getLineOrCreate(key).getDataWithSyncStatusObservable();
- }
-
- getLine(key: TKey): DataLine<TData> | null {
- const keyString = this.keyToString(key);
- return this.subscriptionLineMap.get(keyString) ?? null;
- }
-
- getLineOrCreate(key: TKey): DataLine<TData> {
- const keyString = this.keyToString(key);
- return this.subscriptionLineMap.get(keyString) ?? this.createLine(key);
- }
-
- getLineOrCreateWithoutInitSync(key: TKey): DataLine<TData> {
- const keyString = this.keyToString(key);
- return (
- this.subscriptionLineMap.get(keyString) ?? this.createLine(key, true)
- );
- }
-
- optionalInitLineWithSyncAction(
- key: TKey,
- syncAction: (line: DataLine<TData>) => Promise<void>
- ): Promise<void> {
- const optionalLine = this.getLine(key);
- if (optionalLine != null) return Promise.resolve();
- const line = this.createLine(key, true);
- return line.syncWithAction(syncAction);
- }
-}
diff --git a/Timeline/ClientApp/src/app/services/alert.ts b/Timeline/ClientApp/src/app/services/alert.ts
deleted file mode 100644
index e4c0e653..00000000
--- a/Timeline/ClientApp/src/app/services/alert.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from "react";
-import pull from "lodash/pull";
-
-export interface AlertInfo {
- type?: "primary" | "secondary" | "success" | "danger" | "warning" | "info";
- message: string | React.FC<unknown> | { type: "i18n"; key: string };
- dismissTime?: number | "never";
-}
-
-export interface AlertInfoEx extends AlertInfo {
- id: number;
-}
-
-export type AlertConsumer = (alerts: AlertInfoEx) => void;
-
-export class AlertService {
- private consumers: AlertConsumer[] = [];
- private savedAlerts: AlertInfoEx[] = [];
- private currentId = 1;
-
- private produce(alert: AlertInfoEx): void {
- for (const consumer of this.consumers) {
- consumer(alert);
- }
- }
-
- registerConsumer(consumer: AlertConsumer): void {
- this.consumers.push(consumer);
- if (this.savedAlerts.length !== 0) {
- for (const alert of this.savedAlerts) {
- this.produce(alert);
- }
- this.savedAlerts = [];
- }
- }
-
- unregisterConsumer(consumer: AlertConsumer): void {
- pull(this.consumers, consumer);
- }
-
- push(alert: AlertInfo): void {
- const newAlert: AlertInfoEx = { ...alert, id: this.currentId++ };
- if (this.consumers.length === 0) {
- this.savedAlerts.push(newAlert);
- } else {
- this.produce(newAlert);
- }
- }
-}
-
-export const alertService = new AlertService();
-
-export function pushAlert(alert: AlertInfo): void {
- alertService.push(alert);
-}
-
-export const kAlertHostId = "alert-host";
-
-export function getAlertHost(): HTMLElement | null {
- return document.getElementById(kAlertHostId);
-}
diff --git a/Timeline/ClientApp/src/app/services/common.ts b/Timeline/ClientApp/src/app/services/common.ts
deleted file mode 100644
index 3bb6b9d7..00000000
--- a/Timeline/ClientApp/src/app/services/common.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import localforage from "localforage";
-
-import { HttpNetworkError } from "@/http/common";
-
-export const dataStorage = localforage.createInstance({
- name: "data",
- description: "Database for offline data.",
- driver: localforage.INDEXEDDB,
-});
-
-export class ForbiddenError extends Error {
- constructor(message?: string) {
- super(message);
- }
-}
-
-export function throwIfNotNetworkError(e: unknown): void {
- if (!(e instanceof HttpNetworkError)) {
- throw e;
- }
-}
-
-export type BlobOrStatus = Blob | "loading" | "error";
diff --git a/Timeline/ClientApp/src/app/services/timeline.ts b/Timeline/ClientApp/src/app/services/timeline.ts
deleted file mode 100644
index 9db76281..00000000
--- a/Timeline/ClientApp/src/app/services/timeline.ts
+++ /dev/null
@@ -1,702 +0,0 @@
-import React from "react";
-import XRegExp from "xregexp";
-import { Observable, from, combineLatest, of } from "rxjs";
-import { map, switchMap, startWith } from "rxjs/operators";
-import { uniqBy } from "lodash";
-
-import { convertError } from "@/utilities/rxjs";
-import {
- TimelineVisibility,
- HttpTimelineInfo,
- HttpTimelinePatchRequest,
- HttpTimelinePostPostRequest,
- HttpTimelinePostPostRequestContent,
- HttpTimelinePostPostRequestTextContent,
- HttpTimelinePostPostRequestImageContent,
- HttpTimelinePostInfo,
- HttpTimelinePostTextContent,
- getHttpTimelineClient,
- HttpTimelineNotExistError,
- HttpTimelineNameConflictError,
-} from "@/http/timeline";
-import { BlobWithEtag, NotModified, HttpForbiddenError } from "@/http/common";
-import { HttpUser } from "@/http/user";
-
-export { kTimelineVisibilities } from "@/http/timeline";
-
-export type { TimelineVisibility } from "@/http/timeline";
-
-import { dataStorage, throwIfNotNetworkError, BlobOrStatus } from "./common";
-import { DataHub, WithSyncStatus } from "./DataHub";
-import { UserAuthInfo, checkLogin, userService, userInfoService } from "./user";
-
-export type TimelineInfo = HttpTimelineInfo;
-export type TimelineChangePropertyRequest = HttpTimelinePatchRequest;
-export type TimelineCreatePostRequest = HttpTimelinePostPostRequest;
-export type TimelineCreatePostContent = HttpTimelinePostPostRequestContent;
-export type TimelineCreatePostTextContent = HttpTimelinePostPostRequestTextContent;
-export type TimelineCreatePostImageContent = HttpTimelinePostPostRequestImageContent;
-
-export type TimelinePostTextContent = HttpTimelinePostTextContent;
-
-export interface TimelinePostImageContent {
- type: "image";
- data: BlobOrStatus;
-}
-
-export type TimelinePostContent =
- | TimelinePostTextContent
- | TimelinePostImageContent;
-
-export interface TimelinePostInfo {
- id: number;
- content: TimelinePostContent;
- time: Date;
- lastUpdated: Date;
- author: HttpUser;
-}
-
-export const timelineVisibilityTooltipTranslationMap: Record<
- TimelineVisibility,
- string
-> = {
- Public: "timeline.visibilityTooltip.public",
- Register: "timeline.visibilityTooltip.register",
- Private: "timeline.visibilityTooltip.private",
-};
-
-export class TimelineNotExistError extends Error {}
-export class TimelineNameConflictError extends Error {}
-
-export type TimelineWithSyncStatus = WithSyncStatus<
- | {
- type: "cache";
- timeline: TimelineInfo;
- }
- | {
- type: "offline" | "synced";
- timeline: TimelineInfo | null;
- }
->;
-
-export type TimelinePostsWithSyncState = WithSyncStatus<{
- type:
- | "cache"
- | "offline" // Sync failed and use cache.
- | "synced" // Sync succeeded.
- | "forbid" // The list is forbidden to see.
- | "notexist"; // The timeline does not exist.
- posts: TimelinePostInfo[];
-}>;
-
-type TimelineData = Omit<HttpTimelineInfo, "owner" | "members"> & {
- owner: string;
- members: string[];
-};
-
-type TimelinePostData = Omit<HttpTimelinePostInfo, "author"> & {
- author: string;
-};
-
-export class TimelineService {
- private getCachedTimeline(
- timelineName: string
- ): Promise<TimelineData | null> {
- return dataStorage.getItem<TimelineData | null>(`timeline.${timelineName}`);
- }
-
- private saveTimeline(
- timelineName: string,
- data: TimelineData
- ): Promise<void> {
- return dataStorage
- .setItem<TimelineData>(`timeline.${timelineName}`, data)
- .then();
- }
-
- private async clearTimelineData(timelineName: string): Promise<void> {
- const keys = (await dataStorage.keys()).filter((k) =>
- k.startsWith(`timeline.${timelineName}`)
- );
- await Promise.all(keys.map((k) => dataStorage.removeItem(k)));
- }
-
- private convertHttpTimelineToData(timeline: HttpTimelineInfo): TimelineData {
- return {
- ...timeline,
- owner: timeline.owner.username,
- members: timeline.members.map((m) => m.username),
- };
- }
-
- private _timelineHub = new DataHub<
- string,
- | {
- type: "cache";
- timeline: TimelineData;
- }
- | {
- type: "offline" | "synced";
- timeline: TimelineData | null;
- }
- >({
- sync: async (key, line) => {
- const cache = await this.getCachedTimeline(key);
-
- if (line.value == undefined) {
- if (cache != null) {
- line.next({ type: "cache", timeline: cache });
- }
- }
-
- try {
- const httpTimeline = await getHttpTimelineClient().getTimeline(key);
-
- userInfoService.saveUsers([
- httpTimeline.owner,
- ...httpTimeline.members,
- ]);
-
- const timeline = this.convertHttpTimelineToData(httpTimeline);
-
- if (cache != null && timeline.uniqueId !== cache.uniqueId) {
- console.log(
- `Timeline with name ${key} has changed to a new one. Clear old data.`
- );
- await this.clearTimelineData(key); // If timeline has changed, clear all old data.
- }
-
- await this.saveTimeline(key, timeline);
-
- line.next({ type: "synced", timeline });
- } catch (e) {
- if (e instanceof HttpTimelineNotExistError) {
- line.next({ type: "synced", timeline: null });
- } else {
- if (cache == null) {
- line.next({ type: "offline", timeline: null });
- } else {
- line.next({ type: "offline", timeline: cache });
- }
- throwIfNotNetworkError(e);
- }
- }
- },
- });
-
- syncTimeline(timelineName: string): Promise<void> {
- return this._timelineHub.getLineOrCreate(timelineName).sync();
- }
-
- getTimeline$(timelineName: string): Observable<TimelineWithSyncStatus> {
- return this._timelineHub.getDataWithSyncStatusObservable(timelineName).pipe(
- switchMap((state) => {
- const { timeline } = state;
- if (timeline != null) {
- return combineLatest(
- [timeline.owner, ...timeline.members].map((u) =>
- userInfoService.getUser$(u)
- )
- ).pipe(
- map((users) => {
- return {
- ...state,
- timeline: {
- ...timeline,
- owner: users[0],
- members: users.slice(1),
- },
- };
- })
- );
- } else {
- return of(state as TimelineWithSyncStatus);
- }
- })
- );
- }
-
- createTimeline(timelineName: string): Observable<TimelineInfo> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient().postTimeline(
- {
- name: timelineName,
- },
- user.token
- )
- ).pipe(
- convertError(HttpTimelineNameConflictError, TimelineNameConflictError)
- );
- }
-
- changeTimelineProperty(
- timelineName: string,
- req: TimelineChangePropertyRequest
- ): Observable<TimelineInfo> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient()
- .patchTimeline(timelineName, req, user.token)
- .then((timeline) => {
- void this.syncTimeline(timelineName);
- return timeline;
- })
- );
- }
-
- deleteTimeline(timelineName: string): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient().deleteTimeline(timelineName, user.token)
- );
- }
-
- addMember(timelineName: string, username: string): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient()
- .memberPut(timelineName, username, user.token)
- .then(() => {
- void this.syncTimeline(timelineName);
- })
- );
- }
-
- removeMember(timelineName: string, username: string): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient()
- .memberDelete(timelineName, username, user.token)
- .then(() => {
- void this.syncTimeline(timelineName);
- })
- );
- }
-
- private convertHttpPostToData(post: HttpTimelinePostInfo): TimelinePostData {
- return {
- ...post,
- author: post.author.username,
- };
- }
-
- private convertHttpPostToDataList(
- posts: HttpTimelinePostInfo[]
- ): TimelinePostData[] {
- return posts.map((post) => this.convertHttpPostToData(post));
- }
-
- private getCachedPosts(
- timelineName: string
- ): Promise<TimelinePostData[] | null> {
- return dataStorage.getItem<TimelinePostData[] | null>(
- `timeline.${timelineName}.posts`
- );
- }
-
- private savePosts(
- timelineName: string,
- data: TimelinePostData[]
- ): Promise<void> {
- return dataStorage
- .setItem<TimelinePostData[]>(`timeline.${timelineName}.posts`, data)
- .then();
- }
-
- private syncPosts(timelineName: string): Promise<void> {
- return this._postsHub.getLineOrCreate(timelineName).sync();
- }
-
- private _postsHub = new DataHub<
- string,
- {
- type: "cache" | "offline" | "synced" | "forbid" | "notexist";
- posts: TimelinePostData[];
- }
- >({
- sync: async (key, line) => {
- // Wait for timeline synced. In case the timeline has changed to another and old data has been cleaned.
- await this.syncTimeline(key);
-
- if (line.value == null) {
- const cache = await this.getCachedPosts(key);
- if (cache != null) {
- line.next({ type: "cache", posts: cache });
- }
- }
-
- const now = new Date();
-
- const lastUpdatedTime = await dataStorage.getItem<Date | null>(
- `timeline.${key}.lastUpdated`
- );
-
- try {
- if (lastUpdatedTime == null) {
- const httpPosts = await getHttpTimelineClient().listPost(
- key,
- userService.currentUser?.token
- );
-
- userInfoService.saveUsers(
- uniqBy(
- httpPosts.map((post) => post.author),
- "username"
- )
- );
-
- const posts = this.convertHttpPostToDataList(httpPosts);
- await this.savePosts(key, posts);
- await dataStorage.setItem<Date>(`timeline.${key}.lastUpdated`, now);
-
- line.next({ type: "synced", posts });
- } else {
- const httpPosts = await getHttpTimelineClient().listPost(
- key,
- userService.currentUser?.token,
- {
- modifiedSince: lastUpdatedTime,
- includeDeleted: true,
- }
- );
-
- const deletedIds = httpPosts
- .filter((p) => p.deleted)
- .map((p) => p.id);
- const changed = httpPosts.filter(
- (p): p is HttpTimelinePostInfo => !p.deleted
- );
-
- userInfoService.saveUsers(
- uniqBy(
- httpPosts
- .map((post) => post.author)
- .filter((u): u is HttpUser => u != null),
- "username"
- )
- );
-
- const cache = (await this.getCachedPosts(key)) ?? [];
-
- const posts = cache.filter((p) => !deletedIds.includes(p.id));
-
- for (const changedPost of changed) {
- const savedChangedPostIndex = posts.findIndex(
- (p) => p.id === changedPost.id
- );
- if (savedChangedPostIndex === -1) {
- posts.push(this.convertHttpPostToData(changedPost));
- } else {
- posts[savedChangedPostIndex] = this.convertHttpPostToData(
- changedPost
- );
- }
- }
-
- await this.savePosts(key, posts);
- await dataStorage.setItem<Date>(`timeline.${key}.lastUpdated`, now);
- line.next({ type: "synced", posts });
- }
- } catch (e) {
- if (e instanceof HttpTimelineNotExistError) {
- line.next({ type: "notexist", posts: [] });
- } else if (e instanceof HttpForbiddenError) {
- line.next({ type: "forbid", posts: [] });
- } else {
- const cache = await this.getCachedPosts(key);
- if (cache == null) {
- line.next({ type: "offline", posts: [] });
- } else {
- line.next({ type: "offline", posts: cache });
- }
- throwIfNotNetworkError(e);
- }
- }
- },
- });
-
- getPosts$(timelineName: string): Observable<TimelinePostsWithSyncState> {
- return this._postsHub.getDataWithSyncStatusObservable(timelineName).pipe(
- switchMap((state) => {
- if (state.posts.length === 0) {
- return of({
- ...state,
- posts: [],
- });
- }
-
- return combineLatest([
- combineLatest(
- state.posts.map((post) => userInfoService.getUser$(post.author))
- ),
- combineLatest(
- state.posts.map((post) => {
- if (post.content.type === "image") {
- return this.getPostData$(timelineName, post.id);
- } else {
- return of(null);
- }
- })
- ),
- ]).pipe(
- map(([authors, datas]) => {
- return {
- ...state,
- posts: state.posts.map((post, i) => {
- const { content } = post;
-
- return {
- ...post,
- author: authors[i],
- content: (() => {
- if (content.type === "text") return content;
- else
- return {
- type: "image",
- data: datas[i],
- } as TimelinePostImageContent;
- })(),
- };
- }),
- };
- })
- );
- })
- );
- }
-
- private getCachedPostData(key: {
- timelineName: string;
- postId: number;
- }): Promise<BlobWithEtag | null> {
- return dataStorage.getItem<BlobWithEtag | null>(
- `timeline.${key.timelineName}.post.${key.postId}.data`
- );
- }
-
- private savePostData(
- key: {
- timelineName: string;
- postId: number;
- },
- data: BlobWithEtag
- ): Promise<void> {
- return dataStorage
- .setItem<BlobWithEtag>(
- `timeline.${key.timelineName}.post.${key.postId}.data`,
- data
- )
- .then();
- }
-
- private syncPostData(key: {
- timelineName: string;
- postId: number;
- }): Promise<void> {
- return this._postDataHub.getLineOrCreate(key).sync();
- }
-
- private _postDataHub = new DataHub<
- { timelineName: string; postId: number },
- | { data: Blob; type: "cache" | "synced" | "offline" }
- | { data?: undefined; type: "notexist" | "offline" }
- >({
- keyToString: (key) => `${key.timelineName}.${key.postId}`,
- sync: async (key, line) => {
- const cache = await this.getCachedPostData(key);
- if (line.value == null) {
- if (cache != null) {
- line.next({ type: "cache", data: cache.data });
- }
- }
-
- if (cache == null) {
- try {
- const res = await getHttpTimelineClient().getPostData(
- key.timelineName,
- key.postId
- );
- await this.savePostData(key, res);
- line.next({ data: res.data, type: "synced" });
- } catch (e) {
- line.next({ type: "offline" });
- throwIfNotNetworkError(e);
- }
- } else {
- try {
- const res = await getHttpTimelineClient().getPostData(
- key.timelineName,
- key.postId,
- cache.etag
- );
- if (res instanceof NotModified) {
- line.next({ data: cache.data, type: "synced" });
- } else {
- await this.savePostData(key, res);
- line.next({ data: res.data, type: "synced" });
- }
- } catch (e) {
- line.next({ data: cache.data, type: "offline" });
- throwIfNotNetworkError(e);
- }
- }
- },
- });
-
- getPostData$(timelineName: string, postId: number): Observable<BlobOrStatus> {
- return this._postDataHub.getObservable({ timelineName, postId }).pipe(
- map((state): BlobOrStatus => state.data ?? "error"),
- startWith("loading")
- );
- }
-
- createPost(
- timelineName: string,
- request: TimelineCreatePostRequest
- ): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient()
- .postPost(timelineName, request, user.token)
- .then(() => {
- void this.syncPosts(timelineName);
- })
- );
- }
-
- deletePost(timelineName: string, postId: number): Observable<unknown> {
- const user = checkLogin();
- return from(
- getHttpTimelineClient()
- .deletePost(timelineName, postId, user.token)
- .then(() => {
- void this.syncPosts(timelineName);
- })
- );
- }
-
- isMemberOf(username: string, timeline: TimelineInfo): boolean {
- return timeline.members.findIndex((m) => m.username == username) >= 0;
- }
-
- hasReadPermission(
- user: UserAuthInfo | null | undefined,
- timeline: TimelineInfo
- ): boolean {
- if (user != null && user.administrator) return true;
-
- const { visibility } = timeline;
- if (visibility === "Public") {
- return true;
- } else if (visibility === "Register") {
- if (user != null) return true;
- } else if (visibility === "Private") {
- if (
- user != null &&
- (user.username === timeline.owner.username ||
- this.isMemberOf(user.username, timeline))
- ) {
- return true;
- }
- }
- return false;
- }
-
- hasPostPermission(
- user: UserAuthInfo | null | undefined,
- timeline: TimelineInfo
- ): boolean {
- if (user != null && user.administrator) return true;
-
- return (
- user != null &&
- (timeline.owner.username === user.username ||
- this.isMemberOf(user.username, timeline))
- );
- }
-
- hasManagePermission(
- user: UserAuthInfo | null | undefined,
- timeline: TimelineInfo
- ): boolean {
- if (user != null && user.administrator) return true;
-
- return user != null && user.username == timeline.owner.username;
- }
-
- hasModifyPostPermission(
- user: UserAuthInfo | null | undefined,
- timeline: TimelineInfo,
- post: TimelinePostInfo
- ): boolean {
- if (user != null && user.administrator) return true;
-
- return (
- user != null &&
- (user.username === timeline.owner.username ||
- user.username === post.author.username)
- );
- }
-}
-
-export const timelineService = new TimelineService();
-
-const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u");
-
-export function validateTimelineName(name: string): boolean {
- return timelineNameReg.test(name);
-}
-
-export function useTimelineInfo(
- timelineName: string
-): TimelineWithSyncStatus | undefined {
- const [state, setState] = React.useState<TimelineWithSyncStatus | undefined>(
- undefined
- );
- React.useEffect(() => {
- const subscription = timelineService
- .getTimeline$(timelineName)
- .subscribe((data) => {
- setState(data);
- });
- return () => {
- subscription.unsubscribe();
- };
- }, [timelineName]);
- return state;
-}
-
-export function usePostList(
- timelineName: string | null | undefined
-): TimelinePostsWithSyncState | undefined {
- const [state, setState] = React.useState<
- TimelinePostsWithSyncState | undefined
- >(undefined);
- React.useEffect(() => {
- if (timelineName == null) {
- setState(undefined);
- return;
- }
-
- const subscription = timelineService
- .getPosts$(timelineName)
- .subscribe((data) => {
- setState(data);
- });
- return () => {
- subscription.unsubscribe();
- };
- }, [timelineName]);
- return state;
-}
-
-export async function getAllCachedTimelineNames(): Promise<string[]> {
- const keys = await dataStorage.keys();
- return keys
- .filter(
- (key) =>
- key.startsWith("timeline.") && (key.match(/\./g) ?? []).length === 1
- )
- .map((key) => key.substr("timeline.".length));
-}
diff --git a/Timeline/ClientApp/src/app/services/user.ts b/Timeline/ClientApp/src/app/services/user.ts
deleted file mode 100644
index f253fc19..00000000
--- a/Timeline/ClientApp/src/app/services/user.ts
+++ /dev/null
@@ -1,393 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { BehaviorSubject, Observable, from } from "rxjs";
-import { map, filter } from "rxjs/operators";
-
-import { UiLogicError } from "@/common";
-import { convertError } from "@/utilities/rxjs";
-
-import { HttpNetworkError, BlobWithEtag, NotModified } from "@/http/common";
-import {
- getHttpTokenClient,
- HttpCreateTokenBadCredentialError,
-} from "@/http/token";
-import {
- getHttpUserClient,
- HttpUserNotExistError,
- HttpUser,
-} from "@/http/user";
-
-import { dataStorage, throwIfNotNetworkError } from "./common";
-import { DataHub } from "./DataHub";
-import { pushAlert } from "./alert";
-
-export type User = HttpUser;
-
-export interface UserAuthInfo {
- username: string;
- administrator: boolean;
-}
-
-export interface UserWithToken extends User {
- token: string;
-}
-
-export interface LoginCredentials {
- username: string;
- password: string;
-}
-
-export class BadCredentialError {
- message = "login.badCredential";
-}
-
-const USER_STORAGE_KEY = "currentuser";
-
-export class UserService {
- private userSubject = new BehaviorSubject<UserWithToken | null | undefined>(
- undefined
- );
-
- get user$(): Observable<UserWithToken | null | undefined> {
- return this.userSubject;
- }
-
- get currentUser(): UserWithToken | null | undefined {
- return this.userSubject.value;
- }
-
- async checkLoginState(): Promise<UserWithToken | null> {
- if (this.currentUser !== undefined) {
- console.warn("Already checked user. Can't check twice.");
- }
-
- const savedUser = await dataStorage.getItem<UserWithToken | null>(
- USER_STORAGE_KEY
- );
-
- if (savedUser == null) {
- this.userSubject.next(null);
- return null;
- }
-
- this.userSubject.next(savedUser);
-
- const savedToken = savedUser.token;
- try {
- const res = await getHttpTokenClient().verify({ token: savedToken });
- const user: UserWithToken = { ...res.user, token: savedToken };
- await dataStorage.setItem<UserWithToken>(USER_STORAGE_KEY, user);
- this.userSubject.next(user);
- pushAlert({
- type: "success",
- message: {
- type: "i18n",
- key: "user.welcomeBack",
- },
- });
- return user;
- } catch (error) {
- if (error instanceof HttpNetworkError) {
- pushAlert({
- type: "danger",
- message: { type: "i18n", key: "user.verifyTokenFailedNetwork" },
- });
- return savedUser;
- } else {
- await dataStorage.removeItem(USER_STORAGE_KEY);
- this.userSubject.next(null);
- pushAlert({
- type: "danger",
- message: { type: "i18n", key: "user.verifyTokenFailed" },
- });
- return null;
- }
- }
- }
-
- async login(
- credentials: LoginCredentials,
- rememberMe: boolean
- ): Promise<void> {
- if (this.currentUser) {
- throw new UiLogicError("Already login.");
- }
- try {
- const res = await getHttpTokenClient().create({
- ...credentials,
- expire: 30,
- });
- const user: UserWithToken = {
- ...res.user,
- token: res.token,
- };
- if (rememberMe) {
- await dataStorage.setItem<UserWithToken>(USER_STORAGE_KEY, user);
- }
- this.userSubject.next(user);
- } catch (e) {
- if (e instanceof HttpCreateTokenBadCredentialError) {
- throw new BadCredentialError();
- } else {
- throw e;
- }
- }
- }
-
- async logout(): Promise<void> {
- if (this.currentUser === undefined) {
- throw new UiLogicError("Please check user first.");
- }
- if (this.currentUser === null) {
- throw new UiLogicError("No login.");
- }
- await dataStorage.removeItem(USER_STORAGE_KEY);
- this.userSubject.next(null);
- }
-
- changePassword(
- oldPassword: string,
- newPassword: string
- ): Observable<unknown> {
- if (this.currentUser == undefined) {
- throw new UiLogicError("Not login or checked now, can't log out.");
- }
- const $ = from(
- getHttpUserClient().changePassword(
- {
- oldPassword,
- newPassword,
- },
- this.currentUser.token
- )
- );
- $.subscribe(() => {
- void this.logout();
- });
- return $;
- }
-}
-
-export const userService = new UserService();
-
-export function useRawUser(): UserWithToken | null | undefined {
- const [user, setUser] = useState<UserWithToken | null | undefined>(
- userService.currentUser
- );
- useEffect(() => {
- const subscription = userService.user$.subscribe((u) => setUser(u));
- return () => {
- subscription.unsubscribe();
- };
- });
- return user;
-}
-
-export function useUser(): UserWithToken | null {
- const [user, setUser] = useState<UserWithToken | null>(() => {
- const initUser = userService.currentUser;
- if (initUser === undefined) {
- throw new UiLogicError(
- "This is a logic error in user module. Current user can't be undefined in useUser."
- );
- }
- return initUser;
- });
- useEffect(() => {
- const sub = userService.user$.subscribe((u) => {
- if (u === undefined) {
- throw new UiLogicError(
- "This is a logic error in user module. User emitted can't be undefined later."
- );
- }
- setUser(u);
- });
- return () => {
- sub.unsubscribe();
- };
- });
- return user;
-}
-
-export function useUserLoggedIn(): UserWithToken {
- const user = useUser();
- if (user == null) {
- throw new UiLogicError("You assert user has logged in but actually not.");
- }
- return user;
-}
-
-export function checkLogin(): UserWithToken {
- const user = userService.currentUser;
- if (user == null) {
- throw new UiLogicError("You must login to perform the operation.");
- }
- return user;
-}
-
-export class UserNotExistError extends Error {}
-
-export class UserInfoService {
- saveUser(user: HttpUser): void {
- const key = user.username;
- void this._userHub.optionalInitLineWithSyncAction(key, async (line) => {
- await this.doSaveUser(user);
- line.next({ user, type: "synced" });
- });
- }
-
- saveUsers(users: HttpUser[]): void {
- return users.forEach((user) => this.saveUser(user));
- }
-
- private getCachedUser(username: string): Promise<User | null> {
- return dataStorage.getItem<HttpUser | null>(`user.${username}`);
- }
-
- private doSaveUser(user: HttpUser): Promise<void> {
- return dataStorage.setItem<HttpUser>(`user.${user.username}`, user).then();
- }
-
- syncUser(username: string): Promise<void> {
- return this._userHub.getLineOrCreate(username).sync();
- }
-
- private _userHub = new DataHub<
- string,
- | { user: User; type: "cache" | "synced" | "offline" }
- | { user?: undefined; type: "notexist" | "offline" }
- >({
- sync: async (key, line) => {
- if (line.value == undefined) {
- const cache = await this.getCachedUser(key);
- if (cache != null) {
- line.next({ user: cache, type: "cache" });
- }
- }
-
- try {
- const res = await getHttpUserClient().get(key);
- await this.doSaveUser(res);
- line.next({ user: res, type: "synced" });
- } catch (e) {
- if (e instanceof HttpUserNotExistError) {
- line.next({ type: "notexist" });
- } else {
- const cache = await this.getCachedUser(key);
- line.next({ user: cache ?? undefined, type: "offline" });
- throwIfNotNetworkError(e);
- }
- }
- },
- });
-
- getUser$(username: string): Observable<User> {
- return this._userHub.getObservable(username).pipe(
- map((state) => state?.user),
- filter((user): user is User => user != null)
- );
- }
-
- private getCachedAvatar(username: string): Promise<BlobWithEtag | null> {
- return dataStorage.getItem<BlobWithEtag | null>(`user.${username}.avatar`);
- }
-
- private saveAvatar(username: string, data: BlobWithEtag): Promise<void> {
- return dataStorage
- .setItem<BlobWithEtag>(`user.${username}.avatar`, data)
- .then();
- }
-
- syncAvatar(username: string): Promise<void> {
- return this._avatarHub.getLineOrCreate(username).sync();
- }
-
- private _avatarHub = new DataHub<
- string,
- | { data: Blob; type: "cache" | "synced" | "offline" }
- | { data?: undefined; type: "notexist" | "offline" }
- >({
- sync: async (key, line) => {
- const cache = await this.getCachedAvatar(key);
- if (line.value == null) {
- if (cache != null) {
- line.next({ data: cache.data, type: "cache" });
- }
- }
-
- if (cache == null) {
- try {
- const avatar = await getHttpUserClient().getAvatar(key);
- await this.saveAvatar(key, avatar);
- line.next({ data: avatar.data, type: "synced" });
- } catch (e) {
- line.next({ type: "offline" });
- throwIfNotNetworkError(e);
- }
- } else {
- try {
- const res = await getHttpUserClient().getAvatar(key, cache.etag);
- if (res instanceof NotModified) {
- line.next({ data: cache.data, type: "synced" });
- } else {
- const avatar = res;
- await this.saveAvatar(key, avatar);
- line.next({ data: avatar.data, type: "synced" });
- }
- } catch (e) {
- line.next({ data: cache.data, type: "offline" });
- throwIfNotNetworkError(e);
- }
- }
- },
- });
-
- getAvatar$(username: string): Observable<Blob> {
- return this._avatarHub.getObservable(username).pipe(
- map((state) => state.data),
- filter((blob): blob is Blob => blob != null)
- );
- }
-
- getUserInfo(username: string): Observable<User> {
- return from(getHttpUserClient().get(username)).pipe(
- convertError(HttpUserNotExistError, UserNotExistError)
- );
- }
-
- async setAvatar(username: string, blob: Blob): Promise<void> {
- const user = checkLogin();
- await getHttpUserClient().putAvatar(username, blob, user.token);
- this._avatarHub.getLine(username)?.next({ data: blob, type: "synced" });
- }
-
- async setNickname(username: string, nickname: string): Promise<void> {
- const user = checkLogin();
- return getHttpUserClient()
- .patch(username, { nickname }, user.token)
- .then((user) => {
- this.saveUser(user);
- });
- }
-}
-
-export const userInfoService = new UserInfoService();
-
-export function useAvatar(username?: string): Blob | undefined {
- const [state, setState] = React.useState<Blob | undefined>(undefined);
- React.useEffect(() => {
- if (username == null) {
- setState(undefined);
- return;
- }
-
- const subscription = userInfoService
- .getAvatar$(username)
- .subscribe((blob) => {
- setState(blob);
- });
- return () => {
- subscription.unsubscribe();
- };
- }, [username]);
- return state;
-}
diff --git a/Timeline/ClientApp/src/app/tsconfig.json b/Timeline/ClientApp/src/app/tsconfig.json
deleted file mode 100644
index 14e6327f..00000000
--- a/Timeline/ClientApp/src/app/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ]
- },
- "include": [
- "."
- ]
-}
diff --git a/Timeline/ClientApp/src/app/typings.d.ts b/Timeline/ClientApp/src/app/typings.d.ts
deleted file mode 100644
index 34381682..00000000
--- a/Timeline/ClientApp/src/app/typings.d.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-declare module "*.png" {
- const content: string;
- export default content;
-}
-
-declare module "*.jpeg" {
- const content: string;
- export default content;
-}
-
-declare module "*.jpg" {
- const content: string;
- export default content;
-}
-
-declare module "*.gif" {
- const content: string;
- export default content;
-}
-
-declare module "*.svg" {
- const content: string;
- export default content;
-}
diff --git a/Timeline/ClientApp/src/app/utilities/rxjs.ts b/Timeline/ClientApp/src/app/utilities/rxjs.ts
deleted file mode 100644
index 0730b899..00000000
--- a/Timeline/ClientApp/src/app/utilities/rxjs.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { OperatorFunction } from "rxjs";
-import { catchError } from "rxjs/operators";
-
-export function convertError<T, NewError>(
- oldErrorType: { new (...args: never[]): unknown },
- newErrorType: { new (): NewError }
-): OperatorFunction<T, T> {
- return catchError((error) => {
- if (error instanceof oldErrorType) {
- throw new newErrorType();
- }
- throw error;
- });
-}
diff --git a/Timeline/ClientApp/src/app/utilities/url.ts b/Timeline/ClientApp/src/app/utilities/url.ts
deleted file mode 100644
index 17ead5b2..00000000
--- a/Timeline/ClientApp/src/app/utilities/url.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-//copied from https://stackoverflow.com/questions/5999118/how-can-i-add-or-update-a-query-string-parameter
-export function updateQueryString(
- key: string,
- value: undefined | string | null,
- url: string
-): string {
- const re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi");
- let hash;
-
- if (re.test(url)) {
- if (typeof value !== "undefined" && value !== null) {
- return url.replace(re, "$1" + key + "=" + value + "$2$3");
- } else {
- hash = url.split("#");
- url = hash[0].replace(re, "$1$3").replace(/(&|\?)$/, "");
- if (typeof hash[1] !== "undefined" && hash[1] !== null) {
- url += "#" + hash[1];
- }
- return url;
- }
- } else {
- if (typeof value !== "undefined" && value !== null) {
- const separator = url.includes("?") ? "&" : "?";
- hash = url.split("#");
- url = hash[0] + separator + key + "=" + value;
- if (typeof hash[1] !== "undefined" && hash[1] !== null) {
- url += "#" + hash[1];
- }
- return url;
- } else {
- return url;
- }
- }
-}
-
-export function applyQueryParameters<T>(url: string, query: T): string {
- if (query == null) return url;
-
- for (const [key, value] of Object.entries(query)) {
- if (typeof value === "string") url = updateQueryString(key, value, url);
- else if (typeof value === "number")
- url = updateQueryString(key, String(value), url);
- else if (typeof value === "boolean")
- url = updateQueryString(key, value ? "true" : "false", url);
- else if (value instanceof Date)
- url = updateQueryString(key, value.toISOString(), url);
- else {
- console.error("Unknown query parameter type. Param: ", value);
- }
- }
- return url;
-}
diff --git a/Timeline/ClientApp/src/app/views/about/about.sass b/Timeline/ClientApp/src/app/views/about/about.sass
deleted file mode 100644
index 3b5840cd..00000000
--- a/Timeline/ClientApp/src/app/views/about/about.sass
+++ /dev/null
@@ -1,4 +0,0 @@
-.about-link-icon
- @extend .mx-2
- width: 1.2em
- height: 1.2em
diff --git a/Timeline/ClientApp/src/app/views/about/author-avatar.png b/Timeline/ClientApp/src/app/views/about/author-avatar.png
deleted file mode 100644
index d890d8d0..00000000
--- a/Timeline/ClientApp/src/app/views/about/author-avatar.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/src/app/views/about/github.png b/Timeline/ClientApp/src/app/views/about/github.png
deleted file mode 100644
index ea6ff545..00000000
--- a/Timeline/ClientApp/src/app/views/about/github.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/ClientApp/src/app/views/about/index.tsx b/Timeline/ClientApp/src/app/views/about/index.tsx
deleted file mode 100644
index e7771cec..00000000
--- a/Timeline/ClientApp/src/app/views/about/index.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import React from "react";
-import { useTranslation, Trans } from "react-i18next";
-
-import authorAvatarUrl from "./author-avatar.png";
-import githubLogoUrl from "./github.png";
-
-const frontendCredits: {
- name: string;
- url: string;
-}[] = [
- {
- name: "reactjs",
- url: "https://reactjs.org",
- },
- {
- name: "typescript",
- url: "https://www.typescriptlang.org",
- },
- {
- name: "bootstrap",
- url: "https://getbootstrap.com",
- },
- {
- name: "react-bootstrap",
- url: "https://react-bootstrap.github.io",
- },
- {
- name: "babeljs",
- url: "https://babeljs.io",
- },
- {
- name: "webpack",
- url: "https://webpack.js.org",
- },
- {
- name: "sass",
- url: "https://sass-lang.com",
- },
- {
- name: "eslint",
- url: "https://eslint.org",
- },
- {
- name: "prettier",
- url: "https://prettier.io",
- },
- {
- name: "pepjs",
- url: "https://github.com/jquery/PEP",
- },
- {
- name: "react-inlinesvg",
- url: "https://github.com/gilbarbara/react-inlinesvg",
- },
-];
-
-const backendCredits: {
- name: string;
- url: string;
-}[] = [
- {
- name: "ASP.NET Core",
- url: "https://dotnet.microsoft.com/learn/aspnet/what-is-aspnet-core",
- },
- { name: "sqlite", url: "https://sqlite.org" },
- {
- name: "ImageSharp",
- url: "https://github.com/SixLabors/ImageSharp",
- },
-];
-
-const AboutPage: React.FC = () => {
- const { t } = useTranslation();
-
- return (
- <div className="mt-appbar px-2 mb-4">
- <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
- <h4 id="author-info">{t("about.author.title")}</h4>
- <div>
- <div className="d-flex">
- <img
- src={authorAvatarUrl}
- className="align-self-start avatar large rounded-circle"
- />
- <div>
- <p>
- <small>{t("about.author.fullname")}</small>
- <span className="text-primary">杨宇千</span>
- </p>
- <p>
- <small>{t("about.author.nickname")}</small>
- <span className="text-primary">crupest</span>
- </p>
- <p>
- <small>{t("about.author.introduction")}</small>
- {t("about.author.introductionContent")}
- </p>
- </div>
- </div>
- <p>
- <small>{t("about.author.links")}</small>
- <a
- href="https://github.com/crupest"
- target="_blank"
- rel="noopener noreferrer"
- >
- <img src={githubLogoUrl} className="about-link-icon text-body" />
- </a>
- </p>
- </div>
- </div>
- <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
- <h4>{t("about.site.title")}</h4>
- <p>
- <Trans i18nKey="about.site.content">
- 0<span className="text-primary">1</span>2<b>3</b>4
- <a href="#author-info">5</a>6
- </Trans>
- </p>
- <p>
- <a
- href="https://github.com/crupest/Timeline"
- target="_blank"
- rel="noopener noreferrer"
- >
- {t("about.site.repo")}
- </a>
- </p>
- </div>
- <div className="container mt-4 py-3 shadow border border-primary rounded bg-light">
- <h4>{t("about.credits.title")}</h4>
- <p>{t("about.credits.content")}</p>
- <p>{t("about.credits.frontend")}</p>
- <ul>
- {frontendCredits.map((item, index) => {
- return (
- <li key={index}>
- <a href={item.url} target="_blank" rel="noopener noreferrer">
- {item.name}
- </a>
- </li>
- );
- })}
- <li>...</li>
- </ul>
- <p>{t("about.credits.backend")}</p>
- <ul>
- {backendCredits.map((item, index) => {
- return (
- <li key={index}>
- <a href={item.url} target="_blank" rel="noopener noreferrer">
- {item.name}
- </a>
- </li>
- );
- })}
- <li>...</li>
- </ul>
- </div>
- </div>
- );
-};
-
-export default AboutPage;
diff --git a/Timeline/ClientApp/src/app/views/admin/Admin.tsx b/Timeline/ClientApp/src/app/views/admin/Admin.tsx
deleted file mode 100644
index 9c0250e7..00000000
--- a/Timeline/ClientApp/src/app/views/admin/Admin.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { Fragment } from "react";
-import {
- Redirect,
- Route,
- Switch,
- useRouteMatch,
- useHistory,
-} from "react-router";
-import { Nav } from "react-bootstrap";
-
-import { UserWithToken } from "@/services/user";
-
-import UserAdmin from "./UserAdmin";
-
-interface AdminProps {
- user: UserWithToken;
-}
-
-const Admin: React.FC<AdminProps> = (props) => {
- const match = useRouteMatch();
- const history = useHistory();
- type TabNames = "users" | "more";
-
- const tabName = history.location.pathname.replace(match.path + "/", "");
-
- function toggle(newTab: TabNames): void {
- history.push(`${match.url}/${newTab}`);
- }
-
- const createRoute = (
- name: string,
- body: React.ReactNode
- ): React.ReactNode => {
- return (
- <Route path={`${match.path}/${name}`}>
- <div style={{ height: 56 }} className="flex-fix-length" />
- <Nav variant="tabs">
- <Nav.Item>
- <Nav.Link
- active={tabName === "users"}
- onClick={() => {
- toggle("users");
- }}
- >
- Users
- </Nav.Link>
- </Nav.Item>
- <Nav.Item>
- <Nav.Link
- active={tabName === "more"}
- onClick={() => {
- toggle("more");
- }}
- >
- More
- </Nav.Link>
- </Nav.Item>
- </Nav>
- {body}
- </Route>
- );
- };
-
- return (
- <Fragment>
- <Switch>
- <Redirect from={match.path} to={`${match.path}/users`} exact />
- {createRoute("users", <UserAdmin user={props.user} />)}
- {createRoute("more", <div>More Page Works</div>)}
- </Switch>
- </Fragment>
- );
-};
-
-export default Admin;
diff --git a/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx b/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx
deleted file mode 100644
index 18b77ca8..00000000
--- a/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx
+++ /dev/null
@@ -1,460 +0,0 @@
-import React, { useState, useEffect } from "react";
-import axios from "axios";
-import {
- ListGroup,
- Row,
- Col,
- Dropdown,
- Spinner,
- Button,
-} from "react-bootstrap";
-
-import OperationDialog from "../common/OperationDialog";
-import { User, UserWithToken } from "@/services/user";
-
-const apiBaseUrl = "/api";
-
-async function fetchUserList(_token: string): Promise<User[]> {
- const res = await axios.get<User[]>(`${apiBaseUrl}/users`);
- return res.data;
-}
-
-interface CreateUserInfo {
- username: string;
- password: string;
- administrator: boolean;
-}
-
-async function createUser(user: CreateUserInfo, token: string): Promise<User> {
- const res = await axios.post<User>(
- `${apiBaseUrl}/userop/createuser?token=${token}`,
- user
- );
- return res.data;
-}
-
-function deleteUser(username: string, token: string): Promise<void> {
- return axios.delete(`${apiBaseUrl}/users/${username}?token=${token}`);
-}
-
-function changeUsername(
- oldUsername: string,
- newUsername: string,
- token: string
-): Promise<void> {
- return axios.patch(`${apiBaseUrl}/users/${oldUsername}?token=${token}`, {
- username: newUsername,
- });
-}
-
-function changePassword(
- username: string,
- newPassword: string,
- token: string
-): Promise<void> {
- return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, {
- password: newPassword,
- });
-}
-
-function changePermission(
- username: string,
- newPermission: boolean,
- token: string
-): Promise<void> {
- return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, {
- administrator: newPermission,
- });
-}
-
-const kChangeUsername = "changeusername";
-const kChangePassword = "changepassword";
-const kChangePermission = "changepermission";
-const kDelete = "delete";
-
-type TChangeUsername = typeof kChangeUsername;
-type TChangePassword = typeof kChangePassword;
-type TChangePermission = typeof kChangePermission;
-type TDelete = typeof kDelete;
-
-type ContextMenuItem =
- | TChangeUsername
- | TChangePassword
- | TChangePermission
- | TDelete;
-
-interface UserCardProps {
- onContextMenu: (item: ContextMenuItem) => void;
- user: User;
-}
-
-const UserItem: React.FC<UserCardProps> = (props) => {
- const user = props.user;
-
- const createClickCallback = (item: ContextMenuItem): (() => void) => {
- return () => {
- props.onContextMenu(item);
- };
- };
-
- return (
- <ListGroup.Item className="container">
- <Row className="align-items-center">
- <Col>
- <p className="mb-0 text-primary">{user.username}</p>
- <small
- className={user.administrator ? "text-danger" : "text-secondary"}
- >
- {user.administrator ? "administrator" : "user"}
- </small>
- </Col>
- <Col className="col-auto">
- <Dropdown>
- <Dropdown.Toggle variant="warning" className="text-light">
- Manage
- </Dropdown.Toggle>
- <Dropdown.Menu>
- <Dropdown.Item onClick={createClickCallback(kChangeUsername)}>
- Change Username
- </Dropdown.Item>
- <Dropdown.Item onClick={createClickCallback(kChangePassword)}>
- Change Password
- </Dropdown.Item>
- <Dropdown.Item onClick={createClickCallback(kChangePermission)}>
- Change Permission
- </Dropdown.Item>
- <Dropdown.Item
- className="text-danger"
- onClick={createClickCallback(kDelete)}
- >
- Delete
- </Dropdown.Item>
- </Dropdown.Menu>
- </Dropdown>
- </Col>
- </Row>
- </ListGroup.Item>
- );
-};
-
-interface DialogProps {
- open: boolean;
- close: () => void;
-}
-
-interface CreateUserDialogProps extends DialogProps {
- process: (user: CreateUserInfo) => Promise<void>;
-}
-
-const CreateUserDialog: React.FC<CreateUserDialogProps> = (props) => {
- return (
- <OperationDialog
- title="Create"
- titleColor="create"
- inputPrompt="You are creating a new user."
- inputScheme={[
- { type: "text", label: "Username" },
- { type: "text", label: "Password" },
- { type: "bool", label: "Administrator" },
- ]}
- onProcess={([username, password, administrator]) =>
- props.process({
- username: username as string,
- password: password as string,
- administrator: administrator as boolean,
- })
- }
- close={props.close}
- open={props.open}
- />
- );
-};
-
-const UsernameLabel: React.FC = (props) => {
- return <span style={{ color: "blue" }}>{props.children}</span>;
-};
-
-interface UserDeleteDialogProps extends DialogProps {
- username: string;
- process: () => Promise<void>;
-}
-
-const UserDeleteDialog: React.FC<UserDeleteDialogProps> = (props) => {
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- title="Dangerous"
- titleColor="dangerous"
- inputPrompt={() => (
- <>
- {"You are deleting user "}
- <UsernameLabel>{props.username}</UsernameLabel>
- {" !"}
- </>
- )}
- onProcess={props.process}
- />
- );
-};
-
-interface UserModifyDialogProps<T> extends DialogProps {
- username: string;
- process: (value: T) => Promise<void>;
-}
-
-const UserChangeUsernameDialog: React.FC<UserModifyDialogProps<string>> = (
- props
-) => {
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- title="Caution"
- titleColor="dangerous"
- inputPrompt={() => (
- <>
- {"You are change the username of user "}
- <UsernameLabel>{props.username}</UsernameLabel>
- {" !"}
- </>
- )}
- inputScheme={[{ type: "text", label: "New Username" }]}
- onProcess={([newUsername]) => {
- return props.process(newUsername as string);
- }}
- />
- );
-};
-
-const UserChangePasswordDialog: React.FC<UserModifyDialogProps<string>> = (
- props
-) => {
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- title="Caution"
- titleColor="dangerous"
- inputPrompt={() => (
- <>
- {"You are change the password of user "}
- <UsernameLabel>{props.username}</UsernameLabel>
- {" !"}
- </>
- )}
- inputScheme={[{ type: "text", label: "New Password" }]}
- onProcess={([newPassword]) => {
- return props.process(newPassword as string);
- }}
- />
- );
-};
-
-interface UserChangePermissionDialogProps extends DialogProps {
- username: string;
- newPermission: boolean;
- process: () => Promise<void>;
-}
-
-const UserChangePermissionDialog: React.FC<UserChangePermissionDialogProps> = (
- props
-) => {
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- title="Caution"
- titleColor="dangerous"
- inputPrompt={() => (
- <>
- {"You are change user "}
- <UsernameLabel>{props.username}</UsernameLabel>
- {" to "}
- <span style={{ color: "orange" }}>
- {props.newPermission ? "administrator" : "normal user"}
- </span>
- {" !"}
- </>
- )}
- onProcess={props.process}
- />
- );
-};
-
-interface UserAdminProps {
- user: UserWithToken;
-}
-
-const UserAdmin: React.FC<UserAdminProps> = (props) => {
- type DialogInfo =
- | null
- | {
- type: "create";
- }
- | { type: TDelete; username: string }
- | {
- type: TChangeUsername;
- username: string;
- }
- | {
- type: TChangePassword;
- username: string;
- }
- | {
- type: TChangePermission;
- username: string;
- newPermission: boolean;
- };
-
- const [users, setUsers] = useState<User[] | null>(null);
- const [dialog, setDialog] = useState<DialogInfo>(null);
-
- const token = props.user.token;
-
- useEffect(() => {
- let subscribe = true;
- void fetchUserList(props.user.token).then((us) => {
- if (subscribe) {
- setUsers(us);
- }
- });
- return () => {
- subscribe = false;
- };
- }, [props.user]);
-
- let dialogNode: React.ReactNode;
- if (dialog)
- switch (dialog.type) {
- case "create":
- dialogNode = (
- <CreateUserDialog
- open
- close={() => setDialog(null)}
- process={async (user) => {
- const u = await createUser(user, token);
- setUsers((oldUsers) => [...(oldUsers ?? []), u]);
- }}
- />
- );
- break;
- case "delete":
- dialogNode = (
- <UserDeleteDialog
- open
- close={() => setDialog(null)}
- username={dialog.username}
- process={async () => {
- await deleteUser(dialog.username, token);
- setUsers((oldUsers) =>
- (oldUsers ?? []).filter((u) => u.username !== dialog.username)
- );
- }}
- />
- );
- break;
- case kChangeUsername:
- dialogNode = (
- <UserChangeUsernameDialog
- open
- close={() => setDialog(null)}
- username={dialog.username}
- process={async (newUsername) => {
- await changeUsername(dialog.username, newUsername, token);
- setUsers((oldUsers) => {
- const users = (oldUsers ?? []).slice();
- const findedUser = users.find(
- (u) => u.username === dialog.username
- );
- if (findedUser) findedUser.username = newUsername;
- return users;
- });
- }}
- />
- );
- break;
- case kChangePassword:
- dialogNode = (
- <UserChangePasswordDialog
- open
- close={() => setDialog(null)}
- username={dialog.username}
- process={async (newPassword) => {
- await changePassword(dialog.username, newPassword, token);
- }}
- />
- );
- break;
- case kChangePermission: {
- const newPermission = dialog.newPermission;
- dialogNode = (
- <UserChangePermissionDialog
- open
- close={() => setDialog(null)}
- username={dialog.username}
- newPermission={newPermission}
- process={async () => {
- await changePermission(dialog.username, newPermission, token);
- setUsers((oldUsers) => {
- const users = (oldUsers ?? []).slice();
- const findedUser = users.find(
- (u) => u.username === dialog.username
- );
- if (findedUser) findedUser.administrator = newPermission;
- return users;
- });
- }}
- />
- );
- break;
- }
- }
-
- if (users) {
- const userComponents = users.map((user) => {
- return (
- <UserItem
- key={user.username}
- user={user}
- onContextMenu={(item) => {
- setDialog(
- item === kChangePermission
- ? {
- type: kChangePermission,
- username: user.username,
- newPermission: !user.administrator,
- }
- : {
- type: item,
- username: user.username,
- }
- );
- }}
- />
- );
- });
-
- return (
- <>
- <Button
- variant="success"
- onClick={() =>
- setDialog({
- type: "create",
- })
- }
- className="align-self-end"
- >
- Create User
- </Button>
- {userComponents}
- {dialogNode}
- </>
- );
- } else {
- return <Spinner animation="border" />;
- }
-};
-
-export default UserAdmin;
diff --git a/Timeline/ClientApp/src/app/views/common/AppBar.tsx b/Timeline/ClientApp/src/app/views/common/AppBar.tsx
deleted file mode 100644
index ee4ead8f..00000000
--- a/Timeline/ClientApp/src/app/views/common/AppBar.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { LinkContainer } from "react-router-bootstrap";
-import { Navbar, Nav } from "react-bootstrap";
-
-import { useUser, useAvatar } from "@/services/user";
-
-import TimelineLogo from "./TimelineLogo";
-import BlobImage from "./BlobImage";
-
-const AppBar: React.FC = (_) => {
- const user = useUser();
- const avatar = useAvatar(user?.username);
-
- const { t } = useTranslation();
-
- const isAdministrator = user && user.administrator;
-
- return (
- <Navbar bg="primary" variant="dark" expand="md" sticky="top">
- <LinkContainer to="/">
- <Navbar.Brand className="d-flex align-items-center">
- <TimelineLogo style={{ height: "1em" }} />
- Timeline
- </Navbar.Brand>
- </LinkContainer>
-
- <Navbar.Toggle />
- <Navbar.Collapse>
- <Nav className="mr-auto">
- <LinkContainer to="/settings">
- <Nav.Link>{t("nav.settings")}</Nav.Link>
- </LinkContainer>
-
- <LinkContainer to="/about">
- <Nav.Link>{t("nav.about")}</Nav.Link>
- </LinkContainer>
-
- {isAdministrator && (
- <LinkContainer to="/admin">
- <Nav.Link>Administration</Nav.Link>
- </LinkContainer>
- )}
- </Nav>
- <Nav className="ml-auto mr-2">
- {user != null ? (
- <LinkContainer to={`/users/${user.username}`}>
- <BlobImage
- className="avatar small rounded-circle bg-white"
- blob={avatar}
- />
- </LinkContainer>
- ) : (
- <LinkContainer to="/login">
- <Nav.Link>{t("nav.login")}</Nav.Link>
- </LinkContainer>
- )}
- </Nav>
- </Navbar.Collapse>
- </Navbar>
- );
-};
-
-export default AppBar;
diff --git a/Timeline/ClientApp/src/app/views/common/BlobImage.tsx b/Timeline/ClientApp/src/app/views/common/BlobImage.tsx
deleted file mode 100644
index 0dd25c52..00000000
--- a/Timeline/ClientApp/src/app/views/common/BlobImage.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from "react";
-
-const BlobImage: React.FC<
- Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> & {
- blob?: Blob | unknown;
- }
-> = (props) => {
- const { blob, ...otherProps } = props;
-
- const [url, setUrl] = React.useState<string | undefined>(undefined);
-
- React.useEffect(() => {
- if (blob instanceof Blob) {
- const url = URL.createObjectURL(blob);
- setUrl(url);
- return () => {
- URL.revokeObjectURL(url);
- };
- } else {
- setUrl(undefined);
- }
- }, [blob]);
-
- return <img {...otherProps} src={url} />;
-};
-
-export default BlobImage;
diff --git a/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx b/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx
deleted file mode 100644
index b9db8b99..00000000
--- a/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx
+++ /dev/null
@@ -1,306 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-
-import { UiLogicError } from "@/common";
-
-export interface Clip {
- left: number;
- top: number;
- width: number;
-}
-
-interface NormailizedClip extends Clip {
- height: number;
-}
-
-interface ImageInfo {
- width: number;
- height: number;
- landscape: boolean;
- ratio: number;
- maxClipWidth: number;
- maxClipHeight: number;
-}
-
-interface ImageCropperSavedState {
- clip: NormailizedClip;
- x: number;
- y: number;
- pointerId: number;
-}
-
-export interface ImageCropperProps {
- clip: Clip | null;
- imageUrl: string;
- onChange: (clip: Clip) => void;
- imageElementCallback?: (element: HTMLImageElement | null) => void;
- className?: string;
-}
-
-const ImageCropper = (props: ImageCropperProps): React.ReactElement => {
- const { clip, imageUrl, onChange, imageElementCallback, className } = props;
-
- const [oldState, setOldState] = React.useState<ImageCropperSavedState | null>(
- null
- );
- const [imageInfo, setImageInfo] = React.useState<ImageInfo | null>(null);
-
- const normalizeClip = (c: Clip | null | undefined): NormailizedClip => {
- if (c == null) {
- return { left: 0, top: 0, width: 0, height: 0 };
- }
-
- return {
- left: c.left || 0,
- top: c.top || 0,
- width: c.width || 0,
- height: imageInfo != null ? (c.width || 0) / imageInfo.ratio : 0,
- };
- };
-
- const c = normalizeClip(clip);
-
- const imgElementRef = React.useRef<HTMLImageElement | null>(null);
-
- const onImageRef = React.useCallback(
- (e: HTMLImageElement | null) => {
- imgElementRef.current = e;
- if (imageElementCallback != null && e == null) {
- imageElementCallback(null);
- }
- },
- [imageElementCallback]
- );
-
- const onImageLoad = React.useCallback(
- (e: React.SyntheticEvent<HTMLImageElement>) => {
- const img = e.currentTarget;
- const landscape = img.naturalWidth >= img.naturalHeight;
-
- const info = {
- width: img.naturalWidth,
- height: img.naturalHeight,
- landscape,
- ratio: img.naturalHeight / img.naturalWidth,
- maxClipWidth: landscape ? img.naturalHeight / img.naturalWidth : 1,
- maxClipHeight: landscape ? 1 : img.naturalWidth / img.naturalHeight,
- };
- setImageInfo(info);
- onChange({ left: 0, top: 0, width: info.maxClipWidth });
- if (imageElementCallback != null) {
- imageElementCallback(img);
- }
- },
- [onChange, imageElementCallback]
- );
-
- const onPointerDown = React.useCallback(
- (e: React.PointerEvent) => {
- if (oldState != null) return;
- e.currentTarget.setPointerCapture(e.pointerId);
- setOldState({
- x: e.clientX,
- y: e.clientY,
- clip: c,
- pointerId: e.pointerId,
- });
- },
- [oldState, c]
- );
-
- const onPointerUp = React.useCallback(
- (e: React.PointerEvent) => {
- if (oldState == null || oldState.pointerId !== e.pointerId) return;
- e.currentTarget.releasePointerCapture(e.pointerId);
- setOldState(null);
- },
- [oldState]
- );
-
- const onPointerMove = React.useCallback(
- (e: React.PointerEvent) => {
- if (oldState == null) return;
-
- const oldClip = oldState.clip;
-
- const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y };
-
- const { current: imgElement } = imgElementRef;
-
- if (imgElement == null) throw new UiLogicError("Image element is null.");
-
- const moveRatio = {
- x: movement.x / imgElement.width,
- y: movement.y / imgElement.height,
- };
-
- const newRatio = {
- x: oldClip.left + moveRatio.x,
- y: oldClip.top + moveRatio.y,
- };
- if (newRatio.x < 0) {
- newRatio.x = 0;
- } else if (newRatio.x > 1 - oldClip.width) {
- newRatio.x = 1 - oldClip.width;
- }
- if (newRatio.y < 0) {
- newRatio.y = 0;
- } else if (newRatio.y > 1 - oldClip.height) {
- newRatio.y = 1 - oldClip.height;
- }
-
- onChange({ left: newRatio.x, top: newRatio.y, width: oldClip.width });
- },
- [oldState, onChange]
- );
-
- const onHandlerPointerMove = React.useCallback(
- (e: React.PointerEvent) => {
- if (oldState == null) return;
-
- const oldClip = oldState.clip;
-
- const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y };
-
- const ratio = imageInfo == null ? 1 : imageInfo.ratio;
-
- const { current: imgElement } = imgElementRef;
-
- if (imgElement == null) throw new UiLogicError("Image element is null.");
-
- const moveRatio = {
- x: movement.x / imgElement.width,
- y: movement.x / imgElement.width / ratio,
- };
-
- const newRatio = {
- x: oldClip.width + moveRatio.x,
- y: oldClip.height + moveRatio.y,
- };
-
- const maxRatio = {
- x: Math.min(1 - oldClip.left, newRatio.x),
- y: Math.min(1 - oldClip.top, newRatio.y),
- };
-
- const maxWidthRatio = Math.min(maxRatio.x, maxRatio.y * ratio);
-
- let newWidth;
- if (newRatio.x < 0) {
- newWidth = 0;
- } else if (newRatio.x > maxWidthRatio) {
- newWidth = maxWidthRatio;
- } else {
- newWidth = newRatio.x;
- }
-
- onChange({ left: oldClip.left, top: oldClip.top, width: newWidth });
- },
- [imageInfo, oldState, onChange]
- );
-
- const toPercentage = (n: number): string => `${n}%`;
-
- // fuck!!! I just can't find a better way to implement this in pure css
- const containerStyle: React.CSSProperties = (() => {
- if (imageInfo == null) {
- return { width: "100%", paddingTop: "100%", height: 0 };
- } else {
- if (imageInfo.ratio > 1) {
- return {
- width: toPercentage(100 / imageInfo.ratio),
- paddingTop: "100%",
- height: 0,
- };
- } else {
- return {
- width: "100%",
- paddingTop: toPercentage(100 * imageInfo.ratio),
- height: 0,
- };
- }
- }
- })();
-
- return (
- <div
- className={clsx("image-cropper-container", className)}
- style={containerStyle}
- >
- <img ref={onImageRef} src={imageUrl} onLoad={onImageLoad} alt="to crop" />
- <div className="image-cropper-mask-container">
- <div
- className="image-cropper-mask"
- touch-action="none"
- style={{
- left: toPercentage(c.left * 100),
- top: toPercentage(c.top * 100),
- width: toPercentage(c.width * 100),
- height: toPercentage(c.height * 100),
- }}
- onPointerMove={onPointerMove}
- onPointerDown={onPointerDown}
- onPointerUp={onPointerUp}
- />
- </div>
- <div
- className="image-cropper-handler"
- touch-action="none"
- style={{
- left: `calc(${(c.left + c.width) * 100}% - 15px)`,
- top: `calc(${(c.top + c.height) * 100}% - 15px)`,
- }}
- onPointerMove={onHandlerPointerMove}
- onPointerDown={onPointerDown}
- onPointerUp={onPointerUp}
- />
- </div>
- );
-};
-
-export default ImageCropper;
-
-export function applyClipToImage(
- image: HTMLImageElement,
- clip: Clip,
- mimeType: string
-): Promise<Blob> {
- return new Promise((resolve, reject) => {
- const naturalSize = {
- width: image.naturalWidth,
- height: image.naturalHeight,
- };
- const clipArea = {
- x: naturalSize.width * clip.left,
- y: naturalSize.height * clip.top,
- length: naturalSize.width * clip.width,
- };
-
- const canvas = document.createElement("canvas");
- canvas.width = clipArea.length;
- canvas.height = clipArea.length;
- const context = canvas.getContext("2d");
-
- if (context == null) throw new Error("Failed to create context.");
-
- context.drawImage(
- image,
- clipArea.x,
- clipArea.y,
- clipArea.length,
- clipArea.length,
- 0,
- 0,
- clipArea.length,
- clipArea.length
- );
-
- canvas.toBlob((blob) => {
- if (blob == null) {
- reject(new Error("canvas.toBlob returns null"));
- } else {
- resolve(blob);
- }
- }, mimeType);
- });
-}
diff --git a/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx b/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx
deleted file mode 100644
index 154334a7..00000000
--- a/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import { Button, ButtonProps, Spinner } from "react-bootstrap";
-
-const LoadingButton: React.FC<{ loading?: boolean } & ButtonProps> = ({
- loading,
- variant,
- disabled,
- ...otherProps
-}) => {
- return (
- <Button
- variant={variant != null ? `outline-${variant}` : "outline-primary"}
- disabled={disabled || loading}
- {...otherProps}
- >
- {otherProps.children}
- {loading ? (
- <Spinner
- className="ml-1"
- variant={variant}
- animation="grow"
- size="sm"
- />
- ) : null}
- </Button>
- );
-};
-
-export default LoadingButton;
diff --git a/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx b/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx
deleted file mode 100644
index 590fafa0..00000000
--- a/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from "react";
-import { Spinner } from "react-bootstrap";
-
-const LoadingPage: React.FC = () => {
- return (
- <div className="position-fixed w-100 h-100 d-flex justify-content-center align-items-center">
- <Spinner variant="primary" animation="border" />
- </div>
- );
-};
-
-export default LoadingPage;
diff --git a/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx b/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx
deleted file mode 100644
index 841392a6..00000000
--- a/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx
+++ /dev/null
@@ -1,364 +0,0 @@
-import React, { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Form, Button, Modal } from "react-bootstrap";
-
-import { UiLogicError } from "@/common";
-
-import LoadingButton from "./LoadingButton";
-
-interface DefaultErrorPromptProps {
- error?: string;
-}
-
-const DefaultErrorPrompt: React.FC<DefaultErrorPromptProps> = (props) => {
- const { t } = useTranslation();
-
- let result = <p className="text-danger">{t("operationDialog.error")}</p>;
-
- if (props.error != null) {
- result = (
- <>
- {result}
- <p className="text-danger">{props.error}</p>
- </>
- );
- }
-
- return result;
-};
-
-export type OperationInputOptionalError = undefined | null | string;
-
-export interface OperationInputErrorInfo {
- [index: number]: OperationInputOptionalError;
-}
-
-export type OperationInputValidator<TValue> = (
- value: TValue,
- values: (string | boolean)[]
-) => OperationInputOptionalError | OperationInputErrorInfo;
-
-export interface OperationTextInputInfo {
- type: "text";
- password?: boolean;
- label?: string;
- initValue?: string;
- textFieldProps?: Omit<
- React.InputHTMLAttributes<HTMLInputElement>,
- "type" | "value" | "onChange" | "aria-relevant"
- >;
- helperText?: string;
- validator?: OperationInputValidator<string>;
-}
-
-export interface OperationBoolInputInfo {
- type: "bool";
- label: string;
- initValue?: boolean;
-}
-
-export interface OperationSelectInputInfoOption {
- value: string;
- label: string;
- icon?: React.ReactElement;
-}
-
-export interface OperationSelectInputInfo {
- type: "select";
- label: string;
- options: OperationSelectInputInfoOption[];
- initValue?: string;
-}
-
-export type OperationInputInfo =
- | OperationTextInputInfo
- | OperationBoolInputInfo
- | OperationSelectInputInfo;
-
-interface OperationResult {
- type: "success" | "failure";
- data: unknown;
-}
-
-interface OperationDialogProps {
- open: boolean;
- close: () => void;
- title: React.ReactNode;
- titleColor?: "default" | "dangerous" | "create" | string;
- onProcess: (inputs: (string | boolean)[]) => Promise<unknown>;
- inputScheme?: OperationInputInfo[];
- inputPrompt?: string | (() => React.ReactNode);
- processPrompt?: () => React.ReactNode;
- successPrompt?: (data: unknown) => React.ReactNode;
- failurePrompt?: (error: unknown) => React.ReactNode;
- onSuccessAndClose?: () => void;
-}
-
-const OperationDialog: React.FC<OperationDialogProps> = (props) => {
- const inputScheme = props.inputScheme ?? [];
-
- const { t } = useTranslation();
-
- type Step = "input" | "process" | OperationResult;
- const [step, setStep] = useState<Step>("input");
- const [values, setValues] = useState<(boolean | string)[]>(
- inputScheme.map((i) => {
- if (i.type === "bool") {
- return i.initValue ?? false;
- } else if (i.type === "text" || i.type === "select") {
- return i.initValue ?? "";
- } else {
- throw new UiLogicError("Unknown input scheme.");
- }
- })
- );
- const [inputError, setInputError] = useState<OperationInputErrorInfo>({});
-
- const close = (): void => {
- if (step !== "process") {
- props.close();
- if (
- typeof step === "object" &&
- step.type === "success" &&
- props.onSuccessAndClose
- ) {
- props.onSuccessAndClose();
- }
- } else {
- console.log("Attempt to close modal when processing.");
- }
- };
-
- const onConfirm = (): void => {
- setStep("process");
- props.onProcess(values).then(
- (d: unknown) => {
- setStep({
- type: "success",
- data: d,
- });
- },
- (e: unknown) => {
- setStep({
- type: "failure",
- data: e,
- });
- }
- );
- };
-
- let body: React.ReactNode;
- if (step === "input" || step === "process") {
- const process = step === "process";
-
- let inputPrompt =
- typeof props.inputPrompt === "function"
- ? props.inputPrompt()
- : props.inputPrompt;
- inputPrompt = <h6>{inputPrompt}</h6>;
-
- const updateValue = (
- index: number,
- newValue: string | boolean
- ): (string | boolean)[] => {
- const oldValues = values;
- const newValues = oldValues.slice();
- newValues[index] = newValue;
- setValues(newValues);
- return newValues;
- };
-
- const testErrorInfo = (errorInfo: OperationInputErrorInfo): boolean => {
- for (let i = 0; i < inputScheme.length; i++) {
- if (inputScheme[i].type === "text" && errorInfo[i] != null) {
- return true;
- }
- }
- return false;
- };
-
- const calculateError = (
- oldError: OperationInputErrorInfo,
- index: number,
- newError: OperationInputOptionalError | OperationInputErrorInfo
- ): OperationInputErrorInfo => {
- if (newError === undefined) {
- return oldError;
- } else if (newError === null || typeof newError === "string") {
- return { ...oldError, [index]: newError };
- } else {
- const newInputError: OperationInputErrorInfo = { ...oldError };
- for (const [index, error] of Object.entries(newError)) {
- if (error !== undefined) {
- newInputError[+index] = error as OperationInputOptionalError;
- }
- }
- return newInputError;
- }
- };
-
- const validateAll = (): boolean => {
- let newInputError = inputError;
- for (let i = 0; i < inputScheme.length; i++) {
- const item = inputScheme[i];
- if (item.type === "text") {
- newInputError = calculateError(
- newInputError,
- i,
- item.validator?.(values[i] as string, values)
- );
- }
- }
- const result = !testErrorInfo(newInputError);
- setInputError(newInputError);
- return result;
- };
-
- body = (
- <>
- <Modal.Body>
- {inputPrompt}
- {inputScheme.map((item, index) => {
- const value = values[index];
- const error: string | undefined = ((e) =>
- typeof e === "string" ? t(e) : undefined)(inputError?.[index]);
-
- if (item.type === "text") {
- return (
- <Form.Group key={index}>
- {item.label && <Form.Label>{t(item.label)}</Form.Label>}
- <Form.Control
- type={item.password === true ? "password" : "text"}
- value={value as string}
- onChange={(e) => {
- const v = e.target.value;
- const newValues = updateValue(index, v);
- setInputError(
- calculateError(
- inputError,
- index,
- item.validator?.(v, newValues)
- )
- );
- }}
- isInvalid={error != null}
- disabled={process}
- />
- {error != null && (
- <Form.Control.Feedback type="invalid">
- {error}
- </Form.Control.Feedback>
- )}
- {item.helperText && (
- <Form.Text>{t(item.helperText)}</Form.Text>
- )}
- </Form.Group>
- );
- } else if (item.type === "bool") {
- return (
- <Form.Group key={index}>
- <Form.Check<"input">
- type="checkbox"
- checked={value as boolean}
- onChange={(event) => {
- updateValue(index, event.currentTarget.checked);
- }}
- label={t(item.label)}
- disabled={process}
- />
- </Form.Group>
- );
- } else if (item.type === "select") {
- return (
- <Form.Group key={index}>
- <Form.Label>{t(item.label)}</Form.Label>
- <Form.Control
- as="select"
- value={value as string}
- onChange={(event) => {
- updateValue(index, event.target.value);
- }}
- disabled={process}
- >
- {item.options.map((option, i) => {
- return (
- <option value={option.value} key={i}>
- {option.icon}
- {t(option.label)}
- </option>
- );
- })}
- </Form.Control>
- </Form.Group>
- );
- }
- })}
- </Modal.Body>
- <Modal.Footer>
- <Button variant="outline-secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- <LoadingButton
- variant="primary"
- loading={process}
- disabled={testErrorInfo(inputError)}
- onClick={() => {
- if (validateAll()) {
- onConfirm();
- }
- }}
- >
- {t("operationDialog.confirm")}
- </LoadingButton>
- </Modal.Footer>
- </>
- );
- } else {
- let content: React.ReactNode;
- const result = step;
- if (result.type === "success") {
- content =
- props.successPrompt?.(result.data) ?? t("operationDialog.success");
- if (typeof content === "string")
- content = <p className="text-success">{content}</p>;
- } else {
- content = props.failurePrompt?.(result.data) ?? <DefaultErrorPrompt />;
- if (typeof content === "string")
- content = <DefaultErrorPrompt error={content} />;
- }
- body = (
- <>
- <Modal.Body>{content}</Modal.Body>
- <Modal.Footer>
- <Button variant="primary" onClick={close}>
- {t("operationDialog.ok")}
- </Button>
- </Modal.Footer>
- </>
- );
- }
-
- const title = typeof props.title === "string" ? t(props.title) : props.title;
-
- return (
- <Modal show={props.open} onHide={close}>
- <Modal.Header
- className={
- props.titleColor != null
- ? "text-" +
- (props.titleColor === "create"
- ? "success"
- : props.titleColor === "dangerous"
- ? "danger"
- : props.titleColor)
- : undefined
- }
- >
- {title}
- </Modal.Header>
- {body}
- </Modal>
- );
-};
-
-export default OperationDialog;
diff --git a/Timeline/ClientApp/src/app/views/common/SearchInput.tsx b/Timeline/ClientApp/src/app/views/common/SearchInput.tsx
deleted file mode 100644
index 9833d515..00000000
--- a/Timeline/ClientApp/src/app/views/common/SearchInput.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import React, { useCallback } from "react";
-import clsx from "clsx";
-import { useTranslation } from "react-i18next";
-import { Spinner, Form, Button } from "react-bootstrap";
-
-export interface SearchInputProps {
- value: string;
- onChange: (value: string) => void;
- onButtonClick: () => void;
- className?: string;
- loading?: boolean;
- buttonText?: string;
- placeholder?: string;
- additionalButton?: React.ReactNode;
-}
-
-const SearchInput: React.FC<SearchInputProps> = (props) => {
- const { onChange, onButtonClick } = props;
-
- const { t } = useTranslation();
-
- const onInputChange = useCallback(
- (event: React.ChangeEvent<HTMLInputElement>): void => {
- onChange(event.currentTarget.value);
- },
- [onChange]
- );
-
- const onInputKeyPress = useCallback(
- (event: React.KeyboardEvent<HTMLInputElement>): void => {
- if (event.key === "Enter") {
- onButtonClick();
- }
- },
- [onButtonClick]
- );
-
- return (
- <Form inline className={clsx("my-2", props.className)}>
- <Form.Control
- className="mr-sm-2 flex-grow-1"
- value={props.value}
- onChange={onInputChange}
- onKeyPress={onInputKeyPress}
- placeholder={props.placeholder}
- />
- <div className="mt-2 mt-sm-0 order-sm-last ml-sm-3">
- {props.additionalButton}
- </div>
- <div className="mt-2 mt-sm-0 ml-auto ml-sm-0">
- {props.loading ? (
- <Spinner variant="primary" animation="border" />
- ) : (
- <Button variant="outline-primary" onClick={props.onButtonClick}>
- {props.buttonText ?? t("search")}
- </Button>
- )}
- </div>
- </Form>
- );
-};
-
-export default SearchInput;
diff --git a/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx b/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx
deleted file mode 100644
index 27d188fc..00000000
--- a/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { SVGAttributes } from "react";
-
-export interface TimelineLogoProps extends SVGAttributes<SVGElement> {
- color?: string;
-}
-
-const TimelineLogo: React.FC<TimelineLogoProps> = (props) => {
- const { color, ...forwardProps } = props;
- const coercedColor = color ?? "currentcolor";
- return (
- <svg
- className={props.className}
- viewBox="0 0 100 100"
- fill="none"
- strokeWidth="12"
- stroke={coercedColor}
- {...forwardProps}
- >
- <line x1="50" y1="0" x2="50" y2="25" />
- <circle cx="50" cy="50" r="22" />
- <line x1="50" y1="75" x2="50" y2="100" />
- </svg>
- );
-};
-
-export default TimelineLogo;
diff --git a/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx b/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx
deleted file mode 100644
index 29f6a69f..00000000
--- a/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { SVGAttributes } from "react";
-
-export interface UserTimelineLogoProps extends SVGAttributes<SVGElement> {
- color?: string;
-}
-
-const UserTimelineLogo: React.FC<UserTimelineLogoProps> = (props) => {
- const { color, ...forwardProps } = props;
- const coercedColor = color ?? "currentcolor";
-
- return (
- <svg viewBox="0 0 100 100" {...forwardProps}>
- <g fill="none" stroke={coercedColor} strokeWidth="12">
- <line x1="50" x2="50" y1="0" y2="25" />
- <circle cx="50" cy="50" r="22" />
- <line x1="50" x2="50" y1="75" y2="100" />
- </g>
- <g fill={color}>
- <circle cx="85" cy="75" r="10" />
- <path d="m70,100c0,0 15,-30 30,0.25" />
- </g>
- </svg>
- );
-};
-
-export default UserTimelineLogo;
diff --git a/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx b/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx
deleted file mode 100644
index c74f18e2..00000000
--- a/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import React, { useCallback } from "react";
-import without from "lodash/without";
-import concat from "lodash/concat";
-import { useTranslation } from "react-i18next";
-import { Alert } from "react-bootstrap";
-
-import {
- alertService,
- AlertInfoEx,
- kAlertHostId,
- AlertInfo,
-} from "@/services/alert";
-
-interface AutoCloseAlertProps {
- alert: AlertInfo;
- close: () => void;
-}
-
-export const AutoCloseAlert: React.FC<AutoCloseAlertProps> = (props) => {
- const { alert } = props;
- const { dismissTime } = alert;
-
- const { t } = useTranslation();
-
- React.useEffect(() => {
- const tag =
- dismissTime === "never"
- ? null
- : typeof dismissTime === "number"
- ? window.setTimeout(props.close, dismissTime)
- : window.setTimeout(props.close, 5000);
- return () => {
- if (tag != null) {
- window.clearTimeout(tag);
- }
- };
- }, [dismissTime, props.close]);
-
- return (
- <Alert
- className="m-3"
- variant={alert.type ?? "primary"}
- onClose={props.close}
- dismissible
- >
- {(() => {
- const { message } = alert;
- if (typeof message === "function") {
- const Message = message;
- return <Message />;
- } else if (typeof message === "object" && message.type === "i18n") {
- return t(message.key);
- } else return alert.message;
- })()}
- </Alert>
- );
-};
-
-// oh what a bad name!
-interface AlertInfoExEx extends AlertInfoEx {
- close: () => void;
-}
-
-const AlertHost: React.FC = () => {
- const [alerts, setAlerts] = React.useState<AlertInfoExEx[]>([]);
-
- // react guarantee that state setters are stable, so we don't need to add it to dependency list
-
- const consume = useCallback((alert: AlertInfoEx): void => {
- const alertEx: AlertInfoExEx = {
- ...alert,
- close: () => {
- setAlerts((oldAlerts) => {
- return without(oldAlerts, alertEx);
- });
- },
- };
- setAlerts((oldAlerts) => {
- return concat(oldAlerts, alertEx);
- });
- }, []);
-
- React.useEffect(() => {
- alertService.registerConsumer(consume);
- return () => {
- alertService.unregisterConsumer(consume);
- };
- }, [consume]);
-
- return (
- <div id={kAlertHostId} className="alert-container">
- {alerts.map((alert) => {
- return (
- <AutoCloseAlert key={alert.id} alert={alert} close={alert.close} />
- );
- })}
- </div>
- );
-};
-
-export default AlertHost;
diff --git a/Timeline/ClientApp/src/app/views/common/alert/alert.sass b/Timeline/ClientApp/src/app/views/common/alert/alert.sass
deleted file mode 100644
index 5b6e65c2..00000000
--- a/Timeline/ClientApp/src/app/views/common/alert/alert.sass
+++ /dev/null
@@ -1,15 +0,0 @@
-.alert-container
- position: fixed
- z-index: $zindex-popover
-
-@include media-breakpoint-up(sm)
- .alert-container
- bottom: 0
- right: 0
-
-@include media-breakpoint-down(sm)
- .alert-container
- bottom: 0
- right: 0
- left: 0
- text-align: center
diff --git a/Timeline/ClientApp/src/app/views/common/common.sass b/Timeline/ClientApp/src/app/views/common/common.sass
deleted file mode 100644
index 15d34d7c..00000000
--- a/Timeline/ClientApp/src/app/views/common/common.sass
+++ /dev/null
@@ -1,33 +0,0 @@
-.image-cropper-container
- position: relative
- box-sizing: border-box
- user-select: none
-
-.image-cropper-container img
- position: absolute
- left: 0
- top: 0
- width: 100%
- height: 100%
-
-.image-cropper-mask-container
- position: absolute
- left: 0
- top: 0
- right: 0
- bottom: 0
- overflow: hidden
-
-.image-cropper-mask
- position: absolute
- box-shadow: 0 0 0 10000px rgba(255, 255, 255, 80%)
- touch-action: none
-
-.image-cropper-handler
- position: absolute
- width: 26px
- height: 26px
- border: black solid 2px
- border-radius: 50%
- background: white
- touch-action: none
diff --git a/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx b/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx
deleted file mode 100644
index dcd39cbe..00000000
--- a/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import React from "react";
-import { Row, Col } from "react-bootstrap";
-import { useTranslation } from "react-i18next";
-
-import { UserWithToken } from "@/services/user";
-import { TimelineInfo } from "@/services/timeline";
-import { getHttpTimelineClient } from "@/http/timeline";
-
-import TimelineBoard from "./TimelineBoard";
-import OfflineBoard from "./OfflineBoard";
-
-const BoardWithUser: React.FC<{ user: UserWithToken }> = ({ user }) => {
- const { t } = useTranslation();
-
- const [ownTimelines, setOwnTimelines] = React.useState<
- TimelineInfo[] | "offline" | "loading"
- >("loading");
- const [joinTimelines, setJoinTimelines] = React.useState<
- TimelineInfo[] | "offline" | "loading"
- >("loading");
-
- React.useEffect(() => {
- let subscribe = true;
- if (ownTimelines === "loading") {
- void getHttpTimelineClient()
- .listTimeline({ relate: user.username, relateType: "own" })
- .then(
- (timelines) => {
- if (subscribe) {
- setOwnTimelines(timelines);
- }
- },
- () => {
- setOwnTimelines("offline");
- }
- );
- }
- return () => {
- subscribe = false;
- };
- }, [user, ownTimelines]);
-
- React.useEffect(() => {
- let subscribe = true;
- if (joinTimelines === "loading") {
- void getHttpTimelineClient()
- .listTimeline({ relate: user.username, relateType: "join" })
- .then(
- (timelines) => {
- if (subscribe) {
- setJoinTimelines(timelines);
- }
- },
- () => {
- setJoinTimelines("offline");
- }
- );
- }
- return () => {
- subscribe = false;
- };
- }, [user, joinTimelines]);
-
- return (
- <Row className="my-2 justify-content-center">
- {ownTimelines === "offline" && joinTimelines === "offline" ? (
- <Col className="py-2" sm="8" lg="6">
- <OfflineBoard
- onReload={() => {
- setOwnTimelines("loading");
- setJoinTimelines("loading");
- }}
- />
- </Col>
- ) : (
- <>
- <Col sm="6" lg="5" className="py-2">
- <TimelineBoard
- title={t("home.ownTimeline")}
- timelines={ownTimelines}
- onReload={() => {
- setOwnTimelines("loading");
- }}
- />
- </Col>
- <Col sm="6" lg="5" className="py-2">
- <TimelineBoard
- title={t("home.joinTimeline")}
- timelines={joinTimelines}
- onReload={() => {
- setJoinTimelines("loading");
- }}
- />
- </Col>
- </>
- )}
- </Row>
- );
-};
-
-export default BoardWithUser;
diff --git a/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx b/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx
deleted file mode 100644
index ebfddb50..00000000
--- a/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from "react";
-import { Row, Col } from "react-bootstrap";
-
-import { TimelineInfo } from "@/services/timeline";
-import { getHttpTimelineClient } from "@/http/timeline";
-
-import TimelineBoard from "./TimelineBoard";
-import OfflineBoard from "./OfflineBoard";
-
-const BoardWithoutUser: React.FC = () => {
- const [publicTimelines, setPublicTimelines] = React.useState<
- TimelineInfo[] | "offline" | "loading"
- >("loading");
-
- React.useEffect(() => {
- let subscribe = true;
- if (publicTimelines === "loading") {
- void getHttpTimelineClient()
- .listTimeline({ visibility: "Public" })
- .then(
- (timelines) => {
- if (subscribe) {
- setPublicTimelines(timelines);
- }
- },
- () => {
- setPublicTimelines("offline");
- }
- );
- }
- return () => {
- subscribe = false;
- };
- }, [publicTimelines]);
-
- return (
- <Row className="my-2 justify-content-center">
- {publicTimelines === "offline" ? (
- <Col sm="8" lg="6">
- <OfflineBoard
- onReload={() => {
- setPublicTimelines("loading");
- }}
- />
- </Col>
- ) : (
- <Col sm="8" lg="6">
- <TimelineBoard
- timelines={publicTimelines}
- onReload={() => {
- setPublicTimelines("loading");
- }}
- />
- </Col>
- )}
- </Row>
- );
-};
-
-export default BoardWithoutUser;
diff --git a/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx b/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx
deleted file mode 100644
index fc05bd74..00000000
--- a/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from "react";
-import { Link } from "react-router-dom";
-import { Trans } from "react-i18next";
-
-import { getAllCachedTimelineNames } from "@/services/timeline";
-import UserTimelineLogo from "../common/UserTimelineLogo";
-import TimelineLogo from "../common/TimelineLogo";
-
-export interface OfflineBoardProps {
- onReload: () => void;
-}
-
-const OfflineBoard: React.FC<OfflineBoardProps> = ({ onReload }) => {
- const [timelines, setTimelines] = React.useState<string[]>([]);
-
- React.useEffect(() => {
- let subscribe = true;
- void getAllCachedTimelineNames().then((t) => {
- if (subscribe) setTimelines(t);
- });
- return () => {
- subscribe = false;
- };
- });
-
- return (
- <>
- <Trans i18nKey="home.offlinePrompt">
- 0
- <a
- href="#"
- onClick={(e) => {
- onReload();
- e.preventDefault();
- }}
- >
- 1
- </a>
- 2
- </Trans>
- {timelines.map((timeline) => {
- const isPersonal = timeline.startsWith("@");
- const url = isPersonal
- ? `/users/${timeline.slice(1)}`
- : `/timelines/${timeline}`;
- return (
- <div key={timeline} className="timeline-board-item">
- {isPersonal ? (
- <UserTimelineLogo className="icon" />
- ) : (
- <TimelineLogo className="icon" />
- )}
- <Link to={url}>{timeline}</Link>
- </div>
- );
- })}
- </>
- );
-};
-
-export default OfflineBoard;
diff --git a/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx b/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx
deleted file mode 100644
index a3d176e1..00000000
--- a/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-import { Link } from "react-router-dom";
-import { Trans } from "react-i18next";
-import { Spinner } from "react-bootstrap";
-
-import { TimelineInfo } from "@/services/timeline";
-import TimelineLogo from "../common/TimelineLogo";
-import UserTimelineLogo from "../common/UserTimelineLogo";
-
-export interface TimelineBoardProps {
- title?: string;
- timelines: TimelineInfo[] | "offline" | "loading";
- onReload: () => void;
- className?: string;
-}
-
-const TimelineBoard: React.FC<TimelineBoardProps> = (props) => {
- const { title, timelines, className } = props;
-
- return (
- <div className={clsx("timeline-board", className)}>
- {title != null && <h3 className="text-center">{title}</h3>}
- {(() => {
- if (timelines === "loading") {
- return (
- <div className="d-flex flex-grow-1 justify-content-center align-items-center">
- <Spinner variant="primary" animation="border" />
- </div>
- );
- } else if (timelines === "offline") {
- return (
- <div className="d-flex flex-grow-1 justify-content-center align-items-center">
- <Trans i18nKey="loadFailReload" parent="div">
- 0
- <a
- href="#"
- onClick={(e) => {
- props.onReload();
- e.preventDefault();
- }}
- >
- 1
- </a>
- 2
- </Trans>
- </div>
- );
- } else {
- return timelines.map((timeline) => {
- const { name } = timeline;
- const isPersonal = name.startsWith("@");
- const url = isPersonal
- ? `/users/${timeline.owner.username}`
- : `/timelines/${name}`;
- return (
- <div key={name} className="timeline-board-item">
- {isPersonal ? (
- <UserTimelineLogo className="icon" />
- ) : (
- <TimelineLogo className="icon" />
- )}
- <Link to={url}>{name}</Link>
- </div>
- );
- });
- }
- })()}
- </div>
- );
-};
-
-export default TimelineBoard;
diff --git a/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx b/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx
deleted file mode 100644
index d9467719..00000000
--- a/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from "react";
-import { useHistory } from "react-router";
-
-import { validateTimelineName, timelineService } from "@/services/timeline";
-import OperationDialog from "../common/OperationDialog";
-
-interface TimelineCreateDialogProps {
- open: boolean;
- close: () => void;
-}
-
-const TimelineCreateDialog: React.FC<TimelineCreateDialogProps> = (props) => {
- const history = useHistory();
-
- let nameSaved: string;
-
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- titleColor="success"
- title="home.createDialog.title"
- inputScheme={[
- {
- type: "text",
- label: "home.createDialog.name",
- helperText: "home.createDialog.nameFormat",
- validator: (name) => {
- if (name.length === 0) {
- return "home.createDialog.noEmpty";
- } else if (name.length > 26) {
- return "home.createDialog.tooLong";
- } else if (!validateTimelineName(name)) {
- return "home.createDialog.badFormat";
- } else {
- return null;
- }
- },
- },
- ]}
- onProcess={([name]) => {
- nameSaved = name as string;
- return timelineService.createTimeline(nameSaved).toPromise();
- }}
- onSuccessAndClose={() => {
- history.push(`timelines/${nameSaved}`);
- }}
- failurePrompt={(e) => `${e as string}`}
- />
- );
-};
-
-export default TimelineCreateDialog;
diff --git a/Timeline/ClientApp/src/app/views/home/home.sass b/Timeline/ClientApp/src/app/views/home/home.sass
deleted file mode 100644
index f5d6ffc3..00000000
--- a/Timeline/ClientApp/src/app/views/home/home.sass
+++ /dev/null
@@ -1,13 +0,0 @@
-.timeline-board-item
- font-size: 1.1em
- @extend .my-2
- .icon
- height: 1.3em
- @extend .mr-2
-
-.timeline-board
- @extend .cru-card
- @extend .d-flex
- @extend .flex-column
- @extend .p-3
- min-height: 200px
diff --git a/Timeline/ClientApp/src/app/views/home/index.tsx b/Timeline/ClientApp/src/app/views/home/index.tsx
deleted file mode 100644
index 760adcea..00000000
--- a/Timeline/ClientApp/src/app/views/home/index.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from "react";
-import { useHistory } from "react-router";
-import { useTranslation } from "react-i18next";
-import { Row, Container, Button, Col } from "react-bootstrap";
-
-import { useUser } from "@/services/user";
-import SearchInput from "../common/SearchInput";
-
-import BoardWithoutUser from "./BoardWithoutUser";
-import BoardWithUser from "./BoardWithUser";
-import TimelineCreateDialog from "./TimelineCreateDialog";
-
-const HomePage: React.FC = () => {
- const history = useHistory();
-
- const { t } = useTranslation();
-
- const user = useUser();
-
- const [navText, setNavText] = React.useState<string>("");
-
- const [dialog, setDialog] = React.useState<"create" | null>(null);
-
- const goto = React.useCallback((): void => {
- if (navText === "") {
- history.push("users/crupest");
- } else if (navText.startsWith("@")) {
- history.push(`users/${navText.slice(1)}`);
- } else {
- history.push(`timelines/${navText}`);
- }
- }, [navText, history]);
-
- return (
- <>
- <Container fluid>
- <Row className="justify-content-center">
- <Col xs={12} sm={10} md={8} lg={6}>
- <SearchInput
- className="justify-content-center"
- value={navText}
- onChange={setNavText}
- onButtonClick={goto}
- buttonText={t("home.go")}
- placeholder="@crupest"
- additionalButton={
- user != null && (
- <Button
- variant="outline-success"
- onClick={() => {
- setDialog("create");
- }}
- >
- {t("home.createButton")}
- </Button>
- )
- }
- />
- </Col>
- </Row>
- {(() => {
- if (user == null) {
- return <BoardWithoutUser />;
- } else {
- return <BoardWithUser user={user} />;
- }
- })()}
- </Container>
- <footer className="text-right">
- <a
- className="mx-3 text-muted"
- href="http://beian.miit.gov.cn/"
- target="_blank"
- rel="noopener noreferrer"
- >
- <small>鄂ICP备18030913号-1</small>
- </a>
- <a
- className="mx-3 text-muted"
- href="http://www.beian.gov.cn/"
- target="_blank"
- rel="noopener noreferrer"
- >
- <small className="white-space-no-wrap">公安备案 42112102000124</small>
- </a>
- </footer>
- {dialog === "create" && (
- <TimelineCreateDialog
- open
- close={() => {
- setDialog(null);
- }}
- />
- )}
- </>
- );
-};
-
-export default HomePage;
diff --git a/Timeline/ClientApp/src/app/views/login/index.tsx b/Timeline/ClientApp/src/app/views/login/index.tsx
deleted file mode 100644
index 61b9a525..00000000
--- a/Timeline/ClientApp/src/app/views/login/index.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-import React from "react";
-import { useHistory } from "react-router";
-import { useTranslation } from "react-i18next";
-import { Container, Form } from "react-bootstrap";
-
-import { useUser, userService } from "@/services/user";
-
-import AppBar from "../common/AppBar";
-import LoadingButton from "../common/LoadingButton";
-
-const LoginPage: React.FC = (_) => {
- const { t } = useTranslation();
- const history = useHistory();
- const [username, setUsername] = React.useState<string>("");
- const [usernameDirty, setUsernameDirty] = React.useState<boolean>(false);
- const [password, setPassword] = React.useState<string>("");
- const [passwordDirty, setPasswordDirty] = React.useState<boolean>(false);
- const [rememberMe, setRememberMe] = React.useState<boolean>(true);
- const [process, setProcess] = React.useState<boolean>(false);
- const [error, setError] = React.useState<string | null>(null);
-
- const user = useUser();
-
- React.useEffect(() => {
- if (user != null) {
- const id = setTimeout(() => history.push("/"), 3000);
- return () => {
- clearTimeout(id);
- };
- }
- }, [history, user]);
-
- if (user != null) {
- return (
- <>
- <AppBar />
- <p className="mt-appbar">{t("login.alreadyLogin")}</p>
- </>
- );
- }
-
- const submit = (): void => {
- if (username === "" || password === "") {
- setUsernameDirty(true);
- setPasswordDirty(true);
- return;
- }
-
- setProcess(true);
- userService
- .login(
- {
- username: username,
- password: password,
- },
- rememberMe
- )
- .then(
- () => {
- if (history.length === 0) {
- history.push("/");
- } else {
- history.goBack();
- }
- },
- (e: Error) => {
- setProcess(false);
- setError(e.message);
- }
- );
- };
-
- const onEnterPressInPassword: React.KeyboardEventHandler = (e) => {
- if (e.key === "Enter") {
- submit();
- }
- };
-
- return (
- <Container fluid className="login-container mt-2">
- <h1 className="text-center">{t("welcome")}</h1>
- <Form>
- <Form.Group>
- <Form.Label htmlFor="username">{t("user.username")}</Form.Label>
- <Form.Control
- id="username"
- disabled={process}
- onChange={(e) => {
- setUsername(e.target.value);
- setUsernameDirty(true);
- }}
- value={username}
- isInvalid={usernameDirty && username === ""}
- />
- {usernameDirty && username === "" && (
- <Form.Control.Feedback type="invalid">
- {t("login.emptyUsername")}
- </Form.Control.Feedback>
- )}
- </Form.Group>
- <Form.Group>
- <Form.Label htmlFor="password">{t("user.password")}</Form.Label>
- <Form.Control
- id="password"
- type="password"
- disabled={process}
- onChange={(e) => {
- setPassword(e.target.value);
- setPasswordDirty(true);
- }}
- value={password}
- onKeyDown={onEnterPressInPassword}
- isInvalid={passwordDirty && password === ""}
- />
- {passwordDirty && password === "" && (
- <Form.Control.Feedback type="invalid">
- {t("login.emptyPassword")}
- </Form.Control.Feedback>
- )}
- </Form.Group>
- <Form.Group>
- <Form.Check<"input">
- id="remember-me"
- type="checkbox"
- checked={rememberMe}
- onChange={(e) => {
- setRememberMe(e.currentTarget.checked);
- }}
- label={t("user.rememberMe")}
- />
- </Form.Group>
- {error ? <p className="text-danger">{t(error)}</p> : null}
- <div className="text-right">
- <LoadingButton
- loading={process}
- variant="primary"
- onClick={(e) => {
- submit();
- e.preventDefault();
- }}
- disabled={username === "" || password === "" ? true : undefined}
- >
- {t("user.login")}
- </LoadingButton>
- </div>
- </Form>
- </Container>
- );
-};
-
-export default LoginPage;
diff --git a/Timeline/ClientApp/src/app/views/login/login.sass b/Timeline/ClientApp/src/app/views/login/login.sass
deleted file mode 100644
index 0bf385f5..00000000
--- a/Timeline/ClientApp/src/app/views/login/login.sass
+++ /dev/null
@@ -1,2 +0,0 @@
-.login-container
- max-width: 600px
diff --git a/Timeline/ClientApp/src/app/views/settings/index.tsx b/Timeline/ClientApp/src/app/views/settings/index.tsx
deleted file mode 100644
index 964e7442..00000000
--- a/Timeline/ClientApp/src/app/views/settings/index.tsx
+++ /dev/null
@@ -1,209 +0,0 @@
-import React, { useState } from "react";
-import { useHistory } from "react-router";
-import { useTranslation } from "react-i18next";
-import { Form, Container, Row, Col, Button, Modal } from "react-bootstrap";
-
-import { useUser, userService } from "@/services/user";
-import OperationDialog, {
- OperationInputErrorInfo,
-} from "../common/OperationDialog";
-
-interface ChangePasswordDialogProps {
- open: boolean;
- close: () => void;
-}
-
-const ChangePasswordDialog: React.FC<ChangePasswordDialogProps> = (props) => {
- const history = useHistory();
- const { t } = useTranslation();
-
- const [redirect, setRedirect] = useState<boolean>(false);
-
- return (
- <OperationDialog
- open={props.open}
- title={t("settings.dialogChangePassword.title")}
- titleColor="dangerous"
- inputPrompt={t("settings.dialogChangePassword.prompt")}
- inputScheme={[
- {
- type: "text",
- label: t("settings.dialogChangePassword.inputOldPassword"),
- password: true,
- validator: (v) =>
- v === ""
- ? "settings.dialogChangePassword.errorEmptyOldPassword"
- : null,
- },
- {
- type: "text",
- label: t("settings.dialogChangePassword.inputNewPassword"),
- password: true,
- validator: (v, values) => {
- const error: OperationInputErrorInfo = {};
- error[1] =
- v === ""
- ? "settings.dialogChangePassword.errorEmptyNewPassword"
- : null;
- if (v === values[2]) {
- error[2] = null;
- } else {
- if (values[2] !== "") {
- error[2] = "settings.dialogChangePassword.errorRetypeNotMatch";
- }
- }
- return error;
- },
- },
- {
- type: "text",
- label: t("settings.dialogChangePassword.inputRetypeNewPassword"),
- password: true,
- validator: (v, values) =>
- v !== values[1]
- ? "settings.dialogChangePassword.errorRetypeNotMatch"
- : null,
- },
- ]}
- onProcess={async ([oldPassword, newPassword]) => {
- await userService
- .changePassword(oldPassword as string, newPassword as string)
- .toPromise();
- await userService.logout();
- setRedirect(true);
- }}
- close={() => {
- props.close();
- if (redirect) {
- history.push("/login");
- }
- }}
- />
- );
-};
-
-const ConfirmLogoutDialog: React.FC<{
- toggle: () => void;
- onConfirm: () => void;
-}> = ({ toggle, onConfirm }) => {
- const { t } = useTranslation();
-
- return (
- <Modal show centered onHide={toggle}>
- <Modal.Header>
- <Modal.Title className="text-danger">
- {t("settings.dialogConfirmLogout.title")}
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>{t("settings.dialogConfirmLogout.prompt")}</Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={toggle}>
- {t("operationDialog.cancel")}
- </Button>
- <Button variant="danger" onClick={onConfirm}>
- {t("operationDialog.confirm")}
- </Button>
- </Modal.Footer>
- </Modal>
- );
-};
-
-const SettingsPage: React.FC = (_) => {
- const { i18n, t } = useTranslation();
- const user = useUser();
- const history = useHistory();
-
- const [dialog, setDialog] = useState<null | "changepassword" | "logout">(
- null
- );
-
- const language = i18n.language.slice(0, 2);
-
- return (
- <Container fluid>
- {user ? (
- <>
- <Row className="border-bottom p-3 cursor-pointer">
- <Col xs="12">
- <h5
- onClick={() => {
- history.push(`/users/${user.username}`);
- }}
- >
- {t("settings.gotoSelf")}
- </h5>
- </Col>
- </Row>
- <Row className="border-bottom p-3 cursor-pointer">
- <Col xs="12">
- <h5
- className="text-danger"
- onClick={() => setDialog("changepassword")}
- >
- {t("settings.changePassword")}
- </h5>
- </Col>
- </Row>
- <Row className="border-bottom p-3 cursor-pointer">
- <Col xs="12">
- <h5
- className="text-danger"
- onClick={() => {
- setDialog("logout");
- }}
- >
- {t("settings.logout")}
- </h5>
- </Col>
- </Row>
- </>
- ) : null}
- <Row className="align-items-center border-bottom p-3">
- <Col xs="12" sm="auto">
- <h5>{t("settings.languagePrimary")}</h5>
- <p>{t("settings.languageSecondary")}</p>
- </Col>
- <Col xs="auto" className="ml-auto">
- <Form.Control
- as="select"
- value={language}
- onChange={(e) => {
- void i18n.changeLanguage(e.target.value);
- }}
- >
- <option value="zh">中文</option>
- <option value="en">English</option>
- </Form.Control>
- </Col>
- </Row>
- {(() => {
- switch (dialog) {
- case "changepassword":
- return (
- <ChangePasswordDialog
- open
- close={() => {
- setDialog(null);
- }}
- />
- );
- case "logout":
- return (
- <ConfirmLogoutDialog
- toggle={() => setDialog(null)}
- onConfirm={() => {
- void userService.logout().then(() => {
- history.push("/");
- });
- }}
- />
- );
- default:
- return null;
- }
- })()}
- </Container>
- );
-};
-
-export default SettingsPage;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx b/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx
deleted file mode 100644
index 3c52150f..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-import Svg from "react-inlinesvg";
-import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg";
-import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg";
-
-const CollapseButton: React.FC<{
- collapse: boolean;
- onClick: () => void;
- className?: string;
- style?: React.CSSProperties;
-}> = ({ collapse, onClick, className, style }) => {
- return (
- <Svg
- src={collapse ? arrowsAngleExpandIcon : arrowsAngleContractIcon}
- onClick={onClick}
- className={clsx("text-primary icon-button", className)}
- style={style}
- />
- );
-};
-
-export default CollapseButton;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx b/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx
deleted file mode 100644
index a8de20aa..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-
-import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI";
-import SyncStatusBadge from "../timeline-common/SyncStatusBadge";
-import CollapseButton from "../timeline-common/CollapseButton";
-
-const InfoCardTemplate: React.FC<
- Pick<
- TimelineCardComponentProps<"">,
- "collapse" | "toggleCollapse" | "syncStatus" | "className"
- > & { children: React.ReactElement[] }
-> = ({ collapse, toggleCollapse, syncStatus, className, children }) => {
- return (
- <div className={clsx("cru-card p-2 clearfix", className)}>
- <div className="float-right d-flex align-items-center">
- <SyncStatusBadge status={syncStatus} className="mr-2" />
- <CollapseButton collapse={collapse} onClick={toggleCollapse} />
- </div>
-
- <div style={{ display: collapse ? "none" : "block" }}>{children}</div>
- </div>
- );
-};
-
-export default InfoCardTemplate;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx b/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx
deleted file mode 100644
index e67cfb43..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-import { useTranslation } from "react-i18next";
-
-import { UiLogicError } from "@/common";
-
-export type TimelineSyncStatus = "syncing" | "synced" | "offline";
-
-const SyncStatusBadge: React.FC<{
- status: TimelineSyncStatus;
- style?: React.CSSProperties;
- className?: string;
-}> = ({ status, style, className }) => {
- const { t } = useTranslation();
-
- return (
- <div style={style} className={clsx("timeline-sync-state-badge", className)}>
- {(() => {
- switch (status) {
- case "syncing": {
- return (
- <>
- <span className="timeline-sync-state-badge-pin bg-warning" />
- <span className="text-warning">
- {t("timeline.postSyncState.syncing")}
- </span>
- </>
- );
- }
- case "synced": {
- return (
- <>
- <span className="timeline-sync-state-badge-pin bg-success" />
- <span className="text-success">
- {t("timeline.postSyncState.synced")}
- </span>
- </>
- );
- }
- case "offline": {
- return (
- <>
- <span className="timeline-sync-state-badge-pin bg-danger" />
- <span className="text-danger">
- {t("timeline.postSyncState.offline")}
- </span>
- </>
- );
- }
- default:
- throw new UiLogicError("Unknown sync state.");
- }
- })()}
- </div>
- );
-};
-
-export default SyncStatusBadge;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx b/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx
deleted file mode 100644
index fd051d45..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-
-import { TimelinePostInfo } from "@/services/timeline";
-
-import TimelineItem from "./TimelineItem";
-
-export interface TimelinePostInfoEx extends TimelinePostInfo {
- deletable: boolean;
-}
-
-export type TimelineDeleteCallback = (index: number, id: number) => void;
-
-export interface TimelineProps {
- className?: string;
- posts: TimelinePostInfoEx[];
- onDelete: TimelineDeleteCallback;
- onResize?: () => void;
- containerRef?: React.Ref<HTMLDivElement>;
-}
-
-const Timeline: React.FC<TimelineProps> = (props) => {
- const { posts, onDelete, onResize } = props;
-
- const [indexShowDeleteButton, setIndexShowDeleteButton] = React.useState<
- number
- >(-1);
-
- const onItemClick = React.useCallback(() => {
- setIndexShowDeleteButton(-1);
- }, []);
-
- const onToggleDelete = React.useMemo(() => {
- return posts.map((post, i) => {
- return post.deletable
- ? () => {
- setIndexShowDeleteButton((oldIndexShowDeleteButton) => {
- return oldIndexShowDeleteButton !== i ? i : -1;
- });
- }
- : undefined;
- });
- }, [posts]);
-
- const onItemDelete = React.useMemo(() => {
- return posts.map((post, i) => {
- return () => {
- onDelete(i, post.id);
- };
- });
- }, [posts, onDelete]);
-
- return (
- <div ref={props.containerRef} className={clsx("timeline", props.className)}>
- {(() => {
- const length = posts.length;
- return posts.map((post, i) => {
- const toggleMore = onToggleDelete[i];
-
- return (
- <TimelineItem
- post={post}
- key={post.id}
- current={length - 1 === i}
- more={
- toggleMore
- ? {
- isOpen: indexShowDeleteButton === i,
- toggle: toggleMore,
- onDelete: onItemDelete[i],
- }
- : undefined
- }
- onClick={onItemClick}
- onResize={onResize}
- />
- );
- });
- })()}
- </div>
- );
-};
-
-export default Timeline;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx
deleted file mode 100644
index 4db23371..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-import { Link } from "react-router-dom";
-import { useTranslation } from "react-i18next";
-import Svg from "react-inlinesvg";
-import chevronDownIcon from "bootstrap-icons/icons/chevron-down.svg";
-import trashIcon from "bootstrap-icons/icons/trash.svg";
-import { Modal, Button } from "react-bootstrap";
-
-import { useAvatar } from "@/services/user";
-import { TimelinePostInfo } from "@/services/timeline";
-
-import BlobImage from "../common/BlobImage";
-
-const TimelinePostDeleteConfirmDialog: React.FC<{
- toggle: () => void;
- onConfirm: () => void;
-}> = ({ toggle, onConfirm }) => {
- const { t } = useTranslation();
-
- return (
- <Modal toggle={toggle} isOpen centered>
- <Modal.Header>
- <Modal.Title className="text-danger">
- {t("timeline.post.deleteDialog.title")}
- </Modal.Title>
- </Modal.Header>
- <Modal.Body>{t("timeline.post.deleteDialog.prompt")}</Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={toggle}>
- {t("operationDialog.cancel")}
- </Button>
- <Button
- variant="danger"
- onClick={() => {
- onConfirm();
- toggle();
- }}
- >
- {t("operationDialog.confirm")}
- </Button>
- </Modal.Footer>
- </Modal>
- );
-};
-
-export interface TimelineItemProps {
- post: TimelinePostInfo;
- current?: boolean;
- more?: {
- isOpen: boolean;
- toggle: () => void;
- onDelete: () => void;
- };
- onClick?: () => void;
- onResize?: () => void;
- className?: string;
- style?: React.CSSProperties;
-}
-
-const TimelineItem: React.FC<TimelineItemProps> = (props) => {
- const { i18n } = useTranslation();
-
- const current = props.current === true;
-
- const { more, onResize } = props;
-
- const avatar = useAvatar(props.post.author.username);
-
- const [deleteDialog, setDeleteDialog] = React.useState<boolean>(false);
- const toggleDeleteDialog = React.useCallback(
- () => setDeleteDialog((old) => !old),
- []
- );
-
- return (
- <div
- className={clsx(
- "timeline-item position-relative",
- current && "current",
- props.className
- )}
- onClick={props.onClick}
- style={props.style}
- >
- <div className="timeline-line-area-container">
- <div className="timeline-line-area">
- <div className="timeline-line-segment start"></div>
- <div className="timeline-line-node-container">
- <div className="timeline-line-node"></div>
- </div>
- <div className="timeline-line-segment end"></div>
- {current && <div className="timeline-line-segment current-end" />}
- </div>
- </div>
- <div className="timeline-content-area">
- <div>
- <span className="mr-2">
- <span className="text-primary white-space-no-wrap mr-2">
- {props.post.time.toLocaleString(i18n.languages)}
- </span>
- <small className="text-dark">{props.post.author.nickname}</small>
- </span>
- {more != null ? (
- <Svg
- src={chevronDownIcon}
- className="text-info icon-button"
- onClick={(e) => {
- more.toggle();
- e.stopPropagation();
- }}
- />
- ) : null}
- </div>
- <div className="timeline-content">
- <Link
- className="float-left m-2"
- to={"/users/" + props.post.author.username}
- >
- <BlobImage
- onLoad={onResize}
- blob={avatar}
- className="avatar rounded"
- />
- </Link>
- {(() => {
- const { content } = props.post;
- if (content.type === "text") {
- return content.text;
- } else {
- return (
- <BlobImage
- onLoad={onResize}
- blob={content.data}
- className="timeline-content-image"
- />
- );
- }
- })()}
- </div>
- </div>
- {more != null && more.isOpen ? (
- <>
- <div
- className="position-absolute position-lt w-100 h-100 mask d-flex justify-content-center align-items-center"
- onClick={more.toggle}
- >
- <Svg
- src={trashIcon}
- className="text-danger icon-button large"
- onClick={(e) => {
- toggleDeleteDialog();
- e.stopPropagation();
- }}
- />
- </div>
- {deleteDialog ? (
- <TimelinePostDeleteConfirmDialog
- toggle={() => {
- toggleDeleteDialog();
- more.toggle();
- }}
- onConfirm={more.onDelete}
- />
- ) : null}
- </>
- ) : null}
- </div>
- );
-};
-
-export default TimelineItem;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx
deleted file mode 100644
index 67a8543a..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-import React, { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Container, ListGroup, Modal, Row, Col, Button } from "react-bootstrap";
-
-import { User, useAvatar } from "@/services/user";
-
-import SearchInput from "../common/SearchInput";
-import BlobImage from "../common/BlobImage";
-
-const TimelineMemberItem: React.FC<{
- user: User;
- owner: boolean;
- onRemove?: (username: string) => void;
-}> = ({ user, owner, onRemove }) => {
- const { t } = useTranslation();
-
- const avatar = useAvatar(user.username);
-
- return (
- <ListGroup.Item className="container">
- <Row>
- <Col xs="auto">
- <BlobImage blob={avatar} className="avatar small" />
- </Col>
- <Col>
- <Row>{user.nickname}</Row>
- <Row>
- <small>{"@" + user.username}</small>
- </Row>
- </Col>
- {(() => {
- if (owner) {
- return null;
- }
- if (onRemove == null) {
- return null;
- }
- return (
- <Button
- className="align-self-center"
- variant="danger"
- onClick={() => {
- onRemove(user.username);
- }}
- >
- {t("timeline.member.remove")}
- </Button>
- );
- })()}
- </Row>
- </ListGroup.Item>
- );
-};
-
-export interface TimelineMemberCallbacks {
- onCheckUser: (username: string) => Promise<User | null>;
- onAddUser: (user: User) => Promise<void>;
- onRemoveUser: (username: string) => void;
-}
-
-export interface TimelineMemberProps {
- members: User[];
- edit: TimelineMemberCallbacks | null | undefined;
-}
-
-const TimelineMember: React.FC<TimelineMemberProps> = (props) => {
- const { t } = useTranslation();
-
- const [userSearchText, setUserSearchText] = useState<string>("");
- const [userSearchState, setUserSearchState] = useState<
- | {
- type: "user";
- data: User;
- }
- | { type: "error"; data: string }
- | { type: "loading" }
- | { type: "init" }
- >({ type: "init" });
-
- const userSearchAvatar = useAvatar(
- userSearchState.type === "user" ? userSearchState.data.username : undefined
- );
-
- const members = props.members;
-
- return (
- <Container className="px-4">
- <ListGroup className="my-3">
- {members.map((member, index) => (
- <TimelineMemberItem
- key={member.username}
- user={member}
- owner={index === 0}
- onRemove={props.edit?.onRemoveUser}
- />
- ))}
- </ListGroup>
- {(() => {
- const edit = props.edit;
- if (edit != null) {
- return (
- <>
- <SearchInput
- value={userSearchText}
- onChange={(v) => {
- setUserSearchText(v);
- }}
- loading={userSearchState.type === "loading"}
- onButtonClick={() => {
- if (userSearchText === "") {
- setUserSearchState({
- type: "error",
- data: "login.emptyUsername",
- });
- return;
- }
-
- setUserSearchState({ type: "loading" });
- edit.onCheckUser(userSearchText).then(
- (u) => {
- if (u == null) {
- setUserSearchState({
- type: "error",
- data: "timeline.userNotExist",
- });
- } else {
- setUserSearchState({ type: "user", data: u });
- }
- },
- (e) => {
- setUserSearchState({
- type: "error",
- data: `${e as string}`,
- });
- }
- );
- }}
- />
- {(() => {
- if (userSearchState.type === "user") {
- const u = userSearchState.data;
- const addable =
- members.findIndex((m) => m.username === u.username) === -1;
- return (
- <>
- {!addable ? (
- <p>{t("timeline.member.alreadyMember")}</p>
- ) : null}
- <Container className="mb-3">
- <Row>
- <Col className="col-auto">
- <BlobImage
- blob={userSearchAvatar}
- className="avatar small"
- />
- </Col>
- <Col>
- <Row>{u.nickname}</Row>
- <Row>
- <small>{"@" + u.username}</small>
- </Row>
- </Col>
- <Button
- variant="primary"
- className="align-self-center"
- disabled={!addable}
- onClick={() => {
- void edit.onAddUser(u).then((_) => {
- setUserSearchText("");
- setUserSearchState({ type: "init" });
- });
- }}
- >
- {t("timeline.member.add")}
- </Button>
- </Row>
- </Container>
- </>
- );
- } else if (userSearchState.type === "error") {
- return (
- <p className="text-danger">{t(userSearchState.data)}</p>
- );
- }
- })()}
- </>
- );
- } else {
- return null;
- }
- })()}
- </Container>
- );
-};
-
-export default TimelineMember;
-
-export interface TimelineMemberDialogProps extends TimelineMemberProps {
- open: boolean;
- onClose: () => void;
-}
-
-export const TimelineMemberDialog: React.FC<TimelineMemberDialogProps> = (
- props
-) => {
- return (
- <Modal show centered onHide={props.onClose}>
- <TimelineMember {...props} />
- </Modal>
- );
-};
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx
deleted file mode 100644
index d5c91622..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { of } from "rxjs";
-import { catchError } from "rxjs/operators";
-
-import { UiLogicError } from "@/common";
-import { pushAlert } from "@/services/alert";
-import { useUser, userInfoService, UserNotExistError } from "@/services/user";
-import {
- timelineService,
- usePostList,
- useTimelineInfo,
-} from "@/services/timeline";
-
-import { TimelineDeleteCallback } from "./Timeline";
-import { TimelineMemberDialog } from "./TimelineMember";
-import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog";
-import { TimelinePageTemplateUIProps } from "./TimelinePageTemplateUI";
-import { TimelinePostSendCallback } from "./TimelinePostEdit";
-
-export interface TimelinePageTemplateProps<TManageItem> {
- name: string;
- onManage: (item: TManageItem) => void;
- UiComponent: React.ComponentType<
- Omit<TimelinePageTemplateUIProps<TManageItem>, "CardComponent">
- >;
- notFoundI18nKey: string;
-}
-
-export default function TimelinePageTemplate<TManageItem>(
- props: TimelinePageTemplateProps<TManageItem>
-): React.ReactElement | null {
- const { t } = useTranslation();
-
- const { name } = props;
-
- const service = timelineService;
-
- const user = useUser();
-
- const [dialog, setDialog] = React.useState<null | "property" | "member">(
- null
- );
-
- const timelineState = useTimelineInfo(name);
-
- const timeline = timelineState?.timeline;
-
- const postListState = usePostList(name);
-
- const error: string | undefined = (() => {
- if (timelineState != null) {
- const { type, timeline } = timelineState;
- if (type === "offline" && timeline == null) return "Network Error";
- if (type === "synced" && timeline == null)
- return t(props.notFoundI18nKey);
- }
- return undefined;
- })();
-
- const closeDialog = React.useCallback((): void => {
- setDialog(null);
- }, []);
-
- let dialogElement: React.ReactElement | undefined;
-
- if (dialog === "property") {
- if (timeline == null) {
- throw new UiLogicError(
- "Timeline is null but attempt to open change property dialog."
- );
- }
-
- dialogElement = (
- <TimelinePropertyChangeDialog
- open
- close={closeDialog}
- oldInfo={{
- visibility: timeline.visibility,
- description: timeline.description,
- }}
- onProcess={(req) => {
- return service.changeTimelineProperty(name, req).toPromise().then();
- }}
- />
- );
- } else if (dialog === "member") {
- if (timeline == null) {
- throw new UiLogicError(
- "Timeline is null but attempt to open change property dialog."
- );
- }
-
- dialogElement = (
- <TimelineMemberDialog
- open
- onClose={closeDialog}
- members={[timeline.owner, ...timeline.members]}
- edit={
- service.hasManagePermission(user, timeline)
- ? {
- onCheckUser: (u) => {
- return userInfoService
- .getUserInfo(u)
- .pipe(
- catchError((e) => {
- if (e instanceof UserNotExistError) {
- return of(null);
- } else {
- throw e;
- }
- })
- )
- .toPromise();
- },
- onAddUser: (u) => {
- return service.addMember(name, u.username).toPromise().then();
- },
- onRemoveUser: (u) => {
- service.removeMember(name, u);
- },
- }
- : null
- }
- />
- );
- }
-
- const { UiComponent } = props;
-
- const onDelete: TimelineDeleteCallback = React.useCallback(
- (index, id) => {
- service.deletePost(name, id).subscribe(null, () => {
- pushAlert({
- type: "danger",
- message: t("timeline.deletePostFailed"),
- });
- });
- },
- [service, name, t]
- );
-
- const onPost: TimelinePostSendCallback = React.useCallback(
- (req) => {
- return service.createPost(name, req).toPromise().then();
- },
- [service, name]
- );
-
- const onManageProp = props.onManage;
-
- const onManage = React.useCallback(
- (item: "property" | TManageItem) => {
- if (item === "property") {
- setDialog(item);
- } else {
- onManageProp(item);
- }
- },
- [onManageProp]
- );
-
- return (
- <>
- <UiComponent
- error={error}
- timeline={timeline ?? undefined}
- postListState={postListState}
- onDelete={onDelete}
- onPost={
- timeline != null && service.hasPostPermission(user, timeline)
- ? onPost
- : undefined
- }
- onManage={
- timeline != null && service.hasManagePermission(user, timeline)
- ? onManage
- : undefined
- }
- onMember={() => setDialog("member")}
- />
- {dialogElement}
- </>
- );
-}
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
deleted file mode 100644
index 6c2c43c1..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { fromEvent } from "rxjs";
-import { Spinner } from "react-bootstrap";
-
-import { getAlertHost } from "@/services/alert";
-import { useEventEmiiter, UiLogicError } from "@/common";
-import {
- TimelineInfo,
- TimelinePostsWithSyncState,
- timelineService,
-} from "@/services/timeline";
-import { userService } from "@/services/user";
-
-import Timeline, {
- TimelinePostInfoEx,
- TimelineDeleteCallback,
-} from "./Timeline";
-import TimelineTop from "./TimelineTop";
-import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit";
-import { TimelineSyncStatus } from "./SyncStatusBadge";
-
-export interface TimelineCardComponentProps<TManageItems> {
- timeline: TimelineInfo;
- onManage?: (item: TManageItems | "property") => void;
- onMember: () => void;
- className?: string;
- collapse: boolean;
- syncStatus: TimelineSyncStatus;
- toggleCollapse: () => void;
-}
-
-export interface TimelinePageTemplateUIProps<TManageItems> {
- timeline?: TimelineInfo;
- postListState?: TimelinePostsWithSyncState;
- CardComponent: React.ComponentType<TimelineCardComponentProps<TManageItems>>;
- onMember: () => void;
- onManage?: (item: TManageItems | "property") => void;
- onPost?: TimelinePostSendCallback;
- onDelete: TimelineDeleteCallback;
- error?: string;
-}
-
-export default function TimelinePageTemplateUI<TManageItems>(
- props: TimelinePageTemplateUIProps<TManageItems>
-): React.ReactElement | null {
- const { timeline, postListState } = props;
-
- const { t } = useTranslation();
-
- const bottomSpaceRef = React.useRef<HTMLDivElement | null>(null);
-
- const onPostEditHeightChange = React.useCallback((height: number): void => {
- const { current: bottomSpaceDiv } = bottomSpaceRef;
- if (bottomSpaceDiv != null) {
- bottomSpaceDiv.style.height = `${height}px`;
- }
- if (height === 0) {
- const alertHost = getAlertHost();
- if (alertHost != null) {
- alertHost.style.removeProperty("margin-bottom");
- }
- } else {
- const alertHost = getAlertHost();
- if (alertHost != null) {
- alertHost.style.marginBottom = `${height}px`;
- }
- }
- }, []);
-
- const timelineRef = React.useRef<HTMLDivElement | null>(null);
-
- const [getResizeEvent, triggerResizeEvent] = useEventEmiiter();
-
- React.useEffect(() => {
- const { current: timelineElement } = timelineRef;
- if (timelineElement != null) {
- let loadingScrollToBottom = true;
- let pinBottom = false;
-
- const isAtBottom = (): boolean =>
- window.innerHeight + window.scrollY + 10 >= document.body.scrollHeight;
-
- const disableLoadingScrollToBottom = (): void => {
- loadingScrollToBottom = false;
- if (isAtBottom()) pinBottom = true;
- };
-
- const checkAndScrollToBottom = (): void => {
- if (loadingScrollToBottom || pinBottom) {
- window.scrollTo(0, document.body.scrollHeight);
- }
- };
-
- const subscriptions = [
- fromEvent(timelineElement, "wheel").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(timelineElement, "pointerdown").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(timelineElement, "keydown").subscribe(
- disableLoadingScrollToBottom
- ),
- fromEvent(window, "scroll").subscribe(() => {
- if (loadingScrollToBottom) return;
-
- if (isAtBottom()) {
- pinBottom = true;
- } else {
- pinBottom = false;
- }
- }),
- fromEvent(window, "resize").subscribe(checkAndScrollToBottom),
- getResizeEvent().subscribe(checkAndScrollToBottom),
- ];
-
- return () => {
- subscriptions.forEach((s) => s.unsubscribe());
- };
- }
- }, [getResizeEvent, triggerResizeEvent, timeline, postListState]);
-
- const genCardCollapseLocalStorageKey = (uniqueId: string): string =>
- `timeline.${uniqueId}.cardCollapse`;
-
- const cardCollapseLocalStorageKey =
- timeline != null ? genCardCollapseLocalStorageKey(timeline.uniqueId) : null;
-
- const [cardCollapse, setCardCollapse] = React.useState<boolean>(true);
- React.useEffect(() => {
- if (cardCollapseLocalStorageKey != null) {
- const savedCollapse =
- window.localStorage.getItem(cardCollapseLocalStorageKey) === "true";
- setCardCollapse(savedCollapse);
- }
- }, [cardCollapseLocalStorageKey]);
-
- const toggleCardCollapse = (): void => {
- const newState = !cardCollapse;
- setCardCollapse(newState);
- if (timeline != null) {
- window.localStorage.setItem(
- genCardCollapseLocalStorageKey(timeline.uniqueId),
- newState.toString()
- );
- }
- };
-
- let body: React.ReactElement;
-
- if (props.error != null) {
- body = <p className="text-danger">{t(props.error)}</p>;
- } else {
- if (timeline != null) {
- let timelineBody: React.ReactElement;
- if (postListState != null) {
- if (postListState.type === "notexist") {
- throw new UiLogicError(
- "Timeline is not null but post list state is notexist."
- );
- }
- if (postListState.type === "forbid") {
- timelineBody = (
- <p className="text-danger">{t("timeline.messageCantSee")}</p>
- );
- } else {
- const posts: TimelinePostInfoEx[] = postListState.posts.map(
- (post) => ({
- ...post,
- deletable: timelineService.hasModifyPostPermission(
- userService.currentUser,
- timeline,
- post
- ),
- })
- );
-
- timelineBody = (
- <Timeline
- containerRef={timelineRef}
- posts={posts}
- onDelete={props.onDelete}
- onResize={triggerResizeEvent}
- />
- );
- if (props.onPost != null) {
- timelineBody = (
- <>
- {timelineBody}
- <div ref={bottomSpaceRef} className="flex-fix-length" />
- <TimelinePostEdit
- className="fixed-bottom"
- onPost={props.onPost}
- onHeightChange={onPostEditHeightChange}
- timelineUniqueId={timeline.uniqueId}
- />
- </>
- );
- }
- }
- } else {
- timelineBody = (
- <div className="full-viewport-center-child">
- <Spinner variant="primary" animation="grow" />
- </div>
- );
- }
-
- const { CardComponent } = props;
- const syncStatus: TimelineSyncStatus =
- postListState == null || postListState.syncing
- ? "syncing"
- : postListState.type === "synced"
- ? "synced"
- : "offline";
-
- body = (
- <>
- <CardComponent
- className="timeline-template-card"
- timeline={timeline}
- onManage={props.onManage}
- onMember={props.onMember}
- syncStatus={syncStatus}
- collapse={cardCollapse}
- toggleCollapse={toggleCardCollapse}
- />
- <TimelineTop height="56px" />
- {timelineBody}
- </>
- );
- } else {
- body = (
- <div className="full-viewport-center-child">
- <Spinner variant="primary" animation="grow" />
- </div>
- );
- }
- }
-
- return body;
-}
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx
deleted file mode 100644
index dfa2f879..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx
+++ /dev/null
@@ -1,241 +0,0 @@
-import React from "react";
-import clsx from "clsx";
-import { useTranslation } from "react-i18next";
-import Svg from "react-inlinesvg";
-import { Button, Spinner, Row, Col, Form } from "react-bootstrap";
-import textIcon from "bootstrap-icons/icons/card-text.svg";
-import imageIcon from "bootstrap-icons/icons/image.svg";
-
-import { UiLogicError } from "@/common";
-
-import { pushAlert } from "@/services/alert";
-import { TimelineCreatePostRequest } from "@/services/timeline";
-
-interface TimelinePostEditImageProps {
- onSelect: (blob: Blob | null) => void;
-}
-
-const TimelinePostEditImage: React.FC<TimelinePostEditImageProps> = (props) => {
- const { onSelect } = props;
- const { t } = useTranslation();
-
- const [file, setFile] = React.useState<File | null>(null);
- const [fileUrl, setFileUrl] = React.useState<string | null>(null);
- const [error, setError] = React.useState<string | null>(null);
-
- React.useEffect(() => {
- if (file != null) {
- const url = URL.createObjectURL(file);
- setFileUrl(url);
- return () => {
- URL.revokeObjectURL(url);
- };
- }
- }, [file]);
-
- const onInputChange: React.ChangeEventHandler<HTMLInputElement> = React.useCallback(
- (e) => {
- const files = e.target.files;
- if (files == null || files.length === 0) {
- setFile(null);
- setFileUrl(null);
- } else {
- setFile(files[0]);
- }
- onSelect(null);
- setError(null);
- },
- [onSelect]
- );
-
- const onImgLoad = React.useCallback(() => {
- onSelect(file);
- }, [onSelect, file]);
-
- const onImgError = React.useCallback(() => {
- setError("loadImageError");
- }, []);
-
- return (
- <>
- <Form.File
- label={t("chooseImage")}
- onChange={onInputChange}
- accept="image/*"
- className="mx-3 my-1 d-inline-block"
- />
- {fileUrl && error == null && (
- <img
- src={fileUrl}
- className="timeline-post-edit-image"
- onLoad={onImgLoad}
- onError={onImgError}
- />
- )}
- {error != null && <div className="text-danger">{t(error)}</div>}
- </>
- );
-};
-
-export type TimelinePostSendCallback = (
- content: TimelineCreatePostRequest
-) => Promise<void>;
-
-export interface TimelinePostEditProps {
- className?: string;
- onPost: TimelinePostSendCallback;
- onHeightChange?: (height: number) => void;
- timelineUniqueId: string;
-}
-
-const TimelinePostEdit: React.FC<TimelinePostEditProps> = (props) => {
- const { onPost } = props;
-
- const { t } = useTranslation();
-
- const [state, setState] = React.useState<"input" | "process">("input");
- const [kind, setKind] = React.useState<"text" | "image">("text");
- const [text, setText] = React.useState<string>("");
- const [imageBlob, setImageBlob] = React.useState<Blob | null>(null);
-
- const draftLocalStorageKey = `timeline.${props.timelineUniqueId}.postDraft`;
-
- React.useEffect(() => {
- setText(window.localStorage.getItem(draftLocalStorageKey) ?? "");
- }, [draftLocalStorageKey]);
-
- const canSend = kind === "text" || (kind === "image" && imageBlob != null);
-
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const containerRef = React.useRef<HTMLDivElement>(null!);
-
- const notifyHeightChange = (): void => {
- if (props.onHeightChange) {
- props.onHeightChange(containerRef.current.clientHeight);
- }
- };
-
- React.useEffect(() => {
- if (props.onHeightChange) {
- props.onHeightChange(containerRef.current.clientHeight);
- }
- return () => {
- if (props.onHeightChange) {
- props.onHeightChange(0);
- }
- };
- });
-
- const toggleKind = React.useCallback(() => {
- setKind((oldKind) => (oldKind === "text" ? "image" : "text"));
- setImageBlob(null);
- }, []);
-
- const onSend = React.useCallback(() => {
- setState("process");
-
- const req: TimelineCreatePostRequest = (() => {
- switch (kind) {
- case "text":
- return {
- content: {
- type: "text",
- text: text,
- },
- } as TimelineCreatePostRequest;
- case "image":
- if (imageBlob == null) {
- throw new UiLogicError(
- "Content type is image but image blob is null."
- );
- }
- return {
- content: {
- type: "image",
- data: imageBlob,
- },
- } as TimelineCreatePostRequest;
- default:
- throw new UiLogicError("Unknown content type.");
- }
- })();
-
- onPost(req).then(
- (_) => {
- if (kind === "text") {
- setText("");
- window.localStorage.removeItem(draftLocalStorageKey);
- }
- setState("input");
- setKind("text");
- },
- (_) => {
- pushAlert({
- type: "danger",
- message: t("timeline.sendPostFailed"),
- });
- setState("input");
- }
- );
- }, [onPost, kind, text, imageBlob, t, draftLocalStorageKey]);
-
- const onImageSelect = React.useCallback((blob: Blob | null) => {
- setImageBlob(blob);
- }, []);
-
- return (
- <div
- ref={containerRef}
- className={clsx("container-fluid bg-light", props.className)}
- >
- <Row>
- <Col className="px-1 py-1">
- {kind === "text" ? (
- <Form.Control
- as="textarea"
- className="w-100 h-100 timeline-post-edit"
- value={text}
- disabled={state === "process"}
- onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
- const value = event.currentTarget.value;
- setText(value);
- window.localStorage.setItem(draftLocalStorageKey, value);
- }}
- />
- ) : (
- <TimelinePostEditImage onSelect={onImageSelect} />
- )}
- </Col>
- <Col xs="auto" className="align-self-end m-1">
- {(() => {
- if (state === "input") {
- return (
- <>
- <div className="d-block text-center mt-1 mb-2">
- <Svg
- onLoad={notifyHeightChange}
- src={kind === "text" ? imageIcon : textIcon}
- className="icon-button"
- onClick={toggleKind}
- />
- </div>
- <Button
- variant="primary"
- onClick={onSend}
- disabled={!canSend}
- >
- {t("timeline.send")}
- </Button>
- </>
- );
- } else {
- return <Spinner variant="primary" animation="border" />;
- }
- })()}
- </Col>
- </Row>
- </div>
- );
-};
-
-export default TimelinePostEdit;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
deleted file mode 100644
index 87638f31..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from "react";
-
-import {
- TimelineVisibility,
- kTimelineVisibilities,
- TimelineChangePropertyRequest,
-} from "@/services/timeline";
-
-import OperationDialog, {
- OperationSelectInputInfoOption,
-} from "../common/OperationDialog";
-
-export interface TimelinePropertyInfo {
- visibility: TimelineVisibility;
- description: string;
-}
-
-export interface TimelinePropertyChangeDialogProps {
- open: boolean;
- close: () => void;
- oldInfo: TimelinePropertyInfo;
- onProcess: (request: TimelineChangePropertyRequest) => Promise<void>;
-}
-
-const labelMap: { [key in TimelineVisibility]: string } = {
- Private: "timeline.visibility.private",
- Public: "timeline.visibility.public",
- Register: "timeline.visibility.register",
-};
-
-const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps> = (
- props
-) => {
- return (
- <OperationDialog
- title={"timeline.dialogChangeProperty.title"}
- titleColor="default"
- inputScheme={[
- {
- type: "select",
- label: "timeline.dialogChangeProperty.visibility",
- options: kTimelineVisibilities.map<OperationSelectInputInfoOption>(
- (v) => ({
- label: labelMap[v],
- value: v,
- })
- ),
- initValue: props.oldInfo.visibility,
- },
- {
- type: "text",
- label: "timeline.dialogChangeProperty.description",
- initValue: props.oldInfo.description,
- },
- ]}
- open={props.open}
- close={props.close}
- onProcess={([newVisibility, newDescription]) => {
- const req: TimelineChangePropertyRequest = {};
- if (newVisibility !== props.oldInfo.visibility) {
- req.visibility = newVisibility as TimelineVisibility;
- }
- if (newDescription !== props.oldInfo.description) {
- req.description = newDescription as string;
- }
- return props.onProcess(req);
- }}
- />
- );
-};
-
-export default TimelinePropertyChangeDialog;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx
deleted file mode 100644
index 93a2a32c..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from "react";
-
-export interface TimelineTopProps {
- height?: number | string;
- children?: React.ReactElement;
-}
-
-const TimelineTop: React.FC<TimelineTopProps> = ({ height, children }) => {
- return (
- <div style={{ height: height }} className="timeline-top">
- <div className="timeline-line-area-container">
- <div className="timeline-line-area">
- <div className="timeline-line-segment"></div>
- </div>
- </div>
- {children}
- </div>
- );
-};
-
-export default TimelineTop;
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass b/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass
deleted file mode 100644
index 4151bfcc..00000000
--- a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass
+++ /dev/null
@@ -1,146 +0,0 @@
-@use 'sass:color'
-
-.timeline
- z-index: 0
- position: relative
-
- &-item
- display: flex
-
-$timeline-line-width: 7px
-$timeline-line-node-radius: 18px
-$timeline-line-color: $primary
-$timeline-line-color-current: #36c2e6
-
-@keyframes timeline-line-node-noncurrent
- from
- background: $timeline-line-color
-
- to
- background: color.adjust($timeline-line-color, $lightness: +10%)
- box-shadow: 0 0 20px 3px color.adjust($timeline-line-color, $lightness: +10%, $alpha: -0.1)
-
-@keyframes timeline-line-node-current
- from
- background: $timeline-line-color-current
-
- to
- background: color.adjust($timeline-line-color-current, $lightness: +10%)
- box-shadow: 0 0 20px 3px color.adjust($timeline-line-color-current, $lightness: +10%, $alpha: -0.1)
-
-.timeline-line
- &-area-container
- display: flex
- justify-content: flex-end
- padding-right: 5px
-
- flex: 0 0 auto
- width: 60px
-
- &-area
- display: flex
- flex-direction: column
- align-items: center
- width: 30px
-
- &-segment
- width: $timeline-line-width
- background: $timeline-line-color
-
- &.start
- height: 14px
- flex: 0 0 auto
-
- &.end
- flex: 1 1 auto
-
- &.current-end
- height: 20px
- flex: 0 0 auto
- background: linear-gradient($timeline-line-color-current, transparent)
-
- &-node-container
- flex: 0 0 auto
- position: relative
- width: $timeline-line-node-radius
- height: $timeline-line-node-radius
-
- &-node
- width: $timeline-line-node-radius + 2
- height: $timeline-line-node-radius + 2
- position: absolute
- left: -1px
- top: -1px
- border-radius: 50%
- box-sizing: border-box
- z-index: 1
- animation: 1s infinite alternate
- animation-name: timeline-line-node-noncurrent
-
-.timeline-top
- display: flex
- justify-content: space-between
-
- .timeline-line-segment
- flex: 1 1 auto
-
-.current
- .timeline-line
- &-segment
-
- &.start
- background: linear-gradient($timeline-line-color, $timeline-line-color-current)
-
- &.end
- background: $timeline-line-color-current
-
- &-node
- animation-name: timeline-line-node-current
-
-.timeline-content-area
- padding: 10px 0
- flex-grow: 1
-
-.timeline-item-delete-button
- position: absolute
- right: 0
- bottom: 0
-
-.timeline-content
- white-space: pre-line
-
-.timeline-content-image
- max-width: 60%
- max-height: 200px
-
-.timeline-post-edit-image
- max-width: 100px
- max-height: 100px
-
-.mask
- background: change-color($color: white, $alpha: 0.8)
- z-index: 100
-
-.timeline-page-top-space
- transition: height 0.5s
-
-.timeline-sync-state-badge
- font-size: 0.8em
- padding: 3px 8px
- border-radius: 5px
- background: #e8fbff
-
-.timeline-sync-state-badge-pin
- display: inline-block
- width: 0.4em
- height: 0.4em
- border-radius: 50%
- vertical-align: middle
- margin-right: 0.6em
-
-.timeline-template-card
- position: fixed
- z-index: 1
- top: 56px
- right: 0
- margin: 0.5em
diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx
deleted file mode 100644
index 894b8195..00000000
--- a/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from "react";
-import { useHistory } from "react-router";
-import { Trans } from "react-i18next";
-
-import { timelineService } from "@/services/timeline";
-
-import OperationDialog from "../common/OperationDialog";
-
-interface TimelineDeleteDialog {
- open: boolean;
- name: string;
- close: () => void;
-}
-
-const TimelineDeleteDialog: React.FC<TimelineDeleteDialog> = (props) => {
- const history = useHistory();
-
- const { name } = props;
-
- return (
- <OperationDialog
- open={props.open}
- close={props.close}
- title="timeline.deleteDialog.title"
- titleColor="danger"
- inputPrompt={() => {
- return (
- <Trans i18nKey="timeline.deleteDialog.inputPrompt">
- 0<code className="mx-2">{{ name }}</code>2
- </Trans>
- );
- }}
- inputScheme={[
- {
- type: "text",
- validator: (value) => {
- if (value !== name) {
- return "timeline.deleteDialog.notMatch";
- } else {
- return null;
- }
- },
- },
- ]}
- onProcess={() => {
- return timelineService.deleteTimeline(name).toPromise();
- }}
- onSuccessAndClose={() => {
- history.replace("/");
- }}
- />
- );
-};
-
-export default TimelineDeleteDialog;
diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx
deleted file mode 100644
index 2d787709..00000000
--- a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { Dropdown, Button } from "react-bootstrap";
-
-import { useAvatar } from "@/services/user";
-import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline";
-
-import BlobImage from "../common/BlobImage";
-import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI";
-import InfoCardTemplate from "../timeline-common/InfoCardTemplate";
-
-export type OrdinaryTimelineManageItem = "delete";
-
-export type TimelineInfoCardProps = TimelineCardComponentProps<
- OrdinaryTimelineManageItem
->;
-
-const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => {
- const {
- timeline,
- collapse,
- onMember,
- onManage,
- syncStatus,
- toggleCollapse,
- } = props;
-
- const { t } = useTranslation();
-
- const avatar = useAvatar(timeline?.owner?.username);
-
- return (
- <InfoCardTemplate
- className={props.className}
- syncStatus={syncStatus}
- collapse={collapse}
- toggleCollapse={toggleCollapse}
- >
- <h3 className="text-primary mx-3 d-inline-block align-middle">
- {timeline.name}
- </h3>
- <div className="d-inline-block align-middle">
- <BlobImage blob={avatar} className="avatar small rounded-circle" />
- {timeline.owner.nickname}
- <small className="ml-3 text-secondary">
- @{timeline.owner.username}
- </small>
- </div>
- <p className="mb-0">{timeline.description}</p>
- <small className="mt-1 d-block">
- {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])}
- </small>
- <div className="text-right mt-2">
- {onManage != null ? (
- <Dropdown>
- <Dropdown.Toggle variant="outline-primary">
- {t("timeline.manage")}
- </Dropdown.Toggle>
- <Dropdown.Menu>
- <Dropdown.Item onClick={() => onManage("property")}>
- {t("timeline.manageItem.property")}
- </Dropdown.Item>
- <Dropdown.Item onClick={onMember}>
- {t("timeline.manageItem.member")}
- </Dropdown.Item>
- <Dropdown.Divider />
- <Dropdown.Item
- className="text-danger"
- onClick={() => onManage("delete")}
- >
- {t("timeline.manageItem.delete")}
- </Dropdown.Item>
- </Dropdown.Menu>
- </Dropdown>
- ) : (
- <Button variant="outline-primary" onClick={onMember}>
- {t("timeline.memberButton")}
- </Button>
- )}
- </div>
- </InfoCardTemplate>
- );
-};
-
-export default TimelineInfoCard;
diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx
deleted file mode 100644
index 67ea699e..00000000
--- a/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import React from "react";
-
-import TimelinePageTemplateUI, {
- TimelinePageTemplateUIProps,
-} from "../timeline-common/TimelinePageTemplateUI";
-
-import TimelineInfoCard, {
- OrdinaryTimelineManageItem,
-} from "./TimelineInfoCard";
-
-export type TimelinePageUIProps = Omit<
- TimelinePageTemplateUIProps<OrdinaryTimelineManageItem>,
- "CardComponent"
->;
-
-const TimelinePageUI: React.FC<TimelinePageUIProps> = (props) => {
- return <TimelinePageTemplateUI {...props} CardComponent={TimelineInfoCard} />;
-};
-
-export default TimelinePageUI;
diff --git a/Timeline/ClientApp/src/app/views/timeline/index.tsx b/Timeline/ClientApp/src/app/views/timeline/index.tsx
deleted file mode 100644
index 225a1a59..00000000
--- a/Timeline/ClientApp/src/app/views/timeline/index.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from "react";
-import { useParams } from "react-router";
-
-import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate";
-
-import TimelinePageUI from "./TimelinePageUI";
-import { OrdinaryTimelineManageItem } from "./TimelineInfoCard";
-import TimelineDeleteDialog from "./TimelineDeleteDialog";
-
-const TimelinePage: React.FC = (_) => {
- const { name } = useParams<{ name: string }>();
-
- const [dialog, setDialog] = React.useState<OrdinaryTimelineManageItem | null>(
- null
- );
-
- let dialogElement: React.ReactElement | undefined;
- if (dialog === "delete") {
- dialogElement = (
- <TimelineDeleteDialog open close={() => setDialog(null)} name={name} />
- );
- }
-
- return (
- <>
- <TimelinePageTemplate
- name={name}
- UiComponent={TimelinePageUI}
- onManage={(item) => setDialog(item)}
- notFoundI18nKey="timeline.timelineNotExist"
- />
- {dialogElement}
- </>
- );
-};
-
-export default TimelinePage;
diff --git a/Timeline/ClientApp/src/app/views/timeline/timeline.sass b/Timeline/ClientApp/src/app/views/timeline/timeline.sass
deleted file mode 100644
index e69de29b..00000000
--- a/Timeline/ClientApp/src/app/views/timeline/timeline.sass
+++ /dev/null
diff --git a/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx b/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx
deleted file mode 100644
index ffa2218b..00000000
--- a/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx
+++ /dev/null
@@ -1,302 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { useTranslation } from "react-i18next";
-import { AxiosError } from "axios";
-import { Modal, Row, Button } from "react-bootstrap";
-
-import { UiLogicError } from "@/common";
-
-import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper";
-
-export interface ChangeAvatarDialogProps {
- open: boolean;
- close: () => void;
- process: (blob: Blob) => Promise<void>;
-}
-
-const ChangeAvatarDialog: React.FC<ChangeAvatarDialogProps> = (props) => {
- const { t } = useTranslation();
-
- const [file, setFile] = React.useState<File | null>(null);
- const [fileUrl, setFileUrl] = React.useState<string | null>(null);
- const [clip, setClip] = React.useState<Clip | null>(null);
- const [
- cropImgElement,
- setCropImgElement,
- ] = React.useState<HTMLImageElement | null>(null);
- const [resultBlob, setResultBlob] = React.useState<Blob | null>(null);
- const [resultUrl, setResultUrl] = React.useState<string | null>(null);
-
- const [state, setState] = React.useState<
- | "select"
- | "crop"
- | "processcrop"
- | "preview"
- | "uploading"
- | "success"
- | "error"
- >("select");
-
- const [message, setMessage] = useState<
- string | { type: "custom"; text: string } | null
- >("userPage.dialogChangeAvatar.prompt.select");
-
- const trueMessage =
- message == null
- ? null
- : typeof message === "string"
- ? t(message)
- : message.text;
-
- const closeDialog = props.close;
-
- const close = React.useCallback((): void => {
- if (!(state === "uploading")) {
- closeDialog();
- }
- }, [state, closeDialog]);
-
- useEffect(() => {
- if (file != null) {
- const url = URL.createObjectURL(file);
- setClip(null);
- setFileUrl(url);
- setState("crop");
- return () => {
- URL.revokeObjectURL(url);
- };
- } else {
- setFileUrl(null);
- setState("select");
- }
- }, [file]);
-
- React.useEffect(() => {
- if (resultBlob != null) {
- const url = URL.createObjectURL(resultBlob);
- setResultUrl(url);
- setState("preview");
- return () => {
- URL.revokeObjectURL(url);
- };
- } else {
- setResultUrl(null);
- }
- }, [resultBlob]);
-
- const onSelectFile = React.useCallback(
- (e: React.ChangeEvent<HTMLInputElement>): void => {
- const files = e.target.files;
- if (files == null || files.length === 0) {
- setFile(null);
- } else {
- setFile(files[0]);
- }
- },
- []
- );
-
- const onCropNext = React.useCallback(() => {
- if (
- cropImgElement == null ||
- clip == null ||
- clip.width === 0 ||
- file == null
- ) {
- throw new UiLogicError();
- }
-
- setState("processcrop");
- void applyClipToImage(cropImgElement, clip, file.type).then((b) => {
- setResultBlob(b);
- });
- }, [cropImgElement, clip, file]);
-
- const onCropPrevious = React.useCallback(() => {
- setFile(null);
- setState("select");
- }, []);
-
- const onPreviewPrevious = React.useCallback(() => {
- setResultBlob(null);
- setState("crop");
- }, []);
-
- const process = props.process;
-
- const upload = React.useCallback(() => {
- if (resultBlob == null) {
- throw new UiLogicError();
- }
-
- setState("uploading");
- process(resultBlob).then(
- () => {
- setState("success");
- },
- (e: unknown) => {
- setState("error");
- setMessage({ type: "custom", text: (e as AxiosError).message });
- }
- );
- }, [resultBlob, process]);
-
- const createPreviewRow = (): React.ReactElement => {
- if (resultUrl == null) {
- throw new UiLogicError();
- }
- return (
- <Row className="justify-content-center">
- <img
- className="change-avatar-img"
- src={resultUrl}
- alt={t("userPage.dialogChangeAvatar.previewImgAlt")}
- />
- </Row>
- );
- };
-
- return (
- <Modal show={props.open} onHide={close}>
- <Modal.Header>
- <Modal.Title> {t("userPage.dialogChangeAvatar.title")}</Modal.Title>
- </Modal.Header>
- {(() => {
- if (state === "select") {
- return (
- <>
- <Modal.Body className="container">
- <Row>{t("userPage.dialogChangeAvatar.prompt.select")}</Row>
- <Row>
- <input type="file" accept="image/*" onChange={onSelectFile} />
- </Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- </Modal.Footer>
- </>
- );
- } else if (state === "crop") {
- if (fileUrl == null) {
- throw new UiLogicError();
- }
- return (
- <>
- <Modal.Body className="container">
- <Row className="justify-content-center">
- <ImageCropper
- clip={clip}
- onChange={setClip}
- imageUrl={fileUrl}
- imageElementCallback={setCropImgElement}
- />
- </Row>
- <Row>{t("userPage.dialogChangeAvatar.prompt.crop")}</Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- <Button variant="secondary" onClick={onCropPrevious}>
- {t("operationDialog.previousStep")}
- </Button>
- <Button
- color="primary"
- onClick={onCropNext}
- disabled={
- cropImgElement == null || clip == null || clip.width === 0
- }
- >
- {t("operationDialog.nextStep")}
- </Button>
- </Modal.Footer>
- </>
- );
- } else if (state === "processcrop") {
- return (
- <>
- <Modal.Body className="container">
- <Row>
- {t("userPage.dialogChangeAvatar.prompt.processingCrop")}
- </Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- <Button variant="secondary" onClick={onPreviewPrevious}>
- {t("operationDialog.previousStep")}
- </Button>
- </Modal.Footer>
- </>
- );
- } else if (state === "preview") {
- return (
- <>
- <Modal.Body className="container">
- {createPreviewRow()}
- <Row>{t("userPage.dialogChangeAvatar.prompt.preview")}</Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- <Button variant="secondary" onClick={onPreviewPrevious}>
- {t("operationDialog.previousStep")}
- </Button>
- <Button variant="primary" onClick={upload}>
- {t("userPage.dialogChangeAvatar.upload")}
- </Button>
- </Modal.Footer>
- </>
- );
- } else if (state === "uploading") {
- return (
- <>
- <Modal.Body className="container">
- {createPreviewRow()}
- <Row>{t("userPage.dialogChangeAvatar.prompt.uploading")}</Row>
- </Modal.Body>
- <Modal.Footer></Modal.Footer>
- </>
- );
- } else if (state === "success") {
- return (
- <>
- <Modal.Body className="container">
- <Row className="p-4 text-success">
- {t("operationDialog.success")}
- </Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="success" onClick={close}>
- {t("operationDialog.ok")}
- </Button>
- </Modal.Footer>
- </>
- );
- } else {
- return (
- <>
- <Modal.Body className="container">
- {createPreviewRow()}
- <Row className="text-danger">{trueMessage}</Row>
- </Modal.Body>
- <Modal.Footer>
- <Button variant="secondary" onClick={close}>
- {t("operationDialog.cancel")}
- </Button>
- <Button variant="primary" onClick={upload}>
- {t("operationDialog.retry")}
- </Button>
- </Modal.Footer>
- </>
- );
- }
- })()}
- </Modal>
- );
-};
-
-export default ChangeAvatarDialog;
diff --git a/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx b/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx
deleted file mode 100644
index 251b18c5..00000000
--- a/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from "react";
-
-import OperationDialog from "../common/OperationDialog";
-
-export interface ChangeNicknameDialogProps {
- open: boolean;
- close: () => void;
- onProcess: (newNickname: string) => Promise<void>;
-}
-
-const ChangeNicknameDialog: React.FC<ChangeNicknameDialogProps> = (props) => {
- return (
- <OperationDialog
- open={props.open}
- title="userPage.dialogChangeNickname.title"
- titleColor="default"
- inputScheme={[
- { type: "text", label: "userPage.dialogChangeNickname.inputLabel" },
- ]}
- onProcess={([newNickname]) => {
- return props.onProcess(newNickname as string);
- }}
- close={props.close}
- />
- );
-};
-
-export default ChangeNicknameDialog;
diff --git a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx b/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx
deleted file mode 100644
index 888fb18a..00000000
--- a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { Dropdown, Button } from "react-bootstrap";
-
-import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline";
-import { useAvatar } from "@/services/user";
-
-import BlobImage from "../common/BlobImage";
-import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI";
-import InfoCardTemplate from "../timeline-common/InfoCardTemplate";
-
-export type PersonalTimelineManageItem = "avatar" | "nickname";
-
-export type UserInfoCardProps = TimelineCardComponentProps<
- PersonalTimelineManageItem
->;
-
-const UserInfoCard: React.FC<UserInfoCardProps> = (props) => {
- const {
- timeline,
- collapse,
- onMember,
- onManage,
- syncStatus,
- toggleCollapse,
- } = props;
- const { t } = useTranslation();
-
- const avatar = useAvatar(timeline?.owner?.username);
-
- return (
- <InfoCardTemplate
- className={props.className}
- syncStatus={syncStatus}
- collapse={collapse}
- toggleCollapse={toggleCollapse}
- >
- <div>
- <BlobImage blob={avatar} className="avatar" />
- {timeline.owner.nickname}
- <small className="ml-3 text-secondary">
- @{timeline.owner.username}
- </small>
- </div>
- <p className="mb-0">{timeline.description}</p>
- <small className="mt-1 d-block">
- {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])}
- </small>
- <div className="text-right mt-2">
- {onManage != null ? (
- <Dropdown>
- <Dropdown.Toggle variant="outline-primary">
- {t("timeline.manage")}
- </Dropdown.Toggle>
- <Dropdown.Menu>
- <Dropdown.Item onClick={() => onManage("nickname")}>
- {t("timeline.manageItem.nickname")}
- </Dropdown.Item>
- <Dropdown.Item onClick={() => onManage("avatar")}>
- {t("timeline.manageItem.avatar")}
- </Dropdown.Item>
- <Dropdown.Item onClick={() => onManage("property")}>
- {t("timeline.manageItem.property")}
- </Dropdown.Item>
- <Dropdown.Item onClick={onMember}>
- {t("timeline.manageItem.member")}
- </Dropdown.Item>
- </Dropdown.Menu>
- </Dropdown>
- ) : (
- <Button variant="outline-primary" onClick={onMember}>
- {t("timeline.memberButton")}
- </Button>
- )}
- </div>
- </InfoCardTemplate>
- );
-};
-
-export default UserInfoCard;
diff --git a/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx b/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx
deleted file mode 100644
index d405399c..00000000
--- a/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-
-import TimelinePageTemplateUI, {
- TimelinePageTemplateUIProps,
-} from "../timeline-common/TimelinePageTemplateUI";
-
-import UserInfoCard, { PersonalTimelineManageItem } from "./UserInfoCard";
-
-export type UserPageUIProps = Omit<
- TimelinePageTemplateUIProps<PersonalTimelineManageItem>,
- "CardComponent"
->;
-
-const UserPageUI: React.FC<UserPageUIProps> = (props) => {
- return <TimelinePageTemplateUI {...props} CardComponent={UserInfoCard} />;
-};
-
-export default UserPageUI;
diff --git a/Timeline/ClientApp/src/app/views/user/index.tsx b/Timeline/ClientApp/src/app/views/user/index.tsx
deleted file mode 100644
index 7c0b1563..00000000
--- a/Timeline/ClientApp/src/app/views/user/index.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React, { useState } from "react";
-import { useParams } from "react-router";
-
-import { UiLogicError } from "@/common";
-import { useUser, userInfoService } from "@/services/user";
-
-import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate";
-
-import UserPageUI from "./UserPageUI";
-import { PersonalTimelineManageItem } from "./UserInfoCard";
-import ChangeNicknameDialog from "./ChangeNicknameDialog";
-import ChangeAvatarDialog from "./ChangeAvatarDialog";
-
-const UserPage: React.FC = (_) => {
- const { username } = useParams<{ username: string }>();
-
- const user = useUser();
-
- const [dialog, setDialog] = useState<null | PersonalTimelineManageItem>(null);
-
- let dialogElement: React.ReactElement | undefined;
-
- const closeDialogHandler = (): void => {
- setDialog(null);
- };
-
- if (dialog === "nickname") {
- if (user == null) {
- throw new UiLogicError("Change nickname without login.");
- }
-
- dialogElement = (
- <ChangeNicknameDialog
- open
- close={closeDialogHandler}
- onProcess={(newNickname) =>
- userInfoService.setNickname(username, newNickname)
- }
- />
- );
- } else if (dialog === "avatar") {
- if (user == null) {
- throw new UiLogicError("Change avatar without login.");
- }
-
- dialogElement = (
- <ChangeAvatarDialog
- open
- close={closeDialogHandler}
- process={(file) => userInfoService.setAvatar(username, file)}
- />
- );
- }
-
- const onManage = React.useCallback((item: PersonalTimelineManageItem) => {
- setDialog(item);
- }, []);
-
- return (
- <>
- <TimelinePageTemplate
- name={`@${username}`}
- UiComponent={UserPageUI}
- onManage={onManage}
- notFoundI18nKey="timeline.userNotExist"
- />
- {dialogElement}
- </>
- );
-};
-
-export default UserPage;
diff --git a/Timeline/ClientApp/src/app/views/user/user.sass b/Timeline/ClientApp/src/app/views/user/user.sass
deleted file mode 100644
index 5b7fcae7..00000000
--- a/Timeline/ClientApp/src/app/views/user/user.sass
+++ /dev/null
@@ -1,7 +0,0 @@
-.change-avatar-cropper-row
- max-height: 400px
-
-.change-avatar-img
- min-width: 50%
- max-width: 100%
- max-height: 400px
diff --git a/Timeline/ClientApp/src/sw/sw.ts b/Timeline/ClientApp/src/sw/sw.ts
deleted file mode 100644
index d6202f36..00000000
--- a/Timeline/ClientApp/src/sw/sw.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { precacheAndRoute, matchPrecache } from "workbox-precaching";
-import { setDefaultHandler } from "workbox-routing";
-import { NetworkOnly } from "workbox-strategies";
-
-declare let self: ServiceWorkerGlobalScope;
-
-self.addEventListener("message", (event) => {
- if (event.data && (event.data as { type: string }).type === "SKIP_WAITING") {
- void self.skipWaiting();
- }
-});
-
-precacheAndRoute(self.__WB_MANIFEST);
-
-const networkOnly = new NetworkOnly();
-
-setDefaultHandler((options) => {
- const { request, url } = options;
- if (url && url.pathname.startsWith("/api/")) {
- return networkOnly.handle(options);
- }
-
- if (request instanceof Request && request.destination === "document")
- return matchPrecache("/index.html").then((r) =>
- r == null ? Response.error() : r
- );
- else return networkOnly.handle(options);
-});
diff --git a/Timeline/ClientApp/src/sw/tsconfig.json b/Timeline/ClientApp/src/sw/tsconfig.json
deleted file mode 100644
index aac99e59..00000000
--- a/Timeline/ClientApp/src/sw/tsconfig.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "lib": [
- "esnext",
- "webworker"
- ]
- },
- "include": [
- "."
- ]
-}
diff --git a/Timeline/ClientApp/src/tsconfig.json b/Timeline/ClientApp/src/tsconfig.json
deleted file mode 100644
index 6937be63..00000000
--- a/Timeline/ClientApp/src/tsconfig.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "compilerOptions": {
- "target": "esnext",
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "sourceMap": true,
- "baseUrl": "./",
- "paths": {
- "@/*": [
- "app/*"
- ]
- }
- }
-}
diff --git a/Timeline/ClientApp/webpack.common.js b/Timeline/ClientApp/webpack.common.js
deleted file mode 100644
index 3779003e..00000000
--- a/Timeline/ClientApp/webpack.common.js
+++ /dev/null
@@ -1,86 +0,0 @@
-const path = require("path");
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const postcssPresetEnv = require("postcss-preset-env");
-const Config = require("webpack-chain");
-
-const config = new Config();
-
-config.entry("index").add(path.resolve(__dirname, "src/app/index.tsx"));
-
-config.module
- .rule("ts")
- .test(/\.ts(x?)$/)
- .exclude.add(/node_modules/)
- .end()
- .use("babel")
- .loader("babel-loader")
- .end()
- .use("ts")
- .loader("ts-loader")
- .end();
-
-config.module
- .rule("js")
- .test(/\.js(x?)$/)
- .exclude.add(/node_modules/)
- .end()
- .use("babel")
- .loader("babel-loader")
- .end();
-
-config.module
- .rule("css")
- .test(/\.css$/)
- .use("css")
- .loader("css-loader")
- .end()
- .use("postcss")
- .loader("postcss-loader")
- .end();
-
-config.module
- .rule("sass")
- .test(/\.(scss|sass)$/)
- .use("css")
- .loader("css-loader")
- .end()
- .use("postcss")
- .loader("postcss-loader")
- .end()
- .use("sass")
- .loader("sass-loader")
- .end();
-
-config.module
- .rule("file")
- .test(/\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot)$/i)
- .use("url")
- .loader("url-loader")
- .options({
- limit: 8192,
- });
-
-config.resolve.extensions
- .add("*")
- .add(".js")
- .add(".jsx")
- .add(".ts")
- .add(".tsx")
- .end();
-
-config.resolve.alias.set("@", path.resolve(__dirname, "src/app"));
-
-config.output
- .path(path.resolve(__dirname, "dist/"))
- .filename("[name].[contenthash].js")
- .chunkFilename("[name].[contenthash].js")
- .publicPath("/");
-
-config.plugin("html").use(HtmlWebpackPlugin, [
- {
- template: "src/app/index.ejs",
- title: "Timeline",
- },
-]);
-
-module.exports = config;
diff --git a/Timeline/ClientApp/webpack.config.dev.js b/Timeline/ClientApp/webpack.config.dev.js
deleted file mode 100644
index c88e1aaf..00000000
--- a/Timeline/ClientApp/webpack.config.dev.js
+++ /dev/null
@@ -1,52 +0,0 @@
-const path = require("path");
-const webpack = require("webpack");
-
-const config = require("./webpack.common");
-
-config.mode("development");
-
-config.entry("index").add("react-hot-loader/patch");
-
-config.module
- .rule("ts")
- .use("babel")
- .options({
- plugins: ["react-hot-loader/babel"],
- });
-
-config.module
- .rule("js")
- .use("babel")
- .options({
- plugins: ["react-hot-loader/babel"],
- });
-
-config.module
- .rule("css")
- .use("style")
- .before("css")
- .loader("style-loader")
- .end();
-
-config.module
- .rule("sass")
- .use("style")
- .before("css")
- .loader("style-loader")
- .end();
-
-config.devtool("eval-cheap-module-source-map");
-
-config.resolve.alias.set("react-dom", "@hot-loader/react-dom");
-
-config.devServer
- .contentBase(path.resolve(__dirname, "public/"))
- .host("0.0.0.0")
- .port(3000)
- .historyApiFallback(true)
- .hotOnly(true)
- .allowedHosts.add(".myide.io");
-
-config.plugin("hot").use(webpack.HotModuleReplacementPlugin);
-
-module.exports = config.toConfig();
diff --git a/Timeline/ClientApp/webpack.config.prod.js b/Timeline/ClientApp/webpack.config.prod.js
deleted file mode 100644
index 188cb940..00000000
--- a/Timeline/ClientApp/webpack.config.prod.js
+++ /dev/null
@@ -1,53 +0,0 @@
-const path = require("path");
-const { CleanWebpackPlugin } = require("clean-webpack-plugin");
-const CopyPlugin = require("copy-webpack-plugin");
-const WorkboxPlugin = require("workbox-webpack-plugin");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
-const config = require("./webpack.common");
-
-config.mode("production");
-
-config
- .entry("index")
- .add(path.resolve(__dirname, "src/app/service-worker.tsx"));
-
-config.module
- .rule("css")
- .use("mini-css-extract")
- .before("css")
- .loader(MiniCssExtractPlugin.loader)
- .end();
-
-config.module
- .rule("sass")
- .use("mini-css-extract")
- .before("css")
- .loader(MiniCssExtractPlugin.loader)
- .end();
-
-config.devtool("source-map");
-
-config.plugin("mini-css-extract").use(MiniCssExtractPlugin);
-
-config.plugin("clean").use(CleanWebpackPlugin);
-
-config.plugin("copy").use(CopyPlugin, [
- {
- patterns: [
- {
- from: path.resolve(__dirname, "public/"),
- to: path.resolve(__dirname, "dist/"),
- },
- ],
- },
-]);
-
-config.plugin("workbox").use(WorkboxPlugin.InjectManifest, [
- {
- swSrc: path.resolve(__dirname, "src/sw/sw.ts"),
- maximumFileSizeToCacheInBytes: 15000000,
- },
-]);
-
-module.exports = config.toConfig();
diff --git a/Timeline/Configs/ApplicationConfiguration.cs b/Timeline/Configs/ApplicationConfiguration.cs
deleted file mode 100644
index df281adb..00000000
--- a/Timeline/Configs/ApplicationConfiguration.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Timeline.Configs
-{
- public static class ApplicationConfiguration
- {
- public const string WorkDirKey = "WorkDir";
- public const string DefaultWorkDir = "/timeline";
- public const string DatabaseFileName = "timeline.db";
- public const string DatabaseBackupDirectoryName = "backup";
- public const string DisableFrontEndKey = "DisableFrontEnd";
- public const string UseMockFrontEndKey = "UseMockFrontEnd";
- public const string UseProxyFrontEndKey = "UseProxyFrontEnd";
- }
-}
diff --git a/Timeline/Configs/JwtConfiguration.cs b/Timeline/Configs/JwtConfiguration.cs
deleted file mode 100644
index af8052de..00000000
--- a/Timeline/Configs/JwtConfiguration.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Timeline.Configs
-{
- public class JwtConfiguration
- {
- public string Issuer { get; set; } = default!;
- public string Audience { get; set; } = default!;
-
- /// <summary>
- /// Set the default value of expire offset of jwt token.
- /// Unit is second. Default is 3600 * 24 seconds, aka 1 day.
- /// </summary>
- public long DefaultExpireOffset { get; set; } = 3600 * 24;
- }
-}
diff --git a/Timeline/Controllers/ControllerAuthExtensions.cs b/Timeline/Controllers/ControllerAuthExtensions.cs
deleted file mode 100644
index 00a65454..00000000
--- a/Timeline/Controllers/ControllerAuthExtensions.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Security.Claims;
-using Timeline.Auth;
-using static Timeline.Resources.Controllers.ControllerAuthExtensions;
-
-namespace Timeline.Controllers
-{
- public static class ControllerAuthExtensions
- {
- public static bool IsAdministrator(this ControllerBase controller)
- {
- return controller.User != null && controller.User.IsAdministrator();
- }
-
- public static long GetUserId(this ControllerBase controller)
- {
- var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
- if (claim == null)
- throw new InvalidOperationException(ExceptionNoUserIdentifierClaim);
-
- if (long.TryParse(claim.Value, out var value))
- return value;
-
- throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
- }
-
- public static long? GetOptionalUserId(this ControllerBase controller)
- {
- var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
- if (claim == null)
- return null;
-
- if (long.TryParse(claim.Value, out var value))
- return value;
-
- throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
- }
- }
-}
diff --git a/Timeline/Controllers/Testing/TestingAuthController.cs b/Timeline/Controllers/Testing/TestingAuthController.cs
deleted file mode 100644
index 4d3b3ec7..00000000
--- a/Timeline/Controllers/Testing/TestingAuthController.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Timeline.Auth;
-
-namespace Timeline.Controllers.Testing
-{
- [Route("testing/auth")]
- [ApiController]
- public class TestingAuthController : Controller
- {
- [HttpGet("[action]")]
- [Authorize]
- public ActionResult Authorize()
- {
- return Ok();
- }
-
- [HttpGet("[action]")]
- [UserAuthorize]
- public new ActionResult User()
- {
- return Ok();
- }
-
- [HttpGet("[action]")]
- [AdminAuthorize]
- public ActionResult Admin()
- {
- return Ok();
- }
- }
-}
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs
deleted file mode 100644
index 9a3147ea..00000000
--- a/Timeline/Controllers/TimelineController.cs
+++ /dev/null
@@ -1,491 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Threading.Tasks;
-using Timeline.Filters;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about timeline.
- /// </summary>
- [ApiController]
- [CatchTimelineNotExistException]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class TimelineController : Controller
- {
- private readonly ILogger<TimelineController> _logger;
-
- private readonly IUserService _userService;
- private readonly ITimelineService _service;
-
- private readonly IMapper _mapper;
-
- /// <summary>
- ///
- /// </summary>
- public TimelineController(ILogger<TimelineController> logger, IUserService userService, ITimelineService service, IMapper mapper)
- {
- _logger = logger;
- _userService = userService;
- _service = service;
- _mapper = mapper;
- }
-
- /// <summary>
- /// List all timelines.
- /// </summary>
- /// <param name="relate">A username. If set, only timelines related to the user will return.</param>
- /// <param name="relateType">Specify the relation type, may be 'own' or 'join'. If not set, both type will return.</param>
- /// <param name="visibility">"Private" or "Register" or "Public". If set, only timelines whose visibility is specified one will return.</param>
- /// <returns>The timeline list.</returns>
- [HttpGet("timelines")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType, [FromQuery] string? visibility)
- {
- List<TimelineVisibility>? visibilityFilter = null;
- if (visibility != null)
- {
- visibilityFilter = new List<TimelineVisibility>();
- var items = visibility.Split('|');
- foreach (var item in items)
- {
- if (item.Equals(nameof(TimelineVisibility.Private), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Private))
- visibilityFilter.Add(TimelineVisibility.Private);
- }
- else if (item.Equals(nameof(TimelineVisibility.Register), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Register))
- visibilityFilter.Add(TimelineVisibility.Register);
- }
- else if (item.Equals(nameof(TimelineVisibility.Public), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Public))
- visibilityFilter.Add(TimelineVisibility.Public);
- }
- else
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_QueryVisibilityUnknown, item));
- }
- }
- }
-
- TimelineUserRelationship? relationship = null;
- if (relate != null)
- {
- try
- {
- var relatedUserId = await _userService.GetUserIdByUsername(relate);
-
- relationship = new TimelineUserRelationship(relateType switch
- {
- "own" => TimelineUserRelationshipType.Own,
- "join" => TimelineUserRelationshipType.Join,
- _ => TimelineUserRelationshipType.Default
- }, relatedUserId);
- }
- catch (UserNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.QueryRelateNotExist());
- }
- }
-
- var timelines = await _service.GetTimelines(relationship, visibilityFilter);
- var result = _mapper.Map<List<TimelineInfo>>(timelines);
- return result;
- }
-
- /// <summary>
- /// Get info of a timeline.
- /// </summary>
- /// <param name="name">The timeline name.</param>
- /// <param name="checkUniqueId">A unique id. If specified and if-modified-since is also specified, the timeline info will return when unique id is not the specified one even if it is not modified.</param>
- /// <param name="queryIfModifiedSince">Same effect as If-Modified-Since header and take precedence than it.</param>
- /// <param name="headerIfModifiedSince">If specified, will return 304 if not modified.</param>
- /// <returns>The timeline info.</returns>
- [HttpGet("timelines/{name}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][GeneralTimelineName] string name, [FromQuery] string? checkUniqueId, [FromQuery(Name = "ifModifiedSince")] DateTime? queryIfModifiedSince, [FromHeader(Name = "If-Modified-Since")] DateTime? headerIfModifiedSince)
- {
- DateTime? ifModifiedSince = null;
- if (queryIfModifiedSince.HasValue)
- {
- ifModifiedSince = queryIfModifiedSince.Value;
- }
- else if (headerIfModifiedSince != null)
- {
- ifModifiedSince = headerIfModifiedSince.Value;
- }
-
- bool returnNotModified = false;
-
- if (ifModifiedSince.HasValue)
- {
- var lastModified = await _service.GetTimelineLastModifiedTime(name);
- if (lastModified < ifModifiedSince.Value)
- {
- if (checkUniqueId != null)
- {
- var uniqueId = await _service.GetTimelineUniqueId(name);
- if (uniqueId == checkUniqueId)
- {
- returnNotModified = true;
- }
- }
- else
- {
- returnNotModified = true;
- }
- }
- }
-
- if (returnNotModified)
- {
- return StatusCode(StatusCodes.Status304NotModified);
- }
- else
- {
- var timeline = await _service.GetTimeline(name);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
- }
-
- /// <summary>
- /// Get posts of a timeline.
- /// </summary>
- /// <param name="name">The name of the timeline.</param>
- /// <param name="modifiedSince">If set, only posts modified since the time will return.</param>
- /// <param name="includeDeleted">If set to true, deleted post will also return.</param>
- /// <returns>The post list.</returns>
- [HttpGet("timelines/{name}/posts")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<List<TimelinePostInfo>>> PostListGet([FromRoute][GeneralTimelineName] string name, [FromQuery] DateTime? modifiedSince, [FromQuery] bool? includeDeleted)
- {
- if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- List<TimelinePost> posts = await _service.GetPosts(name, modifiedSince, includeDeleted ?? false);
-
- var result = _mapper.Map<List<TimelinePostInfo>>(posts);
- return result;
- }
-
- /// <summary>
- /// Get the data of a post. Usually a image post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="id">The id of the post.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
- /// <returns>The data.</returns>
- [HttpGet("timelines/{name}/posts/{id}/data")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
- [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> PostDataGet([FromRoute][GeneralTimelineName] string name, [FromRoute] long id, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
- {
- _ = ifNoneMatch;
- if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- return await DataCacheHelper.GenerateActionResult(this, () => _service.GetPostDataETag(name, id), async () =>
- {
- var data = await _service.GetPostData(name, id);
- return data;
- });
- }
- catch (TimelinePostNotExistException)
- {
- return NotFound(ErrorResponse.TimelineController.PostNotExist());
- }
- catch (TimelinePostNoDataException)
- {
- return BadRequest(ErrorResponse.TimelineController.PostNoData());
- }
- }
-
- /// <summary>
- /// Create a new post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="body"></param>
- /// <returns>Info of new post.</returns>
- [HttpPost("timelines/{name}/posts")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePostCreateRequest body)
- {
- var id = this.GetUserId();
- if (!this.IsAdministrator() && !await _service.IsMemberOf(name, id))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- var content = body.Content;
-
- TimelinePost post;
-
- if (content.Type == TimelinePostContentTypes.Text)
- {
- var text = content.Text;
- if (text == null)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_TextContentTextRequired));
- }
- post = await _service.CreateTextPost(name, id, text, body.Time);
- }
- else if (content.Type == TimelinePostContentTypes.Image)
- {
- var base64Data = content.Data;
- if (base64Data == null)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataRequired));
- }
- byte[] data;
- try
- {
- data = Convert.FromBase64String(base64Data);
- }
- catch (FormatException)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotBase64));
- }
-
- try
- {
- post = await _service.CreateImagePost(name, id, data, body.Time);
- }
- catch (ImageException)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotImage));
- }
- }
- else
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType));
- }
-
- var result = _mapper.Map<TimelinePostInfo>(post);
- return result;
- }
-
- /// <summary>
- /// Delete a post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="id">Post id.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("timelines/{name}/posts/{id}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> PostDelete([FromRoute][GeneralTimelineName] string name, [FromRoute] long id)
- {
- if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(name, id, this.GetUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
- try
- {
- await _service.DeletePost(name, id);
- return CommonDeleteResponse.Delete();
- }
- catch (TimelinePostNotExistException)
- {
- return CommonDeleteResponse.NotExist();
- }
- }
-
- /// <summary>
- /// Change properties of a timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="body"></param>
- /// <returns>The new info.</returns>
- [HttpPatch("timelines/{name}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePatchRequest body)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
- await _service.ChangeProperty(name, _mapper.Map<TimelineChangePropertyRequest>(body));
- var timeline = await _service.GetTimeline(name);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
-
- /// <summary>
- /// Add a member to timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="member">The new member's username.</param>
- [HttpPut("timelines/{name}/members/{member}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult> TimelineMemberPut([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.ChangeMember(name, new List<string> { member }, null);
- return Ok();
- }
- catch (UserNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.MemberPut_NotExist());
- }
- }
-
- /// <summary>
- /// Remove a member from timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="member">The member's username.</param>
- [HttpDelete("timelines/{name}/members/{member}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult> TimelineMemberDelete([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.ChangeMember(name, null, new List<string> { member });
- return Ok(CommonDeleteResponse.Delete());
- }
- catch (UserNotExistException)
- {
- return Ok(CommonDeleteResponse.NotExist());
- }
- }
-
- /// <summary>
- /// Create a timeline.
- /// </summary>
- /// <param name="body"></param>
- /// <returns>Info of new timeline.</returns>
- [HttpPost("timelines")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task<ActionResult<TimelineInfo>> TimelineCreate([FromBody] TimelineCreateRequest body)
- {
- var userId = this.GetUserId();
-
- try
- {
- var timeline = await _service.CreateTimeline(body.Name, userId);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.Timeline)
- {
- return BadRequest(ErrorResponse.TimelineController.NameConflict());
- }
- }
-
- /// <summary>
- /// Delete a timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("timelines/{name}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> TimelineDelete([FromRoute][TimelineName] string name)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.DeleteTimeline(name);
- return CommonDeleteResponse.Delete();
- }
- catch (TimelineNotExistException)
- {
- return CommonDeleteResponse.NotExist();
- }
- }
-
- [HttpPost("timelineop/changename")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelineInfo>> TimelineOpChangeName([FromBody] TimelineChangeNameRequest body)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(body.OldName, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- var timeline = await _service.ChangeTimelineName(body.OldName, body.NewName);
- return Ok(_mapper.Map<TimelineInfo>(timeline));
- }
- catch (EntityAlreadyExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.NameConflict());
- }
- }
- }
-}
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs
deleted file mode 100644
index 8f2ca600..00000000
--- a/Timeline/Controllers/TokenController.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Globalization;
-using System.Threading.Tasks;
-using Timeline.Helpers;
-using Timeline.Models.Http;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.TokenController;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operation about tokens.
- /// </summary>
- [Route("token")]
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class TokenController : Controller
- {
- private readonly IUserTokenManager _userTokenManager;
- private readonly ILogger<TokenController> _logger;
- private readonly IClock _clock;
-
- private readonly IMapper _mapper;
-
- /// <summary></summary>
- public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock, IMapper mapper)
- {
- _userTokenManager = userTokenManager;
- _logger = logger;
- _clock = clock;
- _mapper = mapper;
- }
-
- /// <summary>
- /// Create a new token for a user.
- /// </summary>
- /// <returns>Result of token creation.</returns>
- [HttpPost("create")]
- [AllowAnonymous]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<CreateTokenResponse>> Create([FromBody] CreateTokenRequest request)
- {
- void LogFailure(string reason, Exception? e = null)
- {
- _logger.LogInformation(e, Log.Format(LogCreateFailure,
- ("Reason", reason),
- ("Username", request.Username),
- ("Password", request.Password),
- ("Expire (in days)", request.Expire)
- ));
- }
-
- try
- {
- DateTime? expireTime = null;
- if (request.Expire != null)
- expireTime = _clock.GetCurrentTime().AddDays(request.Expire.Value);
-
- var result = await _userTokenManager.CreateToken(request.Username, request.Password, expireTime);
-
- _logger.LogInformation(Log.Format(LogCreateSuccess,
- ("Username", request.Username),
- ("Expire At", expireTime?.ToString(CultureInfo.CurrentCulture.DateTimeFormat) ?? "default")
- ));
- return Ok(new CreateTokenResponse
- {
- Token = result.Token,
- User = _mapper.Map<UserInfo>(result.User)
- });
- }
- catch (UserNotExistException e)
- {
- LogFailure(LogUserNotExist, e);
- return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
- }
- catch (BadPasswordException e)
- {
- LogFailure(LogBadPassword, e);
- return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
- }
- }
-
- /// <summary>
- /// Verify a token.
- /// </summary>
- /// <returns>Result of token verification.</returns>
- [HttpPost("verify")]
- [AllowAnonymous]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<VerifyTokenResponse>> Verify([FromBody] VerifyTokenRequest request)
- {
- void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties)
- {
- var properties = new (string, object?)[2 + otherProperties.Length];
- properties[0] = ("Reason", reason);
- properties[1] = ("Token", request.Token);
- otherProperties.CopyTo(properties, 2);
- _logger.LogInformation(e, Log.Format(LogVerifyFailure, properties));
- }
-
- try
- {
- var result = await _userTokenManager.VerifyToken(request.Token);
- _logger.LogInformation(Log.Format(LogVerifySuccess,
- ("Username", result.Username), ("Token", request.Token)));
- return Ok(new VerifyTokenResponse
- {
- User = _mapper.Map<UserInfo>(result)
- });
- }
- catch (UserTokenTimeExpireException e)
- {
- LogFailure(LogVerifyExpire, e, ("Expire Time", e.ExpireTime), ("Verify Time", e.VerifyTime));
- return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired());
- }
- catch (UserTokenBadVersionException e)
- {
- LogFailure(LogVerifyOldVersion, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion));
- return BadRequest(ErrorResponse.TokenController.Verify_OldVersion());
-
- }
- catch (UserTokenBadFormatException e)
- {
- LogFailure(LogVerifyBadFormat, e);
- return BadRequest(ErrorResponse.TokenController.Verify_BadFormat());
- }
- catch (UserNotExistException e)
- {
- LogFailure(LogVerifyUserNotExist, e);
- return BadRequest(ErrorResponse.TokenController.Verify_UserNotExist());
- }
- }
- }
-}
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs
deleted file mode 100644
index bc4afa30..00000000
--- a/Timeline/Controllers/UserAvatarController.cs
+++ /dev/null
@@ -1,174 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Threading.Tasks;
-using Timeline.Auth;
-using Timeline.Filters;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.UserAvatarController;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about user avatar.
- /// </summary>
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class UserAvatarController : Controller
- {
- private readonly ILogger<UserAvatarController> _logger;
-
- private readonly IUserService _userService;
- private readonly IUserAvatarService _service;
-
- /// <summary>
- ///
- /// </summary>
- public UserAvatarController(ILogger<UserAvatarController> logger, IUserService userService, IUserAvatarService service)
- {
- _logger = logger;
- _userService = userService;
- _service = service;
- }
-
- /// <summary>
- /// Get avatar of a user.
- /// </summary>
- /// <param name="username">Username of the user to get avatar of.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
- /// <returns>Avatar data.</returns>
- [HttpGet("users/{username}/avatar")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
- [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> Get([FromRoute][Username] string username, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
- {
- _ = ifNoneMatch;
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
-
- return await DataCacheHelper.GenerateActionResult(this, () => _service.GetAvatarETag(id), async () =>
- {
- var avatar = await _service.GetAvatar(id);
- return avatar.ToCacheableData();
- });
- }
-
- /// <summary>
- /// Set avatar of a user. You have to be administrator to change other's.
- /// </summary>
- /// <param name="username">Username of the user to set avatar of.</param>
- /// <param name="body">The avatar data.</param>
- [HttpPut("users/{username}/avatar")]
- [Authorize]
- [Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
- [MaxContentLength(1000 * 1000 * 10)]
- [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<IActionResult> Put([FromRoute][Username] string username, [FromBody] ByteData body)
- {
- if (!User.IsAdministrator() && User.Identity.Name != username)
- {
- _logger.LogInformation(Log.Format(LogPutForbid,
- ("Operator Username", User.Identity.Name), ("Username To Put Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
- return BadRequest(ErrorResponse.UserCommon.NotExist());
- }
-
- try
- {
- var etag = await _service.SetAvatar(id, new Avatar
- {
- Data = body.Data,
- Type = body.ContentType
- });
-
- _logger.LogInformation(Log.Format(LogPutSuccess,
- ("Username", username), ("Mime Type", Request.ContentType)));
-
- Response.Headers.Append("ETag", new EntityTagHeaderValue($"\"{etag}\"").ToString());
-
- return Ok();
- }
- catch (ImageException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username)));
- return BadRequest(e.Error switch
- {
- ImageException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(),
- ImageException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(),
- ImageException.ErrorReason.NotSquare => ErrorResponse.UserAvatar.BadFormat_BadSize(),
- _ =>
- throw new Exception(ExceptionUnknownAvatarFormatError)
- });
- }
- }
-
- /// <summary>
- /// Reset the avatar to the default one. You have to be administrator to reset other's.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <response code="200">Succeeded to reset.</response>
- /// <response code="400">Error code is 10010001 if user does not exist.</response>
- /// <response code="401">You have not logged in.</response>
- /// <response code="403">You are not administrator.</response>
- [HttpDelete("users/{username}/avatar")]
- [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [Authorize]
- public async Task<IActionResult> Delete([FromRoute][Username] string username)
- {
- if (!User.IsAdministrator() && User.Identity.Name != username)
- {
- _logger.LogInformation(Log.Format(LogDeleteForbid,
- ("Operator Username", User.Identity.Name), ("Username To Delete Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
- return BadRequest(ErrorResponse.UserCommon.NotExist());
- }
-
- await _service.SetAvatar(id, null);
- return Ok();
- }
- }
-}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
deleted file mode 100644
index 02c09aab..00000000
--- a/Timeline/Controllers/UserController.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Auth;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.UserController;
-using static Timeline.Resources.Messages;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about users.
- /// </summary>
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class UserController : Controller
- {
- private readonly ILogger<UserController> _logger;
- private readonly IUserService _userService;
- private readonly IUserDeleteService _userDeleteService;
- private readonly IMapper _mapper;
-
- /// <summary></summary>
- public UserController(ILogger<UserController> logger, IUserService userService, IUserDeleteService userDeleteService, IMapper mapper)
- {
- _logger = logger;
- _userService = userService;
- _userDeleteService = userDeleteService;
- _mapper = mapper;
- }
-
- private UserInfo ConvertToUserInfo(User user) => _mapper.Map<UserInfo>(user);
-
- /// <summary>
- /// Get all users.
- /// </summary>
- /// <returns>All user list.</returns>
- [HttpGet("users")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<UserInfo[]>> List()
- {
- var users = await _userService.GetUsers();
- var result = users.Select(u => ConvertToUserInfo(u)).ToArray();
- return Ok(result);
- }
-
- /// <summary>
- /// Get a user's info.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>User info.</returns>
- [HttpGet("users/{username}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
- {
- try
- {
- var user = await _userService.GetUserByUsername(username);
- return Ok(ConvertToUserInfo(user));
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
- }
-
- /// <summary>
- /// Change a user's property.
- /// </summary>
- /// <param name="body"></param>
- /// <param name="username">Username of the user to change.</param>
- /// <returns>The new user info.</returns>
- [HttpPatch("users/{username}"), Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<UserInfo>> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username)
- {
- if (this.IsAdministrator())
- {
- try
- {
- var user = await _userService.ModifyUser(username, _mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User)
- {
- return BadRequest(ErrorResponse.UserController.UsernameConflict());
- }
- }
- else
- {
- if (User.Identity.Name != username)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(Common_Forbid_NotSelf));
-
- if (body.Username != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Username));
-
- if (body.Password != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Password));
-
- if (body.Administrator != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Administrator));
-
- var user = await _userService.ModifyUser(this.GetUserId(), _mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- }
-
- /// <summary>
- /// Delete a user and all his related data. You have to be administrator.
- /// </summary>
- /// <param name="username">Username of the user to delete.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("users/{username}"), AdminAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
- {
- var delete = await _userDeleteService.DeleteUser(username);
- if (delete)
- return Ok(CommonDeleteResponse.Delete());
- else
- return Ok(CommonDeleteResponse.NotExist());
- }
-
- /// <summary>
- /// Create a new user. You have to be administrator.
- /// </summary>
- /// <returns>The new user's info.</returns>
- [HttpPost("userop/createuser"), AdminAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<UserInfo>> CreateUser([FromBody] CreateUserRequest body)
- {
- try
- {
- var user = await _userService.CreateUser(_mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User)
- {
- return BadRequest(ErrorResponse.UserController.UsernameConflict());
- }
- }
-
- /// <summary>
- /// Change password with old password.
- /// </summary>
- [HttpPost("userop/changepassword"), Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
- {
- try
- {
- await _userService.ChangePassword(this.GetUserId(), request.OldPassword, request.NewPassword);
- return Ok();
- }
- catch (BadPasswordException e)
- {
- _logger.LogInformation(e, Log.Format(LogChangePasswordBadPassword,
- ("Username", User.Identity.Name), ("Old Password", request.OldPassword)));
- return BadRequest(ErrorResponse.UserController.ChangePassword_BadOldPassword());
- }
- // User can't be non-existent or the token is bad.
- }
- }
-}
diff --git a/Timeline/Entities/DataEntity.cs b/Timeline/Entities/DataEntity.cs
deleted file mode 100644
index b21e2dbf..00000000
--- a/Timeline/Entities/DataEntity.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("data")]
- public class DataEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("tag"), Required]
- public string Tag { get; set; } = default!;
-
- [Column("data"), Required]
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
-
- [Column("ref"), Required]
- public int Ref { get; set; }
- }
-}
diff --git a/Timeline/Entities/DatabaseContext.cs b/Timeline/Entities/DatabaseContext.cs
deleted file mode 100644
index ecadd703..00000000
--- a/Timeline/Entities/DatabaseContext.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-
-namespace Timeline.Entities
-{
- public class DatabaseContext : DbContext
- {
- public DatabaseContext(DbContextOptions<DatabaseContext> options)
- : base(options)
- {
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity<UserEntity>().Property(e => e.Version).HasDefaultValue(0);
- modelBuilder.Entity<UserEntity>().HasIndex(e => e.Username).IsUnique();
- modelBuilder.Entity<UserEntity>().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))");
- modelBuilder.Entity<UserEntity>().Property(e => e.UsernameChangeTime).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<UserEntity>().Property(e => e.CreateTime).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<UserEntity>().Property(e => e.LastModified).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<DataEntity>().HasIndex(e => e.Tag).IsUnique();
- modelBuilder.Entity<TimelineEntity>().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- modelBuilder.ApplyUtcDateTimeConverter();
- }
-
- public DbSet<UserEntity> Users { get; set; } = default!;
- public DbSet<UserAvatarEntity> UserAvatars { get; set; } = default!;
- public DbSet<TimelineEntity> Timelines { get; set; } = default!;
- public DbSet<TimelinePostEntity> TimelinePosts { get; set; } = default!;
- public DbSet<TimelineMemberEntity> TimelineMembers { get; set; } = default!;
- public DbSet<JwtTokenEntity> JwtToken { get; set; } = default!;
- public DbSet<DataEntity> Data { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/JwtTokenEntity.cs b/Timeline/Entities/JwtTokenEntity.cs
deleted file mode 100644
index 40cb230a..00000000
--- a/Timeline/Entities/JwtTokenEntity.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("jwt_token")]
- public class JwtTokenEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Required, Column("key")]
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Key { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
- }
-}
diff --git a/Timeline/Entities/TimelineEntity.cs b/Timeline/Entities/TimelineEntity.cs
deleted file mode 100644
index 3e592673..00000000
--- a/Timeline/Entities/TimelineEntity.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-using Timeline.Models;
-
-namespace Timeline.Entities
-{
-#pragma warning disable CA2227 // Collection properties should be read only
- // TODO: Create index for this table.
- [Table("timelines")]
- public class TimelineEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("unique_id"), Required]
- public string UniqueId { get; set; } = default!;
-
- /// <summary>
- /// If null, then this timeline is a personal timeline.
- /// </summary>
- [Column("name")]
- public string? Name { get; set; }
-
- [Column("title")]
- public string? Title { get; set; }
-
- [Column("name_last_modified")]
- public DateTime NameLastModified { get; set; }
-
- [Column("description")]
- public string? Description { get; set; }
-
- [Column("owner")]
- public long OwnerId { get; set; }
-
- [ForeignKey(nameof(OwnerId))]
- public UserEntity Owner { get; set; } = default!;
-
- [Column("visibility")]
- public TimelineVisibility Visibility { get; set; }
-
- [Column("create_time")]
- public DateTime CreateTime { get; set; }
-
- [Column("last_modified")]
- public DateTime LastModified { get; set; }
-
- [Column("current_post_local_id")]
- public long CurrentPostLocalId { get; set; }
-
- public List<TimelineMemberEntity> Members { get; set; } = default!;
-
- public List<TimelinePostEntity> Posts { get; set; } = default!;
- }
-#pragma warning restore CA2227 // Collection properties should be read only
-}
diff --git a/Timeline/Entities/TimelineMemberEntity.cs b/Timeline/Entities/TimelineMemberEntity.cs
deleted file mode 100644
index e76f2099..00000000
--- a/Timeline/Entities/TimelineMemberEntity.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("timeline_members")]
- public class TimelineMemberEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("user")]
- public long UserId { get; set; }
-
- [ForeignKey(nameof(UserId))]
- public UserEntity User { get; set; } = default!;
-
- [Column("timeline")]
- public long TimelineId { get; set; }
-
- [ForeignKey(nameof(TimelineId))]
- public TimelineEntity Timeline { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/TimelinePostEntity.cs b/Timeline/Entities/TimelinePostEntity.cs
deleted file mode 100644
index 07367fba..00000000
--- a/Timeline/Entities/TimelinePostEntity.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("timeline_posts")]
- public class TimelinePostEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("local_id")]
- public long LocalId { get; set; }
-
- [Column("timeline")]
- public long TimelineId { get; set; }
-
- [ForeignKey(nameof(TimelineId))]
- public TimelineEntity Timeline { get; set; } = default!;
-
- [Column("author")]
- public long? AuthorId { get; set; }
-
- [ForeignKey(nameof(AuthorId))]
- public UserEntity? Author { get; set; } = default!;
-
- [Column("content_type"), Required]
- public string ContentType { get; set; } = default!;
-
- [Column("content")]
- public string? Content { get; set; }
-
- [Column("extra_content")]
- public string? ExtraContent { get; set; }
-
- [Column("time")]
- public DateTime Time { get; set; }
-
- [Column("last_updated")]
- public DateTime LastUpdated { get; set; }
- }
-}
diff --git a/Timeline/Entities/UserAvatarEntity.cs b/Timeline/Entities/UserAvatarEntity.cs
deleted file mode 100644
index 3c2720f7..00000000
--- a/Timeline/Entities/UserAvatarEntity.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "This is data base entity.")]
- [Table("user_avatars")]
- public class UserAvatarEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("data_tag")]
- public string? DataTag { get; set; }
-
- [Column("type")]
- public string? Type { get; set; }
-
- [Column("last_modified"), Required]
- public DateTime LastModified { get; set; }
-
- [Column("user"), Required]
- public long UserId { get; set; }
-
- [ForeignKey(nameof(UserId))]
- public UserEntity User { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/UserEntity.cs b/Timeline/Entities/UserEntity.cs
deleted file mode 100644
index 0cfaa335..00000000
--- a/Timeline/Entities/UserEntity.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- public static class UserRoles
- {
- public const string Admin = "admin";
- public const string User = "user";
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is an entity class.")]
- [Table("users")]
- public class UserEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("unique_id"), Required]
- public string UniqueId { get; set; } = default!;
-
- [Column("username"), Required]
- public string Username { get; set; } = default!;
-
- [Column("username_change_time")]
- public DateTime UsernameChangeTime { get; set; }
-
- [Column("password"), Required]
- public string Password { get; set; } = default!;
-
- [Column("roles"), Required]
- public string Roles { get; set; } = default!;
-
- [Column("version"), Required]
- public long Version { get; set; }
-
- [Column("nickname")]
- public string? Nickname { get; set; }
-
- [Column("create_time")]
- public DateTime CreateTime { get; set; }
-
- [Column("last_modified")]
- public DateTime LastModified { get; set; }
-
- public UserAvatarEntity? Avatar { get; set; }
-
- public List<TimelineEntity> Timelines { get; set; } = default!;
-
- public List<TimelinePostEntity> TimelinePosts { get; set; } = default!;
-
- public List<TimelineMemberEntity> TimelinesJoined { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/UtcDateAnnotation.cs b/Timeline/Entities/UtcDateAnnotation.cs
deleted file mode 100644
index 6600e701..00000000
--- a/Timeline/Entities/UtcDateAnnotation.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using System;
-
-namespace Timeline.Entities
-{
- // Copied from https://github.com/dotnet/efcore/issues/4711#issuecomment-589842988
- public static class UtcDateAnnotation
- {
- private const string IsUtcAnnotation = "IsUtc";
- private static readonly ValueConverter<DateTime, DateTime> UtcConverter =
- new ValueConverter<DateTime, DateTime>(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
-
- public static PropertyBuilder<TProperty> IsUtc<TProperty>(this PropertyBuilder<TProperty> builder, bool isUtc = true) =>
- builder.HasAnnotation(IsUtcAnnotation, isUtc);
-
- public static bool IsUtc(this IMutableProperty property) =>
- ((bool?)property.FindAnnotation(IsUtcAnnotation)?.Value) ?? true;
-
- /// <summary>
- /// Make sure this is called after configuring all your entities.
- /// </summary>
- public static void ApplyUtcDateTimeConverter(this ModelBuilder builder)
- {
- foreach (var entityType in builder.Model.GetEntityTypes())
- {
- foreach (var property in entityType.GetProperties())
- {
- if (!property.IsUtc())
- {
- continue;
- }
-
- if (property.ClrType == typeof(DateTime))
- {
- property.SetValueConverter(UtcConverter);
- }
- }
- }
- }
- }
-}
diff --git a/Timeline/Filters/Header.cs b/Timeline/Filters/Header.cs
deleted file mode 100644
index cc5ddd9f..00000000
--- a/Timeline/Filters/Header.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Timeline.Models.Http;
-
-namespace Timeline.Filters
-{
- /// <summary>
- /// Restrict max content length.
- /// </summary>
- public class MaxContentLengthFilter : IResourceFilter
- {
- /// <summary>
- ///
- /// </summary>
- /// <param name="maxByteLength">Max length.</param>
- public MaxContentLengthFilter(long maxByteLength)
- {
- MaxByteLength = maxByteLength;
- }
-
- /// <summary>
- /// Max length.
- /// </summary>
- public long MaxByteLength { get; set; }
-
- /// <inheritdoc/>
- public void OnResourceExecuted(ResourceExecutedContext context)
- {
- }
-
- /// <inheritdoc/>
- public void OnResourceExecuting(ResourceExecutingContext context)
- {
- var contentLength = context.HttpContext.Request.ContentLength;
- if (contentLength != null && contentLength > MaxByteLength)
- {
- context.Result = new BadRequestObjectResult(ErrorResponse.Common.Content.TooBig(MaxByteLength + "B"));
- }
- }
- }
-
- /// <summary>
- /// Restrict max content length.
- /// </summary>
- public class MaxContentLengthAttribute : TypeFilterAttribute
- {
- /// <summary>
- ///
- /// </summary>
- /// <param name="maxByteLength">Max length.</param>
- public MaxContentLengthAttribute(long maxByteLength)
- : base(typeof(MaxContentLengthFilter))
- {
- MaxByteLength = maxByteLength;
- Arguments = new object[] { maxByteLength };
- }
-
- /// <summary>
- /// Max length.
- /// </summary>
- public long MaxByteLength { get; }
- }
-}
diff --git a/Timeline/Filters/Timeline.cs b/Timeline/Filters/Timeline.cs
deleted file mode 100644
index 6a730ee7..00000000
--- a/Timeline/Filters/Timeline.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Timeline.Models.Http;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Filters
-{
- public class CatchTimelineNotExistExceptionAttribute : ExceptionFilterAttribute
- {
- public override void OnException(ExceptionContext context)
- {
- if (context.Exception is TimelineNotExistException e)
- {
- if (e.InnerException is UserNotExistException)
- {
- if (HttpMethods.IsGet(context.HttpContext.Request.Method))
- context.Result = new NotFoundObjectResult(ErrorResponse.UserCommon.NotExist());
- else
- context.Result = new BadRequestObjectResult(ErrorResponse.UserCommon.NotExist());
- }
- else
- {
- if (HttpMethods.IsGet(context.HttpContext.Request.Method))
- context.Result = new NotFoundObjectResult(ErrorResponse.TimelineController.NotExist());
- else
- context.Result = new BadRequestObjectResult(ErrorResponse.TimelineController.NotExist());
- }
- }
- }
- }
-}
diff --git a/Timeline/Formatters/BytesInputFormatter.cs b/Timeline/Formatters/BytesInputFormatter.cs
deleted file mode 100644
index ac6537c9..00000000
--- a/Timeline/Formatters/BytesInputFormatter.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using Microsoft.AspNetCore.Mvc.Formatters;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Threading.Tasks;
-using Timeline.Models;
-
-namespace Timeline.Formatters
-{
- /// <summary>
- /// Formatter that reads body as bytes.
- /// </summary>
- public class BytesInputFormatter : InputFormatter
- {
- /// <summary>
- ///
- /// </summary>
- public BytesInputFormatter()
- {
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/gif"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/webp"));
- }
-
- /// <inheritdoc/>
- public override bool CanRead(InputFormatterContext context)
- {
- if (context == null) throw new ArgumentNullException(nameof(context));
-
- if (context.ModelType == typeof(ByteData))
- return true;
-
- return false;
- }
-
- /// <inheritdoc/>
- public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
- {
- var request = context.HttpContext.Request;
- var contentLength = request.ContentLength;
-
- var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<BytesInputFormatter>>();
-
- if (contentLength == null)
- {
- logger.LogInformation("Failed to read body as bytes. Content-Length is not set.");
- return await InputFormatterResult.FailureAsync();
- }
-
- if (contentLength == 0)
- {
- logger.LogInformation("Failed to read body as bytes. Content-Length is 0.");
- return await InputFormatterResult.FailureAsync();
- }
-
- var bodyStream = request.Body;
-
- var data = new byte[contentLength.Value];
- var bytesRead = await bodyStream.ReadAsync(data);
-
- if (bytesRead != contentLength)
- {
- logger.LogInformation("Failed to read body as bytes. Actual length of body is smaller than Content-Length.");
- return await InputFormatterResult.FailureAsync();
- }
-
- var extraByte = new byte[1];
- if (await bodyStream.ReadAsync(extraByte) != 0)
- {
- logger.LogInformation("Failed to read body as bytes. Actual length of body is greater than Content-Length.");
- return await InputFormatterResult.FailureAsync();
- }
-
- return await InputFormatterResult.SuccessAsync(new ByteData(data, request.ContentType));
- }
- }
-}
diff --git a/Timeline/Formatters/StringInputFormatter.cs b/Timeline/Formatters/StringInputFormatter.cs
deleted file mode 100644
index b1924268..00000000
--- a/Timeline/Formatters/StringInputFormatter.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Microsoft.AspNetCore.Mvc.Formatters;
-using Microsoft.Net.Http.Headers;
-using System.IO;
-using System.Net.Mime;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Timeline.Formatters
-{
- public class StringInputFormatter : TextInputFormatter
- {
- public StringInputFormatter()
- {
- SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(MediaTypeNames.Text.Plain));
- SupportedEncodings.Add(Encoding.UTF8);
- }
-
- public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
- {
- var request = context.HttpContext.Request;
- using var reader = new StreamReader(request.Body, effectiveEncoding);
- var stringContent = await reader.ReadToEndAsync();
- return await InputFormatterResult.SuccessAsync(stringContent);
- }
- }
-}
diff --git a/Timeline/GlobalSuppressions.cs b/Timeline/GlobalSuppressions.cs
deleted file mode 100644
index 2b0da576..00000000
--- a/Timeline/GlobalSuppressions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// This file is used by Code Analysis to maintain SuppressMessage
-// attributes that are applied to this project.
-// Project-level suppressions either have no target or are given
-// a specific target and scoped to a namespace, type, member, etc.
-
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "This is not a UI application.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "This is not bad.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "No need to check the null because it's ASP.Net's duty.", Scope = "namespaceanddescendants", Target = "Timeline.Controllers")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Migrations code are auto generated.", Scope = "namespaceanddescendants", Target = "Timeline.Migrations")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Generated error response.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1056:Uri properties should not be strings", Justification = "That's unnecessary.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Adundant")]
diff --git a/Timeline/Helpers/DataCacheHelper.cs b/Timeline/Helpers/DataCacheHelper.cs
deleted file mode 100644
index 1ad69708..00000000
--- a/Timeline/Helpers/DataCacheHelper.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Models.Http;
-using static Timeline.Resources.Helper.DataCacheHelper;
-
-namespace Timeline.Helpers
-{
- public interface ICacheableData
- {
- string Type { get; }
-#pragma warning disable CA1819 // Properties should not return arrays
- byte[] Data { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
- DateTime? LastModified { get; }
- }
-
- public class CacheableData : ICacheableData
- {
- public CacheableData(string type, byte[] data, DateTime? lastModified)
- {
- Type = type;
- Data = data;
- LastModified = lastModified;
- }
-
- public string Type { get; set; }
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; }
-#pragma warning restore CA1819 // Properties should not return arrays
- public DateTime? LastModified { get; set; }
- }
-
- public interface ICacheableDataProvider
- {
- Task<string> GetDataETag();
- Task<ICacheableData> GetData();
- }
-
- public class DelegateCacheableDataProvider : ICacheableDataProvider
- {
- private readonly Func<Task<string>> _getDataETagDelegate;
- private readonly Func<Task<ICacheableData>> _getDataDelegate;
-
- public DelegateCacheableDataProvider(Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate)
- {
- _getDataETagDelegate = getDataETagDelegate;
- _getDataDelegate = getDataDelegate;
- }
-
- public Task<ICacheableData> GetData()
- {
- return _getDataDelegate();
- }
-
- public Task<string> GetDataETag()
- {
- return _getDataETagDelegate();
- }
- }
-
- public static class DataCacheHelper
- {
- public static async Task<ActionResult> GenerateActionResult(Controller controller, ICacheableDataProvider provider, TimeSpan? maxAge = null)
- {
- const string CacheControlHeaderKey = "Cache-Control";
- const string IfNonMatchHeaderKey = "If-None-Match";
- const string ETagHeaderKey = "ETag";
-
- string GenerateCacheControlHeaderValue()
- {
- var cacheControlHeader = new CacheControlHeaderValue()
- {
- NoCache = true,
- NoStore = false,
- MaxAge = maxAge ?? TimeSpan.FromDays(14),
- Private = true,
- MustRevalidate = true
- };
- return cacheControlHeader.ToString();
- }
-
- var loggerFactory = controller.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
- var logger = loggerFactory.CreateLogger(typeof(DataCacheHelper));
-
- var eTagValue = await provider.GetDataETag();
- eTagValue = '"' + eTagValue + '"';
- var eTag = new EntityTagHeaderValue(eTagValue);
-
-
- if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
- {
- if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
- {
- logger.LogInformation(Log.Format(LogBadIfNoneMatch, ("Header Value", value)));
- return controller.BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
- }
-
- if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
- {
- logger.LogInformation(LogResultNotModified);
- controller.Response.Headers.Add(ETagHeaderKey, eTagValue);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
-
- return controller.StatusCode(StatusCodes.Status304NotModified, null);
- }
- }
-
- var data = await provider.GetData();
- logger.LogInformation(LogResultData);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
- return controller.File(data.Data, data.Type, data.LastModified, eTag);
- }
-
- public static Task<ActionResult> GenerateActionResult(Controller controller, Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate, TimeSpan? maxAge = null)
- {
- return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDataETagDelegate, getDataDelegate), maxAge);
- }
- }
-}
diff --git a/Timeline/Helpers/DateTimeExtensions.cs b/Timeline/Helpers/DateTimeExtensions.cs
deleted file mode 100644
index 374f3bc9..00000000
--- a/Timeline/Helpers/DateTimeExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace Timeline.Helpers
-{
- public static class DateTimeExtensions
- {
- public static DateTime MyToUtc(this DateTime dateTime)
- {
- if (dateTime.Kind == DateTimeKind.Utc) return dateTime;
- if (dateTime.Kind == DateTimeKind.Local) return dateTime.ToUniversalTime();
- return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
- }
- }
-}
diff --git a/Timeline/Helpers/InvalidModelResponseFactory.cs b/Timeline/Helpers/InvalidModelResponseFactory.cs
deleted file mode 100644
index 9b253e7d..00000000
--- a/Timeline/Helpers/InvalidModelResponseFactory.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using System.Text;
-using Timeline.Models.Http;
-
-namespace Timeline.Helpers
-{
- public static class InvalidModelResponseFactory
- {
- public static IActionResult Factory(ActionContext context)
- {
- var modelState = context.ModelState;
-
- var messageBuilder = new StringBuilder();
- foreach (var model in modelState)
- foreach (var error in model.Value.Errors)
- {
- messageBuilder.Append(model.Key);
- messageBuilder.Append(" : ");
- messageBuilder.AppendLine(error.ErrorMessage);
- }
-
- return new BadRequestObjectResult(ErrorResponse.Common.CustomMessage_InvalidModel(messageBuilder.ToString()));
- }
- }
-}
diff --git a/Timeline/Helpers/LanguageHelper.cs b/Timeline/Helpers/LanguageHelper.cs
deleted file mode 100644
index b0156b8b..00000000
--- a/Timeline/Helpers/LanguageHelper.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Linq;
-
-namespace Timeline.Helpers
-{
- public static class LanguageHelper
- {
- public static bool AreSame(this bool firstBool, params bool[] otherBools)
- {
- return otherBools.All(b => b == firstBool);
- }
- }
-}
diff --git a/Timeline/Helpers/Log.cs b/Timeline/Helpers/Log.cs
deleted file mode 100644
index af0b7e13..00000000
--- a/Timeline/Helpers/Log.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Text;
-
-namespace Timeline.Helpers
-{
- public static class Log
- {
- public static string Format(string summary, params (string, object?)[] properties)
- {
- var builder = new StringBuilder();
- builder.Append(summary);
- foreach (var property in properties)
- {
- var (key, value) = property;
- builder.AppendLine();
- builder.Append(key);
- builder.Append(" : ");
- builder.Append(value);
- }
- return builder.ToString();
- }
- }
-}
diff --git a/Timeline/Migrations/20200105150407_Initialize.Designer.cs b/Timeline/Migrations/20200105150407_Initialize.Designer.cs
deleted file mode 100644
index 99e4eaac..00000000
--- a/Timeline/Migrations/20200105150407_Initialize.Designer.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200105150407_Initialize")]
- partial class Initialize
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.0");
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.User", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("EncryptedPassword")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .IsRequired()
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<string>("RoleString")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatar", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserDetail", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_details");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.User", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.User", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.User", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatar", b =>
- {
- b.HasOne("Timeline.Entities.User", null)
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatar", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserDetail", b =>
- {
- b.HasOne("Timeline.Entities.User", null)
- .WithOne("Detail")
- .HasForeignKey("Timeline.Entities.UserDetail", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200105150407_Initialize.cs b/Timeline/Migrations/20200105150407_Initialize.cs
deleted file mode 100644
index 4e12ef83..00000000
--- a/Timeline/Migrations/20200105150407_Initialize.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class Initialize : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "users",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- name = table.Column<string>(nullable: false),
- password = table.Column<string>(nullable: false),
- roles = table.Column<string>(nullable: false),
- version = table.Column<long>(nullable: false, defaultValue: 0L)
- .Annotation("Sqlite:Autoincrement", true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_users", x => x.id);
- });
-
- migrationBuilder.CreateTable(
- name: "timelines",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- name = table.Column<string>(nullable: true),
- description = table.Column<string>(nullable: true),
- owner = table.Column<long>(nullable: false),
- visibility = table.Column<int>(nullable: false),
- create_time = table.Column<DateTime>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timelines", x => x.id);
- table.ForeignKey(
- name: "FK_timelines_users_owner",
- column: x => x.owner,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "user_avatars",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- data = table.Column<byte[]>(nullable: true),
- type = table.Column<string>(nullable: true),
- etag = table.Column<string>(nullable: true),
- last_modified = table.Column<DateTime>(nullable: false),
- UserId = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_user_avatars", x => x.id);
- table.ForeignKey(
- name: "FK_user_avatars_users_UserId",
- column: x => x.UserId,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "user_details",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- nickname = table.Column<string>(nullable: true),
- UserId = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_user_details", x => x.id);
- table.ForeignKey(
- name: "FK_user_details_users_UserId",
- column: x => x.UserId,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "timeline_members",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- user = table.Column<long>(nullable: false),
- timeline = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timeline_members", x => x.id);
- table.ForeignKey(
- name: "FK_timeline_members_timelines_timeline",
- column: x => x.timeline,
- principalTable: "timelines",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_timeline_members_users_user",
- column: x => x.user,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "timeline_posts",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- timeline = table.Column<long>(nullable: false),
- author = table.Column<long>(nullable: false),
- content = table.Column<string>(nullable: true),
- time = table.Column<DateTime>(nullable: false),
- last_updated = table.Column<DateTime>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timeline_posts", x => x.id);
- table.ForeignKey(
- name: "FK_timeline_posts_users_author",
- column: x => x.author,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_timeline_posts_timelines_timeline",
- column: x => x.timeline,
- principalTable: "timelines",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_members_timeline",
- table: "timeline_members",
- column: "timeline");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_members_user",
- table: "timeline_members",
- column: "user");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_posts_author",
- table: "timeline_posts",
- column: "author");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_posts_timeline",
- table: "timeline_posts",
- column: "timeline");
-
- migrationBuilder.CreateIndex(
- name: "IX_timelines_owner",
- table: "timelines",
- column: "owner");
-
- migrationBuilder.CreateIndex(
- name: "IX_user_avatars_UserId",
- table: "user_avatars",
- column: "UserId",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_user_details_UserId",
- table: "user_details",
- column: "UserId",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_users_name",
- table: "users",
- column: "name",
- unique: true);
-
- // Add a init user. Username is "administrator". Password is "crupest".
- migrationBuilder.InsertData("users", new string[] { "name", "password", "roles" },
- new object[] { "administrator", "AQAAAAEAACcQAAAAENsspZrk8Wo+UuMyg6QuWJsNvRg6gVu4K/TumVod3h9GVLX9zDVuQQds3o7V8QWJ2w==", "user,admin" });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "timeline_members");
-
- migrationBuilder.DropTable(
- name: "timeline_posts");
-
- migrationBuilder.DropTable(
- name: "user_avatars");
-
- migrationBuilder.DropTable(
- name: "user_details");
-
- migrationBuilder.DropTable(
- name: "timelines");
-
- migrationBuilder.DropTable(
- name: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs b/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs
deleted file mode 100644
index 9b78eb15..00000000
--- a/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200131100517_RefactorUser")]
- partial class RefactorUser
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.1");
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200131100517_RefactorUser.cs b/Timeline/Migrations/20200131100517_RefactorUser.cs
deleted file mode 100644
index 8597ed50..00000000
--- a/Timeline/Migrations/20200131100517_RefactorUser.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class RefactorUser : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.RenameColumn(name: "name", table: "users", newName: "username");
- migrationBuilder.RenameIndex(name: "IX_users_name", table: "users", newName: "IX_users_username");
-
- migrationBuilder.AddColumn<string>(
- name: "nickname",
- table: "users",
- nullable: true);
-
- migrationBuilder.Sql(@"
-UPDATE users
- SET nickname = (
- SELECT nickname
- FROM user_details
- WHERE user_details.UserId = users.id
- );
- ");
-
- /*
- migrationBuilder.RenameColumn(name: "UserId", table: "user_avatars", newName: "user");
-
- migrationBuilder.DropForeignKey(
- name: "FK_user_avatars_users_UserId",
- table: "user_avatars");
-
- migrationBuilder.AddForeignKey(
- name: "FK_user_avatars_users_user",
- table: "user_avatars",
- column: "user",
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
-
- migrationBuilder.RenameIndex(
- name: "IX_user_avatars_UserId",
- table: "user_avatars",
- newName: "IX_user_avatars_user");
- */
-
- migrationBuilder.Sql(@"
-CREATE TABLE user_avatars_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data BLOB,
- type TEXT,
- etag TEXT,
- last_modified TEXT NOT NULL,
- user INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_user FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars_backup (id, data, type, etag, last_modified, user)
- SELECT id, data, type, etag, last_modified, UserId FROM user_avatars;
-
-DROP TABLE user_avatars;
-
-ALTER TABLE user_avatars_backup
- RENAME TO user_avatars;
-
-CREATE UNIQUE INDEX IX_user_avatars_user ON user_avatars (user);
- ");
-
- // migrationBuilder.DropTable(name: "user_details");
-
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(@"
-CREATE TABLE user_avatars_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data BLOB,
- type TEXT,
- etag TEXT,
- last_modified TEXT NOT NULL,
- UserId INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_UserId FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars_backup (id, data, type, etag, last_modified, UserId)
- SELECT id, data, type, etag, last_modified, user FROM user_avatars;
-
-DROP TABLE user_avatars;
-
-ALTER TABLE user_avatars_backup
- RENAME TO user_avatars;
-
-CREATE UNIQUE INDEX IX_user_avatars_UserId ON user_avatars (UserId);
- ");
-
- migrationBuilder.Sql(@"
-CREATE TABLE users_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0
-);
-
-INSERT INTO users_backup (id, name, password, roles, version)
- SELECT id, username, password, roles, version FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE users_backup
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_name ON users (name);
- ");
- }
- }
-}
diff --git a/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs b/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs
deleted file mode 100644
index eb328b52..00000000
--- a/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs
+++ /dev/null
@@ -1,257 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200221064341_AddJwtToken")]
- partial class AddJwtToken
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200221064341_AddJwtToken.cs b/Timeline/Migrations/20200221064341_AddJwtToken.cs
deleted file mode 100644
index 628970c6..00000000
--- a/Timeline/Migrations/20200221064341_AddJwtToken.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Security.Cryptography;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public static class JwtTokenGenerateHelper
- {
- public static byte[] GenerateKey()
- {
- using var random = RandomNumberGenerator.Create();
- var key = new byte[16];
- random.GetBytes(key);
- return key;
- }
- }
-
- public partial class AddJwtToken : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "jwt_token",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- key = table.Column<byte[]>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_jwt_token", x => x.id);
- });
-
-
- migrationBuilder.InsertData("jwt_token", "key", JwtTokenGenerateHelper.GenerateKey());
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "jwt_token");
- }
- }
-}
diff --git a/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs b/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs
deleted file mode 100644
index cf6ae8a3..00000000
--- a/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs
+++ /dev/null
@@ -1,265 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200229103848_AddPostLocalId")]
- partial class AddPostLocalId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200229103848_AddPostLocalId.cs b/Timeline/Migrations/20200229103848_AddPostLocalId.cs
deleted file mode 100644
index 497b38a1..00000000
--- a/Timeline/Migrations/20200229103848_AddPostLocalId.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddPostLocalId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
-
- migrationBuilder.AddColumn<long>(
- name: "current_post_local_id",
- table: "timelines",
- nullable: false,
- defaultValue: 0L);
-
- migrationBuilder.AddColumn<long>(
- name: "local_id",
- table: "timeline_posts",
- nullable: false,
- defaultValue: 0L);
-
- migrationBuilder.Sql(@"
-UPDATE timeline_posts
-SET local_id = (SELECT COUNT (*)
- FROM timeline_posts AS p
- WHERE p.timeline = timeline_posts.timeline
- AND p.id <= timeline_posts.id);
-
-UPDATE timelines
-SET current_post_local_id = (SELECT COUNT (*)
- FROM timeline_posts AS p
- WHERE p.timeline = timelines.id);
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs b/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs
deleted file mode 100644
index 336ffc18..00000000
--- a/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs
+++ /dev/null
@@ -1,290 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200306110049_AddDataTable")]
- partial class AddDataTable
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200306110049_AddDataTable.cs b/Timeline/Migrations/20200306110049_AddDataTable.cs
deleted file mode 100644
index e33bf4c9..00000000
--- a/Timeline/Migrations/20200306110049_AddDataTable.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddDataTable : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "data",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- tag = table.Column<string>(nullable: false),
- data = table.Column<byte[]>(nullable: false),
- @ref = table.Column<int>(name: "ref", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_data", x => x.id);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_data_tag",
- table: "data",
- column: "tag",
- unique: true);
-
- migrationBuilder.Sql(@"
-ALTER TABLE user_avatars
- RENAME TO user_avatars_backup;
-
-CREATE TABLE user_avatars (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data_tag TEXT,
- type TEXT,
- last_modified TEXT NOT NULL,
- user INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_user FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars (id, data_tag, type, last_modified, user)
- SELECT id, etag, type, last_modified, user FROM user_avatars_backup;
-
-INSERT OR IGNORE INTO data (tag, data, ref)
- SELECT etag, data, 0 FROM user_avatars_backup;
-
-UPDATE data
-SET ref = (SELECT COUNT (*)
- FROM user_avatars_backup AS a
- WHERE a.etag == data.tag);
-
-DROP TABLE user_avatars_backup;
-
-CREATE UNIQUE INDEX IX_user_avatars_user ON user_avatars (user);
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "data");
-
- migrationBuilder.DropColumn(
- name: "data_tag",
- table: "user_avatars");
-
- migrationBuilder.AddColumn<byte[]>(
- name: "data",
- table: "user_avatars",
- type: "BLOB",
- nullable: true);
-
- migrationBuilder.AddColumn<string>(
- name: "etag",
- table: "user_avatars",
- type: "TEXT",
- nullable: true);
- }
- }
-}
diff --git a/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs b/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs
deleted file mode 100644
index f0c4dc08..00000000
--- a/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs
+++ /dev/null
@@ -1,290 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200306111553_DropUserDetails")]
- partial class DropUserDetails
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200306111553_DropUserDetails.cs b/Timeline/Migrations/20200306111553_DropUserDetails.cs
deleted file mode 100644
index 0a176461..00000000
--- a/Timeline/Migrations/20200306111553_DropUserDetails.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class DropUserDetails : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(name: "user_details");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs b/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs
deleted file mode 100644
index bd75a916..00000000
--- a/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs
+++ /dev/null
@@ -1,299 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200312112552_AddImagePost")]
- partial class AddImagePost
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200312112552_AddImagePost.cs b/Timeline/Migrations/20200312112552_AddImagePost.cs
deleted file mode 100644
index d5098ce0..00000000
--- a/Timeline/Migrations/20200312112552_AddImagePost.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-using Timeline.Models;
-
-namespace Timeline.Migrations
-{
- public partial class AddImagePost : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn<string>(
- name: "content_type",
- table: "timeline_posts",
- nullable: false,
- defaultValue: "");
-
- migrationBuilder.AddColumn<string>(
- name: "extra_content",
- table: "timeline_posts",
- nullable: true);
-
- migrationBuilder.Sql($@"
-UPDATE timeline_posts
-SET content_type = '{TimelinePostContentTypes.Text}';
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "content_type",
- table: "timeline_posts");
-
- migrationBuilder.DropColumn(
- name: "extra_content",
- table: "timeline_posts");
- }
- }
-}
diff --git a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs b/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs
deleted file mode 100644
index adcc6308..00000000
--- a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs
+++ /dev/null
@@ -1,306 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200614061237_AddTimelineUniqueId")]
- partial class AddTimelineUniqueId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.4");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs b/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs
deleted file mode 100644
index 7abbed79..00000000
--- a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddTimelineUniqueId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timelines (
- id INTEGER NOT NULL CONSTRAINT PK_timelines PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- name TEXT NULL,
- description TEXT NULL,
- owner INTEGER NOT NULL,
- visibility INTEGER NOT NULL,
- create_time TEXT NOT NULL,
- current_post_local_id INTEGER NOT NULL DEFAULT 0,
- CONSTRAINT FK_timelines_users_owner FOREIGN KEY (owner) REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timelines (id, name, description, owner, visibility, create_time, current_post_local_id)
- SELECT id, name, description, owner, visibility, create_time, current_post_local_id FROM timelines;
-
-DROP TABLE timelines;
-
-ALTER TABLE new_timelines
- RENAME TO timelines;
-
-CREATE INDEX IX_timelines_owner ON timelines (owner);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- }
- }
-}
diff --git a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs b/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs
deleted file mode 100644
index fd10dfa9..00000000
--- a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs
+++ /dev/null
@@ -1,314 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200618064936_TimelineAddModifiedTime")]
- partial class TimelineAddModifiedTime
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs b/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs
deleted file mode 100644
index c277fe39..00000000
--- a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-namespace Timeline.Migrations
-{
- public partial class TimelineAddModifiedTime : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- var currentTime = new DateTimeToStringConverter().ConvertToProvider(DateTime.Now);
-
- migrationBuilder.Sql(
-@$"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timelines (
- id INTEGER NOT NULL CONSTRAINT PK_timelines PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- name TEXT NULL,
- name_last_modified TEXT NOT NULL,
- description TEXT NULL,
- owner INTEGER NOT NULL,
- visibility INTEGER NOT NULL,
- create_time TEXT NOT NULL,
- last_modified TEXT NOT NULL,
- current_post_local_id INTEGER NOT NULL DEFAULT 0,
- CONSTRAINT FK_timelines_users_owner FOREIGN KEY (owner) REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timelines (id, unique_id, name, name_last_modified, description, owner, visibility, create_time, last_modified, current_post_local_id)
- SELECT id, unique_id, name, '{currentTime}', description, owner, visibility, create_time, '{currentTime}', current_post_local_id FROM timelines;
-
-DROP TABLE timelines;
-
-ALTER TABLE new_timelines
- RENAME TO timelines;
-
-CREATE INDEX IX_timelines_owner ON timelines (owner);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs b/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs
deleted file mode 100644
index fe2329e4..00000000
--- a/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs
+++ /dev/null
@@ -1,321 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200808071611_UserAddUniqueId")]
- partial class UserAddUniqueId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200808071611_UserAddUniqueId.cs b/Timeline/Migrations/20200808071611_UserAddUniqueId.cs
deleted file mode 100644
index 651a2b05..00000000
--- a/Timeline/Migrations/20200808071611_UserAddUniqueId.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class UserAddUniqueId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_users (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- username TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0,
- nickname TEXT
-);
-
-INSERT INTO new_users (id, username, password, roles, version, nickname)
- SELECT id, username, password, roles, version, nickname FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE new_users
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_username ON users (
- username
-);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "unique_id",
- table: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs b/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs
deleted file mode 100644
index 71cc54dc..00000000
--- a/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200810155908_AddTimesToUser")]
- partial class AddTimesToUser
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTimeOffset>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTimeOffset>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTimeOffset>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200810155908_AddTimesToUser.cs b/Timeline/Migrations/20200810155908_AddTimesToUser.cs
deleted file mode 100644
index 369f85e6..00000000
--- a/Timeline/Migrations/20200810155908_AddTimesToUser.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddTimesToUser : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_users (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- username TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0,
- nickname TEXT,
- create_time TEXT NOT NULL DEFAULT (datetime('now', 'utc')),
- last_modified TEXT NOT NULL DEFAULT (datetime('now', 'utc')),
- username_change_time TEXT NOT NULL DEFAULT (datetime('now', 'utc'))
-);
-
-INSERT INTO new_users (id, unique_id, username, password, roles, version, nickname)
- SELECT id, unique_id, username, password, roles, version, nickname FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE new_users
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_username ON users (
- username
-);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
-, true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "create_time",
- table: "users");
-
- migrationBuilder.DropColumn(
- name: "last_modified",
- table: "users");
-
- migrationBuilder.DropColumn(
- name: "username_change_time",
- table: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs b/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs
deleted file mode 100644
index 80598fdf..00000000
--- a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs
+++ /dev/null
@@ -1,337 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200810170533_MakePostAuthorOptional")]
- partial class MakePostAuthorOptional
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTimeOffset>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTimeOffset>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTimeOffset>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs b/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs
deleted file mode 100644
index b0f0bca7..00000000
--- a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class MakePostAuthorOptional : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(@"
-PRAGMA foreign_keys = 0;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timeline_posts (
- id INTEGER NOT NULL
- CONSTRAINT PK_timeline_posts PRIMARY KEY AUTOINCREMENT,
- timeline INTEGER NOT NULL,
- author INTEGER,
- content TEXT,
- time TEXT NOT NULL,
- last_updated TEXT NOT NULL,
- local_id INTEGER NOT NULL
- DEFAULT 0,
- content_type TEXT NOT NULL
- DEFAULT '',
- extra_content TEXT,
- CONSTRAINT FK_timeline_posts_users_author FOREIGN KEY (
- author
- )
- REFERENCES users (id),
- CONSTRAINT FK_timeline_posts_timelines_timeline FOREIGN KEY (
- timeline
- )
- REFERENCES timelines (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timeline_posts SELECT * FROM timeline_posts;
-
-DROP TABLE timeline_posts;
-
-ALTER TABLE new_timeline_posts RENAME TO timeline_posts;
-
-CREATE INDEX IX_timeline_posts_author ON timeline_posts (author);
-
-CREATE INDEX IX_timeline_posts_timeline ON timeline_posts(timeline);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys = 1;
- ", true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_timeline_posts_users_author",
- table: "timeline_posts");
-
- migrationBuilder.AlterColumn<long>(
- name: "author",
- table: "timeline_posts",
- type: "INTEGER",
- nullable: false,
- oldClrType: typeof(long),
- oldNullable: true);
-
- migrationBuilder.AddForeignKey(
- name: "FK_timeline_posts_users_author",
- table: "timeline_posts",
- column: "author",
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- }
- }
-}
diff --git a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs b/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs
deleted file mode 100644
index 58238557..00000000
--- a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs
+++ /dev/null
@@ -1,337 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200811080808_ChangeDateTimeOffsetToDateTime")]
- partial class ChangeDateTimeOffsetToDateTime
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs b/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs
deleted file mode 100644
index eb6b44f3..00000000
--- a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class ChangeDateTimeOffsetToDateTime : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
-
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs b/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs
deleted file mode 100644
index f2279f3b..00000000
--- a/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs
+++ /dev/null
@@ -1,341 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200826164553_TimelineAddTitle")]
- partial class TimelineAddTitle
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.7");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Title")
- .HasColumnName("title")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200826164553_TimelineAddTitle.cs b/Timeline/Migrations/20200826164553_TimelineAddTitle.cs
deleted file mode 100644
index 7e8c498b..00000000
--- a/Timeline/Migrations/20200826164553_TimelineAddTitle.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class TimelineAddTitle : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn<string>(
- name: "title",
- table: "timelines",
- nullable: true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "title",
- table: "timelines");
- }
- }
-}
diff --git a/Timeline/Migrations/DatabaseContextModelSnapshot.cs b/Timeline/Migrations/DatabaseContextModelSnapshot.cs
deleted file mode 100644
index 65ae6c9a..00000000
--- a/Timeline/Migrations/DatabaseContextModelSnapshot.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- partial class DatabaseContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.7");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Title")
- .HasColumnName("title")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/MockClientApp/index.html b/Timeline/MockClientApp/index.html
deleted file mode 100644
index 03cf371e..00000000
--- a/Timeline/MockClientApp/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8" />
- <title>Mock Client App</title>
-</head>
-<body>
- This is a mock client app for testing.
-</body>
-</html>
diff --git a/Timeline/Models/ByteData.cs b/Timeline/Models/ByteData.cs
deleted file mode 100644
index 7b832eb5..00000000
--- a/Timeline/Models/ByteData.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using NSwag.Annotations;
-
-namespace Timeline.Models
-{
- /// <summary>
- /// Model for reading http body as bytes.
- /// </summary>
- [OpenApiFile]
- public class ByteData
- {
- /// <summary>
- /// </summary>
- /// <param name="data">The data.</param>
- /// <param name="contentType">The content type.</param>
- public ByteData(byte[] data, string contentType)
- {
- Data = data;
- ContentType = contentType;
- }
-
- /// <summary>
- /// Data.
- /// </summary>
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
-
- /// <summary>
- /// Content type.
- /// </summary>
- public string ContentType { get; }
- }
-}
diff --git a/Timeline/Models/Converters/JsonDateTimeConverter.cs b/Timeline/Models/Converters/JsonDateTimeConverter.cs
deleted file mode 100644
index 94b5cab0..00000000
--- a/Timeline/Models/Converters/JsonDateTimeConverter.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Timeline.Helpers;
-
-namespace Timeline.Models.Converters
-{
- public class JsonDateTimeConverter : JsonConverter<DateTime>
- {
- public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- Debug.Assert(typeToConvert == typeof(DateTime));
- return DateTime.Parse(reader.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
- }
-
- public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
- {
- writer.WriteStringValue(value.MyToUtc().ToString("s", CultureInfo.InvariantCulture) + "Z");
- }
- }
-}
diff --git a/Timeline/Models/Converters/MyDateTimeConverter.cs b/Timeline/Models/Converters/MyDateTimeConverter.cs
deleted file mode 100644
index f125cd5c..00000000
--- a/Timeline/Models/Converters/MyDateTimeConverter.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Globalization;
-
-namespace Timeline.Models.Converters
-{
- public class MyDateTimeConverter : TypeConverter
- {
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
- {
- return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
- {
- return base.CanConvertTo(context, destinationType);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
- {
- if (value is string text)
- {
- text = text.Trim();
- if (text.Length == 0)
- {
- return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
- }
-
- return DateTime.Parse(text, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
- }
-
- return base.ConvertFrom(context, culture, value);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
- {
- if (destinationType == typeof(string) && value is DateTime)
- {
- DateTime dt = (DateTime)value;
- if (dt == DateTime.MinValue)
- {
- return string.Empty;
- }
-
- return dt.ToString("s", CultureInfo.InvariantCulture) + "Z";
- }
-
- return base.ConvertTo(context, culture, value, destinationType);
- }
- }
-}
diff --git a/Timeline/Models/Http/ActionContextAccessorExtensions.cs b/Timeline/Models/Http/ActionContextAccessorExtensions.cs
deleted file mode 100644
index bcc55c5a..00000000
--- a/Timeline/Models/Http/ActionContextAccessorExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using System;
-
-namespace Timeline.Models.Http
-{
- public static class ActionContextAccessorExtensions
- {
- public static ActionContext AssertActionContextForUrlFill(this IActionContextAccessor accessor)
- {
- return accessor.ActionContext ?? throw new InvalidOperationException(Resources.Models.Http.Exception.ActionContextNull);
- }
- }
-}
diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs
deleted file mode 100644
index 5fa22c9e..00000000
--- a/Timeline/Models/Http/Common.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using static Timeline.Resources.Models.Http.Common;
-
-namespace Timeline.Models.Http
-{
- public class CommonResponse
- {
- public CommonResponse()
- {
-
- }
-
- public CommonResponse(int code, string message)
- {
- Code = code;
- Message = message;
- }
-
- public int Code { get; set; }
- public string? Message { get; set; }
- }
-
- public class CommonDataResponse<T> : CommonResponse
- {
- public CommonDataResponse()
- {
-
- }
-
- public CommonDataResponse(int code, string message, T data)
- : base(code, message)
- {
- Data = data;
- }
-
- public T Data { get; set; } = default!;
- }
-
- public class CommonPutResponse : CommonDataResponse<CommonPutResponse.ResponseData>
- {
- public class ResponseData
- {
- public ResponseData() { }
-
- public ResponseData(bool create)
- {
- Create = create;
- }
-
- public bool Create { get; set; }
- }
-
- public CommonPutResponse()
- {
-
- }
-
- public CommonPutResponse(int code, string message, bool create)
- : base(code, message, new ResponseData(create))
- {
-
- }
-
- internal static CommonPutResponse Create()
- {
- return new CommonPutResponse(0, MessagePutCreate, true);
- }
-
- internal static CommonPutResponse Modify()
- {
- return new CommonPutResponse(0, MessagePutModify, false);
- }
- }
-
- /// <summary>
- /// Common response for delete method.
- /// </summary>
- public class CommonDeleteResponse : CommonDataResponse<CommonDeleteResponse.ResponseData>
- {
- /// <summary></summary>
- public class ResponseData
- {
- /// <summary></summary>
- public ResponseData() { }
-
- /// <summary></summary>
- public ResponseData(bool delete)
- {
- Delete = delete;
- }
-
- /// <summary>
- /// True if the entry is deleted. False if the entry does not exist.
- /// </summary>
- public bool Delete { get; set; }
- }
-
- /// <summary></summary>
- public CommonDeleteResponse()
- {
-
- }
-
- /// <summary></summary>
- public CommonDeleteResponse(int code, string message, bool delete)
- : base(code, message, new ResponseData(delete))
- {
-
- }
-
- internal static CommonDeleteResponse Delete()
- {
- return new CommonDeleteResponse(0, MessageDeleteDelete, true);
- }
-
- internal static CommonDeleteResponse NotExist()
- {
- return new CommonDeleteResponse(0, MessageDeleteNotExist, false);
- }
- }
-}
diff --git a/Timeline/Models/Http/ErrorResponse.cs b/Timeline/Models/Http/ErrorResponse.cs
deleted file mode 100644
index ac86481f..00000000
--- a/Timeline/Models/Http/ErrorResponse.cs
+++ /dev/null
@@ -1,261 +0,0 @@
-using static Timeline.Resources.Messages;
-
-namespace Timeline.Models.Http
-{
- public static class ErrorResponse
- {
- public static class Common
- {
- public static CommonResponse InvalidModel(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.InvalidModel, string.Format(Common_InvalidModel, formatArgs));
- }
-
- public static CommonResponse CustomMessage_InvalidModel(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.InvalidModel, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Forbid(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Forbid, string.Format(Common_Forbid, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Forbid(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Forbid, string.Format(message, formatArgs));
- }
-
- public static CommonResponse UnknownEndpoint(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.UnknownEndpoint, string.Format(Common_UnknownEndpoint, formatArgs));
- }
-
- public static CommonResponse CustomMessage_UnknownEndpoint(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.UnknownEndpoint, string.Format(message, formatArgs));
- }
-
- public static class Header
- {
- public static CommonResponse IfNonMatch_BadFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, string.Format(Common_Header_IfNonMatch_BadFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_IfNonMatch_BadFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, string.Format(message, formatArgs));
- }
-
- }
-
- public static class Content
- {
- public static CommonResponse TooBig(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Content.TooBig, string.Format(Common_Content_TooBig, formatArgs));
- }
-
- public static CommonResponse CustomMessage_TooBig(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Content.TooBig, string.Format(message, formatArgs));
- }
-
- }
-
- }
-
- public static class UserCommon
- {
- public static CommonResponse NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserCommon.NotExist, string.Format(UserCommon_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserCommon.NotExist, string.Format(message, formatArgs));
- }
-
- }
-
- public static class TokenController
- {
- public static CommonResponse Create_BadCredential(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Create_BadCredential, string.Format(TokenController_Create_BadCredential, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Create_BadCredential(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Create_BadCredential, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_BadFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_BadFormat, string.Format(TokenController_Verify_BadFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_BadFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_BadFormat, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_UserNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_UserNotExist, string.Format(TokenController_Verify_UserNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_UserNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_UserNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_OldVersion(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_OldVersion, string.Format(TokenController_Verify_OldVersion, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_OldVersion(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_OldVersion, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_TimeExpired(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_TimeExpired, string.Format(TokenController_Verify_TimeExpired, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_TimeExpired(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_TimeExpired, string.Format(message, formatArgs));
- }
-
- }
-
- public static class UserController
- {
- public static CommonResponse UsernameConflict(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.UsernameConflict, string.Format(UserController_UsernameConflict, formatArgs));
- }
-
- public static CommonResponse CustomMessage_UsernameConflict(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.UsernameConflict, string.Format(message, formatArgs));
- }
-
- public static CommonResponse ChangePassword_BadOldPassword(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.ChangePassword_BadOldPassword, string.Format(UserController_ChangePassword_BadOldPassword, formatArgs));
- }
-
- public static CommonResponse CustomMessage_ChangePassword_BadOldPassword(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.ChangePassword_BadOldPassword, string.Format(message, formatArgs));
- }
-
- }
-
- public static class UserAvatar
- {
- public static CommonResponse BadFormat_CantDecode(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_CantDecode, string.Format(UserAvatar_BadFormat_CantDecode, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_CantDecode(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_CantDecode, string.Format(message, formatArgs));
- }
-
- public static CommonResponse BadFormat_UnmatchedFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_UnmatchedFormat, string.Format(UserAvatar_BadFormat_UnmatchedFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_UnmatchedFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_UnmatchedFormat, string.Format(message, formatArgs));
- }
-
- public static CommonResponse BadFormat_BadSize(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_BadSize, string.Format(UserAvatar_BadFormat_BadSize, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_BadSize(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_BadSize, string.Format(message, formatArgs));
- }
-
- }
-
- public static class TimelineController
- {
- public static CommonResponse NameConflict(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NameConflict, string.Format(TimelineController_NameConflict, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NameConflict(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NameConflict, string.Format(message, formatArgs));
- }
-
- public static CommonResponse NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NotExist, string.Format(TimelineController_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse MemberPut_NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.MemberPut_NotExist, string.Format(TimelineController_MemberPut_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_MemberPut_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.MemberPut_NotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse QueryRelateNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(TimelineController_QueryRelateNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_QueryRelateNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse PostNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNotExist, string.Format(TimelineController_PostNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_PostNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse PostNoData(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(TimelineController_PostNoData, formatArgs));
- }
-
- public static CommonResponse CustomMessage_PostNoData(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(message, formatArgs));
- }
-
- }
-
- }
-
-}
diff --git a/Timeline/Models/Http/Timeline.cs b/Timeline/Models/Http/Timeline.cs
deleted file mode 100644
index a81b33f5..00000000
--- a/Timeline/Models/Http/Timeline.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using System;
-using System.Collections.Generic;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Info of post content.
- /// </summary>
- public class TimelinePostContentInfo
- {
- /// <summary>
- /// Type of the post content.
- /// </summary>
- public string Type { get; set; } = default!;
- /// <summary>
- /// If post is of text type. This is the text.
- /// </summary>
- public string? Text { get; set; }
- /// <summary>
- /// If post is of image type. This is the image url.
- /// </summary>
- public string? Url { get; set; }
- /// <summary>
- /// If post has data (currently it means it's a image post), this is the data etag.
- /// </summary>
- public string? ETag { get; set; }
- }
-
- /// <summary>
- /// Info of a post.
- /// </summary>
- public class TimelinePostInfo
- {
- /// <summary>
- /// Post id.
- /// </summary>
- public long Id { get; set; }
- /// <summary>
- /// Content of the post. May be null if post is deleted.
- /// </summary>
- public TimelinePostContentInfo? Content { get; set; }
- /// <summary>
- /// True if post is deleted.
- /// </summary>
- public bool Deleted { get; set; }
- /// <summary>
- /// Post time.
- /// </summary>
- public DateTime Time { get; set; }
- /// <summary>
- /// The author. May be null if the user has been deleted.
- /// </summary>
- public UserInfo? Author { get; set; } = default!;
- /// <summary>
- /// Last updated time.
- /// </summary>
- public DateTime LastUpdated { get; set; } = default!;
- }
-
- /// <summary>
- /// Info of a timeline.
- /// </summary>
- public class TimelineInfo
- {
- /// <summary>
- /// Unique id.
- /// </summary>
- public string UniqueId { get; set; } = default!;
- /// <summary>
- /// Title.
- /// </summary>
- public string Title { get; set; } = default!;
- /// <summary>
- /// Name of timeline.
- /// </summary>
- public string Name { get; set; } = default!;
- /// <summary>
- /// Last modified time of timeline name.
- /// </summary>
- public DateTime NameLastModifed { get; set; } = default!;
- /// <summary>
- /// Timeline description.
- /// </summary>
- public string Description { get; set; } = default!;
- /// <summary>
- /// Owner of the timeline.
- /// </summary>
- public UserInfo Owner { get; set; } = default!;
- /// <summary>
- /// Visibility of the timeline.
- /// </summary>
- public TimelineVisibility Visibility { get; set; }
-#pragma warning disable CA2227 // Collection properties should be read only
- /// <summary>
- /// Members of timeline.
- /// </summary>
- public List<UserInfo> Members { get; set; } = default!;
-#pragma warning restore CA2227 // Collection properties should be read only
- /// <summary>
- /// Create time of timeline.
- /// </summary>
- public DateTime CreateTime { get; set; } = default!;
- /// <summary>
- /// Last modified time of timeline.
- /// </summary>
- public DateTime LastModified { get; set; } = default!;
-
-#pragma warning disable CA1707 // Identifiers should not contain underscores
- /// <summary>
- /// Related links.
- /// </summary>
- public TimelineInfoLinks _links { get; set; } = default!;
-#pragma warning restore CA1707 // Identifiers should not contain underscores
- }
-
- /// <summary>
- /// Related links for timeline.
- /// </summary>
- public class TimelineInfoLinks
- {
- /// <summary>
- /// Self.
- /// </summary>
- public string Self { get; set; } = default!;
- /// <summary>
- /// Posts url.
- /// </summary>
- public string Posts { get; set; } = default!;
- }
-
- public class TimelineInfoLinksValueResolver : IValueResolver<Timeline, TimelineInfo, TimelineInfoLinks>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public TimelineInfoLinksValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public TimelineInfoLinks Resolve(Timeline source, TimelineInfo destination, TimelineInfoLinks destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- return new TimelineInfoLinks
- {
- Self = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { source.Name }),
- Posts = urlHelper.ActionLink(nameof(TimelineController.PostListGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { source.Name })
- };
- }
- }
-
- public class TimelinePostContentResolver : IValueResolver<TimelinePost, TimelinePostInfo, TimelinePostContentInfo?>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public TimelinePostContentResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public TimelinePostContentInfo? Resolve(TimelinePost source, TimelinePostInfo destination, TimelinePostContentInfo? destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- var sourceContent = source.Content;
-
- if (sourceContent == null)
- {
- return null;
- }
-
- if (sourceContent is TextTimelinePostContent textContent)
- {
- return new TimelinePostContentInfo
- {
- Type = TimelinePostContentTypes.Text,
- Text = textContent.Text
- };
- }
- else if (sourceContent is ImageTimelinePostContent imageContent)
- {
- return new TimelinePostContentInfo
- {
- Type = TimelinePostContentTypes.Image,
- Url = urlHelper.ActionLink(
- action: nameof(TimelineController.PostDataGet),
- controller: nameof(TimelineController)[0..^nameof(Controller).Length],
- values: new { Name = source.TimelineName, Id = source.Id }),
- ETag = $"\"{imageContent.DataTag}\""
- };
- }
- else
- {
- throw new InvalidOperationException(Resources.Models.Http.Exception.UnknownPostContentType);
- }
- }
- }
-
- public class TimelineInfoAutoMapperProfile : Profile
- {
- public TimelineInfoAutoMapperProfile()
- {
- CreateMap<Timeline, TimelineInfo>().ForMember(u => u._links, opt => opt.MapFrom<TimelineInfoLinksValueResolver>());
- CreateMap<TimelinePost, TimelinePostInfo>().ForMember(p => p.Content, opt => opt.MapFrom<TimelinePostContentResolver>());
- CreateMap<TimelinePatchRequest, TimelineChangePropertyRequest>();
- }
- }
-}
diff --git a/Timeline/Models/Http/TimelineController.cs b/Timeline/Models/Http/TimelineController.cs
deleted file mode 100644
index 7bd141ed..00000000
--- a/Timeline/Models/Http/TimelineController.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Timeline.Models.Validation;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Content of post create request.
- /// </summary>
- public class TimelinePostCreateRequestContent
- {
- /// <summary>
- /// Type of post content.
- /// </summary>
- [Required]
- public string Type { get; set; } = default!;
- /// <summary>
- /// If post is of text type, this is the text.
- /// </summary>
- public string? Text { get; set; }
- /// <summary>
- /// If post is of image type, this is base64 of image data.
- /// </summary>
- public string? Data { get; set; }
- }
-
- public class TimelinePostCreateRequest
- {
- /// <summary>
- /// Content of the new post.
- /// </summary>
- [Required]
- public TimelinePostCreateRequestContent Content { get; set; } = default!;
-
- /// <summary>
- /// Time of the post. If not set, current time will be used.
- /// </summary>
- public DateTime? Time { get; set; }
- }
-
- /// <summary>
- /// Create timeline request model.
- /// </summary>
- public class TimelineCreateRequest
- {
- /// <summary>
- /// Name of the new timeline. Must be a valid name.
- /// </summary>
- [Required]
- [TimelineName]
- public string Name { get; set; } = default!;
- }
-
- /// <summary>
- /// Patch timeline request model.
- /// </summary>
- public class TimelinePatchRequest
- {
- /// <summary>
- /// New title. Null for not change.
- /// </summary>
- public string? Title { get; set; }
-
- /// <summary>
- /// New description. Null for not change.
- /// </summary>
- public string? Description { get; set; }
-
- /// <summary>
- /// New visibility. Null for not change.
- /// </summary>
- public TimelineVisibility? Visibility { get; set; }
- }
-
- /// <summary>
- /// Change timeline name request model.
- /// </summary>
- public class TimelineChangeNameRequest
- {
- /// <summary>
- /// Old name of timeline.
- /// </summary>
- [Required]
- [TimelineName]
- public string OldName { get; set; } = default!;
- /// <summary>
- /// New name of timeline.
- /// </summary>
- [Required]
- [TimelineName]
- public string NewName { get; set; } = default!;
- }
-}
diff --git a/Timeline/Models/Http/TokenController.cs b/Timeline/Models/Http/TokenController.cs
deleted file mode 100644
index a42c44e5..00000000
--- a/Timeline/Models/Http/TokenController.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Request model for <see cref="TokenController.Create(CreateTokenRequest)"/>.
- /// </summary>
- public class CreateTokenRequest
- {
- /// <summary>
- /// The username.
- /// </summary>
- public string Username { get; set; } = default!;
- /// <summary>
- /// The password.
- /// </summary>
- public string Password { get; set; } = default!;
- /// <summary>
- /// Optional token validation period. In days. If not specified, server will use a default one.
- /// </summary>
- [Range(1, 365)]
- public int? Expire { get; set; }
- }
-
- /// <summary>
- /// Response model for <see cref="TokenController.Create(CreateTokenRequest)"/>.
- /// </summary>
- public class CreateTokenResponse
- {
- /// <summary>
- /// The token created.
- /// </summary>
- public string Token { get; set; } = default!;
- /// <summary>
- /// The user owning the token.
- /// </summary>
- public UserInfo User { get; set; } = default!;
- }
-
- /// <summary>
- /// Request model for <see cref="TokenController.Verify(VerifyTokenRequest)"/>.
- /// </summary>
- public class VerifyTokenRequest
- {
- /// <summary>
- /// The token to verify.
- /// </summary>
- public string Token { get; set; } = default!;
- }
-
- /// <summary>
- /// Response model for <see cref="TokenController.Verify(VerifyTokenRequest)"/>.
- /// </summary>
- public class VerifyTokenResponse
- {
- /// <summary>
- /// The user owning the token.
- /// </summary>
- public UserInfo User { get; set; } = default!;
- }
-}
diff --git a/Timeline/Models/Http/UserController.cs b/Timeline/Models/Http/UserController.cs
deleted file mode 100644
index 6bc5a66e..00000000
--- a/Timeline/Models/Http/UserController.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using AutoMapper;
-using System.ComponentModel.DataAnnotations;
-using Timeline.Controllers;
-using Timeline.Models.Validation;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Request model for <see cref="UserController.Patch(UserPatchRequest, string)"/>.
- /// </summary>
- public class UserPatchRequest
- {
- /// <summary>
- /// New username. Null if not change. Need to be administrator.
- /// </summary>
- [Username]
- public string? Username { get; set; }
-
- /// <summary>
- /// New password. Null if not change. Need to be administrator.
- /// </summary>
- [MinLength(1)]
- public string? Password { get; set; }
-
- /// <summary>
- /// New nickname. Null if not change. Need to be administrator to change other's.
- /// </summary>
- [Nickname]
- public string? Nickname { get; set; }
-
- /// <summary>
- /// Whether to be administrator. Null if not change. Need to be administrator.
- /// </summary>
- public bool? Administrator { get; set; }
- }
-
- /// <summary>
- /// Request model for <see cref="UserController.CreateUser(CreateUserRequest)"/>.
- /// </summary>
- public class CreateUserRequest
- {
- /// <summary>
- /// Username of the new user.
- /// </summary>
- [Required, Username]
- public string Username { get; set; } = default!;
-
- /// <summary>
- /// Password of the new user.
- /// </summary>
- [Required, MinLength(1)]
- public string Password { get; set; } = default!;
-
- /// <summary>
- /// Whether the new user is administrator.
- /// </summary>
- [Required]
- public bool? Administrator { get; set; }
-
- /// <summary>
- /// Nickname of the new user.
- /// </summary>
- [Nickname]
- public string? Nickname { get; set; }
- }
-
- /// <summary>
- /// Request model for <see cref="UserController.ChangePassword(ChangePasswordRequest)"/>.
- /// </summary>
- public class ChangePasswordRequest
- {
- /// <summary>
- /// Old password.
- /// </summary>
- [Required(AllowEmptyStrings = false)]
- public string OldPassword { get; set; } = default!;
-
- /// <summary>
- /// New password.
- /// </summary>
- [Required(AllowEmptyStrings = false)]
- public string NewPassword { get; set; } = default!;
- }
-
- public class UserControllerAutoMapperProfile : Profile
- {
- public UserControllerAutoMapperProfile()
- {
- CreateMap<UserPatchRequest, User>(MemberList.Source);
- CreateMap<CreateUserRequest, User>(MemberList.Source);
- }
- }
-}
diff --git a/Timeline/Models/Http/UserInfo.cs b/Timeline/Models/Http/UserInfo.cs
deleted file mode 100644
index d92a12c4..00000000
--- a/Timeline/Models/Http/UserInfo.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Info of a user.
- /// </summary>
- public class UserInfo
- {
- /// <summary>
- /// Unique id.
- /// </summary>
- public string UniqueId { get; set; } = default!;
- /// <summary>
- /// Username.
- /// </summary>
- public string Username { get; set; } = default!;
- /// <summary>
- /// Nickname.
- /// </summary>
- public string Nickname { get; set; } = default!;
- /// <summary>
- /// True if the user is a administrator.
- /// </summary>
- public bool? Administrator { get; set; } = default!;
-#pragma warning disable CA1707 // Identifiers should not contain underscores
- /// <summary>
- /// Related links.
- /// </summary>
- public UserInfoLinks _links { get; set; } = default!;
-#pragma warning restore CA1707 // Identifiers should not contain underscores
- }
-
- /// <summary>
- /// Related links for user.
- /// </summary>
- public class UserInfoLinks
- {
- /// <summary>
- /// Self.
- /// </summary>
- public string Self { get; set; } = default!;
- /// <summary>
- /// Avatar url.
- /// </summary>
- public string Avatar { get; set; } = default!;
- /// <summary>
- /// Personal timeline url.
- /// </summary>
- public string Timeline { get; set; } = default!;
- }
-
- public class UserInfoLinksValueResolver : IValueResolver<User, UserInfo, UserInfoLinks>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public UserInfoLinksValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public UserInfoLinks Resolve(User source, UserInfo destination, UserInfoLinks destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- var result = new UserInfoLinks
- {
- Self = urlHelper.ActionLink(nameof(UserController.Get), nameof(UserController)[0..^nameof(Controller).Length], new { destination.Username }),
- Avatar = urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController)[0..^nameof(Controller).Length], new { destination.Username }),
- Timeline = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { Name = "@" + destination.Username })
- };
- return result;
- }
- }
-
- public class UserInfoAutoMapperProfile : Profile
- {
- public UserInfoAutoMapperProfile()
- {
- CreateMap<User, UserInfo>().ForMember(u => u._links, opt => opt.MapFrom<UserInfoLinksValueResolver>());
- }
- }
-}
diff --git a/Timeline/Models/Timeline.cs b/Timeline/Models/Timeline.cs
deleted file mode 100644
index a5987577..00000000
--- a/Timeline/Models/Timeline.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Timeline.Models
-{
- public enum TimelineVisibility
- {
- /// <summary>
- /// All people including those without accounts.
- /// </summary>
- Public,
- /// <summary>
- /// Only people signed in.
- /// </summary>
- Register,
- /// <summary>
- /// Only member.
- /// </summary>
- Private
- }
-
- public static class TimelinePostContentTypes
- {
- public const string Text = "text";
- public const string Image = "image";
- }
-
- public interface ITimelinePostContent
- {
- public string Type { get; }
- }
-
- public class TextTimelinePostContent : ITimelinePostContent
- {
- public TextTimelinePostContent(string text) { Text = text; }
-
- public string Type { get; } = TimelinePostContentTypes.Text;
- public string Text { get; set; }
- }
-
- public class ImageTimelinePostContent : ITimelinePostContent
- {
- public ImageTimelinePostContent(string dataTag) { DataTag = dataTag; }
-
- public string Type { get; } = TimelinePostContentTypes.Image;
-
- /// <summary>
- /// The tag of the data. The tag of the entry in DataManager. Also the etag (not quoted).
- /// </summary>
- public string DataTag { get; set; }
- }
-
- public class TimelinePost
- {
- public TimelinePost(long id, ITimelinePostContent? content, DateTime time, User? author, DateTime lastUpdated, string timelineName)
- {
- Id = id;
- Content = content;
- Time = time;
- Author = author;
- LastUpdated = lastUpdated;
- TimelineName = timelineName;
- }
-
- public long Id { get; set; }
- public ITimelinePostContent? Content { get; set; }
- public bool Deleted => Content == null;
- public DateTime Time { get; set; }
- public User? Author { get; set; }
- public DateTime LastUpdated { get; set; }
- public string TimelineName { get; set; }
- }
-
-#pragma warning disable CA1724 // Type names should not match namespaces
- public class Timeline
-#pragma warning restore CA1724 // Type names should not match namespaces
- {
- public string UniqueID { get; set; } = default!;
- public string Name { get; set; } = default!;
- public DateTime NameLastModified { get; set; } = default!;
- public string Title { get; set; } = default!;
- public string Description { get; set; } = default!;
- public User Owner { get; set; } = default!;
- public TimelineVisibility Visibility { get; set; }
-#pragma warning disable CA2227 // Collection properties should be read only
- public List<User> Members { get; set; } = default!;
-#pragma warning restore CA2227 // Collection properties should be read only
- public DateTime CreateTime { get; set; } = default!;
- public DateTime LastModified { get; set; } = default!;
- }
-
- public class TimelineChangePropertyRequest
- {
- public string? Title { get; set; }
- public string? Description { get; set; }
- public TimelineVisibility? Visibility { get; set; }
- }
-}
diff --git a/Timeline/Models/User.cs b/Timeline/Models/User.cs
deleted file mode 100644
index f08a62db..00000000
--- a/Timeline/Models/User.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace Timeline.Models
-{
- public class User
- {
- public string? UniqueId { get; set; }
- public string? Username { get; set; }
- public string? Nickname { get; set; }
- public bool? Administrator { get; set; }
-
- #region secret
- public long? Id { get; set; }
- public string? Password { get; set; }
- public long? Version { get; set; }
- public DateTime? UsernameChangeTime { get; set; }
- public DateTime? CreateTime { get; set; }
- public DateTime? LastModified { get; set; }
- #endregion secret
- }
-}
diff --git a/Timeline/Models/Validation/GeneralTimelineNameValidator.cs b/Timeline/Models/Validation/GeneralTimelineNameValidator.cs
deleted file mode 100644
index e1c96fbd..00000000
--- a/Timeline/Models/Validation/GeneralTimelineNameValidator.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-
-namespace Timeline.Models.Validation
-{
- public class GeneralTimelineNameValidator : Validator<string>
- {
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
- private readonly TimelineNameValidator _timelineNameValidator = new TimelineNameValidator();
-
- protected override (bool, string) DoValidate(string value)
- {
- if (value.StartsWith('@'))
- {
- return _usernameValidator.Validate(value.Substring(1));
- }
- else
- {
- return _timelineNameValidator.Validate(value);
- }
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class GeneralTimelineNameAttribute : ValidateWithAttribute
- {
- public GeneralTimelineNameAttribute()
- : base(typeof(GeneralTimelineNameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/NameValidator.cs b/Timeline/Models/Validation/NameValidator.cs
deleted file mode 100644
index b74c40b7..00000000
--- a/Timeline/Models/Validation/NameValidator.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.Linq;
-using System.Text.RegularExpressions;
-using static Timeline.Resources.Models.Validation.NameValidator;
-
-namespace Timeline.Models.Validation
-{
- public class NameValidator : Validator<string>
- {
- private static Regex UniqueIdRegex { get; } = new Regex(@"^[a-zA-Z0-9]{32}$");
-
- public const int MaxLength = 26;
-
- protected override (bool, string) DoValidate(string value)
- {
- if (value.Length == 0)
- {
- return (false, MessageEmptyString);
- }
-
- if (value.Length > MaxLength)
- {
- return (false, MessageTooLong);
- }
-
- foreach ((char c, int i) in value.Select((c, i) => (c, i)))
- {
- if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_'))
- {
- return (false, MessageInvalidChar);
- }
- }
-
- // Currently name can't be longer than 26. So this is not needed. But reserve it for future use.
- if (UniqueIdRegex.IsMatch(value))
- {
- return (false, MessageUnqiueId);
- }
-
- return (true, GetSuccessMessage());
- }
- }
-}
diff --git a/Timeline/Models/Validation/NicknameValidator.cs b/Timeline/Models/Validation/NicknameValidator.cs
deleted file mode 100644
index 1d6ab163..00000000
--- a/Timeline/Models/Validation/NicknameValidator.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using static Timeline.Resources.Models.Validation.NicknameValidator;
-
-namespace Timeline.Models.Validation
-{
- public class NicknameValidator : Validator<string>
- {
- protected override (bool, string) DoValidate(string value)
- {
- if (value.Length > 25)
- return (false, MessageTooLong);
-
- return (true, GetSuccessMessage());
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
- public class NicknameAttribute : ValidateWithAttribute
- {
- public NicknameAttribute() : base(typeof(NicknameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/TimelineNameValidator.cs b/Timeline/Models/Validation/TimelineNameValidator.cs
deleted file mode 100644
index f1ab54e8..00000000
--- a/Timeline/Models/Validation/TimelineNameValidator.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace Timeline.Models.Validation
-{
- public class TimelineNameValidator : NameValidator
- {
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class TimelineNameAttribute : ValidateWithAttribute
- {
- public TimelineNameAttribute()
- : base(typeof(TimelineNameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/UsernameValidator.cs b/Timeline/Models/Validation/UsernameValidator.cs
deleted file mode 100644
index 87bbf85f..00000000
--- a/Timeline/Models/Validation/UsernameValidator.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace Timeline.Models.Validation
-{
- public class UsernameValidator : NameValidator
- {
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class UsernameAttribute : ValidateWithAttribute
- {
- public UsernameAttribute()
- : base(typeof(UsernameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/Validator.cs b/Timeline/Models/Validation/Validator.cs
deleted file mode 100644
index aef7891c..00000000
--- a/Timeline/Models/Validation/Validator.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using static Timeline.Resources.Models.Validation.Validator;
-
-namespace Timeline.Models.Validation
-{
- /// <summary>
- /// A validator to validate value.
- /// </summary>
- public interface IValidator
- {
- /// <summary>
- /// Validate given value.
- /// </summary>
- /// <param name="value">The value to validate.</param>
- /// <returns>Validation success or not and message.</returns>
- (bool, string) Validate(object? value);
- }
-
- public static class ValidatorExtensions
- {
- public static bool Validate(this IValidator validator, object? value, out string message)
- {
- if (validator == null)
- throw new ArgumentNullException(nameof(validator));
-
- var (r, m) = validator.Validate(value);
- message = m;
- return r;
- }
- }
-
- /// <summary>
- /// Convenient base class for validator.
- /// </summary>
- /// <typeparam name="T">The type of accepted value.</typeparam>
- /// <remarks>
- /// Subclass should override <see cref="DoValidate(T)"/> to do the real validation.
- /// This class will check the nullity and type of value.
- /// If value is null, it will pass or fail depending on <see cref="PermitNull"/>.
- /// If value is not null and not of type <typeparamref name="T"/>
- /// it will fail and not call <see cref="DoValidate(T)"/>.
- ///
- /// <see cref="PermitNull"/> is true by default.
- ///
- /// If you want some other behaviours, write the validator from scratch.
- /// </remarks>
- public abstract class Validator<T> : IValidator
- {
- protected bool PermitNull { get; set; } = true;
-
- public (bool, string) Validate(object? value)
- {
- if (value == null)
- {
- if (PermitNull)
- return (true, GetSuccessMessage());
- else
- return (false, ValidatorMessageNull);
- }
-
- if (value is T v)
- {
- return DoValidate(v);
- }
- else
- {
- return (false, ValidatorMessageBadType);
- }
- }
-
- protected static string GetSuccessMessage() => ValidatorMessageSuccess;
-
- protected abstract (bool, string) DoValidate(T value);
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class ValidateWithAttribute : ValidationAttribute
- {
- private readonly IValidator _validator;
-
- /// <summary>
- /// Create with a given validator.
- /// </summary>
- /// <param name="validator">The validator used to validate.</param>
- public ValidateWithAttribute(IValidator validator)
- {
- _validator = validator ?? throw new ArgumentNullException(nameof(validator));
- }
-
- /// <summary>
- /// Create the validator with default constructor.
- /// </summary>
- /// <param name="validatorType">The type of the validator.</param>
- public ValidateWithAttribute(Type validatorType)
- {
- if (validatorType == null)
- throw new ArgumentNullException(nameof(validatorType));
-
- if (!typeof(IValidator).IsAssignableFrom(validatorType))
- throw new ArgumentException(ValidateWithAttributeExceptionNotValidator, nameof(validatorType));
-
- try
- {
- _validator = (Activator.CreateInstance(validatorType) as IValidator)!;
- }
- catch (Exception e)
- {
- throw new ArgumentException(ValidateWithAttributeExceptionCreateFail, e);
- }
- }
-
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- var (result, message) = _validator.Validate(value);
- if (result)
- {
- return ValidationResult.Success;
- }
- else
- {
- return new ValidationResult(message);
- }
- }
- }
-}
diff --git a/Timeline/Program.cs b/Timeline/Program.cs
deleted file mode 100644
index 87e330a2..00000000
--- a/Timeline/Program.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using System.Resources;
-using Timeline.Entities;
-using Timeline.Services;
-
-[assembly: NeutralResourcesLanguage("en")]
-
-namespace Timeline
-{
- public static class Program
- {
- public static void Main(string[] args)
- {
- var host = CreateWebHostBuilder(args).Build();
-
- var env = host.Services.GetRequiredService<IWebHostEnvironment>();
-
- var databaseBackupService = host.Services.GetRequiredService<IDatabaseBackupService>();
- databaseBackupService.BackupNow();
-
- if (env.IsProduction())
- {
- using (var scope = host.Services.CreateScope())
- {
- var databaseContext = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
- databaseContext.Database.Migrate();
- }
- }
-
- host.Run();
- }
-
- public static IHostBuilder CreateWebHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup<Startup>();
- });
- }
-}
diff --git a/Timeline/Properties/launchSettings.json b/Timeline/Properties/launchSettings.json
deleted file mode 100644
index de8186db..00000000
--- a/Timeline/Properties/launchSettings.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "profiles": {
- "Development": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "ASPNETCORE_USEPROXYFRONTEND": "true",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- },
- "Development-Mock": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "ASPNETCORE_USEMOCKFRONTEND": "true",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- },
- "Staging": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Staging",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- }
- }
-}
diff --git a/Timeline/Resources/Authentication/AuthHandler.Designer.cs b/Timeline/Resources/Authentication/AuthHandler.Designer.cs
deleted file mode 100644
index fd4540ea..00000000
--- a/Timeline/Resources/Authentication/AuthHandler.Designer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Authentication {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class AuthHandler {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal AuthHandler() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Authentication.AuthHandler", typeof(AuthHandler).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token is found in authorization header. Token is {0} ..
- /// </summary>
- internal static string LogTokenFoundInHeader {
- get {
- return ResourceManager.GetString("LogTokenFoundInHeader", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token is found in query param with key &quot;{0}&quot;. Token is {1} ..
- /// </summary>
- internal static string LogTokenFoundInQuery {
- get {
- return ResourceManager.GetString("LogTokenFoundInQuery", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No jwt token is found..
- /// </summary>
- internal static string LogTokenNotFound {
- get {
- return ResourceManager.GetString("LogTokenNotFound", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A jwt token validation failed..
- /// </summary>
- internal static string LogTokenValidationFail {
- get {
- return ResourceManager.GetString("LogTokenValidationFail", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Authentication/AuthHandler.resx b/Timeline/Resources/Authentication/AuthHandler.resx
deleted file mode 100644
index 4cddc8ce..00000000
--- a/Timeline/Resources/Authentication/AuthHandler.resx
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogTokenFoundInHeader" xml:space="preserve">
- <value>Token is found in authorization header. Token is {0} .</value>
- </data>
- <data name="LogTokenFoundInQuery" xml:space="preserve">
- <value>Token is found in query param with key "{0}". Token is {1} .</value>
- </data>
- <data name="LogTokenNotFound" xml:space="preserve">
- <value>No jwt token is found.</value>
- </data>
- <data name="LogTokenValidationFail" xml:space="preserve">
- <value>A jwt token validation failed.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs b/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs
deleted file mode 100644
index 70a1d605..00000000
--- a/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class ControllerAuthExtensions {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal ControllerAuthExtensions() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.ControllerAuthExtensions", typeof(ControllerAuthExtensions).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to get user id because User has no NameIdentifier claim..
- /// </summary>
- internal static string ExceptionNoUserIdentifierClaim {
- get {
- return ResourceManager.GetString("ExceptionNoUserIdentifierClaim", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to get user id because NameIdentifier claim is not a number..
- /// </summary>
- internal static string ExceptionUserIdentifierClaimBadFormat {
- get {
- return ResourceManager.GetString("ExceptionUserIdentifierClaimBadFormat", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/ControllerAuthExtensions.resx b/Timeline/Resources/Controllers/ControllerAuthExtensions.resx
deleted file mode 100644
index 03e6d95a..00000000
--- a/Timeline/Resources/Controllers/ControllerAuthExtensions.resx
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionNoUserIdentifierClaim" xml:space="preserve">
- <value>Failed to get user id because User has no NameIdentifier claim.</value>
- </data>
- <data name="ExceptionUserIdentifierClaimBadFormat" xml:space="preserve">
- <value>Failed to get user id because NameIdentifier claim is not a number.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Controllers/TimelineController.Designer.cs b/Timeline/Resources/Controllers/TimelineController.Designer.cs
deleted file mode 100644
index ae6414e6..00000000
--- a/Timeline/Resources/Controllers/TimelineController.Designer.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TimelineController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TimelineController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.TimelineController", typeof(TimelineController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An unknown timeline visibility value. Can&apos;t convert it..
- /// </summary>
- internal static string ExceptionStringToVisibility {
- get {
- return ResourceManager.GetString("ExceptionStringToVisibility", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An unknown TimelineMemberOperationUserException is thrown. Can&apos;t recognize its inner exception. It is rethrown..
- /// </summary>
- internal static string LogUnknownTimelineMemberOperationUserException {
- get {
- return ResourceManager.GetString("LogUnknownTimelineMemberOperationUserException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/TimelineController.resx b/Timeline/Resources/Controllers/TimelineController.resx
deleted file mode 100644
index 4cf3d6fb..00000000
--- a/Timeline/Resources/Controllers/TimelineController.resx
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionStringToVisibility" xml:space="preserve">
- <value>An unknown timeline visibility value. Can't convert it.</value>
- </data>
- <data name="LogUnknownTimelineMemberOperationUserException" xml:space="preserve">
- <value>An unknown TimelineMemberOperationUserException is thrown. Can't recognize its inner exception. It is rethrown.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Controllers/TokenController.Designer.cs b/Timeline/Resources/Controllers/TokenController.Designer.cs
deleted file mode 100644
index a7c2864b..00000000
--- a/Timeline/Resources/Controllers/TokenController.Designer.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TokenController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TokenController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.TokenController", typeof(TokenController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The password is wrong..
- /// </summary>
- internal static string LogBadPassword {
- get {
- return ResourceManager.GetString("LogBadPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user failed to create a token..
- /// </summary>
- internal static string LogCreateFailure {
- get {
- return ResourceManager.GetString("LogCreateFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user succeeded to create a token..
- /// </summary>
- internal static string LogCreateSuccess {
- get {
- return ResourceManager.GetString("LogCreateSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user does not exist..
- /// </summary>
- internal static string LogUserNotExist {
- get {
- return ResourceManager.GetString("LogUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format. It might not be created by the server..
- /// </summary>
- internal static string LogVerifyBadFormat {
- get {
- return ResourceManager.GetString("LogVerifyBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired..
- /// </summary>
- internal static string LogVerifyExpire {
- get {
- return ResourceManager.GetString("LogVerifyExpire", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token failed to be verified..
- /// </summary>
- internal static string LogVerifyFailure {
- get {
- return ResourceManager.GetString("LogVerifyFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token has an old version. User might have update some info..
- /// </summary>
- internal static string LogVerifyOldVersion {
- get {
- return ResourceManager.GetString("LogVerifyOldVersion", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token succeeded to be verified..
- /// </summary>
- internal static string LogVerifySuccess {
- get {
- return ResourceManager.GetString("LogVerifySuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to User does not exist. Administrator might have deleted this user..
- /// </summary>
- internal static string LogVerifyUserNotExist {
- get {
- return ResourceManager.GetString("LogVerifyUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/TokenController.resx b/Timeline/Resources/Controllers/TokenController.resx
deleted file mode 100644
index 683d6cc9..00000000
--- a/Timeline/Resources/Controllers/TokenController.resx
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogBadPassword" xml:space="preserve">
- <value>The password is wrong.</value>
- </data>
- <data name="LogCreateFailure" xml:space="preserve">
- <value>A user failed to create a token.</value>
- </data>
- <data name="LogCreateSuccess" xml:space="preserve">
- <value>A user succeeded to create a token.</value>
- </data>
- <data name="LogUserNotExist" xml:space="preserve">
- <value>The user does not exist.</value>
- </data>
- <data name="LogVerifyBadFormat" xml:space="preserve">
- <value>The token is of bad format. It might not be created by the server.</value>
- </data>
- <data name="LogVerifyExpire" xml:space="preserve">
- <value>The token is expired.</value>
- </data>
- <data name="LogVerifyFailure" xml:space="preserve">
- <value>A token failed to be verified.</value>
- </data>
- <data name="LogVerifyOldVersion" xml:space="preserve">
- <value>Token has an old version. User might have update some info.</value>
- </data>
- <data name="LogVerifySuccess" xml:space="preserve">
- <value>A token succeeded to be verified.</value>
- </data>
- <data name="LogVerifyUserNotExist" xml:space="preserve">
- <value>User does not exist. Administrator might have deleted this user.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Controllers/UserAvatarController.Designer.cs b/Timeline/Resources/Controllers/UserAvatarController.Designer.cs
deleted file mode 100644
index b0c35ff9..00000000
--- a/Timeline/Resources/Controllers/UserAvatarController.Designer.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserAvatarController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserAvatarController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.UserAvatarController", typeof(UserAvatarController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown AvatarDataException.ErrorReason value..
- /// </summary>
- internal static string ExceptionUnknownAvatarFormatError {
- get {
- return ResourceManager.GetString("ExceptionUnknownAvatarFormatError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to delete a avatar of other user as a non-admin failed..
- /// </summary>
- internal static string LogDeleteForbid {
- get {
- return ResourceManager.GetString("LogDeleteForbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to delete a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogDeleteNotExist {
- get {
- return ResourceManager.GetString("LogDeleteNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Succeed to delete a avatar of a user..
- /// </summary>
- internal static string LogDeleteSuccess {
- get {
- return ResourceManager.GetString("LogDeleteSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to get a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogGetUserNotExist {
- get {
- return ResourceManager.GetString("LogGetUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of other user as a non-admin failed..
- /// </summary>
- internal static string LogPutForbid {
- get {
- return ResourceManager.GetString("LogPutForbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Succeed to put a avatar of a user..
- /// </summary>
- internal static string LogPutSuccess {
- get {
- return ResourceManager.GetString("LogPutSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of a bad format failed..
- /// </summary>
- internal static string LogPutUserBadFormat {
- get {
- return ResourceManager.GetString("LogPutUserBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogPutUserNotExist {
- get {
- return ResourceManager.GetString("LogPutUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/UserAvatarController.resx b/Timeline/Resources/Controllers/UserAvatarController.resx
deleted file mode 100644
index 864d96c0..00000000
--- a/Timeline/Resources/Controllers/UserAvatarController.resx
+++ /dev/null
@@ -1,147 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionUnknownAvatarFormatError" xml:space="preserve">
- <value>Unknown AvatarDataException.ErrorReason value.</value>
- </data>
- <data name="LogDeleteForbid" xml:space="preserve">
- <value>Attempt to delete a avatar of other user as a non-admin failed.</value>
- </data>
- <data name="LogDeleteNotExist" xml:space="preserve">
- <value>Attempt to delete a avatar of a non-existent user failed.</value>
- </data>
- <data name="LogDeleteSuccess" xml:space="preserve">
- <value>Succeed to delete a avatar of a user.</value>
- </data>
- <data name="LogGetUserNotExist" xml:space="preserve">
- <value>Attempt to get a avatar of a non-existent user failed.</value>
- </data>
- <data name="LogPutForbid" xml:space="preserve">
- <value>Attempt to put a avatar of other user as a non-admin failed.</value>
- </data>
- <data name="LogPutSuccess" xml:space="preserve">
- <value>Succeed to put a avatar of a user.</value>
- </data>
- <data name="LogPutUserBadFormat" xml:space="preserve">
- <value>Attempt to put a avatar of a bad format failed.</value>
- </data>
- <data name="LogPutUserNotExist" xml:space="preserve">
- <value>Attempt to put a avatar of a non-existent user failed.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Controllers/UserController.Designer.cs b/Timeline/Resources/Controllers/UserController.Designer.cs
deleted file mode 100644
index c8067614..00000000
--- a/Timeline/Resources/Controllers/UserController.Designer.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.UserController", typeof(UserController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown PutResult..
- /// </summary>
- internal static string ExceptionUnknownPutResult {
- get {
- return ResourceManager.GetString("ExceptionUnknownPutResult", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change password with wrong old password failed..
- /// </summary>
- internal static string LogChangePasswordBadPassword {
- get {
- return ResourceManager.GetString("LogChangePasswordBadPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change a user&apos;s username to a existent one failed..
- /// </summary>
- internal static string LogChangeUsernameConflict {
- get {
- return ResourceManager.GetString("LogChangeUsernameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change a username of a user that does not exist failed..
- /// </summary>
- internal static string LogChangeUsernameNotExist {
- get {
- return ResourceManager.GetString("LogChangeUsernameNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to retrieve info of a user that does not exist failed..
- /// </summary>
- internal static string LogGetUserNotExist {
- get {
- return ResourceManager.GetString("LogGetUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to patch a user that does not exist failed..
- /// </summary>
- internal static string LogPatchUserNotExist {
- get {
- return ResourceManager.GetString("LogPatchUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/UserController.resx b/Timeline/Resources/Controllers/UserController.resx
deleted file mode 100644
index 0bdf4845..00000000
--- a/Timeline/Resources/Controllers/UserController.resx
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionUnknownPutResult" xml:space="preserve">
- <value>Unknown PutResult.</value>
- </data>
- <data name="LogChangePasswordBadPassword" xml:space="preserve">
- <value>Attempt to change password with wrong old password failed.</value>
- </data>
- <data name="LogChangeUsernameConflict" xml:space="preserve">
- <value>Attempt to change a user's username to a existent one failed.</value>
- </data>
- <data name="LogChangeUsernameNotExist" xml:space="preserve">
- <value>Attempt to change a username of a user that does not exist failed.</value>
- </data>
- <data name="LogGetUserNotExist" xml:space="preserve">
- <value>Attempt to retrieve info of a user that does not exist failed.</value>
- </data>
- <data name="LogPatchUserNotExist" xml:space="preserve">
- <value>Attempt to patch a user that does not exist failed.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Entities.Designer.cs b/Timeline/Resources/Entities.Designer.cs
deleted file mode 100644
index 5f286f23..00000000
--- a/Timeline/Resources/Entities.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Entities {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Entities() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Entities", typeof(Entities).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Only sqlite is supported..
- /// </summary>
- internal static string ExceptionOnlySqliteSupported {
- get {
- return ResourceManager.GetString("ExceptionOnlySqliteSupported", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Entities.resx b/Timeline/Resources/Entities.resx
deleted file mode 100644
index 1538b533..00000000
--- a/Timeline/Resources/Entities.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionOnlySqliteSupported" xml:space="preserve">
- <value>Only sqlite is supported.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Filters.Designer.cs b/Timeline/Resources/Filters.Designer.cs
deleted file mode 100644
index dedfe498..00000000
--- a/Timeline/Resources/Filters.Designer.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Filters {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Filters() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Filters", typeof(Filters).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, but there is no user. Try add AuthorizeAttribute..
- /// </summary>
- internal static string LogSelfOrAdminNoUser {
- get {
- return ResourceManager.GetString("LogSelfOrAdminNoUser", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, but it does not have a model named username..
- /// </summary>
- internal static string LogSelfOrAdminNoUsername {
- get {
- return ResourceManager.GetString("LogSelfOrAdminNoUsername", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, found a model named username, but it is not string..
- /// </summary>
- internal static string LogSelfOrAdminUsernameNotString {
- get {
- return ResourceManager.GetString("LogSelfOrAdminUsernameNotString", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Filters.resx b/Timeline/Resources/Filters.resx
deleted file mode 100644
index 22620889..00000000
--- a/Timeline/Resources/Filters.resx
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogSelfOrAdminNoUser" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, but there is no user. Try add AuthorizeAttribute.</value>
- </data>
- <data name="LogSelfOrAdminNoUsername" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, but it does not have a model named username.</value>
- </data>
- <data name="LogSelfOrAdminUsernameNotString" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, found a model named username, but it is not string.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Helper/DataCacheHelper.Designer.cs b/Timeline/Resources/Helper/DataCacheHelper.Designer.cs
deleted file mode 100644
index acf56d13..00000000
--- a/Timeline/Resources/Helper/DataCacheHelper.Designer.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Helper {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class DataCacheHelper {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal DataCacheHelper() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Helper.DataCacheHelper", typeof(DataCacheHelper).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header If-None-Match is of bad format..
- /// </summary>
- internal static string LogBadIfNoneMatch {
- get {
- return ResourceManager.GetString("LogBadIfNoneMatch", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cache is invalid and data is returned..
- /// </summary>
- internal static string LogResultData {
- get {
- return ResourceManager.GetString("LogResultData", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cache is valid and 304 Not Modified is returned..
- /// </summary>
- internal static string LogResultNotModified {
- get {
- return ResourceManager.GetString("LogResultNotModified", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Helper/DataCacheHelper.resx b/Timeline/Resources/Helper/DataCacheHelper.resx
deleted file mode 100644
index 515cfa9b..00000000
--- a/Timeline/Resources/Helper/DataCacheHelper.resx
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogBadIfNoneMatch" xml:space="preserve">
- <value>Header If-None-Match is of bad format.</value>
- </data>
- <data name="LogResultData" xml:space="preserve">
- <value>Cache is invalid and data is returned.</value>
- </data>
- <data name="LogResultNotModified" xml:space="preserve">
- <value>Cache is valid and 304 Not Modified is returned.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Messages.Designer.cs b/Timeline/Resources/Messages.Designer.cs
deleted file mode 100644
index bb654ce6..00000000
--- a/Timeline/Resources/Messages.Designer.cs
+++ /dev/null
@@ -1,396 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Messages {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Messages() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Messages", typeof(Messages).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Body is too big. It can&apos;t be bigger than {0}..
- /// </summary>
- internal static string Common_Content_TooBig {
- get {
- return ResourceManager.GetString("Common_Content_TooBig", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Actual body length is bigger than it in header..
- /// </summary>
- internal static string Common_Content_UnmatchedLength_Bigger {
- get {
- return ResourceManager.GetString("Common_Content_UnmatchedLength_Bigger", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Actual body length is smaller than it in header..
- /// </summary>
- internal static string Common_Content_UnmatchedLength_Smaller {
- get {
- return ResourceManager.GetString("Common_Content_UnmatchedLength_Smaller", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You have no permission to do the operation..
- /// </summary>
- internal static string Common_Forbid {
- get {
- return ResourceManager.GetString("Common_Forbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You are not the resource owner..
- /// </summary>
- internal static string Common_Forbid_NotSelf {
- get {
- return ResourceManager.GetString("Common_Forbid_NotSelf", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Length is missing or of bad format..
- /// </summary>
- internal static string Common_Header_ContentLength_Missing {
- get {
- return ResourceManager.GetString("Common_Header_ContentLength_Missing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Length must not be 0..
- /// </summary>
- internal static string Common_Header_ContentLength_Zero {
- get {
- return ResourceManager.GetString("Common_Header_ContentLength_Zero", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Type is missing..
- /// </summary>
- internal static string Common_Header_ContentType_Missing {
- get {
- return ResourceManager.GetString("Common_Header_ContentType_Missing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header If-Non-Match is of bad format..
- /// </summary>
- internal static string Common_Header_IfNonMatch_BadFormat {
- get {
- return ResourceManager.GetString("Common_Header_IfNonMatch_BadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Model is of bad format..
- /// </summary>
- internal static string Common_InvalidModel {
- get {
- return ResourceManager.GetString("Common_InvalidModel", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The api endpoint you request is unknown. You might get the wrong api entry..
- /// </summary>
- internal static string Common_UnknownEndpoint {
- get {
- return ResourceManager.GetString("Common_UnknownEndpoint", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown type of post content..
- /// </summary>
- internal static string TimelineController_ContentUnknownType {
- get {
- return ResourceManager.GetString("TimelineController_ContentUnknownType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is not a valid base64 string in image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataNotBase64 {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataNotBase64", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is not a valid image after base64 decoding in image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataNotImage {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataNotImage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is required for image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataRequired {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user to set as member does not exist..
- /// </summary>
- internal static string TimelineController_MemberPut_NotExist {
- get {
- return ResourceManager.GetString("TimelineController_MemberPut_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A timeline with given name already exists..
- /// </summary>
- internal static string TimelineController_NameConflict {
- get {
- return ResourceManager.GetString("TimelineController_NameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline with given name does not exist..
- /// </summary>
- internal static string TimelineController_NotExist {
- get {
- return ResourceManager.GetString("TimelineController_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post of that type has no data..
- /// </summary>
- internal static string TimelineController_PostNoData {
- get {
- return ResourceManager.GetString("TimelineController_PostNoData", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post to operate on does not exist..
- /// </summary>
- internal static string TimelineController_PostNotExist {
- get {
- return ResourceManager.GetString("TimelineController_PostNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user specified by query param &quot;relate&quot; does not exist..
- /// </summary>
- internal static string TimelineController_QueryRelateNotExist {
- get {
- return ResourceManager.GetString("TimelineController_QueryRelateNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to &apos;{0}&apos; is an unkown visibility in the query parameter &apos;visibility&apos;. .
- /// </summary>
- internal static string TimelineController_QueryVisibilityUnknown {
- get {
- return ResourceManager.GetString("TimelineController_QueryVisibilityUnknown", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Text field is required for text content..
- /// </summary>
- internal static string TimelineController_TextContentTextRequired {
- get {
- return ResourceManager.GetString("TimelineController_TextContentTextRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username or password is invalid..
- /// </summary>
- internal static string TokenController_Create_BadCredential {
- get {
- return ResourceManager.GetString("TokenController_Create_BadCredential", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format. It might not be created by the server..
- /// </summary>
- internal static string TokenController_Verify_BadFormat {
- get {
- return ResourceManager.GetString("TokenController_Verify_BadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token has an old version. User might have update some info..
- /// </summary>
- internal static string TokenController_Verify_OldVersion {
- get {
- return ResourceManager.GetString("TokenController_Verify_OldVersion", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired..
- /// </summary>
- internal static string TokenController_Verify_TimeExpired {
- get {
- return ResourceManager.GetString("TokenController_Verify_TimeExpired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to User does not exist. Administrator might have deleted this user..
- /// </summary>
- internal static string TokenController_Verify_UserNotExist {
- get {
- return ResourceManager.GetString("TokenController_Verify_UserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image is not a square..
- /// </summary>
- internal static string UserAvatar_BadFormat_BadSize {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_BadSize", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image decode failed..
- /// </summary>
- internal static string UserAvatar_BadFormat_CantDecode {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_CantDecode", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image format does not match the one in header..
- /// </summary>
- internal static string UserAvatar_BadFormat_UnmatchedFormat {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_UnmatchedFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user to operate on does not exist..
- /// </summary>
- internal static string UserCommon_NotExist {
- get {
- return ResourceManager.GetString("UserCommon_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Old password is wrong..
- /// </summary>
- internal static string UserController_ChangePassword_BadOldPassword {
- get {
- return ResourceManager.GetString("UserController_ChangePassword_BadOldPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can&apos;t set permission unless you are administrator..
- /// </summary>
- internal static string UserController_Patch_Forbid_Administrator {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Administrator", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can&apos;t set password unless you are administrator. If you want to change password, use /userop/changepassword ..
- /// </summary>
- internal static string UserController_Patch_Forbid_Password {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Password", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can&apos;t set username unless you are administrator..
- /// </summary>
- internal static string UserController_Patch_Forbid_Username {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Username", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user with given username already exists..
- /// </summary>
- internal static string UserController_UsernameConflict {
- get {
- return ResourceManager.GetString("UserController_UsernameConflict", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Messages.resx b/Timeline/Resources/Messages.resx
deleted file mode 100644
index 2bbf494e..00000000
--- a/Timeline/Resources/Messages.resx
+++ /dev/null
@@ -1,231 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="Common_Content_TooBig" xml:space="preserve">
- <value>Body is too big. It can't be bigger than {0}.</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Bigger" xml:space="preserve">
- <value>Actual body length is bigger than it in header.</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Smaller" xml:space="preserve">
- <value>Actual body length is smaller than it in header.</value>
- </data>
- <data name="Common_Forbid" xml:space="preserve">
- <value>You have no permission to do the operation.</value>
- </data>
- <data name="Common_Forbid_NotSelf" xml:space="preserve">
- <value>You are not the resource owner.</value>
- </data>
- <data name="Common_Header_ContentLength_Missing" xml:space="preserve">
- <value>Header Content-Length is missing or of bad format.</value>
- </data>
- <data name="Common_Header_ContentLength_Zero" xml:space="preserve">
- <value>Header Content-Length must not be 0.</value>
- </data>
- <data name="Common_Header_ContentType_Missing" xml:space="preserve">
- <value>Header Content-Type is missing.</value>
- </data>
- <data name="Common_Header_IfNonMatch_BadFormat" xml:space="preserve">
- <value>Header If-Non-Match is of bad format.</value>
- </data>
- <data name="Common_InvalidModel" xml:space="preserve">
- <value>Model is of bad format.</value>
- </data>
- <data name="Common_UnknownEndpoint" xml:space="preserve">
- <value>The api endpoint you request is unknown. You might get the wrong api entry.</value>
- </data>
- <data name="TimelineController_ContentUnknownType" xml:space="preserve">
- <value>Unknown type of post content.</value>
- </data>
- <data name="TimelineController_ImageContentDataNotBase64" xml:space="preserve">
- <value>Data field is not a valid base64 string in image content.</value>
- </data>
- <data name="TimelineController_ImageContentDataNotImage" xml:space="preserve">
- <value>Data field is not a valid image after base64 decoding in image content.</value>
- </data>
- <data name="TimelineController_ImageContentDataRequired" xml:space="preserve">
- <value>Data field is required for image content.</value>
- </data>
- <data name="TimelineController_MemberPut_NotExist" xml:space="preserve">
- <value>The user to set as member does not exist.</value>
- </data>
- <data name="TimelineController_NameConflict" xml:space="preserve">
- <value>A timeline with given name already exists.</value>
- </data>
- <data name="TimelineController_NotExist" xml:space="preserve">
- <value>The timeline with given name does not exist.</value>
- </data>
- <data name="TimelineController_PostNoData" xml:space="preserve">
- <value>The post of that type has no data.</value>
- </data>
- <data name="TimelineController_PostNotExist" xml:space="preserve">
- <value>The post to operate on does not exist.</value>
- </data>
- <data name="TimelineController_QueryRelateNotExist" xml:space="preserve">
- <value>The user specified by query param "relate" does not exist.</value>
- </data>
- <data name="TimelineController_QueryVisibilityUnknown" xml:space="preserve">
- <value>'{0}' is an unkown visibility in the query parameter 'visibility'. </value>
- </data>
- <data name="TimelineController_TextContentTextRequired" xml:space="preserve">
- <value>Text field is required for text content.</value>
- </data>
- <data name="TokenController_Create_BadCredential" xml:space="preserve">
- <value>Username or password is invalid.</value>
- </data>
- <data name="TokenController_Verify_BadFormat" xml:space="preserve">
- <value>The token is of bad format. It might not be created by the server.</value>
- </data>
- <data name="TokenController_Verify_OldVersion" xml:space="preserve">
- <value>Token has an old version. User might have update some info.</value>
- </data>
- <data name="TokenController_Verify_TimeExpired" xml:space="preserve">
- <value>The token is expired.</value>
- </data>
- <data name="TokenController_Verify_UserNotExist" xml:space="preserve">
- <value>User does not exist. Administrator might have deleted this user.</value>
- </data>
- <data name="UserAvatar_BadFormat_BadSize" xml:space="preserve">
- <value>Image is not a square.</value>
- </data>
- <data name="UserAvatar_BadFormat_CantDecode" xml:space="preserve">
- <value>Image decode failed.</value>
- </data>
- <data name="UserAvatar_BadFormat_UnmatchedFormat" xml:space="preserve">
- <value>Image format does not match the one in header.</value>
- </data>
- <data name="UserCommon_NotExist" xml:space="preserve">
- <value>The user to operate on does not exist.</value>
- </data>
- <data name="UserController_ChangePassword_BadOldPassword" xml:space="preserve">
- <value>Old password is wrong.</value>
- </data>
- <data name="UserController_Patch_Forbid_Administrator" xml:space="preserve">
- <value>You can't set permission unless you are administrator.</value>
- </data>
- <data name="UserController_Patch_Forbid_Password" xml:space="preserve">
- <value>You can't set password unless you are administrator. If you want to change password, use /userop/changepassword .</value>
- </data>
- <data name="UserController_Patch_Forbid_Username" xml:space="preserve">
- <value>You can't set username unless you are administrator.</value>
- </data>
- <data name="UserController_UsernameConflict" xml:space="preserve">
- <value>A user with given username already exists.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Http/Common.Designer.cs b/Timeline/Resources/Models/Http/Common.Designer.cs
deleted file mode 100644
index 5165463e..00000000
--- a/Timeline/Resources/Models/Http/Common.Designer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Http {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Common {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Common() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Http.Common", typeof(Common).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An existent item is deleted..
- /// </summary>
- internal static string MessageDeleteDelete {
- get {
- return ResourceManager.GetString("MessageDeleteDelete", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The item does not exist, so nothing is changed..
- /// </summary>
- internal static string MessageDeleteNotExist {
- get {
- return ResourceManager.GetString("MessageDeleteNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A new item is created..
- /// </summary>
- internal static string MessagePutCreate {
- get {
- return ResourceManager.GetString("MessagePutCreate", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An existent item is modified..
- /// </summary>
- internal static string MessagePutModify {
- get {
- return ResourceManager.GetString("MessagePutModify", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Http/Common.resx b/Timeline/Resources/Models/Http/Common.resx
deleted file mode 100644
index 85ec4d32..00000000
--- a/Timeline/Resources/Models/Http/Common.resx
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageDeleteDelete" xml:space="preserve">
- <value>An existent item is deleted.</value>
- </data>
- <data name="MessageDeleteNotExist" xml:space="preserve">
- <value>The item does not exist, so nothing is changed.</value>
- </data>
- <data name="MessagePutCreate" xml:space="preserve">
- <value>A new item is created.</value>
- </data>
- <data name="MessagePutModify" xml:space="preserve">
- <value>An existent item is modified.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Http/Exception.Designer.cs b/Timeline/Resources/Models/Http/Exception.Designer.cs
deleted file mode 100644
index 19f42793..00000000
--- a/Timeline/Resources/Models/Http/Exception.Designer.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Http {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exception {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exception() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Http.Exception", typeof(Exception).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No action context currently, can&apos;t fill urls in value resolver..
- /// </summary>
- internal static string ActionContextNull {
- get {
- return ResourceManager.GetString("ActionContextNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown post content type..
- /// </summary>
- internal static string UnknownPostContentType {
- get {
- return ResourceManager.GetString("UnknownPostContentType", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Http/Exception.resx b/Timeline/Resources/Models/Http/Exception.resx
deleted file mode 100644
index 3f7bddb6..00000000
--- a/Timeline/Resources/Models/Http/Exception.resx
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ActionContextNull" xml:space="preserve">
- <value>No action context currently, can't fill urls in value resolver.</value>
- </data>
- <data name="UnknownPostContentType" xml:space="preserve">
- <value>Unknown post content type.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/NameValidator.Designer.cs b/Timeline/Resources/Models/Validation/NameValidator.Designer.cs
deleted file mode 100644
index 3050049e..00000000
--- a/Timeline/Resources/Models/Validation/NameValidator.Designer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class NameValidator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal NameValidator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NameValidator", typeof(NameValidator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An empty string is not allowed..
- /// </summary>
- internal static string MessageEmptyString {
- get {
- return ResourceManager.GetString("MessageEmptyString", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Invalid character, only alphabet, digit, underscore and hyphen are allowed..
- /// </summary>
- internal static string MessageInvalidChar {
- get {
- return ResourceManager.GetString("MessageInvalidChar", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Too long, more than 26 characters is not premitted..
- /// </summary>
- internal static string MessageTooLong {
- get {
- return ResourceManager.GetString("MessageTooLong", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Name can&apos;t be of the same format of unique id..
- /// </summary>
- internal static string MessageUnqiueId {
- get {
- return ResourceManager.GetString("MessageUnqiueId", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/NameValidator.resx b/Timeline/Resources/Models/Validation/NameValidator.resx
deleted file mode 100644
index 5e7e1745..00000000
--- a/Timeline/Resources/Models/Validation/NameValidator.resx
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageEmptyString" xml:space="preserve">
- <value>An empty string is not allowed.</value>
- </data>
- <data name="MessageInvalidChar" xml:space="preserve">
- <value>Invalid character, only alphabet, digit, underscore and hyphen are allowed.</value>
- </data>
- <data name="MessageTooLong" xml:space="preserve">
- <value>Too long, more than 26 characters is not premitted.</value>
- </data>
- <data name="MessageUnqiueId" xml:space="preserve">
- <value>Name can't be of the same format of unique id.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs b/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs
deleted file mode 100644
index 522f305a..00000000
--- a/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class NicknameValidator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal NicknameValidator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NicknameValidator", typeof(NicknameValidator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Nickname is too long..
- /// </summary>
- internal static string MessageTooLong {
- get {
- return ResourceManager.GetString("MessageTooLong", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/NicknameValidator.resx b/Timeline/Resources/Models/Validation/NicknameValidator.resx
deleted file mode 100644
index b191b505..00000000
--- a/Timeline/Resources/Models/Validation/NicknameValidator.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageTooLong" xml:space="preserve">
- <value>Nickname is too long.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Models/Validation/Validator.Designer.cs b/Timeline/Resources/Models/Validation/Validator.Designer.cs
deleted file mode 100644
index 74d4c169..00000000
--- a/Timeline/Resources/Models/Validation/Validator.Designer.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Validator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Validator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.Validator", typeof(Validator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to create a validator instance from default constructor. See inner exception..
- /// </summary>
- internal static string ValidateWithAttributeExceptionCreateFail {
- get {
- return ResourceManager.GetString("ValidateWithAttributeExceptionCreateFail", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Given type is not assignable to IValidator..
- /// </summary>
- internal static string ValidateWithAttributeExceptionNotValidator {
- get {
- return ResourceManager.GetString("ValidateWithAttributeExceptionNotValidator", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Value is not of type {0}..
- /// </summary>
- internal static string ValidatorMessageBadType {
- get {
- return ResourceManager.GetString("ValidatorMessageBadType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Value can&apos;t be null..
- /// </summary>
- internal static string ValidatorMessageNull {
- get {
- return ResourceManager.GetString("ValidatorMessageNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Validation succeeded..
- /// </summary>
- internal static string ValidatorMessageSuccess {
- get {
- return ResourceManager.GetString("ValidatorMessageSuccess", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/Validator.resx b/Timeline/Resources/Models/Validation/Validator.resx
deleted file mode 100644
index 8317e3eb..00000000
--- a/Timeline/Resources/Models/Validation/Validator.resx
+++ /dev/null
@@ -1,135 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ValidateWithAttributeExceptionCreateFail" xml:space="preserve">
- <value>Failed to create a validator instance from default constructor. See inner exception.</value>
- </data>
- <data name="ValidateWithAttributeExceptionNotValidator" xml:space="preserve">
- <value>Given type is not assignable to IValidator.</value>
- </data>
- <data name="ValidatorMessageBadType" xml:space="preserve">
- <value>Value is not of type {0}.</value>
- </data>
- <data name="ValidatorMessageNull" xml:space="preserve">
- <value>Value can't be null.</value>
- </data>
- <data name="ValidatorMessageSuccess" xml:space="preserve">
- <value>Validation succeeded.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/DataManager.Designer.cs b/Timeline/Resources/Services/DataManager.Designer.cs
deleted file mode 100644
index 0872059a..00000000
--- a/Timeline/Resources/Services/DataManager.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class DataManager {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal DataManager() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.DataManager", typeof(DataManager).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Entry with given tag does not exist..
- /// </summary>
- internal static string ExceptionEntryNotExist {
- get {
- return ResourceManager.GetString("ExceptionEntryNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/DataManager.resx b/Timeline/Resources/Services/DataManager.resx
deleted file mode 100644
index 688e0e96..00000000
--- a/Timeline/Resources/Services/DataManager.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionEntryNotExist" xml:space="preserve">
- <value>Entry with given tag does not exist.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/Exception.Designer.cs b/Timeline/Resources/Services/Exception.Designer.cs
deleted file mode 100644
index 21ca7b86..00000000
--- a/Timeline/Resources/Services/Exception.Designer.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exception {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exception() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.Exception", typeof(Exception).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The password is wrong..
- /// </summary>
- internal static string BadPasswordException {
- get {
- return ResourceManager.GetString("BadPasswordException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The hashes password is of bad format. It might not be created by server..
- /// </summary>
- internal static string HashedPasswordBadFromatException {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Not of valid base64 format. See inner exception..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotBase64 {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotBase64", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Decoded hashed password is of length 0..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotLength0 {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotLength0", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to See inner exception..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotOthers {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotOthers", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Salt length &lt; 128 bits..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotSaltTooShort {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotSaltTooShort", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Subkey length &lt; 128 bits..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotSubkeyTooShort {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotSubkeyTooShort", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown format marker..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotUnknownMarker {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotUnknownMarker", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token didn&apos;t pass verification because {0}..
- /// </summary>
- internal static string JwtUserTokenBadFormatException {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim is not a number.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionIdBadFormat {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionIdBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim does not exist.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionIdMissing {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionIdMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to other error, see inner exception for information.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionOthers {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionOthers", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to unknown error.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionUnknown {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionUnknown", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim is not a number..
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionVersionBadFormat {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionVersionBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim does not exist..
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionVersionMissing {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionVersionMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password is of bad format..
- /// </summary>
- internal static string PasswordBadFormatException {
- get {
- return ResourceManager.GetString("PasswordBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format, which means it may not be created by the server..
- /// </summary>
- internal static string UserTokenBadFormatException {
- get {
- return ResourceManager.GetString("UserTokenBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad version..
- /// </summary>
- internal static string UserTokenBadVersionException {
- get {
- return ResourceManager.GetString("UserTokenBadVersionException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired because its expiration time has passed..
- /// </summary>
- internal static string UserTokenTimeExpireException {
- get {
- return ResourceManager.GetString("UserTokenTimeExpireException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/Exception.resx b/Timeline/Resources/Services/Exception.resx
deleted file mode 100644
index c31ed7c7..00000000
--- a/Timeline/Resources/Services/Exception.resx
+++ /dev/null
@@ -1,177 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="BadPasswordException" xml:space="preserve">
- <value>The password is wrong.</value>
- </data>
- <data name="HashedPasswordBadFromatException" xml:space="preserve">
- <value>The hashes password is of bad format. It might not be created by server.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotBase64" xml:space="preserve">
- <value>Not of valid base64 format. See inner exception.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotLength0" xml:space="preserve">
- <value>Decoded hashed password is of length 0.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotOthers" xml:space="preserve">
- <value>See inner exception.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotSaltTooShort" xml:space="preserve">
- <value>Salt length &lt; 128 bits.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotSubkeyTooShort" xml:space="preserve">
- <value>Subkey length &lt; 128 bits.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotUnknownMarker" xml:space="preserve">
- <value>Unknown format marker.</value>
- </data>
- <data name="JwtUserTokenBadFormatException" xml:space="preserve">
- <value>The token didn't pass verification because {0}.</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionIdBadFormat" xml:space="preserve">
- <value>id claim is not a number</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionIdMissing" xml:space="preserve">
- <value>id claim does not exist</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionOthers" xml:space="preserve">
- <value>other error, see inner exception for information</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionUnknown" xml:space="preserve">
- <value>unknown error</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionVersionBadFormat" xml:space="preserve">
- <value>version claim is not a number.</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionVersionMissing" xml:space="preserve">
- <value>version claim does not exist.</value>
- </data>
- <data name="PasswordBadFormatException" xml:space="preserve">
- <value>Password is of bad format.</value>
- </data>
- <data name="UserTokenBadFormatException" xml:space="preserve">
- <value>The token is of bad format, which means it may not be created by the server.</value>
- </data>
- <data name="UserTokenBadVersionException" xml:space="preserve">
- <value>The token is of bad version.</value>
- </data>
- <data name="UserTokenTimeExpireException" xml:space="preserve">
- <value>The token is expired because its expiration time has passed.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/Exceptions.Designer.cs b/Timeline/Resources/Services/Exceptions.Designer.cs
deleted file mode 100644
index 1dbe11c9..00000000
--- a/Timeline/Resources/Services/Exceptions.Designer.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exceptions {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exceptions() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.Exceptions", typeof(Exceptions).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A entity of type &quot;{0}&quot; already exists..
- /// </summary>
- internal static string EntityAlreadyExistError {
- get {
- return ResourceManager.GetString("EntityAlreadyExistError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The entity already exists..
- /// </summary>
- internal static string EntityAlreadyExistErrorDefault {
- get {
- return ResourceManager.GetString("EntityAlreadyExistErrorDefault", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The required entity of type &quot;{0}&quot; does not exist..
- /// </summary>
- internal static string EntityNotExistError {
- get {
- return ResourceManager.GetString("EntityNotExistError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The required entity does not exist..
- /// </summary>
- internal static string EntityNotExistErrorDefault {
- get {
- return ResourceManager.GetString("EntityNotExistErrorDefault", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image is in valid because {0}..
- /// </summary>
- internal static string ImageException {
- get {
- return ResourceManager.GetString("ImageException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to image is not of required size.
- /// </summary>
- internal static string ImageExceptionBadSize {
- get {
- return ResourceManager.GetString("ImageExceptionBadSize", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to failed to decode image, see inner exception.
- /// </summary>
- internal static string ImageExceptionCantDecode {
- get {
- return ResourceManager.GetString("ImageExceptionCantDecode", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to unknown error.
- /// </summary>
- internal static string ImageExceptionUnknownError {
- get {
- return ResourceManager.GetString("ImageExceptionUnknownError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to image&apos;s actual mime type is not the specified one.
- /// </summary>
- internal static string ImageExceptionUnmatchedFormat {
- get {
- return ResourceManager.GetString("ImageExceptionUnmatchedFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline has no data..
- /// </summary>
- internal static string TimelineNoDataException {
- get {
- return ResourceManager.GetString("TimelineNoDataException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is &quot;{0}&quot;. If this is a personal timeline whose name starts with &apos;@&apos;, it means the user does not exist and inner exception should be a UserNotExistException..
- /// </summary>
- internal static string TimelineNotExistException {
- get {
- return ResourceManager.GetString("TimelineNotExistException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is &quot;{0}&quot;. Request timeline post id is &quot;{1}&quot;..
- /// </summary>
- internal static string TimelinePostNotExistException {
- get {
- return ResourceManager.GetString("TimelinePostNotExistException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is &quot;{0}&quot;. Request timeline post id is &quot;{1}&quot;. The post does not exist because it is deleted..
- /// </summary>
- internal static string TimelinePostNotExistExceptionDeleted {
- get {
- return ResourceManager.GetString("TimelinePostNotExistExceptionDeleted", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request username is &quot;{0}&quot;. Request id is &quot;{1}&quot;..
- /// </summary>
- internal static string UserNotExistException {
- get {
- return ResourceManager.GetString("UserNotExistException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/Exceptions.resx b/Timeline/Resources/Services/Exceptions.resx
deleted file mode 100644
index e9595caa..00000000
--- a/Timeline/Resources/Services/Exceptions.resx
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 1.3
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">1.3</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1">this is my long string</data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- [base64 mime encoded serialized .NET Framework object]
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- [base64 mime encoded string representing a byte array form of the .NET Framework object]
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>1.3</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="EntityNotExistError" xml:space="preserve">
- <value>The required entity of type "{0}" does not exist.</value>
- </data>
- <data name="EntityAlreadyExistErrorDefault" xml:space="preserve">
- <value>The entity already exists.</value>
- </data>
- <data name="EntityAlreadyExistError" xml:space="preserve">
- <value>A entity of type "{0}" already exists.</value>
- </data>
- <data name="EntityNotExistErrorDefault" xml:space="preserve">
- <value>The required entity does not exist.</value>
- </data>
- <data name="TimelineNotExistException" xml:space="preserve">
- <value>Request timeline name is "{0}". If this is a personal timeline whose name starts with '@', it means the user does not exist and inner exception should be a UserNotExistException.</value>
- </data>
- <data name="TimelinePostNotExistException" xml:space="preserve">
- <value>Request timeline name is "{0}". Request timeline post id is "{1}".</value>
- </data>
- <data name="UserNotExistException" xml:space="preserve">
- <value>Request username is "{0}". Request id is "{1}".</value>
- </data>
- <data name="TimelineNoDataException" xml:space="preserve">
- <value>The timeline has no data.</value>
- </data>
- <data name="ImageException" xml:space="preserve">
- <value>Image is in valid because {0}.</value>
- </data>
- <data name="ImageExceptionBadSize" xml:space="preserve">
- <value>image is not of required size</value>
- </data>
- <data name="ImageExceptionCantDecode" xml:space="preserve">
- <value>failed to decode image, see inner exception</value>
- </data>
- <data name="ImageExceptionUnknownError" xml:space="preserve">
- <value>unknown error</value>
- </data>
- <data name="ImageExceptionUnmatchedFormat" xml:space="preserve">
- <value>image's actual mime type is not the specified one</value>
- </data>
- <data name="TimelinePostNotExistExceptionDeleted" xml:space="preserve">
- <value>Request timeline name is "{0}". Request timeline post id is "{1}". The post does not exist because it is deleted.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/TimelineService.Designer.cs b/Timeline/Resources/Services/TimelineService.Designer.cs
deleted file mode 100644
index e16c1337..00000000
--- a/Timeline/Resources/Services/TimelineService.Designer.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TimelineService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TimelineService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.TimelineService", typeof(TimelineService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The number {0} username is invalid..
- /// </summary>
- internal static string ExceptionChangeMemberUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionChangeMemberUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown post content type &quot;{0}&quot; is saved in database..
- /// </summary>
- internal static string ExceptionDatabaseUnknownContentType {
- get {
- return ResourceManager.GetString("ExceptionDatabaseUnknownContentType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The owner username of personal timeline is of bad format..
- /// </summary>
- internal static string ExceptionFindTimelineUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionFindTimelineUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The data entry of the tag of the image post does not exist..
- /// </summary>
- internal static string ExceptionGetDataDataEntryNotExist {
- get {
- return ResourceManager.GetString("ExceptionGetDataDataEntryNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Can&apos;t get data of a non-image post..
- /// </summary>
- internal static string ExceptionGetDataNonImagePost {
- get {
- return ResourceManager.GetString("ExceptionGetDataNonImagePost", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post has been deleted because content of entity is null..
- /// </summary>
- internal static string ExceptionPostDeleted {
- get {
- return ResourceManager.GetString("ExceptionPostDeleted", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline name is of bad format..
- /// </summary>
- internal static string ExceptionTimelineNameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionTimelineNameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline with given name already exists..
- /// </summary>
- internal static string ExceptionTimelineNameConflict {
- get {
- return ResourceManager.GetString("ExceptionTimelineNameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image format type of the post does not exist in column &quot;extra_content&quot;. Normally this couldn&apos;t be possible because it should be saved when post was created. However, we now re-detect the format and save it..
- /// </summary>
- internal static string LogGetDataNoFormat {
- get {
- return ResourceManager.GetString("LogGetDataNoFormat", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/TimelineService.resx b/Timeline/Resources/Services/TimelineService.resx
deleted file mode 100644
index 9314f51b..00000000
--- a/Timeline/Resources/Services/TimelineService.resx
+++ /dev/null
@@ -1,147 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionChangeMemberUsernameBadFormat" xml:space="preserve">
- <value>The number {0} username is invalid.</value>
- </data>
- <data name="ExceptionDatabaseUnknownContentType" xml:space="preserve">
- <value>Unknown post content type "{0}" is saved in database.</value>
- </data>
- <data name="ExceptionFindTimelineUsernameBadFormat" xml:space="preserve">
- <value>The owner username of personal timeline is of bad format.</value>
- </data>
- <data name="ExceptionGetDataDataEntryNotExist" xml:space="preserve">
- <value>The data entry of the tag of the image post does not exist.</value>
- </data>
- <data name="ExceptionGetDataNonImagePost" xml:space="preserve">
- <value>Can't get data of a non-image post.</value>
- </data>
- <data name="ExceptionTimelineNameBadFormat" xml:space="preserve">
- <value>The timeline name is of bad format.</value>
- </data>
- <data name="ExceptionTimelineNameConflict" xml:space="preserve">
- <value>The timeline with given name already exists.</value>
- </data>
- <data name="LogGetDataNoFormat" xml:space="preserve">
- <value>Image format type of the post does not exist in column "extra_content". Normally this couldn't be possible because it should be saved when post was created. However, we now re-detect the format and save it.</value>
- </data>
- <data name="ExceptionPostDeleted" xml:space="preserve">
- <value>The post has been deleted because content of entity is null.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/UserAvatarService.Designer.cs b/Timeline/Resources/Services/UserAvatarService.Designer.cs
deleted file mode 100644
index c72d4215..00000000
--- a/Timeline/Resources/Services/UserAvatarService.Designer.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserAvatarService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserAvatarService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserAvatarService", typeof(UserAvatarService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data of avatar is null..
- /// </summary>
- internal static string ExceptionAvatarDataNull {
- get {
- return ResourceManager.GetString("ExceptionAvatarDataNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Type of avatar is null or empty..
- /// </summary>
- internal static string ExceptionAvatarTypeNullOrEmpty {
- get {
- return ResourceManager.GetString("ExceptionAvatarTypeNullOrEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Database corupted! One of type and data of a avatar is null but the other is not..
- /// </summary>
- internal static string ExceptionDatabaseCorruptedDataAndTypeNotSame {
- get {
- return ResourceManager.GetString("ExceptionDatabaseCorruptedDataAndTypeNotSame", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Created an entry in user_avatars..
- /// </summary>
- internal static string LogCreateEntity {
- get {
- return ResourceManager.GetString("LogCreateEntity", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Updated an entry in user_avatars..
- /// </summary>
- internal static string LogUpdateEntity {
- get {
- return ResourceManager.GetString("LogUpdateEntity", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserAvatarService.resx b/Timeline/Resources/Services/UserAvatarService.resx
deleted file mode 100644
index da9d7203..00000000
--- a/Timeline/Resources/Services/UserAvatarService.resx
+++ /dev/null
@@ -1,135 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionAvatarDataNull" xml:space="preserve">
- <value>Data of avatar is null.</value>
- </data>
- <data name="ExceptionAvatarTypeNullOrEmpty" xml:space="preserve">
- <value>Type of avatar is null or empty.</value>
- </data>
- <data name="ExceptionDatabaseCorruptedDataAndTypeNotSame" xml:space="preserve">
- <value>Database corupted! One of type and data of a avatar is null but the other is not.</value>
- </data>
- <data name="LogCreateEntity" xml:space="preserve">
- <value>Created an entry in user_avatars.</value>
- </data>
- <data name="LogUpdateEntity" xml:space="preserve">
- <value>Updated an entry in user_avatars.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/UserService.Designer.cs b/Timeline/Resources/Services/UserService.Designer.cs
deleted file mode 100644
index cdf7f390..00000000
--- a/Timeline/Resources/Services/UserService.Designer.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserService", typeof(UserService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to New username is of bad format..
- /// </summary>
- internal static string ExceptionNewUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionNewUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Nickname is of bad format, because {}..
- /// </summary>
- internal static string ExceptionNicknameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionNicknameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Old username is of bad format..
- /// </summary>
- internal static string ExceptionOldUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionOldUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password can&apos;t be empty..
- /// </summary>
- internal static string ExceptionPasswordEmpty {
- get {
- return ResourceManager.GetString("ExceptionPasswordEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password can&apos;t be null..
- /// </summary>
- internal static string ExceptionPasswordNull {
- get {
- return ResourceManager.GetString("ExceptionPasswordNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username is of bad format, because {}..
- /// </summary>
- internal static string ExceptionUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user with given username already exists..
- /// </summary>
- internal static string ExceptionUsernameConflict {
- get {
- return ResourceManager.GetString("ExceptionUsernameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username can&apos;t be null..
- /// </summary>
- internal static string ExceptionUsernameNull {
- get {
- return ResourceManager.GetString("ExceptionUsernameNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A new user entry is added to the database..
- /// </summary>
- internal static string LogDatabaseCreate {
- get {
- return ResourceManager.GetString("LogDatabaseCreate", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user entry is removed from the database..
- /// </summary>
- internal static string LogDatabaseRemove {
- get {
- return ResourceManager.GetString("LogDatabaseRemove", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user entry is updated to the database..
- /// </summary>
- internal static string LogDatabaseUpdate {
- get {
- return ResourceManager.GetString("LogDatabaseUpdate", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserService.resx b/Timeline/Resources/Services/UserService.resx
deleted file mode 100644
index 09bd4abb..00000000
--- a/Timeline/Resources/Services/UserService.resx
+++ /dev/null
@@ -1,153 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionNewUsernameBadFormat" xml:space="preserve">
- <value>New username is of bad format.</value>
- </data>
- <data name="ExceptionNicknameBadFormat" xml:space="preserve">
- <value>Nickname is of bad format, because {}.</value>
- </data>
- <data name="ExceptionOldUsernameBadFormat" xml:space="preserve">
- <value>Old username is of bad format.</value>
- </data>
- <data name="ExceptionPasswordEmpty" xml:space="preserve">
- <value>Password can't be empty.</value>
- </data>
- <data name="ExceptionPasswordNull" xml:space="preserve">
- <value>Password can't be null.</value>
- </data>
- <data name="ExceptionUsernameBadFormat" xml:space="preserve">
- <value>Username is of bad format, because {}.</value>
- </data>
- <data name="ExceptionUsernameConflict" xml:space="preserve">
- <value>A user with given username already exists.</value>
- </data>
- <data name="ExceptionUsernameNull" xml:space="preserve">
- <value>Username can't be null.</value>
- </data>
- <data name="LogDatabaseCreate" xml:space="preserve">
- <value>A new user entry is added to the database.</value>
- </data>
- <data name="LogDatabaseRemove" xml:space="preserve">
- <value>A user entry is removed from the database.</value>
- </data>
- <data name="LogDatabaseUpdate" xml:space="preserve">
- <value>A user entry is updated to the database.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Resources/Services/UserTokenService.Designer.cs b/Timeline/Resources/Services/UserTokenService.Designer.cs
deleted file mode 100644
index 3c3c7e41..00000000
--- a/Timeline/Resources/Services/UserTokenService.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserTokenService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserTokenService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserTokenService", typeof(UserTokenService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Jwt token key is not set in database. Maybe you forget to migrate the database..
- /// </summary>
- internal static string JwtKeyNotExist {
- get {
- return ResourceManager.GetString("JwtKeyNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserTokenService.resx b/Timeline/Resources/Services/UserTokenService.resx
deleted file mode 100644
index 1ce78427..00000000
--- a/Timeline/Resources/Services/UserTokenService.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="JwtKeyNotExist" xml:space="preserve">
- <value>Jwt token key is not set in database. Maybe you forget to migrate the database.</value>
- </data>
-</root> \ No newline at end of file
diff --git a/Timeline/Routes/ApiRoutePrefixConvention.cs b/Timeline/Routes/ApiRoutePrefixConvention.cs
deleted file mode 100644
index ca38a0d9..00000000
--- a/Timeline/Routes/ApiRoutePrefixConvention.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using System.Linq;
-
-namespace Timeline.Routes
-{
- public static class MvcOptionsExtensions
- {
- public static void UseApiRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
- {
- opts.Conventions.Add(new ApiRoutePrefixConvention(routeAttribute));
- }
-
- public static void UseApiRoutePrefix(this MvcOptions opts, string prefix)
- {
- opts.UseApiRoutePrefix(new RouteAttribute(prefix));
- }
- }
-
- public class ApiRoutePrefixConvention : IApplicationModelConvention
- {
- private readonly AttributeRouteModel _routePrefix;
-
- public ApiRoutePrefixConvention(IRouteTemplateProvider route)
- {
- _routePrefix = new AttributeRouteModel(route);
- }
-
- public void Apply(ApplicationModel application)
- {
- foreach (var selector in application.Controllers.Where(c => c.Filters.Any(f => f is IApiBehaviorMetadata)).SelectMany(c => c.Selectors))
- {
- if (selector.AttributeRouteModel != null)
- {
- selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel);
- }
- else
- {
- selector.AttributeRouteModel = _routePrefix;
- }
- }
- }
- }
-}
diff --git a/Timeline/Routes/UnknownEndpointMiddleware.cs b/Timeline/Routes/UnknownEndpointMiddleware.cs
deleted file mode 100644
index 25ec563c..00000000
--- a/Timeline/Routes/UnknownEndpointMiddleware.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using System;
-using System.Net.Mime;
-using System.Text.Json;
-using Timeline.Models.Http;
-
-namespace Timeline.Routes
-{
- public static class UnknownEndpointMiddleware
- {
- public static void Attach(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- if (context.GetEndpoint() != null)
- {
- await next();
- return;
- }
-
- if (context.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase))
- {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
- context.Response.ContentType = MediaTypeNames.Application.Json;
-
- var body = JsonSerializer.SerializeToUtf8Bytes(ErrorResponse.Common.UnknownEndpoint(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
-
- context.Response.ContentLength = body.Length;
- await context.Response.Body.WriteAsync(body);
- await context.Response.CompleteAsync();
- return;
- }
-
- await next();
- });
- }
- }
-}
diff --git a/Timeline/Services/BadPasswordException.cs b/Timeline/Services/BadPasswordException.cs
deleted file mode 100644
index f609371d..00000000
--- a/Timeline/Services/BadPasswordException.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using Timeline.Helpers;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class BadPasswordException : Exception
- {
- public BadPasswordException() : base(Resources.Services.Exception.BadPasswordException) { }
- public BadPasswordException(string message, Exception inner) : base(message, inner) { }
-
- public BadPasswordException(string badPassword)
- : base(Log.Format(Resources.Services.Exception.BadPasswordException, ("Bad Password", badPassword)))
- {
- Password = badPassword;
- }
-
- protected BadPasswordException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The wrong password.
- /// </summary>
- public string? Password { get; set; }
- }
-}
diff --git a/Timeline/Services/Clock.cs b/Timeline/Services/Clock.cs
deleted file mode 100644
index 4395edcd..00000000
--- a/Timeline/Services/Clock.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// Convenient for unit test.
- /// </summary>
- public interface IClock
- {
- /// <summary>
- /// Get current time.
- /// </summary>
- /// <returns>Current time.</returns>
- DateTime GetCurrentTime();
- }
-
- public class Clock : IClock
- {
- public Clock()
- {
-
- }
-
- public DateTime GetCurrentTime()
- {
- return DateTime.UtcNow;
- }
- }
-}
diff --git a/Timeline/Services/DataManager.cs b/Timeline/Services/DataManager.cs
deleted file mode 100644
index d447b0d5..00000000
--- a/Timeline/Services/DataManager.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// A data manager controlling data.
- /// </summary>
- /// <remarks>
- /// Identical data will be saved as one copy and return the same tag.
- /// Every data has a ref count. When data is retained, ref count increase.
- /// When data is freed, ref count decease. If ref count is decreased
- /// to 0, the data entry will be destroyed and no longer occupy space.
- /// </remarks>
- public interface IDataManager
- {
- /// <summary>
- /// Saves the data to a new entry if it does not exist,
- /// increases its ref count and returns a tag to the entry.
- /// </summary>
- /// <param name="data">The data. Can't be null.</param>
- /// <returns>The tag of the created entry.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
- public Task<string> RetainEntry(byte[] data);
-
- /// <summary>
- /// Decrease the the ref count of the entry.
- /// Remove it if ref count is zero.
- /// </summary>
- /// <param name="tag">The tag of the entry.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tag"/> is null.</exception>
- /// <remarks>
- /// It's no-op if entry with tag does not exist.
- /// </remarks>
- public Task FreeEntry(string tag);
-
- /// <summary>
- /// Retrieve the entry with given tag.
- /// </summary>
- /// <param name="tag">The tag of the entry.</param>
- /// <returns>The data of the entry.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tag"/> is null.</exception>
- /// <exception cref="InvalidOperationException">Thrown when entry with given tag does not exist.</exception>
- public Task<byte[]> GetEntry(string tag);
- }
-
- public class DataManager : IDataManager
- {
- private readonly DatabaseContext _database;
- private readonly IETagGenerator _eTagGenerator;
-
- public DataManager(DatabaseContext database, IETagGenerator eTagGenerator)
- {
- _database = database;
- _eTagGenerator = eTagGenerator;
- }
-
- public async Task<string> RetainEntry(byte[] data)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var tag = await _eTagGenerator.Generate(data);
-
- var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync();
-
- if (entity == null)
- {
- entity = new DataEntity
- {
- Tag = tag,
- Data = data,
- Ref = 1
- };
- _database.Data.Add(entity);
- }
- else
- {
- entity.Ref += 1;
- }
- await _database.SaveChangesAsync();
- return tag;
- }
-
- public async Task FreeEntry(string tag)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
-
- var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync();
-
- if (entity != null)
- {
- if (entity.Ref == 1)
- {
- _database.Data.Remove(entity);
- }
- else
- {
- entity.Ref -= 1;
- }
- await _database.SaveChangesAsync();
- }
- }
-
- public async Task<byte[]> GetEntry(string tag)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
-
- var entity = await _database.Data.Where(d => d.Tag == tag).Select(d => new { d.Data }).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new InvalidOperationException(Resources.Services.DataManager.ExceptionEntryNotExist);
-
- return entity.Data;
- }
- }
-}
diff --git a/Timeline/Services/DatabaseBackupService.cs b/Timeline/Services/DatabaseBackupService.cs
deleted file mode 100644
index a76b2a0d..00000000
--- a/Timeline/Services/DatabaseBackupService.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Globalization;
-using System.IO;
-
-namespace Timeline.Services
-{
- public interface IDatabaseBackupService
- {
- void BackupNow();
- }
-
- public class DatabaseBackupService : IDatabaseBackupService
- {
- private readonly IPathProvider _pathProvider;
- private readonly IClock _clock;
-
- public DatabaseBackupService(IPathProvider pathProvider, IClock clock)
- {
- _pathProvider = pathProvider;
- _clock = clock;
- }
-
- public void BackupNow()
- {
- var databasePath = _pathProvider.GetDatabaseFilePath();
- if (File.Exists(databasePath))
- {
- var backupDirPath = _pathProvider.GetDatabaseBackupDirectory();
- Directory.CreateDirectory(backupDirPath);
- var fileName = _clock.GetCurrentTime().ToString("yyyy-MM-ddTHH-mm-ss", CultureInfo.InvariantCulture);
- var path = Path.Combine(backupDirPath, fileName);
- File.Copy(databasePath, path);
- }
- }
- }
-}
diff --git a/Timeline/Services/DatabaseCorruptedException.cs b/Timeline/Services/DatabaseCorruptedException.cs
deleted file mode 100644
index 9988e0ad..00000000
--- a/Timeline/Services/DatabaseCorruptedException.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class DatabaseCorruptedException : Exception
- {
- public DatabaseCorruptedException() { }
- public DatabaseCorruptedException(string message) : base(message) { }
- public DatabaseCorruptedException(string message, Exception inner) : base(message, inner) { }
- protected DatabaseCorruptedException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/ETagGenerator.cs b/Timeline/Services/ETagGenerator.cs
deleted file mode 100644
index 4493e903..00000000
--- a/Timeline/Services/ETagGenerator.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Security.Cryptography;
-using System.Threading.Tasks;
-
-namespace Timeline.Services
-{
- public interface IETagGenerator
- {
- /// <summary>
- /// Generate a etag for given source.
- /// </summary>
- /// <param name="source">The source data.</param>
- /// <returns>The generated etag.</returns>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="source"/> is null.</exception>
- Task<string> Generate(byte[] source);
- }
-
- public sealed class ETagGenerator : IETagGenerator, IDisposable
- {
- private readonly SHA1 _sha1;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5350:Do Not Use Weak Cryptographic Algorithms", Justification = "Sha1 is enough ??? I don't know.")]
- public ETagGenerator()
- {
- _sha1 = SHA1.Create();
- }
-
- public Task<string> Generate(byte[] source)
- {
- if (source == null)
- throw new ArgumentNullException(nameof(source));
-
- return Task.Run(() => Convert.ToBase64String(_sha1.ComputeHash(source)));
- }
-
- private bool _disposed; // To detect redundant calls
-
- public void Dispose()
- {
- if (_disposed) return;
- _sha1.Dispose();
- _disposed = true;
- }
- }
-}
diff --git a/Timeline/Services/EntityNames.cs b/Timeline/Services/EntityNames.cs
deleted file mode 100644
index 0ce1de3b..00000000
--- a/Timeline/Services/EntityNames.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace Timeline.Services
-{
- public static class EntityNames
- {
- public const string User = "User";
- public const string Timeline = "Timeline";
- public const string TimelinePost = "TimelinePost";
- }
-}
diff --git a/Timeline/Services/Exceptions/EntityAlreadyExistError.cs b/Timeline/Services/Exceptions/EntityAlreadyExistError.cs
deleted file mode 100644
index 7db2e860..00000000
--- a/Timeline/Services/Exceptions/EntityAlreadyExistError.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// Thrown when an entity is already exists.
- /// </summary>
- /// <remarks>
- /// For example, want to create a timeline but a timeline with the same name already exists.
- /// </remarks>
- [Serializable]
- public class EntityAlreadyExistException : Exception
- {
- private readonly string? _entityName;
-
- public EntityAlreadyExistException() : this(null, null, null, null) { }
-
- public EntityAlreadyExistException(string? entityName) : this(entityName, null) { }
-
- public EntityAlreadyExistException(string? entityName, Exception? inner) : this(entityName, null, null, null, inner) { }
-
- public EntityAlreadyExistException(string? entityName, object? entity = null) : this(entityName, null, entity, null, null) { }
- public EntityAlreadyExistException(Type? entityType, object? entity = null) : this(null, entityType, entity, null, null) { }
- public EntityAlreadyExistException(string? entityName, Type? entityType, object? entity = null, string? message = null, Exception? inner = null) : base(MakeMessage(entityName, entityType, message), inner)
- {
- _entityName = entityName;
- EntityType = entityType;
- Entity = entity;
- }
-
- private static string MakeMessage(string? entityName, Type? entityType, string? message)
- {
- string? name = entityName ?? (entityType?.Name);
-
- var result = new StringBuilder();
-
- if (name == null)
- result.Append(Resources.Services.Exceptions.EntityAlreadyExistErrorDefault);
- else
- result.AppendFormat(CultureInfo.InvariantCulture, Resources.Services.Exceptions.EntityAlreadyExistError, name);
-
- if (message != null)
- {
- result.Append(' ');
- result.Append(message);
- }
-
- return result.ToString();
- }
-
- protected EntityAlreadyExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? EntityName => _entityName ?? (EntityType?.Name);
-
- public Type? EntityType { get; }
-
- public object? Entity { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/EntityNotExistError.cs b/Timeline/Services/Exceptions/EntityNotExistError.cs
deleted file mode 100644
index e79496d3..00000000
--- a/Timeline/Services/Exceptions/EntityNotExistError.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// Thrown when you want to get an entity that does not exist.
- /// </summary>
- /// <example>
- /// For example, you want to get a timeline with given name but it does not exist.
- /// </example>
- [Serializable]
- public class EntityNotExistException : Exception
- {
- public EntityNotExistException() : this(null, null, null, null) { }
- public EntityNotExistException(string? entityName) : this(entityName, null, null, null) { }
- public EntityNotExistException(Type? entityType) : this(null, entityType, null, null) { }
- public EntityNotExistException(string? entityName, Exception? inner) : this(entityName, null, null, inner) { }
- public EntityNotExistException(Type? entityType, Exception? inner) : this(null, entityType, null, inner) { }
- public EntityNotExistException(string? entityName, Type? entityType, string? message = null, Exception? inner = null) : base(MakeMessage(entityName, entityType, message), inner)
- {
- EntityName = entityName;
- EntityType = entityType;
- }
-
- private static string MakeMessage(string? entityName, Type? entityType, string? message)
- {
- string? name = entityName ?? (entityType?.Name);
-
- var result = new StringBuilder();
-
- if (name == null)
- result.Append(Resources.Services.Exceptions.EntityNotExistErrorDefault);
- else
- result.AppendFormat(CultureInfo.InvariantCulture, Resources.Services.Exceptions.EntityNotExistError, name);
-
- if (message != null)
- {
- result.Append(' ');
- result.Append(message);
- }
-
- return result.ToString();
- }
-
- protected EntityNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? EntityName { get; }
-
- public Type? EntityType { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/ExceptionMessageHelper.cs b/Timeline/Services/Exceptions/ExceptionMessageHelper.cs
deleted file mode 100644
index be3c42a4..00000000
--- a/Timeline/Services/Exceptions/ExceptionMessageHelper.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Timeline.Services.Exceptions
-{
- public static class ExceptionMessageHelper
- {
- public static string AppendAdditionalMessage(this string origin, string? message)
- {
- if (message == null)
- return origin;
- else
- return origin + " " + message;
- }
- }
-}
diff --git a/Timeline/Services/Exceptions/ImageException.cs b/Timeline/Services/Exceptions/ImageException.cs
deleted file mode 100644
index 20dd48ae..00000000
--- a/Timeline/Services/Exceptions/ImageException.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class ImageException : Exception
- {
- public enum ErrorReason
- {
- /// <summary>
- /// Decoding image failed.
- /// </summary>
- CantDecode,
- /// <summary>
- /// Decoding succeeded but the real type is not the specified type.
- /// </summary>
- UnmatchedFormat,
- /// <summary>
- /// Image is not of required size.
- /// </summary>
- NotSquare,
- /// <summary>
- /// Other unknown errer.
- /// </summary>
- Unknown
- }
-
- public ImageException() : this(null) { }
- public ImageException(string? message) : this(message, null) { }
- public ImageException(string? message, Exception? inner) : this(ErrorReason.Unknown, null, null, null, message, inner) { }
-
- public ImageException(ErrorReason error, byte[]? data, string? requestType = null, string? realType = null, string? message = null, Exception? inner = null) : base(MakeMessage(error).AppendAdditionalMessage(message), inner) { Error = error; ImageData = data; RequestType = requestType; RealType = realType; }
-
- protected ImageException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- private static string MakeMessage(ErrorReason? reason) =>
- string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.ImageException, reason switch
- {
- ErrorReason.CantDecode => Resources.Services.Exceptions.ImageExceptionCantDecode,
- ErrorReason.UnmatchedFormat => Resources.Services.Exceptions.ImageExceptionUnmatchedFormat,
- ErrorReason.NotSquare => Resources.Services.Exceptions.ImageExceptionBadSize,
- _ => Resources.Services.Exceptions.ImageExceptionUnknownError
- });
-
- public ErrorReason Error { get; }
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[]? ImageData { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
- public string? RequestType { get; }
-
- // This field will be null if decoding failed.
- public string? RealType { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelineNotExistException.cs b/Timeline/Services/Exceptions/TimelineNotExistException.cs
deleted file mode 100644
index 70970b24..00000000
--- a/Timeline/Services/Exceptions/TimelineNotExistException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelineNotExistException : EntityNotExistException
- {
- public TimelineNotExistException() : this(null, null) { }
- public TimelineNotExistException(string? timelineName) : this(timelineName, null) { }
- public TimelineNotExistException(string? timelineName, Exception? inner) : this(timelineName, null, inner) { }
- public TimelineNotExistException(string? timelineName, string? message, Exception? inner = null)
- : base(EntityNames.Timeline, null, string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.TimelineNotExistException, timelineName ?? "").AppendAdditionalMessage(message), inner) { TimelineName = timelineName; }
-
- protected TimelineNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? TimelineName { get; set; }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelinePostNoDataException.cs b/Timeline/Services/Exceptions/TimelinePostNoDataException.cs
deleted file mode 100644
index c4b6bf62..00000000
--- a/Timeline/Services/Exceptions/TimelinePostNoDataException.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelinePostNoDataException : Exception
- {
- public TimelinePostNoDataException() : this(null, null) { }
- public TimelinePostNoDataException(string? message) : this(message, null) { }
- public TimelinePostNoDataException(string? message, Exception? inner) : base(Resources.Services.Exceptions.TimelineNoDataException.AppendAdditionalMessage(message), inner) { }
- protected TimelinePostNoDataException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelinePostNotExistException.cs b/Timeline/Services/Exceptions/TimelinePostNotExistException.cs
deleted file mode 100644
index f95dd410..00000000
--- a/Timeline/Services/Exceptions/TimelinePostNotExistException.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelinePostNotExistException : EntityNotExistException
- {
- public TimelinePostNotExistException() : this(null, null, false, null, null) { }
- [Obsolete("This has no meaning.")]
- public TimelinePostNotExistException(string? message) : this(message, null) { }
- [Obsolete("This has no meaning.")]
- public TimelinePostNotExistException(string? message, Exception? inner) : this(null, null, false, message, inner) { }
- protected TimelinePostNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public TimelinePostNotExistException(string? timelineName, long? id, bool isDelete, string? message = null, Exception? inner = null) : base(EntityNames.TimelinePost, null, MakeMessage(timelineName, id, isDelete).AppendAdditionalMessage(message), inner) { TimelineName = timelineName; Id = id; IsDelete = isDelete; }
-
- private static string MakeMessage(string? timelineName, long? id, bool isDelete)
- {
- return string.Format(CultureInfo.InvariantCulture, isDelete ? Resources.Services.Exceptions.TimelinePostNotExistExceptionDeleted : Resources.Services.Exceptions.TimelinePostNotExistException, timelineName ?? "", id);
- }
-
- public string? TimelineName { get; set; }
- public long? Id { get; set; }
-
- /// <summary>
- /// True if the post is deleted. False if the post does not exist at all.
- /// </summary>
- public bool IsDelete { get; set; }
- }
-}
diff --git a/Timeline/Services/Exceptions/UserNotExistException.cs b/Timeline/Services/Exceptions/UserNotExistException.cs
deleted file mode 100644
index 7ef714df..00000000
--- a/Timeline/Services/Exceptions/UserNotExistException.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// The user requested does not exist.
- /// </summary>
- [Serializable]
- public class UserNotExistException : EntityNotExistException
- {
- public UserNotExistException() : this(null, null, null, null) { }
- public UserNotExistException(string? username, Exception? inner) : this(username, null, null, inner) { }
-
- public UserNotExistException(string? username) : this(username, null, null, null) { }
-
- public UserNotExistException(long id) : this(null, id, null, null) { }
-
- public UserNotExistException(string? username, long? id, string? message, Exception? inner) : base(EntityNames.User, null,
- string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.UserNotExistException, username ?? "", id).AppendAdditionalMessage(message), inner)
- {
- Username = username;
- Id = id;
- }
-
- protected UserNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The username of the user that does not exist.
- /// </summary>
- public string? Username { get; set; }
-
- /// <summary>
- /// The id of the user that does not exist.
- /// </summary>
- public long? Id { get; set; }
- }
-}
diff --git a/Timeline/Services/ImageValidator.cs b/Timeline/Services/ImageValidator.cs
deleted file mode 100644
index 59424a7c..00000000
--- a/Timeline/Services/ImageValidator.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public interface IImageValidator
- {
- /// <summary>
- /// Validate a image data.
- /// </summary>
- /// <param name="data">The data of the image. Can't be null.</param>
- /// <param name="requestType">If not null, the real image format will be check against the requested format and throw if not match. If null, then do not check.</param>
- /// <param name="square">If true, image must be square.</param>
- /// <returns>The format.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
- /// <exception cref="ImageException">Thrown when image data can't be decoded or real type does not match request type or image is not square when required.</exception>
- Task<IImageFormat> Validate(byte[] data, string? requestType = null, bool square = false);
- }
-
- public class ImageValidator : IImageValidator
- {
- public ImageValidator()
- {
- }
-
- public async Task<IImageFormat> Validate(byte[] data, string? requestType = null, bool square = false)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var format = await Task.Run(() =>
- {
- try
- {
- using var image = Image.Load(data, out IImageFormat format);
- if (requestType != null && !format.MimeTypes.Contains(requestType))
- throw new ImageException(ImageException.ErrorReason.UnmatchedFormat, data, requestType, format.DefaultMimeType);
- if (square && image.Width != image.Height)
- throw new ImageException(ImageException.ErrorReason.NotSquare, data, requestType, format.DefaultMimeType);
- return format;
- }
- catch (UnknownImageFormatException e)
- {
- throw new ImageException(ImageException.ErrorReason.CantDecode, data, requestType, null, null, e);
- }
- });
- return format;
- }
- }
-}
diff --git a/Timeline/Services/JwtUserTokenBadFormatException.cs b/Timeline/Services/JwtUserTokenBadFormatException.cs
deleted file mode 100644
index c528c3e3..00000000
--- a/Timeline/Services/JwtUserTokenBadFormatException.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Globalization;
-using static Timeline.Resources.Services.Exception;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class JwtUserTokenBadFormatException : UserTokenBadFormatException
- {
- public enum ErrorKind
- {
- NoIdClaim,
- IdClaimBadFormat,
- NoVersionClaim,
- VersionClaimBadFormat,
- Other
- }
-
- public JwtUserTokenBadFormatException() : this("", ErrorKind.Other) { }
- public JwtUserTokenBadFormatException(string message) : base(message) { }
- public JwtUserTokenBadFormatException(string message, Exception inner) : base(message, inner) { }
-
- public JwtUserTokenBadFormatException(string token, ErrorKind type) : base(token, GetErrorMessage(type)) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, Exception inner) : base(token, GetErrorMessage(type), inner) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, string message, Exception inner) : base(token, message, inner) { ErrorType = type; }
- protected JwtUserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public ErrorKind ErrorType { get; set; }
-
- private static string GetErrorMessage(ErrorKind type)
- {
- var reason = type switch
- {
- ErrorKind.NoIdClaim => JwtUserTokenBadFormatExceptionIdMissing,
- ErrorKind.IdClaimBadFormat => JwtUserTokenBadFormatExceptionIdBadFormat,
- ErrorKind.NoVersionClaim => JwtUserTokenBadFormatExceptionVersionMissing,
- ErrorKind.VersionClaimBadFormat => JwtUserTokenBadFormatExceptionVersionBadFormat,
- ErrorKind.Other => JwtUserTokenBadFormatExceptionOthers,
- _ => JwtUserTokenBadFormatExceptionUnknown
- };
-
- return string.Format(CultureInfo.CurrentCulture,
- Resources.Services.Exception.JwtUserTokenBadFormatException, reason);
- }
- }
-}
diff --git a/Timeline/Services/PasswordBadFormatException.cs b/Timeline/Services/PasswordBadFormatException.cs
deleted file mode 100644
index 2029ebb4..00000000
--- a/Timeline/Services/PasswordBadFormatException.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-
-namespace Timeline.Services
-{
-
- [Serializable]
- public class PasswordBadFormatException : Exception
- {
- public PasswordBadFormatException() : base(Resources.Services.Exception.PasswordBadFormatException) { }
- public PasswordBadFormatException(string message) : base(message) { }
- public PasswordBadFormatException(string message, Exception inner) : base(message, inner) { }
-
- public PasswordBadFormatException(string password, string validationMessage) : this()
- {
- Password = password;
- ValidationMessage = validationMessage;
- }
-
- protected PasswordBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string Password { get; set; } = "";
-
- public string ValidationMessage { get; set; } = "";
- }
-}
diff --git a/Timeline/Services/PasswordService.cs b/Timeline/Services/PasswordService.cs
deleted file mode 100644
index 8114a520..00000000
--- a/Timeline/Services/PasswordService.cs
+++ /dev/null
@@ -1,224 +0,0 @@
-using Microsoft.AspNetCore.Cryptography.KeyDerivation;
-using System;
-using System.Runtime.CompilerServices;
-using System.Security.Cryptography;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// Hashed password is of bad format.
- /// </summary>
- /// <seealso cref="IPasswordService.VerifyPassword(string, string)"/>
- [Serializable]
- public class HashedPasswordBadFromatException : Exception
- {
- private static string MakeMessage(string reason)
- {
- return Resources.Services.Exception.HashedPasswordBadFromatException + " Reason: " + reason;
- }
-
- public HashedPasswordBadFromatException() : base(Resources.Services.Exception.HashedPasswordBadFromatException) { }
-
- public HashedPasswordBadFromatException(string message) : base(message) { }
- public HashedPasswordBadFromatException(string message, Exception inner) : base(message, inner) { }
-
- public HashedPasswordBadFromatException(string hashedPassword, string reason) : base(MakeMessage(reason)) { HashedPassword = hashedPassword; }
- public HashedPasswordBadFromatException(string hashedPassword, string reason, Exception inner) : base(MakeMessage(reason), inner) { HashedPassword = hashedPassword; }
- protected HashedPasswordBadFromatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? HashedPassword { get; set; }
- }
-
- public interface IPasswordService
- {
- /// <summary>
- /// Hash a password.
- /// </summary>
- /// <param name="password">The password to hash.</param>
- /// <returns>A hashed representation of the supplied <paramref name="password"/>.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="password"/> is null.</exception>
- string HashPassword(string password);
-
- /// <summary>
- /// Verify whether the password fits into the hashed one.
- ///
- /// Usually you only need to check the returned bool value.
- /// Catching <see cref="HashedPasswordBadFromatException"/> usually is not necessary.
- /// Because if your program logic is right and always call <see cref="HashPassword(string)"/>
- /// and <see cref="VerifyPassword(string, string)"/> in pair, this exception will never be thrown.
- /// A thrown one usually means the data you saved is corupted, which is a critical problem.
- /// </summary>
- /// <param name="hashedPassword">The hashed password.</param>
- /// <param name="providedPassword">The password supplied for comparison.</param>
- /// <returns>True indicating password is right. Otherwise false.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="hashedPassword"/> or <paramref name="providedPassword"/> is null.</exception>
- /// <exception cref="HashedPasswordBadFromatException">Thrown when the hashed password is of bad format.</exception>
- bool VerifyPassword(string hashedPassword, string providedPassword);
- }
-
- /// <summary>
- /// Copied from https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Extensions.Core/src/PasswordHasher.cs
- /// Remove V2 format and unnecessary format version check.
- /// Remove configuration options.
- /// Remove user related parts.
- /// Change the exceptions.
- /// </summary>
- public class PasswordService : IPasswordService
- {
- /* =======================
- * HASHED PASSWORD FORMATS
- * =======================
- *
- * Version 3:
- * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
- * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
- * (All UInt32s are stored big-endian.)
- */
-
- private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
-
- public PasswordService()
- {
- }
-
- // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private static bool ByteArraysEqual(byte[] a, byte[] b)
- {
- if (a == null && b == null)
- {
- return true;
- }
- if (a == null || b == null || a.Length != b.Length)
- {
- return false;
- }
- var areSame = true;
- for (var i = 0; i < a.Length; i++)
- {
- areSame &= (a[i] == b[i]);
- }
- return areSame;
- }
-
- public string HashPassword(string password)
- {
- if (password == null)
- throw new ArgumentNullException(nameof(password));
- return Convert.ToBase64String(HashPasswordV3(password, _rng));
- }
-
- private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng)
- {
- return HashPasswordV3(password, rng,
- prf: KeyDerivationPrf.HMACSHA256,
- iterCount: 10000,
- saltSize: 128 / 8,
- numBytesRequested: 256 / 8);
- }
-
- private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
- {
- // Produce a version 3 (see comment above) text hash.
- byte[] salt = new byte[saltSize];
- rng.GetBytes(salt);
- byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
-
- var outputBytes = new byte[13 + salt.Length + subkey.Length];
- outputBytes[0] = 0x01; // format marker
- WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
- WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
- WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
- Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
- Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
- return outputBytes;
- }
-
- public bool VerifyPassword(string hashedPassword, string providedPassword)
- {
- if (hashedPassword == null)
- throw new ArgumentNullException(nameof(hashedPassword));
- if (providedPassword == null)
- throw new ArgumentNullException(nameof(providedPassword));
-
- byte[] decodedHashedPassword;
- try
- {
- decodedHashedPassword = Convert.FromBase64String(hashedPassword);
- }
- catch (FormatException e)
- {
- throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotBase64, e);
- }
-
- // read the format marker from the hashed password
- if (decodedHashedPassword.Length == 0)
- {
- throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotLength0);
- }
-
- return (decodedHashedPassword[0]) switch
- {
- 0x01 => VerifyHashedPasswordV3(decodedHashedPassword, providedPassword, hashedPassword),
- _ => throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotUnknownMarker),
- };
- }
-
- private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, string hashedPasswordString)
- {
- try
- {
- // Read header information
- KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1);
- int iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5);
- int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9);
-
- // Read the salt: must be >= 128 bits
- if (saltLength < 128 / 8)
- {
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotSaltTooShort);
- }
- byte[] salt = new byte[saltLength];
- Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length);
-
- // Read the subkey (the rest of the payload): must be >= 128 bits
- int subkeyLength = hashedPassword.Length - 13 - salt.Length;
- if (subkeyLength < 128 / 8)
- {
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotSubkeyTooShort);
- }
- byte[] expectedSubkey = new byte[subkeyLength];
- Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);
-
- // Hash the incoming password and verify it
- byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength);
- return ByteArraysEqual(actualSubkey, expectedSubkey);
- }
- catch (Exception e)
- {
- // This should never occur except in the case of a malformed payload, where
- // we might go off the end of the array. Regardless, a malformed payload
- // implies verification failed.
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotOthers, e);
- }
- }
-
- private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
- {
- return ((uint)(buffer[offset + 0]) << 24)
- | ((uint)(buffer[offset + 1]) << 16)
- | ((uint)(buffer[offset + 2]) << 8)
- | ((uint)(buffer[offset + 3]));
- }
-
- private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
- {
- buffer[offset + 0] = (byte)(value >> 24);
- buffer[offset + 1] = (byte)(value >> 16);
- buffer[offset + 2] = (byte)(value >> 8);
- buffer[offset + 3] = (byte)(value >> 0);
- }
- }
-}
diff --git a/Timeline/Services/PathProvider.cs b/Timeline/Services/PathProvider.cs
deleted file mode 100644
index 1baba5c0..00000000
--- a/Timeline/Services/PathProvider.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using System.IO;
-using Timeline.Configs;
-
-namespace Timeline.Services
-{
- public interface IPathProvider
- {
- public string GetWorkingDirectory();
- public string GetDatabaseFilePath();
- public string GetDatabaseBackupDirectory();
- }
-
- public class PathProvider : IPathProvider
- {
- private readonly IConfiguration _configuration;
-
- private readonly string _workingDirectory;
-
-
- public PathProvider(IConfiguration configuration)
- {
- _configuration = configuration;
- _workingDirectory = configuration.GetValue<string?>(ApplicationConfiguration.WorkDirKey) ?? ApplicationConfiguration.DefaultWorkDir;
- }
-
- public string GetWorkingDirectory()
- {
- return _workingDirectory;
- }
-
- public string GetDatabaseFilePath()
- {
- return Path.Combine(_workingDirectory, ApplicationConfiguration.DatabaseFileName);
- }
-
- public string GetDatabaseBackupDirectory()
- {
- return Path.Combine(_workingDirectory, ApplicationConfiguration.DatabaseBackupDirectoryName);
- }
- }
-}
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs
deleted file mode 100644
index 4bcae596..00000000
--- a/Timeline/Services/TimelineService.cs
+++ /dev/null
@@ -1,1166 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using SixLabors.ImageSharp;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Validation;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Services.TimelineService;
-
-namespace Timeline.Services
-{
- public static class TimelineHelper
- {
- public static string ExtractTimelineName(string name, out bool isPersonal)
- {
- if (name.StartsWith("@", StringComparison.OrdinalIgnoreCase))
- {
- isPersonal = true;
- return name.Substring(1);
- }
- else
- {
- isPersonal = false;
- return name;
- }
- }
- }
-
- public enum TimelineUserRelationshipType
- {
- Own = 0b1,
- Join = 0b10,
- Default = Own | Join
- }
-
- public class TimelineUserRelationship
- {
- public TimelineUserRelationship(TimelineUserRelationshipType type, long userId)
- {
- Type = type;
- UserId = userId;
- }
-
- public TimelineUserRelationshipType Type { get; set; }
- public long UserId { get; set; }
- }
-
- public class PostData : ICacheableData
- {
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
- public string Type { get; set; } = default!;
- public string ETag { get; set; } = default!;
- public DateTime? LastModified { get; set; } // TODO: Why nullable?
- }
-
- /// <summary>
- /// This define the interface of both personal timeline and ordinary timeline.
- /// </summary>
- public interface ITimelineService
- {
- /// <summary>
- /// Get the timeline last modified time (not include name change).
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<DateTime> GetTimelineLastModifiedTime(string timelineName);
-
- /// <summary>
- /// Get the timeline unique id.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<string> GetTimelineUniqueId(string timelineName);
-
- /// <summary>
- /// Get the timeline info.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<Models.Timeline> GetTimeline(string timelineName);
-
- /// <summary>
- /// Set the properties of a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="newProperties">The new properties. Null member means not to change.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="newProperties"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task ChangeProperty(string timelineName, TimelineChangePropertyRequest newProperties);
-
- /// <summary>
- /// Get all the posts in the timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="modifiedSince">The time that posts have been modified since.</param>
- /// <param name="includeDeleted">Whether include deleted posts.</param>
- /// <returns>A list of all posts.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<List<TimelinePost>> GetPosts(string timelineName, DateTime? modifiedSince = null, bool includeDeleted = false);
-
- /// <summary>
- /// Get the etag of data of a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <returns>The etag of the data.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostNoDataException">Thrown when post has no data.</exception>
- /// <seealso cref="GetPostData(string, long)"/>
- Task<string> GetPostDataETag(string timelineName, long postId);
-
- /// <summary>
- /// Get the data of a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <returns>The etag of the data.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostNoDataException">Thrown when post has no data.</exception>
- /// <seealso cref="GetPostDataETag(string, long)"/>
- Task<PostData> GetPostData(string timelineName, long postId);
-
- /// <summary>
- /// Create a new text post in timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to create post against.</param>
- /// <param name="authorId">The author's user id.</param>
- /// <param name="text">The content text.</param>
- /// <param name="time">The time of the post. If null, then current time is used.</param>
- /// <returns>The info of the created post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="text"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
- Task<TimelinePost> CreateTextPost(string timelineName, long authorId, string text, DateTime? time);
-
- /// <summary>
- /// Create a new image post in timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to create post against.</param>
- /// <param name="authorId">The author's user id.</param>
- /// <param name="imageData">The image data.</param>
- /// <param name="time">The time of the post. If null, then use current time.</param>
- /// <returns>The info of the created post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="imageData"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
- /// <exception cref="ImageException">Thrown if data is not a image. Validated by <see cref="ImageValidator"/>.</exception>
- Task<TimelinePost> CreateImagePost(string timelineName, long authorId, byte[] imageData, DateTime? time);
-
- /// <summary>
- /// Delete a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to delete post against.</param>
- /// <param name="postId">The id of the post to delete.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already.</exception>
- /// <remarks>
- /// First use <see cref="HasPostModifyPermission(string, long, long, bool)"/> to check the permission.
- /// </remarks>
- 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>
- /// <param name="membersToAdd">A list of usernames of members to add. May be null.</param>
- /// <param name="membersToRemove">A list of usernames of members to remove. May be null.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="ArgumentException">Thrown when names in <paramref name="membersToAdd"/> or <paramref name="membersToRemove"/> is not a valid username.</exception>
- /// <exception cref="UserNotExistException">Thrown when one of the user to change does not exist.</exception>
- /// <remarks>
- /// Operating on a username that is of bad format or does not exist always throws.
- /// Add a user that already is a member has no effects.
- /// Remove a user that is not a member also has not effects.
- /// Add and remove an identical user results in no effects.
- /// More than one same usernames are regarded as one.
- /// </remarks>
- Task ChangeMember(string timelineName, IList<string>? membersToAdd, IList<string>? membersToRemove);
-
- /// <summary>
- /// Check whether a user can manage(change timeline info, member, ...) a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="userId">The id of the user to check on.</param>
- /// <returns>True if the user can manage the timeline, otherwise false.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with user id does not exist.
- /// </remarks>
- Task<bool> HasManagePermission(string timelineName, long userId);
-
- /// <summary>
- /// Verify whether a visitor has the permission to read a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="visitorId">The id of the user to check on. Null means visitor without account.</param>
- /// <returns>True if can read, false if can't read.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with visitor id does not exist.
- /// </remarks>
- Task<bool> HasReadPermission(string timelineName, long? visitorId);
-
- /// <summary>
- /// Verify whether a user has the permission to modify a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="postId">The id of the post.</param>
- /// <param name="modifierId">The id of the user to check on.</param>
- /// <param name="throwOnPostNotExist">True if you want it to throw <see cref="TimelinePostNotExistException"/>. Default false.</param>
- /// <returns>True if can modify, false if can't modify.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already and <paramref name="throwOnPostNotExist"/> is true.</exception>
- /// <remarks>
- /// Unless <paramref name="throwOnPostNotExist"/> is true, this method should return true if the post does not exist.
- /// If the post is deleted, its author info still exists, so it is checked as the post is not deleted unless <paramref name="throwOnPostNotExist"/> is true.
- /// This method does not check whether the user is administrator.
- /// It only checks whether he is the author of the post or the owner of the timeline.
- /// Return false when user with modifier id does not exist.
- /// </remarks>
- Task<bool> HasPostModifyPermission(string timelineName, long postId, long modifierId, bool throwOnPostNotExist = false);
-
- /// <summary>
- /// Verify whether a user is member of a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="userId">The id of user to check on.</param>
- /// <returns>True if it is a member, false if not.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// Timeline owner is also considered as a member.
- /// Return false when user with user id does not exist.
- /// </remarks>
- Task<bool> IsMemberOf(string timelineName, long userId);
-
- /// <summary>
- /// Get all timelines including personal and ordinary timelines.
- /// </summary>
- /// <param name="relate">Filter timelines related (own or is a member) to specific user.</param>
- /// <param name="visibility">Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored.</param>
- /// <returns>The list of timelines.</returns>
- /// <remarks>
- /// If user with related user id does not exist, empty list will be returned.
- /// </remarks>
- Task<List<Models.Timeline>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null);
-
- /// <summary>
- /// Create a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="ownerId">The id of owner of the timeline.</param>
- /// <returns>The info of the new timeline.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when the timeline already exists.</exception>
- /// <exception cref="UserNotExistException">Thrown when the owner user does not exist.</exception>
- Task<Models.Timeline> CreateTimeline(string timelineName, long ownerId);
-
- /// <summary>
- /// Delete a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to delete.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when the timeline does not exist.</exception>
- Task DeleteTimeline(string timelineName);
-
- /// <summary>
- /// Change name of a timeline.
- /// </summary>
- /// <param name="oldTimelineName">The old timeline name.</param>
- /// <param name="newTimelineName">The new timeline name.</param>
- /// <returns>The new timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="oldTimelineName"/> or <paramref name="newTimelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="oldTimelineName"/> or <paramref name="newTimelineName"/> is of invalid format.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when a timeline with new name already exists.</exception>
- /// <remarks>
- /// You can only change name of general timeline.
- /// </remarks>
- Task<Models.Timeline> ChangeTimelineName(string oldTimelineName, string newTimelineName);
- }
-
- public class TimelineService : ITimelineService
- {
- public TimelineService(ILogger<TimelineService> logger, DatabaseContext database, IDataManager dataManager, IUserService userService, IImageValidator imageValidator, IClock clock)
- {
- _logger = logger;
- _database = database;
- _dataManager = dataManager;
- _userService = userService;
- _imageValidator = imageValidator;
- _clock = clock;
- }
-
- private readonly ILogger<TimelineService> _logger;
-
- private readonly DatabaseContext _database;
-
- private readonly IDataManager _dataManager;
-
- private readonly IUserService _userService;
-
- private readonly IImageValidator _imageValidator;
-
- private readonly IClock _clock;
-
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
-
- private readonly TimelineNameValidator _timelineNameValidator = new TimelineNameValidator();
-
- private void ValidateTimelineName(string name, string paramName)
- {
- if (!_timelineNameValidator.Validate(name, out var message))
- {
- throw new ArgumentException(ExceptionTimelineNameBadFormat.AppendAdditionalMessage(message), paramName);
- }
- }
-
- /// Remember to include Members when query.
- private async Task<Models.Timeline> MapTimelineFromEntity(TimelineEntity entity)
- {
- var owner = await _userService.GetUserById(entity.OwnerId);
-
- var members = new List<User>();
- foreach (var memberEntity in entity.Members)
- {
- members.Add(await _userService.GetUserById(memberEntity.UserId));
- }
-
- var name = entity.Name ?? ("@" + owner.Username);
-
- return new Models.Timeline
- {
- UniqueID = entity.UniqueId,
- Name = name,
- NameLastModified = entity.NameLastModified,
- Title = string.IsNullOrEmpty(entity.Title) ? name : entity.Title,
- Description = entity.Description ?? "",
- Owner = owner,
- Visibility = entity.Visibility,
- Members = members,
- CreateTime = entity.CreateTime,
- LastModified = entity.LastModified
- };
- }
-
- private async Task<TimelinePost> MapTimelinePostFromEntity(TimelinePostEntity entity, string timelineName)
- {
- User? author = entity.AuthorId.HasValue ? await _userService.GetUserById(entity.AuthorId.Value) : null;
-
- ITimelinePostContent? content = null;
-
- if (entity.Content != null)
- {
- var type = entity.ContentType;
-
- content = type switch
- {
- TimelinePostContentTypes.Text => new TextTimelinePostContent(entity.Content),
- TimelinePostContentTypes.Image => new ImageTimelinePostContent(entity.Content),
- _ => throw new DatabaseCorruptedException(string.Format(CultureInfo.InvariantCulture, ExceptionDatabaseUnknownContentType, type))
- };
- }
-
- return new TimelinePost(
- id: entity.LocalId,
- author: author,
- content: content,
- time: entity.Time,
- lastUpdated: entity.LastUpdated,
- timelineName: timelineName
- );
- }
-
- private TimelineEntity CreateNewTimelineEntity(string? name, long ownerId)
- {
- var currentTime = _clock.GetCurrentTime();
-
- return new TimelineEntity
- {
- Name = name,
- NameLastModified = currentTime,
- OwnerId = ownerId,
- Visibility = TimelineVisibility.Register,
- CreateTime = currentTime,
- LastModified = currentTime,
- CurrentPostLocalId = 0,
- Members = new List<TimelineMemberEntity>()
- };
- }
-
-
-
- // Get timeline id by name. If it is a personal timeline and it does not exist, it will be created.
- //
- // This method will check the name format and if it is invalid, ArgumentException is thrown.
- //
- // For personal timeline, if the user does not exist, TimelineNotExistException will be thrown with UserNotExistException as inner exception.
- // For ordinary timeline, if the timeline does not exist, TimelineNotExistException will be thrown.
- //
- // It follows all timeline-related function common interface contracts.
- private async Task<long> FindTimelineId(string timelineName)
- {
- timelineName = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
-
- if (isPersonal)
- {
- long userId;
- try
- {
- userId = await _userService.GetUserIdByUsername(timelineName);
- }
- catch (ArgumentException e)
- {
- throw new ArgumentException(ExceptionFindTimelineUsernameBadFormat, nameof(timelineName), e);
- }
- catch (UserNotExistException e)
- {
- throw new TimelineNotExistException(timelineName, e);
- }
-
- var timelineEntity = await _database.Timelines.Where(t => t.OwnerId == userId && t.Name == null).Select(t => new { t.Id }).SingleOrDefaultAsync();
-
- if (timelineEntity != null)
- {
- return timelineEntity.Id;
- }
- else
- {
- var newTimelineEntity = CreateNewTimelineEntity(null, userId);
- _database.Timelines.Add(newTimelineEntity);
- await _database.SaveChangesAsync();
-
- return newTimelineEntity.Id;
- }
- }
- else
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- ValidateTimelineName(timelineName, nameof(timelineName));
-
- var timelineEntity = await _database.Timelines.Where(t => t.Name == timelineName).Select(t => new { t.Id }).SingleOrDefaultAsync();
-
- if (timelineEntity == null)
- {
- throw new TimelineNotExistException(timelineName);
- }
- else
- {
- return timelineEntity.Id;
- }
- }
- }
-
- public async Task<DateTime> GetTimelineLastModifiedTime(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.LastModified }).SingleAsync();
-
- return timelineEntity.LastModified;
- }
-
- public async Task<string> GetTimelineUniqueId(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.UniqueId }).SingleAsync();
-
- return timelineEntity.UniqueId;
- }
-
- public async Task<Models.Timeline> GetTimeline(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Include(t => t.Members).SingleAsync();
-
- return await MapTimelineFromEntity(timelineEntity);
- }
-
- 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);
- IQueryable<TimelinePostEntity> query = _database.TimelinePosts.Where(p => p.TimelineId == timelineId);
-
- if (!includeDeleted)
- {
- query = query.Where(p => p.Content != null);
- }
-
- if (modifiedSince.HasValue)
- {
- 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>();
- foreach (var entity in postEntities)
- {
- posts.Add(await MapTimelinePostFromEntity(entity, timelineName));
- }
- return posts;
- }
-
- public async Task<string> GetPostDataETag(string timelineName, long postId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
-
- if (postEntity == null)
- throw new TimelinePostNotExistException(timelineName, postId, false);
-
- if (postEntity.Content == null)
- throw new TimelinePostNotExistException(timelineName, postId, true);
-
- if (postEntity.ContentType != TimelinePostContentTypes.Image)
- throw new TimelinePostNoDataException(ExceptionGetDataNonImagePost);
-
- var tag = postEntity.Content;
-
- return tag;
- }
-
- public async Task<PostData> GetPostData(string timelineName, long postId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
-
- if (postEntity == null)
- throw new TimelinePostNotExistException(timelineName, postId, false);
-
- if (postEntity.Content == null)
- throw new TimelinePostNotExistException(timelineName, postId, true);
-
- if (postEntity.ContentType != TimelinePostContentTypes.Image)
- throw new TimelinePostNoDataException(ExceptionGetDataNonImagePost);
-
- var tag = postEntity.Content;
-
- byte[] data;
-
- try
- {
- data = await _dataManager.GetEntry(tag);
- }
- catch (InvalidOperationException e)
- {
- throw new DatabaseCorruptedException(ExceptionGetDataDataEntryNotExist, e);
- }
-
- if (postEntity.ExtraContent == null)
- {
- _logger.LogWarning(LogGetDataNoFormat);
- var format = Image.DetectFormat(data);
- postEntity.ExtraContent = format.DefaultMimeType;
- await _database.SaveChangesAsync();
- }
-
- return new PostData
- {
- Data = data,
- Type = postEntity.ExtraContent,
- ETag = tag,
- LastModified = postEntity.LastUpdated
- };
- }
-
- 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)
- throw new ArgumentNullException(nameof(text));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var author = await _userService.GetUserById(authorId);
-
- var currentTime = _clock.GetCurrentTime();
- var finalTime = time ?? currentTime;
-
- timelineEntity.CurrentPostLocalId += 1;
-
- var postEntity = new TimelinePostEntity
- {
- LocalId = timelineEntity.CurrentPostLocalId,
- ContentType = TimelinePostContentTypes.Text,
- Content = text,
- AuthorId = authorId,
- TimelineId = timelineId,
- Time = finalTime,
- LastUpdated = currentTime
- };
- _database.TimelinePosts.Add(postEntity);
- await _database.SaveChangesAsync();
-
-
- return new TimelinePost(
- id: postEntity.LocalId,
- content: new TextTimelinePostContent(text),
- time: finalTime,
- author: author,
- lastUpdated: currentTime,
- timelineName: timelineName
- );
- }
-
- 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)
- throw new ArgumentNullException(nameof(data));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var author = await _userService.GetUserById(authorId);
-
- var imageFormat = await _imageValidator.Validate(data);
-
- var imageFormatText = imageFormat.DefaultMimeType;
-
- var tag = await _dataManager.RetainEntry(data);
-
- var currentTime = _clock.GetCurrentTime();
- var finalTime = time ?? currentTime;
-
- timelineEntity.CurrentPostLocalId += 1;
-
- var postEntity = new TimelinePostEntity
- {
- LocalId = timelineEntity.CurrentPostLocalId,
- ContentType = TimelinePostContentTypes.Image,
- Content = tag,
- ExtraContent = imageFormatText,
- AuthorId = authorId,
- TimelineId = timelineId,
- Time = finalTime,
- LastUpdated = currentTime
- };
- _database.TimelinePosts.Add(postEntity);
- await _database.SaveChangesAsync();
-
- return new TimelinePost(
- id: postEntity.LocalId,
- content: new ImageTimelinePostContent(tag),
- time: finalTime,
- author: author,
- lastUpdated: currentTime,
- timelineName: timelineName
- );
- }
-
- public async Task DeletePost(string timelineName, long id)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var post = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == id).SingleOrDefaultAsync();
-
- if (post == null)
- throw new TimelinePostNotExistException(timelineName, id, false);
-
- if (post.Content == null)
- throw new TimelinePostNotExistException(timelineName, id, true);
-
- string? dataTag = null;
-
- if (post.ContentType == TimelinePostContentTypes.Image)
- {
- dataTag = post.Content;
- }
-
- post.Content = null;
- post.LastUpdated = _clock.GetCurrentTime();
-
- await _database.SaveChangesAsync();
-
- if (dataTag != null)
- {
- await _dataManager.FreeEntry(dataTag);
- }
- }
-
- 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)
- throw new ArgumentNullException(nameof(timelineName));
- if (newProperties == null)
- throw new ArgumentNullException(nameof(newProperties));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var changed = false;
-
- if (newProperties.Title != null)
- {
- changed = true;
- timelineEntity.Title = newProperties.Title;
- }
-
- if (newProperties.Description != null)
- {
- changed = true;
- timelineEntity.Description = newProperties.Description;
- }
-
- if (newProperties.Visibility.HasValue)
- {
- changed = true;
- timelineEntity.Visibility = newProperties.Visibility.Value;
- }
-
- if (changed)
- {
- var currentTime = _clock.GetCurrentTime();
- timelineEntity.LastModified = currentTime;
- }
-
- await _database.SaveChangesAsync();
- }
-
- public async Task ChangeMember(string timelineName, IList<string>? add, IList<string>? remove)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- List<string>? RemoveDuplicateAndCheckFormat(IList<string>? list, string paramName)
- {
- if (list != null)
- {
- List<string> result = new List<string>();
- var count = list.Count;
- for (var index = 0; index < count; index++)
- {
- var username = list[index];
- if (result.Contains(username))
- {
- continue;
- }
- var (validationResult, message) = _usernameValidator.Validate(username);
- if (!validationResult)
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionChangeMemberUsernameBadFormat, index), nameof(paramName));
- result.Add(username);
- }
- return result;
- }
- else
- {
- return null;
- }
- }
- var simplifiedAdd = RemoveDuplicateAndCheckFormat(add, nameof(add));
- var simplifiedRemove = RemoveDuplicateAndCheckFormat(remove, nameof(remove));
-
- // remove those both in add and remove
- if (simplifiedAdd != null && simplifiedRemove != null)
- {
- var usersToClean = simplifiedRemove.Where(u => simplifiedAdd.Contains(u)).ToList();
- foreach (var u in usersToClean)
- {
- simplifiedAdd.Remove(u);
- simplifiedRemove.Remove(u);
- }
-
- if (simplifiedAdd.Count == 0)
- simplifiedAdd = null;
-
- if (simplifiedRemove.Count == 0)
- simplifiedRemove = null;
- }
-
- if (simplifiedAdd == null && simplifiedRemove == null)
- return;
-
- var timelineId = await FindTimelineId(timelineName);
-
- async Task<List<long>?> CheckExistenceAndGetId(List<string>? list)
- {
- if (list == null)
- return null;
-
- List<long> result = new List<long>();
- foreach (var username in list)
- {
- result.Add(await _userService.GetUserIdByUsername(username));
- }
- return result;
- }
- var userIdsAdd = await CheckExistenceAndGetId(simplifiedAdd);
- var userIdsRemove = await CheckExistenceAndGetId(simplifiedRemove);
-
- if (userIdsAdd != null)
- {
- var membersToAdd = userIdsAdd.Select(id => new TimelineMemberEntity { UserId = id, TimelineId = timelineId }).ToList();
- _database.TimelineMembers.AddRange(membersToAdd);
- }
-
- if (userIdsRemove != null)
- {
- var membersToRemove = await _database.TimelineMembers.Where(m => m.TimelineId == timelineId && userIdsRemove.Contains(m.UserId)).ToListAsync();
- _database.TimelineMembers.RemoveRange(membersToRemove);
- }
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
- timelineEntity.LastModified = _clock.GetCurrentTime();
-
- await _database.SaveChangesAsync();
- }
-
- public async Task<bool> HasManagePermission(string timelineName, long userId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- return userId == timelineEntity.OwnerId;
- }
-
- public async Task<bool> HasReadPermission(string timelineName, long? visitorId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.Visibility }).SingleAsync();
-
- if (timelineEntity.Visibility == TimelineVisibility.Public)
- return true;
-
- if (timelineEntity.Visibility == TimelineVisibility.Register && visitorId != null)
- return true;
-
- if (visitorId == null)
- {
- return false;
- }
- else
- {
- var memberEntity = await _database.TimelineMembers.Where(m => m.UserId == visitorId && m.TimelineId == timelineId).SingleOrDefaultAsync();
- return memberEntity != null;
- }
- }
-
- public async Task<bool> HasPostModifyPermission(string timelineName, long postId, long modifierId, bool throwOnPostNotExist = false)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- var postEntity = await _database.TimelinePosts.Where(p => p.Id == postId).Select(p => new { p.Content, p.AuthorId }).SingleOrDefaultAsync();
-
- if (postEntity == null)
- {
- if (throwOnPostNotExist)
- throw new TimelinePostNotExistException(timelineName, postId, false);
- else
- return true;
- }
-
- if (postEntity.Content == null && throwOnPostNotExist)
- {
- throw new TimelinePostNotExistException(timelineName, postId, true);
- }
-
- return timelineEntity.OwnerId == modifierId || postEntity.AuthorId == modifierId;
- }
-
- public async Task<bool> IsMemberOf(string timelineName, long userId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- if (userId == timelineEntity.OwnerId)
- return true;
-
- return await _database.TimelineMembers.AnyAsync(m => m.TimelineId == timelineId && m.UserId == userId);
- }
-
- public async Task<List<Models.Timeline>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null)
- {
- List<TimelineEntity> entities;
-
- IQueryable<TimelineEntity> ApplyTimelineVisibilityFilter(IQueryable<TimelineEntity> query)
- {
- if (visibility != null && visibility.Count != 0)
- {
- return query.Where(t => visibility.Contains(t.Visibility));
- }
- return query;
- }
-
- bool allVisibilities = visibility == null || visibility.Count == 0;
-
- if (relate == null)
- {
- entities = await ApplyTimelineVisibilityFilter(_database.Timelines).Include(t => t.Members).ToListAsync();
- }
- else
- {
- entities = new List<TimelineEntity>();
-
- if ((relate.Type & TimelineUserRelationshipType.Own) != 0)
- {
- entities.AddRange(await ApplyTimelineVisibilityFilter(_database.Timelines.Where(t => t.OwnerId == relate.UserId)).Include(t => t.Members).ToListAsync());
- }
-
- if ((relate.Type & TimelineUserRelationshipType.Join) != 0)
- {
- entities.AddRange(await ApplyTimelineVisibilityFilter(_database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).ThenInclude(t => t.Members).Select(m => m.Timeline)).ToListAsync());
- }
- }
-
- var result = new List<Models.Timeline>();
-
- foreach (var entity in entities)
- {
- result.Add(await MapTimelineFromEntity(entity));
- }
-
- return result;
- }
-
- public async Task<Models.Timeline> CreateTimeline(string name, long owner)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- ValidateTimelineName(name, nameof(name));
-
- var user = await _userService.GetUserById(owner);
-
- var conflict = await _database.Timelines.AnyAsync(t => t.Name == name);
-
- if (conflict)
- throw new EntityAlreadyExistException(EntityNames.Timeline, null, ExceptionTimelineNameConflict);
-
- var newEntity = CreateNewTimelineEntity(name, user.Id!.Value);
-
- _database.Timelines.Add(newEntity);
- await _database.SaveChangesAsync();
-
- return await MapTimelineFromEntity(newEntity);
- }
-
- public async Task DeleteTimeline(string name)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- ValidateTimelineName(name, nameof(name));
-
- var entity = await _database.Timelines.Where(t => t.Name == name).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new TimelineNotExistException(name);
-
- _database.Timelines.Remove(entity);
- await _database.SaveChangesAsync();
- }
-
- public async Task<Models.Timeline> ChangeTimelineName(string oldTimelineName, string newTimelineName)
- {
- if (oldTimelineName == null)
- throw new ArgumentNullException(nameof(oldTimelineName));
- if (newTimelineName == null)
- throw new ArgumentNullException(nameof(newTimelineName));
-
- ValidateTimelineName(oldTimelineName, nameof(oldTimelineName));
- ValidateTimelineName(newTimelineName, nameof(newTimelineName));
-
- var entity = await _database.Timelines.Include(t => t.Members).Where(t => t.Name == oldTimelineName).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new TimelineNotExistException(oldTimelineName);
-
- if (oldTimelineName == newTimelineName)
- return await MapTimelineFromEntity(entity);
-
- var conflict = await _database.Timelines.AnyAsync(t => t.Name == newTimelineName);
-
- if (conflict)
- throw new EntityAlreadyExistException(EntityNames.Timeline, null, ExceptionTimelineNameConflict);
-
- var now = _clock.GetCurrentTime();
-
- entity.Name = newTimelineName;
- entity.NameLastModified = now;
- entity.LastModified = now;
-
- await _database.SaveChangesAsync();
-
- return await MapTimelineFromEntity(entity);
- }
- }
-}
diff --git a/Timeline/Services/UserAvatarService.cs b/Timeline/Services/UserAvatarService.cs
deleted file mode 100644
index b41c45fd..00000000
--- a/Timeline/Services/UserAvatarService.cs
+++ /dev/null
@@ -1,265 +0,0 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public class Avatar
- {
- public string Type { get; set; } = default!;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "DTO Object")]
- public byte[] Data { get; set; } = default!;
- }
-
- public class AvatarInfo
- {
- public Avatar Avatar { get; set; } = default!;
- public DateTime LastModified { get; set; }
-
- public CacheableData ToCacheableData()
- {
- return new CacheableData(Avatar.Type, Avatar.Data, LastModified);
- }
- }
-
- /// <summary>
- /// Provider for default user avatar.
- /// </summary>
- /// <remarks>
- /// Mainly for unit tests.
- /// </remarks>
- public interface IDefaultUserAvatarProvider
- {
- /// <summary>
- /// Get the etag of default avatar.
- /// </summary>
- /// <returns></returns>
- Task<string> GetDefaultAvatarETag();
-
- /// <summary>
- /// Get the default avatar.
- /// </summary>
- Task<AvatarInfo> GetDefaultAvatar();
- }
-
- public interface IUserAvatarService
- {
- /// <summary>
- /// Get the etag of a user's avatar. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to get avatar etag of.</param>
- /// <returns>The etag.</returns>
- Task<string> GetAvatarETag(long id);
-
- /// <summary>
- /// Get avatar of a user. If the user has no avatar set, a default one is returned. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to get avatar of.</param>
- /// <returns>The avatar info.</returns>
- Task<AvatarInfo> GetAvatar(long id);
-
- /// <summary>
- /// Set avatar for a user. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to set avatar for.</param>
- /// <param name="avatar">The avatar. Can be null to delete the saved avatar.</param>
- /// <returns>The etag of the avatar.</returns>
- /// <exception cref="ArgumentException">Thrown if any field in <paramref name="avatar"/> is null when <paramref name="avatar"/> is not null.</exception>
- /// <exception cref="ImageException">Thrown if avatar is of bad format.</exception>
- Task<string> SetAvatar(long id, Avatar? avatar);
- }
-
- // TODO! : Make this configurable.
- public class DefaultUserAvatarProvider : IDefaultUserAvatarProvider
- {
- private readonly IETagGenerator _eTagGenerator;
-
- private readonly string _avatarPath;
-
- private byte[] _cacheData = default!;
- private DateTime _cacheLastModified;
- private string _cacheETag = default!;
-
- public DefaultUserAvatarProvider(IWebHostEnvironment environment, IETagGenerator eTagGenerator)
- {
- _avatarPath = Path.Combine(environment.ContentRootPath, "default-avatar.png");
- _eTagGenerator = eTagGenerator;
- }
-
- private async Task CheckAndInit()
- {
- var path = _avatarPath;
- if (_cacheData == null || File.GetLastWriteTime(path) > _cacheLastModified)
- {
- _cacheData = await File.ReadAllBytesAsync(path);
- _cacheLastModified = File.GetLastWriteTime(path);
- _cacheETag = await _eTagGenerator.Generate(_cacheData);
- }
- }
-
- public async Task<string> GetDefaultAvatarETag()
- {
- await CheckAndInit();
- return _cacheETag;
- }
-
- public async Task<AvatarInfo> GetDefaultAvatar()
- {
- await CheckAndInit();
- return new AvatarInfo
- {
- Avatar = new Avatar
- {
- Type = "image/png",
- Data = _cacheData
- },
- LastModified = _cacheLastModified
- };
- }
- }
-
- public class UserAvatarService : IUserAvatarService
- {
-
- private readonly ILogger<UserAvatarService> _logger;
-
- private readonly DatabaseContext _database;
-
- private readonly IDefaultUserAvatarProvider _defaultUserAvatarProvider;
-
- private readonly IImageValidator _imageValidator;
-
- private readonly IDataManager _dataManager;
-
- private readonly IClock _clock;
-
- public UserAvatarService(
- ILogger<UserAvatarService> logger,
- DatabaseContext database,
- IDefaultUserAvatarProvider defaultUserAvatarProvider,
- IImageValidator imageValidator,
- IDataManager dataManager,
- IClock clock)
- {
- _logger = logger;
- _database = database;
- _defaultUserAvatarProvider = defaultUserAvatarProvider;
- _imageValidator = imageValidator;
- _dataManager = dataManager;
- _clock = clock;
- }
-
- public async Task<string> GetAvatarETag(long id)
- {
- var eTag = (await _database.UserAvatars.Where(a => a.UserId == id).Select(a => new { a.DataTag }).SingleOrDefaultAsync())?.DataTag;
- if (eTag == null)
- return await _defaultUserAvatarProvider.GetDefaultAvatarETag();
- else
- return eTag;
- }
-
- public async Task<AvatarInfo> GetAvatar(long id)
- {
- var avatarEntity = await _database.UserAvatars.Where(a => a.UserId == id).Select(a => new { a.Type, a.DataTag, a.LastModified }).SingleOrDefaultAsync();
-
- if (avatarEntity != null)
- {
- if (!LanguageHelper.AreSame(avatarEntity.DataTag == null, avatarEntity.Type == null))
- {
- var message = Resources.Services.UserAvatarService.ExceptionDatabaseCorruptedDataAndTypeNotSame;
- _logger.LogCritical(message);
- throw new DatabaseCorruptedException(message);
- }
-
-
- if (avatarEntity.DataTag != null)
- {
- var data = await _dataManager.GetEntry(avatarEntity.DataTag);
- return new AvatarInfo
- {
- Avatar = new Avatar
- {
- Type = avatarEntity.Type!,
- Data = data
- },
- LastModified = avatarEntity.LastModified
- };
- }
- }
- var defaultAvatar = await _defaultUserAvatarProvider.GetDefaultAvatar();
- if (avatarEntity != null)
- defaultAvatar.LastModified = defaultAvatar.LastModified > avatarEntity.LastModified ? defaultAvatar.LastModified : avatarEntity.LastModified;
- return defaultAvatar;
- }
-
- public async Task<string> SetAvatar(long id, Avatar? avatar)
- {
- if (avatar != null)
- {
- if (avatar.Data == null)
- throw new ArgumentException(Resources.Services.UserAvatarService.ExceptionAvatarDataNull, nameof(avatar));
- if (string.IsNullOrEmpty(avatar.Type))
- throw new ArgumentException(Resources.Services.UserAvatarService.ExceptionAvatarTypeNullOrEmpty, nameof(avatar));
- }
-
- var avatarEntity = await _database.UserAvatars.Where(a => a.UserId == id).SingleOrDefaultAsync();
-
- if (avatar == null)
- {
- if (avatarEntity != null && avatarEntity.DataTag != null)
- {
- await _dataManager.FreeEntry(avatarEntity.DataTag);
- avatarEntity.DataTag = null;
- avatarEntity.Type = null;
- avatarEntity.LastModified = _clock.GetCurrentTime();
- await _database.SaveChangesAsync();
- _logger.LogInformation(Resources.Services.UserAvatarService.LogUpdateEntity);
- }
- return await _defaultUserAvatarProvider.GetDefaultAvatarETag();
- }
- else
- {
- await _imageValidator.Validate(avatar.Data, avatar.Type, true);
- var tag = await _dataManager.RetainEntry(avatar.Data);
- var oldTag = avatarEntity?.DataTag;
- var create = avatarEntity == null;
- if (avatarEntity == null)
- {
- avatarEntity = new UserAvatarEntity();
- _database.UserAvatars.Add(avatarEntity);
- }
- avatarEntity.DataTag = tag;
- avatarEntity.Type = avatar.Type;
- avatarEntity.LastModified = _clock.GetCurrentTime();
- avatarEntity.UserId = id;
- await _database.SaveChangesAsync();
- _logger.LogInformation(create ?
- Resources.Services.UserAvatarService.LogCreateEntity
- : Resources.Services.UserAvatarService.LogUpdateEntity);
- if (oldTag != null)
- {
- await _dataManager.FreeEntry(oldTag);
- }
-
- return avatarEntity.DataTag;
- }
- }
- }
-
- public static class UserAvatarServiceCollectionExtensions
- {
- public static void AddUserAvatarService(this IServiceCollection services)
- {
- services.AddScoped<IUserAvatarService, UserAvatarService>();
- services.AddScoped<IDefaultUserAvatarProvider, DefaultUserAvatarProvider>();
- }
- }
-}
diff --git a/Timeline/Services/UserDeleteService.cs b/Timeline/Services/UserDeleteService.cs
deleted file mode 100644
index 845de573..00000000
--- a/Timeline/Services/UserDeleteService.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-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/UserRoleConvert.cs b/Timeline/Services/UserRoleConvert.cs
deleted file mode 100644
index f27ee1bb..00000000
--- a/Timeline/Services/UserRoleConvert.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- public static class UserRoleConvert
- {
- public const string UserRole = UserRoles.User;
- public const string AdminRole = UserRoles.Admin;
-
- public static string[] ToArray(bool administrator)
- {
- return administrator ? new string[] { UserRole, AdminRole } : new string[] { UserRole };
- }
-
- public static string[] ToArray(string s)
- {
- return s.Split(',').ToArray();
- }
-
- public static bool ToBool(IReadOnlyCollection<string> roles)
- {
- return roles.Contains(AdminRole);
- }
-
- public static string ToString(IReadOnlyCollection<string> roles)
- {
- return string.Join(',', roles);
- }
-
- public static string ToString(bool administrator)
- {
- return administrator ? UserRole + "," + AdminRole : UserRole;
- }
-
- public static bool ToBool(string s)
- {
- return s.Contains("admin", StringComparison.InvariantCulture);
- }
- }
-}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs
deleted file mode 100644
index 821bc33d..00000000
--- a/Timeline/Services/UserService.cs
+++ /dev/null
@@ -1,437 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Validation;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Services.UserService;
-
-namespace Timeline.Services
-{
- public interface IUserService
- {
- /// <summary>
- /// Try to verify the given username and password.
- /// </summary>
- /// <param name="username">The username of the user to verify.</param>
- /// <param name="password">The password of the user to verify.</param>
- /// <returns>The user info and auth info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or <paramref name="password"/> is empty.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown when password is wrong.</exception>
- Task<User> VerifyCredential(string username, string password);
-
- /// <summary>
- /// Try to get a user by id.
- /// </summary>
- /// <param name="id">The id of the user.</param>
- /// <returns>The user info.</returns>
- /// <exception cref="UserNotExistException">Thrown when the user with given id does not exist.</exception>
- Task<User> GetUserById(long id);
-
- /// <summary>
- /// Get the user info of given username.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>The info of the user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- Task<User> GetUserByUsername(string username);
-
- /// <summary>
- /// Get the user id of given username.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>The id of the user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- Task<long> GetUserIdByUsername(string username);
-
- /// <summary>
- /// List all users.
- /// </summary>
- /// <returns>The user info of users.</returns>
- Task<User[]> GetUsers();
-
- /// <summary>
- /// Create a user with given info.
- /// </summary>
- /// <param name="info">The info of new user.</param>
- /// <returns>The the new user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="info"/>is null.</exception>
- /// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when a user with given username already exists.</exception>
- /// <remarks>
- /// <see cref="User.Username"/> must not be null and must be a valid username.
- /// <see cref="User.Password"/> must not be null or empty.
- /// <see cref="User.Administrator"/> is false by default (null).
- /// <see cref="User.Nickname"/> must be a valid nickname if set. It is empty by default.
- /// Other fields are ignored.
- /// </remarks>
- Task<User> CreateUser(User info);
-
- /// <summary>
- /// Modify a user's info.
- /// </summary>
- /// <param name="id">The id of the user.</param>
- /// <param name="info">The new info. May be null.</param>
- /// <returns>The new user info.</returns>
- /// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
- /// <remarks>
- /// Only <see cref="User.Username"/>, <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
- /// If null, then not change.
- /// Other fields are ignored.
- /// Version will increase if password is changed.
- ///
- /// <see cref="User.Username"/> must be a valid username if set.
- /// <see cref="User.Password"/> can't be empty if set.
- /// <see cref="User.Nickname"/> must be a valid nickname if set.
- ///
- /// </remarks>
- /// <seealso cref="ModifyUser(string, User)"/>
- Task<User> ModifyUser(long id, User? info);
-
- /// <summary>
- /// Modify a user's info.
- /// </summary>
- /// <param name="username">The username of the user.</param>
- /// <param name="info">The new info. May be null.</param>
- /// <returns>The new user info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when user with the newusername already exist.</exception>
- /// <remarks>
- /// Only <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
- /// If null, then not change.
- /// Other fields are ignored.
- /// After modified, even if nothing is changed, version will increase.
- ///
- /// <see cref="User.Username"/> must be a valid username if set.
- /// <see cref="User.Password"/> can't be empty if set.
- /// <see cref="User.Nickname"/> must be a valid nickname if set.
- ///
- /// Note: Whether <see cref="User.Version"/> is set or not, version will increase and not set to the specified value if there is one.
- /// </remarks>
- /// <seealso cref="ModifyUser(long, User)"/>
- Task<User> ModifyUser(string username, User? info);
-
- /// <summary>
- /// Try to change a user's password with old password.
- /// </summary>
- /// <param name="id">The id of user to change password of.</param>
- /// <param name="oldPassword">Old password.</param>
- /// <param name="newPassword">New password.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is empty.</exception>
- /// <exception cref="UserNotExistException">Thrown if the user with given username does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown if the old password is wrong.</exception>
- Task ChangePassword(long id, string oldPassword, string newPassword);
- }
-
- public class UserService : IUserService
- {
- private readonly ILogger<UserService> _logger;
- private readonly IClock _clock;
-
- private readonly DatabaseContext _databaseContext;
-
- private readonly IPasswordService _passwordService;
-
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
- private readonly NicknameValidator _nicknameValidator = new NicknameValidator();
- public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IPasswordService passwordService, IClock clock)
- {
- _logger = logger;
- _clock = clock;
- _databaseContext = databaseContext;
- _passwordService = passwordService;
- }
-
- private void CheckUsernameFormat(string username, string? paramName)
- {
- if (!_usernameValidator.Validate(username, out var message))
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName);
- }
- }
-
- private static void CheckPasswordFormat(string password, string? paramName)
- {
- if (password.Length == 0)
- {
- throw new ArgumentException(ExceptionPasswordEmpty, paramName);
- }
- }
-
- private void CheckNicknameFormat(string nickname, string? paramName)
- {
- if (!_nicknameValidator.Validate(nickname, out var message))
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionNicknameBadFormat, message), paramName);
- }
- }
-
- private static void ThrowUsernameConflict()
- {
- throw new EntityAlreadyExistException(EntityNames.User, ExceptionUsernameConflict);
- }
-
- private static User CreateUserFromEntity(UserEntity entity)
- {
- return new User
- {
- UniqueId = entity.UniqueId,
- Username = entity.Username,
- Administrator = UserRoleConvert.ToBool(entity.Roles),
- Nickname = string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname,
- Id = entity.Id,
- Version = entity.Version,
- CreateTime = entity.CreateTime,
- UsernameChangeTime = entity.UsernameChangeTime,
- LastModified = entity.LastModified
- };
- }
-
- public async Task<User> VerifyCredential(string username, string password)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- if (password == null)
- throw new ArgumentNullException(nameof(password));
-
- CheckUsernameFormat(username, nameof(username));
- CheckPasswordFormat(password, nameof(password));
-
- var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- if (!_passwordService.VerifyPassword(entity.Password, password))
- throw new BadPasswordException(password);
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<User> GetUserById(long id)
- {
- var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
-
- if (user == null)
- throw new UserNotExistException(id);
-
- return CreateUserFromEntity(user);
- }
-
- public async Task<User> GetUserByUsername(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- CheckUsernameFormat(username, nameof(username));
-
- var entity = await _databaseContext.Users.Where(user => user.Username == username).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<long> GetUserIdByUsername(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- CheckUsernameFormat(username, nameof(username));
-
- var entity = await _databaseContext.Users.Where(user => user.Username == username).Select(u => new { u.Id }).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- return entity.Id;
- }
-
- public async Task<User[]> GetUsers()
- {
- var entities = await _databaseContext.Users.ToArrayAsync();
- return entities.Select(user => CreateUserFromEntity(user)).ToArray();
- }
-
- public async Task<User> CreateUser(User info)
- {
- if (info == null)
- throw new ArgumentNullException(nameof(info));
-
- if (info.Username == null)
- throw new ArgumentException(ExceptionUsernameNull, nameof(info));
- CheckUsernameFormat(info.Username, nameof(info));
-
- if (info.Password == null)
- throw new ArgumentException(ExceptionPasswordNull, nameof(info));
- CheckPasswordFormat(info.Password, nameof(info));
-
- if (info.Nickname != null)
- CheckNicknameFormat(info.Nickname, nameof(info));
-
- var username = info.Username;
-
- var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
- if (conflict)
- ThrowUsernameConflict();
-
- var administrator = info.Administrator ?? false;
- var password = info.Password;
- var nickname = info.Nickname;
-
- var newEntity = new UserEntity
- {
- Username = username,
- Password = _passwordService.HashPassword(password),
- Roles = UserRoleConvert.ToString(administrator),
- Nickname = nickname,
- Version = 1
- };
- _databaseContext.Users.Add(newEntity);
- await _databaseContext.SaveChangesAsync();
-
- _logger.LogInformation(Log.Format(LogDatabaseCreate,
- ("Id", newEntity.Id), ("Username", username), ("Administrator", administrator)));
-
- return CreateUserFromEntity(newEntity);
- }
-
- private void ValidateModifyUserInfo(User? info)
- {
- if (info != null)
- {
- if (info.Username != null)
- CheckUsernameFormat(info.Username, nameof(info));
-
- if (info.Password != null)
- CheckPasswordFormat(info.Password, nameof(info));
-
- if (info.Nickname != null)
- CheckNicknameFormat(info.Nickname, nameof(info));
- }
- }
-
- private async Task UpdateUserEntity(UserEntity entity, User? info)
- {
- if (info != null)
- {
- var now = _clock.GetCurrentTime();
- bool updateLastModified = false;
-
- var username = info.Username;
- if (username != null && username != entity.Username)
- {
- var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
- if (conflict)
- ThrowUsernameConflict();
-
- entity.Username = username;
- entity.UsernameChangeTime = now;
- updateLastModified = true;
- }
-
- var password = info.Password;
- if (password != null)
- {
- entity.Password = _passwordService.HashPassword(password);
- entity.Version += 1;
- }
-
- var administrator = info.Administrator;
- if (administrator.HasValue && UserRoleConvert.ToBool(entity.Roles) != administrator)
- {
- entity.Roles = UserRoleConvert.ToString(administrator.Value);
- updateLastModified = true;
- }
-
- var nickname = info.Nickname;
- if (nickname != null && nickname != entity.Nickname)
- {
- entity.Nickname = nickname;
- updateLastModified = true;
- }
-
- if (updateLastModified)
- {
- entity.LastModified = now;
- }
- }
- }
-
-
- public async Task<User> ModifyUser(long id, User? info)
- {
- ValidateModifyUserInfo(info);
-
- var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
- if (entity == null)
- throw new UserNotExistException(id);
-
- await UpdateUserEntity(entity, info);
-
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(LogDatabaseUpdate, ("Id", id));
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<User> ModifyUser(string username, User? info)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- CheckUsernameFormat(username, nameof(username));
-
- ValidateModifyUserInfo(info);
-
- var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
- if (entity == null)
- throw new UserNotExistException(username);
-
- await UpdateUserEntity(entity, info);
-
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(LogDatabaseUpdate, ("Username", username));
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task ChangePassword(long id, string oldPassword, string newPassword)
- {
- if (oldPassword == null)
- throw new ArgumentNullException(nameof(oldPassword));
- if (newPassword == null)
- throw new ArgumentNullException(nameof(newPassword));
- CheckPasswordFormat(oldPassword, nameof(oldPassword));
- CheckPasswordFormat(newPassword, nameof(newPassword));
-
- var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(id);
-
- if (!_passwordService.VerifyPassword(entity.Password, oldPassword))
- throw new BadPasswordException(oldPassword);
-
- entity.Password = _passwordService.HashPassword(newPassword);
- entity.Version += 1;
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(LogDatabaseUpdate, ("Id", id), ("Operation", "Change password")));
- }
- }
-}
diff --git a/Timeline/Services/UserTokenException.cs b/Timeline/Services/UserTokenException.cs
deleted file mode 100644
index d25fabb3..00000000
--- a/Timeline/Services/UserTokenException.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-
-namespace Timeline.Services
-{
-
- [Serializable]
- public class UserTokenException : Exception
- {
- public UserTokenException() { }
- public UserTokenException(string message) : base(message) { }
- public UserTokenException(string message, Exception inner) : base(message, inner) { }
- public UserTokenException(string token, string message) : base(message) { Token = token; }
- public UserTokenException(string token, string message, Exception inner) : base(message, inner) { Token = token; }
- protected UserTokenException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string Token { get; private set; } = "";
- }
-
-
- [Serializable]
- public class UserTokenTimeExpireException : UserTokenException
- {
- public UserTokenTimeExpireException() : base(Resources.Services.Exception.UserTokenTimeExpireException) { }
- public UserTokenTimeExpireException(string message) : base(message) { }
- public UserTokenTimeExpireException(string message, Exception inner) : base(message, inner) { }
- public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resources.Services.Exception.UserTokenTimeExpireException) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resources.Services.Exception.UserTokenTimeExpireException, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- protected UserTokenTimeExpireException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public DateTime ExpireTime { get; private set; }
-
- public DateTime VerifyTime { get; private set; }
- }
-
- [Serializable]
- public class UserTokenBadVersionException : UserTokenException
- {
- public UserTokenBadVersionException() : base(Resources.Services.Exception.UserTokenBadVersionException) { }
- public UserTokenBadVersionException(string message) : base(message) { }
- public UserTokenBadVersionException(string message, Exception inner) : base(message, inner) { }
- public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion) : base(token, Resources.Services.Exception.UserTokenBadVersionException) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadVersionException, inner) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- protected UserTokenBadVersionException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public long TokenVersion { get; set; }
-
- public long RequiredVersion { get; set; }
- }
-
- [Serializable]
- public class UserTokenBadFormatException : UserTokenException
- {
- public UserTokenBadFormatException() : base(Resources.Services.Exception.UserTokenBadFormatException) { }
- public UserTokenBadFormatException(string token) : base(token, Resources.Services.Exception.UserTokenBadFormatException) { }
- public UserTokenBadFormatException(string token, string message) : base(token, message) { }
- public UserTokenBadFormatException(string token, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadFormatException, inner) { }
- public UserTokenBadFormatException(string token, string message, Exception inner) : base(token, message, inner) { }
- protected UserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/UserTokenManager.cs b/Timeline/Services/UserTokenManager.cs
deleted file mode 100644
index 813dae67..00000000
--- a/Timeline/Services/UserTokenManager.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using Microsoft.Extensions.Logging;
-using System;
-using System.Threading.Tasks;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public class UserTokenCreateResult
- {
- public string Token { get; set; } = default!;
- public User User { get; set; } = default!;
- }
-
- public interface IUserTokenManager
- {
- /// <summary>
- /// Try to create a token for given username and password.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <param name="password">The password.</param>
- /// <param name="expireAt">The expire time of the token.</param>
- /// <returns>The created token and the user info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with <paramref name="username"/> does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown when <paramref name="password"/> is wrong.</exception>
- public Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null);
-
- /// <summary>
- /// Verify a token and get the saved user info. This also check the database for existence of the user.
- /// </summary>
- /// <param name="token">The token.</param>
- /// <returns>The user stored in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenTimeExpireException">Thrown when the token is expired.</exception>
- /// <exception cref="UserTokenBadVersionException">Thrown when the token is of bad version.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued.</exception>
- public Task<User> VerifyToken(string token);
- }
-
- public class UserTokenManager : IUserTokenManager
- {
- private readonly ILogger<UserTokenManager> _logger;
- private readonly IUserService _userService;
- private readonly IUserTokenService _userTokenService;
- private readonly IClock _clock;
-
- public UserTokenManager(ILogger<UserTokenManager> logger, IUserService userService, IUserTokenService userTokenService, IClock clock)
- {
- _logger = logger;
- _userService = userService;
- _userTokenService = userTokenService;
- _clock = clock;
- }
-
- 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)
- throw new ArgumentNullException(nameof(password));
-
- var user = await _userService.VerifyCredential(username, password);
- var token = _userTokenService.GenerateToken(new UserTokenInfo { Id = user.Id!.Value, Version = user.Version!.Value, ExpireAt = expireAt });
-
- return new UserTokenCreateResult { Token = token, User = user };
- }
-
-
- public async Task<User> VerifyToken(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- var tokenInfo = _userTokenService.VerifyToken(token);
-
- if (tokenInfo.ExpireAt.HasValue)
- {
- var currentTime = _clock.GetCurrentTime();
- if (tokenInfo.ExpireAt < currentTime)
- throw new UserTokenTimeExpireException(token, tokenInfo.ExpireAt.Value, currentTime);
- }
-
- var user = await _userService.GetUserById(tokenInfo.Id);
-
- if (tokenInfo.Version < user.Version)
- throw new UserTokenBadVersionException(token, tokenInfo.Version, user.Version.Value);
-
- return user;
- }
- }
-}
diff --git a/Timeline/Services/UserTokenService.cs b/Timeline/Services/UserTokenService.cs
deleted file mode 100644
index 86f3a0f7..00000000
--- a/Timeline/Services/UserTokenService.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using Microsoft.Extensions.Options;
-using Microsoft.IdentityModel.Tokens;
-using System;
-using System.Globalization;
-using System.IdentityModel.Tokens.Jwt;
-using System.Linq;
-using System.Security.Claims;
-using Timeline.Configs;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- public class UserTokenInfo
- {
- public long Id { get; set; }
- public long Version { get; set; }
- public DateTime? ExpireAt { get; set; }
- }
-
- public interface IUserTokenService
- {
- /// <summary>
- /// Create a token for a given token info.
- /// </summary>
- /// <param name="tokenInfo">The info to generate token.</param>
- /// <returns>Return the generated token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tokenInfo"/> is null.</exception>
- string GenerateToken(UserTokenInfo tokenInfo);
-
- /// <summary>
- /// Verify a token and get the saved info.
- /// </summary>
- /// <param name="token">The token to verify.</param>
- /// <returns>The saved info in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <remarks>
- /// If this method throw <see cref="UserTokenBadFormatException"/>, it usually means the token is not created by this service.
- /// </remarks>
- UserTokenInfo VerifyToken(string token);
- }
-
- public class JwtUserTokenService : IUserTokenService
- {
- private const string VersionClaimType = "timeline_version";
-
- private readonly IOptionsMonitor<JwtConfiguration> _jwtConfig;
- private readonly IClock _clock;
-
- private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler();
- private SymmetricSecurityKey _tokenSecurityKey;
-
- public JwtUserTokenService(IOptionsMonitor<JwtConfiguration> jwtConfig, IClock clock, DatabaseContext database)
- {
- _jwtConfig = jwtConfig;
- _clock = clock;
-
- var key = database.JwtToken.Select(t => t.Key).SingleOrDefault();
-
- if (key == null)
- {
- throw new InvalidOperationException(Resources.Services.UserTokenService.JwtKeyNotExist);
- }
-
- _tokenSecurityKey = new SymmetricSecurityKey(key);
- }
-
- public string GenerateToken(UserTokenInfo tokenInfo)
- {
- if (tokenInfo == null)
- throw new ArgumentNullException(nameof(tokenInfo));
-
- var config = _jwtConfig.CurrentValue;
-
- var identity = new ClaimsIdentity();
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
- identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
-
- var tokenDescriptor = new SecurityTokenDescriptor()
- {
- Subject = identity,
- Issuer = config.Issuer,
- Audience = config.Audience,
- SigningCredentials = new SigningCredentials(_tokenSecurityKey, SecurityAlgorithms.HmacSha384),
- IssuedAt = _clock.GetCurrentTime(),
- Expires = tokenInfo.ExpireAt.GetValueOrDefault(_clock.GetCurrentTime().AddSeconds(config.DefaultExpireOffset)),
- NotBefore = _clock.GetCurrentTime() // I must explicitly set this or it will use the current time by default and mock is not work in which case test will not pass.
- };
-
- var token = _tokenHandler.CreateToken(tokenDescriptor);
- var tokenString = _tokenHandler.WriteToken(token);
-
- return tokenString;
- }
-
-
- public UserTokenInfo VerifyToken(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- var config = _jwtConfig.CurrentValue;
- try
- {
- var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateIssuerSigningKey = true,
- ValidateLifetime = false,
- ValidIssuer = config.Issuer,
- ValidAudience = config.Audience,
- IssuerSigningKey = _tokenSecurityKey
- }, out var t);
-
- var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier);
- if (idClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoIdClaim);
- if (!long.TryParse(idClaim, out var id))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.IdClaimBadFormat);
-
- var versionClaim = principal.FindFirstValue(VersionClaimType);
- if (versionClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoVersionClaim);
- if (!long.TryParse(versionClaim, out var version))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.VersionClaimBadFormat);
-
- var decodedToken = (JwtSecurityToken)t;
- var exp = decodedToken.Payload.Exp;
- DateTime? expireAt = null;
- if (exp.HasValue)
- {
- expireAt = EpochTime.DateTime(exp.Value);
- }
-
- return new UserTokenInfo
- {
- Id = id,
- Version = version,
- ExpireAt = expireAt
- };
- }
- catch (Exception e) when (e is SecurityTokenException || e is ArgumentException)
- {
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.Other, e);
- }
- }
- }
-}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
deleted file mode 100644
index 82c231cb..00000000
--- a/Timeline/Startup.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
-using NSwag;
-using NSwag.Generation.Processors.Security;
-using System;
-using System.ComponentModel;
-using System.Net.Mime;
-using System.Text.Json.Serialization;
-using Timeline.Auth;
-using Timeline.Configs;
-using Timeline.Entities;
-using Timeline.Formatters;
-using Timeline.Helpers;
-using Timeline.Models.Converters;
-using Timeline.Routes;
-using Timeline.Services;
-using Timeline.Swagger;
-
-namespace Timeline
-{
- public class Startup
- {
- private readonly bool disableFrontEnd;
- private readonly bool useMockFrontEnd;
-
- public Startup(IConfiguration configuration, IWebHostEnvironment environment)
- {
- Environment = environment;
- Configuration = configuration;
-
- disableFrontEnd = Configuration.GetValue<bool?>(ApplicationConfiguration.DisableFrontEndKey) ?? false;
- useMockFrontEnd = Configuration.GetValue<bool?>(ApplicationConfiguration.UseMockFrontEndKey) ?? false;
- }
-
- public IWebHostEnvironment Environment { get; }
- public IConfiguration Configuration { get; }
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- TypeDescriptor.AddAttributes(typeof(DateTime), new TypeConverterAttribute(typeof(MyDateTimeConverter)));
-
- services.AddControllers(setup =>
- {
- setup.InputFormatters.Add(new StringInputFormatter());
- setup.InputFormatters.Add(new BytesInputFormatter());
- setup.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json, "text/json"));
- setup.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json, "text/json"));
- setup.UseApiRoutePrefix("api");
- })
- .AddJsonOptions(options =>
- {
- options.JsonSerializerOptions.IgnoreNullValues = true;
- options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
- options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
- })
- .ConfigureApiBehaviorOptions(options =>
- {
- options.InvalidModelStateResponseFactory = InvalidModelResponseFactory.Factory;
- });
-
- services.Configure<JwtConfiguration>(Configuration.GetSection("Jwt"));
- services.AddAuthentication(AuthenticationConstants.Scheme)
- .AddScheme<MyAuthenticationOptions, MyAuthenticationHandler>(AuthenticationConstants.Scheme, AuthenticationConstants.DisplayName, o => { });
- services.AddAuthorization();
-
- services.AddSingleton<IPathProvider, PathProvider>();
-
- services.AddSingleton<IDatabaseBackupService, DatabaseBackupService>();
-
- services.AddAutoMapper(GetType().Assembly);
-
- services.AddTransient<IClock, Clock>();
-
- services.AddTransient<IPasswordService, PasswordService>();
- services.AddScoped<IUserService, UserService>();
- services.AddScoped<IUserDeleteService, UserDeleteService>();
- services.AddScoped<IUserTokenService, JwtUserTokenService>();
- services.AddScoped<IUserTokenManager, UserTokenManager>();
-
- services.AddScoped<IETagGenerator, ETagGenerator>();
- services.AddScoped<IDataManager, DataManager>();
-
- services.AddScoped<IImageValidator, ImageValidator>();
-
- services.AddUserAvatarService();
-
- services.AddScoped<ITimelineService, TimelineService>();
-
- services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
-
- services.AddDbContext<DatabaseContext>((services, options) =>
- {
- var pathProvider = services.GetRequiredService<IPathProvider>();
- options.UseSqlite($"Data Source={pathProvider.GetDatabaseFilePath()}");
- });
-
- services.AddSwaggerDocument(document =>
- {
- document.DocumentName = "Timeline";
- document.Title = "Timeline REST API Reference";
- document.Version = typeof(Startup).Assembly.GetName().Version?.ToString() ?? "unknown version";
- document.DocumentProcessors.Add(new DocumentDescriptionDocumentProcessor());
- document.DocumentProcessors.Add(
- new SecurityDefinitionAppender("JWT",
- new OpenApiSecurityScheme
- {
- Type = OpenApiSecuritySchemeType.ApiKey,
- Name = "Authorization",
- In = OpenApiSecurityApiKeyLocation.Header,
- Description = "Create token via `/api/token/create` ."
- }));
- document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
- document.OperationProcessors.Add(new DefaultDescriptionOperationProcessor());
- document.OperationProcessors.Add(new ByteDataRequestOperationProcessor());
- });
-
- if (!disableFrontEnd)
- {
- if (useMockFrontEnd)
- {
- services.AddSpaStaticFiles(config =>
- {
- config.RootPath = "MockClientApp";
- });
-
- }
- else if (!Environment.IsDevelopment()) // In development, we don't want to serve dist. Or it will take precedence than front end dev server.
- {
- services.AddSpaStaticFiles(config =>
- {
- config.RootPath = "ClientApp/dist";
- });
- }
- }
- }
-
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app)
- {
- app.UseRouting();
-
- if (!disableFrontEnd && (useMockFrontEnd || !Environment.IsDevelopment()))
- {
- app.UseSpaStaticFiles(new StaticFileOptions
- {
- ServeUnknownFileTypes = true
- });
- }
-
- app.UseOpenApi();
- app.UseReDoc();
-
- app.UseAuthentication();
- app.UseAuthorization();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
-
- UnknownEndpointMiddleware.Attach(app);
-
- if (!disableFrontEnd)
- {
- app.UseSpa(spa =>
- {
- spa.Options.SourcePath = useMockFrontEnd ? "MockClientApp" : "ClientApp";
-
- if (!useMockFrontEnd && (Configuration.GetValue<bool?>(ApplicationConfiguration.UseProxyFrontEndKey) ?? false))
- {
- spa.UseProxyToSpaDevelopmentServer(new UriBuilder("http", "localhost", 3000).Uri);
- }
- });
- }
- }
- }
-}
diff --git a/Timeline/Swagger/ApiConvention.cs b/Timeline/Swagger/ApiConvention.cs
deleted file mode 100644
index dbf0b2fe..00000000
--- a/Timeline/Swagger/ApiConvention.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-[assembly: ApiConventionType(typeof(Timeline.Controllers.ApiConvention))]
-
-namespace Timeline.Controllers
-{
- // There is some bug if nullable is enable. So disable it.
-#nullable disable
- /// <summary>
- /// My api convention.
- /// </summary>
- public static class ApiConvention
- {
- }
-}
diff --git a/Timeline/Swagger/ByteDataRequestOperationProcessor.cs b/Timeline/Swagger/ByteDataRequestOperationProcessor.cs
deleted file mode 100644
index 887831ac..00000000
--- a/Timeline/Swagger/ByteDataRequestOperationProcessor.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using NJsonSchema;
-using NSwag;
-using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System.Linq;
-using Timeline.Models;
-
-namespace Timeline.Swagger
-{
- /// <summary>
- /// Coerce ByteData body type into the right one.
- /// </summary>
- public class ByteDataRequestOperationProcessor : IOperationProcessor
- {
- /// <inheritdoc/>
- public bool Process(OperationProcessorContext context)
- {
- var hasByteDataBody = context.MethodInfo.GetParameters().Where(p => p.ParameterType == typeof(ByteData)).Any();
- if (hasByteDataBody)
- {
- var bodyParameter = context.OperationDescription.Operation.Parameters.Where(p => p.Kind == OpenApiParameterKind.Body).Single();
- bodyParameter.Schema = JsonSchema.FromType<byte[]>();
- }
- return true;
- }
- }
-}
diff --git a/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs b/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs
deleted file mode 100644
index 4967cc6a..00000000
--- a/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System.Collections.Generic;
-
-namespace Timeline.Swagger
-{
- /// <summary>
- /// Swagger operation processor that adds default description to response.
- /// </summary>
- public class DefaultDescriptionOperationProcessor : IOperationProcessor
- {
- private readonly Dictionary<string, string> defaultDescriptionMap = new Dictionary<string, string>
- {
- ["200"] = "Succeeded to perform the operation.",
- ["304"] = "Item does not change.",
- ["400"] = "See code and message for error info.",
- ["401"] = "You need to log in to perform this operation.",
- ["403"] = "You have no permission to perform the operation.",
- ["404"] = "Item does not exist. See code and message for error info."
- };
-
- /// <inheritdoc/>
- public bool Process(OperationProcessorContext context)
- {
- var responses = context.OperationDescription.Operation.Responses;
-
- foreach (var (httpStatusCode, res) in responses)
- {
- if (!string.IsNullOrEmpty(res.Description)) continue;
- if (defaultDescriptionMap.ContainsKey(httpStatusCode))
- {
- res.Description = defaultDescriptionMap[httpStatusCode];
- }
- }
-
- return true;
- }
- }
-}
diff --git a/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs b/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs
deleted file mode 100644
index dc5ddd96..00000000
--- a/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using Timeline.Models.Http;
-
-namespace Timeline.Swagger
-{
- public class DocumentDescriptionDocumentProcessor : IDocumentProcessor
- {
- private static Dictionary<string, int> GetAllErrorCodes()
- {
- var errorCodes = new Dictionary<string, int>();
-
- void RecursiveCheckErrorCode(Type type)
- {
- foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
- .Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.FieldType == typeof(int)))
- {
- var name = (type.FullName + "." + field.Name).Remove(0, typeof(ErrorCodes).FullName!.Length + 1).Replace("+", ".", StringComparison.OrdinalIgnoreCase);
- int value = (int)field.GetRawConstantValue()!;
- errorCodes.Add(name, value);
- }
-
- foreach (var nestedType in type.GetNestedTypes())
- {
- RecursiveCheckErrorCode(nestedType);
- }
- }
-
- RecursiveCheckErrorCode(typeof(ErrorCodes));
-
- return errorCodes;
- }
-
- public void Process(DocumentProcessorContext context)
- {
- StringBuilder description = new StringBuilder();
- description.AppendLine("# Error Codes");
- description.AppendLine("name | value");
- description.AppendLine("---- | -----");
- foreach (var (name, value) in GetAllErrorCodes())
- {
- description.AppendLine($"`{name}` | `{value}`");
- }
-
- context.Document.Info.Description = description.ToString();
- }
- }
-}
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj
deleted file mode 100644
index 39412a65..00000000
--- a/Timeline/Timeline.csproj
+++ /dev/null
@@ -1,289 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
- <PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- <UserSecretsId>1f6fb74d-4277-4bc0-aeea-b1fc5ffb0b43</UserSecretsId>
- <Authors>crupest</Authors>
-
- <IsPackable>false</IsPackable>
-
- <LangVersion>8.0</LangVersion>
- <Nullable>enable</Nullable>
-
- <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
- <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
- <SpaRoot>ClientApp\</SpaRoot>
-
- <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
-
- <Version>0.3.0</Version>
- <GenerateDocumentationFile>true</GenerateDocumentationFile>
- <IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
-
- <NoWarn>1701;1702;1591</NoWarn>
- </PropertyGroup>
-
- <ItemGroup>
- <Content Include="default-avatar.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="MockClientApp\index.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="AutoMapper" Version="10.1.1" />
- <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
- <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.9" />
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.9">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="NSwag.AspNetCore" Version="13.8.2" />
- <PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
- <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\Timeline.ErrorCodes\Timeline.ErrorCodes.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <!-- Don't publish the SPA source files, but do show them in the project files list -->
- <Content Remove="$(SpaRoot)**" />
- <None Remove="$(SpaRoot)**" />
- <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
- </ItemGroup>
-
- <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
- <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn" StdOutEncoding="UTF-8" IgnoreStandardErrorWarningFormat="true" />
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn build" StdOutEncoding="UTF-8" IgnoreStandardErrorWarningFormat="true" />
-
- <!-- Include the newly-built files in the publish output -->
- <ItemGroup>
- <DistFiles Include="$(SpaRoot)dist\**" />
- <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
- <RelativePath>%(DistFiles.Identity)</RelativePath>
- <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
- <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
- </ResolvedFileToPublish>
- </ItemGroup>
- </Target>
-
- <ItemGroup>
- <None Remove="MockClientApp\index.html" />
- </ItemGroup>
-
- <ItemGroup>
- <Compile Update="Resources\Authentication\AuthHandler.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>AuthHandler.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\ControllerAuthExtensions.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>ControllerAuthExtensions.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\TimelineController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TimelineController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\TokenController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TokenController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\UserAvatarController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserAvatarController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\UserController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Entities.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Entities.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Filters.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Filters.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Helper\DataCacheHelper.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>DataCacheHelper.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Messages.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Messages.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Http\Common.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Common.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Http\Exception.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exception.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\NicknameValidator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>NicknameValidator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\NameValidator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>NameValidator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\Validator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Validator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\DataManager.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>DataManager.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\Exception.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exception.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\Exceptions.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exceptions.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\TimelineService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TimelineService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserAvatarService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserAvatarService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserTokenService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserTokenService.resx</DependentUpon>
- </Compile>
- </ItemGroup>
-
- <ItemGroup>
- <EmbeddedResource Update="Resources\Authentication\AuthHandler.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>AuthHandler.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\ControllerAuthExtensions.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>ControllerAuthExtensions.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\TimelineController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TimelineController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\TokenController.resx">
- <SubType>Designer</SubType>
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TokenController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\UserAvatarController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserAvatarController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\UserController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Entities.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Entities.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Filters.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Filters.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Helper\DataCacheHelper.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>DataCacheHelper.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Messages.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Messages.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Http\Common.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Common.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Http\Exception.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exception.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\NicknameValidator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>NicknameValidator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\NameValidator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>NameValidator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\Validator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Validator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\DataManager.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>DataManager.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\Exception.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exception.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\Exceptions.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exceptions.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\TimelineService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TimelineService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserAvatarService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserAvatarService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserTokenService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserTokenService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- </ItemGroup>
-</Project>
diff --git a/Timeline/appsettings.Development.json b/Timeline/appsettings.Development.json
deleted file mode 100644
index a2880cbf..00000000
--- a/Timeline/appsettings.Development.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- }
-}
diff --git a/Timeline/appsettings.json b/Timeline/appsettings.json
deleted file mode 100644
index 804ca43a..00000000
--- a/Timeline/appsettings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Warning"
- }
- },
- "Jwt": {
- "Issuer": "api.crupest.xyz",
- "Audience": "api.crupest.xyz"
- }
-}
diff --git a/Timeline/default-avatar.png b/Timeline/default-avatar.png
deleted file mode 100644
index 4086e1d2..00000000
--- a/Timeline/default-avatar.png
+++ /dev/null
Binary files differ
diff --git a/Timeline/packages.lock.json b/Timeline/packages.lock.json
deleted file mode 100644
index ed92c672..00000000
--- a/Timeline/packages.lock.json
+++ /dev/null
@@ -1,1563 +0,0 @@
-{
- "version": 1,
- "dependencies": {
- ".NETCoreApp,Version=v3.1": {
- "AutoMapper": {
- "type": "Direct",
- "requested": "[10.1.1, )",
- "resolved": "10.1.1",
- "contentHash": "uMgbqOdu9ZG5cIOty0C85hzzayBH2i9BthnS5FlMqKtMSHDv4ts81a2jS1VFaDBVhlBeIqJ/kQKjQY95BZde9w==",
- "dependencies": {
- "Microsoft.CSharp": "4.7.0",
- "System.Reflection.Emit": "4.7.0"
- }
- },
- "AutoMapper.Extensions.Microsoft.DependencyInjection": {
- "type": "Direct",
- "requested": "[8.1.0, )",
- "resolved": "8.1.0",
- "contentHash": "dQyGCAYcHbGuimVvCMu4Ea2S1oYOlgO9XfVdClmY5wgygJMZoS57emPzH0qNfknmtzMm4QbDO9i237W5IDjU1A==",
- "dependencies": {
- "AutoMapper": "[10.1.0, 11.0.0)",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.0",
- "Microsoft.Extensions.Options": "3.0.0"
- }
- },
- "Microsoft.AspNetCore.SpaServices.Extensions": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "ciy2GCvRnh9C22laArLsaItS+72U6Hqf4nDYShdvFgcen2ZV+NNSitb/B3vsmFfIPM8m4mf2x4T+vZ6OlI5XaA==",
- "dependencies": {
- "Microsoft.AspNetCore.SpaServices": "3.1.9",
- "Microsoft.Extensions.FileProviders.Physical": "3.1.9"
- }
- },
- "Microsoft.CodeAnalysis.FxCopAnalyzers": {
- "type": "Direct",
- "requested": "[3.3.0, )",
- "resolved": "3.3.0",
- "contentHash": "k3Icqx8kc+NrHImuiB8Jc/wd32Xeyd2B/7HOR5Qu9pyKzXQ4ikPeBAwzG2FSTuYhyIuNWvwL5k9yYBbbVz6w9w==",
- "dependencies": {
- "Microsoft.CodeAnalysis.VersionCheckAnalyzer": "[3.3.0]",
- "Microsoft.CodeQuality.Analyzers": "[3.3.0]",
- "Microsoft.NetCore.Analyzers": "[3.3.0]",
- "Microsoft.NetFramework.Analyzers": "[3.3.0]"
- }
- },
- "Microsoft.EntityFrameworkCore": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "u3A2W0BvAuAF2jgW+WX+C+Sh8sMGX5Kl1hdA0gu6A/XSrZQoW/BUP4a/q2n3iitDGndaorqjAKx+Spb9gBto+w==",
- "dependencies": {
- "Microsoft.Bcl.AsyncInterfaces": "1.1.1",
- "Microsoft.Bcl.HashCode": "1.1.0",
- "Microsoft.EntityFrameworkCore.Abstractions": "3.1.9",
- "Microsoft.EntityFrameworkCore.Analyzers": "3.1.9",
- "Microsoft.Extensions.Caching.Memory": "3.1.9",
- "Microsoft.Extensions.DependencyInjection": "3.1.9",
- "Microsoft.Extensions.Logging": "3.1.9",
- "System.Collections.Immutable": "1.7.1",
- "System.ComponentModel.Annotations": "4.7.0",
- "System.Diagnostics.DiagnosticSource": "4.7.1"
- }
- },
- "Microsoft.EntityFrameworkCore.Analyzers": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "eXGyx/Lb1fiiKtnIStdxGrfBSSQg8oZytE10f1T/2xAx12W9dKB9U9fg05cwNCDC0S2CXILsmZHYaGqCSXVAqQ=="
- },
- "Microsoft.EntityFrameworkCore.Sqlite": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "sMFCWv/1UcsFQZeGQcbfPbEZKZ1oKZqWZXTbc7PEZVMIXu82nbavstdNQ84x5IBXJkxl8iW3zjChb/FRBr5uLQ==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Sqlite.Core": "3.1.9",
- "SQLitePCLRaw.bundle_e_sqlite3": "2.0.2"
- }
- },
- "Microsoft.EntityFrameworkCore.Tools": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "mSgwjp0h5iqW5V49SVijR5O+kNpI1nitcbN12n9FYx/Ga6oCEFwXR/llBDesD6ASHw3Mx16vodJYJ7CEBx5rig==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Design": "3.1.9"
- }
- },
- "NSwag.AspNetCore": {
- "type": "Direct",
- "requested": "[13.8.2, )",
- "resolved": "13.8.2",
- "contentHash": "SNGlVSZoMyywBWueZBxl3B/nfaIM0fAcuNhTD/cfMKUn3Cn/Oi8d45HZY5vAPqczvppTbk4cZXyVwWDOfgiPbA==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.4",
- "Microsoft.AspNetCore.StaticFiles": "1.0.4",
- "Microsoft.Extensions.ApiDescription.Server": "3.0.0",
- "Microsoft.Extensions.FileProviders.Embedded": "1.0.1",
- "NSwag.Annotations": "13.8.2",
- "NSwag.Core": "13.8.2",
- "NSwag.Generation": "13.8.2",
- "NSwag.Generation.AspNetCore": "13.8.2",
- "System.IO.FileSystem": "4.3.0",
- "System.Xml.XPath.XDocument": "4.0.1"
- }
- },
- "SixLabors.ImageSharp": {
- "type": "Direct",
- "requested": "[1.0.1, )",
- "resolved": "1.0.1",
- "contentHash": "DjLoFNdUfsDP7RhPpr5hcUhl1XiejqBML9uDWuOUwCkc0Y+sG9IJLLbqSOi9XeoWqPviwdcDm1F8nKdF0qTYIQ=="
- },
- "System.IdentityModel.Tokens.Jwt": {
- "type": "Direct",
- "requested": "[6.8.0, )",
- "resolved": "6.8.0",
- "contentHash": "5tBCjAub2Bhd5qmcd0WhR5s354e4oLYa//kOWrkX+6/7ZbDDJjMTfwLSOiZ/MMpWdE4DWPLOfTLOq/juj9CKzA==",
- "dependencies": {
- "Microsoft.IdentityModel.JsonWebTokens": "6.8.0",
- "Microsoft.IdentityModel.Tokens": "6.8.0"
- }
- },
- "Microsoft.AspNetCore.Authorization": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "cN2KJkfHcKwh82c9WGx4Tqfd2h5HflU/Mu5vYLMHON8WahHU9hE32ciIXcEIoKLNpu+zs1u1cN/qxcKTdqu89w==",
- "dependencies": {
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Security.Claims": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Hosting.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "ybY8FOkdNfBPB5PLv1JO+It/94ftBzGUI1WqU4XySbIWyhw2TPmmKAUuO9uvJoR0qpsFup8FJz6trsBcBITg9w==",
- "dependencies": {
- "Microsoft.AspNetCore.Hosting.Server.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.Extensions.Configuration.Abstractions": "1.0.2",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.2",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2"
- }
- },
- "Microsoft.AspNetCore.Hosting.Server.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "XUiQPe/CflK1i0Voo9S6/G1iQh00gQ6sMqi3LRtKeceBbO6AOostaAUdhjyME92MapI4VFNl+Z+/KXUlMAExJQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Features": "1.0.3",
- "Microsoft.Extensions.Configuration.Abstractions": "1.0.2"
- }
- },
- "Microsoft.AspNetCore.Http": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "kfNOIGGgVtMzsSWZzXBqz5zsdo8ssBa90YHzZt95N8ARGXoolSaBHy6yBoMm/XcpbXM+m/x1fixTTMIWMgzJdQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.AspNetCore.WebUtilities": "1.0.3",
- "Microsoft.Extensions.ObjectPool": "1.0.1",
- "Microsoft.Extensions.Options": "1.0.2",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.Buffers": "4.0.0",
- "System.Threading": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Http.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "nnjvAf7ag6P0DyD/0nhRGjLpv+3DkPU0juF8aQh46X8uF4kzjJdrh65oL+4PVOu3K6BgSg6OVUs0QC0SE0FRtg==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Features": "1.0.3",
- "System.Globalization.Extensions": "4.0.1",
- "System.Linq.Expressions": "4.1.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Http.Extensions": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "+7Sd+14nexIJqcB4S1Eur9kzeMZ5CBtrxkei+PNbD78fg8vO3+TcCgrl5SBNTsUB/VJAfD/s0fgs5t+hHRj2Pg==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.Buffers": "4.0.0",
- "System.IO.FileSystem": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Http.Features": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "Ihq57tseNyPbJTmFXY4jQ4JkxLP0lh45VRwocQci/sFx+qcJGvWB+sJJ2/YPLy/qTWFAEfNAcswuY3OsNH9Gwg==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "1.0.1",
- "System.Collections": "4.0.11",
- "System.ComponentModel": "4.0.1",
- "System.Linq": "4.1.0",
- "System.Net.Primitives": "4.0.11",
- "System.Net.WebSockets": "4.0.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Security.Claims": "4.0.1",
- "System.Security.Cryptography.X509Certificates": "4.1.0",
- "System.Security.Principal": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.JsonPatch": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "WVaSVS+dDlWCR/qerHnBxU9tIeJ9GMA3M5tg4cxH7/cJYZZLnr2zvaFHGB+cRRNCKKTJ0pFRxT7ES8knhgAAaA==",
- "dependencies": {
- "Microsoft.CSharp": "4.0.1",
- "Newtonsoft.Json": "9.0.1",
- "System.Collections.Concurrent": "4.0.12",
- "System.ComponentModel.TypeConverter": "4.1.0",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Text.Encoding.Extensions": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "Isqgif1nuB+um86cEkpL8KnoxFCUCXBsbs9PuiuzElvlSiv4Ek3LvtrSUcbivekDDfys8CDbJhxwEI7WKJieAQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Routing.Abstractions": "1.0.4",
- "Microsoft.CSharp": "4.0.1",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.ComponentModel.TypeConverter": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Text.Encoding.Extensions": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.ApiExplorer": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "ujCFTM42U2WKUBhdaoLoiI+wVHgYhrmDrkl5+hWJ7EJW4fhp42w4cRZ97tjuveWr+M6JZjpS0q+7PVofQzFUiw==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4"
- }
- },
- "Microsoft.AspNetCore.Mvc.Core": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "1ukcttN1+T82hWXE8WS5kawkruolKI6LPVqVI4rTzN16kFszS/UqTrcwSUEnmTRpmWgFo665V3c2GpdQ9B6znw==",
- "dependencies": {
- "Microsoft.AspNetCore.Authorization": "1.0.3",
- "Microsoft.AspNetCore.Hosting.Abstractions": "1.0.3",
- "Microsoft.AspNetCore.Http": "1.0.3",
- "Microsoft.AspNetCore.Mvc.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Routing": "1.0.4",
- "Microsoft.Extensions.DependencyModel": "1.0.0",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.PlatformAbstractions": "1.0.0",
- "System.Buffers": "4.0.0",
- "System.Diagnostics.DiagnosticSource": "4.0.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.Formatters.Json": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "i8WWK2GwlBHfOL+d+kknJWPks6DS9tbN6nfJZU4yb+/wfUAYd311B2CIHzdat3IewubnK1TYONwrhQcs2FbLeA==",
- "dependencies": {
- "Microsoft.AspNetCore.JsonPatch": "1.0.0",
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4"
- }
- },
- "Microsoft.AspNetCore.NodeServices": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "bbd3FlSPWiRQrIcBLa5TaOvo4gjmmiNMkxA8VmZ6u0eIpS0Yj35/eTopaGdtzqwlqj5jXbdRoib1MruXuPaW8A==",
- "dependencies": {
- "Microsoft.Extensions.Logging.Console": "3.1.9",
- "Newtonsoft.Json": "12.0.2"
- }
- },
- "Microsoft.AspNetCore.Routing": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "mdIF3ckRothHWuCSFkk6YXACj5zxi5qM+cEAHjcpP04/wCHUoV0gGVnW+HI+LyFXE6JUwu2zXn5tfsCpW0U+SA==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Extensions": "1.0.3",
- "Microsoft.AspNetCore.Routing.Abstractions": "1.0.4",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.ObjectPool": "1.0.1",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Collections": "4.0.11",
- "System.Text.RegularExpressions": "4.1.0"
- }
- },
- "Microsoft.AspNetCore.Routing.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "GHxVt6LlXHFsCUd2Un+/vY1tBTXxnogfbDO0b8G5EGmkapSK+dOGOLJviscxQkp338Uabs081JEIdkRymI5GXA==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "System.Collections.Concurrent": "4.0.12",
- "System.Reflection.Extensions": "4.0.1",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.SpaServices": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Fb+N2ZyF1wNrGeWggT+Ovv6W8AAVxfi4V/SnuEsBOR+nmkFhty9zyh6IDRRS98GJK6OE3adqqPbWMtJqbxYnNA==",
- "dependencies": {
- "Microsoft.AspNetCore.NodeServices": "3.1.9"
- }
- },
- "Microsoft.AspNetCore.StaticFiles": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "2pNvwewAazhaaCdw2CGUvIcDrNQMlqP57JgBDf3v+pRj1rZ29HVnpvkX6a+TrmRYlJNmmxHOKEt468uE/gDcFw==",
- "dependencies": {
- "Microsoft.AspNetCore.Hosting.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Http.Extensions": "1.0.3",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.WebEncoders": "1.0.3"
- }
- },
- "Microsoft.AspNetCore.WebUtilities": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "snSGNs5EEisqivDjDiskFkFyu+DV2Ib9sMPOBQKtoFwI5H1W5YNB/rIVqDZQL16zj/uzdwwxrdE/5xhkVyf6gQ==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "1.0.1",
- "System.Buffers": "4.0.0",
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.Bcl.AsyncInterfaces": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w=="
- },
- "Microsoft.Bcl.HashCode": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "J2G1k+u5unBV+aYcwxo94ip16Rkp65pgWFb0R6zwJipzWNMgvqlWeuI7/+R+e8bob66LnSG+llLJ+z8wI94cHg=="
- },
- "Microsoft.CodeAnalysis.VersionCheckAnalyzer": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "xjLM3DRFZMan3nQyBQEM1mBw6VqQybi4iMJhMFW6Ic1E1GCvqJR3ABOwEL7WtQjDUzxyrGld9bASnAos7G/Xyg=="
- },
- "Microsoft.CodeQuality.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "zZ3miq6u22UFQKhfJyLnVEJ+DgeOopLh3eKJnKAcOetPP2hiv3wa7kHZlBDeTvtqJQiAQhAVbttket8XxjN1zw=="
- },
- "Microsoft.CSharp": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
- },
- "Microsoft.Data.Sqlite.Core": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "+u4PeT1npi2EzhxGc5r1Z2z73zuXw+TlKVZm44WQhNCUw4LtUVDaxGSpUhrjW+X4snBCBfr4kT/uJyKnL4R4og==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2"
- }
- },
- "Microsoft.DotNet.PlatformAbstractions": {
- "type": "Transitive",
- "resolved": "3.1.6",
- "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
- },
- "Microsoft.EntityFrameworkCore.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "IR6Y4RJVlw0QXdWXjF3Kx9s1QLiicJus+BFBKr43lBtriV20j3yrWMoaZ9W1AUUgnicZXpXVcNfklqtmwb9Sxw=="
- },
- "Microsoft.EntityFrameworkCore.Design": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "2zgP7BWcw5nqGQiT4bEtiI6ras+4pvKg5D+tA3AYvjEifzzaWvmRTb3B9nRHpIYJAhPtmWNBVnVXLbu3fS1OYA==",
- "dependencies": {
- "Microsoft.CSharp": "4.7.0",
- "Microsoft.EntityFrameworkCore.Relational": "3.1.9"
- }
- },
- "Microsoft.EntityFrameworkCore.Relational": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "7fhWuSfrCYlv/hvOX5OhbFJF/G9f8sifqTrJiYnAYLDOvNizwv7t9tFPD8JwaF3zM2S54O5/Vni2NxvwzSaW2w==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore": "3.1.9"
- }
- },
- "Microsoft.EntityFrameworkCore.Sqlite.Core": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Da6h8LdpJwKc1az9DMWt2Mt6gHXPRZqwiumV1Zx0AuM3EThyokVDzBGy2sti0AcBhcQMLJHPEr5R9xuiWvaYYQ==",
- "dependencies": {
- "Microsoft.Data.Sqlite.Core": "3.1.9",
- "Microsoft.DotNet.PlatformAbstractions": "3.1.6",
- "Microsoft.EntityFrameworkCore.Relational": "3.1.9",
- "Microsoft.Extensions.DependencyModel": "3.1.6"
- }
- },
- "Microsoft.Extensions.ApiDescription.Server": {
- "type": "Transitive",
- "resolved": "3.0.0",
- "contentHash": "LH4OE/76F6sOCslif7+Xh3fS/wUUrE5ryeXAMcoCnuwOQGT5Smw0p57IgDh/pHgHaGz/e+AmEQb7pRgb++wt0w=="
- },
- "Microsoft.Extensions.Caching.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "/2QsPAsUZD4qvftZkUKHRRRryPDXWh606/iNXPLrulwHLMr9JNsKBJWVqylT3qU92nJok5VoqSblkY9mSyxFyg==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Caching.Memory": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "/JrVMVetX/kpJQUIlJ6NLQ3zbF0yyryXpo4+uFCqYIUZzgmWk8DS/zSKcyj1tQ3410+vhDEAPngxC+hg0IlJeg==",
- "dependencies": {
- "Microsoft.Extensions.Caching.Abstractions": "3.1.9",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "lqdkOGNeTMKG981Q7yWGlRiFbIlsRwTlMMiybT+WOzUCFBS/wc25tZgh7Wm/uRoBbWefgvokzmnea7ZjmFedmA==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "vOJxPKczaHpXeZFrxARxYwsEulhEouXc5aZGgMdkhV/iEXX9/pfjqKk76rTG+4CsJjHV+G/4eMhvOIaQMHENNA==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration.Binder": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "BG6HcT7tARYakftqfQu+cLksgIWG1NdxMY+igI12hdZrUK+WjS973NiRyuao/U9yyTeM9NPwRnC61hCmG3G3jg==",
- "dependencies": {
- "Microsoft.Extensions.Configuration": "3.1.9"
- }
- },
- "Microsoft.Extensions.DependencyInjection": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "ORqfrAACcvTInie1oGola5uky344/PiNfgayTPuZWV4WnSfIQZJQm/ZLpGshJE3h7TqwYaYElGazK/yaM2bFLA==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9"
- }
- },
- "Microsoft.Extensions.DependencyInjection.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "8PkcaPwiTPOhqshoY4+rQUbz86X6YpLDLUqXOezh7L2A3pgpBmeBBByYIffofBlvQxDdQ0zB2DkWjbZWyCxRWg=="
- },
- "Microsoft.Extensions.DependencyModel": {
- "type": "Transitive",
- "resolved": "3.1.6",
- "contentHash": "/UlDKULIVkLQYn1BaHcy/rc91ApDxJb7T75HcCbGdqwvxhnRQRKM2di1E70iCPMF9zsr6f4EgQTotBGxFIfXmw==",
- "dependencies": {
- "System.Text.Json": "4.7.2"
- }
- },
- "Microsoft.Extensions.FileProviders.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Q4SGwEFZKiZbpzPgdGbQUULxtcH1zXMOwCPKSm6QwVcOCGshf3QLfBh+O/GyFH4B0RfZ16nKyeW1mMONlRyjUw==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.FileProviders.Embedded": {
- "type": "Transitive",
- "resolved": "1.0.1",
- "contentHash": "nSEa8bH3fVdTYGqK4twOKLxxgKIW3cz9g9mrzhPh/CmdvGJWKRTIlBIZi7lz+lqNQpxean5vbAo84R/mU+JpGA==",
- "dependencies": {
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "Microsoft.Extensions.FileProviders.Physical": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "HWDSsblTCQp7EEJJmnLzttIhFGzDu+DGqBbOvGCdFT0+pkCuBkn3EiWpEEcm5WMTO5njmsbLSK9ZuUUf2zPsFg==",
- "dependencies": {
- "Microsoft.Extensions.FileProviders.Abstractions": "3.1.9",
- "Microsoft.Extensions.FileSystemGlobbing": "3.1.9"
- }
- },
- "Microsoft.Extensions.FileSystemGlobbing": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "5bnewG1aBiSESPNwcXGIxDDRN95uqdy+fqZZ8Z63Et5rRNlAwAfXHOrg+FTht7UjHobjvtjzquMCbAWhWEPHIw=="
- },
- "Microsoft.Extensions.Logging": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "+V3i0jCQCO6IIOf6e+fL0SqrZd2x/Krug9EEL1JHa9R03RsbEpltCtjVY5hxedyuyuQKwvLoR12sCfu/9XEUAw==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Binder": "3.1.9",
- "Microsoft.Extensions.DependencyInjection": "3.1.9",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.Logging.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "W5fbF8qVR9SMVVJqDQLIR7meWbev6Pu/lbrm7LDNr4Sp7HOotr4k2UULTdFSXOi5aoDdkQZpWnq0ZSpjrR3tjg=="
- },
- "Microsoft.Extensions.Logging.Configuration": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "hv6XsGgikrbkolBJdF1usl9R/nrliC5mifMqHMEY9zWcCLwNkXMJiS8p0lbosrnpVAMi4PbNx39DB51Dqscd0w==",
- "dependencies": {
- "Microsoft.Extensions.Logging": "3.1.9",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "3.1.9"
- }
- },
- "Microsoft.Extensions.Logging.Console": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "8Dusl1rkDivmvLrwj6QAo917xMHPiDBzG3IG3agiyDdtsC/fRp+1VN5iIN+O09PtEaMged2OLA6wCDwfSTSTZw==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9",
- "Microsoft.Extensions.Logging": "3.1.9",
- "Microsoft.Extensions.Logging.Configuration": "3.1.9"
- }
- },
- "Microsoft.Extensions.ObjectPool": {
- "type": "Transitive",
- "resolved": "1.0.1",
- "contentHash": "pJMOnxuqmG37OjccfvtqVoo3bQGoN+0EJUzzp7+2uxSdioER82caAk6Yi/z5aysapn5XENNIIa7SaYnYKSS69A==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "Microsoft.Extensions.Options": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "EIb3G1DL+Rl9MvJR7LjI1wCy2nfTN4y8MflbOftn1HLYQBj/Rwl8kUbGTrSFE01c99Wm4ETjWVsjqKcpFvhPng==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Options.ConfigurationExtensions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "u5jh7RW+Ev81YqK1ZoBG0lftp2MA9xqXiTiRL46XzaPj2ScNUyiVbzcVY0fPbE27UOpT2hj+yPzRSOMIIo55UA==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9",
- "Microsoft.Extensions.Configuration.Binder": "3.1.9",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.PlatformAbstractions": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "zyjUzrOmuevOAJpIo3Mt5GmpALVYCVdLZ99keMbmCxxgQH7oxzU58kGHzE6hAgYEiWsdfMJLjVR7r+vSmaJmtg==",
- "dependencies": {
- "System.AppContext": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "Microsoft.Extensions.Primitives": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "IrHecH0eGG7/XoeEtv++oLg/sJHRNyeCqlA9RhAo6ig4GpOTjtDr32sBMYuuLtUq8ALahneWkrOzoBAwJ4L4iA=="
- },
- "Microsoft.Extensions.WebEncoders": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "TClNvczWRxF6bVPhn5EK3Y3QNi5jTP68Qur+5Fk+MQLPeBI18WN7X145DDJ6bFeNOwgdCHl73lHs5uZp9ish1A==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.2",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.IdentityModel.JsonWebTokens": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "+7JIww64PkMt7NWFxoe4Y/joeF7TAtA/fQ0b2GFGcagzB59sKkTt/sMZWR6aSZht5YC7SdHi3W6yM1yylRGJCQ==",
- "dependencies": {
- "Microsoft.IdentityModel.Tokens": "6.8.0"
- }
- },
- "Microsoft.IdentityModel.Logging": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "Rfh/p4MaN4gkmhPxwbu8IjrmoDncGfHHPh1sTnc0AcM/Oc39/fzC9doKNWvUAjzFb8LqA6lgZyblTrIsX/wDXg=="
- },
- "Microsoft.IdentityModel.Tokens": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "gTqzsGcmD13HgtNePPcuVHZ/NXWmyV+InJgalW/FhWpII1D7V1k0obIseGlWMeA4G+tZfeGMfXr0klnWbMR/mQ==",
- "dependencies": {
- "Microsoft.CSharp": "4.5.0",
- "Microsoft.IdentityModel.Logging": "6.8.0",
- "System.Security.Cryptography.Cng": "4.5.0"
- }
- },
- "Microsoft.Net.Http.Headers": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "2F8USh4hR5xppvaxtw2EStX74Ih+HhRj7aQD1uaB9JmTGy478F7t4VU+IdZXauEDrvS7LYAyyhmOExsUFK3PAw==",
- "dependencies": {
- "System.Buffers": "4.0.0",
- "System.Collections": "4.0.11",
- "System.Diagnostics.Contracts": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "Microsoft.NetCore.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "6qptTHUu1Wfszuf83NhU0IoAb4j7YWOpJs6oc6S4G/nI6aGGWKH/Xi5Vs9L/8lrI74ijEEzPcIwafSQW5ASHtA=="
- },
- "Microsoft.NETCore.Platforms": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
- },
- "Microsoft.NETCore.Targets": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
- },
- "Microsoft.NetFramework.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "JTfMic5fEFWICePbr7GXOGPranqS9Qxu2U/BZEcnnGbK1SFW8TxRyGp6O1L52xsbfOdqmzjc0t5ubhDrjj+Xpg=="
- },
- "Microsoft.Win32.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "fQnBHO9DgcmkC9dYSJoBqo6sH1VJwJprUHh8F3hbcRlxiQiBUuTntdk8tUwV490OqC2kQUrinGwZyQHTieuXRA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "Namotion.Reflection": {
- "type": "Transitive",
- "resolved": "1.0.14",
- "contentHash": "wuJGiFvGfehH2w7jAhMbCJt0/rvUuHyqSZn0sMhNTviDfBZRyX8LFlR/ndQcofkGWulPDfH5nKYTeGXE8xBHPA==",
- "dependencies": {
- "Microsoft.CSharp": "4.3.0"
- }
- },
- "Newtonsoft.Json": {
- "type": "Transitive",
- "resolved": "12.0.2",
- "contentHash": "rTK0s2EKlfHsQsH6Yx2smvcTCeyoDNgCW7FEYyV01drPlh2T243PR2DiDXqtC5N4GDm4Ma/lkxfW5a/4793vbA=="
- },
- "NJsonSchema": {
- "type": "Transitive",
- "resolved": "10.2.1",
- "contentHash": "/BtWbYTusyoSgQkCB4eYijMfZotB/rfASDsl1k9evlkm5vlOP4s4Y09TOzBChU77d/qUABVYL1Xf+TB8E0Wfpw==",
- "dependencies": {
- "Namotion.Reflection": "1.0.14",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Annotations": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "/GO+35CjPYQTPS5/Q8udM5JAMEWVo8JsrkV2Uw3OW4/AJU9iOS7t6WJid6ZlkpLMjnW7oex9mvJ2EZNE4eOG/Q=="
- },
- "NSwag.Core": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "Hm6pU9qFJuXLo3b27+JTXztfeuI/15Ob1sDsfUu4rchN0+bMogtn8Lia8KVbcalw/M+hXc0rWTFp5ueP23e+iA==",
- "dependencies": {
- "NJsonSchema": "10.2.1",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Generation": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "LBIrpHFRZeMMbqL1hdyGb7r8v+T52aOCARxwfAmzE+MlOHVpjsIxyNSXht9EzBFMbSH0tj7CK2Ugo7bm+zUssg==",
- "dependencies": {
- "NJsonSchema": "10.2.1",
- "NSwag.Core": "13.8.2",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Generation.AspNetCore": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "0ydVv6OidspZ/MS6qmU8hswGtXwq5YZPg+2a2PHGD6jNp2Fef4j1wC3xa3hplDAq7cK+BgpyDKtvj9+X01+P5g==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.ApiExplorer": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.4",
- "NJsonSchema": "10.2.1",
- "NSwag.Generation": "13.8.2"
- }
- },
- "runtime.native.System": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "QfS/nQI7k/BLgmLrw7qm7YBoULEvgWnPI+cYsbfCVFTW8Aj+i8JhccxcFMu1RWms0YZzF+UHguNBK4Qn89e2Sg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "runtime.native.System.Net.Http": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "Nh0UPZx2Vifh8r+J+H2jxifZUD3sBrmolgiFWJd2yiNrxO0xTa6bAw3YwRn1VOiSen/tUXMS31ttNItCZ6lKuA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "runtime.native.System.Security.Cryptography": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "2CQK0jmO6Eu7ZeMgD+LOFbNJSXHFVQbCJJkEyEwowh1SCgYnrn9W9RykMfpeeVGw7h4IBvYikzpGUlmZTUafJw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "SQLitePCLRaw.bundle_e_sqlite3": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "OVPI/nh5AqfLCIKhAYqjCa6AHhc7oKApGcGM3UhMRSerFiBx58nSpGwxVFdMgjOCWZR+fA49nzsnKlWp5hFo8w==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2",
- "SQLitePCLRaw.lib.e_sqlite3": "2.0.2",
- "SQLitePCLRaw.provider.dynamic_cdecl": "2.0.2"
- }
- },
- "SQLitePCLRaw.core": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "TFSBX426OelS1tkaVC254NVVlrJIe9YLhWPkEvuqJj2104QpmDmEYOhfdfDJD1E/2SmqDhoRw1ek5cQHj8olcQ==",
- "dependencies": {
- "System.Memory": "4.5.3"
- }
- },
- "SQLitePCLRaw.lib.e_sqlite3": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "S+Tsqe/M7wsc+9HeediI6UHtBKf2X586aRwhi1aBVLGe0WxkAo52O9ZxwEy/v8XMLefcrEMupd2e9CDlIT6QCw=="
- },
- "SQLitePCLRaw.provider.dynamic_cdecl": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "ZSwacbKJUsxJEZxwT23uZVrGbaIvXcADZDz5Sr66fikO5eehdcceDncjzwzTzWfW13di8gpTpstx3WJSt/Ci5Q==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2"
- }
- },
- "System.AppContext": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "3QjO4jNV7PdKkmQAVp9atA+usVnKRwI3Kx1nMwJ93T0LcQfx7pKAYk0nKz5wn1oP5iqlhZuy6RXOFdhr7rDwow==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Buffers": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "msXumHfjjURSkvxUjYuq4N2ghHoRi2VpXcKMA7gK6ujQfU3vGpl+B6ld0ATRg+FZFpRyA6PgEPA+VlIkTeNf2w==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tracing": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Collections": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Collections.Concurrent": {
- "type": "Transitive",
- "resolved": "4.0.12",
- "contentHash": "2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tracing": "4.1.0",
- "System.Globalization": "4.0.11",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Collections.Immutable": {
- "type": "Transitive",
- "resolved": "1.7.1",
- "contentHash": "B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q=="
- },
- "System.Collections.NonGeneric": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Collections.Specialized": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
- "dependencies": {
- "System.Collections.NonGeneric": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.Globalization.Extensions": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.ComponentModel": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.ComponentModel.Annotations": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "0YFqjhp/mYkDGpU0Ye1GjE53HMp9UVfGN7seGpAMttAC0C40v5gw598jCgpbBLMmCo0E5YRLBv5Z2doypO49ZQ=="
- },
- "System.ComponentModel.Primitives": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "sc/7eVCdxPrp3ljpgTKVaQGUXiW05phNWvtv/m2kocXqrUQvTVWKou1Edas2aDjTThLPZOxPYIGNb/HN0QjURg==",
- "dependencies": {
- "System.ComponentModel": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.ComponentModel.TypeConverter": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "MnDAlaeJZy9pdB5ZdOlwdxfpI+LJQ6e0hmH7d2+y2LkiD8DRJynyDYl4Xxf3fWFm7SbEwBZh4elcfzONQLOoQw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Collections.NonGeneric": "4.0.1",
- "System.Collections.Specialized": "4.0.1",
- "System.ComponentModel": "4.0.1",
- "System.ComponentModel.Primitives": "4.1.0",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Diagnostics.Contracts": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "HvQQjy712vnlpPxaloZYkuE78Gn353L0SJLJVeLcNASeg9c4qla2a1Xq8I7B3jZoDzKPtHTkyVO7AZ5tpeQGuA==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.Debug": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.DiagnosticSource": {
- "type": "Transitive",
- "resolved": "4.7.1",
- "contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw=="
- },
- "System.Diagnostics.Tools": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.Tracing": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "vDN1PoMZCkkdNjvZLql592oYJZgS7URcJzJ7bxeBgGtx5UtR5leNm49VmfHGqIffX4FKacHbI3H6UyNSHQknBg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization.Calendars": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "L1c6IqeQ88vuzC1P81JeHmHA8mxq8a18NUBNXnIY/BVb+TCyAaGIFbhpZt60h9FJNmisymoQkHEFSE9Vslja1Q==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "KKo23iKeOaIg61SSXwjANN7QYDr/3op3OWGGzDzz7mypx0Za0fZSeG0l6cco8Ntp8YMYkIQcAqlk8yhm5/Uhcg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0"
- }
- },
- "System.IO": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0",
- "System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
- }
- },
- "System.IO.FileSystem": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.IO": "4.3.0",
- "System.IO.FileSystem.Primitives": "4.3.0",
- "System.Runtime": "4.3.0",
- "System.Runtime.Handles": "4.3.0",
- "System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
- }
- },
- "System.IO.FileSystem.Primitives": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==",
- "dependencies": {
- "System.Runtime": "4.3.0"
- }
- },
- "System.Linq": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "System.Linq.Expressions": {
- "type": "Transitive",
- "resolved": "4.1.1",
- "contentHash": "bXwi8FrK/XIGPvtk1ZnawffhqLPyacj7dZnbFaV52YGaQigNqGEzNAByAIvL9FlEe3TCzoInorHF91IK//Q3Xg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Linq": "4.1.0",
- "System.ObjectModel": "4.0.12",
- "System.Reflection": "4.1.0",
- "System.Reflection.Emit": "4.0.1",
- "System.Reflection.Emit.ILGeneration": "4.0.1",
- "System.Reflection.Emit.Lightweight": "4.0.1",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Memory": {
- "type": "Transitive",
- "resolved": "4.5.3",
- "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
- },
- "System.Net.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "hVvfl4405DRjA2408luZekbPhplJK03j2Y2lSfMlny7GHXlkByw1iLnc9mgKW0GdQn73vvMcWrWewAhylXA4Nw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Handles": "4.0.1"
- }
- },
- "System.Net.WebSockets": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==",
- "dependencies": {
- "Microsoft.Win32.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.ObjectModel": {
- "type": "Transitive",
- "resolved": "4.0.12",
- "contentHash": "tAgJM1xt3ytyMoW4qn4wIqgJYm7L7TShRZG4+Q4Qsi2PCcj96pXN7nRywS9KkB3p/xDUjc2HSwP9SROyPYDYKQ==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Reflection": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.IO": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Emit": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ=="
- },
- "System.Reflection.Emit.ILGeneration": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Emit.Lightweight": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "sSzHHXueZ5Uh0OLpUQprhr+ZYJrLPA2Cmr4gn0wj9+FftNKXx8RIMKvO9qnjk2ebPYUjZ+F2ulGdPOsvj+MEjA==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Reflection.Emit.ILGeneration": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.TypeExtensions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Resources.ResourceManager": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Runtime": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0"
- }
- },
- "System.Runtime.Extensions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Runtime.Handles": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Runtime.InteropServices": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Reflection": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Handles": "4.0.1"
- }
- },
- "System.Runtime.Numerics": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "+XbKFuzdmLP3d1o9pdHu2nxjNr2OEPqGzKeegPLCUMM71a0t50A/rOcIRmGs9wR7a8KuHX6hYs/7/TymIGLNqg==",
- "dependencies": {
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "System.Runtime.Serialization.Primitives": {
- "type": "Transitive",
- "resolved": "4.1.1",
- "contentHash": "HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==",
- "dependencies": {
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Security.Claims": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "4Jlp0OgJLS/Voj1kyFP6MJlIYp3crgfH8kNQk2p7+4JYfc1aAmh9PZyAMMbDhuoolGNtux9HqSOazsioRiDvCw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Security.Principal": "4.0.1"
- }
- },
- "System.Security.Cryptography.Algorithms": {
- "type": "Transitive",
- "resolved": "4.2.0",
- "contentHash": "8JQFxbLVdrtIOKMDN38Fn0GWnqYZw/oMlwOUG/qz1jqChvyZlnUmu+0s7wLx7JYua/nAXoESpHA3iw11QFWhXg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.Cng": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A=="
- },
- "System.Security.Cryptography.Csp": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "/i1Usuo4PgAqgbPNC0NjbO3jPW//BoBlTpcWFD1EHVbidH21y4c1ap5bbEMSGAXjAShhMH4abi/K8fILrnu4BQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11"
- }
- },
- "System.Security.Cryptography.Encoding": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "FbKgE5MbxSQMPcSVRgwM6bXN3GtyAh04NkV8E5zKCBE26X0vYW0UtTa2FIgkH33WVqBVxRgxljlVYumWtU+HcQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.Collections.Concurrent": "4.0.12",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.OpenSsl": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "HUG/zNUJwEiLkoURDixzkzZdB5yGA5pQhDP93ArOpDPQMteURIGERRNzzoJlmTreLBWr5lkFSjjMSk8ySEpQMw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "Wkd7QryWYjkQclX0bngpntW5HSlMzeJU24UaLJQ7YTfI8ydAVAaU2J+HXLLABOVJlKTVvAeL0Aj39VeTe7L+oA==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Security.Cryptography.X509Certificates": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "4HEfsQIKAhA1+ApNn729Gi09zh+lYWwyIuViihoMDWp1vQnEkL2ct7mAbhBlLYm+x/L4Rr/pyGge1lIY635e0w==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Globalization.Calendars": "4.0.1",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.IO.FileSystem.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Cng": "4.2.0",
- "System.Security.Cryptography.Csp": "4.0.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.OpenSsl": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11",
- "runtime.native.System": "4.0.0",
- "runtime.native.System.Net.Http": "4.0.1",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Principal": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "On+SKhXY5rzxh/S8wlH1Rm0ogBlu7zyHNxeNBiXauNrhHRXAe9EuX8Yl5IOzLPGU5Z4kLWHMvORDOCG8iu9hww==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Text.Encoding": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Text.Encoding.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "jtbiTDtvfLYgXn8PTfWI+SiBs51rrmO4AAckx4KR6vFK9Wzf6tI8kcRdsYQNwriUeQ1+CtQbM1W4cMbLXnj/OQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "GgJDO6/1bW6kkttxIiPK2jsqllQ3ifaeeBAJJrcoJq0lAclIZsAZZdEqi6JHq+QLZXL2UsjyWb8K8EOH7nOSPw==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Text.Json": {
- "type": "Transitive",
- "resolved": "4.7.2",
- "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg=="
- },
- "System.Text.RegularExpressions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "i88YCXpRTjCnoSQZtdlHkAOx4KNNik4hMy83n0+Ftlb7jvV6ZiZWMpnEZHhjBp6hQVh8gWd/iKNPzlPF7iyA2g==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Threading": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
- "dependencies": {
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Threading.Tasks": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Threading.Tasks.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "pH4FZDsZQ/WmgJtN4LWYmRdJAEeVkyriSwrv2Teoe5FOU0Yxlb6II6GL8dBPOfRmutHGATduj3ooMt7dJ2+i+w==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Xml.ReaderWriter": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "ZIiLPsf67YZ9zgr31vzrFaYQqxRPX9cVHjtPSnmx4eN6lbS/yEyYNr2vs1doGDEscF0tjCZFsk9yUg1sC9e8tg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.IO.FileSystem.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Text.Encoding": "4.0.11",
- "System.Text.Encoding.Extensions": "4.0.11",
- "System.Text.RegularExpressions": "4.1.0",
- "System.Threading.Tasks": "4.0.11",
- "System.Threading.Tasks.Extensions": "4.0.0"
- }
- },
- "System.Xml.XDocument": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tools": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11"
- }
- },
- "System.Xml.XPath": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "UWd1H+1IJ9Wlq5nognZ/XJdyj8qPE4XufBUkAW59ijsCPjZkZe0MUzKKJFBr+ZWBe5Wq1u1d5f2CYgE93uH7DA==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11"
- }
- },
- "System.Xml.XPath.XDocument": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "FLhdYJx4331oGovQypQ8JIw2kEmNzCsjVOVYY/16kZTUoquZG85oVn7yUhBE2OZt1yGPSXAL0HTEfzjlbNpM7Q==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11",
- "System.Xml.XDocument": "4.0.11",
- "System.Xml.XPath": "4.0.1"
- }
- },
- "timeline.errorcodes": {
- "type": "Project"
- }
- }
- }
-} \ No newline at end of file