first commit
This commit is contained in:
@@ -0,0 +1,715 @@
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user