Files
Core_Be/Controllers/CustomerController.cs
T
Dhananjaya99 bffc9b1686 first commit
2026-09-28 11:44:26 +05:30

716 lines
31 KiB
C#

using Fitness_core_BE.Helpers;
using Fitness_core_BE.Models.IOs;
using Fitness_core_BE.Services.FunctionHandler;
using Fitness_core_BE.Interfaces.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using System.Text.Json;
using static Fitness_core_BE.Helpers.JwtMiddleware;
using static Fitness_core_BE.Helpers.ApiResponseHelper;
using Fitness_core_BE.DTOs;
using Fitness_core_BE.DTOs.Mobile;
namespace Fitness_core_BE.Controllers
{
[Route("api/app")]
[ApiController]
[Produces("application/json")]
[EnableCors("AllowAll")]
public class CustomerController : ControllerBase
{
private readonly CustomerFunctionHandler _customerFuncHandler;
private readonly MealFunctionHandler _mealFuncHandler;
private readonly WorkoutFunctionHandler _workoutFuncHandler;
private readonly ChatFunctionHandler _chatFuncHandler;
private readonly TicketFuncHandler _ticketFuncHandler;
private readonly DocumentFunctionHandler _documentFuncHandler;
private readonly IDocumentService _docService;
private readonly ICustomerService _customerService;
private readonly IMealService _mealService;
private readonly IWorkoutService _workoutService;
private readonly IChatService _chatService;
private readonly IFeedService _feedService;
private readonly ITicketService _ticketService;
private readonly IAuthService _authService;
public CustomerController(
CustomerFunctionHandler customerFuncHandler,
MealFunctionHandler mealFuncHandler,
WorkoutFunctionHandler workoutFuncHandler,
ChatFunctionHandler chatFuncHandler,
TicketFuncHandler ticketFuncHandler,
DocumentFunctionHandler documentFuncHandler,
IDocumentService docService,
ICustomerService customerService,
IMealService mealService,
IWorkoutService workoutService,
IChatService chatService,
IFeedService feedService,
ITicketService ticketService,
IAuthService authService)
{
_customerFuncHandler = customerFuncHandler;
_mealFuncHandler = mealFuncHandler;
_workoutFuncHandler = workoutFuncHandler;
_chatFuncHandler = chatFuncHandler;
_ticketFuncHandler = ticketFuncHandler;
_documentFuncHandler = documentFuncHandler;
_docService = docService;
_customerService = customerService;
_mealService = mealService;
_workoutService = workoutService;
_chatService = chatService;
_feedService = feedService;
_ticketService = ticketService;
_authService = authService;
}
private ActionResult<ApiResponse<object>> FromAuth(Fitness_core_BE.DTOs.Auth.AuthResponse authResponse)
=> Response(authResponse.Data, authResponse.StatusCode, authResponse.Message);
private const string VerifyRequiredMessage =
"Please verify your email or mobile number to use this feature.";
private async Task<ActionResult<ApiResponse<object>>?> RequireVerifiedAsync()
{
if (await _customerService.IsCustomerVerified(HttpContext))
return null;
return Response<object>(null!, 403, VerifyRequiredMessage);
}
[HttpGet("status")]
[AllowAnonymous]
public IActionResult GetStatus()
{
return Ok(new { status = "Customer API is running", timestamp = System.DateTime.UtcNow });
}
// ---------- Auth / account lifecycle (REST) ----------
[AllowAnonymous]
[HttpPost("auth/otp")]
public async Task<ActionResult<ApiResponse<object>>> SendOtp([FromBody] SendOtpRequest request)
=> FromAuth(await _authService.SendOtp(new
{
identifier = request.Identifier,
numberOfDigits = request.NumberOfDigits,
newUser = request.NewUser
}, HttpContext));
[AllowAnonymous]
[HttpPost("auth/otp/verify")]
public async Task<ActionResult<ApiResponse<object>>> VerifyOtpForLogin([FromBody] VerifyOtpLoginRequest request)
=> FromAuth(await _authService.VerifyOtpForLogin(new
{
otpCode = request.OtpCode,
referenceNumber = request.ReferenceNumber
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("auth/otp/verify-alt")]
public async Task<ActionResult<ApiResponse<object>>> VerifyOtpAlt([FromBody] VerifyOtpAltRequest request)
=> FromAuth(await _authService.VerifyOTP(new
{
otpCode = request.OtpCode,
referenceNumber = request.ReferenceNumber,
identifireType = request.IdentifireType,
identifier = request.Identifier
}, HttpContext));
[AllowAnonymous]
[HttpPost("auth/refresh-token")]
public async Task<ActionResult<ApiResponse<object>>> RefreshToken([FromBody] RefreshTokenRequest request)
=> FromAuth(await _authService.RefreshToken(new { refreshToken = request.RefreshToken }, HttpContext));
[AllowAnonymous]
[HttpGet("auth/availability")]
public async Task<ActionResult<ApiResponse<object>>> IsAvailable([FromQuery] string identifier)
=> FromAuth(await _authService.IsAvailable(new { Identifier = identifier, Recovery = "Recovery" }, HttpContext));
// Anonymous so logout still clears cookies even if the access token has expired.
[AllowAnonymous]
[HttpPost("auth/logout")]
public async Task<ActionResult<ApiResponse<object>>> Logout()
=> FromAuth(await _authService.LogOut(new { }, HttpContext));
[AllowAnonymous]
[HttpPost("account/register")]
public async Task<ActionResult<ApiResponse<object>>> RegisterCustomer([FromBody] JsonElement request)
=> Response(await _customerService.CreateCustomer(request, HttpContext));
[AllowAnonymous]
[HttpPost("account/password/forgot")]
public async Task<ActionResult<ApiResponse<object>>> ForgotPassword([FromBody] ForgotPasswordRequest request)
=> FromAuth(await _authService.ForgotPassword(new
{
identifier = request.Identifier,
useResetLink = request.UseResetLink,
numberOfDigits = request.NumberOfDigits
}, HttpContext));
[AllowAnonymous]
[HttpPost("account/password/reset")]
public async Task<ActionResult<ApiResponse<object>>> ResetPassword([FromBody] ResetPasswordRequest request)
=> FromAuth(await _authService.ResetPassword(new
{
referenceNumber = request.ReferenceNumber,
newPassword = request.NewPassword,
confirmPassword = request.ConfirmPassword
}, HttpContext));
// Verify a customer's own email/mobile: validates the OTP (sent via auth/otp with
// newUser:true) and sets the Customer channel flag. Requires an authenticated customer.
[CustomMiddleware]
[TenantResolution]
[HttpPost("account/verify")]
public async Task<ActionResult<ApiResponse<object>>> VerifyChannel([FromBody] VerifyChannelRequest request)
=> FromAuth(await _authService.VerifyOTPForCus(new
{
identifireType = request.IdentifireType,
otpCode = request.OtpCode,
referenceNumber = request.ReferenceNumber,
identifier = request.Identifier
}, HttpContext));
// ---------- Customer-scoped operations ----------
[HttpPost("customer")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteCustomer([FromBody] ApiRequest request)
{
var response = await _customerFuncHandler.Execute(request, HttpContext);
return StatusCode(response.StatusCode, response);
}
[AllowAnonymous]
[HttpGet("customer/exists")]
public async Task<ActionResult<ApiResponse<object>>> CustomerExists([FromQuery] string identifier)
=> Response(await _customerService.CustomerExists(new { identifier }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/me")]
public async Task<ActionResult<ApiResponse<object>>> GetMyProfile()
=> Response(await _customerService.GetCusBasicDetails(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/me/gym")]
public async Task<ActionResult<ApiResponse<object>>> GetMyGym()
=> Response(await _customerService.GetMyGym(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpDelete("customer/me/gym")]
public async Task<ActionResult<ApiResponse<object>>> UnfollowMyGym()
=> Response(await _customerService.UnfollowMyGym(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/me/payments")]
public async Task<ActionResult<ApiResponse<object>>> MyPayment()
=> Response(await _customerService.MyPayment(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPut("customer/me")]
public async Task<ActionResult<ApiResponse<object>>> UpdateMyProfile([FromBody] UpdateCustomerProfileRequest request)
=> Response(await _customerService.UpdateCustomer(new
{
fullname = request.Fullname,
gender = request.Gender,
bod = request.Bod,
primaryPH = request.PrimaryPH,
secondaryPH = request.SecondaryPH,
email = request.Email,
NIC = request.Nic,
address = request.Address
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/me/health")]
public async Task<ActionResult<ApiResponse<object>>> GetMyHealth()
=> Response(await _customerService.GetCusHealthDetails(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPut("customer/me/health")]
public async Task<ActionResult<ApiResponse<object>>> UpdateMyHealth([FromBody] UpdateHealthRequest request)
=> Response(await _customerService.UpdateCusHealthRec(new
{
bloodGroup = request.BloodGroup,
disabilities = request.Disabilities
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/me/progress")]
public async Task<ActionResult<ApiResponse<object>>> CreateMyProgress([FromBody] CreateProgressRequest request)
=> Response(await _customerService.CreateCusProgressRec(new
{
Height = request.Height,
currentWeight = request.CurrentWeight,
targetWeight = request.TargetWeight,
goal = request.Goal,
age = request.Age,
gender = request.Gender,
activityLevel = request.ActivityLevel,
timePeriod = request.TimePeriod
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPut("customer/me/progress")]
public async Task<ActionResult<ApiResponse<object>>> UpdateMyProgress([FromBody] UpdateProgressRequest request)
=> Response(await _customerService.UpdateCusProgressRec(new
{
Height = request.Height,
Weight = request.Weight,
ProgressDate = request.ProgressDate
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/me/progress")]
public async Task<ActionResult<ApiResponse<object>>> GetMyProgress()
=> Response(await _customerService.GetCusProgressDetails(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/progress")]
public async Task<ActionResult<ApiResponse<object>>> GetCustomerProgress([FromQuery] string? gender, [FromQuery] string? age)
=> Response(await _customerService.GetCustomerProgress(new { gender, age }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/progress/weekly/{customerTargetId:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetWeeklyProgress(Guid customerTargetId)
=> Response(await _customerService.GetWeeklyProgressForCustomer(new { customerTargetId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/meal-plans")]
public async Task<ActionResult<ApiResponse<object>>> GetMyFollowedMealPlans()
=> Response(await _customerService.GetFollowedMealPlans(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/meal-plans/mine")]
public async Task<ActionResult<ApiResponse<object>>> GetMyOwnMealPlans()
=> Response(await _customerService.GetMyMealPlans(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/workout-plans")]
public async Task<ActionResult<ApiResponse<object>>> GetMyFollowedWorkoutPlans()
=> Response(await _customerService.GetFollowedWorkoutPlans(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/workout-plans/mine")]
public async Task<ActionResult<ApiResponse<object>>> GetMyOwnWorkoutPlans()
=> Response(await _customerService.GetMyWorkoutPlans(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/meal-plans/{mealPlanId:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetMyMealPlanById(Guid mealPlanId)
=> Response(await _customerService.GetCustomerMealPlanById(new { mealPlanId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/workout-plans/{workoutPlanId:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetMyWorkoutPlanById(Guid workoutPlanId)
=> Response(await _customerService.GetCustomerWorkoutPlanById(new { workoutPlanId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/meal-plans/{mealPlanId:guid}/follow")]
public async Task<ActionResult<ApiResponse<object>>> FollowMealPlan(Guid mealPlanId)
=> await RequireVerifiedAsync()
?? Response(await _customerService.FollowMealPlan(new { mealPlanId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/workout-plans/{workoutPlanId:guid}/follow")]
public async Task<ActionResult<ApiResponse<object>>> FollowWorkoutPlan(Guid workoutPlanId)
=> await RequireVerifiedAsync()
?? Response(await _customerService.FollowWorkOutPlan(new { workOutPlan = workoutPlanId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/workout-plans/{workoutPlanId:guid}/exercises/{exerciseId:guid}/complete")]
public async Task<ActionResult<ApiResponse<object>>> CompleteWorkoutExercise(Guid workoutPlanId, Guid exerciseId)
=> Response(await _customerService.SetWorkoutExerciseCompletion(workoutPlanId, exerciseId, true, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpDelete("customer/workout-plans/{workoutPlanId:guid}/exercises/{exerciseId:guid}/complete")]
public async Task<ActionResult<ApiResponse<object>>> UncompleteWorkoutExercise(Guid workoutPlanId, Guid exerciseId)
=> Response(await _customerService.SetWorkoutExerciseCompletion(workoutPlanId, exerciseId, false, HttpContext));
// ---------- Saved cards / auto-deduct consent ----------
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/subscription")]
public async Task<ActionResult<ApiResponse<object>>> GetMySubscription()
=> Response(await _customerService.GetMySubscription(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/cards")]
public async Task<ActionResult<ApiResponse<object>>> GetMyCards()
=> Response(await _customerService.GetCardDetails(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/cards")]
public async Task<ActionResult<ApiResponse<object>>> SaveCard([FromBody] SaveCardRequest request)
=> Response(await _customerService.SaveCard(new
{
cardToken = request.CardToken,
cardLast4 = request.CardLast4,
cardType = request.CardType,
nickName = request.NickName,
consent = request.Consent
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPatch("customer/subscriptions/{subscriptionId:guid}/auto-charge")]
public async Task<ActionResult<ApiResponse<object>>> SetAutoCharge(Guid subscriptionId, [FromBody] SetAutoChargeRequest request)
=> Response(await _customerService.SetAutoCharge(subscriptionId, new
{
enabled = request.Enabled,
cardId = request.CardId
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/gyms")]
public async Task<ActionResult<ApiResponse<object>>> GetGyms()
=> Response(await _customerService.GetClientGyms(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/gyms/{clientBizId:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetGymById(Guid clientBizId)
=> Response(await _customerService.GetClientDataById(new { clientBizID = clientBizId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/gyms/{clientId:guid}/plans")]
public async Task<ActionResult<ApiResponse<object>>> GetGymPlans(Guid clientId)
=> Response(await _customerService.GetGymPlans(new { clientId }, HttpContext));
// Gym-specific Terms & Conditions: view before joining, record acceptance, download PDF.
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/gyms/{clientId:guid}/terms")]
public async Task<ActionResult<ApiResponse<object>>> GetGymTerms(Guid clientId)
=> Response(await _customerService.GetGymTerms(new { clientId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("customer/gyms/{clientId:guid}/terms/accept")]
public async Task<ActionResult<ApiResponse<object>>> AcceptGymTerms(Guid clientId, [FromBody] AcceptGymTermsRequest request)
=> await RequireVerifiedAsync()
?? Response(await _customerService.AcceptGymTerms(new { clientId, version = request.Version }, HttpContext));
[AllowAnonymous]
[HttpGet("customer/gyms/{clientId:guid}/terms/pdf")]
public async Task<IActionResult> DownloadGymTermsPdf(Guid clientId)
{
var pdf = await _customerService.GetGymTermsPdf(clientId);
if (pdf == null)
return NotFound("This gym has not published any terms yet.");
return File(pdf, "application/pdf", "terms-and-conditions.pdf");
}
[CustomMiddleware]
[TenantResolution]
[HttpGet("meal-plans/templates")]
public async Task<ActionResult<ApiResponse<object>>> GetMealPlanTemplates()
=> Response(await _mealService.GetMealPlanList(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("meal-categories")]
public async Task<ActionResult<ApiResponse<object>>> GetMealCategories()
=> Response(await _mealService.GetMealCategoryList(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("meal-plans/{mealPlanId:guid}/items")]
public async Task<ActionResult<ApiResponse<object>>> GetMealPlanItems(Guid mealPlanId)
=> Response(await _mealService.GetMealItemsByMealPlanId(new { mealPlanId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("meal-plans")]
public async Task<ActionResult<ApiResponse<object>>> CreateMealPlan([FromBody] JsonElement request)
=> await RequireVerifiedAsync()
?? Response(await _mealService.CreateCustomerMealPlan(request, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("workout-plans/templates")]
public async Task<ActionResult<ApiResponse<object>>> GetWorkoutPlanTemplates()
=> Response(await _workoutService.GetWorkoutPlanList(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("workout-categories")]
public async Task<ActionResult<ApiResponse<object>>> GetWorkoutCategories()
=> Response(await _workoutService.GetWorkoutCategoryList(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("workout-plans")]
public async Task<ActionResult<ApiResponse<object>>> CreateWorkoutPlan([FromBody] JsonElement request)
=> await RequireVerifiedAsync()
?? Response(await _workoutService.CreateCustomerWorkout(request, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("chat/meal/sessions")]
public async Task<ActionResult<ApiResponse<object>>> CreateMealChatSession([FromBody] GetOrCreateChatRequest request)
=> Response(await _chatService.GetOrCreateChatAsync(new { otherUserId = request.OtherUserId }));
[CustomMiddleware]
[TenantResolution]
[HttpPost("chat/workout/sessions")]
public async Task<ActionResult<ApiResponse<object>>> CreateWorkoutChatSession([FromBody] GetOrCreateChatRequest request)
=> Response(await _chatService.GetOrCreateChatAsync(new { otherUserId = request.OtherUserId }));
[CustomMiddleware]
[TenantResolution]
[HttpPost("chat/meal/messages")]
public async Task<ActionResult<ApiResponse<object>>> SendMealChatMessage([FromBody] SendChatMessageRequest request)
=> Response(await _chatService.SendMessageAsync(new
{
chatId = request.ChatId,
senderId = request.SenderId,
message = request.Message,
type = request.Type
}));
[CustomMiddleware]
[TenantResolution]
[HttpPost("chat/workout/messages")]
public async Task<ActionResult<ApiResponse<object>>> SendWorkoutChatMessage([FromBody] SendChatMessageRequest request)
=> Response(await _chatService.SendMessageAsync(new
{
chatId = request.ChatId,
senderId = request.SenderId,
message = request.Message,
type = request.Type
}));
[HttpPost("meal")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteMeal([FromBody] ApiRequest request)
{
var response = await _mealFuncHandler.Execute(request, HttpContext);
return StatusCode(response.StatusCode, response);
}
[HttpPost("workout")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteWorkout([FromBody] ApiRequest request)
{
var response = await _workoutFuncHandler.Execute(request, HttpContext);
return StatusCode(response.StatusCode, response);
}
[HttpPost("chat")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteChat([FromBody] ApiRequest request)
{
var response = await _chatFuncHandler.Execute(request);
return StatusCode(response.StatusCode, response);
}
[HttpPost("initChat")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> InitChat([FromBody] ApiRequest request)
{
var response = await _chatFuncHandler.Execute(request);
return StatusCode(response.StatusCode, response);
}
// ---------- Feed / tickets / documents (REST) ----------
[CustomMiddleware]
[TenantResolution]
[HttpGet("feed")]
public async Task<ActionResult<ApiResponse<object>>> GetFeed([FromQuery] int skip = 0, [FromQuery] int take = 5)
=> Response(await _feedService.GetFeedForCustomer(new { skip, take }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("feed/{postId:guid}/like")]
public async Task<ActionResult<ApiResponse<object>>> LikeFeedPost(Guid postId)
=> Response(await _feedService.LikeAsync(new { postId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("feed")]
public async Task<ActionResult<ApiResponse<object>>> CreateFeedPost([FromBody] JsonElement request)
=> await RequireVerifiedAsync()
?? Response(await _customerService.CreateFeed(request, HttpContext));
// User-scoped (mirrors the client portal, which also only shows the caller's own
// tickets via GetTicketByUserId, never the unscoped GetTicketList).
[CustomMiddleware]
[TenantResolution]
[HttpGet("tickets")]
public async Task<ActionResult<ApiResponse<object>>> GetTickets()
=> Response(await _ticketService.GetTicketByUserId(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("tickets/{ticketId:guid}")]
public async Task<ActionResult<ApiResponse<object>>> GetTicketById(Guid ticketId)
=> Response(await _ticketService.GetTicketById(new { ticketId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpGet("ticket-categories")]
public async Task<ActionResult<ApiResponse<object>>> GetTicketCategories()
=> Response(await _ticketService.GetTicketCategoryList(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("tickets")]
public async Task<ActionResult<ApiResponse<object>>> CreateTicket([FromBody] CreateTicketRequest request)
=> await RequireVerifiedAsync()
?? Response(await _ticketService.CreateTicket(new
{
mainReason = request.MainReason,
ticketCategoryId = request.TicketCategoryId,
ticketStatus = request.TicketStatus
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("tickets/{ticketId:guid}/messages")]
public async Task<ActionResult<ApiResponse<object>>> SendTicketMessage(Guid ticketId, [FromBody] SendTicketMessageRequest request)
=> Response(await _ticketService.SendMessage(new { ticketId, message = request.Message }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("tickets/{ticketId:guid}/close")]
public async Task<ActionResult<ApiResponse<object>>> CloseTicket(Guid ticketId)
=> Response(await _ticketService.CloseTicket(new { ticketId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("tickets/{ticketId:guid}/feedback")]
public async Task<ActionResult<ApiResponse<object>>> SubmitTicketFeedback(Guid ticketId, [FromBody] SubmitTicketFeedbackRequest request)
=> Response(await _ticketService.SubmitTicketFeedback(new
{
ticketId,
rating = request.Rating,
feedback = request.Feedback
}, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPost("documents")]
public async Task<ActionResult<ApiResponse<object>>> UploadDocument([FromBody] JsonElement request)
=> Response(await _docService.UploadDocument(request, HttpContext));
[HttpPost("ticket")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteTicket([FromBody] ApiRequest request)
{
var response = await _ticketFuncHandler.Execute(request, HttpContext);
return StatusCode(response.StatusCode, response);
}
[HttpPost("document")]
[CustomMiddleware]
[TenantResolution]
public async Task<IActionResult> ExecuteDocument([FromBody] ApiRequest request)
{
var response = await _documentFuncHandler.Execute(request, HttpContext);
return StatusCode(response.StatusCode, response);
}
// ---------- Notification tab ----------
[CustomMiddleware]
[TenantResolution]
[HttpGet("customer/notifications")]
public async Task<ActionResult<ApiResponse<object>>> GetMyNotifications()
=> Response(await _customerService.GetMyNotifications(new { }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPatch("customer/notifications/{notificationId:guid}/read")]
public async Task<ActionResult<ApiResponse<object>>> MarkNotificationRead(Guid notificationId)
=> Response(await _customerService.MarkNotificationRead(notificationId, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPut("customer/push-token")]
public async Task<ActionResult<ApiResponse<object>>> UpdatePushToken([FromBody] UpdatePushTokenRequest request)
=> Response(await _customerService.UpdatePushToken(new { pushTokenId = request.PushTokenId }, HttpContext));
[CustomMiddleware]
[TenantResolution]
[HttpPut("customer/profile-image")]
public async Task<ActionResult<ApiResponse<object>>> UpdateProfileImage([FromBody] UpdateProfileImageRequest request)
=> Response(await _customerService.UpdateProfileImage(new { documentId = request.DocumentId }, HttpContext));
// Serves an uploaded document by id (mobile builds <base>/docService/{id} for assets).
[HttpGet("docService/{docId}")]
[AllowAnonymous]
public async Task<IActionResult> GetDocument(string docId)
{
if (!int.TryParse(docId, out int documentId))
return BadRequest("Invalid Document ID format.");
DocumentDownloadModel result = await _docService.GetFileBytesById(documentId);
if (result == null)
return NotFound("Document not found");
try
{
byte[] fileBytes = result.FileData;
string fileName = result.FileName;
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(fileName, out var contentType))
{
contentType = "application/octet-stream";
}
return File(fileBytes, contentType, fileName);
}
catch (Exception ex)
{
return BadRequest($"Error processing document: {ex.Message}");
}
}
}
}