Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
37fdc8c
Сделал создание базы из лаунчера входа
J0shlerB0y Apr 28, 2026
9d5f2cf
решил конфликты с изменениями по ревью
J0shlerB0y Apr 28, 2026
c5593f6
сделал клиент создание базы для облака
J0shlerB0y Apr 28, 2026
2266c4b
универсальное наполнение базы данных из скриптов
J0shlerB0y Apr 30, 2026
a43ac62
починил маппинг страниц + скрипты наполнения баз
J0shlerB0y May 1, 2026
0e82442
поменял получения крейтора базы в типе подключения
J0shlerB0y May 2, 2026
ab2ff0d
починил проблему передачи зависимостей
J0shlerB0y May 2, 2026
80ebe2c
добавление title при создании базы
J0shlerB0y May 2, 2026
a771542
реализация IDbCreatorModel для QSCloud и правки
J0shlerB0y May 5, 2026
47c7b2e
Если каналы на проекте отсутствую то кнопку отключить обновления бесс…
Gankov May 5, 2026
b1417d2
Добавлен формат проверки емейлов из несколько адресов.
Gankov May 6, 2026
8229cb5
Создание базы через облако
J0shlerB0y May 6, 2026
3f9ae8e
убрал лишние элементы
J0shlerB0y May 6, 2026
024eeda
настроил клиент создания базы через облако
J0shlerB0y May 11, 2026
2117298
Merge branch 'release/1.8'
Gankov May 13, 2026
0dc4147
сделал адаптер для строго кода и перерабтал под новый интерфейс взаим…
J0shlerB0y May 14, 2026
2611bf3
доработки по ревью
J0shlerB0y Jun 10, 2026
d20c498
Update MySqlDbCreateModel.cs
J0shlerB0y Jun 10, 2026
41ba7a7
доработал легаси UserCreateDbController
J0shlerB0y Jun 11, 2026
c9c4b79
вынес тестовую конфигурацию создания базы
J0shlerB0y Jun 11, 2026
84669e9
вернул комментарий
J0shlerB0y Jun 11, 2026
e8f83e5
Merge branch 'master' into feature/LauncherProviderCreateDb
J0shlerB0y Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions QS.Cloud.Client/Clients/Base/CloudClientByBasicAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,26 @@

namespace QS.Cloud.Client
{
public class CloudClientByBasicAuth : CloudClientServiceBase
{
public class CloudClientByBasicAuth : CloudClientServiceBase {
public CloudClientByBasicAuth(IBasicAuthInfoProvider basicAuthInfoProvider, string serviceAddress, int servicePort)
: base(serviceAddress, servicePort)
{
: base(serviceAddress, servicePort) {
headers = new Metadata
{ {
"Authorization",
$"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{basicAuthInfoProvider.UserName}:{basicAuthInfoProvider.Password}"))}"
} };

}

public override bool CanConnect => throw new NotImplementedException();
public override bool CanConnect { get {
try {
Channel.ConnectAsync().GetAwaiter().GetResult();
}
catch {
return false;
}
return true;
}
}
}
}
21 changes: 21 additions & 0 deletions QS.Cloud.Client/Clients/DataBaseManagementCloudClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Grpc.Core;
using QS.Cloud.Core;
using QS.Project.Versioning;
using System.Threading;

