Compare commits

...

2 Commits

Author SHA1 Message Date
883837633a Simplifies submit request handling.
Removes the SubmissionSourceType from the SubmitSolutionRequest and associated service logic, as it is no longer required.

The submission source type is now determined within the SubmitService based on contest association.

Also increases JWT expiration time for user convenience.
2025-10-27 21:49:45 +03:00
aa0d2b1a73 Removes mission content key from database
Removes the `S3ContentKey` property from the `DbMission` entity and related DTOs and services.

This change streamlines the mission creation process and simplifies the data model.
The content is no longer stored separately.
2025-10-27 21:31:25 +03:00
15 changed files with 33 additions and 313 deletions

View File

@@ -33,22 +33,6 @@ public class MissionsController(IMissionService missionService) : ControllerBase
return Ok(result); return Ok(result);
} }
/// <summary>
/// Получает текстовые данные миссии на определенном языке
/// </summary>
[HttpGet("{id}/texts/{language}")]
public async Task<IActionResult> GetMissionTexts([FromRoute] int id, [FromRoute] string language, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(language))
return BadRequest("Language parameter is required.");
var textData = await missionService.GetMissionTextAsync(id, language, cancellationToken);
if (textData == null)
return NotFound("Mission or language not found.");
return Ok(textData);
}
/// <summary> /// <summary>
/// Получает подробную информацию о миссии /// Получает подробную информацию о миссии
/// </summary> /// </summary>

View File

@@ -65,7 +65,7 @@ public class UploadMissionRequestValidator : AbstractValidator<UploadMissionRequ
RuleFor(x => x.Difficulty) RuleFor(x => x.Difficulty)
.GreaterThan(0) .GreaterThan(0)
.WithMessage("Difficulty must be greater than 0") .WithMessage("Difficulty must be greater than 0")
.LessThanOrEqualTo(5) .LessThanOrEqualTo(10000)
.WithMessage("Difficulty must be between 1 and 5"); .WithMessage("Difficulty must be between 1 and 10000");
} }
} }

View File

