diff --git a/backend/CookifyAPI/CookifyAPI.sln.DotSettings.user b/backend/CookifyAPI/CookifyAPI.sln.DotSettings.user
index 7bffe5d..d2f57e3 100644
--- a/backend/CookifyAPI/CookifyAPI.sln.DotSettings.user
+++ b/backend/CookifyAPI/CookifyAPI.sln.DotSettings.user
@@ -1,4 +1,5 @@
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Controllers/SearchController.cs b/backend/CookifyAPI/CookifyAPI/Controllers/SearchController.cs
new file mode 100644
index 0000000..eb0382b
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Controllers/SearchController.cs
@@ -0,0 +1,39 @@
+using CookifyAPI.Models.DTOs.Requests;
+using CookifyAPI.Models.DTOs.Search;
+using CookifyAPI.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CookifyAPI.Controllers;
+
+[ApiController]
+[Route("api/search")]
+public class SearchController(
+ ISearchService searchService,
+ IRecipeService recipeService) : ControllerBase
+{
+ [HttpGet("tags")]
+ public async Task SearchTags([FromQuery] string? name, [FromQuery] int limit = 20)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return Ok(Array.Empty()); // Пустой массив, если нет запроса
+
+ var results = await searchService.SearchTagsAsync(name, limit);
+ return Ok(results);
+ }
+
+ [HttpGet("ingredients")]
+ public async Task SearchIngredients([FromQuery] string? name, [FromQuery] int limit = 20)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return Ok(Array.Empty());
+ var results = await searchService.SearchIngredientsAsync(name, limit);
+ return Ok(results);
+ }
+
+ [HttpGet("recipes")]
+ public async Task SearchRecipes([FromQuery] RecipeSearchRequest request)
+ {
+ var results = await recipeService.SearchRecipesDetailedAsync(request);
+ return Ok(results);
+ }
+}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/CookifyAPI.csproj b/backend/CookifyAPI/CookifyAPI/CookifyAPI.csproj
index db6946e..153941d 100644
--- a/backend/CookifyAPI/CookifyAPI/CookifyAPI.csproj
+++ b/backend/CookifyAPI/CookifyAPI/CookifyAPI.csproj
@@ -14,6 +14,7 @@
+
diff --git a/backend/CookifyAPI/CookifyAPI/Extensions/ApplicationServiceExtensions.cs b/backend/CookifyAPI/CookifyAPI/Extensions/ApplicationServiceExtensions.cs
index af4f11c..c2561b1 100644
--- a/backend/CookifyAPI/CookifyAPI/Extensions/ApplicationServiceExtensions.cs
+++ b/backend/CookifyAPI/CookifyAPI/Extensions/ApplicationServiceExtensions.cs
@@ -1,11 +1,12 @@
using System.Reflection;
using CookifyAPI.Services;
+using Meilisearch;
namespace CookifyAPI.Extensions;
public static class ApplicationServiceExtensions
{
- public static IServiceCollection AddApplicationServices(this IServiceCollection services)
+ public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddScoped();
services.AddScoped();
@@ -14,7 +15,16 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddScoped();
services.AddScoped();
services.AddScoped();
-
+
+ var meiliUrl = configuration["MeilisearchSettings:Url"];
+ var meiliKey = configuration["MeilisearchSettings:MasterKey"];
+
+ if (string.IsNullOrEmpty(meiliUrl) || string.IsNullOrEmpty(meiliKey))
+ throw new InvalidOperationException("Meilisearch Url or MasterKey is not configured.");
+
+ // Регистрируем клиент (Singleton, т.к. он держит HTTP-соединения открытыми)
+ services.AddSingleton(new MeilisearchClient(meiliUrl, meiliKey));
+ services.AddScoped();
// Автоматический поиск профилей AutoMapper
//services.AddAutoMapper(Assembly.GetExecutingAssembly());
diff --git a/backend/CookifyAPI/CookifyAPI/Extensions/MigrationExtensions.cs b/backend/CookifyAPI/CookifyAPI/Extensions/MigrationExtensions.cs
index de133a4..916e306 100644
--- a/backend/CookifyAPI/CookifyAPI/Extensions/MigrationExtensions.cs
+++ b/backend/CookifyAPI/CookifyAPI/Extensions/MigrationExtensions.cs
@@ -1,38 +1,84 @@
using CookifyAPI.Data;
+using CookifyAPI.Models.DTOs.Search;
+using CookifyAPI.Services;
using Microsoft.EntityFrameworkCore;
namespace CookifyAPI.Extensions;
public static class MigrationExtensions
{
- public static void ApplyMigrations(this IApplicationBuilder app)
+ public static async Task ApplyMigrations(this IApplicationBuilder app)
{
- using (var scope = app.ApplicationServices.CreateScope())
- {
- var db = scope.ServiceProvider.GetRequiredService();
-
- // Попытка применить миграции несколько раз (защита от медленного старта БД)
- int retries = 20;
- while (retries > 0)
+ using var scope = app.ApplicationServices.CreateScope();
+ var services = scope.ServiceProvider;
+
+ var db = services.GetRequiredService();
+ var searchService = services.GetRequiredService();
+
+ // Попытка применить миграции несколько раз (защита от медленного старта БД)
+ var retries = 20;
+ var dbReady = false;
+
+ while (retries > 0)
+ try
{
- try
- {
- db.Database.Migrate();
- Console.WriteLine("Database check/migration completed successfully.");
- break;
- }
- catch (Exception ex)
+ db.Database.Migrate();
+ Console.WriteLine("Database check/migration completed successfully.");
+ dbReady = true;
+ break;
+ }
+ catch (Exception ex)
+ {
+ retries--;
+ if (retries == 0)
{
- retries--;
- if (retries == 0)
- {
- Console.WriteLine("CRITICAL ERROR: Database is not ready after multiple retries.");
- throw; // Приложение упадет, и Docker его перезапустит
- }
- Console.WriteLine($"WARNING: Database is starting up... waiting. ({retries} attempts left)");
- Thread.Sleep(10000);
+ Console.WriteLine("CRITICAL ERROR: Database is not ready after multiple retries.");
+ throw; // Приложение упадет, и Docker его перезапустит
}
+
+ Console.WriteLine($"WARNING: Database is starting up... waiting. ({retries} attempts left)");
+ Thread.Sleep(10000);
+ }
+
+ if (dbReady)
+ try
+ {
+ Console.WriteLine("Starting Meilisearch index synchronization...");
+ await searchService.SetupIndicesAsync();
+
+ // Синхронизируем Ингредиенты
+ var ingredients = await db.Ingredients
+ .AsNoTracking()
+ .Select(i => new IngredientSearchDocument(i.Id, i.Name, i.Calories100g, i.Protein100g, i.Fat100g, i.Carb100g))
+ .ToListAsync();
+
+ if (ingredients.Count != 0)
+ await searchService.IndexIngredientsAsync(ingredients);
+
+ // Синхронизируем Теги
+ var tags = await db.Tags
+ .AsNoTracking()
+ .Select(t => new TagSearchDocument(t.Id, t.Name))
+ .ToListAsync();
+
+ if (tags.Count != 0)
+ await searchService.IndexTagsAsync(tags);
+
+ // Синхронизируем Рецепты
+ var recipes = await db.Recipes
+ .AsNoTracking()
+ .Select(r => new RecipeSearchDocument(r.Id, r.Title))
+ .ToListAsync();
+
+ if (recipes.Count != 0)
+ await searchService.IndexRecipesAsync(recipes);
+
+ Console.WriteLine("Meilisearch synchronization completed successfully.");
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"ERROR: Failed to sync Meilisearch: {ex.Message}");
}
- }
}
}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Models/DTOs/Requests/RecipeSearchRequest.cs b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Requests/RecipeSearchRequest.cs
new file mode 100644
index 0000000..afe567a
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Requests/RecipeSearchRequest.cs
@@ -0,0 +1,15 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace CookifyAPI.Models.DTOs.Requests;
+
+public record RecipeSearchRequest(
+ string? Title,
+ int? MaxCookingTime,
+ float? MinCarb, float? MaxCarb,
+ float? MinProtein, float? MaxProtein,
+ float? MinFat, float? MaxFat,
+ float? MinCalories, float? MaxCalories,
+ int? Difficulty,
+ [FromQuery] int[]? TagIds,
+ [FromQuery] int[]? IngredientIds
+);
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/IngredientSearchDocument.cs b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/IngredientSearchDocument.cs
new file mode 100644
index 0000000..bf30c15
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/IngredientSearchDocument.cs
@@ -0,0 +1,10 @@
+namespace CookifyAPI.Models.DTOs.Search;
+
+public record IngredientSearchDocument(
+ int Id,
+ string Name,
+ float? Calories100g,
+ float? Protein100g,
+ float? Fat100g,
+ float? Carb100g
+);
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/RecipeSearchDocument.cs b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/RecipeSearchDocument.cs
new file mode 100644
index 0000000..5c35088
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/RecipeSearchDocument.cs
@@ -0,0 +1,6 @@
+namespace CookifyAPI.Models.DTOs.Search;
+
+public record RecipeSearchDocument(
+ int Id,
+ string Title
+);
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/TagSearchDocument.cs b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/TagSearchDocument.cs
new file mode 100644
index 0000000..dadb928
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Models/DTOs/Search/TagSearchDocument.cs
@@ -0,0 +1,6 @@
+namespace CookifyAPI.Models.DTOs.Search;
+
+public record TagSearchDocument(
+ int Id,
+ string Name
+);
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Models/Settings/MeilishSettings.cs b/backend/CookifyAPI/CookifyAPI/Models/Settings/MeilishSettings.cs
new file mode 100644
index 0000000..68e7f92
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Models/Settings/MeilishSettings.cs
@@ -0,0 +1,7 @@
+namespace CookifyAPI.Models.Settings;
+
+public record MeilisearchSettings
+{
+ public string Url { get; init; } = string.Empty;
+ public string MasterKey { get; init; } = string.Empty;
+}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Program.cs b/backend/CookifyAPI/CookifyAPI/Program.cs
index c3a9db6..bf8d688 100644
--- a/backend/CookifyAPI/CookifyAPI/Program.cs
+++ b/backend/CookifyAPI/CookifyAPI/Program.cs
@@ -6,7 +6,7 @@
builder.Services.AddIdentityServices(builder.Configuration);
builder.Services.AddSwaggerServices();
-builder.Services.AddApplicationServices();
+builder.Services.AddApplicationServices(builder.Configuration);
builder.Services.AddWebServices();
var app = builder.Build();
@@ -22,7 +22,7 @@
});
}
-app.ApplyMigrations();
+await app.ApplyMigrations();
app.UseCors("AllowAll");
app.UseAuthentication();
diff --git a/backend/CookifyAPI/CookifyAPI/Services/Implementations/MeilisearchService.cs b/backend/CookifyAPI/CookifyAPI/Services/Implementations/MeilisearchService.cs
new file mode 100644
index 0000000..6129f67
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Services/Implementations/MeilisearchService.cs
@@ -0,0 +1,62 @@
+using CookifyAPI.Models.DTOs.Search;
+using Meilisearch;
+
+namespace CookifyAPI.Services;
+
+public class MeilisearchService(MeilisearchClient client) : ISearchService
+{
+ private const string TagsIndex = "tags";
+ private const string IngredientsIndex = "ingredients";
+ private const string RecipesIndex = "recipes";
+
+ public async Task> SearchTagsAsync(string query, int limit = 20)
+ {
+ var index = client.Index(TagsIndex);
+ var searchQuery = new SearchQuery { Limit = limit };
+
+ var result = await index.SearchAsync(query, searchQuery);
+ return result.Hits;
+ }
+
+ public async Task> SearchIngredientsAsync(string query, int limit = 20)
+ {
+ var index = client.Index(IngredientsIndex);
+ var searchQuery = new SearchQuery { Limit = limit };
+
+ var result = await index.SearchAsync(query, searchQuery);
+ return result.Hits;
+ }
+
+ public async Task SearchRecipeIdsAsync(string query, int limit = 30)
+ {
+ var index = client.Index(RecipesIndex);
+ var searchQuery = new SearchQuery { Limit = limit };
+
+ var result = await index.SearchAsync(query, searchQuery);
+ return result.Hits.Select(x => x.Id).ToArray();
+ }
+
+ public async Task IndexTagsAsync(IEnumerable tags)
+ {
+ var index = client.Index(TagsIndex);
+ await index.AddDocumentsAsync(tags);
+ }
+
+ public Task IndexIngredientsAsync(IEnumerable ingredients)
+ {
+ var index = client.Index(IngredientsIndex);
+ return index.AddDocumentsAsync(ingredients);
+ }
+
+ public async Task IndexRecipesAsync(IEnumerable recipes)
+ {
+ await client.Index("recipes").AddDocumentsAsync(recipes);
+ }
+
+ public async Task SetupIndicesAsync()
+ {
+ await client.Index(TagsIndex).UpdateSearchableAttributesAsync(new[] { "name" });
+ await client.Index(IngredientsIndex).UpdateSearchableAttributesAsync(new[] { "name" });
+ await client.Index(RecipesIndex).UpdateSearchableAttributesAsync(new[] { "title" });
+ }
+}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Services/Implementations/RecipeService.cs b/backend/CookifyAPI/CookifyAPI/Services/Implementations/RecipeService.cs
index 5b1fa73..eea8793 100644
--- a/backend/CookifyAPI/CookifyAPI/Services/Implementations/RecipeService.cs
+++ b/backend/CookifyAPI/CookifyAPI/Services/Implementations/RecipeService.cs
@@ -2,13 +2,16 @@
using CookifyAPI.Models.DTOs;
using CookifyAPI.Models.DTOs.Pagination;
using CookifyAPI.Models.DTOs.Recipes;
+using CookifyAPI.Models.DTOs.Requests;
using CookifyAPI.Models.Entities;
using CookifyAPI.Services;
using Microsoft.EntityFrameworkCore;
namespace CookifyAPI.Services;
-public class RecipeService(AppDbContext context) : IRecipeService
+public class RecipeService(
+ AppDbContext context,
+ ISearchService searchService) : IRecipeService
{
public async Task> GetRecipesListAsync()
{
@@ -29,8 +32,6 @@ public async Task> GetRecipesListAsync()
.FirstOrDefault()
})
.ToListAsync();
-
-
}
public async Task GetRecipeByIdAsync(int id)
@@ -175,4 +176,95 @@ public async Task> GetRecipesKeysetAsync(int? l
LastId = newLastId
};
}
+
+ public async Task> SearchRecipesDetailedAsync(RecipeSearchRequest request)
+ {
+ var query = context.Recipes.AsNoTracking();
+
+ if (!string.IsNullOrWhiteSpace(request.Title))
+ {
+ var recipeIds = await searchService.SearchRecipeIdsAsync(request.Title);
+
+ // Если Meili ничего не нашел, возвращаем пустой список, чтобы не делать запрос к БД
+ if (recipeIds.Length == 0)
+ {
+ Console.WriteLine($"No recipes found for {request.Title}");
+ return new List();
+ }
+
+ query = query.Where(r => recipeIds.Contains(r.Id));
+ }
+
+ if (request.MaxCookingTime.HasValue) query = query.Where(r => r.CookingTimeMin <= request.MaxCookingTime);
+ if (request.Difficulty.HasValue) query = query.Where(r => r.Difficulty == request.Difficulty);
+
+ // Фильтры по БЖУ и Калориям
+ if (request.MinCalories.HasValue) query = query.Where(r => r.Calories100g >= request.MinCalories);
+ if (request.MaxCalories.HasValue) query = query.Where(r => r.Calories100g <= request.MaxCalories);
+
+ if (request.MinProtein.HasValue) query = query.Where(r => r.Protein100g >= request.MinProtein);
+ if (request.MaxProtein.HasValue) query = query.Where(r => r.Protein100g <= request.MaxProtein);
+
+ if (request.MinFat.HasValue) query = query.Where(r => r.Fat100g >= request.MinFat);
+ if (request.MaxFat.HasValue) query = query.Where(r => r.Fat100g <= request.MaxFat);
+
+ if (request.MinCarb.HasValue) query = query.Where(r => r.Carb100g >= request.MinCarb);
+ if (request.MaxCarb.HasValue) query = query.Where(r => r.Carb100g <= request.MaxCarb);
+
+ if (request.TagIds is { Length: > 0 })
+ {
+ query = query.Where(r => r.Tags.Any(t => request.TagIds.Contains(t.TagId)));
+ }
+
+ if (request.IngredientIds is { Length: > 0 })
+ {
+ query = query.Where(r => r.Ingredients.Any(i => request.IngredientIds.Contains(i.IngredientId)));
+ }
+
+ var recipes = await query
+ .AsSplitQuery()
+ .Select(r => new RecipeDetailDto
+ {
+ Id = r.Id,
+ Title = r.Title,
+ CookingTimeMinutes = r.CookingTimeMin,
+ Servings = r.Servings,
+ AuthorId = r.AuthorId,
+ Calories100g = r.Calories100g,
+ Protein100g = r.Protein100g,
+ Fat100g = r.Fat100g,
+ Carb100g = r.Carb100g,
+ CreatedAt = r.CreatedAt,
+ Description = r.Description,
+ Difficulty = r.Difficulty,
+
+ // Маппим связанные списки (Коллекции)
+ Images = r.Images.Select(img => new RecipeImageDto
+ {
+ Id = img.Id,
+ Url = img.Url // Подставьте свои поля
+ }).ToList(),
+
+ Steps = r.Steps.Select(step => new RecipeStepDto
+ {
+ Id = step.Id,
+ StepNumber = step.StepNumber,
+ Description = step.Description
+ }).ToList(),
+
+ // Для M2M: Достаем Name из связанной таблицы Tag
+ Tags = r.Tags.Select(m2m => m2m.Tag.Name).ToList(),
+
+ // Для M2M: Создаем IngredientDto из связанной таблицы Ingredient
+ Ingredients = r.Ingredients.Select(m2m => new IngredientDto
+ {
+ Id = m2m.Ingredient.Id,
+ Name = m2m.Ingredient.Name,
+ Amount = m2m.Amount // Предположим, количество хранится в M2M таблице
+ }).ToList()
+ })
+ .ToListAsync();
+
+ return recipes;
+ }
}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Services/Interfaces/IRecipeService.cs b/backend/CookifyAPI/CookifyAPI/Services/Interfaces/IRecipeService.cs
index f6f7812..43a0f8c 100644
--- a/backend/CookifyAPI/CookifyAPI/Services/Interfaces/IRecipeService.cs
+++ b/backend/CookifyAPI/CookifyAPI/Services/Interfaces/IRecipeService.cs
@@ -1,5 +1,6 @@
using CookifyAPI.Models.DTOs.Pagination;
using CookifyAPI.Models.DTOs.Recipes;
+using CookifyAPI.Models.DTOs.Requests;
namespace CookifyAPI.Services;
@@ -9,4 +10,5 @@ public interface IRecipeService
Task GetRecipeByIdAsync(int id);
Task> GetRecipesOffsetAsync(int page);
Task> GetRecipesKeysetAsync(int? lastId);
+ Task> SearchRecipesDetailedAsync(RecipeSearchRequest request);
}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/Services/Interfaces/ISearchService.cs b/backend/CookifyAPI/CookifyAPI/Services/Interfaces/ISearchService.cs
new file mode 100644
index 0000000..18689e7
--- /dev/null
+++ b/backend/CookifyAPI/CookifyAPI/Services/Interfaces/ISearchService.cs
@@ -0,0 +1,19 @@
+using CookifyAPI.Models.DTOs.Search;
+
+namespace CookifyAPI.Services;
+
+public interface ISearchService
+{
+ // Методы поиска (возвращают готовые списки)
+ Task> SearchTagsAsync(string query, int limit = 20);
+ Task> SearchIngredientsAsync(string query, int limit = 20);
+ Task SearchRecipeIdsAsync(string query, int limit = 100);
+
+ // Методы синхронизации (добавление пачками для производительности)
+ Task IndexTagsAsync(IEnumerable tags);
+ Task IndexIngredientsAsync(IEnumerable ingredients);
+ Task IndexRecipesAsync(IEnumerable recipes);
+
+ // Первоначальная настройка индексов
+ Task SetupIndicesAsync();
+}
\ No newline at end of file
diff --git a/backend/CookifyAPI/CookifyAPI/appsettings.json b/backend/CookifyAPI/CookifyAPI/appsettings.json
index e3e13f3..5825fb3 100644
--- a/backend/CookifyAPI/CookifyAPI/appsettings.json
+++ b/backend/CookifyAPI/CookifyAPI/appsettings.json
@@ -48,5 +48,10 @@
"SignIn": {
"RequireConfirmedEmail": true
}
+ },
+
+ "MeilisearchSettings": {
+ "Url": "http://localhost:7700",
+ "MasterKey": "replace-me"
}
}
diff --git a/infrastructure/.env.example b/infrastructure/.env.example
index a3b5aa8..7afe545 100644
--- a/infrastructure/.env.example
+++ b/infrastructure/.env.example
@@ -8,6 +8,9 @@ JWT_KEY=GenerateSomeLongRandomStringHereForDevelopment
SMTP_PASSWORD=get_app_password_from_google
SENDER_EMAIL=your_dev_email@gmail.com
+# Meilisearch
+MEILI_MASTER_KEY=YourSuperSecretMasterKey123!
+
# Cloudinary
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
diff --git a/infrastructure/docker-compose.yml b/infrastructure/docker-compose.yml
index ff47054..e1f99eb 100644
--- a/infrastructure/docker-compose.yml
+++ b/infrastructure/docker-compose.yml
@@ -18,7 +18,23 @@ services:
interval: 10s
timeout: 5s
retries: 15
-
+
+ meilisearch:
+ image: getmeili/meilisearch:latest
+ container_name: meilisearch
+ environment:
+ - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
+ - MEILI_ENV=development # Включает красивый UI для отладки
+ - MEILI_NO_ANALYTICS=true # Отключаем сбор телеметрии
+ ports:
+ - "7700:7700"
+ volumes:
+ - meili_data:/meili_data
+ healthcheck:
+ test: [ "CMD", "curl", "-f", "http://localhost:7700/health" ]
+ interval: 10s
+ timeout: 5s
+ retries: 5
cookifyapi:
build:
@@ -28,6 +44,8 @@ services:
depends_on:
db:
condition: service_healthy
+ meilisearch:
+ condition: service_healthy
environment:
- ASPNETCORE_ENVIRONMENT=Development
# Включаем поддержку HTTPS
@@ -45,10 +63,15 @@ services:
- Cloudinary__CloudName=${CLOUDINARY_CLOUD_NAME}
- Cloudinary__ApiKey=${CLOUDINARY_API_KEY}
- Cloudinary__ApiSecret=${CLOUDINARY_API_SECRET}
+
+ # Передаем ключи Meilisearch в ваше API
+ - MeilisearchSettings__Url=http://meilisearch:7700
+ - MeilisearchSettings__MasterKey=${MEILI_MASTER_KEY}
ports:
- "5022:443"
volumes:
- ./dev-certs:/https:ro
volumes:
- mssql_data:
\ No newline at end of file
+ mssql_data:
+ meili_data:
\ No newline at end of file