cfmgrv4 init...1

This commit is contained in:
최준흠 2024-10-04 19:15:57 +09:00
parent 6ac8f794ce
commit 42f638e019
27 changed files with 255 additions and 288 deletions

View File

@ -139,22 +139,22 @@ define('AUTH_FIELDS', [
//메신저 관련 //메신저 관련
define("MESSENGERS", [ define("MESSENGERS", [
"skype" => [ "skype" => [
"url" => "https://join.skype.com/invite/uKUgXfZThSQC", "url" => "//join.skype.com/invite/uKUgXfZThSQC",
"icon" => '<img src="/images/common/top_skype.png" alt="스카이프">', "icon" => '<img src="/images/common/top_skype.png" alt="스카이프">',
"id" => '', "id" => '',
], ],
"discord" => [ "discord" => [
"url" => "https://discord.gg/k6nQg84N", "url" => "//discord.gg/k6nQg84N",
"icon" => '<img src="/images/common/discord.png" alt="디스코드">', "icon" => '<img src="/images/common/discord.png" alt="디스코드">',
"id" => '', "id" => '',
], ],
"telegram" => [ "telegram" => [
"url" => "https://t.me/daemonidc", "url" => "//t.me/daemonidc",
"icon" => '<img src="/images/common/telegram.png" alt="텔레그램">', "icon" => '<img src="/images/common/telegram.png" alt="텔레그램">',
"id" => '@daemonidc', "id" => '@daemonidc',
], ],
"kakaotalk" => [ "kakaotalk" => [
"url" => "https://t.me/daemonidc", "url" => "//t.me/daemonidc",
"icon" => '<img src="/images/common/kakaotalk.png" alt="카카오톡">', "icon" => '<img src="/images/common/kakaotalk.png" alt="카카오톡">',
"id" => '', "id" => '',
], ],
@ -241,21 +241,21 @@ define('LAYOUTS', [
'<meta property="og:description" content="' . KEYWORD . '">', '<meta property="og:description" content="' . KEYWORD . '">',
], ],
'stylesheets' => [ 'stylesheets' => [
'<link rel="canonical" href="https://daemonidc.com/" />', '<link rel="canonical" href="//daemonidc.com/" />',
'<link rel="icon" href="/favicon.ico">', '<link rel="icon" href="/favicon.ico">',
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">', '<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">', '<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />',
'<link rel="stylesheet" href="/css/style.css" />', '<link rel="stylesheet" href="/css/style.css" />',
], ],
'javascripts' => [ 'javascripts' => [
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>', '<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>', '<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>', '<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
'<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>', '<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
'<script src="/vendors/tinymce/tinymce/tinymce.js" referrerpolicy="origin"></script>', '<script src="/vendors/tinymce/tinymce/tinymce.js" referrerpolicy="origin"></script>',
], ],
], ],
@ -267,16 +267,17 @@ define('LAYOUTS', [
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">', '<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">', '<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />',
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />', '<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />',
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css" />',
'<link rel="stylesheet" href="/css/style.css" />', '<link rel="stylesheet" href="/css/style.css" />',
], ],
'javascripts' => [ 'javascripts' => [
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>', '<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>', '<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>', '<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
'<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>', '<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
'<script src="/vendors/tinymce/tinymce/tinymce.js" referrerpolicy="origin"></script>', '<script src="/vendors/tinymce/tinymce/tinymce.js" referrerpolicy="origin"></script>',
], ],
], ],

View File

@ -25,7 +25,6 @@ $routes->group('admin', ['namespace' => 'App\Controllers\Admin'], function ($rou
$routes->get('toggle/(:num)/(:alpha)', 'UserController::toggle/$1/$2'); $routes->get('toggle/(:num)/(:alpha)', 'UserController::toggle/$1/$2');
$routes->post('batchjob', 'UserController::batcjob'); $routes->post('batchjob', 'UserController::batcjob');
$routes->get('download/(:alpha)', 'UserController::download/$1'); $routes->get('download/(:alpha)', 'UserController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'UserController::download/$1/$2');
}); });
$routes->group('mapurl', function ($routes) { $routes->group('mapurl', function ($routes) {
$routes->get('/', 'MapurlController::index'); $routes->get('/', 'MapurlController::index');
@ -37,7 +36,6 @@ $routes->group('admin', ['namespace' => 'App\Controllers\Admin'], function ($rou
$routes->get('toggle/(:num)/(:alpha)', 'MapurlController::toggle/$1/$2'); $routes->get('toggle/(:num)/(:alpha)', 'MapurlController::toggle/$1/$2');
$routes->post('batchjob', 'MapurlController::batcjob'); $routes->post('batchjob', 'MapurlController::batcjob');
$routes->get('download/(:alpha)', 'MapurlController::download/$1'); $routes->get('download/(:alpha)', 'MapurlController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'MapurlController::download/$1/$2');
}); });
$routes->group('cloudflare', ['namespace' => 'App\Controllers\Admin\Cloudflare'], function ($routes) { $routes->group('cloudflare', ['namespace' => 'App\Controllers\Admin\Cloudflare'], function ($routes) {
$routes->group('auth', function ($routes) { $routes->group('auth', function ($routes) {
@ -46,49 +44,44 @@ $routes->group('admin', ['namespace' => 'App\Controllers\Admin'], function ($rou
$routes->post('create', 'AuthController::create'); $routes->post('create', 'AuthController::create');
$routes->get('modify/(:num)', 'AuthController::modify_form/$1'); $routes->get('modify/(:num)', 'AuthController::modify_form/$1');
$routes->get('delete/(:num)', 'AuthController::delete/$1'); $routes->get('delete/(:num)', 'AuthController::delete/$1');
$routes->post('modify/(:num)', 'AuthController::modify/$1');
$routes->get('toggle/(:num)/(:alpha)', 'AuthController::toggle/$1/$2'); $routes->get('toggle/(:num)/(:alpha)', 'AuthController::toggle/$1/$2');
$routes->post('batchjob', 'AuthController::batcjob'); $routes->post('batchjob', 'AuthController::batcjob');
$routes->get('download/(:alpha)', 'AuthController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'AuthController::download/$1/$2');
$routes->get('reload/(:num)', 'AuthController::reload/$1'); $routes->get('reload/(:num)', 'AuthController::reload/$1');
$routes->get('reload', 'AuthController::reload'); $routes->get('reload', 'AuthController::reload');
$routes->cli('reload/(:num)', 'AuthController::reload/$1'); $routes->cli('reload/(:num)', 'AuthController::reload/$1');
$routes->cli('reload', 'AuthController::reload'); $routes->cli('reload', 'AuthController::reload');
$routes->get('download/(:alpha)', 'AccountController::download/$1');
}); });
$routes->group('account', function ($routes) { $routes->group('account', function ($routes) {
$routes->get('/', 'AccountController::index'); $routes->get('/', 'AccountController::index');
$routes->post('create/(:uuid)', 'AccountController::create'); $routes->post('create/(:uuid)', 'AccountController::create');
$routes->get('download/(:alpha)', 'AccountController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'AccountController::download/$1/$2');
$routes->get('reload/(:alphanum)', 'AccountController::reload/$1'); $routes->get('reload/(:alphanum)', 'AccountController::reload/$1');
$routes->get('reload', 'AccountController::reload'); $routes->get('reload', 'AccountController::reload');
$routes->cli('reload/(:alphanum)', 'AccountController::reload/$1'); $routes->cli('reload/(:alphanum)', 'AccountController::reload/$1');
$routes->cli('reload', 'AccountController::reload'); $routes->cli('reload', 'AccountController::reload');
$routes->get('download/(:alpha)', 'AccountController::download/$1');
}); });
$routes->group('zone', function ($routes) { $routes->group('zone', function ($routes) {
$routes->get('/', 'ZoneController::index'); $routes->get('/', 'ZoneController::index');
$routes->get('create', 'ZoneController::create_form'); $routes->get('create', 'ZoneController::create_form');
$routes->post('create', 'ZoneController::create'); $routes->post('create', 'ZoneController::create');
$routes->get('delete/(:uuid)', 'ZoneController::delete/$1'); $routes->get('delete/(:alphanum)', 'ZoneController::delete/$1');
$routes->get('toggle/(:uuid)/(:alpha)', 'ZoneController::toggle/$1/$2'); $routes->get('toggle/(:alphanum)/(:alpha)', 'ZoneController::toggle/$1/$2');
$routes->post('batchjob', 'ZoneController::batcjob'); $routes->post('batchjob', 'ZoneController::batcjob');
$routes->get('download/(:alpha)', 'ZoneController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'ZoneController::download/$1/$2');
$routes->get('reload/(:alphanum)', 'ZoneController::reload/$1'); $routes->get('reload/(:alphanum)', 'ZoneController::reload/$1');
$routes->get('reload', 'ZoneController::reload'); $routes->get('reload', 'ZoneController::reload');
$routes->cli('reload/(:alphanum)', 'ZoneController::reload/$1'); $routes->cli('reload/(:alphanum)', 'ZoneController::reload/$1');
$routes->cli('reload', 'ZoneController::reload'); $routes->cli('reload', 'ZoneController::reload');
$routes->get('download/(:alpha)', 'ZoneController::download/$1');
}); });
$routes->group('record', function ($routes) { $routes->group('record', function ($routes) {
$routes->get('/', 'RecordController::index'); $routes->get('/', 'RecordController::index');
$routes->get('create', 'RecordController::create_form'); $routes->get('create', 'RecordController::create_form');
$routes->post('create)', 'RecordController::create'); $routes->post('create)', 'RecordController::create');
$routes->get('delete/(:uuid)', 'RecordController::delete/$1'); $routes->get('delete/(:alphanum)', 'RecordController::delete/$1');
$routes->get('toggle/(:uuid)/(:alpha)', 'RecordController::toggle/$1/$2'); $routes->get('toggle/(:alphanum)/(:alpha)', 'RecordController::toggle/$1/$2');
$routes->post('batchjob', 'RecordController::batcjob'); $routes->post('batchjob', 'RecordController::batcjob');
$routes->get('download/(:alpha)', 'RecordController::download/$1'); $routes->get('download/(:alpha)', 'RecordController::download/$1');
$routes->get('download/(:alpha)/(:any)', 'RecordController::download/$1/$2');
}); });
}); });
}); });

View File

@ -78,6 +78,8 @@ class RecordController extends CloudflareController
public function create_form(): RedirectResponse|string public function create_form(): RedirectResponse|string
{ {
$this->init('create'); $this->init('create');
$parent_field = $this->getModel()::PARENT;
$this->$parent_field = $this->request->getVar($parent_field) ?: DEFAULTS["EMPTY"];
return $this->create_form_procedure(); return $this->create_form_procedure();
} }
protected function create_validate(string $action, array $fields): void protected function create_validate(string $action, array $fields): void
@ -88,40 +90,44 @@ class RecordController extends CloudflareController
protected function create_process(): void protected function create_process(): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::create_process()하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::create_process()하면 않됨
//부모데이터정의
$this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]);
foreach ($this->formDatas['hosts'] as $host) {
//Cloudflare 생성
$entity = $this->getMySocket()->create($host, $this->formDatas['type'], $this->formDatas['content'], $this->formDatas['proxied']);
log_message("debug", "Record:{$entity->getTitle()} 생성 작업을 완료하였습니다.");
}
}
public function create(): RedirectResponse|string
{
$this->init(__FUNCTION__);
$this->create_validate($this->action, $this->fields); $this->create_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas(); $this->formDatas = $this->getFormDatas();
//부모데이터정의
$this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]);
//데이터 검증
$cnt = 1; $cnt = 1;
foreach ($this->formDatas['hosts'] as $host) { foreach ($this->formDatas['hosts'] as $host) {
//호스트명 형식확인 //호스트명 형식확인
if (!isHost_CommonHelper($host)) { if (!isHost_CommonHelper($host)) {
throw new \Exception("{$this->_account_entity->getTitle()}{$cnt}번째 {$host} 호스트명 형식 오류"); throw new \Exception("{$this->_zone_entity->getTitle()}{$cnt}번째 {$host} 호스트명 형식 오류");
} }
$cnt++; $cnt++;
} }
//Socket처리
foreach ($this->formDatas['hosts'] as $host) {
$entity = $this->getMySocket()->create($host, $this->formDatas['type'], $this->formDatas['content'], $this->formDatas['proxied']);
log_message("debug", "Record:{$entity->getTitle()} 생성 작업을 완료하였습니다.");
}
}
public function create(mixed $zone_uid = false): RedirectResponse|string
{
$this->init(__FUNCTION__);
return $this->create_procedure($this->action_form); return $this->create_procedure($this->action_form);
} }
//수정 (modify,toggle,batchjob사용) //수정 (modify,toggle,batchjob사용)
protected function modify_process(string $uid): void protected function modify_process(string $uid): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::modify_process($uid)하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::modify_process($uid)하면 않됨
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); $this->modify_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas();
//자신정보정의
$this->entity = $this->getModel()->getEntityByPK($uid);
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception("{$uid} 정보를 찾을수 없습니다.");
} }
//부모데이터정의 //부모데이터정의
$this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]); $this->_zone_entity = $this->getZoneModel()->getEntityByPK($this->entity->getParent());
//Cloudflare 수정 //Socket처리
$entity = $this->getMySocket()->modify($this->entity, $this->formDatas); $entity = $this->getMySocket()->modify($this->entity, $this->formDatas);
log_message("debug", "Record:{$entity->getTitle()} 수정 작업을 완료하였습니다."); log_message("debug", "Record:{$entity->getTitle()} 수정 작업을 완료하였습니다.");
} }
@ -131,14 +137,13 @@ class RecordController extends CloudflareController
$this->action = __FUNCTION__; $this->action = __FUNCTION__;
$this->fields = ['proxied']; $this->fields = ['proxied'];
$this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields);
$this->modify_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas();
return $this->batcjob_procedure(); return $this->batcjob_procedure();
} }
//삭제 //삭제
protected function delete_process(string $uid): void protected function delete_process(string $uid): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::delete_process($uid)하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::delete_process($uid)하면 않됨
//자신정보정의
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); $this->entity = $this->getModel()->getEntityByPK(intval($uid));
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception("{$uid} 정보를 찾을수 없습니다.");

