using Microsoft.AspNetCore.Mvc; using GreenHome.Application; namespace GreenHome.Api.Controllers; [ApiController] [Route("api/[controller]")] public class DevicePostsController : ControllerBase { private readonly IDevicePostService _postService; private readonly ILogger _logger; private readonly IWebHostEnvironment _environment; public DevicePostsController( IDevicePostService postService, ILogger logger, IWebHostEnvironment environment) { _postService = postService; _logger = logger; _environment = environment; } /// /// دریافت پست‌های گروه مجازی دستگاه (تایم‌لاین) /// [HttpGet] public async Task>> GetPosts( [FromQuery] int deviceId, [FromQuery] int? authorUserId, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default) { var filter = new DevicePostFilter { DeviceId = deviceId, AuthorUserId = authorUserId, Page = page, PageSize = pageSize }; var result = await _postService.GetPostsAsync(filter, cancellationToken); return Ok(result); } /// /// دریافت جزئیات یک پست /// [HttpGet("{id}")] public async Task> GetPostById(int id, CancellationToken cancellationToken) { var result = await _postService.GetPostByIdAsync(id, cancellationToken); if (result == null) { return NotFound(new { error = $"پست با شناسه {id} یافت نشد" }); } return Ok(result); } /// /// ایجاد پست جدید در گروه مجازی /// [HttpPost] public async Task> CreatePost( CreateDevicePostRequest request, CancellationToken cancellationToken) { try { var id = await _postService.CreatePostAsync(request, cancellationToken); return Ok(new { id, message = "پست با موفقیت ایجاد شد" }); } catch (UnauthorizedAccessException ex) { return Unauthorized(new { error = ex.Message }); } } /// /// ویرایش پست /// [HttpPut] public async Task UpdatePost( UpdateDevicePostRequest request, CancellationToken cancellationToken) { try { await _postService.UpdatePostAsync(request, cancellationToken); return Ok(new { message = "پست با موفقیت ویرایش شد" }); } catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); } } /// /// حذف پست /// [HttpDelete("{id}")] public async Task DeletePost(int id, CancellationToken cancellationToken) { try { await _postService.DeletePostAsync(id, cancellationToken); return Ok(new { message = "پست با موفقیت حذف شد" }); } catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); } } /// /// آپلود تصویر برای پست /// [HttpPost("{postId}/images")] [Consumes("multipart/form-data")] public async Task> UploadImage( int postId, [FromForm] IFormFile file, CancellationToken cancellationToken) { try { if (file == null || file.Length == 0) { return BadRequest(new { error = "فایل انتخاب نشده است" }); } // Validate file type var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp" }; if (!allowedTypes.Contains(file.ContentType.ToLower())) { return BadRequest(new { error = "فقط فایل‌های تصویری مجاز هستند" }); } // Validate file size (max 5MB) if (file.Length > 5 * 1024 * 1024) { return BadRequest(new { error = "حجم فایل نباید بیشتر از 5 مگابایت باشد" }); } // Create upload directory var uploadsFolder = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "posts"); Directory.CreateDirectory(uploadsFolder); // Generate unique filename var fileName = $"{Guid.NewGuid()}_{Path.GetFileName(file.FileName)}"; var filePath = Path.Combine(uploadsFolder, fileName); // Save file using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream, cancellationToken); } // Save to database var relativePath = $"/uploads/posts/{fileName}"; var imageId = await _postService.AddImageToPostAsync( postId, file.FileName, relativePath, file.ContentType, file.Length, cancellationToken); _logger.LogInformation("Image uploaded for post {PostId}: {ImageId}", postId, imageId); return Ok(new { imageId, filePath = relativePath, message = "تصویر با موفقیت آپلود شد" }); } catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error uploading image for post {PostId}", postId); return StatusCode(500, new { error = "خطا در آپلود تصویر" }); } } /// /// حذف تصویر از پست /// [HttpDelete("images/{imageId}")] public async Task DeleteImage(int imageId, CancellationToken cancellationToken) { try { await _postService.DeleteImageAsync(imageId, cancellationToken); return Ok(new { message = "تصویر با موفقیت حذف شد" }); } catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); } } /// /// بررسی دسترسی کاربر به دستگاه /// [HttpGet("access/{userId}/{deviceId}")] public async Task> CheckAccess(int userId, int deviceId, CancellationToken cancellationToken) { var hasAccess = await _postService.CanUserAccessDeviceAsync(userId, deviceId, cancellationToken); return Ok(new { hasAccess }); } /// /// ایجاد پست اسنپ‌شات وضعیت سنسورها و رله‌ها /// [HttpPost("sensor-snapshot")] public async Task> CreateSensorSnapshot( CreateSensorSnapshotRequest request, CancellationToken cancellationToken) { try { var id = await _postService.CreateSensorSnapshotAsync(request, cancellationToken); return Ok(new { id, message = "وضعیت سنسورها و رله‌ها ارسال شد" }); } catch (UnauthorizedAccessException ex) { return Unauthorized(new { error = ex.Message }); } } /// /// آپلود فایل صوتی (ویس) برای پست /// [HttpPost("{postId}/voice")] [Consumes("multipart/form-data")] public async Task UploadVoice( int postId, [FromForm] IFormFile file, [FromForm] int? durationSeconds, CancellationToken cancellationToken) { try { if (file == null || file.Length == 0) return BadRequest(new { error = "فایل انتخاب نشده است" }); var allowedTypes = new[] { "audio/webm", "audio/ogg", "audio/mp4", "audio/mpeg", "audio/wav", "audio/x-wav", "audio/amr", "audio/aac", "audio/m4a" }; if (!allowedTypes.Contains(file.ContentType.ToLower())) return BadRequest(new { error = "فرمت فایل صوتی مجاز نیست" }); if (file.Length > 10 * 1024 * 1024) return BadRequest(new { error = "حجم فایل صوتی نباید بیشتر از 10 مگابایت باشد" }); var uploadsFolder = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "voice"); Directory.CreateDirectory(uploadsFolder); var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}"; var filePath = Path.Combine(uploadsFolder, fileName); await using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream, cancellationToken); } var relativePath = $"/uploads/voice/{fileName}"; await _postService.SetPostVoiceAsync( postId, file.FileName, relativePath, file.ContentType, file.Length, durationSeconds, cancellationToken); return Ok(new { filePath = relativePath, message = "ویس با موفقیت آپلود شد" }); } catch (InvalidOperationException ex) { return NotFound(new { error = ex.Message }); } catch (Exception ex) { _logger.LogError(ex, "Error uploading voice for post {PostId}", postId); return StatusCode(500, new { error = "خطا در آپلود ویس" }); } } }