BackupWorker Job
This commit is contained in:
parent
b9cc68ff41
commit
2037cfd6d7
11 changed files with 123 additions and 7 deletions
|
|
@ -1066,6 +1066,14 @@
|
||||||
"workerType": "NotificationWorker",
|
"workerType": "NotificationWorker",
|
||||||
"isActive": true,
|
"isActive": true,
|
||||||
"dataSourceCode": "Default"
|
"dataSourceCode": "Default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Backup Databases",
|
||||||
|
"cron": "0 1 * * *",
|
||||||
|
"workerType": "BackupWorker",
|
||||||
|
"isActive": true,
|
||||||
|
"dataSourceCode": "Default",
|
||||||
|
"beforeSp": "Adm_T_DatabaseBackup"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ContactTitles": [
|
"ContactTitles": [
|
||||||
|
|
|
||||||
|
|
@ -4749,9 +4749,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 1, DataField = "Name", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 2, DataField = "Cron", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 2, DataField = "Cron", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 3, DataField = "WorkerType", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
new EditingFormItemDto { Order = 3, DataField = "WorkerType", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
new EditingFormItemDto { Order = 4, DataField = "BeforeSp", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 4, DataField = "IsActive", ColSpan = 1, EditorType2=EditorTypes.dxCheckBox },
|
||||||
new EditingFormItemDto { Order = 5, DataField = "AfterSp", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 5, DataField = "BeforeSp", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 6, DataField = "IsActive", ColSpan = 1, EditorType2=EditorTypes.dxCheckBox },
|
new EditingFormItemDto { Order = 6, DataField = "AfterSp", ColSpan = 1, EditorType2=EditorTypes.dxTextBox },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
new() { Order=2,ColCount=2,ColSpan=1,ItemType="group",Items= [
|
new() { Order=2,ColCount=2,ColSpan=1,ItemType="group",Items= [
|
||||||
|
|
@ -4841,6 +4841,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
new() { Key=2,Name="SqlWorker" },
|
new() { Key=2,Name="SqlWorker" },
|
||||||
new() { Key=3,Name="NotificationWorker" },
|
new() { Key=3,Name="NotificationWorker" },
|
||||||
new() { Key=4,Name="SessionCleanupWorker" },
|
new() { Key=4,Name="SessionCleanupWorker" },
|
||||||
|
new() { Key=5,Name="BackupWorker" },
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,6 @@ public enum WorkerTypeEnum
|
||||||
SqlWorker = 2,
|
SqlWorker = 2,
|
||||||
NotificationWorker = 3,
|
NotificationWorker = 3,
|
||||||
SessionCleanupWorker = 4,
|
SessionCleanupWorker = 4,
|
||||||
|
BackupWorker = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ using Volo.Abp.ExceptionHandling;
|
||||||
using Volo.Abp.ObjectMapping;
|
using Volo.Abp.ObjectMapping;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.BackgroundWorkers;
|
namespace Sozsoft.Platform.BackgroundWorkers;
|
||||||
|
|
||||||
|
|
@ -28,6 +30,7 @@ public interface IPlatformBackgroundWorker : ITransientDependency
|
||||||
|
|
||||||
public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgroundWorker
|
public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgroundWorker
|
||||||
{
|
{
|
||||||
|
private readonly IConfiguration Configuration;
|
||||||
protected IAbpDistributedLock DistributedLock { get; }
|
protected IAbpDistributedLock DistributedLock { get; }
|
||||||
protected ISpRepository SpRepository { get; }
|
protected ISpRepository SpRepository { get; }
|
||||||
public IDynamicDataManager DynamicDataManager { get; }
|
public IDynamicDataManager DynamicDataManager { get; }
|
||||||
|
|
@ -41,11 +44,13 @@ public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgrou
|
||||||
ICurrentUser currentUser,
|
ICurrentUser currentUser,
|
||||||
IObjectMapper objectMapper,
|
IObjectMapper objectMapper,
|
||||||
IDynamicDataManager dynamicDataManager,
|
IDynamicDataManager dynamicDataManager,
|
||||||
|
IConfiguration configuration,
|
||||||
IRepository<Entities.BackgroundWorker> repository) : base(settingProvider, localizer, currentUser, objectMapper)
|
IRepository<Entities.BackgroundWorker> repository) : base(settingProvider, localizer, currentUser, objectMapper)
|
||||||
{
|
{
|
||||||
DistributedLock = distributedLock;
|
DistributedLock = distributedLock;
|
||||||
SpRepository = spRepository;
|
SpRepository = spRepository;
|
||||||
DynamicDataManager = dynamicDataManager;
|
DynamicDataManager = dynamicDataManager;
|
||||||
|
Configuration = configuration;
|
||||||
Repository = repository;
|
Repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +60,8 @@ public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgrou
|
||||||
|
|
||||||
var LogPrefix = $"{Clock.Now:s}_{Worker.Name}: {{0}}";
|
var LogPrefix = $"{Clock.Now:s}_{Worker.Name}: {{0}}";
|
||||||
var DistributedLockName = Worker.Name;
|
var DistributedLockName = Worker.Name;
|
||||||
|
var backupPath = "";
|
||||||
|
var backupDeleteAfterDays = Configuration.GetValue<int>("App:BackupDeleteAfterDays", 3);
|
||||||
|
|
||||||
//using (var scope = ServiceScopeFactory.CreateScope())
|
//using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
|
||||||
|
|
@ -70,6 +77,16 @@ public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgrou
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Backup Worker için yedeklerin saklanacağı klasörün oluşturulması
|
||||||
|
if (Worker.WorkerType == WorkerTypeEnum.BackupWorker)
|
||||||
|
{
|
||||||
|
backupPath = Path.Combine(Configuration["App:CdnPath"], "host", "backup");
|
||||||
|
if (!Directory.Exists(backupPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(backupPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call BeforeSp
|
// Call BeforeSp
|
||||||
if (!Worker.BeforeSp.IsNullOrWhiteSpace())
|
if (!Worker.BeforeSp.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
|
|
@ -87,7 +104,7 @@ public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgrou
|
||||||
worker.Options = JsonSerializer.Deserialize<MailQueueWorkerOptions>(Worker.Options);
|
worker.Options = JsonSerializer.Deserialize<MailQueueWorkerOptions>(Worker.Options);
|
||||||
await worker.StartAsync(cancellationToken);
|
await worker.StartAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
else if (Worker.WorkerType == WorkerTypeEnum.SqlWorker)
|
else if (Worker.WorkerType == WorkerTypeEnum.SqlWorker || Worker.WorkerType == WorkerTypeEnum.BackupWorker)
|
||||||
{
|
{
|
||||||
await LazyServiceProvider.GetRequiredService<SqlWorker>().StartAsync(cancellationToken);
|
await LazyServiceProvider.GetRequiredService<SqlWorker>().StartAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +124,20 @@ public class PlatformBackgroundWorker : PlatformDomainService, IPlatformBackgrou
|
||||||
var result = await SpRepository.CallSpAsync(Worker.AfterSp, Worker.DataSourceCode, null);
|
var result = await SpRepository.CallSpAsync(Worker.AfterSp, Worker.DataSourceCode, null);
|
||||||
Logger.LogInformation(LogPrefix, $"AfterSp çalıştırıldı. Sonuç: {result}");
|
Logger.LogInformation(LogPrefix, $"AfterSp çalıştırıldı. Sonuç: {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backup Worker için yedekleme işlemi ve eski yedeklerin silinmesi
|
||||||
|
if (Worker.WorkerType == WorkerTypeEnum.BackupWorker)
|
||||||
|
{
|
||||||
|
var files = Directory.GetFiles(backupPath, "*.bak");
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (File.GetLastWriteTime(file) < DateTime.Now.AddDays(backupDeleteAfterDays))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -30,3 +30,23 @@ BEGIN
|
||||||
ALTER TABLE [dbo].[Adm_T_Behavior] ADD DEFAULT (CONVERT([bit],(0))) FOR [IsDeleted]
|
ALTER TABLE [dbo].[Adm_T_Behavior] ADD DEFAULT (CONVERT([bit],(0))) FOR [IsDeleted]
|
||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Adm_T_DatabaseBackup]
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
DECLARE @SQL NVARCHAR(MAX) = N'';
|
||||||
|
|
||||||
|
SELECT @SQL = @SQL + N'
|
||||||
|
BACKUP DATABASE ' + QUOTENAME(name) + N'
|
||||||
|
TO DISK = N''/var/opt/mssql/backup/'
|
||||||
|
+ name + N'_' + CONVERT(VARCHAR(8), GETDATE(), 112) + N'.bak''
|
||||||
|
WITH INIT;'
|
||||||
|
FROM sys.databases
|
||||||
|
WHERE name NOT IN ('master','model','msdb','tempdb')
|
||||||
|
AND state_desc = 'ONLINE';
|
||||||
|
|
||||||
|
EXEC sp_executesql @SQL;
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
@ -55,15 +55,67 @@ public class SqlTablesSeeder : IDataSeedContributor, ITransientDependency
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var (action, objectName, objectType) = ExtractSqlInfo(sql);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(objectName))
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Executing SQL: {Action} {ObjectType} {ObjectName}",
|
||||||
|
action,
|
||||||
|
objectType,
|
||||||
|
objectName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Executing SQL batch (object not detected)");
|
||||||
|
}
|
||||||
|
|
||||||
await dbContext.Database.ExecuteSqlRawAsync(sql);
|
await dbContext.Database.ExecuteSqlRawAsync(sql);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("SQL batch skipped (may already exist): {Message}", ex.Message);
|
_logger.LogError(ex, "SQL batch failed: {Sql}", sql);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Database script seeding completed successfully.");
|
_logger.LogInformation("Database script seeding completed successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (string Action, string? ObjectName, string? ObjectType) ExtractSqlInfo(string sql)
|
||||||
|
{
|
||||||
|
var patterns = new[]
|
||||||
|
{
|
||||||
|
(@"CREATE\s+TABLE\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "TABLE"),
|
||||||
|
(@"ALTER\s+TABLE\s+(\[?\w+\]?\.?\[?\w+\]?)", "ALTER", "TABLE"),
|
||||||
|
|
||||||
|
(@"CREATE\s+VIEW\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "VIEW"),
|
||||||
|
|
||||||
|
(@"CREATE\s+PROCEDURE\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "PROCEDURE"),
|
||||||
|
(@"ALTER\s+PROCEDURE\s+(\[?\w+\]?\.?\[?\w+\]?)", "ALTER", "PROCEDURE"),
|
||||||
|
(@"CREATE\s+OR\s+ALTER\s+PROCEDURE\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE/ALTER", "PROCEDURE"),
|
||||||
|
|
||||||
|
(@"CREATE\s+FUNCTION\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "FUNCTION"),
|
||||||
|
|
||||||
|
(@"CREATE\s+INDEX\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "INDEX"),
|
||||||
|
|
||||||
|
(@"CREATE\s+TRIGGER\s+(\[?\w+\]?\.?\[?\w+\]?)", "CREATE", "TRIGGER"),
|
||||||
|
|
||||||
|
(@"DROP\s+TABLE\s+(\[?\w+\]?\.?\[?\w+\]?)", "DROP", "TABLE"),
|
||||||
|
(@"DROP\s+VIEW\s+(\[?\w+\]?\.?\[?\w+\]?)", "DROP", "VIEW"),
|
||||||
|
(@"DROP\s+PROCEDURE\s+(\[?\w+\]?\.?\[?\w+\]?)", "DROP", "PROCEDURE")
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (pattern, action, type) in patterns)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(sql, pattern, RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return (action, match.Groups[1].Value, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ("UNKNOWN", null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<None Remove="Seeds\SqlTables.sql" />
|
||||||
<Content Include="Seeds\SqlTables.sql">
|
<Content Include="Seeds\SqlTables.sql">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
"RedirectAllowedUrls": "https://sozsoft.com,https://sozsoft.com/authentication/callback",
|
"RedirectAllowedUrls": "https://sozsoft.com,https://sozsoft.com/authentication/callback",
|
||||||
"AttachmentsPath": "/etc/api/mail-queue/attachments",
|
"AttachmentsPath": "/etc/api/mail-queue/attachments",
|
||||||
"CdnPath": "/etc/api/cdn",
|
"CdnPath": "/etc/api/cdn",
|
||||||
"BaseDomain": "sozsoft.com"
|
"BaseDomain": "sozsoft.com",
|
||||||
|
"BackupDeleteAfterDays": 3
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 178 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
|
|
@ -46,3 +46,4 @@ services:
|
||||||
- 1433:1433
|
- 1433:1433
|
||||||
volumes:
|
volumes:
|
||||||
- mssql:/var/opt/mssql
|
- mssql:/var/opt/mssql
|
||||||
|
- /etc/api/cdn/host/backup:/var/opt/mssql/backup
|
||||||
Loading…
Reference in a new issue