Compare commits
2 Commits
1a48cd1712
...
3afafd4e91
| Author | SHA1 | Date | |
|---|---|---|---|
| 3afafd4e91 | |||
| b21dae1192 |
@ -18,6 +18,12 @@ namespace Flawless.Client.Remote
|
|||||||
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.5.5.0")]
|
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.5.5.0")]
|
||||||
public partial interface IFlawlessServer
|
public partial interface IFlawlessServer
|
||||||
{
|
{
|
||||||
|
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
|
||||||
|
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||||
|
[Headers("Content-Type: application/json")]
|
||||||
|
[Post("/api/admin/add_user")]
|
||||||
|
Task AddUser([Body] RegisterRequest body, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
|
/// <returns>A <see cref="Task"/> that completes when the request is finished.</returns>
|
||||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||||
[Post("/api/admin/superuser/{username}")]
|
[Post("/api/admin/superuser/{username}")]
|
||||||
@ -243,7 +249,7 @@ namespace Flawless.Client.Remote
|
|||||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||||
[Headers("Accept: text/plain, application/json, text/json")]
|
[Headers("Accept: text/plain, application/json, text/json")]
|
||||||
[Post("/api/repo/{userName}/{repositoryName}/fetch_depot")]
|
[Post("/api/repo/{userName}/{repositoryName}/fetch_depot")]
|
||||||
Task<ApiResponse<Stream>> FetchDepot(string userName, string repositoryName, [Query] string depotId);
|
Task<ApiResponse<Stream>> FetchDepot(string userName, string repositoryName, [Query] string depotId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <returns>OK</returns>
|
/// <returns>OK</returns>
|
||||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||||
|
|||||||
@ -275,11 +275,11 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
|
|
||||||
private async Task StartupTasksAsync()
|
private async Task StartupTasksAsync()
|
||||||
{
|
{
|
||||||
await RefreshRepositoryRoleInfoAsyncCommand.Execute();
|
await RefreshRepositoryRoleInfoAsync();
|
||||||
await RefreshRepositoryIssuesAsyncCommand.Execute();
|
await RefreshRepositoryIssuesAsync();
|
||||||
await RefreshStatisticDataCommand.Execute();
|
await RefreshStatisticDataAsync();
|
||||||
await RefreshWebhooksCommand.Execute();
|
await RefreshWebhooksAsync();
|
||||||
await DetectLocalChangesAsyncCommand.Execute();
|
await DetectLocalChangesAsync();
|
||||||
await RendererFileTreeAsync();
|
await RendererFileTreeAsync();
|
||||||
SyncCommitsFromRepository();
|
SyncCommitsFromRepository();
|
||||||
}
|
}
|
||||||
@ -659,6 +659,8 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
[ReactiveCommand]
|
[ReactiveCommand]
|
||||||
private async ValueTask RefreshRepositoryIssuesAsync()
|
private async ValueTask RefreshRepositoryIssuesAsync()
|
||||||
{
|
{
|
||||||
|
if (!IsReporterRole) return;
|
||||||
|
|
||||||
using var l = UIHelper.MakeLoading("Refreshing issues...");
|
using var l = UIHelper.MakeLoading("Refreshing issues...");
|
||||||
await RepositoryService.C.UpdateIssuesListFromServerAsync(Repository);
|
await RepositoryService.C.UpdateIssuesListFromServerAsync(Repository);
|
||||||
}
|
}
|
||||||
@ -756,6 +758,8 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
[ReactiveCommand]
|
[ReactiveCommand]
|
||||||
private async ValueTask DetectLocalChangesAsync()
|
private async ValueTask DetectLocalChangesAsync()
|
||||||
{
|
{
|
||||||
|
if (!IsDeveloperRole) return;
|
||||||
|
|
||||||
using var l = UIHelper.MakeLoading("Refreshing local changes...");
|
using var l = UIHelper.MakeLoading("Refreshing local changes...");
|
||||||
var ns = await Task.Run(async () =>
|
var ns = await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@ -773,7 +777,16 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
foreach (var n in LocalChangeSetRaw)
|
foreach (var n in LocalChangeSetRaw)
|
||||||
n.Included = true;
|
n.Included = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ReactiveCommand]
|
||||||
|
private void OpenFolder()
|
||||||
|
{
|
||||||
|
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() {
|
||||||
|
FileName = PathUtility.GetWorkspacePath(Api.C.Username.Value!, Repository.OwnerName, Repository.Name),
|
||||||
|
UseShellExecute = true,
|
||||||
|
Verb = "open"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[ReactiveCommand]
|
[ReactiveCommand]
|
||||||
private void DeselectAllChanges()
|
private void DeselectAllChanges()
|
||||||
@ -783,8 +796,10 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[ReactiveCommand]
|
[ReactiveCommand]
|
||||||
private async Task RefreshStatisticData()
|
private async Task RefreshStatisticDataAsync()
|
||||||
{
|
{
|
||||||
|
if (!IsOwnerRole) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var api = Api.C;
|
var api = Api.C;
|
||||||
@ -800,13 +815,12 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
|||||||
foreach (var dp in rsp.Depots)
|
foreach (var dp in rsp.Depots)
|
||||||
DepotsStats.Add(new DepotStatsInfo{ Id = dp.DepotName, Size = dp.DepotSize});
|
DepotsStats.Add(new DepotStatsInfo{ Id = dp.DepotName, Size = dp.DepotSize});
|
||||||
|
|
||||||
ByDay = new[]
|
ByDay = [
|
||||||
{
|
|
||||||
new ColumnSeries<DateTimePoint>
|
new ColumnSeries<DateTimePoint>
|
||||||
{
|
{
|
||||||
Values = rsp.CommitByDay.Select(k => new DateTimePoint(k.Day.LocalDateTime, k.Count)).ToList(),
|
Values = rsp.CommitByDay.Select(k => new DateTimePoint(k.Day.LocalDateTime, k.Count)).ToList(),
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,18 +2,14 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Threading;
|
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using Flawless.Client.Models;
|
using Flawless.Client.Models;
|
||||||
using Flawless.Client.Remote;
|
using Flawless.Client.Remote;
|
||||||
using Flawless.Client.Service;
|
using Flawless.Client.Service;
|
||||||
using Flawless.Client.ViewModels.ModalBox;
|
using Flawless.Client.ViewModels.ModalBox;
|
||||||
using Flawless.Client.Views.ModalBox;
|
using Flawless.Client.Views.ModalBox;
|
||||||
using LiveChartsCore;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.SourceGenerators;
|
using ReactiveUI.SourceGenerators;
|
||||||
using Refit;
|
using Refit;
|
||||||
@ -49,9 +45,9 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
|
|
||||||
[Reactive] private string _bio;
|
[Reactive] private string _bio;
|
||||||
|
|
||||||
[Reactive] private DateTime?
|
[Reactive] private DateTime
|
||||||
_logSearchFrom = null,
|
_logSearchFrom = DateTime.Now.AddDays(-1),
|
||||||
_logSearchTo = null;
|
_logSearchTo = DateTime.Now;
|
||||||
|
|
||||||
[Reactive] private int _page = 1;
|
[Reactive] private int _page = 1;
|
||||||
|
|
||||||
@ -72,10 +68,10 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
{
|
{
|
||||||
using (UIHelper.MakeLoading("Fetch server data..."))
|
using (UIHelper.MakeLoading("Fetch server data..."))
|
||||||
{
|
{
|
||||||
await RefreshUsersCommand.Execute();
|
await RefreshUsersAsync();
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
ServerBlacklist = sb.AppendJoin(",\n", await Api.C.Gateway.IpWhitelistGet()).ToString();
|
ServerWhitelist = sb.AppendJoin(",\n", await Api.C.Gateway.IpWhitelistGet()).ToString();
|
||||||
ServerBlacklist = sb.Clear().AppendJoin(",\n", await Api.C.Gateway.IpBlacklistGet()).ToString();
|
ServerBlacklist = sb.Clear().AppendJoin(",\n", await Api.C.Gateway.IpBlacklistGet()).ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +240,8 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
Username = user.Username,
|
Username = user.Username,
|
||||||
Email = user.Email,
|
Email = user.Email,
|
||||||
IsActive = user.IsActive,
|
IsActive = user.IsActive,
|
||||||
IsAdmin = user.IsAdmin ?? false
|
IsAdmin = user.IsAdmin ?? false,
|
||||||
|
CanEdit = user.Username != Api.C.Username.Value!
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,9 +254,12 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
[ReactiveCommand]
|
[ReactiveCommand]
|
||||||
private async Task CreateUserAsync()
|
private async Task CreateUserAsync()
|
||||||
{
|
{
|
||||||
var result = new UserCreateDialogViewModel();
|
|
||||||
result.Password = GenerateRandomPassword();
|
|
||||||
var opt = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
var opt = UIHelper.DefaultOverlayDialogOptionsYesNo();
|
||||||
|
var result = new UserCreateDialogViewModel
|
||||||
|
{
|
||||||
|
Password = GenerateRandomPassword()
|
||||||
|
};
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var r = await OverlayDialog.ShowModal<UserCreateDialogView, UserCreateDialogViewModel>(result, AppDefaultValues.HostId, opt);
|
var r = await OverlayDialog.ShowModal<UserCreateDialogView, UserCreateDialogViewModel>(result, AppDefaultValues.HostId, opt);
|
||||||
@ -271,14 +271,14 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Api.C.Gateway.Register(new RegisterRequest
|
await Api.C.Gateway.AddUser(new RegisterRequest
|
||||||
{
|
{
|
||||||
Username = result.Username,
|
Username = result.Username,
|
||||||
Password = result.Password,
|
Password = result.Password,
|
||||||
Email = result.Email
|
Email = result.Email
|
||||||
});
|
});
|
||||||
|
|
||||||
Users.Add(UserService.C.GetUserInfoAsync(result.Username)!);
|
await this.RefreshUsersAsync();
|
||||||
UIHelper.NotifySuccess($"User {result.Username} create successfully");
|
UIHelper.NotifySuccess($"User {result.Username} create successfully");
|
||||||
}
|
}
|
||||||
catch (ApiException ex)
|
catch (ApiException ex)
|
||||||
@ -311,11 +311,12 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
var newPassword = GenerateRandomPassword();
|
var newPassword = GenerateRandomPassword();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Api.C.Gateway.RenewPassword(new ResetPasswordRequest
|
await Api.C.Gateway.ResetPassword(new ResetPasswordRequest
|
||||||
{
|
{
|
||||||
Identity = username,
|
Identity = username,
|
||||||
NewPassword = newPassword
|
NewPassword = newPassword
|
||||||
});
|
});
|
||||||
|
|
||||||
await UIHelper.SimpleAlert($"Password has been reset to {newPassword}");
|
await UIHelper.SimpleAlert($"Password has been reset to {newPassword}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -386,7 +387,7 @@ public partial class SettingViewModel : RoutableViewModelBase
|
|||||||
{
|
{
|
||||||
await Api.C.Gateway.SuperuserPost(username, active);
|
await Api.C.Gateway.SuperuserPost(username, active);
|
||||||
Users.First(x => x.Username == username).IsAdmin = active;
|
Users.First(x => x.Username == username).IsAdmin = active;
|
||||||
UIHelper.NotifySuccess($"{username} has already {(active ? "enabled" : "disabled")}.");
|
UIHelper.NotifySuccess($"{username} has already set to {(active ? "superuser" : "nornmal user")}.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<DockPanel Margin="50">
|
<DockPanel Margin="50">
|
||||||
<Grid RowDefinitions="Auto, 18, Auto" ColumnDefinitions="*, Auto" DockPanel.Dock="Top">
|
<Grid RowDefinitions="Auto, 18, Auto" ColumnDefinitions="*, Auto" DockPanel.Dock="Top">
|
||||||
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
|
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
|
||||||
<Label Content="{Binding ServerFriendlyName, StringFormat='Server {0}', FallbackValue='Server LocalTest'}" FontSize="18" FontWeight="400"></Label>
|
<Label Content="{Binding ServerFriendlyName, FallbackValue='Unknown Server'}" FontSize="18" FontWeight="400"></Label>
|
||||||
<Label Content="Repositories" FontSize="32" FontWeight="600"></Label>
|
<Label Content="Repositories" FontSize="32" FontWeight="600"></Label>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
xmlns:vm="clr-namespace:Flawless.Client.ViewModels.ModalBox"
|
xmlns:vm="clr-namespace:Flawless.Client.ViewModels.ModalBox"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:DataType="vm:IssueEditDialogViewModel"
|
x:DataType="vm:IssueEditDialogViewModel"
|
||||||
MinWidth="400"
|
MinWidth="500"
|
||||||
x:Class="Flawless.Client.Views.ModalBox.IssueDetailEditView">
|
x:Class="Flawless.Client.Views.ModalBox.IssueDetailEditView">
|
||||||
|
|
||||||
<u:Form HorizontalAlignment="Stretch">
|
<u:Form HorizontalAlignment="Stretch">
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:Flawless.Client.ViewModels.ModalBox"
|
xmlns:vm="using:Flawless.Client.ViewModels.ModalBox"
|
||||||
x:Class="Flawless.Client.Views.ModalBox.UserCreateDialogView"
|
x:Class="Flawless.Client.Views.ModalBox.UserCreateDialogView"
|
||||||
|
MinWidth="400"
|
||||||
x:DataType="vm:UserCreateDialogViewModel">
|
x:DataType="vm:UserCreateDialogViewModel">
|
||||||
|
|
||||||
<Grid Margin="10" HorizontalAlignment="Stretch" RowDefinitions="Auto,Auto,Auto">
|
<Grid Margin="10" HorizontalAlignment="Stretch" RowDefinitions="Auto,Auto,Auto">
|
||||||
|
|||||||
@ -7,10 +7,10 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Flawless.Client.Views.RepositoryPage.RepoCommitPageView">
|
x:Class="Flawless.Client.Views.RepositoryPage.RepoCommitPageView">
|
||||||
<Grid ColumnDefinitions="2*, *">
|
<Grid ColumnDefinitions="2*, *">
|
||||||
<TreeDataGrid Grid.Column="0" Source="{Binding Commits}">
|
<TreeDataGrid Grid.Column="0" Source="{Binding Commits, Mode=TwoWay}">
|
||||||
<TreeDataGrid.ContextMenu>
|
<TreeDataGrid.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="View File Tree"/>
|
<!-- <MenuItem Header="View File Tree"/> -->
|
||||||
<MenuItem Header="Reset To">
|
<MenuItem Header="Reset To">
|
||||||
<MenuItem Header="Keep" Command="{Binding RevertFileTreeToSelectedCommitKeepCommand}"/>
|
<MenuItem Header="Keep" Command="{Binding RevertFileTreeToSelectedCommitKeepCommand}"/>
|
||||||
<MenuItem Header="Soft" Command="{Binding RevertFileTreeToSelectedCommitSoftCommand}"/>
|
<MenuItem Header="Soft" Command="{Binding RevertFileTreeToSelectedCommitSoftCommand}"/>
|
||||||
|
|||||||
@ -13,8 +13,10 @@
|
|||||||
<u:IconButton
|
<u:IconButton
|
||||||
Icon="{StaticResource SemiIconDownload}" Content="Pull" HorizontalAlignment="Stretch"
|
Icon="{StaticResource SemiIconDownload}" Content="Pull" HorizontalAlignment="Stretch"
|
||||||
Command="{Binding PullLatestRepositoryCommand}"/>
|
Command="{Binding PullLatestRepositoryCommand}"/>
|
||||||
|
<u:IconButton Icon="{StaticResource SemiIconExternalOpen}" Content="Open Folder"
|
||||||
|
Command="{Binding OpenFolderCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TreeDataGrid Grid.Row="1" Source="{Binding FileTree}">
|
<TreeDataGrid Grid.Row="1" Source="{Binding FileTree, Mode=TwoWay}">
|
||||||
</TreeDataGrid>
|
</TreeDataGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -97,11 +97,11 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Statics" IsVisible="{Binding IsOwnerRole}">
|
<TabItem Header="Statics" IsVisible="{Binding IsOwnerRole}">
|
||||||
<ScrollViewer Width="600" HorizontalAlignment="Stretch">
|
<ScrollViewer Width="600" HorizontalAlignment="Left" Margin="6">
|
||||||
<StackPanel Spacing="16" HorizontalAlignment="Stretch">
|
<StackPanel Spacing="16" HorizontalAlignment="Stretch">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||||
<u:IconButton Content="Refresh" Icon="{StaticResource SemiIconRefresh}"
|
<u:IconButton Content="Refresh" Icon="{StaticResource SemiIconRefresh}"
|
||||||
Command="{Binding RefreshWebhooksCommand}"/>
|
Command="{Binding RefreshStatisticDataCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
|
<StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
|
||||||
<Label>Every Day Commits</Label>
|
<Label>Every Day Commits</Label>
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
Command="{Binding SelectAllChangesCommand}"/>
|
Command="{Binding SelectAllChangesCommand}"/>
|
||||||
<u:IconButton Icon="{StaticResource SemiIconList}" Content="None"
|
<u:IconButton Icon="{StaticResource SemiIconList}" Content="None"
|
||||||
Command="{Binding DeselectAllChangesCommand}"/>
|
Command="{Binding DeselectAllChangesCommand}"/>
|
||||||
<!-- <u:IconButton Icon="{StaticResource SemiIconDownload}" Content="Open Folder" -->
|
<u:IconButton Icon="{StaticResource SemiIconExternalOpen}" Content="Open Folder"
|
||||||
<!-- Command="{Binding OpenFolderInSystemDefaultExplorerCommand}"/> -->
|
Command="{Binding OpenFolderCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TreeDataGrid Grid.Row="1" Grid.Column="0" Source="{Binding LocalChange}" CanUserSortColumns="True"/>
|
<TreeDataGrid Grid.Row="1" Grid.Column="0" Source="{Binding LocalChange}" CanUserSortColumns="True"/>
|
||||||
<Border Grid.Row="1" Grid.Column="2" Classes="Shadow" Theme="{StaticResource CardBorder}">
|
<Border Grid.Row="1" Grid.Column="2" Classes="Shadow" Theme="{StaticResource CardBorder}">
|
||||||
|
|||||||
@ -39,19 +39,10 @@
|
|||||||
<TabItem Header="Preference">
|
<TabItem Header="Preference">
|
||||||
<ScrollViewer Width="600" HorizontalAlignment="Left" Margin="6">
|
<ScrollViewer Width="600" HorizontalAlignment="Left" Margin="6">
|
||||||
<u:Form HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
<u:Form HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
<u:FormItem Label="Default Storage Location">
|
<!-- <u:FormItem Label="Default Storage Location"> -->
|
||||||
<u:PathPicker/>
|
<!-- <u:PathPicker/> -->
|
||||||
</u:FormItem>
|
<!-- </u:FormItem> -->
|
||||||
<!-- <u:FormGroup Header="Workspace Refresh"> -->
|
|
||||||
<!-- <StackPanel Spacing="4"> -->
|
|
||||||
<!-- <CheckBox Theme="{StaticResource CardCheckBox}" Content="Open Workspace" -->
|
|
||||||
<!-- IsChecked="{Binding SettingModel.RefreshWorkspaceOnOpen}"/> -->
|
|
||||||
<!-- <CheckBox Theme="{StaticResource CardCheckBox}" Content="Filesystem Changed" -->
|
|
||||||
<!-- IsChecked="{Binding SettingModel.RefreshWorkspaceOnFilesystemChanges}"/> -->
|
|
||||||
<!-- </StackPanel> -->
|
|
||||||
<!-- </u:FormGroup> -->
|
|
||||||
<u:FormGroup Header="Extern Tools">
|
<u:FormGroup Header="Extern Tools">
|
||||||
<!-- <u:PathPicker u:FormItem.Label="Open Folder"/> -->
|
|
||||||
<u:PathPicker u:FormItem.Label="Diff Tool"/>
|
<u:PathPicker u:FormItem.Label="Diff Tool"/>
|
||||||
</u:FormGroup>
|
</u:FormGroup>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
@ -92,36 +83,28 @@
|
|||||||
Command="{Binding CreateUserCommand}"/>
|
Command="{Binding CreateUserCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<DataGrid Grid.Row="1" ItemsSource="{Binding Users, Mode=TwoWay}" AutoGenerateColumns="False">
|
<ListBox Grid.Row="1" ItemsSource="{Binding Users, Mode=TwoWay}">
|
||||||
<DataGrid.Columns>
|
<ListBox.ItemTemplate>
|
||||||
<DataGridTextColumn Header="Username" Binding="{Binding Username}" Width="*"/>
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Vertical" Spacing="12" Margin="6">
|
||||||
<DataGridTextColumn Header="CreateAt" Binding="{Binding JoinDate}" Width="*"/>
|
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||||
|
<PathIcon Data="{StaticResource SemiIconCrown}" Height="12" Width="12" IsVisible="{Binding IsAdmin}"/>
|
||||||
<DataGridCheckBoxColumn Header="Active" Binding="{Binding IsActive}" Width="Auto"/>
|
<PathIcon Data="{StaticResource SemiIconMinusCircle}" Height="12" Width="12" IsVisible="{Binding !IsActive}"/>
|
||||||
|
<Label Content="{Binding Username}"/>
|
||||||
<DataGridCheckBoxColumn Header="Admin" Binding="{Binding IsAdmin}" Width="Auto"/>
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6" VerticalAlignment="Center" IsEnabled="{Binding CanEdit}">
|
||||||
<DataGridTemplateColumn Header="Operations" Width="2*">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
|
||||||
<Button Content="Enable"
|
<Button Content="Enable"
|
||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).InactivateUserCommand}"
|
|
||||||
CommandParameter="{Binding Username}"
|
|
||||||
IsEnabled="{Binding !CanEdit}"
|
|
||||||
IsVisible="{Binding IsActive}"/>
|
|
||||||
|
|
||||||
<Button Content="Disable"
|
|
||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).ActivateUserCommand}"
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).ActivateUserCommand}"
|
||||||
CommandParameter="{Binding Username}"
|
CommandParameter="{Binding Username}"
|
||||||
IsEnabled="{Binding !CanEdit}"
|
|
||||||
IsVisible="{Binding !IsActive}"/>
|
IsVisible="{Binding !IsActive}"/>
|
||||||
|
<Button Content="Disable"
|
||||||
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).InactivateUserCommand}"
|
||||||
|
CommandParameter="{Binding Username}"
|
||||||
|
IsVisible="{Binding IsActive}"/>
|
||||||
|
|
||||||
<Button Content="Promote"
|
<Button Content="Promote"
|
||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).PromoteUserCommand}"
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).PromoteUserCommand}"
|
||||||
CommandParameter="{Binding Username}"
|
CommandParameter="{Binding Username}"
|
||||||
IsEnabled="{Binding !CanEdit}"
|
|
||||||
IsVisible="{Binding !IsAdmin}"
|
IsVisible="{Binding !IsAdmin}"
|
||||||
Classes="Danger"/>
|
Classes="Danger"/>
|
||||||
|
|
||||||
@ -129,39 +112,35 @@
|
|||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).DemoteUserCommand}"
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).DemoteUserCommand}"
|
||||||
CommandParameter="{Binding Username}"
|
CommandParameter="{Binding Username}"
|
||||||
IsVisible="{Binding IsAdmin}"
|
IsVisible="{Binding IsAdmin}"
|
||||||
IsEnabled="{Binding !CanEdit}"
|
|
||||||
Classes="Danger"/>
|
Classes="Danger"/>
|
||||||
|
|
||||||
<Button Content="Delete"
|
<Button Content="Delete"
|
||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).DeleteUserCommand}"
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).DeleteUserCommand}"
|
||||||
CommandParameter="{Binding Username}"
|
CommandParameter="{Binding Username}"
|
||||||
IsEnabled="{Binding !CanEdit}"
|
|
||||||
Classes="Danger"/>
|
Classes="Danger"/>
|
||||||
|
|
||||||
<Button Content="Reset Password"
|
<Button Content="Reset Password"
|
||||||
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).ForceUpdateUserPasswordCommand}"
|
Command="{Binding $parent[views:SettingView].((vm:SettingViewModel)DataContext).ForceUpdateUserPasswordCommand}"
|
||||||
CommandParameter="{Binding Username}"
|
CommandParameter="{Binding Username}"/>
|
||||||
IsEnabled="{Binding !CanEdit}"/>
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</StackPanel>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn>
|
</ListBox.ItemTemplate>
|
||||||
</DataGrid.Columns>
|
</ListBox>
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem IsVisible="{Binding LoginUser.IsAdmin}" Header="Logfile">
|
<TabItem IsVisible="{Binding LoginUser.IsAdmin}" Header="Logfile">
|
||||||
<StackPanel Spacing="8" Orientation="Vertical" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
<StackPanel Spacing="8" Orientation="Vertical" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="4">
|
||||||
<DatePicker SelectedDate="{Binding LogSearchFrom}"/>
|
<DatePicker SelectedDate="{Binding LogSearchFrom}"/>
|
||||||
|
<Label Content=" → "/>
|
||||||
<DatePicker SelectedDate="{Binding LogSearchTo}"/>
|
<DatePicker SelectedDate="{Binding LogSearchTo}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
<u:EnumSelector SelectedValue="{Binding Loglevel}"/>
|
<u:EnumSelector SelectedValue="{Binding Loglevel, Mode=TwoWay}"/>
|
||||||
<NumericUpDown Value="{Binding PageSize}" Minimum="10" Maximum="100"/>
|
<NumericUpDown Value="{Binding PageSize, Mode=TwoWay}" Minimum="10" Maximum="100"/>
|
||||||
<NumericUpDown Value="{Binding Page}" Minimum="1"/>
|
<NumericUpDown Value="{Binding Page, Mode=TwoWay}" Minimum="1"/>
|
||||||
<u:IconButton Icon="{StaticResource SemiIconSearch}"
|
<u:IconButton Icon="{StaticResource SemiIconSearch}"
|
||||||
Command="{Binding DownloadServerLogCommand}"/>
|
Command="{Binding DownloadServerLogCommand}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace Flawless.Server.Controllers;
|
|||||||
public class AdminController(
|
public class AdminController(
|
||||||
UserManager<AppUser> userManager,
|
UserManager<AppUser> userManager,
|
||||||
AccessControlService accessControlService,
|
AccessControlService accessControlService,
|
||||||
|
ILogger<AdminController> logger,
|
||||||
AppDbContext dbContext) : ControllerBase
|
AppDbContext dbContext) : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -23,6 +24,29 @@ public class AdminController(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("add_user")]
|
||||||
|
public async Task<IActionResult> AddUserAsync(RegisterRequest request)
|
||||||
|
{
|
||||||
|
var user = new AppUser
|
||||||
|
{
|
||||||
|
UserName = request.Username,
|
||||||
|
Email = request.Email,
|
||||||
|
EmailConfirmed = true,
|
||||||
|
CreatedOn = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
|
||||||
|
user.RenewSecurityStamp();
|
||||||
|
var result = await userManager.CreateAsync(user, request.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
logger.LogInformation("User '{0}' created (SUPERUSER REGISTER)", user.UserName);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation("User '{0}' NOT created (SUPERUSER REGISTER) : {1}", user.UserName, result.Errors);
|
||||||
|
return BadRequest(new FailedResponse(result.Errors));
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("superuser/{username}")]
|
[HttpPost("superuser/{username}")]
|
||||||
public async Task<IActionResult> SetSuperuserAsync(string username, bool toSuper)
|
public async Task<IActionResult> SetSuperuserAsync(string username, bool toSuper)
|
||||||
{
|
{
|
||||||
@ -59,7 +83,7 @@ public class AdminController(
|
|||||||
var t = await TestIfValid();
|
var t = await TestIfValid();
|
||||||
if (t != null) return t;
|
if (t != null) return t;
|
||||||
|
|
||||||
return await userManager.Users.Select(x => new UserInfoResponse
|
var r = await userManager.Users.Select(x => new UserInfoResponse
|
||||||
{
|
{
|
||||||
Authorized = true,
|
Authorized = true,
|
||||||
Username = x.UserName,
|
Username = x.UserName,
|
||||||
@ -71,8 +95,10 @@ public class AdminController(
|
|||||||
Email = x.Email,
|
Email = x.Email,
|
||||||
Phone = x.PhoneNumber,
|
Phone = x.PhoneNumber,
|
||||||
IsAdmin = x.Admin,
|
IsAdmin = x.Admin,
|
||||||
IsActive = x.LockoutEnabled
|
IsActive = !x.LockoutEnabled
|
||||||
}).ToArrayAsync();
|
}).ToArrayAsync();
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("user/delete/{username}")]
|
[HttpPost("user/delete/{username}")]
|
||||||
@ -93,6 +119,9 @@ public class AdminController(
|
|||||||
[HttpPost("user/enable/{username}")]
|
[HttpPost("user/enable/{username}")]
|
||||||
public async Task<IActionResult> EnableUserAsync(string username)
|
public async Task<IActionResult> EnableUserAsync(string username)
|
||||||
{
|
{
|
||||||
|
var t = await TestIfValid();
|
||||||
|
if (t != null) return t;
|
||||||
|
|
||||||
var user = await userManager.FindByNameAsync(username);
|
var user = await userManager.FindByNameAsync(username);
|
||||||
|
|
||||||
if (user == null) return BadRequest(new FailedResponse("User does not exist!"));
|
if (user == null) return BadRequest(new FailedResponse("User does not exist!"));
|
||||||
@ -124,11 +153,10 @@ public class AdminController(
|
|||||||
if (t != null) return t;
|
if (t != null) return t;
|
||||||
|
|
||||||
if (r.Identity == null) return BadRequest(new FailedResponse("Identity (User Id) is not set!"));
|
if (r.Identity == null) return BadRequest(new FailedResponse("Identity (User Id) is not set!"));
|
||||||
var user = await userManager.FindByIdAsync(r.Identity);
|
var user = await userManager.FindByNameAsync(r.Identity);
|
||||||
|
|
||||||
if (user == null) return BadRequest(new FailedResponse("Identity (User Id) does not exist!"));
|
if (user == null) return BadRequest(new FailedResponse("Identity (User Id) does not exist!"));
|
||||||
var resetToken = await userManager.GeneratePasswordResetTokenAsync(user);
|
var result = await userManager.ResetPasswordAsync(user, "", r.NewPassword);
|
||||||
var result = await userManager.ResetPasswordAsync(user, resetToken, r.NewPassword);
|
|
||||||
|
|
||||||
if (!result.Succeeded) return BadRequest(new FailedResponse(result.Errors));
|
if (!result.Succeeded) return BadRequest(new FailedResponse(result.Errors));
|
||||||
return Ok();
|
return Ok();
|
||||||
@ -174,26 +202,18 @@ public class AdminController(
|
|||||||
|
|
||||||
[HttpGet("logs")]
|
[HttpGet("logs")]
|
||||||
public async Task<ActionResult<IEnumerable<LogEntryResponse>>> GetSystemLogsAsync(
|
public async Task<ActionResult<IEnumerable<LogEntryResponse>>> GetSystemLogsAsync(
|
||||||
[FromQuery] DateTime? startTime = null,
|
[FromQuery] DateTime startTime,
|
||||||
[FromQuery] DateTime? endTime = null,
|
[FromQuery] DateTime endTime,
|
||||||
[FromQuery] LogLevel? level = null,
|
[FromQuery] LogLevel level,
|
||||||
[FromQuery] int page = 1,
|
[FromQuery] int page,
|
||||||
[FromQuery] int pageSize = 50)
|
[FromQuery] int pageSize)
|
||||||
{
|
{
|
||||||
var t = await TestIfValid();
|
var t = await TestIfValid();
|
||||||
if (t != null) return t;
|
if (t != null) return t;
|
||||||
|
|
||||||
var query = dbContext.SystemLogs.AsQueryable();
|
var query = dbContext.SystemLogs.Where(x =>
|
||||||
|
x.Timestamp >= startTime && x.Timestamp <= endTime && x.LogLevel >= level
|
||||||
// 时间过滤
|
).AsQueryable();
|
||||||
if (startTime.HasValue)
|
|
||||||
query = query.Where(l => l.Timestamp >= startTime);
|
|
||||||
if (endTime.HasValue)
|
|
||||||
query = query.Where(l => l.Timestamp <= endTime);
|
|
||||||
|
|
||||||
// 日志级别过滤
|
|
||||||
if (level.HasValue && level.Value != LogLevel.None)
|
|
||||||
query = query.Where(l => l.LogLevel == level.Value);
|
|
||||||
|
|
||||||
// 分页处理
|
// 分页处理
|
||||||
var totalCount = await query.CountAsync();
|
var totalCount = await query.CountAsync();
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
"CoreDb": "Server=localhost;Port=5432;User Id=postgres;Database=flawless"
|
"CoreDb": "Server=localhost;Port=5432;User Id=postgres;Database=flawless"
|
||||||
},
|
},
|
||||||
"LocalStoragePath": "/Users/cardidi/flawless-data",
|
"LocalStoragePath": "/Users/cardidi/flawless-data",
|
||||||
"User": {
|
"UseWebHook": true,
|
||||||
"PublicRegister": true
|
"AllowPublicRegistration": true,
|
||||||
},
|
"ServerName": "Cardidi Private Area",
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"SecretKey": "your_256bit_security_key_at_here_otherwise_not_bootable",
|
"SecretKey": "your_256bit_security_key_at_here_otherwise_not_bootable",
|
||||||
"Issuer": "test",
|
"Issuer": "test",
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
"CoreDb": "Server=localhost;Port=5432;User Id=postgres;Database=flawless"
|
"CoreDb": "Server=localhost;Port=5432;User Id=postgres;Database=flawless"
|
||||||
},
|
},
|
||||||
"LocalStoragePath": "/Users/cardidi/flawless-data",
|
"LocalStoragePath": "/Users/cardidi/flawless-data",
|
||||||
"User": {
|
"UseWebHook": true,
|
||||||
"PublicRegister": true
|
"AllowPublicRegistration": true,
|
||||||
},
|
"ServerName": "Cardidi Private Area",
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"SecretKey": "your_256bit_security_key_at_here_otherwise_not_bootable",
|
"SecretKey": "your_256bit_security_key_at_here_otherwise_not_bootable",
|
||||||
"Issuer": "test",
|
"Issuer": "test",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user