RecipeController.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using Microsoft.AspNetCore.Mvc;
  2. using Microsoft.EntityFrameworkCore;
  3. using MySqlConnector;
  4. using Recepie.Data;
  5. using Recepie.Models;
  6. using Recepie.ViewModels;
  7. namespace Recepie.Controllers
  8. {
  9. public class RecipeController : Controller
  10. {
  11. private readonly RecipeContext _context;
  12. public RecipeController(RecipeContext context)
  13. {
  14. _context = context;
  15. }
  16. public async Task<IActionResult> Index(string searchTerm = "", string includeIngredients = "", string excludeIngredients = "", string category = "")
  17. {
  18. var recipesQuery = _context.Recipes
  19. .Include(r => r.RecipeIngredients)
  20. .ThenInclude(ri => ri.Ingredient)
  21. .AsQueryable();
  22. // Search by title or description
  23. if (!string.IsNullOrEmpty(searchTerm))
  24. {
  25. recipesQuery = recipesQuery.Where(r =>
  26. (r.Title != null && r.Title.Contains(searchTerm)) ||
  27. (r.Description != null && r.Description.Contains(searchTerm)));
  28. }
  29. // Include ingredients filter
  30. if (!string.IsNullOrEmpty(includeIngredients))
  31. {
  32. var includeIngredientsList = includeIngredients.Split(',').Select(i => i.Trim().ToLower()).Where(i => !string.IsNullOrEmpty(i)).ToList();
  33. if (includeIngredientsList.Any())
  34. {
  35. foreach (var ingredient in includeIngredientsList)
  36. {
  37. recipesQuery = recipesQuery.Where(r => r.RecipeIngredients.Any(ri =>
  38. ri.Ingredient != null && ri.Ingredient.Name != null && ri.Ingredient.Name.ToLower().Contains(ingredient)));
  39. }
  40. }
  41. }
  42. // Exclude ingredients filter
  43. if (!string.IsNullOrEmpty(excludeIngredients))
  44. {
  45. var excludeIngredientsList = excludeIngredients.Split(',').Select(i => i.Trim().ToLower()).Where(i => !string.IsNullOrEmpty(i)).ToList();
  46. if (excludeIngredientsList.Any())
  47. {
  48. foreach (var ingredient in excludeIngredientsList)
  49. {
  50. recipesQuery = recipesQuery.Where(r => !r.RecipeIngredients.Any(ri =>
  51. ri.Ingredient != null && ri.Ingredient.Name != null && ri.Ingredient.Name.ToLower().Contains(ingredient)));
  52. }
  53. }
  54. }
  55. // Order by title
  56. recipesQuery = recipesQuery.OrderBy(r => r.Title);
  57. var recipes = await recipesQuery.ToListAsync();
  58. var viewModel = new RecipeIndexViewModel
  59. {
  60. Recipes = recipes,
  61. SearchTerm = searchTerm,
  62. IncludeIngredients = includeIngredients,
  63. ExcludeIngredients = excludeIngredients,
  64. SelectedCategory = "", // Disabled for now
  65. Categories = new List<string>() // Empty for now
  66. };
  67. return View(viewModel);
  68. }
  69. public async Task<IActionResult> Details(int id)
  70. {
  71. var recipe = await _context.Recipes
  72. .Include(r => r.RecipeIngredients)
  73. .ThenInclude(ri => ri.Ingredient)
  74. .Include(r => r.RecipeSteps)
  75. .FirstOrDefaultAsync(r => r.Id == id);
  76. if (recipe == null)
  77. {
  78. return NotFound();
  79. }
  80. return View(recipe);
  81. }
  82. // Action to serve recipe images from BLOB data
  83. public async Task<IActionResult> GetImage(int recipeId)
  84. {
  85. try
  86. {
  87. var connection = _context.Database.GetDbConnection();
  88. await connection.OpenAsync();
  89. using var command = connection.CreateCommand();
  90. command.CommandText = "SELECT image FROM recepieImage WHERE recepieId = @recipeId";
  91. command.Parameters.Add(new MySqlConnector.MySqlParameter("@recipeId", recipeId));
  92. var result = await command.ExecuteScalarAsync();
  93. await connection.CloseAsync();
  94. if (result == null || result == DBNull.Value)
  95. {
  96. return NotFound();
  97. }
  98. var imageData = (byte[])result;
  99. return File(imageData, "image/jpeg");
  100. }
  101. catch
  102. {
  103. return NotFound();
  104. }
  105. }
  106. // Check if recipe has image without loading BLOB data
  107. public async Task<IActionResult> HasImage(int recipeId)
  108. {
  109. try
  110. {
  111. var connection = _context.Database.GetDbConnection();
  112. await connection.OpenAsync();
  113. using var command = connection.CreateCommand();
  114. command.CommandText = "SELECT COUNT(*) FROM recepieImage WHERE recepieId = @recipeId AND image IS NOT NULL";
  115. command.Parameters.Add(new MySqlConnector.MySqlParameter("@recipeId", recipeId));
  116. var count = await command.ExecuteScalarAsync();
  117. await connection.CloseAsync();
  118. var hasImage = Convert.ToInt32(count) > 0;
  119. return Json(hasImage);
  120. }
  121. catch
  122. {
  123. return Json(false);
  124. }
  125. }
  126. // Action to serve recipe images from BLOB data - Temporarily disabled
  127. /*
  128. public async Task<IActionResult> GetImage(int recipeId, int imageId = 0)
  129. {
  130. RecipeImage? recipeImage;
  131. if (imageId > 0)
  132. {
  133. // Get specific image by ID
  134. recipeImage = await _context.RecipeImages
  135. .FirstOrDefaultAsync(ri => ri.Id == imageId && ri.RecipeId == recipeId);
  136. }
  137. else
  138. {
  139. // Get first image for the recipe
  140. recipeImage = await _context.RecipeImages
  141. .FirstOrDefaultAsync(ri => ri.RecipeId == recipeId);
  142. }
  143. if (recipeImage?.ImageData == null)
  144. {
  145. return NotFound();
  146. }
  147. // Determine content type (default to JPEG if not specified)
  148. string contentType = !string.IsNullOrEmpty(recipeImage.ContentType)
  149. ? recipeImage.ContentType
  150. : "image/jpeg";
  151. return File(recipeImage.ImageData, contentType);
  152. }
  153. */
  154. public IActionResult Create()
  155. {
  156. return View();
  157. }
  158. [HttpPost]
  159. [ValidateAntiForgeryToken]
  160. public async Task<IActionResult> Create([Bind("Id,Title,Description,Difficulty,Url,Time")] Recipe recipe)
  161. {
  162. if (ModelState.IsValid)
  163. {
  164. _context.Add(recipe);
  165. await _context.SaveChangesAsync();
  166. return RedirectToAction(nameof(Index));
  167. }
  168. return View(recipe);
  169. }
  170. public async Task<IActionResult> Edit(int id)
  171. {
  172. var recipe = await _context.Recipes.FindAsync(id);
  173. if (recipe == null)
  174. {
  175. return NotFound();
  176. }
  177. return View(recipe);
  178. }
  179. [HttpPost]
  180. [ValidateAntiForgeryToken]
  181. public async Task<IActionResult> Edit(int id, [Bind("Id,Title,Description,Difficulty,Url,Time")] Recipe recipe)
  182. {
  183. if (id != recipe.Id)
  184. {
  185. return NotFound();
  186. }
  187. if (ModelState.IsValid)
  188. {
  189. try
  190. {
  191. _context.Update(recipe);
  192. await _context.SaveChangesAsync();
  193. }
  194. catch (DbUpdateConcurrencyException)
  195. {
  196. if (!RecipeExists(recipe.Id))
  197. {
  198. return NotFound();
  199. }
  200. else
  201. {
  202. throw;
  203. }
  204. }
  205. return RedirectToAction(nameof(Index));
  206. }
  207. return View(recipe);
  208. }
  209. public async Task<IActionResult> Delete(int id)
  210. {
  211. var recipe = await _context.Recipes.FindAsync(id);
  212. if (recipe == null)
  213. {
  214. return NotFound();
  215. }
  216. return View(recipe);
  217. }
  218. [HttpPost, ActionName("Delete")]
  219. [ValidateAntiForgeryToken]
  220. public async Task<IActionResult> DeleteConfirmed(int id)
  221. {
  222. var recipe = await _context.Recipes.FindAsync(id);
  223. if (recipe != null)
  224. {
  225. _context.Recipes.Remove(recipe);
  226. }
  227. await _context.SaveChangesAsync();
  228. return RedirectToAction(nameof(Index));
  229. }
  230. private bool RecipeExists(int id)
  231. {
  232. return _context.Recipes.Any(e => e.Id == id);
  233. }
  234. // Simplified API endpoint for testing
  235. [HttpGet]
  236. public async Task<IActionResult> Api()
  237. {
  238. var recipes = await _context.Recipes.Take(10).ToListAsync();
  239. return Json(recipes);
  240. }
  241. // Simple test page
  242. public IActionResult SimpleTest()
  243. {
  244. return View();
  245. }
  246. // Database inspection
  247. public async Task<IActionResult> InspectDatabase()
  248. {
  249. try
  250. {
  251. // Try to query the information_schema to get table names
  252. var tablesQuery = @"
  253. SELECT TABLE_NAME
  254. FROM information_schema.TABLES
  255. WHERE TABLE_SCHEMA = 'recept'";
  256. var connection = _context.Database.GetDbConnection();
  257. await connection.OpenAsync();
  258. using var command = connection.CreateCommand();
  259. command.CommandText = tablesQuery;
  260. var tables = new List<string>();
  261. using var reader = await command.ExecuteReaderAsync();
  262. while (await reader.ReadAsync())
  263. {
  264. tables.Add(reader.GetString(0));
  265. }
  266. await reader.CloseAsync();
  267. ViewBag.Tables = tables;
  268. // Check if recepieImage table exists
  269. var imageTableExists = tables.Any(t => t.ToLower().Contains("image"));
  270. ViewBag.ImageTableExists = imageTableExists;
  271. if (imageTableExists)
  272. {
  273. var imageTableName = tables.FirstOrDefault(t => t.ToLower().Contains("image"));
  274. ViewBag.ImageTableName = imageTableName;
  275. // Get table structure
  276. try
  277. {
  278. command.CommandText = $"DESCRIBE `{imageTableName}`";
  279. using var structureReader = await command.ExecuteReaderAsync();
  280. var columns = new List<string>();
  281. while (await structureReader.ReadAsync())
  282. {
  283. var field = structureReader.GetString(0);
  284. var type = structureReader.GetString(1);
  285. var isNull = structureReader.GetString(2);
  286. var key = structureReader.GetString(3);
  287. columns.Add($"{field} ({type}) {(key == "PRI" ? "PRIMARY KEY" : "")} {(isNull == "YES" ? "NULL" : "NOT NULL")}");
  288. }
  289. await structureReader.CloseAsync();
  290. ViewBag.ImageTableColumns = columns;
  291. // Get sample data count
  292. command.CommandText = $"SELECT COUNT(*) FROM `{imageTableName}`";
  293. var count = await command.ExecuteScalarAsync();
  294. ViewBag.ImageRecordCount = count;
  295. }
  296. catch (Exception ex)
  297. {
  298. ViewBag.ImageTableError = ex.Message;
  299. }
  300. }
  301. await connection.CloseAsync();
  302. return View();
  303. }
  304. catch (Exception ex)
  305. {
  306. ViewBag.Error = ex.Message;
  307. return View();
  308. }
  309. }
  310. }
  311. }