fix: Adjust api namespace
feat: Add Avalonia client base.
This commit is contained in:
parent
437b5c6ab8
commit
cdcd1d62ab
13
.config/dotnet-tools.json
Normal file
13
.config/dotnet-tools.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"nswag.consolecore": {
|
||||
"version": "14.2.0",
|
||||
"commands": [
|
||||
"nswag"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@
|
||||
<option name="projectPerEditor">
|
||||
<map>
|
||||
<entry key="Flawless.Client.Avanonia/Views/MainWindow.axaml" value="Flawless.Client.Avanonia/Flawless.Client.Avanonia.csproj" />
|
||||
<entry key="Flawless.Client/App.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/MainWindow.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Communication", "F
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Server", "Flawless.Server\Flawless.Server.csproj", "{66142212-034C-4702-92FE-5C625D725048}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Client", "Flawless.Client\Flawless.Client.csproj", "{CEC2183E-0097-4972-BBEB-7CE3C6D874B9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -36,5 +38,9 @@ Global
|
||||
{66142212-034C-4702-92FE-5C625D725048}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{66142212-034C-4702-92FE-5C625D725048}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{66142212-034C-4702-92FE-5C625D725048}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CEC2183E-0097-4972-BBEB-7CE3C6D874B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CEC2183E-0097-4972-BBEB-7CE3C6D874B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CEC2183E-0097-4972-BBEB-7CE3C6D874B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CEC2183E-0097-4972-BBEB-7CE3C6D874B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActionMethodExecutor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fdffdaf205cf54e098aa7d66ba76b38621de920_003F19_003Fe6aa02ee_003FActionMethodExecutor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AApplication_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0ceaca09f3944680b668dee8e1e0370b100a00_003F22_003Fcb1aca4b_003FApplication_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArgumentOutOfRangeException_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F82_003F5d81019e_003FArgumentOutOfRangeException_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssert_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea501b1a950043b99f3df638f1824d6143a18_003Fb8_003Fb16d6a68_003FAssert_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAsyncValueTaskMethodBuilder_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003Fa5_003Ff3a8130e_003FAsyncValueTaskMethodBuilder_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
15
Flawless.Client/App.axaml
Normal file
15
Flawless.Client/App.axaml
Normal file
@ -0,0 +1,15 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="Flawless.Client.App"
|
||||
xmlns:local="using:Flawless.Client"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
40
Flawless.Client/App.axaml.cs
Normal file
40
Flawless.Client/App.axaml.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Flawless.Client.Remote;
|
||||
using Flawless.Client.ViewModels;
|
||||
using Flawless.Client.Views;
|
||||
using Refit;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public IFlawlessServer ApiGateway { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
ApiGateway = RestService.For<IFlawlessServer>(new HttpClient(new AuthHeaderHandler())
|
||||
{
|
||||
BaseAddress = new Uri("http://localhost:5256/"),
|
||||
Timeout = TimeSpan.FromSeconds(60)
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
};
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
BIN
Flawless.Client/Assets/avalonia-logo.ico
Normal file
BIN
Flawless.Client/Assets/avalonia-logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
52
Flawless.Client/AuthHeaderHandler.cs
Normal file
52
Flawless.Client/AuthHeaderHandler.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flawless.Client.Remote;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
public class AuthHeaderHandler : DelegatingHandler
|
||||
{
|
||||
private string? AuthenticationHeader { get; set; }
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await SendCommandAsync(request, cancellationToken);
|
||||
var retryCount = 0;
|
||||
|
||||
while (response.Headers.TryGetValues("Token-Expired", out var expired) && expired.Any(s => s == "true"))
|
||||
{
|
||||
if (retryCount++ > 3)
|
||||
{
|
||||
AuthenticationHeader = null;
|
||||
throw new TimeoutException("Too many retries, login info was cleared");
|
||||
}
|
||||
|
||||
var refreshRequest = new HttpRequestMessage(HttpMethod.Post, "api/auth/refresh");
|
||||
var refresh = await base.SendAsync(refreshRequest, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode) throw new ApplicationException("Login is expired and require login!");
|
||||
|
||||
await using var st = await refresh.Content.ReadAsStreamAsync(cancellationToken);
|
||||
var tk = await JsonSerializer.DeserializeAsync<TokenInfo>(st, cancellationToken: cancellationToken)
|
||||
?? throw new ApplicationException("Not able to refresh token, please login again!");
|
||||
|
||||
AuthenticationHeader = tk.Token;
|
||||
response = await SendCommandAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private Task<HttpResponseMessage> SendCommandAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Prefill this header
|
||||
if (AuthenticationHeader != null)
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationHeader);
|
||||
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
36
Flawless.Client/Flawless.Client.csproj
Normal file
36
Flawless.Client/Flawless.Client.csproj
Normal file
@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
<Folder Include="Service\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.1" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.1" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.1">
|
||||
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
||||
<PackageReference Include="Refit" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flawless.Communication\Flawless.Communication.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
23
Flawless.Client/Program.cs
Normal file
23
Flawless.Client/Program.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using System;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
sealed class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
}
|
||||
458
Flawless.Client/Service/Remote_Generated.cs
Normal file
458
Flawless.Client/Service/Remote_Generated.cs
Normal file
@ -0,0 +1,458 @@
|
||||
// <auto-generated>
|
||||
// This code was generated by Refitter.
|
||||
// </auto-generated>
|
||||
|
||||
|
||||
using Refit;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#nullable enable annotations
|
||||
|
||||
namespace Flawless.Client.Remote
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCode("Refitter", "1.5.2.0")]
|
||||
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>
|
||||
[Post("/api/admin/user/delete/{username}")]
|
||||
Task Delete(string username, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/admin/user/enable/{username}")]
|
||||
Task Enable(string username, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/admin/user/disable/{username}")]
|
||||
Task Disable(string username, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/admin/user/reset_password")]
|
||||
Task ResetPassword([Body] ResetPasswordRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/auth/register")]
|
||||
Task Register([Body] RegisterRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <returns>OK</returns>
|
||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||
[Headers("Accept: text/plain, application/json, text/json")]
|
||||
[Post("/api/auth/login")]
|
||||
Task<TokenInfo> Login([Body] LoginRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <returns>OK</returns>
|
||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||
[Headers("Accept: text/plain, application/json, text/json")]
|
||||
[Post("/api/auth/refresh")]
|
||||
Task<TokenInfo> Refresh([Body] TokenInfo body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/auth/logout_all")]
|
||||
Task LogoutAll(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/auth/renew_password")]
|
||||
Task RenewPassword([Body] ResetPasswordRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/")]
|
||||
Task Index(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/delete_repo")]
|
||||
Task DeleteRepo(string repositoryName, string userName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/get_info")]
|
||||
Task GetInfo(string repositoryName, string userName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/archive_repo")]
|
||||
Task ArchiveRepo(string repositoryName, string userName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/unarchive_repo")]
|
||||
Task UnarchiveRepo(string repositoryName, string userName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/get_users")]
|
||||
Task GetUsers(string repositoryName, string userName, [Body] QueryPagesRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/update_user")]
|
||||
Task UpdateUser(string repositoryName, string userName, [Body] RepoUserRole body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/delete_user")]
|
||||
Task DeleteUser(string repositoryName, string userName, [Body] RepoUserRole body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/fetch_manifest")]
|
||||
Task FetchManifest(string userName, string repositoryName, [Query] string commitId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/fetch_depot")]
|
||||
Task FetchDepot(string userName, string repositoryName, [Query] string depotId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/list_commit")]
|
||||
Task ListCommit(string userName, string repositoryName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/list_locked_files")]
|
||||
Task ListLockedFiles(string userName, string repositoryName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo/{userName}/{repositoryName}/peek_commit")]
|
||||
Task PeekCommit(string userName, string repositoryName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/lock_file")]
|
||||
Task LockFile(string userName, string repositoryName, [Query] string path, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo/{userName}/{repositoryName}/unlock_file")]
|
||||
Task UnlockFile(string userName, string repositoryName, [Query] string path, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Multipart]
|
||||
[Post("/api/repo/{userName}/{repositoryName}/create_commit")]
|
||||
Task CreateCommit(string userName, string repositoryName, StreamPart depot, string message, IEnumerable<WorkspaceFile> workspaceSnapshot, IEnumerable<string> requiredDepots, string mainDepotId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/repo_list")]
|
||||
Task RepoList([Query, AliasAs("Offset")] int offset, [Query, AliasAs("Length")] int length, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/repo_create")]
|
||||
Task RepoCreate([Query] string repositoryName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/user/update_info")]
|
||||
Task UpdateInfo([Body] UserInfoModifyResponse body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/user/update_email")]
|
||||
Task UpdateEmail([Body] UserContactModifyResponse body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Post("/api/user/update_phone")]
|
||||
Task UpdatePhone([Body] UserContactModifyResponse body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <returns>OK</returns>
|
||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||
[Headers("Accept: text/plain, application/json, text/json")]
|
||||
[Get("/api/user/get_info")]
|
||||
Task<UserInfoResponse> GetInfo([Query] string username, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <returns>OK</returns>
|
||||
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
|
||||
[Headers("Accept: text/plain, application/json, text/json")]
|
||||
[Get("/api/user/query_info")]
|
||||
Task<UserInfoResponsePagedResponse> QueryInfo([Query] string keyword, [Body] QueryPagesRequest body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <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>
|
||||
[Get("/api/user/delete")]
|
||||
Task Delete(CancellationToken cancellationToken = default);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
|
||||
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
|
||||
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
|
||||
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||
#pragma warning disable 612 // Disable "CS0612 '...' is obsolete"
|
||||
#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null"
|
||||
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
|
||||
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
|
||||
#pragma warning disable 8603 // Disable "CS8603 Possible null reference return"
|
||||
#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter"
|
||||
#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type"
|
||||
#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)."
|
||||
|
||||
namespace Flawless.Client.Remote
|
||||
{
|
||||
using System = global::System;
|
||||
|
||||
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class LoginRequest
|
||||
{
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonPropertyName("password")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Password { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class QueryPagesRequest
|
||||
{
|
||||
|
||||
[JsonPropertyName("offset")]
|
||||
public int Offset { get; set; }
|
||||
|
||||
[JsonPropertyName("length")]
|
||||
public int Length { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class RegisterRequest
|
||||
{
|
||||
|
||||
[JsonPropertyName("email")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonPropertyName("password")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Password { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class RepoUserRole
|
||||
{
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonPropertyName("role")]
|
||||
public RepositoryRole Role { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public enum RepositoryRole
|
||||
{
|
||||
|
||||
_0 = 0,
|
||||
|
||||
_1 = 1,
|
||||
|
||||
_2 = 2,
|
||||
|
||||
_3 = 3,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class ResetPasswordRequest
|
||||
{
|
||||
|
||||
[JsonPropertyName("identity")]
|
||||
public string Identity { get; set; }
|
||||
|
||||
[JsonPropertyName("oldPassword")]
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
[JsonPropertyName("newPassword")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class TokenInfo
|
||||
{
|
||||
|
||||
[JsonPropertyName("token")]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Token { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class UserContactModifyResponse
|
||||
{
|
||||
|
||||
[JsonPropertyName("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonPropertyName("phone")]
|
||||
public string Phone { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class UserInfoModifyResponse
|
||||
{
|
||||
|
||||
[JsonPropertyName("nickName")]
|
||||
public string NickName { get; set; }
|
||||
|
||||
[JsonPropertyName("gender")]
|
||||
public UserSex Gender { get; set; }
|
||||
|
||||
[JsonPropertyName("bio")]
|
||||
public string Bio { get; set; }
|
||||
|
||||
[JsonPropertyName("publicEmail")]
|
||||
public bool? PublicEmail { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class UserInfoResponse
|
||||
{
|
||||
|
||||
[JsonPropertyName("authorized")]
|
||||
public bool Authorized { get; set; }
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonPropertyName("nickName")]
|
||||
public string NickName { get; set; }
|
||||
|
||||
[JsonPropertyName("gender")]
|
||||
public UserSex Gender { get; set; }
|
||||
|
||||
[JsonPropertyName("bio")]
|
||||
public string Bio { get; set; }
|
||||
|
||||
[JsonPropertyName("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonPropertyName("phone")]
|
||||
public string Phone { get; set; }
|
||||
|
||||
[JsonPropertyName("publicEmail")]
|
||||
public bool? PublicEmail { get; set; }
|
||||
|
||||
[JsonPropertyName("createdAt")]
|
||||
public System.DateTimeOffset? CreatedAt { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class UserInfoResponsePagedResponse
|
||||
{
|
||||
|
||||
[JsonPropertyName("offset")]
|
||||
public int Offset { get; set; }
|
||||
|
||||
[JsonPropertyName("length")]
|
||||
public int Length { get; set; }
|
||||
|
||||
[JsonPropertyName("total")]
|
||||
public int? Total { get; set; }
|
||||
|
||||
[JsonPropertyName("data")]
|
||||
public ICollection<UserInfoResponse> Data { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public enum UserSex
|
||||
{
|
||||
|
||||
_0 = 0,
|
||||
|
||||
_1 = 1,
|
||||
|
||||
_2 = 2,
|
||||
|
||||
_3 = 3,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class WorkspaceFile
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||
public partial class FileParameter
|
||||
{
|
||||
public FileParameter(System.IO.Stream data)
|
||||
: this (data, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
public FileParameter(System.IO.Stream data, string fileName)
|
||||
: this (data, fileName, null)
|
||||
{
|
||||
}
|
||||
|
||||
public FileParameter(System.IO.Stream data, string fileName, string contentType)
|
||||
{
|
||||
Data = data;
|
||||
FileName = fileName;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public System.IO.Stream Data { get; private set; }
|
||||
|
||||
public string FileName { get; private set; }
|
||||
|
||||
public string ContentType { get; private set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma warning restore 108
|
||||
#pragma warning restore 114
|
||||
#pragma warning restore 472
|
||||
#pragma warning restore 612
|
||||
#pragma warning restore 1573
|
||||
#pragma warning restore 1591
|
||||
#pragma warning restore 8073
|
||||
#pragma warning restore 3016
|
||||
#pragma warning restore 8603
|
||||
#pragma warning restore 8604
|
||||
#pragma warning restore 8625
|
||||
30
Flawless.Client/ViewLocator.cs
Normal file
30
Flawless.Client/ViewLocator.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Flawless.Client.ViewModels;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
public class ViewLocator : IDataTemplate
|
||||
{
|
||||
public Control? Build(object? param)
|
||||
{
|
||||
if (param is null)
|
||||
return null;
|
||||
|
||||
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||
var type = Type.GetType(name);
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
return (Control)Activator.CreateInstance(type)!;
|
||||
}
|
||||
|
||||
return new TextBlock { Text = "Not Found: " + name };
|
||||
}
|
||||
|
||||
public bool Match(object? data)
|
||||
{
|
||||
return data is ViewModelBase;
|
||||
}
|
||||
}
|
||||
6
Flawless.Client/ViewModels/MainWindowViewModel.cs
Normal file
6
Flawless.Client/ViewModels/MainWindowViewModel.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Flawless.Client.ViewModels;
|
||||
|
||||
public class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
public string Greeting { get; } = "Welcome to Avalonia!";
|
||||
}
|
||||
7
Flawless.Client/ViewModels/ViewModelBase.cs
Normal file
7
Flawless.Client/ViewModels/ViewModelBase.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Flawless.Client.ViewModels;
|
||||
|
||||
public class ViewModelBase : ReactiveObject
|
||||
{
|
||||
}
|
||||
20
Flawless.Client/Views/MainWindow.axaml
Normal file
20
Flawless.Client/Views/MainWindow.axaml
Normal file
@ -0,0 +1,20 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Flawless.Client.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Flawless.Client.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="Flawless.Client">
|
||||
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainWindowViewModel/>
|
||||
</Design.DataContext>
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
|
||||
</Window>
|
||||
26
Flawless.Client/Views/MainWindow.axaml.cs
Normal file
26
Flawless.Client/Views/MainWindow.axaml.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Avalonia.Controls;
|
||||
using Flawless.Client.Remote;
|
||||
|
||||
namespace Flawless.Client.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Test();
|
||||
}
|
||||
|
||||
private async void Test()
|
||||
{
|
||||
var result = await (App.Current as App).ApiGateway.Login(new LoginRequest
|
||||
{
|
||||
Username = "cardidi",
|
||||
Password = "8888"
|
||||
}, CancellationToken.None);
|
||||
|
||||
Console.WriteLine(result);
|
||||
}
|
||||
}
|
||||
18
Flawless.Client/app.manifest
Normal file
18
Flawless.Client/app.manifest
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<!-- This manifest is used on Windows only.
|
||||
Don't remove it as it might cause problems with window transparency and embedded controls.
|
||||
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||
<assemblyIdentity version="1.0.0.0" name="Flawless.Client.Desktop"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@ -50,7 +50,7 @@ public class AuthenticationController(
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<ActionResult> LoginAsync(LoginRequest r)
|
||||
public async Task<ActionResult<TokenInfo>> LoginAsync(LoginRequest r)
|
||||
{
|
||||
var user = await userManager.FindByNameAsync(r.Username);
|
||||
if (user == null) return BadRequest(new FailedResponse("Invalid username or password."));
|
||||
|
||||
@ -17,7 +17,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
[ApiController, Authorize, Route("api/repo/{userName}/{repositoryName}")]
|
||||
public class RepositoryControl(
|
||||
public class RepositoryInnieController(
|
||||
UserManager<AppUser> userManager,
|
||||
AppDbContext dbContext,
|
||||
PathTransformer transformer,
|
||||
@ -30,6 +30,178 @@ public class RepositoryControl(
|
||||
}
|
||||
|
||||
|
||||
#region Unresoted
|
||||
|
||||
[HttpPost("delete_repo")]
|
||||
public async Task<IActionResult> DeleteRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
dbContext.Repositories.Remove(rp);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("get_info")]
|
||||
public async Task<IActionResult> IsRepositoryArchiveAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Commits)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
return Ok(new RepositoryInfoResponse
|
||||
{
|
||||
RepositoryName = rp.Name,
|
||||
OwnerUsername = rp.Owner.UserName!,
|
||||
LatestCommitId = rp.Commits.Max(cm => cm.Id),
|
||||
Description = rp.Description,
|
||||
IsArchived = rp.IsArchived,
|
||||
Role = rp.Members.First(m => m.User == u).Role
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("archive_repo")]
|
||||
public async Task<IActionResult> ArchiveRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
if (rp.IsArchived) return BadRequest(new FailedResponse("Repository is archived!"));
|
||||
|
||||
rp.IsArchived = true;
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("unarchive_repo")]
|
||||
public async Task<IActionResult> UnarchiveRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
if (!rp.IsArchived) return BadRequest(new FailedResponse("Repository is not archived!"));
|
||||
|
||||
rp.IsArchived = false;
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("get_users")]
|
||||
public async Task<IActionResult> GetUsersAsync(string repositoryName, QueryPagesRequest r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
return Ok(new PagedResponse<RepoUserRole>
|
||||
{
|
||||
Length = r.Length,
|
||||
Offset = r.Offset,
|
||||
Total = rp.Members.Count,
|
||||
Data = rp.Members.Select(pm => new RepoUserRole
|
||||
{
|
||||
Username = pm.User.UserName!,
|
||||
Role = pm.Role
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("update_user")]
|
||||
public async Task<IActionResult> UpdateUserAsync(string repositoryName, [FromBody] RepoUserRole r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var tu = await userManager.FindByNameAsync(r.Username);
|
||||
if (tu == null) return BadRequest(new FailedResponse("User not found!"));
|
||||
if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!"));
|
||||
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
var m = rp.Members.FirstOrDefault(m => m.User == tu);
|
||||
if (m == null)
|
||||
{
|
||||
m = new RepositoryMember
|
||||
{
|
||||
User = tu,
|
||||
Role = r.Role ?? RepositoryRole.Guest
|
||||
};
|
||||
|
||||
rp.Members.Add(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Role = r.Role ?? RepositoryRole.Guest;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("delete_user")]
|
||||
public async Task<IActionResult> DeleteUserAsync(string repositoryName, [FromBody] RepoUserRole r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var tu = await userManager.FindByNameAsync(r.Username);
|
||||
if (tu == null) return BadRequest(new FailedResponse("User not found!"));
|
||||
if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!"));
|
||||
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
var m = rp.Members.FirstOrDefault(m => m.User == tu);
|
||||
if (m == null) return BadRequest(new FailedResponse("User is not being granted to this repository!"));
|
||||
|
||||
rp.Members.Remove(m);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private bool UserNotGranted(out IActionResult? rsp, AppUser user, Repository repo, RepositoryRole minRole)
|
||||
{
|
||||
if (repo.Owner == user || repo.Members.Any(m => m.User == user && m.Role >= minRole))
|
||||
@ -57,8 +229,8 @@ public class RepositoryControl(
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("fetch/manifest/{commitId}")]
|
||||
public async Task<IActionResult> DownloadManifestAsync(string userName, string repositoryName, string commitId)
|
||||
[HttpGet("fetch_manifest")]
|
||||
public async Task<IActionResult> DownloadManifestAsync(string userName, string repositoryName, [FromQuery] string commitId)
|
||||
{
|
||||
if (!Guid.TryParse(commitId, out var commitGuid)) return BadRequest(new FailedResponse("Invalid commit id"));
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -72,8 +244,8 @@ public class RepositoryControl(
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("fetch/depot/{depotId}")]
|
||||
public async Task<IActionResult> DownloadDepotAsync(string userName, string repositoryName, string depotId)
|
||||
[HttpGet("fetch_depot")]
|
||||
public async Task<IActionResult> DownloadDepotAsync(string userName, string repositoryName, [FromQuery] string depotId)
|
||||
{
|
||||
if (!Guid.TryParse(depotId, out var depotGuid)) return BadRequest(new FailedResponse("Invalid depot id"));
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -87,7 +259,7 @@ public class RepositoryControl(
|
||||
|
||||
}
|
||||
|
||||
[HttpGet("list/commit")]
|
||||
[HttpGet("list_commit")]
|
||||
public async Task<IActionResult> ListCommitsAsync(string userName, string repositoryName)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -107,7 +279,7 @@ public class RepositoryControl(
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("list/locked")]
|
||||
[HttpGet("list_locked_files")]
|
||||
public async Task<IActionResult> ListLocksAsync(string userName, string repositoryName)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -126,7 +298,7 @@ public class RepositoryControl(
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("peek/commit")]
|
||||
[HttpGet("peek_commit")]
|
||||
public async Task<IActionResult> PeekCommitAsync(string userName, string repositoryName)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -147,7 +319,7 @@ public class RepositoryControl(
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("lock")]
|
||||
[HttpPost("lock_file")]
|
||||
public async Task<IActionResult> LockAsync(string userName, string repositoryName, string path)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -184,7 +356,7 @@ public class RepositoryControl(
|
||||
return BadRequest("Unknown error");
|
||||
}
|
||||
|
||||
[HttpGet("unlock")]
|
||||
[HttpPost("unlock_file")]
|
||||
public async Task<IActionResult> UnockAsync(string userName, string repositoryName, string path)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -221,7 +393,7 @@ public class RepositoryControl(
|
||||
return BadRequest("Unknown error");
|
||||
}
|
||||
|
||||
[HttpPost("commit")]
|
||||
[HttpPost("create_commit")]
|
||||
public async Task<IActionResult> CommitAsync(string userName, string repositoryName, [FromForm] FormCommitRequest req)
|
||||
{
|
||||
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
@ -1,232 +0,0 @@
|
||||
using Flawless.Communication.Request;
|
||||
using Flawless.Communication.Response;
|
||||
using Flawless.Communication.Shared;
|
||||
using Flawless.Server.Models;
|
||||
using Flawless.Server.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
[ApiController, Authorize, Route("api/repo_manage")]
|
||||
public class RepositoryManageController(AppDbContext dbContext, UserManager<AppUser> userManager) : ControllerBase
|
||||
{
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> ListAllAvailableRepositoriesAsync(QueryPagesRequest r)
|
||||
{
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var query = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Commits)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.Where(rp => rp.Members.Any(m => m.User == u))
|
||||
.Skip(r.Offset)
|
||||
.Take(r.Length)
|
||||
.ToArrayAsync();
|
||||
|
||||
return Ok(new PagedResponse<RepositoryInfoResponse>
|
||||
{
|
||||
Length = r.Length,
|
||||
Offset = r.Offset,
|
||||
Data = query.Select(rp => new RepositoryInfoResponse
|
||||
{
|
||||
RepositoryName = rp.Name,
|
||||
OwnerUsername = rp.Owner.UserName!,
|
||||
LatestCommitId = rp.Commits.OrderByDescending(cm => cm.CommittedOn).FirstOrDefault()?.Id ?? Guid.Empty,
|
||||
Description = rp.Description,
|
||||
IsArchived = rp.IsArchived,
|
||||
Role = rp.Members.First(m => m.User == u).Role
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("create/{repositoryName}")]
|
||||
public async Task<IActionResult> CreateRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
if (await dbContext.Repositories.AnyAsync(rp => rp.Name == repositoryName && u == rp.Owner))
|
||||
return BadRequest(new FailedResponse("Repository name has already created!"));
|
||||
|
||||
await dbContext.Repositories.AddAsync(new Repository()
|
||||
{
|
||||
Name = repositoryName,
|
||||
Owner = u,
|
||||
});
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("delete/{repositoryName}")]
|
||||
public async Task<IActionResult> DeleteRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
dbContext.Repositories.Remove(rp);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("info/{repositoryName}")]
|
||||
public async Task<IActionResult> IsRepositoryArchiveAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Commits)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
return Ok(new RepositoryInfoResponse
|
||||
{
|
||||
RepositoryName = rp.Name,
|
||||
OwnerUsername = rp.Owner.UserName!,
|
||||
LatestCommitId = rp.Commits.Max(cm => cm.Id),
|
||||
Description = rp.Description,
|
||||
IsArchived = rp.IsArchived,
|
||||
Role = rp.Members.First(m => m.User == u).Role
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("archive/{repositoryName}")]
|
||||
public async Task<IActionResult> ArchiveRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
if (rp.IsArchived) return BadRequest(new FailedResponse("Repository is archived!"));
|
||||
|
||||
rp.IsArchived = true;
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("unarchive/{repositoryName}")]
|
||||
public async Task<IActionResult> UnarchiveRepositoryAsync(string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
if (!rp.IsArchived) return BadRequest(new FailedResponse("Repository is not archived!"));
|
||||
|
||||
rp.IsArchived = false;
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("get_users/{repositoryName}")]
|
||||
public async Task<IActionResult> GetUsersAsync(string repositoryName, QueryPagesRequest r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
return Ok(new PagedResponse<RepoUserRole>
|
||||
{
|
||||
Length = r.Length,
|
||||
Offset = r.Offset,
|
||||
Total = rp.Members.Count,
|
||||
Data = rp.Members.Select(pm => new RepoUserRole
|
||||
{
|
||||
Username = pm.User.UserName!,
|
||||
Role = pm.Role
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("update_user/{repositoryName}")]
|
||||
public async Task<IActionResult> UpdateUserAsync(string repositoryName, [FromBody] RepoUserRole r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var tu = await userManager.FindByNameAsync(r.Username);
|
||||
if (tu == null) return BadRequest(new FailedResponse("User not found!"));
|
||||
if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!"));
|
||||
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
var m = rp.Members.FirstOrDefault(m => m.User == tu);
|
||||
if (m == null)
|
||||
{
|
||||
m = new RepositoryMember
|
||||
{
|
||||
User = tu,
|
||||
Role = r.Role ?? RepositoryRole.Guest
|
||||
};
|
||||
|
||||
rp.Members.Add(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.Role = r.Role ?? RepositoryRole.Guest;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("delete_user/{repositoryName}")]
|
||||
public async Task<IActionResult> DeleteUserAsync(string repositoryName, [FromBody] RepoUserRole r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var tu = await userManager.FindByNameAsync(r.Username);
|
||||
if (tu == null) return BadRequest(new FailedResponse("User not found!"));
|
||||
if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!"));
|
||||
|
||||
var rp = await dbContext.Repositories
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner);
|
||||
|
||||
if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
|
||||
|
||||
var m = rp.Members.FirstOrDefault(m => m.User == tu);
|
||||
if (m == null) return BadRequest(new FailedResponse("User is not being granted to this repository!"));
|
||||
|
||||
rp.Members.Remove(m);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
66
Flawless.Server/Controllers/RepositoryOutieController.cs
Normal file
66
Flawless.Server/Controllers/RepositoryOutieController.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using Flawless.Communication.Request;
|
||||
using Flawless.Communication.Response;
|
||||
using Flawless.Communication.Shared;
|
||||
using Flawless.Server.Models;
|
||||
using Flawless.Server.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
//todo Merging RepositoryManageController and RepositoryController
|
||||
[ApiController, Authorize, Route("api")]
|
||||
public class RepositoryOutieController(AppDbContext dbContext, UserManager<AppUser> userManager) : ControllerBase
|
||||
{
|
||||
[HttpGet("repo_list")]
|
||||
public async Task<IActionResult> ListAllAvailableRepositoriesAsync([FromQuery] QueryPagesRequest r)
|
||||
{
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
var query = await dbContext.Repositories
|
||||
.Include(repository => repository.Owner)
|
||||
.Include(repository => repository.Commits)
|
||||
.Include(repository => repository.Members)
|
||||
.ThenInclude(repositoryMember => repositoryMember.User)
|
||||
.Where(rp => rp.Members.Any(m => m.User == u))
|
||||
.Skip(r.Offset)
|
||||
.Take(r.Length)
|
||||
.ToArrayAsync();
|
||||
|
||||
return Ok(new PagedResponse<RepositoryInfoResponse>
|
||||
{
|
||||
Length = r.Length,
|
||||
Offset = r.Offset,
|
||||
Data = query.Select(rp => new RepositoryInfoResponse
|
||||
{
|
||||
RepositoryName = rp.Name,
|
||||
OwnerUsername = rp.Owner.UserName!,
|
||||
LatestCommitId = rp.Commits.OrderByDescending(cm => cm.CommittedOn).FirstOrDefault()?.Id ?? Guid.Empty,
|
||||
Description = rp.Description,
|
||||
IsArchived = rp.IsArchived,
|
||||
Role = rp.Members.First(m => m.User == u).Role
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("repo_create")]
|
||||
public async Task<IActionResult> CreateRepositoryAsync([FromQuery] string repositoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
|
||||
return BadRequest(new FailedResponse("Repository name is empty or too short!"));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
if (await dbContext.Repositories.AnyAsync(rp => rp.Name == repositoryName && u == rp.Owner))
|
||||
return BadRequest(new FailedResponse("Repository name has already created!"));
|
||||
|
||||
await dbContext.Repositories.AddAsync(new Repository()
|
||||
{
|
||||
Name = repositoryName,
|
||||
Owner = u,
|
||||
});
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@ public class UserController(
|
||||
) : ControllerBase
|
||||
{
|
||||
|
||||
[HttpPost("update/info")]
|
||||
[HttpPost("update_info")]
|
||||
public async Task<IActionResult> UpdateUserInfoAsync(UserInfoModifyResponse r)
|
||||
{
|
||||
bool update = false;
|
||||
@ -54,7 +54,7 @@ public class UserController(
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("update/email")]
|
||||
[HttpPost("update_email")]
|
||||
public async Task<IActionResult> UpdateEmailAsync(UserContactModifyResponse r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(r.Email))
|
||||
@ -68,7 +68,7 @@ public class UserController(
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("update/phone")]
|
||||
[HttpPost("update_phone")]
|
||||
public async Task<IActionResult> UpdatePhoneAsync(UserContactModifyResponse r)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(r.Phone))
|
||||
@ -81,19 +81,11 @@ public class UserController(
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("get")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync()
|
||||
{
|
||||
var self = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
|
||||
// Return self as default
|
||||
return Ok(GetUserInfoInternal(self, self));
|
||||
}
|
||||
|
||||
[HttpGet("get/{username}")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync(string username)
|
||||
[HttpGet("get_info")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync([FromQuery] string username)
|
||||
{
|
||||
var self = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
if (string.IsNullOrWhiteSpace(username)) return Ok(GetUserInfoInternal(self, self));
|
||||
|
||||
var u = await userManager.FindByNameAsync(username);
|
||||
if (u == null) return BadRequest(new FailedResponse("User is not existed!"));
|
||||
@ -101,8 +93,8 @@ public class UserController(
|
||||
return Ok(GetUserInfoInternal(u, self));
|
||||
}
|
||||
|
||||
[HttpGet("query/{keyword}")]
|
||||
public async Task<ActionResult<PagedResponse<UserInfoResponse>>> GetUserInfoAsync(QueryPagesRequest r, string keyword)
|
||||
[HttpGet("query_info")]
|
||||
public async Task<ActionResult<PagedResponse<UserInfoResponse>>> GetUserInfoAsync(QueryPagesRequest r, [FromQuery] string keyword)
|
||||
{
|
||||
var payload = await userManager.Users
|
||||
.Where(u => u.UserName!.Contains(keyword) || (u.NickName != null && u.NickName.Contains(keyword)))
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
# Flawless Version Control
|
||||
|
||||
Yet another version control software for programmer, project manager, artist and designer which provides a **FLAWLESS**
|
||||
felling on deploy and manage.
|
||||
felling on deploy and manage.
|
||||
|
||||
# Create Interfaces
|
||||
|
||||
```
|
||||
refitter http://localhost:5256/swagger/v1/swagger.json --namespace "Flawless.Client.Remote" --multiple-interfaces ByTag --multiple-files --cancellation-tokens --contracts-namespac "Flawless.Communication" --output ".\Flawless.Client\Service\Remote"
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user