716 lines
31 KiB
C#
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}");
|
|
}
|
|
}
|
|
}
|
|
}
|