@@ -10,7 +10,6 @@ public record MissionResponse(
int AuthorId, int AuthorId,
string Name, string Name,
int Difficulty, int Difficulty,
string S3ContentKey,
IReadOnlyList<string> Tags, IReadOnlyList<string> Tags,
DateTime CreatedAt, DateTime CreatedAt,
DateTime UpdatedAt DateTime UpdatedAt
@@ -24,7 +23,6 @@ public record MissionResponse(
entity.Author.Id, entity.Author.Id,
entity.Name, entity.Name,
entity.Difficulty, entity.Difficulty,
entity.S3ContentKey,
entity.MissionTags.Select(mt => mt.Tag.Name).Distinct().OrderBy(name => name).ToList(), entity.MissionTags.Select(mt => mt.Tag.Name).Distinct().OrderBy(name => name).ToList(),
entity.CreatedAt, entity.CreatedAt,
entity.UpdatedAt entity.UpdatedAt

View File

@@ -11,6 +11,4 @@ public record SubmitSolutionRequest(
[Required] [StringLength(16)] string Language, [Required] [StringLength(16)] string Language,
[Required] [StringLength(16)] string LanguageVersion, [Required] [StringLength(16)] string LanguageVersion,
[Required] [StringLength(10000, MinimumLength = 1)] string SourceCode, [Required] [StringLength(10000, MinimumLength = 1)] string SourceCode,
int? ContestId, int? ContestId);
SubmissionSourceType SourceType = SubmissionSourceType.Direct
);

View File

@@ -2,7 +2,9 @@ using System;
using System.Linq; using System.Linq;
using LiquidCode.Api.Submits.Requests; using LiquidCode.Api.Submits.Requests;
using LiquidCode.Api.Submits.Responses; using LiquidCode.Api.Submits.Responses;
using LiquidCode.Domain.Services.Contests;
using LiquidCode.Domain.Services.Submits; using LiquidCode.Domain.Services.Submits;
using LiquidCode.Infrastructure.Database.Entities;
using LiquidCode.Infrastructure.External.TestingModule; using LiquidCode.Infrastructure.External.TestingModule;
using LiquidCode.Shared.Constants; using LiquidCode.Shared.Constants;
using LiquidCode.Shared.Extensions; using LiquidCode.Shared.Extensions;
@@ -44,6 +46,9 @@ public class SubmitController(
if (!ModelState.IsValid) if (!ModelState.IsValid)
return BadRequest(ModelState); return BadRequest(ModelState);
var contestId = request.ContestId;
var contest = contestId == null ? null : await _contestService.GetAsync(contestId.Value, cancellationToken);
var solution = await _submitService.SubmitSolutionAsync( var solution = await _submitService.SubmitSolutionAsync(
request.MissionId, request.MissionId,
userId, userId,
@@ -51,7 +56,6 @@ public class SubmitController(
request.Language, request.Language,
request.LanguageVersion, request.LanguageVersion,
request.ContestId, request.ContestId,
request.SourceType,
cancellationToken); cancellationToken);
if (solution == null) if (solution == null)

View File

@@ -32,26 +32,6 @@ public interface IMissionRepository : IRepository<DbMission>
/// </summary> /// </summary>
Task SyncTagsAsync(DbMission mission, IEnumerable<int> tagIds, CancellationToken cancellationToken = default); Task SyncTagsAsync(DbMission mission, IEnumerable<int> tagIds, CancellationToken cancellationToken = default);
/// <summary>
/// Получает текстовые данные миссии на определенном языке
/// </summary>
Task<DbMissionPublicTextData?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default);
/// <summary>
/// Получает все доступные языки для миссии
/// </summary>
Task<IEnumerable<string>> GetMissionLanguagesAsync(int missionId, CancellationToken cancellationToken = default);
/// <summary>
/// Добавляет текстовые данные миссии
/// </summary>
Task CreateMissionTextAsync(DbMissionPublicTextData textData, CancellationToken cancellationToken = default);
/// <summary>
/// Добавляет несколько записей текстовых данных миссии
/// </summary>
Task CreateMissionTextsAsync(IEnumerable<DbMissionPublicTextData> textData, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Подсчитывает общее количество миссий /// Подсчитывает общее количество миссий
/// </summary> /// </summary>

View File

@@ -17,16 +17,7 @@ public interface IMissionService
/// <param name="cancellationToken">Токен отмены</param> /// <param name="cancellationToken">Токен отмены</param>
/// <returns>Созданная модель миссии или null, если загрузка не удалась</returns> /// <returns>Созданная модель миссии или null, если загрузка не удалась</returns>
Task<MissionResponse?> UploadMissionAsync(UploadMissionRequest form, int userId, CancellationToken cancellationToken = default); Task<MissionResponse?> UploadMissionAsync(UploadMissionRequest form, int userId, CancellationToken cancellationToken = default);
/// <summary>
/// Получает текстовые данные миссии на определенном языке
/// </summary>
/// <param name="missionId">ID миссии</param>
/// <param name="language">Код языка</param>
/// <param name="cancellationToken">Токен отмены</param>
/// <returns>Текстовые данные миссии в виде строки JSON или null, если не найдено</returns>
Task<string?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Получает постраничный список миссий /// Получает постраничный список миссий
/// </summary> /// </summary>

View File

@@ -49,39 +49,10 @@ public class MissionService : IMissionService
var tempDir = Path.GetTempPath(); var tempDir = Path.GetTempPath();
var unpackFolder = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); var unpackFolder = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
var packageZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip"); var packageZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip");
var statementsZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip");
try try
{ {
// Сохранить загруженный файл // Получить юзера
_logger.LogInformation("Saving mission file: {FileName}", form.MissionFile.Name);
using (var fileStream = System.IO.File.Open(packageZipPath, FileMode.OpenOrCreate))
{
await form.MissionFile.CopyToAsync(fileStream, cancellationToken);
}
// Распаковать ZIP файл
_logger.LogInformation("Extracting mission ZIP to: {UnpackFolder}", unpackFolder);
ZipFile.ExtractToDirectory(packageZipPath, unpackFolder);
// Проверить, существует ли папка statement-sections
var statementSectionsPath = Path.Combine(unpackFolder, MissionStatementPaths.StatementSectionsFolder);
if (!Directory.Exists(statementSectionsPath))
{
_logger.LogError("statement-sections folder not found in mission ZIP");
return null;
}
// Упаковать разделы утверждений
_logger.LogInformation("Creating statements ZIP: {StatementsZipPath}", statementsZipPath);
ZipFile.CreateFromDirectory(statementSectionsPath, statementsZipPath, CompressionLevel.SmallestSize, false);
// Загрузить на S3
_logger.LogInformation("Uploading mission files to S3");
var privateKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PrivateProblems, packageZipPath);
var contentKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PublicContent, statementsZipPath);
// Создать миссию в базе данных
var existingUser = await _userRepository.FindByIdAsync(userId, cancellationToken); var existingUser = await _userRepository.FindByIdAsync(userId, cancellationToken);
if (existingUser == null) if (existingUser == null)
{ {
@@ -89,12 +60,23 @@ public class MissionService : IMissionService
return null; return null;
} }
// Сохранить загруженный файл
_logger.LogInformation("Saving mission file: {FileName}", form.MissionFile.Name);
using (var fileStream = System.IO.File.Open(packageZipPath, FileMode.OpenOrCreate))
{
await form.MissionFile.CopyToAsync(fileStream, cancellationToken);
}
// Загрузить на S3
_logger.LogInformation("Uploading mission files to S3");
var privateKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PrivateProblems, packageZipPath);
// Создать миссию в базе данных
var dbMission = new DbMission var dbMission = new DbMission
{ {
Author = existingUser, Author = existingUser,
Name = form.Name, Name = form.Name,
S3PrivateKey = privateKey, S3PrivateKey = privateKey,
S3ContentKey = contentKey,
Difficulty = form.Difficulty, Difficulty = form.Difficulty,
CreatedAt = DateTime.UtcNow, CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow UpdatedAt = DateTime.UtcNow
@@ -102,27 +84,6 @@ public class MissionService : IMissionService
await _missionRepository.CreateAsync(dbMission, cancellationToken); await _missionRepository.CreateAsync(dbMission, cancellationToken);
// Распарсить и сохранить текстовые данные миссии
var missionTexts = ExtractMissionTexts(statementSectionsPath, dbMission.Id);
// Обновить имя миссии из русского языка, если доступно, иначе из первого доступного языка
var russianText = missionTexts.FirstOrDefault(t => t.Language == "russian");
if (russianText != null)
{
var russianData = JsonSerializer.Deserialize<JsonMissionData>(russianText.Data, JsonSerializerOptions);
if (russianData?.Name != null)
dbMission.Name = russianData.Name;
}
else if (missionTexts.Count > 0)
{
var firstData = JsonSerializer.Deserialize<JsonMissionData>(missionTexts[0].Data, JsonSerializerOptions);
if (firstData?.Name != null)
dbMission.Name = firstData.Name;
}
// Добавить текстовые данные миссии в базу данных
await _missionRepository.CreateMissionTextsAsync(missionTexts, cancellationToken);
// Обработать теги // Обработать теги
await SyncMissionTagsAsync(dbMission, form.Tags, cancellationToken); await SyncMissionTagsAsync(dbMission, form.Tags, cancellationToken);
@@ -140,34 +101,7 @@ public class MissionService : IMissionService
finally finally
{ {
// Очистить временные файлы // Очистить временные файлы
CleanupTemporaryFiles(unpackFolder, packageZipPath, statementsZipPath); CleanupTemporaryFiles(unpackFolder, packageZipPath);
}
}
public async Task<string?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default)
{
try
{
var mission = await _missionRepository.FindByIdAsync(missionId, cancellationToken);
if (mission == null)
{
_logger.LogWarning("Mission not found: {MissionId}", missionId);
return null;
}
var textData = await _missionRepository.GetMissionTextAsync(missionId, language, cancellationToken);
if (textData == null)
{
_logger.LogWarning("Mission text not found: {MissionId}, {Language}", missionId, language);
return null;
}
return textData.Data;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting mission text: {MissionId}, {Language}", missionId, language);
return null;
} }
} }
@@ -263,79 +197,17 @@ public class MissionService : IMissionService
await _missionRepository.SyncTagsAsync(mission, allTags.Values.Select(t => t.Id), cancellationToken); await _missionRepository.SyncTagsAsync(mission, allTags.Values.Select(t => t.Id), cancellationToken);
} }
private List<DbMissionPublicTextData> ExtractMissionTexts(string statementSectionsPath, int missionId) private void CleanupTemporaryFiles(params string[] paths)
{
var missionTexts = new List<DbMissionPublicTextData>();
var directoryInfo = new DirectoryInfo(statementSectionsPath);
foreach (var languageDir in directoryInfo.GetDirectories())
{
try
{
var data = GetDataFromStatementSections(languageDir);
var json = JsonSerializer.Serialize(data, JsonSerializerOptions);
missionTexts.Add(new DbMissionPublicTextData
{
MissionId = missionId,
Language = languageDir.Name,
Data = json
});
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error extracting mission text for language: {Language}", languageDir.Name);
}
}
return missionTexts;
}
private JsonMissionData GetDataFromStatementSections(DirectoryInfo dir)
{
var files = dir.GetFiles();
var data = new JsonMissionData
{
Name = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.NameFile).FullName),
Input = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.InputFile).FullName),
Output = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.OutputFile).FullName),
Legend = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.LegendFile).FullName),
Examples = [],
ExampleAnswers = []
};
var exampleFiles = dir.GetFiles()
.Where(f => f.Name.StartsWith(MissionStatementPaths.ExampleFilePrefix))
.OrderBy(f =>
{
var numberPart = f.Name[MissionStatementPaths.ExampleFilePrefix.Length..];
if (numberPart.Contains('.'))
numberPart = numberPart[..numberPart.IndexOf(".", StringComparison.Ordinal)];
return int.TryParse(numberPart, out var num) ? num : int.MaxValue;
});
foreach (var exampleFile in exampleFiles)
{
var content = System.IO.File.ReadAllText(exampleFile.FullName);
if (exampleFile.Name.EndsWith("a"))
data.ExampleAnswers.Add(content);
else
data.Examples.Add(content);
}
return data;
}
private void CleanupTemporaryFiles(string unpackFolder, string packageZipPath, string statementsZipPath)
{ {
try try
{ {
if (Directory.Exists(unpackFolder)) foreach (var path in paths)
Directory.Delete(unpackFolder, true); {
if (System.IO.File.Exists(packageZipPath)) if (Directory.Exists(path))
System.IO.File.Delete(packageZipPath); Directory.Delete(path, true);
if (System.IO.File.Exists(statementsZipPath)) else if (System.IO.File.Exists(path))
System.IO.File.Delete(statementsZipPath); System.IO.File.Delete(path);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -343,16 +215,3 @@ public class MissionService : IMissionService
} }
} }
} }
/// <summary>
/// Внутренняя модель для структуры данных описания миссии
/// </summary>
internal class JsonMissionData
{
public string Name { get; set; } = "";
public string Input { get; set; } = "";
public string Output { get; set; } = "";
public string Legend { get; set; } = "";
public List<string> Examples { get; set; } = [];
public List<string> ExampleAnswers { get; set; } = [];
}

