BaseApiController.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. namespace App\Controllers\Api;
  3. use CodeIgniter\RESTful\ResourceController;
  4. use CodeIgniter\HTTP\ResponseInterface;
  5. class BaseApiController extends ResourceController
  6. {
  7. protected $format = 'json';
  8. protected $db;
  9. // DB lazy loading
  10. protected function getDB()
  11. {
  12. if (!$this->db) {
  13. $this->db = \Config\Database::connect();
  14. }
  15. return $this->db;
  16. }
  17. /**
  18. * Success response
  19. */
  20. protected function respondSuccess($data = null, $message = 'Success', $code = ResponseInterface::HTTP_OK)
  21. {
  22. return $this->respond([
  23. 'success' => true,
  24. 'message' => $message,
  25. 'data' => $data
  26. ], $code);
  27. }
  28. /**
  29. * Error response
  30. */
  31. protected function respondError($message = 'Error occurred', $code = ResponseInterface::HTTP_BAD_REQUEST, $errors = null)
  32. {
  33. return $this->respond([
  34. 'success' => false,
  35. 'message' => $message,
  36. 'errors' => $errors
  37. ], $code);
  38. }
  39. /**
  40. * Get Authorization header from various sources
  41. */
  42. protected function getAuthHeader()
  43. {
  44. // Try standard Authorization header
  45. $authHeader = $this->request->getHeaderLine('Authorization');
  46. if (!empty($authHeader)) {
  47. return $authHeader;
  48. }
  49. // Try from $_SERVER (Apache mod_rewrite)
  50. if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
  51. return $_SERVER['HTTP_AUTHORIZATION'];
  52. }
  53. // Try from $_SERVER with REDIRECT_ prefix
  54. if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
  55. return $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
  56. }
  57. // Try from getallheaders() if available
  58. if (function_exists('getallheaders')) {
  59. $headers = getallheaders();
  60. if (isset($headers['Authorization'])) {
  61. return $headers['Authorization'];
  62. }
  63. if (isset($headers['authorization'])) {
  64. return $headers['authorization'];
  65. }
  66. }
  67. return null;
  68. }
  69. /**
  70. * Validate token
  71. */
  72. protected function validateToken()
  73. {
  74. $authHeader = $this->getAuthHeader();
  75. if (empty($authHeader)) {
  76. return false;
  77. }
  78. $token = str_replace('Bearer ', '', $authHeader);
  79. if (empty($token)) {
  80. return false;
  81. }
  82. // Check token in database
  83. try {
  84. $builder = $this->getDB()->table('admin_tokens');
  85. $tokenData = $builder->where('token', $token)
  86. ->get()
  87. ->getRow();
  88. if (!$tokenData) {
  89. return false;
  90. }
  91. // Check expiration (만료 체크)
  92. $expiresAt = strtotime($tokenData->expires_at);
  93. $now = time();
  94. if ($expiresAt < $now) {
  95. return false;
  96. }
  97. return $tokenData;
  98. } catch (\Exception $e) {
  99. log_message('error', 'validateToken error: ' . $e->getMessage());
  100. return false;
  101. }
  102. }
  103. /**
  104. * Require authentication
  105. */
  106. protected function requireAuth()
  107. {
  108. $authHeader = $this->getAuthHeader();
  109. // 디버깅: Authorization 헤더 확인
  110. if (empty($authHeader)) {
  111. return $this->respondError('No Authorization header', ResponseInterface::HTTP_UNAUTHORIZED);
  112. }
  113. $token = str_replace('Bearer ', '', $authHeader);
  114. if (empty($token)) {
  115. return $this->respondError('Empty token', ResponseInterface::HTTP_UNAUTHORIZED);
  116. }
  117. $tokenData = $this->validateToken();
  118. if (!$tokenData) {
  119. return $this->respondError('Token validation failed - token: ' . substr($token, 0, 20) . '...', ResponseInterface::HTTP_UNAUTHORIZED);
  120. }
  121. return $tokenData;
  122. }
  123. /**
  124. * Get pagination params
  125. */
  126. protected function getPaginationParams()
  127. {
  128. $page = $this->request->getGet('page') ?? 1;
  129. $perPage = $this->request->getGet('per_page') ?? 10;
  130. $offset = ($page - 1) * $perPage;
  131. return [
  132. 'page' => (int)$page,
  133. 'per_page' => (int)$perPage,
  134. 'offset' => (int)$offset
  135. ];
  136. }
  137. /**
  138. * Build paginated response
  139. */
  140. protected function paginatedResponse($builder, $params)
  141. {
  142. $total = $builder->countAllResults(false);
  143. $items = $builder->limit($params['per_page'], $params['offset'])->get()->getResult();
  144. return [
  145. 'items' => $items,
  146. 'total' => $total,
  147. 'page' => $params['page'],
  148. 'per_page' => $params['per_page'],
  149. 'total_pages' => ceil($total / $params['per_page'])
  150. ];
  151. }
  152. /**
  153. * Fix localhost URLs to current domain URLs
  154. */
  155. protected function fixUrl($url)
  156. {
  157. if (empty($url)) {
  158. return $url;
  159. }
  160. // 현재 요청의 도메인 감지
  161. $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
  162. $currentDomain = $protocol . ($_SERVER['HTTP_HOST'] ?? 'localhost');
  163. $currentBaseUrl = rtrim($currentDomain, '/') . '/';
  164. // localhost 또는 127.0.0.1로 시작하는 URL을 현재 도메인으로 변경
  165. $patterns = [
  166. 'http://localhost:8080/',
  167. 'http://localhost/',
  168. 'https://localhost:8080/',
  169. 'https://localhost/',
  170. 'http://127.0.0.1:8080/',
  171. 'http://127.0.0.1/',
  172. 'https://127.0.0.1:8080/',
  173. 'https://127.0.0.1/',
  174. ];
  175. foreach ($patterns as $pattern) {
  176. if (strpos($url, $pattern) === 0) {
  177. $url = str_replace($pattern, $currentBaseUrl, $url);
  178. }
  179. }
  180. // writable/uploads 경로를 uploads로 변경 (심볼릭 링크 대응)
  181. $url = str_replace('/writable/uploads/', '/uploads/', $url);
  182. $url = str_replace('writable/uploads/', 'uploads/', $url);
  183. return $url;
  184. }
  185. /**
  186. * Fix URLs in object/array
  187. */
  188. protected function fixUrls($data, $fields = ['image_url', 'file_url', 'url'])
  189. {
  190. if (is_object($data)) {
  191. foreach ($fields as $field) {
  192. if (isset($data->$field)) {
  193. $data->$field = $this->fixUrl($data->$field);
  194. }
  195. }
  196. } elseif (is_array($data)) {
  197. foreach ($data as &$item) {
  198. $item = $this->fixUrls($item, $fields);
  199. }
  200. }
  201. return $data;
  202. }
  203. }