namespace QS.Cloud.Client.Clients {
public class DataBaseManagementCloudClient : CloudClientByBasicAuth {
private IApplicationInfo applicationInfo { get; set; }
public DataBaseManagementCloudClient(IBasicAuthInfoProvider basicAuthInfoProvider)
: base(basicAuthInfoProvider, "core.cloud.qsolution.ru", 443)
{
this.applicationInfo = applicationInfo;
}

public CreateDataBaseResponse CreateDataBase(string dbName, string dbTitle, IApplicationInfo applicationInfo) {
var client = new DataBaseManagement.DataBaseManagementClient(Channel);
var request = new CreateDataBaseRequest { Name = dbName, Title = dbTitle, ProductId = applicationInfo.ProductCode };
return client.CreateDataBase(request, headers);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,42 @@
using System.Linq;
using System.Reflection;
using System;
using System.Threading;
using QS.Cloud.Client.Clients;
using QS.DBScripts.Controllers;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace QS.Cloud.Client
namespace QS.Cloud.Client.DataBase
{
public class QSCloudProvider : IDbProvider {
public string ConnectionString { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public bool IsConnected => throw new NotImplementedException();
public int BaseId { get; private set; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не согласен, с решением, по сути ты смешиваешь зачем то 2 различные функциональности, вход в саму админку сервера и вход в базу. Просто в моем представлении у IDbProvider не может быть базы в принцепе. Или я не понял задумки.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не согласен, с решением, по сути ты смешиваешь зачем то 2 различные функциональности, вход в саму админку сервера и вход в базу. Просто в моем представлении у IDbProvider не может быть базы в принцепе. Или я не понял задумки.

я все же пока что оставил BaseId он его получает от сервера после создания, чтобы потом при наполнении базы получить эту базу, иначе он никак не сможет его передать. потому что интферфейс создания базы возвращает только bool успешности, можно сделать более универсальый тип вывода функции создания, но как будто это только для облака нужно будет,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

при этом правйдер базу не наполняет, он лишь несет данные после создания базы

public BasicAuthInfoProvider AuthInfo { get; private set; }

public bool IsConnected { get; private set; }

public bool IsAdmin { get; protected set; }

#region Параметры подключени
public string Account { get; private set; }


#endregion
public string UserName { get; private set; }

private CloudFeaturesClient featuresClient;
public bool CanCreateDatabase => dbClient.CanConnect;

private LoginManagementCloudClient loginClient;
private SessionManagementCloudClient sessionClient;
private UserManagementCloudClient userClient;
private DataBaseManagementCloudClient dbClient;


public QSCloudProvider(IList<ConnectionParameterValue> parameters, string password = null) {
Account = parameters.First(p => p.Name == "Account").Value;
UserName = parameters.First(p => p.Name == "Login").Value;
BasicAuthInfoProvider authInfo = new BasicAuthInfoProvider($@"{Account}\{UserName}", password);
AuthInfo = new BasicAuthInfoProvider($@"{Account}\{UserName}", password);

loginClient = new LoginManagementCloudClient(authInfo);
loginClient = new LoginManagementCloudClient(AuthInfo);
dbClient = new DataBaseManagementCloudClient(AuthInfo);
}

public bool AddUser(string username, string password)
Expand All @@ -49,9 +56,12 @@ public bool ChangePassword(string username, string oldPassword, string newPasswo
throw new NotImplementedException();
}

public bool CreateDatabase(string databaseName)
public bool CreateDatabase(string databaseName, string title, IServiceProvider services)
{
throw new NotImplementedException();
IApplicationInfo applicationInfo = services.GetService<IApplicationInfo>();
CreateDataBaseResponse response = dbClient.CreateDataBase(databaseName, title, applicationInfo);
BaseId = response.BaseId;
return true;
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
using Microsoft.Extensions.DependencyInjection;
using QS.DbManagement;
using QS.DBScripts;
using QS.DBScripts.Controllers;
using QS.DBScripts.Models;
using QS.Utilities.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QS.Utilities.Extensions;

namespace QS.Cloud.Client {
namespace QS.Cloud.Client.DataBase {
public class QsCloudConnectionTypeBase : ConnectionTypeBase {

public QsCloudConnectionTypeBase() {
Title = "QS: Облако";
ConnectionTypeName = "QSCloud";

Parameters.Add(new ConnectionParameter("Account","Организация"));
Parameters.Add(new ConnectionParameter("Login","Пользователь"));

IconBytes = Assembly.GetExecutingAssembly().GetResourceByteArray("QS.Cloud.Client.Icons.qscloud.ico");

CreatorFactory = args => {
var provider = (QSCloudProvider)args.Provider;
var scripts = args.ServiceProvider.GetRequiredService<IDbScriptsConfiguration>();
return new QsCloudDbCreator(
provider.BaseId,
provider.AuthInfo,
scripts,
args.Progress,
args.Interaction,
args.CancellationToken);
};
}

public override bool CanConnect(IEnumerable<ConnectionParameterValue> parameters) {
return parameters.Any(p => p.Name == "Account" && !string.IsNullOrEmpty(p.Value)) &&
parameters.Any(p => p.Name == "Login" && !string.IsNullOrEmpty(p.Value));
}

public override IDbProvider CreateProvider(IList<ConnectionParameterValue> parameters, string password = null)
=> new QSCloudProvider(parameters, password);
public override IDbProvider CreateProvider(IList<ConnectionParameterValue> parameters, string password = null) {
return new QSCloudProvider(parameters, password);
}
}
}
85 changes: 85 additions & 0 deletions QS.Cloud.Client/DataBase/QsCloudDbCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using QS.Cloud.Core;
using QS.DBScripts;
using QS.DBScripts.Controllers;
using QS.DBScripts.Models;
using QS.Dialog;
using System;
using System.Threading;

namespace QS.Cloud.Client.DataBase
{
public class QsCloudDbCreator : IDbCreatorModel
{
static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

private readonly int baseId;
private readonly IProgressBarDisplayable progress;
private readonly IDbCreatorInteraction interaction;
private readonly IDbScriptsConfiguration configuration;
private readonly CancellationToken cancellationToken;

private LoginManagementCloudClient loginClient;

public QsCloudDbCreator(
int baseId,
BasicAuthInfoProvider authInfo,
IDbScriptsConfiguration configuration,
IProgressBarDisplayable progress,
IDbCreatorInteraction interaction,
CancellationToken cancellationToken)
{
this.baseId = baseId;
loginClient = new LoginManagementCloudClient(authInfo);
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
this.interaction = interaction ?? throw new ArgumentNullException(nameof(interaction));
this.cancellationToken = cancellationToken;
}

public bool RunCreation(string dbName, string dbTitle) {
try {
cancellationToken.ThrowIfCancellationRequested();

StartSessionResponse session = loginClient.StartSession(baseId);

if(!session.Success) {
interaction.ReportError("Ошибка в создании сессии", "Запрос в облако");
return false;
}
if(!session.IsAdmin) {
interaction.ReportError("Вы не имеете прав Администратора", "Запрос в облако");
return false;
}

var infoProvider = new SessionInfoProvider(sessionId: session.SessionId);
var sessionLife = new AliveCloudClient(infoProvider);
sessionLife.NewMessage += (mes) => {
progress.Update("Сессия: " + mes + " в статусе " + sessionLife.LastStatus.ToString());
};
sessionLife.KeepAlive();

var creator = new MySqlDbCreateModel(
session.Db.Server, session.Db.Port, session.Db.Login, session.Db.Password,
configuration.MakeCreationScript(), progress, interaction, cancellationToken);
creator.FillBaseGuid = false;
bool success = creator.RunCreation(session.Db.BaseName, dbTitle);

sessionLife.Dispose();
return success;
}
catch(OperationCanceledException) {
logger.Info("Создание базы в облаке отменено пользователем.");
return false;
}
catch(Exception ex) {
logger.Error(ex, "Ошибка при создании базы в облаке.");
interaction.ReportError(ex.Message, null);
throw;
}
finally {
if(progress.IsStarted)
progress.Close();
}
}
}
}
19 changes: 19 additions & 0 deletions QS.Cloud.Client/Protos/DataBaseManagement.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";

package QS.Cloud.Core;

service DataBaseManagement{
// Создать пустую базу
rpc CreateDataBase (CreateDataBaseRequest) returns (CreateDataBaseResponse);
}

message CreateDataBaseRequest{
string name = 1;
string title = 2;
uint32 product_id = 3;
}

message CreateDataBaseResponse{
int32 base_id = 1;
string base_guid = 2;
}
10 changes: 9 additions & 1 deletion QS.Cloud.Client/QS.Cloud.Client.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
Expand All @@ -15,18 +15,26 @@
<PackageReference Include="NLog" Version="6.1.1" />
</ItemGroup>

<ItemGroup>
<None Remove="Scripts\*.sql" />
<EmbeddedResource Include="Scripts\*.sql" />
</ItemGroup>

<ItemGroup>
<None Remove="Protos\BaseManagement.proto" />
<None Remove="Protos\CloudFeatures.proto" />
<None Remove="Protos\LoginManagement.proto" />
<None Remove="Protos\SessionManagement.proto" />
<None Remove="Protos\UserControl.proto" />
<None Remove="Protos\UserManagement.proto" />
<None Remove="Protos\DataBaseManagement.proto" />
<ProjectReference Include="..\QS.DbManagement\QS.DbManagement.csproj" />
<ProjectReference Include="..\QS.Project.Abstract\QS.Project.Abstract.csproj" />
<ProjectReference Include="..\QS.Updater.Core\QS.Updater.Core.csproj" />
<Protobuf Include="Protos\CloudFeatures.proto" />
<Protobuf Include="Protos\LoginManagement.proto" />
<Protobuf Include="Protos\SessionManagement.proto" />
<Protobuf Include="Protos\DataBaseManagement.proto" />
<Protobuf Include="Protos\UserManagement.proto" />
</ItemGroup>

Expand Down
37 changes: 36 additions & 1 deletion QS.DbManagement/ConnectionTypeBase.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using QS.DBScripts;
using QS.DBScripts.Controllers;
using QS.Dialog;
using System;
using System.Collections.Generic;

Expand All @@ -10,9 +14,40 @@ public abstract class ConnectionTypeBase {
public List<ConnectionParameter> Parameters { get; } = new List<ConnectionParameter>();

public byte[] IconBytes { get; protected set; }

public abstract bool CanConnect(IEnumerable<ConnectionParameterValue> parameters);

public abstract IDbProvider CreateProvider(IList<ConnectionParameterValue> parameters, string password = null);

public Func<CreatorFactoryArgs, IDbCreatorModel> CreatorFactory { get; set; }

/// <summary>
/// Создание базы доступно, только если задана фабрика и приложение
/// зарегистрировало конфигурацию скриптов с реальным скриптом создания
/// </summary>
public virtual bool SupportsDatabaseCreation(IServiceProvider services) {
return CreatorFactory != null
&& services.GetService<IDbScriptsConfiguration>()?.HasCreationScript() == true;
}

public IDbCreatorModel CreateCreator(CreatorFactoryArgs args) {
if(CreatorFactory == null)
throw new InvalidOperationException(
$"Для типа подключения '{ConnectionTypeName}' не настроена фабрика создания БД (CreatorFactory). "
+ "Заполните её в композиционном корне приложения.");
return CreatorFactory(args);
}
}

/// <summary>
/// interaction — канал диалогов с пользователем
/// serviceProvider — для получения дополнительных зависимостей
/// </summary>
public class CreatorFactoryArgs {
public IDbProvider Provider { get; set; }
public IProgressBarDisplayable Progress { get; set; }
public IDbCreatorInteraction Interaction { get; set; }
public System.Threading.CancellationToken CancellationToken { get; set; }
public IServiceProvider ServiceProvider { get; set; }
}
}
17 changes: 17 additions & 0 deletions QS.DbManagement/DbCreationPhase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using QS.DbManagement;

namespace QS.Launcher.ViewModels.PageViewModels.DataBase {
/// <summary>
/// Один шаг пайплайна создания базы
/// </summary>
public sealed class DbCreationPhase {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем для этого целый класс?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем для этого целый класс?

Это делает прогресс страницу универсальной, просто атомарная операция, мне кажется, классы и нужны, чтобы инкапсулировать подобную логику.

public string Title { get; }
public Func<CreatorFactoryArgs, bool> Action { get; }

public DbCreationPhase(string title, Func<CreatorFactoryArgs, bool> action) {
Title = title ?? throw new ArgumentNullException(nameof(title));
Action = action ?? throw new ArgumentNullException(nameof(action));
}
}
}
5 changes: 3 additions & 2 deletions QS.DbManagement/IDbProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public interface IDbProvider : IDisposable
string UserName { get; }

bool ChangePassword(string username, string oldPassword, string newPassword);
bool CreateDatabase(string databaseName);

bool CreateDatabase(string databaseName, string title, IServiceProvider services = null);

bool DropDatabase(string databaseName);

Expand All @@ -26,5 +26,6 @@ public interface IDbProvider : IDisposable
bool IsConnected { get; }

bool IsAdmin { get; }
bool CanCreateDatabase { get; }
}
}
Loading