View File

@ -91,6 +91,8 @@ class ZoneController extends CloudflareController
public function create_form(): RedirectResponse|string public function create_form(): RedirectResponse|string
{ {
$this->init('create'); $this->init('create');
$parent_field = $this->getModel()::PARENT;
$this->$parent_field = $this->request->getVar($parent_field) ?: DEFAULTS["EMPTY"];
return $this->create_form_procedure(); return $this->create_form_procedure();
} }
protected function create_validate(string $action, array $fields): void protected function create_validate(string $action, array $fields): void
@ -101,31 +103,11 @@ class ZoneController extends CloudflareController
protected function create_process(): void protected function create_process(): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::create_process()하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::create_process()하면 않됨
//Zone생성
$cnt = 1;
$zone_entitys = [];
foreach ($this->formDatas['domains'] as $domain) {
$entity = $this->getMySocket()->create($domain);
log_message("debug", "Zone:{$entity->getTitle()} 작업을 완료하였습니다.");
$zone_entitys[] = $entity;
$cnt++;
}
//Record생성
foreach ($zone_entitys as $zone_entity) {
$record_socket = new RecordSocket($entity);
foreach ($this->formDatas['hosts'] as $host) {
$entity = $record_socket->create($host, $this->formDatas['type'], $this->formDatas['content'], $this->formDatas['proxied']);
log_message("debug", "Record:{$entity->getTitle()} 작업을 완료하였습니다.");
}
}
}
public function create(): RedirectResponse|string
{
$this->init(__FUNCTION__);
$this->create_validate($this->action, $this->fields); $this->create_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas(); $this->formDatas = $this->getFormDatas();
//부모데이터 정의 //부모데이터 정의
$this->_account_entity = $this->getAccountModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]); $this->_account_entity = $this->getAccountModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]);
//데이터검증
//Type이 A형식일경우 IP형태인지 확인 //Type이 A형식일경우 IP형태인지 확인
if ($this->formDatas['type'] === 'A') { if ($this->formDatas['type'] === 'A') {
if (!isIPAddress_CommonHelper($this->formDatas['content'], $this->formDatas['type'])) { if (!isIPAddress_CommonHelper($this->formDatas['content'], $this->formDatas['type'])) {
@ -152,19 +134,44 @@ class ZoneController extends CloudflareController
} }
$cnt++; $cnt++;
} }
//Socket처리
//Zone생성
$cnt = 1;
$zone_entitys = [];
foreach ($this->formDatas['domains'] as $domain) {
$entity = $this->getMySocket()->create($domain);
log_message("debug", "Zone:{$entity->getTitle()} 작업을 완료하였습니다.");
$zone_entitys[] = $entity;
$cnt++;
}
//Record생성
foreach ($zone_entitys as $zone_entity) {
$record_socket = new RecordSocket($entity);
foreach ($this->formDatas['hosts'] as $host) {
$entity = $record_socket->create($host, $this->formDatas['type'], $this->formDatas['content'], $this->formDatas['proxied']);
log_message("debug", "Record:{$entity->getTitle()} 작업을 완료하였습니다.");
}
}
}
public function create(): RedirectResponse|string
{
$this->init(__FUNCTION__);
return $this->create_procedure($this->action_form); return $this->create_procedure($this->action_form);
} }
//수정 (modify,toggle,batchjob사용) //수정 (modify,toggle,batchjob사용)
protected function modify_process(string $uid): void protected function modify_process(string $uid): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::modify_process($uid)하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::modify_process($uid)하면 않됨
$this->modify_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas();
//자신정보정의
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); $this->entity = $this->getModel()->getEntityByPK(intval($uid));
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception("{$uid} 정보를 찾을수 없습니다.");
} }
//부모데이터정의 //부모데이터정의
$this->_account_entity = $this->getAccountModel()->getEntityByPK($this->formDatas[$this->getModel()::PARENT]); $this->_account_entity = $this->getAccountModel()->getEntityByPK($this->entity->getParent());
//Cloudflare 수정 //Socket처리
$entity = $this->getMySocket()->modify($this->entity, $this->formDatas); $entity = $this->getMySocket()->modify($this->entity, $this->formDatas);
log_message("debug", "Record:{$entity->getTitle()} 수정 작업을 완료하였습니다."); log_message("debug", "Record:{$entity->getTitle()} 수정 작업을 완료하였습니다.");
} }
@ -174,14 +181,13 @@ class ZoneController extends CloudflareController
$this->action = __FUNCTION__; $this->action = __FUNCTION__;
$this->fields = ['development_mode', 'ipv6', 'security_level']; $this->fields = ['development_mode', 'ipv6', 'security_level'];
$this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields); $this->field_rules = $this->getModel()->getFieldRules($this->action, $this->fields);
$this->modify_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas();
return $this->batcjob_procedure(); return $this->batcjob_procedure();
} }
//삭제 //삭제
protected function delete_process(string $uid): void protected function delete_process(string $uid): void
{ {
//DB작업도 Socket에서 다 처리하므로 parent::delete_process($uid)하면 않됨 //DB작업도 Socket에서 다 처리하므로 parent::delete_process($uid)하면 않됨
//자신정보정의
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); $this->entity = $this->getModel()->getEntityByPK(intval($uid));
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception("{$uid} 정보를 찾을수 없습니다.");

View File

@ -179,12 +179,12 @@ abstract class MVController extends CommonController
} }
protected function modify_process(string $uid): void protected function modify_process(string $uid): void
{ {
// dd($this->fields);
$this->modify_validate($this->action, $this->fields); $this->modify_validate($this->action, $this->fields);
$this->formDatas = $this->getFormDatas(); $this->formDatas = $this->getFormDatas();
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); //자신정보정의
$this->entity = $this->getModel()->getEntityByPK($uid);
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception(__FUNCTION__, " => {$uid} 정보를 찾을수 없습니다.");
} }
$this->entity = $this->getModel()->modify($this->entity, $this->formDatas); $this->entity = $this->getModel()->modify($this->entity, $this->formDatas);
} }
@ -258,6 +258,7 @@ abstract class MVController extends CommonController
//삭제 //삭제
protected function delete_process(string $uid): void protected function delete_process(string $uid): void
{ {
//자신정보정의
$this->entity = $this->getModel()->getEntityByPK(intval($uid)); $this->entity = $this->getModel()->getEntityByPK(intval($uid));
if ($this->entity === null) { if ($this->entity === null) {
throw new \Exception("{$uid} 정보를 찾을수 없습니다."); throw new \Exception("{$uid} 정보를 찾을수 없습니다.");
@ -372,7 +373,7 @@ abstract class MVController extends CommonController
//모델 처리 //모델 처리
$this->entitys = $this->list_entitys_process(); $this->entitys = $this->list_entitys_process();
//setting return_url to session flashdata //setting return_url to session flashdata
$this->session->setFlashdata(SESSION_NAMES['RETURN_URL'], current_url() . '?' . $this->request->getUri()->getQuery() ?: ""); $this->session->setFlashdata(SESSION_NAMES['RETURN_URL'], current_url() . '?' . $this->uri->getQuery() ?: "");
return view( return view(
strtolower($this->class_path) . "/index", strtolower($this->class_path) . "/index",
['viewDatas' => $this->getViewDatas()] ['viewDatas' => $this->getViewDatas()]

View File

@ -25,7 +25,7 @@ function getFieldForm_AccountHelper(string $field, mixed $value, array $viewData
$form = form_dropdown($field, [ $form = form_dropdown($field, [
"" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택', "" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택',
...$viewDatas['field_options'][$field] ...$viewDatas['field_options'][$field]
], $value, [...$extras, 'class' => "select-field"]); ], isset($viewDatas[AccountModel::PARENT]) ? $viewDatas[AccountModel::PARENT] : $value, [...$extras, 'class' => "select-field"]);
// // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]); // // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]);
// foreach ($viewDatas['field_options'][$field] as $key => $label) { // foreach ($viewDatas['field_options'][$field] as $key => $label) {
// $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label; // $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label;
@ -170,7 +170,7 @@ function getListButton_AccountHelper(string $action, array $viewDatas, array $ex
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action . '?' . $viewDatas['uri']->getQuery(['only' => AccountModel::PARENT]);
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_AccountHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_AccountHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -138,7 +138,7 @@ function getListButton_AuthHelper(string $action, array $viewDatas, array $extra
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action;
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_AuthHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_AuthHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -1,6 +1,7 @@
<?php <?php
use App\Models\Cloudflare\RecordModel; use App\Models\Cloudflare\RecordModel;
use App\Models\Cloudflare\ZoneModel;
function getFieldLabel_RecordHelper(string $field, array $viewDatas, array $extras = []): string function getFieldLabel_RecordHelper(string $field, array $viewDatas, array $extras = []): string
{ {
@ -22,10 +23,15 @@ function getFieldForm_RecordHelper(string $field, mixed $value, array $viewDatas
$value = $value ?: DEFAULTS['EMPTY']; $value = $value ?: DEFAULTS['EMPTY'];
switch ($field) { switch ($field) {
case RecordModel::PARENT: case RecordModel::PARENT:
$form = form_dropdown($field, [ $form = form_dropdown(
$field,
[
"" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택', "" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택',
...$viewDatas['field_options'][$field] ...$viewDatas['field_options'][$field]
], $value, [...$extras, 'class' => "select-field"]); ],
isset($viewDatas[RecordModel::PARENT]) ? $viewDatas[RecordModel::PARENT] : $value,
[...$extras, 'class' => "select-field"]
);
// // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]); // // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]);
// foreach ($viewDatas['field_options'][$field] as $key => $label) { // foreach ($viewDatas['field_options'][$field] as $key => $label) {
// $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label; // $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label;
@ -84,19 +90,12 @@ function getFieldView_RecordHelper(string $field, array $viewDatas, array $extra
$value = sprintf("%s%s", $viewDatas['entity']->fixed == 'on' ? ICONS['LOCK'] : "", $value); $value = sprintf("%s%s", $viewDatas['entity']->fixed == 'on' ? ICONS['LOCK'] : "", $value);
$value = anchor($url, $value, ["target" => "_self"]); $value = anchor($url, $value, ["target" => "_self"]);
break; break;
case 'category_uid': case 'content':
foreach (array_values($viewDatas['field_options'][$field]) as $category_2depths) { $value = sprintf(
foreach ($category_2depths as $key => $depth) { "<span class=\"short-text\" data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"%s\">%s</span>",
if ($key == $depth) { $value,
$value = $depth; mb_strlen($value) > 40 ? mb_substr($value, 0, 40) . '...' : $value
} );
}
}
break;
case 'cost':
case 'price':
case 'sale':
$value = number_format(!$value ? 0 : $value) . "";
break; break;
case 'updated_at': case 'updated_at':
case 'created_at': case 'created_at':
@ -197,7 +196,7 @@ function getListButton_RecordHelper(string $action, array $viewDatas, array $ext
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action . '?' . $viewDatas['uri']->getQuery(['only' => RecordModel::PARENT]);
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_RecordHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_RecordHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -25,7 +25,7 @@ function getFieldForm_ZoneHelper(string $field, mixed $value, array $viewDatas,
$form = form_dropdown($field, [ $form = form_dropdown($field, [
"" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택', "" => lang($viewDatas['class_path'] . '.label.' . $field) . ' 선택',
...$viewDatas['field_options'][$field] ...$viewDatas['field_options'][$field]
], $value, [...$extras, 'class' => "select-field"]); ], isset($viewDatas[ZoneModel::PARENT]) ? $viewDatas[ZoneModel::PARENT] : $value, [...$extras, 'class' => "select-field"]);
// // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]); // // return form_multiselect($field, $field_options[$field], is_array($value) ? [...$value] : [$value], [$extras]);
// foreach ($viewDatas['field_options'][$field] as $key => $label) { // foreach ($viewDatas['field_options'][$field] as $key => $label) {
// $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label; // $checkboxs[] = form_checkbox("{$field}[]", $key, in_array($key, explode(DEFAULTS["DELIMITER_ROLE"], $value))) . $label;
@ -223,7 +223,7 @@ function getListButton_ZoneHelper(string $action, array $viewDatas, array $extra
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action . '?' . $viewDatas['uri']->getQuery(['only' => ZoneModel::PARENT]);
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_ZoneHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_ZoneHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -132,7 +132,7 @@ function getListButton_MapurlHelper(string $action, array $viewDatas, array $ext
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action;
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_MapurlHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_MapurlHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -151,7 +151,7 @@ function getListButton_UserHelper(string $action, array $viewDatas, array $extra
{ {
switch ($action) { switch ($action) {
case 'create': case 'create':
$viewDatas['list_action_url'] = $url = current_url() . '/' . $action; $viewDatas['list_action_url'] = current_url() . '/' . $action;
$extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras]; $extras = ["class" => "btn btn-outline btn-primary btn-circle", "target" => "_self", ...$extras];
$action = getListButtonLabel_UserHelper($action, '입력', $viewDatas, $extras); $action = getListButtonLabel_UserHelper($action, '입력', $viewDatas, $extras);
break; break;

View File

@ -52,16 +52,19 @@ class RecordSocket extends CloudflareSocket
public function create(string $host, string $type, string $content, string $proxied): RecordEntity public function create(string $host, string $type, string $content, string $proxied): RecordEntity
{ {
//Socket용 //Socket용
//도메인생성을 위해 Cloudflare에 전송 //호스트생성을 위해 Cloudflare에 전송
$cf = $this->getClient()->post('zones/' . $this->_zone_entity->getPK() . '/dns_records', [ $datas = [
'name' => $host, 'name' => $host,
'type' => $type, 'type' => $type,
'content' => $content, 'content' => $content,
'proxied' => $proxied === 'on' ? true : false 'proxied' => $proxied === 'on' ? true : false
]); ];
$cf = $this->getRequest('post', "zones/{$this->_zone_entity->getPK()}/dns_records", $datas);
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Record:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Record:" . __FUNCTION__ . "에서 실패:\nrequest:" . var_export($datas, true) . "\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//DB생성 //DB생성
$formDatas = $this->getArrayByResult($cf->result); $formDatas = $this->getArrayByResult($cf->result);
@ -77,17 +80,23 @@ class RecordSocket extends CloudflareSocket
'proxied' => $entity->proxied == 'on' ? true : false, 'proxied' => $entity->proxied == 'on' ? true : false,
'ttl' => intval($entity->ttl), 'ttl' => intval($entity->ttl),
]; ];
if (isset($formDatas['proxied']) && $formDatas['proxied'] === 'on') { if (isset($formDatas['proxied'])) {
if ($formDatas['proxied'] === 'on') {
$datas['proxied'] = true; $datas['proxied'] = true;
$datas['ttl'] = 1; $datas['ttl'] = 1;
} elseif (isset($formDatas['proxied']) && $formDatas['proxied'] === 'off') { } else {
$datas['proxied'] = false; $datas['proxied'] = false;
$datas['ttl'] = 120; $datas['ttl'] = 120;
} }
$cf = $this->getClient()->put('zones/' . $this->_zone_entity->getPK() . '/dns_records', $datas); }
// 인코딩된 JSON을 확인
// throw new \Exception("Record:" . __FUNCTION__ . "\n" . json_encode($datas, JSON_PRETTY_PRINT) . "\n" . var_export($datas, true));
$cf = $this->getRequest('put', "zones/{$this->_zone_entity->getPK()}/dns_records/{$entity->getPK()}", $datas);
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Record:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Record:" . __FUNCTION__ . "에서 실패:\nrequest:" . var_export($datas, true) . "\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//DB수정 //DB수정
$formDatas = $this->getArrayByResult($cf->result); $formDatas = $this->getArrayByResult($cf->result);
@ -95,10 +104,12 @@ class RecordSocket extends CloudflareSocket
} }
public function delete(RecordEntity $entity): void public function delete(RecordEntity $entity): void
{ {
$cf = $this->getClient()->delete('zones/' . $this->_zone_entity->getPK() . '/dns_records/' . $entity->getPK()); $cf = $this->getRequest('delete', "zones/{$this->_zone_entity->getPK()}/dns_records/{$entity->getPK()}");
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Record:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Record:" . __FUNCTION__ . "에서 실패:\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//DB삭제 //DB삭제
$this->getModel()->where(RecordModel::PK, $entity->getPK()); $this->getModel()->where(RecordModel::PK, $entity->getPK());
@ -107,14 +118,14 @@ class RecordSocket extends CloudflareSocket
public function sync(RecordEntity $entity): void public function sync(RecordEntity $entity): void
{ {
//기존 Sync형태 //기존 Sync형태
// $cf = $this->getClient()->get("zones/{$this->_zone_entity->getPK()}/dns_records/" . $entity->getPK()); // $cf = $this->getRequest('get',"zones/{$this->_zone_entity->getPK()}/dns_records/{$entity->getPK()}");
// $cf = json_decode($cf->getBody()); // $cf = json_decode($cf->getBody());
// if (!$cf->success) { // if (!$cf->success) {
// throw new \Exception(__FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); // throw new \Exception(__FUNCTION__ . "에서 실패:\n" . var_export($cf, true));
// } // }
// return new RecordEntity($this->getArrayByResult($cf->result)); // return new RecordEntity($this->getArrayByResult($cf->result));
//Async형태 //Async형태
$promise = $this->getClient()->getAsync("zones/{$this->_zone_entity->getPK()}/dns_records/{$entity->getPK()}"); $promise = $this->getRequest('getAsync', "zones/{$this->_zone_entity->getPK()}/dns_records/{$entity->getPK()}");
$promise->then( $promise->then(
onFulfilled: function ($response) use ($entity): RecordEntity { onFulfilled: function ($response) use ($entity): RecordEntity {
$record = json_decode($response->getBody(), true)['result']; $record = json_decode($response->getBody(), true)['result'];

View File

@ -59,10 +59,12 @@ class ZoneSocket extends CloudflareSocket
//Cfzone에서 가져온 값을 zone에 setting //Cfzone에서 가져온 값을 zone에 setting
protected function getCFSetting(ZoneEntity $entity): ZoneEntity protected function getCFSetting(ZoneEntity $entity): ZoneEntity
{ {
$cf = $this->getClient()->patch('zones/' . $entity->getPK() . '/settings/'); $cf = $this->getRequest('patch', 'zones/' . $entity->getPK() . '/settings/');
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Zone:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Zone:" . __FUNCTION__ . "에서 실패:\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
foreach ($cf->result as $cf) { foreach ($cf->result as $cf) {
switch ($cf->id) { switch ($cf->id) {
@ -81,10 +83,13 @@ class ZoneSocket extends CloudflareSocket
} }
protected function setCFSetting(ZoneEntity $entity, string $field, string $value): ZoneEntity protected function setCFSetting(ZoneEntity $entity, string $field, string $value): ZoneEntity
{ {
$cf = $this->getClient()->patch('zones/' . $entity->getPK() . '/settings/' . $field, array('value' => $value)); $datas = ['value' => $value];
$cf = $this->getRequest('patch', 'zones/' . $entity->getPK() . '/settings/' . $field, $datas);
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success || $cf->result->id !== $field) { if (!$cf->success || $cf->result->id !== $field) {
throw new \Exception("Zone:" . __FUNCTION__ . "에서 {$field}->{$value} 변경실패:\n" . var_export($cf, true)); $message = "Zone:" . __FUNCTION__ . "에서 실패:\nrequest:" . var_export($datas, true) . "\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//최종 결과값은 body->result->id='필드명',body->result->value='on/off'이런식으로 받음 //최종 결과값은 body->result->id='필드명',body->result->value='on/off'이런식으로 받음
return $cf->result->value; return $cf->result->value;
@ -93,14 +98,17 @@ class ZoneSocket extends CloudflareSocket
{ {
//Socket용 //Socket용
//도메인생성을 위해 Cloudflare에 전송 //도메인생성을 위해 Cloudflare에 전송
$cf = $this->getClient()->post('zones/', [ $datas = [
'accountId' => $this->_account_entity->getPK(), 'accountId' => $this->_account_entity->getPK(),
'name' => $domain, 'name' => $domain,
'jump_start' => $jump_start, 'jump_start' => $jump_start,
]); ];
$cf = $this->getRequest('post', 'zones/', $datas);
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Zone:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Zone:" . __FUNCTION__ . "에서 실패:\nrequest:" . var_export($datas, true) . "\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//DB생성 //DB생성
$formDatas = $this->getArrayByResult($cf->result); $formDatas = $this->getArrayByResult($cf->result);
@ -126,10 +134,12 @@ class ZoneSocket extends CloudflareSocket
} }
public function delete(ZoneEntity $entity): void public function delete(ZoneEntity $entity): void
{ {
$cf = $this->getClient()->delete('zones/' . $entity->getPK()); $cf = $this->getRequest('delete', "zones/{$entity->getPK()}");
$cf = json_decode($cf->getBody()); $cf = json_decode($cf->getBody());
if (!$cf->success) { if (!$cf->success) {
throw new \Exception("Zone:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); $message = "Zone:" . __FUNCTION__ . "에서 실패:\nresponse:" . var_export($cf, true);
log_message("error", $message);
throw new \Exception($message);
} }
//DB삭제 //DB삭제
$this->getModel()->where(ZoneModel::PK, $entity->getPK()); $this->getModel()->where(ZoneModel::PK, $entity->getPK());
@ -138,7 +148,7 @@ class ZoneSocket extends CloudflareSocket
public function sync(ZoneEntity $entity): void public function sync(ZoneEntity $entity): void
{ {
//기존 Sync형태 //기존 Sync형태
// $cf = $this->getClient()->get('zones/' . $entity->getPK()); // $cf = $this->getRequest('get',"zones/{$entity->getPK()}");
// $cf = json_decode($cf->getBody()); // $cf = json_decode($cf->getBody());
// if (!$cf->success) { // if (!$cf->success) {
// throw new \Exception("Zone:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true)); // throw new \Exception("Zone:" . __FUNCTION__ . "에서 실패:\n" . var_export($cf, true));
@ -146,7 +156,7 @@ class ZoneSocket extends CloudflareSocket
// log_message("notice", "Zone:" . __FUNCTION__ . "=> 작업을 완료하였습니다."); // log_message("notice", "Zone:" . __FUNCTION__ . "=> 작업을 완료하였습니다.");
// return new ZoneEntity($this->getArrayByResult($cf->result)); // return new ZoneEntity($this->getArrayByResult($cf->result));
//Async형태 //Async형태
$promise = $this->getClient()->getAsync("zones/{$entity->getPK()}"); $promise = $this->getRequest('getAsync', "zones/{$entity->getPK()}");
$promise->then( $promise->then(
onFulfilled: function ($response) use ($entity): ZoneEntity { onFulfilled: function ($response) use ($entity): ZoneEntity {
$record = json_decode($response->getBody(), true)['result']; $record = json_decode($response->getBody(), true)['result'];

View File

@ -3,11 +3,13 @@
namespace App\Libraries\MySocket; namespace App\Libraries\MySocket;
use GuzzleHttp\Client;
use App\Models\Cloudflare\AuthModel;
use App\Models\Cloudflare\AccountModel;
use App\Libraries\CommonLibrary;
use App\Entities\Cloudflare\AuthEntity; use App\Entities\Cloudflare\AuthEntity;
use App\Libraries\CommonLibrary;
use App\Models\Cloudflare\AccountModel;
use App\Models\Cloudflare\AuthModel;
use Cloudflare\API\Adapter\ResponseException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
abstract class CloudflareSocket extends CommonLibrary abstract class CloudflareSocket extends CommonLibrary
{ {
@ -29,6 +31,22 @@ abstract class CloudflareSocket extends CommonLibrary
self::$_request_timewait = getenv("cfmgr.request.timewait") ?: 60; self::$_request_timewait = getenv("cfmgr.request.timewait") ?: 60;
} }
abstract protected function getArrayByResult($result, array $formDatas = []): array; abstract protected function getArrayByResult($result, array $formDatas = []): array;
private function getClient(): Client
{
if ($this->_client === null) {
// Guzzle HTTP 클라이언트를 설정하면서 Cloudflare API 토큰 사용
$this->_client = new Client([
'base_uri' => 'https://api.cloudflare.com/client/v4/',
'headers' => [
'X-Auth-Email' => $this->_auth_entity->getID(), // 인증 토큰 사용
'X-Auth-Key' => $this->_auth_entity->getAuthKey(), // 인증 토큰 사용
'Content-Type' => 'application/json',
],
'verify' => false // SSL 인증서 검증을 비활성화
]);
}
return $this->_client;
}
final protected function getAuthModel(): AuthModel final protected function getAuthModel(): AuthModel
{ {
if ($this->_authModel === null) { if ($this->_authModel === null) {
@ -43,20 +61,15 @@ abstract class CloudflareSocket extends CommonLibrary
} }
return $this->_accountModel; return $this->_accountModel;
} }
final protected function getClient(): Client final protected function getRequest(string $method, string $uri, array $datas = [])
{ {
if ($this->_client === null) { if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) {
// Guzzle HTTP 클라이언트를 설정하면서 Cloudflare API 토큰 사용 throw new \InvalidArgumentException('Request method must be get, post, put, patch, or delete');
$this->_client = new Client([
'base_uri' => 'https://api.cloudflare.com/client/v4/',
'headers' => [
'X-Auth-Email' => $this->_auth_entity->getID(), // 인증 토큰 사용
'X-Auth-Key' => $this->_auth_entity->getAuthKey(), // 인증 토큰 사용
'Content-Type' => 'application/json',
],
'verify' => false // SSL 인증서 검증을 비활성화
]);
} }
try {
$response = $this->getClient()->$method($uri, [
$method == 'get' ? 'query' : 'json' => $datas,
]);
if (self::$_request >= self::$_request_max) { if (self::$_request >= self::$_request_max) {
log_message('warning', sprintf("--Cloudflare API Call %s초 대기 시작--", self::$_request_timewait)); log_message('warning', sprintf("--Cloudflare API Call %s초 대기 시작--", self::$_request_timewait));
sleep(intval(self::$_request_timewait)); sleep(intval(self::$_request_timewait));
@ -64,7 +77,10 @@ abstract class CloudflareSocket extends CommonLibrary
log_message('warning', sprintf("--Cloudflare API Call %s초 대기 종료--", self::$_request_timewait)); log_message('warning', sprintf("--Cloudflare API Call %s초 대기 종료--", self::$_request_timewait));
} }
self::$_request++; self::$_request++;
return $this->_client; } catch (RequestException $err) {
throw ResponseException::fromRequestException($err);
}
return $response;
} }
final protected function reload_procedure($uri): array final protected function reload_procedure($uri): array

View File

@ -54,9 +54,9 @@ class AuthModel extends CommonModel
} }
return $options; return $options;
} }
public function getEntityByPK(int $uid): null|AuthEntity public function getEntityByPK(string $uid): null|AuthEntity
{ {
$this->where($this->getPKField(), $uid); $this->where($this->getPKField(), intval($uid));
return $this->getEntity(); return $this->getEntity();
} }
public function getEntityByID(string $id): null|AuthEntity public function getEntityByID(string $id): null|AuthEntity

View File

@ -45,9 +45,9 @@ class MapurlModel extends CommonModel
} }
return $rule; return $rule;
} }
public function getEntityByPK(int $uid): null|MapurlEntity public function getEntityByPK(string $uid): null|MapurlEntity
{ {
$this->where($this->getPKField(), $uid); $this->where($this->getPKField(), intval($uid));
return $this->getEntity(); return $this->getEntity();
} }
//create용 //create용

View File

@ -46,9 +46,9 @@ class SNSUserModel extends CommonModel
} }
return $rule; return $rule;
} }
public function getEntityByPK(int $uid): null|SNSUSerEntity public function getEntityByPK(string $uid): null|SNSUSerEntity
{ {
$this->where($this->getPKField(), $uid); $this->where($this->getPKField(), intval($uid));
return $this->getEntity(); return $this->getEntity();
} }
public function getEntityByID(string $id): null|SNSUSerEntity public function getEntityByID(string $id): null|SNSUSerEntity

View File

@ -54,9 +54,9 @@ class UserModel extends CommonModel
return $rule; return $rule;
} }
public function getEntityByPK(int $uid): null|UserEntity public function getEntityByPK(string $uid): null|UserEntity
{ {
$this->where($this->getPKField(), $uid); $this->where($this->getPKField(), intval($uid));
return $this->getEntity(); return $this->getEntity();
} }
public function getEntityByID(string $id): null|UserEntity public function getEntityByID(string $id): null|UserEntity

View File

@ -29,9 +29,9 @@
<div class="middle"> <div class="middle">
<div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div> <div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div>
<div class="center"> <div class="center">
<div class="header"><?= $this->include('templates/admin/header'); ?></div> <div class="header"><?= $this->include("templates/{$viewDatas['layout']}/header"); ?></div>
<div class="content"><?= $this->renderSection('content') ?></div> <div class="content"><?= $this->renderSection('content') ?></div>
<div class="footer"><?= $this->include('templates/admin/footer'); ?></div> <div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
</div> </div>
</div> </div>
<div class="bottom"> <div class="bottom">

View File

@ -29,9 +29,9 @@
<div class="middle"> <div class="middle">
<div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div> <div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div>
<div class="center"> <div class="center">
<div class="header"><?= $this->include('templates/admin/header'); ?></div> <div class="header"><?= $this->include("templates/{$viewDatas['layout']}/header"); ?></div>
<div class="content"><?= $this->renderSection('content') ?></div> <div class="content"><?= $this->renderSection('content') ?></div>
<div class="footer"><?= $this->include('templates/admin/footer'); ?></div> <div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
</div> </div>
</div> </div>
<div class="bottom"> <div class="bottom">

View File

@ -24,6 +24,7 @@
<body> <body>
<div class="content"><?= $this->renderSection('content') ?></div> <div class="content"><?= $this->renderSection('content') ?></div>
<div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
</body> </body>
</html> </html>

View File

@ -24,6 +24,7 @@
<body> <body>
<div class="content"><?= $this->renderSection('content') ?></div> <div class="content"><?= $this->renderSection('content') ?></div>
<div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
</body> </body>
</html> </html>

View File

@ -2,40 +2,41 @@
<html lang="ko"> <html lang="ko">
<head> <head>
<?php foreach ($viewDatas['layout']['metas'] as $meta) : ?><?= $meta ?><?php endforeach; ?> <meta charset="UTF-8">
<?php foreach ($viewDatas['layout']['stylesheets'] as $stylesheet) : ?><?= $stylesheet ?><?php endforeach; ?> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php foreach ($viewDatas['layout']['javascripts'] as $javascript) : ?><?= $javascript ?><?php endforeach; ?> <meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link href="/css/front.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/front.js"></script>
<title><?= $viewDatas['title'] ?></title> <title><?= $viewDatas['title'] ?></title>
<?php foreach (LAYOUTS[$viewDatas['layout']]['stylesheets'] as $stylesheet): ?>
<?= $stylesheet ?>
<?php endforeach; ?>
<?php foreach (LAYOUTS[$viewDatas['layout']]['javascripts'] as $javascript): ?>
<?= $javascript ?>
<?php endforeach; ?>
<link href="/css/<?= $viewDatas['layout'] ?>.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/<?= $viewDatas['layout'] ?>.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn' t work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head> </head>
<body> <body>
<div id="head"> <div class="top">
<?= $this->include($viewDatas['layout']['path'] . '/top_navigator'); ?> <?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/top'); ?>
<?= $this->include($viewDatas['layout']['path'] . '/top_menu'); ?>
<?= TOP_BANNER[array_key_exists('currentCategory', $viewDatas) ? $viewDatas['currentCategory']->parent : 'default'] ?>
</div> </div>
<div class="container-fluid"> <div class="middle">
<nav class="nav"></nav> <div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div>
<nav class="nav justify-content-center"> <div class="center">
<table id="layout"> <div class="header"><?= $this->include("templates/{$viewDatas['layout']}/header"); ?></div>
<tr> <div class="content"><?= $this->renderSection('content') ?></div>
<td id="left" valign="top" width="200"> <div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
<?= $this->include($viewDatas['layout']['path'] . '/left_menu'); ?> </div>
</td> </div>
<td id="body" valign="top" width="*"> <div class="bottom">
<?= $this->include('templates/front/header'); ?> <?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/bottom'); ?>
<?= $this->renderSection('content') ?>
<?= $this->include('templates/front/footer'); ?>
</td>
</tr>
</table>
<nav class="nav justify-content-end"></nav>
</div> </div>
<nav id="tail" class="navbar navbar-expand-lg" style="background-color:white">
<?= $this->include($viewDatas['layout']['path'] . '/../common/copyright'); ?>
</nav>
</body> </body>
</html> </html>

View File

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" id="viewport" content="width=1280">
<meta name="subject" content="IT Solution">
<meta name="description" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<meta name="keywords" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<meta property="og:type" content="website">
<meta property="og:title" content="IT Solution">
<meta property="og:description" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<?php foreach ($viewDatas['layout']['stylesheets'] as $stylesheet) : ?>
<?= $stylesheet ?>
<?php endforeach; ?>
<?php foreach ($viewDatas['layout']['javascripts'] as $javascript) : ?>
<?= $javascript ?>
<?php endforeach; ?>
<link href="/css/front.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/front.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<title><?= $viewDatas['title'] ?></title>
</head>
<body>
<div id="head">
<?= $this->include($viewDatas['layout']['path'] . '/top_navigator'); ?>
<?= $this->include($viewDatas['layout']['path'] . '/top_menu'); ?>
</div>
<div class="container-fluid">
<nav class="nav"></nav>
<nav class="nav justify-content-center">
<?= $this->renderSection('content') ?>
</nav>
<nav class="nav justify-content-end"></nav>
</div>
<nav id="tail" class="navbar navbar-expand-lg" style="background-color:white">
<?= $this->include($viewDatas['layout']['path'] . '/../common/copyright'); ?>
</nav>
</body>
</html>

View File

@ -2,70 +2,41 @@
<html lang="ko"> <html lang="ko">
<head> <head>
<meta charset="utf-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=Edge"> <meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" id="viewport" content="width=1280"> <title><?= $viewDatas['title'] ?></title>
<meta name="subject" content="IT Solution"> <?php foreach (LAYOUTS[$viewDatas['layout']]['stylesheets'] as $stylesheet): ?>
<meta name="description" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<meta name="keywords" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<meta property="og:type" content="website">
<meta property="og:title" content="IT Solution">
<meta property="og:description" content="일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버">
<?php foreach ($viewDatas['layout']['stylesheets'] as $stylesheet) : ?>
<?= $stylesheet ?> <?= $stylesheet ?>
<?php endforeach; ?> <?php endforeach; ?>
<?php foreach ($viewDatas['layout']['javascripts'] as $javascript) : ?> <?php foreach (LAYOUTS[$viewDatas['layout']]['javascripts'] as $javascript): ?>
<?= $javascript ?> <?= $javascript ?>
<?php endforeach; ?> <?php endforeach; ?>
<link href="/css/<?= $viewDatas['layout'] ?>.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/<?= $viewDatas['layout'] ?>.js"></script>
<link href="/css/main.css" media="screen" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/js/main.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn' t work if you view the page via file:// --> <!-- WARNING: Respond.js doesn' t work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<title><?= $viewDatas['title'] ?></title>
</head> </head>
<body> <body>
<div id="head"> <div class="top">
<?= $this->include($viewDatas['layout']['path'] . '/top_navigator'); ?> <?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/top'); ?>
<?= $this->include($viewDatas['layout']['path'] . '/top_menu'); ?>
</div> </div>
<div id="carouselExampleAutoplaying" class="carousel slide" data-bs-ride="carousel"> <div class="middle">
<div class="carousel-inner"> <div class="left"><?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/left_menu'); ?></div>
<div class="carousel-item active"> <div class="center">
<img src="/images/main/visual1.jpg" class="d-block w-100" alt="..."> <div class="header"><?= $this->include("templates/{$viewDatas['layout']}/header"); ?></div>
</div> <div class="content"><?= $this->renderSection('content') ?></div>
<div class="carousel-item"> <div class="footer"><?= $this->include("templates/{$viewDatas['layout']}/footer"); ?></div>
<img src="/images/main/visual2.jpg" class="d-block w-100" alt="...">
</div>
<div class="carousel-item">
<img src="/images/main/visual3.jpg" class="d-block w-100" alt="...">
</div> </div>
</div> </div>
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleAutoplaying" data-bs-slide="prev"> <div class="bottom">
<span class="carousel-control-prev-icon" aria-hidden="true"></span> <?= $this->include(LAYOUTS[$viewDatas['layout']]['path'] . '/bottom'); ?>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carouselExampleAutoplaying" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div> </div>
<div class="container-fluid">
<nav class="nav"></nav>
<nav class="nav justify-content-center">
<?= $this->renderSection('content') ?>
</nav>
<nav class="nav justify-content-end"></nav>
</div>
<nav id="tail" class="navbar navbar-expand-lg" style="background-color:white">
<?= $this->include($viewDatas['layout']['path'] . '/../common/copyright'); ?>
</nav>
</body> </body>
</html> </html>

View File

@ -30,7 +30,7 @@
//참고: https://phppot.com/menu/php/learn-php/ //참고: https://phppot.com/menu/php/learn-php/
// class가 editor인 textarea용 // class가 editor인 textarea용
tinymce.init({ tinymce.init({
selector: 'textarea.editor', selector: 'textarea.tinymce',
plugins: ['code', 'image', 'preview', 'table', 'emoticons', 'autoresize'], plugins: ['code', 'image', 'preview', 'table', 'emoticons', 'autoresize'],
height: 600, height: 600,
// content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }' // content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'

View File

@ -11,13 +11,13 @@
}, },
"require": { "require": {
"php": "^8.3", "php": "^8.3",
"cloudflare/sdk": "^1.3",
"codeigniter4/framework": "^4.5", "codeigniter4/framework": "^4.5",
"guzzlehttp/guzzle": "^7.9", "guzzlehttp/guzzle": "^7.9",
"phpoffice/phpspreadsheet": "^1.27",
"symfony/css-selector": "^7.1", "symfony/css-selector": "^7.1",
"symfony/dom-crawler": "^7.1", "symfony/dom-crawler": "^7.1",
"tinymce/tinymce": "^7.3", "tinymce/tinymce": "^7.3"
"phpoffice/phpspreadsheet": "^1.27",
"cloudflare/sdk": "^1.3"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.9", "fakerphp/faker": "^1.9",