View File

@@ -41,7 +41,6 @@ public class SubmitService : ISubmitService
string language, string language,
string languageVersion, string languageVersion,
int? contestId, int? contestId,
SubmissionSourceType sourceType,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
try try
@@ -70,6 +69,7 @@ public class SubmitService : ISubmitService
} }
DbContest? contest = null; DbContest? contest = null;
var finalSourceType = sourceType; var finalSourceType = sourceType;
if (contestId.HasValue) if (contestId.HasValue)
{ {

View File

@@ -22,9 +22,6 @@ public class DbMission : ISoftDeletable, ITimestamped
[StringLength(256)] [StringLength(256)]
public string S3PrivateKey { get; init; } = ""; public string S3PrivateKey { get; init; } = "";
[StringLength(256)]
public string S3ContentKey { get; set; } = "";
public int Difficulty { get; init; } public int Difficulty { get; init; }
public ICollection<DbMissionTag> MissionTags { get; init; } = new HashSet<DbMissionTag>(); public ICollection<DbMissionTag> MissionTags { get; init; } = new HashSet<DbMissionTag>();

View File

@@ -1,27 +0,0 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace LiquidCode.Infrastructure.Database.Entities;
/// <summary>
/// Сущность текстовых данных миссии с составным индексом
/// </summary>
[Index(nameof(MissionId), nameof(Language), IsUnique = true)]
[Index(nameof(Language))]
public class DbMissionPublicTextData : ITimestamped
{
public int Id { get; init; }
[Required]
public int? MissionId { get; init; }
[StringLength(64)]
public string Language { get; init; } = "";
[StringLength(30000)]
public string Data { get; init; } = "";
// Timestamps
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -13,7 +13,6 @@ public class LiquidDbContext : DbContext
public DbSet<DbUser> Users { get; set; } = null!; public DbSet<DbUser> Users { get; set; } = null!;
public DbSet<DbRefreshToken> RefreshTokens { get; set; } = null!; public DbSet<DbRefreshToken> RefreshTokens { get; set; } = null!;
public DbSet<DbMission> Missions { get; set; } = null!; public DbSet<DbMission> Missions { get; set; } = null!;
public DbSet<DbMissionPublicTextData> MissionsTextData { get; set; } = null!;
public DbSet<DbSolution> Solutions { get; set; } = null!; public DbSet<DbSolution> Solutions { get; set; } = null!;
public DbSet<DbUserSubmission> UserSubmits { get; set; } = null!; public DbSet<DbUserSubmission> UserSubmits { get; set; } = null!;
public DbSet<DbArticle> Articles { get; set; } = null!; public DbSet<DbArticle> Articles { get; set; } = null!;

View File

@@ -124,22 +124,6 @@ public class MissionRepository : IMissionRepository
await _dbContext.SaveChangesAsync(cancellationToken); await _dbContext.SaveChangesAsync(cancellationToken);
} }
public async Task<DbMissionPublicTextData?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default) =>
await _dbContext.MissionsTextData
.FirstOrDefaultAsync(m => m.MissionId == missionId && m.Language == language, cancellationToken);
public async Task<IEnumerable<string>> GetMissionLanguagesAsync(int missionId, CancellationToken cancellationToken = default) =>
await _dbContext.MissionsTextData
.Where(m => m.MissionId == missionId)
.Select(m => m.Language)
.ToListAsync(cancellationToken);
public async Task CreateMissionTextAsync(DbMissionPublicTextData textData, CancellationToken cancellationToken = default) =>
await _dbContext.MissionsTextData.AddAsync(textData, cancellationToken);
public async Task CreateMissionTextsAsync(IEnumerable<DbMissionPublicTextData> textData, CancellationToken cancellationToken = default) =>
await _dbContext.MissionsTextData.AddRangeAsync(textData, cancellationToken);
public async Task<int> CountMissionsAsync(CancellationToken cancellationToken = default) => public async Task<int> CountMissionsAsync(CancellationToken cancellationToken = default) =>
await _dbContext.Set<DbMission>().CountAsync(cancellationToken); await _dbContext.Set<DbMission>().CountAsync(cancellationToken);
} }

