LoginController.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using System.Diagnostics;
  2. using Microsoft.AspNetCore.Authorization;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.EntityFrameworkCore;
  5. using Microsoft.AspNetCore.Mvc.Filters;
  6. using Microsoft.AspNetCore.WebUtilities;
  7. using DeviceDetectorNET;
  8. using bitforum.Helpers;
  9. using bitforum.Models;
  10. namespace bitforum.Controllers.Member.Log
  11. {
  12. [Authorize]
  13. [Route("Member/Log")]
  14. public class LoginController : Controller
  15. {
  16. private readonly ILogger<LoginController> _logger;
  17. private readonly DefaultDbContext _db;
  18. private string? _queryString = null;
  19. public LoginController(ILogger<LoginController> logger, DefaultDbContext db)
  20. {
  21. _logger = logger;
  22. _db = db;
  23. }
  24. [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
  25. public IActionResult Error()
  26. {
  27. return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
  28. }
  29. public override void OnActionExecuting(ActionExecutingContext context)
  30. {
  31. ViewBag.QueryString = _queryString = string.Join("&", QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).Select(r => $"{r.Key}={r.Value.FirstOrDefault()}"));
  32. base.OnActionExecuting(context);
  33. }
  34. [HttpGet("Login")]
  35. public async Task<IActionResult> Index([FromQuery] int page = 1, int perPage = 10, byte? search = null, string? keyword = null, string startAt = null!, string endAt = null!)
  36. {
  37. var query = _db.LoginLog.AsQueryable(); // 기본 정렬 유지
  38. // 검색 조건 적용
  39. if (!string.IsNullOrEmpty(keyword) && search.HasValue)
  40. {
  41. if (search == 1) // ID 검색
  42. {
  43. if (int.TryParse(keyword, out int memberID))
  44. {
  45. query = query.Where(log => log.MemberID == memberID);
  46. }
  47. else
  48. {
  49. query = query.Where(log => log.MemberID == 0);
  50. }
  51. }
  52. else if (search == 2)
  53. {
  54. query = query.Where(log => _db.LoginLog.Any(c => c.Account.Contains(keyword))); // 이메일 검색
  55. }
  56. else if (search == 3)
  57. {
  58. query = query.Where(log => _db.Member.Any(m => m.Name != null && m.Name.Contains(keyword))); // 사용자명 검색
  59. }
  60. }
  61. var today = DateTime.UtcNow.Date;
  62. if (startAt != null && DateTime.TryParse(startAt, out var st))
  63. {
  64. query = query.Where(log => log.CreatedAt >= st);
  65. }
  66. if (endAt != null && DateTime.TryParse(endAt, out var et))
  67. {
  68. query = query.Where(log => log.CreatedAt <= et);
  69. }
  70. query = query.OrderByDescending(c => c.CreatedAt);
  71. // 페이징 처리
  72. var logs = await query
  73. .Skip((page - 1) * perPage)
  74. .Take(perPage)
  75. .Select(c => new
  76. {
  77. Name = _db.Member.Where(m => m.ID == c.MemberID).Select(m => m.Name).FirstOrDefault(), // 사용자명 추가
  78. c.ID,
  79. c.MemberID,
  80. c.Account,
  81. c.Success,
  82. c.Reason,
  83. c.Referer,
  84. c.Url,
  85. c.IpAddress,
  86. c.UserAgent,
  87. c.CreatedAt
  88. })
  89. .ToListAsync();
  90. var data = new List<object>();
  91. if (logs.Count > 0)
  92. {
  93. foreach (var row in logs)
  94. {
  95. var deviceDetector = new DeviceDetector(row.UserAgent);
  96. deviceDetector.Parse();
  97. var browser = deviceDetector.GetClient();
  98. var osInfo = deviceDetector.GetOs();
  99. var device = deviceDetector.GetModel();
  100. data.Add(new
  101. {
  102. row.ID,
  103. row.MemberID,
  104. row.Name,
  105. row.Account,
  106. Success = row.Success ? 'Y' : 'N',
  107. row.Reason,
  108. row.Referer,
  109. row.Url,
  110. row.IpAddress,
  111. Browser = browser,
  112. OS = osInfo,
  113. Device = device,
  114. ViewURL = $"/Member/Log/Login/{row.ID}?{_queryString}",
  115. DeleteURL = $"/Member/Log/Login/{row.ID}/Delete?{_queryString}",
  116. CreatedAt = row.CreatedAt.GetDateAt()
  117. });
  118. }
  119. }
  120. var parameter = new
  121. {
  122. Page = page,
  123. PerPage = perPage,
  124. Search = search,
  125. Keyword = keyword,
  126. StartAt = startAt,
  127. EndAt = endAt
  128. };
  129. ViewBag.Data = data;
  130. ViewBag.Total = await query.CountAsync();
  131. ViewBag.Parameter = parameter;
  132. ViewBag.Pagination = new Pagination(ViewBag.Total, page, perPage, parameter);
  133. return View("~/Views/Member/Log/Login/Index.cshtml");
  134. }
  135. [HttpGet("Login/{id}")]
  136. public async Task<IActionResult> View([FromRoute] int id)
  137. {
  138. try
  139. {
  140. if (id <= 0)
  141. {
  142. throw new Exception("유효하지 않은 접근입니다.");
  143. }
  144. var loginData = await _db.LoginLog.FindAsync(id);
  145. if (loginData == null)
  146. {
  147. throw new Exception("로그인 정보를 찾을 수 없습니다.");
  148. }
  149. var deviceDetector = new DeviceDetector(loginData.UserAgent);
  150. deviceDetector.Parse();
  151. var botInfo = deviceDetector.GetBot();
  152. var clientInfo = deviceDetector.GetClient();
  153. var osInfo = deviceDetector.GetOs();
  154. var device = deviceDetector.GetDeviceName();
  155. var brand = deviceDetector.GetBrandName();
  156. var model = deviceDetector.GetModel();
  157. ViewBag.Data = new
  158. {
  159. loginData.ID,
  160. loginData.MemberID,
  161. Name = _db.Member.Where(m => m.ID == loginData.MemberID).Select(m => m.Name).FirstOrDefault(), // 사용자명 추가
  162. Success = loginData.Success ? 'Y' : 'N',
  163. loginData.Account,
  164. loginData.Reason,
  165. loginData.Referer,
  166. loginData.Url,
  167. loginData.IpAddress,
  168. loginData.UserAgent,
  169. loginData.CreatedAt,
  170. Bot = botInfo,
  171. Browser = clientInfo,
  172. OS = osInfo,
  173. Device = device,
  174. Brand = brand,
  175. Model = model,
  176. };
  177. }
  178. catch (Exception e)
  179. {
  180. TempData["ErrorMessages"] = e.Message;
  181. _logger.LogError(e, e.Message);
  182. }
  183. return View("~/Views/Member/Log/Login/View.cshtml");
  184. }
  185. [HttpGet("Login/{id}/Delete")]
  186. public async Task<IActionResult> Delete(int id)
  187. {
  188. try
  189. {
  190. if (id <= 0)
  191. {
  192. throw new Exception("유효하지 않은 접근입니다.");
  193. }
  194. var data = await _db.LoginLog.FindAsync(id);
  195. if (data == null)
  196. {
  197. throw new Exception("로그인 정보를 찾을 수 없습니다.");
  198. }
  199. _db.LoginLog.Remove(data);
  200. int affectedRows = await _db.SaveChangesAsync();
  201. if (affectedRows <= 0)
  202. {
  203. throw new Exception("로그인 내역 삭제 중 오류가 발생했습니다.");
  204. }
  205. string message = "로그인 내역이 삭제되었습니다.";
  206. TempData["SuccessMessage"] = message;
  207. _logger.LogInformation(message);
  208. }
  209. catch (Exception e)
  210. {
  211. TempData["ErrorMessages"] = e.Message;
  212. _logger.LogError(e, e.Message);
  213. }
  214. return Redirect($"/Member/Log/Login?{_queryString}");
  215. }
  216. [HttpPost("Login/Delete")]
  217. public async Task<IActionResult> Delete([FromForm] int[] ids)
  218. {
  219. try
  220. {
  221. if (ids == null || ids.Length <= 0)
  222. {
  223. throw new Exception("유효하지 않은 접근입니다.");
  224. }
  225. foreach (var id in ids)
  226. {
  227. var data = await _db.LoginLog.FindAsync(id);
  228. if (data == null)
  229. {
  230. throw new Exception($"{id}번 로그인 정보를 찾을 수 없습니다.");
  231. }
  232. _db.LoginLog.Remove(data);
  233. int affectedRows = await _db.SaveChangesAsync();
  234. if (affectedRows <= 0)
  235. {
  236. throw new Exception($"{id}번 로그인 내역 삭제 중 오류가 발생했습니다.");
  237. }
  238. }
  239. string message = $"{ids.Length}건의 로그인 내역이 삭제되었습니다.";
  240. TempData["SuccessMessage"] = message;
  241. _logger.LogInformation(message);
  242. }
  243. catch (Exception e)
  244. {
  245. TempData["ErrorMessages"] = e.Message;
  246. _logger.LogError(e, e.Message);
  247. }
  248. return Redirect($"/Member/Log/Login?{_queryString}");
  249. }
  250. }
  251. }