213 lines
7.6 KiB
C#
213 lines
7.6 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using QRRapidoApp.Data;
|
|
using QRRapidoApp.Models;
|
|
using QRRapidoApp.Services;
|
|
using MongoDB.Driver;
|
|
using System.Security.Claims;
|
|
|
|
namespace QRRapidoApp.Controllers
|
|
{
|
|
public class AdminController : Controller
|
|
{
|
|
private readonly MongoDbContext _context;
|
|
private readonly ILogger<AdminController> _logger;
|
|
private readonly IConfiguration _config;
|
|
private readonly IUserService _userService;
|
|
|
|
public AdminController(MongoDbContext context, ILogger<AdminController> logger, IConfiguration config, IUserService userService)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
_config = config;
|
|
_userService = userService;
|
|
}
|
|
|
|
private bool IsAdmin()
|
|
{
|
|
// 1. Check if authenticated
|
|
if (User?.Identity?.IsAuthenticated != true) return false;
|
|
|
|
// 2. Get User Email
|
|
var userEmail = User.FindFirst(ClaimTypes.Email)?.Value;
|
|
if (string.IsNullOrEmpty(userEmail))
|
|
{
|
|
// Fallback: try to find user by ID to get email
|
|
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
|
if (!string.IsNullOrEmpty(userId))
|
|
{
|
|
var user = _userService.GetUserAsync(userId).Result;
|
|
userEmail = user?.Email;
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(userEmail)) return false;
|
|
|
|
// 3. Check against AllowedEmails list
|
|
var allowedEmails = _config.GetSection("Admin:AllowedEmails").Get<List<string>>() ?? new List<string>();
|
|
return allowedEmails.Contains(userEmail, StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private bool IsLocalhost()
|
|
{
|
|
var remoteIp = HttpContext.Connection.RemoteIpAddress;
|
|
return remoteIp != null &&
|
|
(remoteIp.ToString() == "127.0.0.1" ||
|
|
remoteIp.ToString() == "::1" ||
|
|
remoteIp.ToString() == "localhost");
|
|
}
|
|
|
|
// --- View Actions ---
|
|
|
|
[HttpGet("/Admin")]
|
|
public async Task<IActionResult> Index()
|
|
{
|
|
if (!IsAdmin()) return RedirectToAction("Index", "Home");
|
|
|
|
try
|
|
{
|
|
var orders = await _context.Orders
|
|
.Find(o => o.Status == "Pending")
|
|
.SortByDescending(o => o.CreatedAt)
|
|
.ToListAsync();
|
|
|
|
return View(orders);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error fetching pending orders for view");
|
|
return View(new List<Order>());
|
|
}
|
|
}
|
|
|
|
// --- API Endpoints ---
|
|
|
|
[HttpGet("api/Admin/Orders/Pending")]
|
|
public async Task<IActionResult> GetPendingOrders()
|
|
{
|
|
if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required.");
|
|
|
|
try
|
|
{
|
|
var orders = await _context.Orders
|
|
.Find(o => o.Status == "Pending")
|
|
.SortByDescending(o => o.CreatedAt)
|
|
.ToListAsync();
|
|
|
|
return Ok(orders);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error fetching pending orders");
|
|
return StatusCode(500, new { error = "Internal server error" });
|
|
}
|
|
}
|
|
|
|
[HttpPost("api/Admin/Orders/{orderId}/Approve")]
|
|
public async Task<IActionResult> ApproveOrder(string orderId)
|
|
{
|
|
if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required.");
|
|
|
|
try
|
|
{
|
|
var adminEmail = User.FindFirst(ClaimTypes.Email)?.Value ?? "unknown_admin";
|
|
|
|
// 1. Get the order
|
|
var order = await _context.Orders
|
|
.Find(o => o.Id == orderId)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (order == null) return NotFound("Order not found");
|
|
if (order.Status == "Paid") return BadRequest("Order already paid");
|
|
|
|
// 2. Update Order Status
|
|
var updateOrder = Builders<Order>.Update
|
|
.Set(o => o.Status, "Paid")
|
|
.Set(o => o.PaidAt, DateTime.UtcNow)
|
|
.Set(o => o.ApprovedBy, adminEmail);
|
|
|
|
await _context.Orders.UpdateOneAsync(o => o.Id == orderId, updateOrder);
|
|
|
|
// 3. Add Credits to User
|
|
var userUpdate = Builders<User>.Update.Inc(u => u.Credits, order.CreditsAmount);
|
|
await _context.Users.UpdateOneAsync(u => u.Id == order.UserId, userUpdate);
|
|
|
|
_logger.LogInformation($"Order {orderId} approved by {adminEmail}. {order.CreditsAmount} credits added to user {order.UserId}");
|
|
|
|
return Ok(new { success = true, message = "Order approved and credits added." });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error approving order {orderId}");
|
|
return StatusCode(500, new { error = "Internal server error" });
|
|
}
|
|
}
|
|
|
|
[HttpPost("api/Admin/Orders/{orderId}/Reject")]
|
|
public async Task<IActionResult> RejectOrder(string orderId)
|
|
{
|
|
if (!IsAdmin()) return Unauthorized("Access denied. Admin rights required.");
|
|
|
|
try
|
|
{
|
|
var adminEmail = User.FindFirst(ClaimTypes.Email)?.Value ?? "unknown_admin";
|
|
|
|
var update = Builders<Order>.Update
|
|
.Set(o => o.Status, "Rejected")
|
|
.Set(o => o.ApprovedBy, adminEmail); // Rejected by...
|
|
|
|
var result = await _context.Orders.UpdateOneAsync(o => o.Id == orderId, update);
|
|
|
|
if (result.ModifiedCount == 0) return NotFound("Order not found");
|
|
|
|
_logger.LogInformation($"Order {orderId} rejected by {adminEmail}");
|
|
|
|
return Ok(new { success = true, message = "Order rejected." });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error rejecting order {orderId}");
|
|
return StatusCode(500, new { error = "Internal server error" });
|
|
}
|
|
}
|
|
|
|
// --- Legacy Localhost-Only Endpoints ---
|
|
|
|
[HttpPost("api/Admin/SeedPlans")]
|
|
public async Task<IActionResult> SeedPlans([FromBody] List<Plan> plans)
|
|
{
|
|
if (!IsLocalhost()) return Forbid("Localhost only");
|
|
|
|
try
|
|
{
|
|
foreach (var plan in plans)
|
|
{
|
|
var filter = Builders<Plan>.Filter.Eq(p => p.Interval, plan.Interval);
|
|
var options = new ReplaceOptions { IsUpsert = true };
|
|
await _context.Plans.ReplaceOneAsync(filter, plan, options);
|
|
}
|
|
return Ok(new { success = true, message = "Plans seeded" });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return StatusCode(500, new { error = ex.Message });
|
|
}
|
|
}
|
|
|
|
[HttpGet("api/Admin/Plans")]
|
|
public async Task<IActionResult> GetPlans()
|
|
{
|
|
if (!IsLocalhost()) return Forbid("Localhost only");
|
|
var plans = await _context.Plans.Find(_ => true).ToListAsync();
|
|
return Ok(new { success = true, plans });
|
|
}
|
|
|
|
[HttpDelete("api/Admin/Plans")]
|
|
public async Task<IActionResult> DeleteAllPlans()
|
|
{
|
|
if (!IsLocalhost()) return Forbid("Localhost only");
|
|
await _context.Plans.DeleteManyAsync(_ => true);
|
|
return Ok(new { success = true });
|
|
}
|
|
}
|
|
}
|