View File

@@ -13,7 +13,7 @@ public static class AppConstants
/// <summary> /// <summary>
/// Время истечения JWT токена в минутах /// Время истечения JWT токена в минутах
/// </summary> /// </summary>
public const int JwtExpirationMinutes = 10; public const int JwtExpirationMinutes = 1440; // TODO: убавить, день для удобства
/// <summary> /// <summary>
/// Время истечения токена обновления в днях /// Время истечения токена обновления в днях

View File

@@ -1,47 +0,0 @@
#!/bin/bash
# Script to update namespaces in the LiquidCode project
cd "$(dirname "$0")/LiquidCode"
echo "Updating namespaces..."
# Update Infrastructure/Database/Entities
find Infrastructure/Database/Entities -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Models\.Database/namespace LiquidCode.Infrastructure.Database.Entities/g' {} \;
# Update Infrastructure/Database
sed -i 's/namespace LiquidCode\.Db/namespace LiquidCode.Infrastructure.Database/g' Infrastructure/Database/LiquidDbContext.cs
sed -i 's/namespace LiquidCode\.Db/namespace LiquidCode.Infrastructure.Database/g' Infrastructure/Database/ConnectionStringParser.cs
sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' Infrastructure/Database/LiquidDbContext.cs
# Update Domain/Services
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.AuthService/namespace LiquidCode.Domain.Services.Authentication/g' {} \;
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.MissionService/namespace LiquidCode.Domain.Services.Missions/g' {} \;
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.SubmitService/namespace LiquidCode.Domain.Services.Submits/g' {} \;
# Update Domain/Repositories
find Domain/Repositories -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Repositories/namespace LiquidCode.Domain.Repositories/g' {} \;
# Update Infrastructure/External
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.S3ClientService/namespace LiquidCode.Infrastructure.External.S3/g' {} \;
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.TestingModuleHttpClient/namespace LiquidCode.Infrastructure.External.TestingModule/g' {} \;
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services/namespace LiquidCode.Infrastructure.External.S3/g' {} \;
# Update Shared
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Models\.Constants/namespace LiquidCode.Shared.Constants/g' {} \;
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Extensions/namespace LiquidCode.Shared.Extensions/g' {} \;
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Middleware/namespace LiquidCode.Shared.Middleware/g' {} \;
# Update using statements in all new files
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' {} \;
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Repositories/using LiquidCode.Domain.Repositories/g' {} \;
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Extensions/using LiquidCode.Shared.Extensions/g' {} \;
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Tools/using LiquidCode.Shared.Tools/g' {} \;
find Infrastructure -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
find Infrastructure -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' {} \;
find Shared -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
echo "Namespaces updated successfully!"