Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #17

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/贡献 ⚡

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public Task<SettingDetailDto> GetDetailAsync(long appId, string environment, Can
.ContinueWith(t => t.Result.Result, cancellationToken);
}

/// <inheritdoc />
public Task<long> CreateAsync(long appId, string environment, string format, SettingEditDto data, CancellationToken cancellationToken = default)
{
var useCase = LazyServiceProvider.GetRequiredService<ISettingCreateUseCase>();
Expand All @@ -45,6 +46,7 @@ public Task<long> CreateAsync(long appId, string environment, string format, Set
.ContinueWith(t => t.Result, cancellationToken);
}

/// <inheritdoc />
public async Task UpdateAsync(long appId, string environment, string format, SettingEditDto data, CancellationToken cancellationToken = default)
{
var useCase = LazyServiceProvider.GetRequiredService<ISettingUpdateUseCase>();
Expand All @@ -60,6 +62,7 @@ public Task DeleteAsync(long appId, string environment, CancellationToken cancel
return useCase.ExecuteAsync(input, cancellationToken);
}

/// <inheritdoc />
public Task UpdateAsync(long appId, string environment, string key, string value, CancellationToken cancellationToken = default)
{
var useCase = LazyServiceProvider.GetRequiredService<ISettingValueUpdateUseCase>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using AutoMapper;
using Nerosoft.Starfish.Domain;
using Nerosoft.Starfish.Transit;
using Nerosoft.Starfish.UseCases;

namespace Nerosoft.Starfish.Application;

Expand All @@ -14,6 +15,8 @@ internal class AppsMappingProfile : Profile
/// </summary>
public AppsMappingProfile()
{
CreateMap<AppInfoItemModel, AppInfoItemDto>()
.ForMember(dest => dest.StatusDescription, opt => opt.MapFrom(src => src.Status.GetDescription(Resources.ResourceManager, Resources.Culture)));
CreateMap<AppInfo, AppInfoItemDto>()
.ForMember(dest => dest.StatusDescription, opt => opt.MapFrom(src => src.Status.GetDescription(Resources.ResourceManager, Resources.Culture)));
CreateMap<AppInfo, AppInfoDetailDto>()
Expand Down
63 changes: 47 additions & 16 deletions Source/Starfish.Service/UseCases/Apps/AppInfoQueryUseCase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Security.Authentication;
using Microsoft.EntityFrameworkCore;
using Nerosoft.Euonia.Application;
using Nerosoft.Euonia.Claims;
using Nerosoft.Starfish.Domain;
Expand Down Expand Up @@ -65,25 +66,55 @@ public Task<AppInfoQueryOutput> 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<AppInfoItemDto>()), cancellationToken);

IQueryable<AppInfo> Permission(IQueryable<AppInfo> query)
{
if (!_identity.IsInRole("SA"))
{
var userId = _identity.GetUserIdOfInt64();
var teamQuery = _repository.Context.Set<TeamMember>();
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<AppInfo>().Where(predicate)
join team in context.Set<Team>() 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<TeamMember>();
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<AppInfoItemDto>();
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; }
}
10 changes: 10 additions & 0 deletions Source/Starfish.Transit/Apps/AppInfoItemDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ public class AppInfoItemDto
/// </summary>
public long Id { get; set; }

/// <summary>
/// 所属团队Id
/// </summary>
public long TeamId { get; set; }

/// <summary>
/// 所属团队名称
/// </summary>
public string TeamName { get; set; }

/// <summary>
/// 名称
/// </summary>
Expand Down
93 changes: 63 additions & 30 deletions Source/Starfish.Webapp/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -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


<FluentDesignTheme Mode="DesignThemeModes.Dark" OfficeColor="OfficeColor.Office"/>
<FluentDesignTheme Mode="DesignThemeModes.Dark" OfficeColor="OfficeColor.Office" />
<FluentLayout>
<FluentHeader>
Starfish
<FluentSpacer/>
<FluentSpacer />
<div style="display: flex; gap: 10px;">
<FluentIcon Color="Color.Lightweight" Value="@(new Icons.Filled.Size20.Alert())"/>
<FluentIcon Color="Color.Lightweight" Value="@(new Icons.Filled.Size20.Alert())" />

<FluentIcon Color="Color.Lightweight" Value="@(new Icons.Filled.Size20.Settings())"/>
<FluentIcon Color="Color.Lightweight" Value="@(new Icons.Filled.Size20.Settings())" />

<FluentPersona Name="@(Identity?.Username ?? "starfish")" Id="persona"
ImageSize="36px"
Image="https://s.gravatar.com/avatar/38a519cc759f85fc814868d0656c0c18?s=480"
Status="PresenceStatus.Available"
StatusSize="PresenceBadgeSize.Small"/>
StatusSize="PresenceBadgeSize.Small" />
</div>
</FluentHeader>

Expand All @@ -36,27 +37,38 @@
<FluentNavLink Icon="@(new Icons.Regular.Size24.DocumentText())" Href="/logs">@(Resources.IDS_MENU_TEXT_LOGS)</FluentNavLink>
</FluentNavMenu>
<FluentBodyContent>
<ErrorBoundary>
<ChildContent>
<div class="content">
@Body
</div>
</ChildContent>
<ErrorContent Context="ex">
@if (ex is UnauthorizedAccessException)
{
<Redirect Uri="/login" />
}
else if(ex is HttpRequestException e && e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
<Redirect Uri="/login" />
}
else
{
<div class="blazor-error-boundary">@ex.Message</div>
}
</ErrorContent>
</ErrorBoundary>
@if (!Constants.IsDebug)
{
<ErrorBoundary>
<ChildContent>
<div class="content">
@Body
</div>
</ChildContent>

<ErrorContent Context="ex">
@if (ex is AuthenticationException)
{
<Redirect Uri="/login" />
}
else if (ex is HttpRequestException e && e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
<Redirect Uri="/login" />
}
else
{
<div class="blazor-error-boundary">@ex.Message</div>
}
</ErrorContent>

</ErrorBoundary>
}
else
{
<div class="content">
@Body
</div>
}
</FluentBodyContent>
</FluentStack>

Expand All @@ -69,8 +81,8 @@
</div>
</FluentFooter>

<FluentToastProvider MaxToastCount="10"/>
<FluentDialogProvider/>
<FluentToastProvider MaxToastCount="10" />
<FluentDialogProvider />
</FluentLayout>

@code {
Expand All @@ -87,6 +99,27 @@
StateHasChanged();

Authentication.AuthenticationStateChanged += OnAuthenticationStateChanged;

if (!WeakReferenceMessenger.Default.IsRegistered<MainLayout>(this))
{
WeakReferenceMessenger.Default.Register<Exception, string>(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<AuthenticationState> task)
Expand Down
Loading
Loading