diff --git a/README.md b/README.md index 5fcb780..6f7adc5 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,41 @@ See the Swagger UI at [http://localhost:5229/swagger](http://localhost:5229/swag # Roadmap/路线图 ⚡ +## v1.0 + + - Basic configuration management/基础配置管理 + - Web API + - Basic admin panel/基础管理面板 + - Configuration client/配置客户端 + - WebSockets protocol support of Client. / 客户端支持WebSockets协议。 + +## v1.1 + + - gRPC protocol support of Client. / 客户端支持gRPC协议。 + - Rollback to history version. / 回滚到历史版本。 + - Role-based access control. / 基于角色的访问控制。 + +## v1.2 + + - Docker support. / 支持Docker部署。 + - MongoDB support. / 支持MongoDB。 + - User registration. / 用户注册。 + +## v1.3 + + - Multiple node deployment. / 支持多节点部署。 + - Common configuration. / 公共配置。 + +## v2.0 + + - Service discovery. / 服务发现。 + +## v3.0 + + - Service insights. / 服务监控。 + - Service tracing. / 服务追踪。 + - Log management. / 日志管理。 + # Contributing/贡献 ⚡ diff --git a/Source/Starfish.Service/Application/Implements/SettingApplicationService.cs b/Source/Starfish.Service/Application/Implements/SettingApplicationService.cs index 65492b9..492afa6 100644 --- a/Source/Starfish.Service/Application/Implements/SettingApplicationService.cs +++ b/Source/Starfish.Service/Application/Implements/SettingApplicationService.cs @@ -37,6 +37,7 @@ public Task GetDetailAsync(long appId, string environment, Can .ContinueWith(t => t.Result.Result, cancellationToken); } + /// public Task CreateAsync(long appId, string environment, string format, SettingEditDto data, CancellationToken cancellationToken = default) { var useCase = LazyServiceProvider.GetRequiredService(); @@ -45,6 +46,7 @@ public Task CreateAsync(long appId, string environment, string format, Set .ContinueWith(t => t.Result, cancellationToken); } + /// public async Task UpdateAsync(long appId, string environment, string format, SettingEditDto data, CancellationToken cancellationToken = default) { var useCase = LazyServiceProvider.GetRequiredService(); @@ -60,6 +62,7 @@ public Task DeleteAsync(long appId, string environment, CancellationToken cancel return useCase.ExecuteAsync(input, cancellationToken); } + /// public Task UpdateAsync(long appId, string environment, string key, string value, CancellationToken cancellationToken = default) { var useCase = LazyServiceProvider.GetRequiredService(); diff --git a/Source/Starfish.Service/Application/Mappings/AppsMappingProfile.cs b/Source/Starfish.Service/Application/Mappings/AppsMappingProfile.cs index 9b81f32..7a91a67 100644 --- a/Source/Starfish.Service/Application/Mappings/AppsMappingProfile.cs +++ b/Source/Starfish.Service/Application/Mappings/AppsMappingProfile.cs @@ -1,6 +1,7 @@ using AutoMapper; using Nerosoft.Starfish.Domain; using Nerosoft.Starfish.Transit; +using Nerosoft.Starfish.UseCases; namespace Nerosoft.Starfish.Application; @@ -14,6 +15,8 @@ internal class AppsMappingProfile : Profile /// public AppsMappingProfile() { + CreateMap() + .ForMember(dest => dest.StatusDescription, opt => opt.MapFrom(src => src.Status.GetDescription(Resources.ResourceManager, Resources.Culture))); CreateMap() .ForMember(dest => dest.StatusDescription, opt => opt.MapFrom(src => src.Status.GetDescription(Resources.ResourceManager, Resources.Culture))); CreateMap() diff --git a/Source/Starfish.Service/UseCases/Apps/AppInfoQueryUseCase.cs b/Source/Starfish.Service/UseCases/Apps/AppInfoQueryUseCase.cs index d42a3f3..356e77e 100644 --- a/Source/Starfish.Service/UseCases/Apps/AppInfoQueryUseCase.cs +++ b/Source/Starfish.Service/UseCases/Apps/AppInfoQueryUseCase.cs @@ -1,4 +1,5 @@ using System.Security.Authentication; +using Microsoft.EntityFrameworkCore; using Nerosoft.Euonia.Application; using Nerosoft.Euonia.Claims; using Nerosoft.Starfish.Domain; @@ -65,25 +66,55 @@ public Task ExecuteAsync(AppInfoQueryInput input, Cancellati } var predicate = input.Criteria.GetSpecification().Satisfy(); - return _repository.FindAsync(predicate, Permission, input.Skip, input.Count, cancellationToken) - .ContinueWith(task => new AppInfoQueryOutput(task.Result.ProjectedAsCollection()), cancellationToken); - IQueryable Permission(IQueryable query) - { - if (!_identity.IsInRole("SA")) - { - var userId = _identity.GetUserIdOfInt64(); - var teamQuery = _repository.Context.Set(); - query = from app in query - join member in teamQuery on app.TeamId equals member.TeamId - where member.UserId == userId - select app; - } + var context = _repository.Context; - { - } + var query = from app in context.Set().Where(predicate) + join team in context.Set() on app.TeamId equals team.Id + select new AppInfoItemModel + { + Id = app.Id, + TeamId = app.TeamId, + TeamName = team.Name, + Name = app.Name, + Code = app.Code, + Status = app.Status, + CreateTime = app.CreateTime, + UpdateTime = app.UpdateTime + }; - return query.OrderByDescending(t => t.Id); + if (!_identity.IsInRole("SA")) + { + var userId = _identity.GetUserIdOfInt64(); + var teamQuery = _repository.Context.Set(); + query = from app in query + join member in teamQuery on app.TeamId equals member.TeamId + where member.UserId == userId + select app; } + + { + } + + return query.OrderByDescending(t => t.Id) + .ToListAsync(cancellationToken) + .ContinueWith(task => + { + task.WaitAndUnwrapException(cancellationToken); + var result = task.Result.ProjectedAsCollection(); + return new AppInfoQueryOutput(result); + }, cancellationToken); } +} + +internal class AppInfoItemModel +{ + public long Id { get; set; } + public long TeamId { get; set; } + public string TeamName { get; set; } + public string Name { get; set; } + public string Code { get; set; } + public AppStatus Status { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } } \ No newline at end of file diff --git a/Source/Starfish.Transit/Apps/AppInfoItemDto.cs b/Source/Starfish.Transit/Apps/AppInfoItemDto.cs index 4cc6ac9..4aa370f 100644 --- a/Source/Starfish.Transit/Apps/AppInfoItemDto.cs +++ b/Source/Starfish.Transit/Apps/AppInfoItemDto.cs @@ -11,6 +11,16 @@ public class AppInfoItemDto /// public long Id { get; set; } + /// + /// 所属团队Id + /// + public long TeamId { get; set; } + + /// + /// 所属团队名称 + /// + public string TeamName { get; set; } + /// /// 名称 /// diff --git a/Source/Starfish.Webapp/Layout/MainLayout.razor b/Source/Starfish.Webapp/Layout/MainLayout.razor index aef4046..77a2eef 100644 --- a/Source/Starfish.Webapp/Layout/MainLayout.razor +++ b/Source/Starfish.Webapp/Layout/MainLayout.razor @@ -1,26 +1,27 @@ -@inherits LayoutComponentBase +@using System.Security.Authentication +@inherits LayoutComponentBase @implements IDisposable @attribute [Authorize] @inject AuthenticationStateProvider Authentication @inject NavigationManager Navigation +@inject IToastService ToastService - - + Starfish - +
- + - + + StatusSize="PresenceBadgeSize.Small" />
@@ -36,27 +37,38 @@ @(Resources.IDS_MENU_TEXT_LOGS) - - -
- @Body -
-
- - @if (ex is UnauthorizedAccessException) - { - - } - else if(ex is HttpRequestException e && e.StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - - } - else - { -
@ex.Message
- } -
-
+ @if (!Constants.IsDebug) + { + + +
+ @Body +
+
+ + + @if (ex is AuthenticationException) + { + + } + else if (ex is HttpRequestException e && e.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + + } + else + { +
@ex.Message
+ } +
+ +
+ } + else + { +
+ @Body +
+ }
@@ -69,8 +81,8 @@ - - + +
@code { @@ -87,6 +99,27 @@ StateHasChanged(); Authentication.AuthenticationStateChanged += OnAuthenticationStateChanged; + + if (!WeakReferenceMessenger.Default.IsRegistered(this)) + { + WeakReferenceMessenger.Default.Register(this, Constants.Message.ExceptionThrown, OnExceptionThrown); + } + } + + private void OnExceptionThrown(object recipient, Exception exception) + { + switch (exception) + { + case UnauthorizedAccessException: + case HttpRequestException e when e.StatusCode == System.Net.HttpStatusCode.Unauthorized: + var returnUrl = Navigation.Uri; + Navigation.NavigateTo($"/login?returnUrl={returnUrl}"); + break; + default: + var message = exception.GetPromptMessage(); + ToastService.ShowError(message); + break; + } } private async void OnAuthenticationStateChanged(Task task) diff --git a/Source/Starfish.Webapp/Pages/Apps/Detail.razor b/Source/Starfish.Webapp/Pages/Apps/Detail.razor index 5ab5e5a..c90d44e 100644 --- a/Source/Starfish.Webapp/Pages/Apps/Detail.razor +++ b/Source/Starfish.Webapp/Pages/Apps/Detail.razor @@ -1,17 +1,86 @@ -@page "/apps/{id:long}" +@implements IDialogContentComponent - - @(Resources.IDS_MENU_TEXT_HOME) - @(Resources.IDS_MENU_TEXT_APPS) - @(Resources.IDS_BREADCRUMB_APPS_DETAIL) - +@inject IAppsApi AppsApi + + + + + + @Dialog.Instance.Parameters.Title + + + + + + @if (Loading) + { + + + + } + else + { + + + + + + + + + + + } + + + + + @(Resources.IDS_COMMON_CLOSE) + @code { [Parameter] - public long Id { get; set; } + public long Content { get; set; } [CascadingParameter] - private Task AuthenticationState { get; set; } + public FluentDialog Dialog { get; set; } = default!; + + private bool Loading { get; set; } + + private AppInfoDetailDto Data { get; } = new(); + + protected override async Task OnInitializedAsync() + { + try + { + Loading = true; + + await AppsApi.GetAsync(Content).ContinueWith(task => + { + task.WaitAndUnwrapException(); + var result = task.Result.EnsureSuccess(); + Data.Name = result.Name; + Data.Code = result.Code; + Data.Description = result.Description; + Data.Status = result.Status; + }); + } + catch (Exception exception) + { + exception.Send(); + } + finally + { + Loading = false; + } + } - private UserPrincipal User { get; set; } + private async Task CancelAsync() + { + await Dialog.CancelAsync(); + } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Apps/Edit.razor b/Source/Starfish.Webapp/Pages/Apps/Edit.razor index c20a460..4ea200d 100644 --- a/Source/Starfish.Webapp/Pages/Apps/Edit.razor +++ b/Source/Starfish.Webapp/Pages/Apps/Edit.razor @@ -2,7 +2,6 @@ @inject IAppsApi AppsApi @inject ITeamApi TeamApi -@inject IToastService ToastService @@ -30,11 +29,12 @@ + + @if (Content == 0) { - @@ -93,12 +93,8 @@ var tasks = new List { - LoadDetailAsync(Content) + Content == 0 ? LoadTeamsAsync() : LoadDetailAsync(Content) }; - if (Content == 0) - { - tasks.Add(LoadTeamsAsync()); - } await Task.WhenAll(tasks); } @@ -114,6 +110,7 @@ var request = new AppInfoUpdateDto() { Name = Name, + Code = Code, Description = Description }; await AppsApi.UpdateAsync(Content, request) @@ -145,8 +142,7 @@ } catch (Exception exception) { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); + exception.Send(); } finally { @@ -177,7 +173,8 @@ { TeamId = result[0].Id.ToString(); } - }); + }) + .Guard(); } private async Task LoadDetailAsync(long id) @@ -201,6 +198,10 @@ Code = result.Code; Name = result.Name; Description = result.Description; + }) + .Guard(async _ => + { + await Dialog.CancelAsync(); }); } diff --git a/Source/Starfish.Webapp/Pages/Apps/Index.razor b/Source/Starfish.Webapp/Pages/Apps/Index.razor index b3bac48..5a61602 100644 --- a/Source/Starfish.Webapp/Pages/Apps/Index.razor +++ b/Source/Starfish.Webapp/Pages/Apps/Index.razor @@ -3,7 +3,6 @@ @attribute [Authorize] @inject IDialogService DialogService -@inject IToastService ToastService @inject NavigationManager Navigation @inject IAppsApi Api @@ -16,31 +15,51 @@ - - + + - @(context.Name) + + @(context.Name) + + + + + + + - - - - - - - + + + + - + @@ -70,41 +89,37 @@ { List items = null; var tasks = new List - { + { Api.QueryAsync(Criteria, request.StartIndex, Pagination.ItemsPerPage, request.CancellationToken) - .ContinueWith(task => - { - task.WaitAndUnwrapException(request.CancellationToken); - items = task.Result.EnsureSuccess(); - }, request.CancellationToken) - }; + .EnsureSuccess(result=> items = result,request.CancellationToken) + }; if (request.StartIndex == 0) { - tasks.Add(Api.CountAsync(Criteria, request.CancellationToken) - .ContinueWith(task => - { - task.WaitAndUnwrapException(request.CancellationToken); - Total = task.Result.EnsureSuccess(); - }, request.CancellationToken)); + tasks.Add(Api.CountAsync(Criteria, request.CancellationToken).EnsureSuccess(request.CancellationToken)); } - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).Guard(); await Pagination.SetTotalItemCountAsync(Total); return GridItemsProviderResult.From(items, Total); }; } - private async Task HandleDetailClicked(long id) + private Task OnDetailButtonClicked(long id) + { + return DialogService.ShowDialogAsync(id, new DialogParameters { Title = Resources.IDS_APPS_DETAIL_DIALOG_TITLE, PreventDismissOnOverlayClick = true }); + } + + private async Task OnGotoSettingClicked(long id) { Navigation.NavigateTo($"/apps/{id}/settings"); await Task.CompletedTask; } - private async Task OnEditClicked(long id) + private async Task OnEditButtonClicked(long id) { var title = id == 0 ? Resources.IDS_APPS_EDIT_TITLE_ADD : Resources.IDS_APPS_EDIT_TITLE_EDIT; - var dialog = await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, Modal = true }); + var dialog = await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (result.Cancelled) { @@ -112,34 +127,22 @@ } } - private async Task OnDeleteClicked(long id, string name) + private async Task OnDeleteButtonClicked(long id, string name) { var confirmationMessage = string.Format(Resources.IDS_APPS_INDEX_REMOVE_CONFIRMATION_MESSAGE, name); - var confirmation = await DialogService.ShowConfirmationAsync(confirmationMessage, primaryText: Resources.IDS_COMMON_YES, secondaryText: Resources.IDS_COMMON_NO, title: Resources.IDS_APPS_INDEX_REMOVE_CONFIRMATION_TITLE); + var confirmation = await DialogService.ShowConfirmationAsync(confirmationMessage, primaryText: Resources.IDS_COMMON_YES, + secondaryText: Resources.IDS_COMMON_NO, title: Resources.IDS_APPS_INDEX_REMOVE_CONFIRMATION_TITLE); var result = await confirmation.Result; if (!result.Cancelled) { - try - { - await Api.DeleteAsync(id) - .ContinueWith(task => - { - task.WaitAndUnwrapException(); - task.Result.EnsureSuccess(); - }); - await Pagination.SetCurrentPageIndexAsync(0); - } - catch (Exception exception) - { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); - } - } + await Api.DeleteAsync(id).EnsureSuccess().Guard(); + await Pagination.SetCurrentPageIndexAsync(0); + } } private async Task OnResetSecretClicked(long id) { - await DialogService.ShowDialogAsync(id, new DialogParameters { Modal = true }); + await DialogService.ShowDialogAsync(id, new DialogParameters { PreventDismissOnOverlayClick = true }); } private async Task OnSearchClicked() @@ -147,4 +150,16 @@ await Pagination.SetCurrentPageIndexAsync(0); } + private async Task OnStatusChanged(AppInfoItemDto context, bool status) + { + try + { + await Api.ChangeStatusAsync(context.Id, status ? "enable" : "disable").EnsureSuccess(); + context.Status = status ? "Enabled" : "Disabled"; + } + catch (Exception exception) + { + exception.Send(); + } + } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Apps/ResetSecret.razor b/Source/Starfish.Webapp/Pages/Apps/ResetSecret.razor index e4bb95b..5e9b6c9 100644 --- a/Source/Starfish.Webapp/Pages/Apps/ResetSecret.razor +++ b/Source/Starfish.Webapp/Pages/Apps/ResetSecret.razor @@ -1,7 +1,6 @@ @implements IDialogContentComponent @inject IAppsApi AppsApi -@inject IToastService ToastService @@ -60,8 +59,7 @@ } catch (Exception exception) { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); + exception.Send(); } finally { diff --git a/Source/Starfish.Webapp/Pages/Logging/Index.razor b/Source/Starfish.Webapp/Pages/Logging/Index.razor index 62492a7..82052dd 100644 --- a/Source/Starfish.Webapp/Pages/Logging/Index.razor +++ b/Source/Starfish.Webapp/Pages/Logging/Index.razor @@ -54,24 +54,19 @@ private int Total { get; set; } protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - } - - private async Task OnSearchClicked() { _provider = async request => { List items = null; var tasks = new List - { + { LogsApi.QueryAsync(Criteria, request.StartIndex, Pagination.ItemsPerPage, request.CancellationToken) .ContinueWith(task => { task.WaitAndUnwrapException(request.CancellationToken); items = task.Result.EnsureSuccess(); }, request.CancellationToken) - }; + }; if (request.StartIndex == 0) { @@ -85,7 +80,7 @@ ); } - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).Guard(); await Pagination.SetTotalItemCountAsync(Total); return GridItemsProviderResult.From(items, Total); }; @@ -93,8 +88,13 @@ await Task.CompletedTask; } - private async Task OnGotoDetailClicked(OperateLogDto dto) + private Task OnSearchClicked() + { + return Pagination.SetCurrentPageIndexAsync(0); + } + + private Task OnGotoDetailClicked(OperateLogDto dto) { - await DialogService.ShowDialogAsync(dto, new DialogParameters { Title = Resources.IDS_LOGS_DETAIL_DIALOG_TITLE, Modal = true }); + return DialogService.ShowDialogAsync(dto, new DialogParameters { Title = Resources.IDS_LOGS_DETAIL_DIALOG_TITLE }); } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Setting/Edit.razor b/Source/Starfish.Webapp/Pages/Setting/Edit.razor index ef719d4..186be82 100644 --- a/Source/Starfish.Webapp/Pages/Setting/Edit.razor +++ b/Source/Starfish.Webapp/Pages/Setting/Edit.razor @@ -109,8 +109,7 @@ } catch (Exception exception) { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); + exception.Send(); } finally { @@ -131,7 +130,7 @@ task.WaitAndUnwrapException(); var value = Cryptography.Base64.Decrypt(task.Result.EnsureSuccess()); _editor.SetValue(value); - }); + }) + .Guard(); } - } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Setting/EditValue.razor b/Source/Starfish.Webapp/Pages/Setting/EditValue.razor index b650245..fc782c8 100644 --- a/Source/Starfish.Webapp/Pages/Setting/EditValue.razor +++ b/Source/Starfish.Webapp/Pages/Setting/EditValue.razor @@ -64,8 +64,7 @@ } catch (Exception exception) { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); + exception.Send(); } finally { diff --git a/Source/Starfish.Webapp/Pages/Setting/Index.razor b/Source/Starfish.Webapp/Pages/Setting/Index.razor index f72c107..ddd590e 100644 --- a/Source/Starfish.Webapp/Pages/Setting/Index.razor +++ b/Source/Starfish.Webapp/Pages/Setting/Index.razor @@ -33,9 +33,9 @@ - - - + + + @@ -49,22 +49,22 @@
- + - App name: @AppDetail?.Name - App code: @AppDetail?.Code - App name: @AppDetail?.StatusDescription + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_APP_NAME, AppDetail?.Name) + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_APP_CODE, AppDetail?.Code) + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_APP_STATUS, AppDetail?.StatusDescription) @AppDetail?.Description @if (SettingDetail != null) { - + - Version: @(SettingDetail?.Version ?? "--") - Publish: @(SettingDetail?.PublishTime) - Last update: @(SettingDetail?.UpdateTime) + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_SETTING_VERSION, SettingDetail?.Version ?? "--") + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_SETTING_PUBLISH_TIME, SettingDetail?.PublishTime) + @string.Format(Resources.IDS_SETTING_INDEX_PANEL_LABEL_SETTING_UPDATE_TIME, SettingDetail?.UpdateTime) } @@ -143,7 +143,7 @@ ); } - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).Guard(); await Pagination.SetTotalItemCountAsync(Total); return GridItemsProviderResult.From(items, Total); }; @@ -157,7 +157,7 @@ Pagination.SetCurrentPageIndexAsync(0), LoadSettingDetailAsync() }; - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).Guard(); } private Task LoadSettingDetailAsync(CancellationToken cancellationToken = default) @@ -167,7 +167,8 @@ { task.WaitAndUnwrapException(cancellationToken); SettingDetail = task.Result.EnsureSuccess(); - }, cancellationToken); + }, cancellationToken) + .Guard(); } private Task LoadAppDetailAsync() @@ -177,7 +178,8 @@ { task.WaitAndUnwrapException(); AppDetail = task.Result.EnsureSuccess(); - }); + }) + .Guard(); } private async Task OnEditJsonClicked() @@ -192,7 +194,7 @@ }; var title = SettingDetail == null ? Resources.IDS_SETTING_EDIT_DIALOG_TITLE_CREATE_FROM_JSON : Resources.IDS_SETTING_EDIT_DIALOG_TITLE_EDIT_AS_JSON; - var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = title, Width = "calc(100% - 2px)", Height = "calc(100% - 2px)" }); + var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = title, Width = "calc(100% - 2px)", Height = "calc(100% - 2px)", PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (!result.Cancelled) { @@ -213,7 +215,7 @@ var title = SettingDetail == null ? Resources.IDS_SETTING_EDIT_DIALOG_TITLE_CREATE_FROM_TEXT : Resources.IDS_SETTING_EDIT_DIALOG_TITLE_EDIT_AS_TEXT; - var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = title, Width = "calc(100% - 2px)", Height = "calc(100% - 2px)" }); + var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = title, Width = "calc(100% - 2px)", Height = "calc(100% - 2px)", PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (!result.Cancelled) { @@ -232,7 +234,7 @@ } }; - var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_EDIT_DIALOG_TITLE_EDIT_VALUE }); + var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_EDIT_DIALOG_TITLE_EDIT_VALUE, PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (!result.Cancelled) { @@ -243,7 +245,7 @@ private async Task OnPublishClicked() { var args = new EditDialogArgs(Id, Environment); - var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_PUBLISH_DIALOG_TITLE }); + var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_PUBLISH_DIALOG_TITLE, PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (!result.Cancelled) { @@ -254,11 +256,31 @@ private async Task OnSyncRedisClicked() { var args = new EditDialogArgs(Id, Environment); - var dialog = await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_SYNC_REDIS_DIALOG_TITLE }); + await DialogService.ShowDialogAsync(args, new DialogParameters { Title = Resources.IDS_SETTING_SYNC_REDIS_DIALOG_TITLE, PreventDismissOnOverlayClick = true }); } private async Task OnDeleteClicked() { + var confirmation = await DialogService.ShowConfirmationAsync(Resources.IDS_SETTING_DELETE_CONFIRM_MESSAGE, primaryText: Resources.IDS_COMMON_YES, secondaryText: Resources.IDS_COMMON_NO, title: Resources.IDS_SETTING_DELETE_CONFIRM_TITLE); + var result = await confirmation.Result; + if (!result.Cancelled) + { + try + { + await SettingApi.DeleteAsync(Id, Environment) + .ContinueWith(task => + { + task.WaitAndUnwrapException(); + task.Result.EnsureSuccess(); + }); + await Pagination.SetCurrentPageIndexAsync(0); + StateHasChanged(); + } + catch (Exception exception) + { + exception.Send(); + } + } } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Setting/Revision.razor b/Source/Starfish.Webapp/Pages/Setting/Revision.razor new file mode 100644 index 0000000..5cac8ba --- /dev/null +++ b/Source/Starfish.Webapp/Pages/Setting/Revision.razor @@ -0,0 +1,15 @@ +@page "/apps/{appId:long}/settings/{settingId}/revision" + + + @(Resources.IDS_MENU_TEXT_HOME) + @(Resources.IDS_MENU_TEXT_APPS) + @(Resources.IDS_BREADCRUMB_SETTING) + + +@code { + [Parameter] + public long AppId { get; set; } + + [Parameter] + public long SettingId { get; set; } +} \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Team/AppendMember.razor b/Source/Starfish.Webapp/Pages/Team/AppendMember.razor index 0e4bfe8..e9997d6 100644 --- a/Source/Starfish.Webapp/Pages/Team/AppendMember.razor +++ b/Source/Starfish.Webapp/Pages/Team/AppendMember.razor @@ -2,7 +2,6 @@ @inject ITeamApi TeamApi @inject IUserApi UserApi -@inject IToastService ToastService @(Resources.IDS_MENU_TEXT_HOME) @@ -22,7 +21,7 @@ @Data.Description @if (Data.OwnerId != Identity?.GetUserIdOfInt64()) { - + @(Resources.IDS_COMMON_OPERATIONS) @(Resources.IDS_TEAM_DETAIL_BUTTON_EDIT) @@ -167,15 +166,14 @@ } catch (Exception exception) { - var message = exception.GetPromptMessage(); - ToastService.ShowError(message); + exception.Send(); } - } + } } private async Task OnAppendMemberClicked() { - var reference = await DialogService.ShowDialogAsync(Id, new DialogParameters { Title = Resources.IDS_TEAM_APPEND_MEMBER_DIALOG_TITLE, Modal = true }); + var reference = await DialogService.ShowDialogAsync(Id, new DialogParameters { Title = Resources.IDS_TEAM_APPEND_MEMBER_DIALOG_TITLE, PreventDismissOnOverlayClick = true }); var result = await reference.Result; if (!result.Cancelled) { @@ -186,6 +184,7 @@ private async Task OnEditClicked() { - await DialogService.ShowDialogAsync(Id, new DialogParameters { Title = Resources.IDS_TEAM_EDIT_TITLE_EDIT, Modal = true }); + await DialogService.ShowDialogAsync(Id, new DialogParameters { Title = Resources.IDS_TEAM_EDIT_TITLE_EDIT, PreventDismissOnOverlayClick = true }); } + } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Pages/Team/Index.razor b/Source/Starfish.Webapp/Pages/Team/Index.razor index e8852e7..16fe23f 100644 --- a/Source/Starfish.Webapp/Pages/Team/Index.razor +++ b/Source/Starfish.Webapp/Pages/Team/Index.razor @@ -103,7 +103,7 @@ private async Task OnEditClicked(long id) { var title = id == 0 ? Resources.IDS_TEAM_EDIT_TITLE_ADD : Resources.IDS_TEAM_EDIT_TITLE_EDIT; - await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, Modal = true }); + await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, PreventDismissOnOverlayClick = true }); } private async Task OnSearchClicked() diff --git a/Source/Starfish.Webapp/Pages/User/Index.razor b/Source/Starfish.Webapp/Pages/User/Index.razor index 3d25179..e5a446b 100644 --- a/Source/Starfish.Webapp/Pages/User/Index.razor +++ b/Source/Starfish.Webapp/Pages/User/Index.razor @@ -80,7 +80,7 @@ ); } - await Task.WhenAll(tasks); + await Task.WhenAll(tasks).Guard(); await Pagination.SetTotalItemCountAsync(Total); return GridItemsProviderResult.From(items, Total); }; @@ -97,7 +97,7 @@ private async Task OnEditClicked(long id) { var title = id == 0 ? Resources.IDS_USER_DIALOG_TITLE_ADD : Resources.IDS_USER_DIALOG_TITLE_EDIT; - var dialog = await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, Modal = true }); + var dialog = await DialogService.ShowDialogAsync(id, new DialogParameters { Title = title, PreventDismissOnOverlayClick = true }); var result = await dialog.Result; if (!result.Cancelled) { @@ -112,7 +112,7 @@ private async Task OnResetPasswordClicked(long id) { - await DialogService.ShowDialogAsync(id, new DialogParameters { Modal = true }); + await DialogService.ShowDialogAsync(id, new DialogParameters { PreventDismissOnOverlayClick = true }); } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Properties/Resources.resx b/Source/Starfish.Webapp/Properties/Resources.resx index 3f3b178..1c4c236 100644 --- a/Source/Starfish.Webapp/Properties/Resources.resx +++ b/Source/Starfish.Webapp/Properties/Resources.resx @@ -117,6 +117,24 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + App detail + + + Code + + + Description + + + Name + + + Status + + + Team + Code @@ -150,6 +168,9 @@ Status + + Team + Are you sure to remove this app '{0}'? @@ -375,6 +396,36 @@ Sync to Redis + + Key + + + Value + + + App code: {0} + + + App name: {0} + + + App Status: {0} + + + Publish: {0:yyyy-MM-dd HH:mm:ss} + + + Updated: {0:yyyy-MM-dd HH:mm:ss} + + + Version: {0} + + + App information + + + Setting information + Publish diff --git a/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx b/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx index 89df8b6..217e1d5 100644 --- a/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx +++ b/Source/Starfish.Webapp/Properties/Resources.zh-Hans.resx @@ -117,6 +117,24 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 应用详情 + + + 应用代码 + + + 应用描述 + + + 应用名称 + + + 应用状态 + + + 团队 + 应用代码 @@ -150,6 +168,9 @@ 状态 + + 团队 + 确定要删除应用 '{0}'? @@ -375,6 +396,36 @@ 同步到Redis + + + + + + + + 代码:{0} + + + 名称:{0} + + + 状态:{0} + + + 发布:{0} + + + 修改:{0} + + + 版本:{0} + + + 应用信息 + + + 配置信息 + 发布 diff --git a/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx b/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx index 08130cd..d7daac8 100644 --- a/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx +++ b/Source/Starfish.Webapp/Properties/Resources.zh-Hant.resx @@ -1,76 +1,96 @@  + 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. + --> + + + + + + + + + + + + + + + + + + - + + @@ -89,14 +109,32 @@ text/microsoft-resx - 1.3 + 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 應用詳情 + + + 應用代碼 + + + 應用描述 + + + 應用名稱 + + + 應用狀態 + + + 團隊 + 應用代碼 @@ -130,6 +168,9 @@ 狀態 + + 團隊 + 確定要刪除此應用“{0}”嗎? @@ -355,6 +396,36 @@ 同步到Redis + + + + + + + + 代碼:{0} + + + 代碼:{0} + + + 狀態:{0} + + + 發布:{0} + + + 修改:{0} + + + 版本:{0} + + + 應用訊息 + + + 配置訊息 + 發布 diff --git a/Source/Starfish.Webapp/Rest/ApiResponseExtension.cs b/Source/Starfish.Webapp/Rest/ApiResponseExtension.cs index 9a74e01..9e24b8c 100644 --- a/Source/Starfish.Webapp/Rest/ApiResponseExtension.cs +++ b/Source/Starfish.Webapp/Rest/ApiResponseExtension.cs @@ -36,4 +36,35 @@ public static ApiResponseDetail GetDetail(this ApiException exception) return response; } + + public static Task EnsureSuccess(this Task task, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(task, nameof(task)); + return task.ContinueWith(t => + { + t.WaitAndUnwrapException(cancellationToken); + t.Result.EnsureSuccess(); + }, cancellationToken); + } + + public static Task EnsureSuccess(this Task> task, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(task, nameof(task)); + return task.ContinueWith(t => + { + t.WaitAndUnwrapException(cancellationToken); + return t.Result.EnsureSuccess(); + }, cancellationToken); + } + + public static Task EnsureSuccess(this Task> task, Action next, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(task, nameof(task)); + return task.ContinueWith(t => + { + t.WaitAndUnwrapException(cancellationToken); + var result = t.Result.EnsureSuccess(); + next?.Invoke(result); + }, cancellationToken); + } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Rest/Defines/IAppsApi.cs b/Source/Starfish.Webapp/Rest/Defines/IAppsApi.cs index 92db88b..3768382 100644 --- a/Source/Starfish.Webapp/Rest/Defines/IAppsApi.cs +++ b/Source/Starfish.Webapp/Rest/Defines/IAppsApi.cs @@ -25,4 +25,7 @@ internal interface IAppsApi [Delete("/api/apps/{id}")] Task DeleteAsync(long id, CancellationToken cancellationToken = default); + + [Put("/api/apps/{id}/{status}")] + Task ChangeStatusAsync(long id, string status, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Seedwork/Constants.cs b/Source/Starfish.Webapp/Seedwork/Constants.cs index b9fcf66..00286a9 100644 --- a/Source/Starfish.Webapp/Seedwork/Constants.cs +++ b/Source/Starfish.Webapp/Seedwork/Constants.cs @@ -2,6 +2,20 @@ internal class Constants { + public static bool IsDebug + { + get + { +#if DEBUG + return true; +#else + return false; +#endif + } + } + + + public static class LocalStorage { public const string AccessToken = "session_access_token"; diff --git a/Source/Starfish.Webapp/Seedwork/ExceptionExtensions.cs b/Source/Starfish.Webapp/Seedwork/ExceptionExtensions.cs index 1482abb..f198ef3 100644 --- a/Source/Starfish.Webapp/Seedwork/ExceptionExtensions.cs +++ b/Source/Starfish.Webapp/Seedwork/ExceptionExtensions.cs @@ -1,4 +1,5 @@ -using Nerosoft.Starfish.Webapp.Rest; +using CommunityToolkit.Mvvm.Messaging; +using Nerosoft.Starfish.Webapp.Rest; using Refit; namespace Nerosoft.Starfish.Webapp; @@ -31,4 +32,9 @@ public static string GetPromptMessage(this Exception exception) _ => exception.Message }; } + + public static void Send(this Exception exception, string token = Constants.Message.ExceptionThrown) + { + WeakReferenceMessenger.Default.Send(exception, token); + } } \ No newline at end of file diff --git a/Source/Starfish.Webapp/Seedwork/TaskExtensions.cs b/Source/Starfish.Webapp/Seedwork/TaskExtensions.cs new file mode 100644 index 0000000..4ac4436 --- /dev/null +++ b/Source/Starfish.Webapp/Seedwork/TaskExtensions.cs @@ -0,0 +1,44 @@ +using CommunityToolkit.Mvvm.Messaging; +using Nerosoft.Starfish.Webapp.Rest; +using Refit; + +namespace Nerosoft.Starfish.Webapp; + +internal static class TaskExtensions +{ + public static async Task Guard(this Task task, Func handler = null) + { + try + { + await task; + } + catch (Exception exception) + { + WeakReferenceMessenger.Default.Send(exception, Constants.Message.ExceptionThrown); + if (handler != null) + { + await handler(exception); + } + } + } + + public static async Task Guard(this Task task, Func next, Func handler = null) + { + try + { + var result = await task; + if (next != null) + { + await next(result); + } + } + catch (Exception exception) + { + WeakReferenceMessenger.Default.Send(exception, Constants.Message.ExceptionThrown); + if (handler != null) + { + await handler(exception); + } + } + } +} \ No newline at end of file diff --git a/Source/Starfish.Webapp/_Imports.razor b/Source/Starfish.Webapp/_Imports.razor index 7629a87..0d3d84c 100644 --- a/Source/Starfish.Webapp/_Imports.razor +++ b/Source/Starfish.Webapp/_Imports.razor @@ -1,4 +1,5 @@ @using System.Collections.ObjectModel +@using System.Diagnostics @using System.Net.Http @using System.Net.Http.Json @using Microsoft.AspNetCore.Authorization