From a8e2a63db0fafd28de4bdd1681fd2bdc462c8c6b Mon Sep 17 00:00:00 2001 From: "seonkyu.kim" Date: Sat, 30 Nov 2024 23:21:49 +0900 Subject: [PATCH] =?UTF-8?q?[=F0=9F=90=9B]=2013=EC=B0=A8=20=ED=91=B8?= =?UTF-8?q?=EC=8B=9C=20=ED=99=95=EC=9D=B8=20-=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20=EC=97=AC=EB=9F=AC=EA=B0=80?= =?UTF-8?q?=EC=A7=80=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC,=20?= =?UTF-8?q?=ED=91=B8=EC=8B=9C=20=EC=A0=84=EC=86=A1=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: seonkyu.kim --- Program/Common/Model/Status.cs | 22 +++++--- Program/V1/Controllers/AppController.cs | 3 +- Program/V1/Controllers/PushController.cs | 61 ++++++++++++++++++---- Program/V1/Models/APIResult.cs | 15 ++++++ Program/V1/Models/PushPayload.cs | 6 +++ Program/V1/Services/PushService.cs | 64 +++++++++++++++++++++--- 6 files changed, 146 insertions(+), 25 deletions(-) create mode 100644 Program/V1/Models/APIResult.cs diff --git a/Program/Common/Model/Status.cs b/Program/Common/Model/Status.cs index b6cc911..18752e1 100644 --- a/Program/Common/Model/Status.cs +++ b/Program/Common/Model/Status.cs @@ -1,15 +1,23 @@ +using System.Text.Json; + namespace AcaMate.Common.Models; public class APIResponseStatus { public Status status { get; set; } - public T data { get; set; } + public T? data { get; set; } + + public string JsonToString() + { + return JsonSerializer.Serialize(this); + } } public class Status { public string code { get; set; } public string message { get; set; } + } public static class DefaultResponse @@ -22,7 +30,7 @@ public static class DefaultResponse // // 외부 초기화 방지 // } - public static readonly APIResponseStatus Success = new APIResponseStatus + public static APIResponseStatus Success = new APIResponseStatus { status = new Status() { @@ -31,7 +39,7 @@ public static class DefaultResponse } }; - public static readonly APIResponseStatus InvalidInputError = new APIResponseStatus + public static APIResponseStatus InvalidInputError = new APIResponseStatus { status = new Status() { @@ -40,7 +48,7 @@ public static class DefaultResponse } }; - public static readonly APIResponseStatus NotFoundError = new APIResponseStatus + public static APIResponseStatus NotFoundError = new APIResponseStatus { status = new Status() { @@ -49,9 +57,9 @@ public static class DefaultResponse } }; - public static readonly APIResponseStatus InternalSeverError = new APIResponseStatus + public static APIResponseStatus InternalSeverError = new APIResponseStatus { - status = new Status() + status = new Status { code = "003", message = "통신에 오류가 발생하였습니다." @@ -59,7 +67,7 @@ public static class DefaultResponse }; - public static readonly APIResponseStatus UnknownError = new APIResponseStatus + public static APIResponseStatus UnknownError = new APIResponseStatus { status = new Status() { diff --git a/Program/V1/Controllers/AppController.cs b/Program/V1/Controllers/AppController.cs index 56ca1e3..ad6d8e7 100644 --- a/Program/V1/Controllers/AppController.cs +++ b/Program/V1/Controllers/AppController.cs @@ -56,7 +56,8 @@ public class AppController : ControllerBase string jsonString = JsonSerializer.Serialize(response); - return Ok(jsonString); + // return Ok(jsonString); + return Ok(response.JsonToString()); } catch (Exception ex) { diff --git a/Program/V1/Controllers/PushController.cs b/Program/V1/Controllers/PushController.cs index e6ac310..7cc0d10 100644 --- a/Program/V1/Controllers/PushController.cs +++ b/Program/V1/Controllers/PushController.cs @@ -1,3 +1,4 @@ +using AcaMate.Common.Models; using AcaMate.V1.Models; using Microsoft.AspNetCore.Mvc; @@ -12,32 +13,58 @@ namespace AcaMate.V1.Controllers; [ApiExplorerSettings(GroupName = "공통")] public class PushController : ControllerBase { - - private readonly IWebHostEnvironment _env; + private readonly IWebHostEnvironment _env; public PushController(IWebHostEnvironment env) { _env = env; } - + [HttpGet()] [CustomOperation("푸시 확인", "저장된 양식을 확인 할 수 있다..", "푸시")] public IActionResult GetPushData() { return Ok("SEND"); } - + + + /// + /// Sends a push notification to the specified device token with the provided payload. + /// + /// The device token to send the notification to. + /// The payload of the push notification. + /// An IActionResult indicating the result of the operation. + /// Push notification sent successfully. + /// Invalid input parameters. + /// Internal server error occurred. + /// Service unavailable. [HttpPost("send")] [CustomOperation("푸시전송", "저장된 양식으로, 사용자에게 푸시를 전송한다.", "푸시")] - // public async Task SendPush(string deviceToken, string title, string body, int badge) - public async Task SendPush(string deviceToken, [FromBody] Payload payload) + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(APIResponseStatus))] + public async Task SendPush(string deviceToken, [FromBody] Payload? payload) { + if (string.IsNullOrWhiteSpace(deviceToken)) + { + var inputError = DefaultResponse.InvalidInputError; + inputError.status.message = "Deviece Toekn 오류"; + return StatusCode(500,inputError); + } + + + if (payload == null) + { + var inputError = DefaultResponse.InvalidInputError; + inputError.status.message = "payload 입력 오류"; + return StatusCode(500,inputError); + } + + string uri = ""; string p12Path = ""; string p12PWPath = "/src/private/appleKeys.json"; // string p12PWPath = "private/appleKeys.json"; string apnsTopic = "me.myds.ipstein.acamate.AcaMate"; - string uri = ""; + if (_env.IsDevelopment()) { @@ -56,7 +83,23 @@ public class PushController : ControllerBase var pushService = new ApnsPushService(uri, p12Path, p12PWPath, apnsTopic); // 푸시 알림 전송 - await pushService.SendPushNotificationAsync(deviceToken, payload); - return Ok("done"); + var result = await pushService.SendPushNotificationAsync(deviceToken, payload); + if (result.Success) + { + return Ok(DefaultResponse.Success.JsonToString()); + } + else + { + + var apnsError = DefaultResponse.InternalSeverError; + apnsError.status.message = $"{result.Message}"; + return result.Code switch + { + "002" => StatusCode(002, apnsError), + "003" => StatusCode(003, apnsError), + "999" => StatusCode(999, apnsError) + }; + } + } } \ No newline at end of file diff --git a/Program/V1/Models/APIResult.cs b/Program/V1/Models/APIResult.cs new file mode 100644 index 0000000..2c00628 --- /dev/null +++ b/Program/V1/Models/APIResult.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace AcaMate.V1.Models; + +public class APIResult +{ + public bool Success { get; set; } + public string Code { get; set; } + public string Message { get; set; } + + public string JsonToString() + { + return JsonSerializer.Serialize(this); + } +} \ No newline at end of file diff --git a/Program/V1/Models/PushPayload.cs b/Program/V1/Models/PushPayload.cs index 4c5e63e..fd7eae8 100644 --- a/Program/V1/Models/PushPayload.cs +++ b/Program/V1/Models/PushPayload.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Security.Cryptography.X509Certificates; namespace AcaMate.V1.Models; @@ -20,7 +21,10 @@ public class Aps sound = "default"; content_available = 1; } + [Required(ErrorMessage = "필수 입력 누락 (alert)")] public Alert alert { get; set; } + + [Required(ErrorMessage = "필수 입력 누락 (badge")] public int badge { get; set; } // 앱 아이콘 표시 배지 숫자 설정 public string sound { get; set; } // 사운드 파일 이름 default = "default" public int content_available { get; set; } // 백그라운드 알림 활성화: 필수 (1) @@ -29,7 +33,9 @@ public class Aps public class Alert { + [Required(ErrorMessage = "필수 입력 누락 (title")] public string title { get; set; } // 제목 + [Required(ErrorMessage = "필수 입력 누락 (body)")] public string body { get; set; } // 내용 public string? subtitle { get; set; } // 부제목 (선택) } \ No newline at end of file diff --git a/Program/V1/Services/PushService.cs b/Program/V1/Services/PushService.cs index a9e6b75..b227921 100644 --- a/Program/V1/Services/PushService.cs +++ b/Program/V1/Services/PushService.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; @@ -22,20 +23,32 @@ public class ApnsPushService _apnsTopic = apnsTopic ?? throw new ArgumentNullException(nameof(apnsTopic)); } - public async Task SendPushNotificationAsync(string deviceToken, Payload payload) + public async Task SendPushNotificationAsync(string deviceToken, Payload payload) { + // 존재 안하면 예외 던져 버리는거 + if (!File.Exists(_p12PWPath)) + { + Console.WriteLine($"File not found: {_p12PWPath}"); + return new APIResult + { + Success = false, + Code = "003", + Message = "서버 오류 : p12 PW 파일 확인 필요" + }; + } + var jsonPayload = JsonSerializer.Serialize(payload); var keys = JsonSerializer.Deserialize>(File.ReadAllText(_p12PWPath)); var p12Password = keys["Password"]; - + try { var certificate = new X509Certificate2(_p12Path, p12Password); Console.WriteLine($"Certificate Subject: {certificate.Subject}"); Console.WriteLine($"Certificate Issuer: {certificate.Issuer}"); - + var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12; @@ -56,23 +69,58 @@ public class ApnsPushService request.Headers.Add("apns-topic", _apnsTopic); request.Headers.Add("apns-push-type", "alert"); - Console.WriteLine("Sending push notification..."); - Console.WriteLine($"Payload: {jsonPayload}"); + + Console.WriteLine($"Send -> Payload: {jsonPayload}"); var response = await client.SendAsync(request); + if (response.IsSuccessStatusCode) { - Console.WriteLine("Push notification sent successfully."); + return new APIResult + { + Success = true + }; } else { var errorContent = await response.Content.ReadAsStringAsync(); - Console.WriteLine($"Failed. Status Code: {response.StatusCode}, Error: {errorContent}"); + return new APIResult + { + Success = false, + Code = "003", + Message = $"APN 통신 실패 = {response.StatusCode} : {errorContent}" + }; } } + catch (HttpRequestException httpEx) + { + Console.WriteLine($"HttpRequestException: {httpEx.Message}"); + return new APIResult + { + Success = false, + Code = "003", + Message = $"통신 실패 : {httpEx.Message}" + }; + } + + catch (CryptographicException ex) + { + return new APIResult + { + Success = false, + Code = "999", + Message = $"오류 발생 : {ex.Message}" + }; + } + catch (Exception ex) { - Console.WriteLine($"Error sending push notification: {ex.Message}"); + return new APIResult + { + Success = false, + Code = "999", + Message = $"오류 발생 : {ex.Message}" + }; } } } \ No newline at end of file