commit dd1b77f1c3cac96ce744552d3cbcc70a94048023 Author: 최준흠 Date: Tue Nov 18 16:54:55 2025 +0900 dbmsv4 init...1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..289a5bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +#codeigniter4 +.env +composer.lock +vendor/ +public/vendor/ +writable/caceh/* +!writable/caceh/index.html +writable/logs/* +!writable/logs/index.html +writable/session/* +!writable/session/index.html +writable/uploads/* +!writable/uploads/index.html +writable/debugbar/* +!writable/debugbar/index.html +writable/download/* +!writable/download/index.html \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..24728f6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 British Columbia Institute of Technology +Copyright (c) 2019-present CodeIgniter Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d14b4c9 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# CodeIgniter 4 Application Starter + +## What is CodeIgniter? + +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds a composer-installable app starter. +It has been built from the +[development repository](https://github.com/codeigniter4/CodeIgniter4). + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +You can read the [user guide](https://codeigniter.com/user_guide/) +corresponding to the latest version of the framework. + +## Installation & updates + +`composer create-project codeigniter4/appstarter` then `composer update` whenever +there is a new release of the framework. + +When updating, check the release notes to see if there are any changes you might need to apply +to your `app` folder. The affected files can be copied or merged from +`vendor/codeigniter4/framework/app`. + +## Setup + +Copy `env` to `.env` and tailor for your app, specifically the baseURL +and any database settings. + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +We use GitHub issues, in our main repository, to track **BUGS** and to track approved **DEVELOPMENT** work packages. +We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +FEATURE REQUESTS. + +This repository is a "distribution" one, built by our release preparation script. +Problems with it can be raised on our forum, or as issues in the main repository. + +## Server Requirements + +PHP version 8.1 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +> [!WARNING] +> - The end of life date for PHP 7.4 was November 28, 2022. +> - The end of life date for PHP 8.0 was November 26, 2023. +> - If you are still using PHP 7.4 or 8.0, you should upgrade immediately. +> - The end of life date for PHP 8.1 will be December 31, 2025. + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/app/Config/App.php b/app/Config/App.php new file mode 100644 index 0000000..b761da7 --- /dev/null +++ b/app/Config/App.php @@ -0,0 +1,202 @@ + + */ + public array $allowedHostnames = []; + + /** + * -------------------------------------------------------------------------- + * Index File + * -------------------------------------------------------------------------- + * + * Typically, this will be your `index.php` file, unless you've renamed it to + * something else. If you have configured your web server to remove this file + * from your site URIs, set this variable to an empty string. + */ + public string $indexPage = 'index.php'; + + /** + * -------------------------------------------------------------------------- + * URI PROTOCOL + * -------------------------------------------------------------------------- + * + * This item determines which server global should be used to retrieve the + * URI string. The default setting of 'REQUEST_URI' works for most servers. + * If your links do not seem to work, try one of the other delicious flavors: + * + * 'REQUEST_URI': Uses $_SERVER['REQUEST_URI'] + * 'QUERY_STRING': Uses $_SERVER['QUERY_STRING'] + * 'PATH_INFO': Uses $_SERVER['PATH_INFO'] + * + * WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! + */ + public string $uriProtocol = 'REQUEST_URI'; + + /* + |-------------------------------------------------------------------------- + | Allowed URL Characters + |-------------------------------------------------------------------------- + | + | This lets you specify which characters are permitted within your URLs. + | When someone tries to submit a URL with disallowed characters they will + | get a warning message. + | + | As a security measure you are STRONGLY encouraged to restrict URLs to + | as few characters as possible. + | + | By default, only these are allowed: `a-z 0-9~%.:_-` + | + | Set an empty string to allow all characters -- but only if you are insane. + | + | The configured value is actually a regular expression character group + | and it will be used as: '/\A[]+\z/iu' + | + | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! + | + */ + public string $permittedURIChars = 'a-z 0-9~%.:_\-'; + + /** + * -------------------------------------------------------------------------- + * Default Locale + * -------------------------------------------------------------------------- + * + * The Locale roughly represents the language and location that your visitor + * is viewing the site from. It affects the language strings and other + * strings (like currency markers, numbers, etc), that your program + * should run under for this request. + */ + public string $defaultLocale = 'en'; + + /** + * -------------------------------------------------------------------------- + * Negotiate Locale + * -------------------------------------------------------------------------- + * + * If true, the current Request object will automatically determine the + * language to use based on the value of the Accept-Language header. + * + * If false, no automatic detection will be performed. + */ + public bool $negotiateLocale = false; + + /** + * -------------------------------------------------------------------------- + * Supported Locales + * -------------------------------------------------------------------------- + * + * If $negotiateLocale is true, this array lists the locales supported + * by the application in descending order of priority. If no match is + * found, the first locale will be used. + * + * IncomingRequest::setLocale() also uses this list. + * + * @var list + */ + public array $supportedLocales = ['en']; + + /** + * -------------------------------------------------------------------------- + * Application Timezone + * -------------------------------------------------------------------------- + * + * The default timezone that will be used in your application to display + * dates with the date helper, and can be retrieved through app_timezone() + * + * @see https://www.php.net/manual/en/timezones.php for list of timezones + * supported by PHP. + */ + public string $appTimezone = 'UTC'; + + /** + * -------------------------------------------------------------------------- + * Default Character Set + * -------------------------------------------------------------------------- + * + * This determines which character set is used by default in various methods + * that require a character set to be provided. + * + * @see http://php.net/htmlspecialchars for a list of supported charsets. + */ + public string $charset = 'UTF-8'; + + /** + * -------------------------------------------------------------------------- + * Force Global Secure Requests + * -------------------------------------------------------------------------- + * + * If true, this will force every request made to this application to be + * made via a secure connection (HTTPS). If the incoming request is not + * secure, the user will be redirected to a secure version of the page + * and the HTTP Strict Transport Security (HSTS) header will be set. + */ + public bool $forceGlobalSecureRequests = false; + + /** + * -------------------------------------------------------------------------- + * Reverse Proxy IPs + * -------------------------------------------------------------------------- + * + * If your server is behind a reverse proxy, you must whitelist the proxy + * IP addresses from which CodeIgniter should trust headers such as + * X-Forwarded-For or Client-IP in order to properly identify + * the visitor's IP address. + * + * You need to set a proxy IP address or IP address with subnets and + * the HTTP header for the client IP address. + * + * Here are some examples: + * [ + * '10.0.1.200' => 'X-Forwarded-For', + * '192.168.5.0/24' => 'X-Real-IP', + * ] + * + * @var array + */ + public array $proxyIPs = []; + + /** + * -------------------------------------------------------------------------- + * Content Security Policy + * -------------------------------------------------------------------------- + * + * Enables the Response's Content Secure Policy to restrict the sources that + * can be used for images, scripts, CSS files, audio, video, etc. If enabled, + * the Response object will populate default values for the policy from the + * `ContentSecurityPolicy.php` file. Controllers can always add to those + * restrictions at run time. + * + * For a better understanding of CSP, see these documents: + * + * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + * @see http://www.w3.org/TR/CSP/ + */ + public bool $CSPEnabled = false; +} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php new file mode 100644 index 0000000..9a92824 --- /dev/null +++ b/app/Config/Autoload.php @@ -0,0 +1,92 @@ +|string> + */ + public $psr4 = [ + APP_NAMESPACE => APPPATH, + ]; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * $classmap = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + * + * @var array + */ + public $classmap = []; + + /** + * ------------------------------------------------------------------- + * Files + * ------------------------------------------------------------------- + * The files array provides a list of paths to __non-class__ files + * that will be autoloaded. This can be useful for bootstrap operations + * or for loading functions. + * + * Prototype: + * $files = [ + * '/path/to/my/file.php', + * ]; + * + * @var list + */ + public $files = []; + + /** + * ------------------------------------------------------------------- + * Helpers + * ------------------------------------------------------------------- + * Prototype: + * $helpers = [ + * 'form', + * ]; + * + * @var list + */ + public $helpers = []; +} diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php new file mode 100644 index 0000000..a868447 --- /dev/null +++ b/app/Config/Boot/development.php @@ -0,0 +1,34 @@ + WRITEPATH . 'cache/', + 'mode' => 0640, + ]; + + /** + * ------------------------------------------------------------------------- + * Memcached settings + * ------------------------------------------------------------------------- + * + * Your Memcached servers can be specified below, if you are using + * the Memcached drivers. + * + * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached + * + * @var array{host?: string, port?: int, weight?: int, raw?: bool} + */ + public array $memcached = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + 'raw' => false, + ]; + + /** + * ------------------------------------------------------------------------- + * Redis settings + * ------------------------------------------------------------------------- + * + * Your Redis server can be specified below, if you are using + * the Redis or Predis drivers. + * + * @var array{host?: string, password?: string|null, port?: int, timeout?: int, database?: int} + */ + public array $redis = [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + 'database' => 0, + ]; + + /** + * -------------------------------------------------------------------------- + * Available Cache Handlers + * -------------------------------------------------------------------------- + * + * This is an array of cache engine alias' and class names. Only engines + * that are listed here are allowed to be used. + * + * @var array> + */ + public array $validHandlers = [ + 'dummy' => DummyHandler::class, + 'file' => FileHandler::class, + 'memcached' => MemcachedHandler::class, + 'predis' => PredisHandler::class, + 'redis' => RedisHandler::class, + 'wincache' => WincacheHandler::class, + ]; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Include Query String + * -------------------------------------------------------------------------- + * + * Whether to take the URL query string into consideration when generating + * output cache files. Valid options are: + * + * false = Disabled + * true = Enabled, take all query parameters into account. + * Please be aware that this may result in numerous cache + * files generated for the same page over and over again. + * ['q'] = Enabled, but only take into account the specified list + * of query parameters. + * + * @var bool|list + */ + public $cacheQueryString = false; +} diff --git a/app/Config/Constants.php b/app/Config/Constants.php new file mode 100644 index 0000000..728cc18 --- /dev/null +++ b/app/Config/Constants.php @@ -0,0 +1,535 @@ + '생성되었습니다.', + 'UPDATED' => '수정되였습니다.', + 'DELETED' => '삭제되였습니다.', + 'SUCCESS' => '작업이 성공적으로 완료되었습니다.', + 'FAILED' => '작업이 실패하였습니다.', + 'NOT_FOUND' => '데이터가 존재하지 않습니다.', + 'NOT_AUTH' => '권한이 없습니다.', + 'NOT_LOGIN' => '로그인이 필요합니다.', + 'NOT_MATCH' => '데이터가 일치하지 않습니다.', + 'NOT_EMPTY' => '데이터가 비어있습니다.', + 'NOT_UNIQUE' => '중복된 데이터가 존재합니다.', + 'NOT_DELETE' => '삭제할 수 없는 데이터가 존재합니다.', + 'NOT_UPDATE' => '수정할 수 없는 데이터가 존재합니다.', + 'NOT_CREATE' => '생성할 수 없는 데이터가 존재합니다.', + 'NOT_SYNC' => '동기화할 수 없는 데이터가 존재합니다.', + 'NOT_SYNC_RESULT' => '동기화 결과가 실패하였습니다.', + 'NOT_SYNC_SUCCESS' => '동기화 결과가 성공하였습니다.', + 'NOT_SYNC_ERROR' => '동기화 결과가 실패하였습니다.', + 'NOT_SYNC_NOTHING' => '동기화할 데이터가 없습니다.', + 'NOT_SYNC_NOTHING_RESULT' => '동기화 결과가 없습니다.', + 'NOT_SYNC_NOTHING_ERROR' => '동기화 결과가 없습니다.', + 'LOGIN' => '로그인 하셨습니다.', + 'LOGOUT' => '로그아웃 하셨습니다.' +]); +//URL +define('URLS', [ + 'LOGIN' => '/auth/login', + 'GOOGLE_LOGIN' => '/auth/google_login', + 'SIGNUP' => '/auth/signup', + 'LOGOUT' => '/auth/logout', +]); +//SESSION 관련 +define('SESSION_NAMES', [ + 'RETURN_URL' => "return_url", + 'RETURN_MSG' => "return_message", + 'ISLOGIN' => "islogined", + 'AUTH' => 'auth', +]); +//메신저 관련 +define("MESSENGERS", [ + "skype" => [ + "url" => "//join.skype.com/invite/uKUgXfZThSQC", + "icon" => '스카이프', + "id" => '', + ], + "discord" => [ + "url" => "//discord.gg/k6nQg84N", + "icon" => '디스코드', + "id" => '', + ], + "telegram" => [ + "url" => "//t.me/daemonidc", + "icon" => '텔레그램', + "id" => '@daemonidc', + ], + "kakaotalk" => [ + "url" => "//t.me/daemonidc", + "icon" => '카카오톡', + "id" => '', + ], +]); +//아이콘 및 Sound관련 +define('ICONS', [ + 'ADD' => '', + 'LOGO' => '', + 'EXCEL' => '', + 'PDF' => '', + 'GOOGLE' => 'Google', + 'MEMBER' => '', + 'LOGIN' => '', + 'LOGOUT' => '', + 'HOME' => '', + 'MENU' => '', + 'NEW' => '', + 'REPLY' => '', + 'DATABASE' => '', + 'DISLIKE' => '', + 'LIKE' => '', + 'DOWNLOAD' => '', + 'UPLOAD' => '', + 'COPY' => '', + 'PASTE' => '', + 'EDIT' => '', + 'VIEW' => '', + 'VIEW_OFF' => '', + 'PRINT' => '', + 'SAVE' => '', + 'CANCEL' => '', + 'CLOSE' => '', + 'CLIENT' => '', + 'CHART' => '', + 'CHECK' => '', + 'CHECK_OFF' => '', + 'CHECK_ON' => '', + 'CHECK_ALL' => '', + 'CHECK_NONE' => '', + 'CHECK_SOME' => '', + 'COUPON' => '', + 'HISTORY' => '', + 'MODIFY' => '', + 'MODIFY_ALL' => '', + 'BATCHJOB' => '', + 'DELETE' => '', + 'REBOOT' => '🔄', + 'RELOAD' => '', + 'SETUP' => '', + 'FLAG' => '', + 'SEARCH' => '', + 'PLAY' => '', + 'CART' => '', + 'CARD' => '', + 'DEPOSIT' => '', + 'DESKTOP' => '', + 'DEVICE' => '', + 'UP' => '', + 'DOWN' => '', + 'LEFT' => '', + 'RIGHT' => '', + 'IMAGE_FILE' => '', + 'CLOUD' => '', + 'SIGNPOST' => '', + 'LOCK' => '', + 'UNLOCK' => '', + 'BOX' => '', + 'BOXS' => '', + 'ONETIME' => '', + 'MONTH' => '', + 'EMAIL' => '', + 'MAIL' => '', + 'PHONE' => '', + 'POINT' => '', + 'ALRAM' => '', + 'PAYMENT' => '', + 'LINK' => '', + 'SALE_UP' => '', + 'SALE_DOWN' => '', + 'SERVICE' => '', + 'SERVICE_ITEM' => '', + 'SERVICE_ITEM_LINE' => '', + 'SERVICE_ITEM_IP' => '', + 'SERVICE_ITEM_SERVER' => '', + 'SERVICE_ITEM_CPU' => '', + 'SERVICE_ITEM_RAM' => '', + 'SERVICE_ITEM_STORAGE' => '', + 'SERVICE_ITEM_SOFTWARE' => '', + 'SERVICE_ITEM_DEFENCE' => '', + 'SERVICE_ITEM_DOMAIN' => '', + 'SERVICE_ITEM_OTHER' => '', + 'SERVER_ITEM_CPU' => '', + 'SERVER_ITEM_RAM' => '', + 'SERVER_ITEM_DISK' => '', + 'SERVER_ITEM_SWITCH' => '', + 'SERVER_ITEM_OS' => '', + 'SERVER_ITEM_DB' => '', + 'SERVER_ITEM_SOFTWARE' => '', + 'SERVER_ITEM_IP' => '', + 'SERVER_ITEM_CS' => '', + 'SERVER_ITEM_ETC' => '', +]); +//메신저 아이콘 +define('MESSENGER_ICONS', [ + 'WHATSAPP' => '', + 'VIBER' => '', + 'LINE' => '', + 'KAKAO' => '', + 'DISCORD' => '', + 'TELEGRAM' => '', + 'SKYPE' => '', + 'YOUTUBE' => '', + 'FACEBOOK' => '', + 'TWITTER' => '', + 'INSTAGRAM' => '', + 'LINKEDIN' => '', + 'GITHUB' => '', + 'GITLAB' => '', + 'BITBUCKET' => '', + 'REDDIT' => '', + 'TIKTOK' => '', + 'PINTEREST' => '', + 'TUMBLR' => '', + 'SNAPCHAT' => '', +]); +//배너관련 +define('TOP_BANNER', [ + 'default' => '', + 'aboutus' => '', + 'member' => '', + 'hosting' => '', + 'serverdevice' => '', + 'service' => '', + 'support' => '', +]); +//소리관련 +define('AUDIOS', [ + 'Alram_GetEmail' => '', +]); +//Layout관련 +define('KEYWORD', '일본IDC 일본서버 일본 서버 일본호스팅 서버호스팅 디도스 공격 해외 호스팅 DDOS 방어 ddos 의뢰 디도스 보안 일본 단독서버 가상서버'); +define('LAYOUTS', [ + 'empty' => [ + 'title' => KEYWORD, + 'path' => 'layouts' . DIRECTORY_SEPARATOR . 'empty', + 'metas' => [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + ], + 'stylesheets' => [ + '', + '', + '', + ], + 'javascripts' => [ + '', + ], + ], + 'front' => [ + 'title' => KEYWORD, + 'path' => 'layouts' . DIRECTORY_SEPARATOR . 'front', + 'topmenus' => ['aboutus', 'hosting', 'service', 'support'], + 'metas' => [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + ], + 'stylesheets' => [ + '', + '', + '', + '', + '', + ], + 'javascripts' => [ + '', + '', + '', + '', + ], + ], + 'admin' => [ + 'title' => '관리자화면', + 'path' => 'layouts' . DIRECTORY_SEPARATOR . 'admin', + 'metas' => [ + '', + '', + '', + '', + '', + '', + '', + '', + '', + ], + 'stylesheets' => [ + '', + '', + '', + '', + '', + '', + '', + '', + ], + 'javascripts' => [ + '', + '', + '', + '', + '', + '' + ], + ], +]); + +//STATUS +define("STATUS", [ + 'AVAILABLE' => "available", + 'FORBIDDEN' => "forbidden", + 'OCCUPIED' => "occupied", + 'SUCCESS' => "success", + 'FAILED' => "fail", + 'PAUSE' => "pause", + 'TERMINATED' => "terminated", + 'WITHDRAWAL' => "withdrawal", + 'DEPOSIT' => "deposit", + 'PAID' => 'paid', + 'UNPAID' => 'unpaid', +]); + +//ROLE +define("ROLE", [ + 'USER' => [ + 'MANAGER' => "manager", + 'CLOUDFLARE' => "cloudflare", + 'FIREWALL' => "firewall", + 'SECURITY' => "security", + 'DIRECTOR' => "director", + 'MASTER' => "master", + ], + 'CLIENT' => [], +]); + +//Default값 정의 +define('DEFAULTS', [ + 'DELIMITER_FILE' => "||", + 'DELIMITER_ROLE' => ",", + 'INDEX_PERPAGE' => 20, + 'STATUS' => STATUS['AVAILABLE'] +]); +######################### +//서비스 관련 +define("SERVICE", [ + "NEW_INTERVAL" => $_ENV['SERVICE_NEW_INTERVAL'] ?? $_SERVER['SERVICE_NEW_INTERVAL'] ?? 7, +]); +//서버 관련 +define("SERVER", [ + "TYPES" => [ + "NORMAL" => 'normal', + "DEFENCE" => 'defence', + "DEDICATED" => 'dedicated', + "VPN" => 'vpn', + "EVENT" => 'event', + "TEST" => 'test', + "ALTERNATIVE" => 'alternative', + "OURS" => 'ours', + "COLOCATION" => 'colocation' + ], + "CHASSISES" => [ + "HP DL360 GEN6B" => "HP DL360 Gen6 B", + "HP DL360 GEN7C" => "HP DL360 Gen7 C", + "HP DL360 GEN7D" => "HP DL360 Gen7 D", + "HP DL360 GEN8D" => "HP DL360 Gen8 D", + "HP DL360 GEN8E" => "HP DL360 Gen8 E", + "HP DL360 GEN9E" => "HP DL360 Gen9 E", + "HP DL360 GEN10" => "HP DL360 Gen10", + "Hitach HA3000" => "Hitach HA3000", + "DESKTOP I5-9" => "데탑 I5 9세대", + "DESKTOP I5-10" => "데탑 I5 10세대", + "DESKTOP I5-12" => "데탑 I5 12세대", + "DESKTOP I7-9" => "데탑 I5 9세대", + "DESKTOP I7-10" => "데탑 I5 10세대", + "DESKTOP I7-12" => "데탑 I5 12세대", + "MINI I5-12" => "Mini I5 12세대", + "VPC" => "아마존-VPN", + "KCS" => "KT-VPN", + ], + "STOCKS" => [ + "HP DL360 GEN6B" => 100, + "HP DL360 GEN7C" => 100, + "HP DL360 GEN7D" => 100, + "HP DL360 GEN8D" => 100, + "HP DL360 GEN8E" => 100, + "HP DL360 GEN9E" => 100, + "HP DL360 GEN10" => 100, + "Hitach HA3000" => 100, + "DESKTOP I5-9" => 100, + "DESKTOP I5-10" => 100, + "DESKTOP I5-12" => 100, + "DESKTOP I7-9" => 100, + "DESKTOP I7-10" => 100, + "DESKTOP I7-12" => 100, + "MINI I5-12" => 100, + "VPC" => 100, + "KCS" => 100, + ] +]); +//서버파트 관련 +define("SERVERPART", [ + "CNT_RANGE" => array_combine(range(1, 10), range(1, 10)), + "SERVER_PARTTYPES" => ['CPU', 'RAM', 'DISK'], + "SERVICE_PARTTYPES" => ['SOFTWARE', 'CS', 'IP'], + "ALL_PARTTYPES" => ['CPU', 'RAM', 'DISK', 'SOFTWARE', 'IP', 'CS'], + "CPU" => [ + "HP DL360 GEN6B" => [["UID" => 1, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN7C" => [["UID" => 2, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN7D" => [["UID" => 3, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN8D" => [["UID" => 3, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN8E" => [["UID" => 4, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN9E" => [["UID" => 4, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN10" => [["UID" => 5, "CNT" => 2, "EXTRA" => ""]], + "Hitach HA3000" => [["UID" => 5, "CNT" => 2, "EXTRA" => ""]], + "DESKTOP I5-9" => [["UID" => 7, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-10" => [["UID" => 8, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-12" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-9" => [["UID" => 10, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-10" => [["UID" => 11, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-12" => [["UID" => 12, "CNT" => 1, "EXTRA" => ""]], + ], + "RAM" => [ + "HP DL360 GEN6B" => [["UID" => 2, "CNT" => 1, "EXTRA" => ""]], + "HP DL360 GEN7C" => [["UID" => 2, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN7D" => [["UID" => 2, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN8D" => [["UID" => 3, "CNT" => 2, "EXTRA" => ""]], + "HP DL360 GEN8E" => [["UID" => 3, "CNT" => 4, "EXTRA" => ""]], + "HP DL360 GEN9E" => [["UID" => 4, "CNT" => 4, "EXTRA" => ""]], + "HP DL360 GEN10" => [["UID" => 5, "CNT" => 2, "EXTRA" => ""]], + "Hitach HA3000" => [["UID" => 5, "CNT" => 2, "EXTRA" => ""]], + "DESKTOP I5-9" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-10" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-12" => [["UID" => 10, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-9" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-10" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-12" => [["UID" => 10, "CNT" => 1, "EXTRA" => ""]], + ], + "DISK" => [ + "HP DL360 GEN6B" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN7C" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN7D" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN8C" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN8D" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN8E" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN9E" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "HP DL360 GEN10" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "Hitach HA3000" => [["UID" => 8, "CNT" => 2, "EXTRA" => "RAID1"], ["UID" => 9, "CNT" => 2, "EXTRA" => "RAID1"],], + "DESKTOP I5-9" => [["UID" => 8, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-10" => [["UID" => 8, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I5-12" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-9" => [["UID" => 8, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-10" => [["UID" => 8, "CNT" => 1, "EXTRA" => ""]], + "DESKTOP I7-12" => [["UID" => 9, "CNT" => 1, "EXTRA" => ""]], + ] +]); +//결제 관련 +define("PAYMENT", [ + 'BILLING' => [ + 'BASE' => 'base', + 'MONTH' => 'month', + 'ONETIME' => 'onetime' + ], + 'PAY' => [ + 'ACCOUNT' => 'account', + 'COUPON' => 'coupon', + 'POINT' => 'point' + ] +]); +//게시판 관련 +define("BOARD", [ + 'CATEGORY' => [ + 'NOTICE' => 'notice', + 'REQUESTTASK' => 'requesttask' + ], +]); diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php new file mode 100644 index 0000000..2ac41a7 --- /dev/null +++ b/app/Config/ContentSecurityPolicy.php @@ -0,0 +1,176 @@ +|string|null + */ + public $defaultSrc; + + /** + * Lists allowed scripts' URLs. + * + * @var list|string + */ + public $scriptSrc = 'self'; + + /** + * Lists allowed stylesheets' URLs. + * + * @var list|string + */ + public $styleSrc = 'self'; + + /** + * Defines the origins from which images can be loaded. + * + * @var list|string + */ + public $imageSrc = 'self'; + + /** + * Restricts the URLs that can appear in a page's `` element. + * + * Will default to self if not overridden + * + * @var list|string|null + */ + public $baseURI; + + /** + * Lists the URLs for workers and embedded frame contents + * + * @var list|string + */ + public $childSrc = 'self'; + + /** + * Limits the origins that you can connect to (via XHR, + * WebSockets, and EventSource). + * + * @var list|string + */ + public $connectSrc = 'self'; + + /** + * Specifies the origins that can serve web fonts. + * + * @var list|string + */ + public $fontSrc; + + /** + * Lists valid endpoints for submission from `
` tags. + * + * @var list|string + */ + public $formAction = 'self'; + + /** + * Specifies the sources that can embed the current page. + * This directive applies to ``, ` + + + + + + \ No newline at end of file diff --git a/app/Views/templates/front/index_footer.php b/app/Views/templates/front/index_footer.php new file mode 100644 index 0000000..e69de29 diff --git a/app/Views/templates/front/index_header.php b/app/Views/templates/front/index_header.php new file mode 100644 index 0000000..f0c64c4 --- /dev/null +++ b/app/Views/templates/front/index_header.php @@ -0,0 +1,87 @@ + + \ No newline at end of file diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php new file mode 100644 index 0000000..c18eca3 --- /dev/null +++ b/app/Views/welcome_message.php @@ -0,0 +1,331 @@ + + + + + Welcome to CodeIgniter 4! + + + + + + + + + + + +
+ + + +
+ +

Welcome to CodeIgniter

+ +

The small framework with powerful features

+ +
+ +
+ + + +
+ +

About this page

+ +

The page you are looking at is being generated dynamically by CodeIgniter.

+ +

If you would like to edit this page you will find it located at:

+ +
app/Views/welcome_message.php
+ +

The corresponding controller for this page can be found at:

+ +
app/Controllers/Home.php
+ +
+ +
+ +
+ +

Go further

+ +

+ + Learn +

+ +

The User Guide contains an introduction, tutorial, a number of "how to" + guides, and then reference documentation for the components that make up + the framework. Check the User Guide !

+ +

+ + Discuss +

+ +

CodeIgniter is a community-developed open source project, with several + venues for the community members to gather and exchange ideas. View all + the threads on CodeIgniter's forum, or chat on Slack !

+ +

+ + Contribute +

+ +

CodeIgniter is a community driven project and accepts contributions + of code and documentation from the community. Why not + + join us ?

+ +
+ +
+ + + +
+
+ +

Page rendered in {elapsed_time} seconds using {memory_usage} MB of memory.

+ +

Environment:

+ +
+ +
+ +

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT + open source licence.

+ +
+ +
+ + + + + + + + + diff --git a/builds b/builds new file mode 100644 index 0000000..cc2ca08 --- /dev/null +++ b/builds @@ -0,0 +1,125 @@ +#!/usr/bin/env php + 'vcs', + 'url' => GITHUB_URL, + ]; + } + + $array['require']['codeigniter4/codeigniter4'] = 'dev-develop'; + unset($array['require']['codeigniter4/framework']); + } else { + unset($array['minimum-stability']); + + if (isset($array['repositories'])) { + foreach ($array['repositories'] as $i => $repository) { + if ($repository['url'] === GITHUB_URL) { + unset($array['repositories'][$i]); + break; + } + } + + if (empty($array['repositories'])) { + unset($array['repositories']); + } + } + + $array['require']['codeigniter4/framework'] = LATEST_RELEASE; + unset($array['require']['codeigniter4/codeigniter4']); + } + + file_put_contents($file, json_encode($array, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL); + + $modified[] = $file; + } else { + echo 'Warning: Unable to decode composer.json! Skipping...' . PHP_EOL; + } + } else { + echo 'Warning: Unable to read composer.json! Skipping...' . PHP_EOL; + } +} + +$files = [ + __DIR__ . DIRECTORY_SEPARATOR . 'app/Config/Paths.php', + __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml.dist', + __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml', +]; + +foreach ($files as $file) { + if (is_file($file)) { + $contents = file_get_contents($file); + + if ($dev) { + $contents = str_replace('vendor/codeigniter4/framework', 'vendor/codeigniter4/codeigniter4', $contents); + } else { + $contents = str_replace('vendor/codeigniter4/codeigniter4', 'vendor/codeigniter4/framework', $contents); + } + + file_put_contents($file, $contents); + + $modified[] = $file; + } +} + +if ($modified === []) { + echo 'No files modified.' . PHP_EOL; +} else { + echo 'The following files were modified:' . PHP_EOL; + + foreach ($modified as $file) { + echo " * {$file}" . PHP_EOL; + } + + echo 'Run `composer update` to sync changes with your vendor folder.' . PHP_EOL; +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0c322e2 --- /dev/null +++ b/composer.json @@ -0,0 +1,48 @@ +{ + "name": "codeigniter4/appstarter", + "description": "CodeIgniter4 starter app", + "license": "MIT", + "type": "project", + "homepage": "https://codeigniter.com", + "support": { + "forum": "https://forum.codeigniter.com/", + "source": "https://github.com/codeigniter4/CodeIgniter4", + "slack": "https://codeigniterchat.slack.com" + }, + "require": { +"php": "^8.2", + "codeigniter4/framework": "^4.5", + "guzzlehttp/guzzle": "^7.9", + "io-developer/php-whois": "^4.1", + "phpoffice/phpspreadsheet": "^1.27", + "symfony/css-selector": "^7.1", + "twbs/bootstrap": "5.3.3" + }, + "require-dev": { + "fakerphp/faker": "^1.9", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^10.5.16" + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Config\\": "app/Config/" + }, + "exclude-from-classmap": [ + "**/Database/Migrations/**" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\Support\\": "tests/_support" + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + }, + "scripts": { + "test": "phpunit" + } +} diff --git a/env b/env new file mode 100644 index 0000000..f359ec2 --- /dev/null +++ b/env @@ -0,0 +1,69 @@ +#-------------------------------------------------------------------- +# Example Environment Configuration file +# +# This file can be used as a starting point for your own +# custom .env files, and contains most of the possible settings +# available in a default install. +# +# By default, all of the settings are commented out. If you want +# to override the setting, you must un-comment it by removing the '#' +# at the beginning of the line. +#-------------------------------------------------------------------- + +#-------------------------------------------------------------------- +# ENVIRONMENT +#-------------------------------------------------------------------- + +# CI_ENVIRONMENT = production + +#-------------------------------------------------------------------- +# APP +#-------------------------------------------------------------------- + +# app.baseURL = '' +# If you have trouble with `.`, you could also use `_`. +# app_baseURL = '' +# app.forceGlobalSecureRequests = false +# app.CSPEnabled = false + +#-------------------------------------------------------------------- +# DATABASE +#-------------------------------------------------------------------- + +# database.default.hostname = localhost +# database.default.database = ci4 +# database.default.username = root +# database.default.password = root +# database.default.DBDriver = MySQLi +# database.default.DBPrefix = +# database.default.port = 3306 + +# If you use MySQLi as tests, first update the values of Config\Database::$tests. +# database.tests.hostname = localhost +# database.tests.database = ci4_test +# database.tests.username = root +# database.tests.password = root +# database.tests.DBDriver = MySQLi +# database.tests.DBPrefix = +# database.tests.charset = utf8mb4 +# database.tests.DBCollat = utf8mb4_general_ci +# database.tests.port = 3306 + +#-------------------------------------------------------------------- +# ENCRYPTION +#-------------------------------------------------------------------- + +# encryption.key = + +#-------------------------------------------------------------------- +# SESSION +#-------------------------------------------------------------------- + +# session.driver = 'CodeIgniter\Session\Handlers\FileHandler' +# session.savePath = null + +#-------------------------------------------------------------------- +# LOGGER +#-------------------------------------------------------------------- + +# logger.threshold = 4 diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..b408a99 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,63 @@ + + + + + + + + + + + + + ./tests + + + + + + + + + + ./app + + + ./app/Views + ./app/Config/Routes.php + + + + + + + + + + + + + + + diff --git a/preload.php b/preload.php new file mode 100644 index 0000000..9d16bb3 --- /dev/null +++ b/preload.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use CodeIgniter\Boot; +use Config\Paths; + +/* + *--------------------------------------------------------------- + * Sample file for Preloading + *--------------------------------------------------------------- + * See https://www.php.net/manual/en/opcache.preloading.php + * + * How to Use: + * 0. Copy this file to your project root folder. + * 1. Set the $paths property of the preload class below. + * 2. Set opcache.preload in php.ini. + * php.ini: + * opcache.preload=/path/to/preload.php + */ + +// Load the paths config file +require __DIR__ . '/app/Config/Paths.php'; + +// Path to the front controller +define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR); + +class preload +{ + /** + * @var array Paths to preload. + */ + private array $paths = [ + [ + 'include' => __DIR__ . '/vendor/codeigniter4/framework/system', // Change this path if using manual installation + 'exclude' => [ + // Not needed if you don't use them. + '/system/Database/OCI8/', + '/system/Database/Postgre/', + '/system/Database/SQLite3/', + '/system/Database/SQLSRV/', + // Not needed for web apps. + '/system/Database/Seeder.php', + '/system/Test/', + '/system/CLI/', + '/system/Commands/', + '/system/Publisher/', + '/system/ComposerScripts.php', + // Not Class/Function files. + '/system/Config/Routes.php', + '/system/Language/', + '/system/bootstrap.php', + '/system/util_bootstrap.php', + '/system/rewrite.php', + '/Views/', + // Errors occur. + '/system/ThirdParty/', + ], + ], + ]; + + public function __construct() + { + $this->loadAutoloader(); + } + + private function loadAutoloader(): void + { + $paths = new Paths(); + require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'Boot.php'; + + Boot::preload($paths); + } + + /** + * Load PHP files. + */ + public function load(): void + { + foreach ($this->paths as $path) { + $directory = new RecursiveDirectoryIterator($path['include']); + $fullTree = new RecursiveIteratorIterator($directory); + $phpFiles = new RegexIterator( + $fullTree, + '/.+((? $file) { + foreach ($path['exclude'] as $exclude) { + if (str_contains($file[0], $exclude)) { + continue 2; + } + } + + require_once $file[0]; + echo 'Loaded: ' . $file[0] . "\n"; + } + } + } +} + +(new preload())->load(); diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..abac3cb --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,49 @@ +# Disable directory browsing +Options -Indexes + +# ---------------------------------------------------------------------- +# Rewrite engine +# ---------------------------------------------------------------------- + +# Turning on the rewrite engine is necessary for the following rules and features. +# FollowSymLinks must be enabled for this to work. + + Options +FollowSymlinks + RewriteEngine On + + # If you installed CodeIgniter in a subfolder, you will need to + # change the following line to match the subfolder you need. + # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase + # RewriteBase / + + # Redirect Trailing Slashes... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Rewrite "www.example.com -> example.com" + RewriteCond %{HTTPS} !=on + RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] + + # Checks to see if the user is attempting to access a valid file, + # such as an image or css document, if this isn't true it sends the + # request to the front controller, index.php + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA] + + # Ensure Authorization header is passed along + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + + + # If we don't have mod_rewrite installed, all 404's + # can be sent to index.php, and everything works as normal. + ErrorDocument 404 index.php + + +# Disable server signature start +ServerSignature Off +# Disable server signature end diff --git a/public/css/admin.css b/public/css/admin.css new file mode 100644 index 0000000..4576f3b --- /dev/null +++ b/public/css/admin.css @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------ + * Name : admin.css + * Desc : Admin StyleSheet + * Created : 2016/9/11 Tri-aBility by Junheum,Choi + * Updated : + ------------------------------------------------------------ */ +* { + margin: 0px; + padding: 0px; + border: 0px; +} + +html, +body { + height: 100%; +} + +div.layout_top { + height: 51px; + margin-bottom: 10px; + border-top: 1px solid gray; + border-bottom: 1px solid gray; + background-color: #e8e9ea; +} + +div.layout_bottom { + height: 51px; + margin-top: 10px; + border-top: 1px solid gray; + border-bottom: 1px solid gray; + background-color: #efefef; + background-color: #e8e9ea; +} + +table.layout_middle { + width: 100%; + /* border: 1px solid blue; */ +} + +table.layout_middle td.layout_left { + vertical-align: top; + /* border: 1px solid red; */ +} + +table.layout_middle td.layout_right { + padding-top: 5px; + padding-left: 23px; + padding-right: 5px; + padding-bottom: 5px; + /* overflow: auto; */ + /* border: 1px solid blue; */ +} + +table.layout_middle td.layout_right div.layout_header { + /*content 헤더라인*/ + height: 55px; + padding-top: 15px; + background-color: #e7e7e7; + border-top: 1px solid gray; + border-left: 1px solid gray; + border-right: 1px solid gray; + border-radius: 15px 15px 0px 0px; +} + +table.layout_middle td.layout_right div.layout_header li.nav-item {} + +table.layout_middle td.layout_right div.layout_footer { + /*content 하단라인*/ + height: 20px; + border-left: 1px solid gray; + border-right: 1px solid gray; + border-bottom: 1px solid gray; + border-radius: 0px 0px 15px 15px; +} + +table.layout_middle td.layout_right div.layout_content { + /*content 부분*/ + padding: 5px; + border-left: 1px solid gray; + border-right: 1px solid gray; +} \ No newline at end of file diff --git a/public/css/admin/form.css b/public/css/admin/form.css new file mode 100644 index 0000000..6f14a64 --- /dev/null +++ b/public/css/admin/form.css @@ -0,0 +1,40 @@ +.form-label { + font-size: 12px; + margin: 0 !important; + padding: 0 !important; +} + +/* create,modify,view 페이지용 */ +div.action_form { + /* 블록 요소로 변경 */ + margin-left: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + margin-right: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + /* border: 1px solid blue; */ + /* table-layout: fixed; 고정 레이아웃 */ + border-collapse: collapse; + /* 테두리 결합 */ +} + +div.action_form table {} + +div.action_form table th { + background-color: #f0f0f0; +} + +div.action_form table td { + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ +} + +/* create,modify,view 페이지용 */ + +/*Form 안에서 datepicker 사용시*/ +/* datepicker의 z-index를 높여서 다른 요소 위에 표시되도록 설정 tinyMCE와 겹치는 문제 처리용용*/ +.ui-datepicker { + z-index: 9999 !important; +} \ No newline at end of file diff --git a/public/css/admin/index.css b/public/css/admin/index.css new file mode 100644 index 0000000..c9b79ff --- /dev/null +++ b/public/css/admin/index.css @@ -0,0 +1,161 @@ +.form-label { + font-size: 12px; + margin: 0 !important; + padding: 0 !important; +} + +/* create,modify,view 페이지용 */ +table.action_form { + /* 블록 요소로 변경 */ + margin-left: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + margin-right: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + /* border: 1px solid blue; */ + /* table-layout: fixed; 고정 레이아웃 */ + border-collapse: collapse; + /* 테두리 결합 */ +} + +table.action_form th { + text-align: center; + background-color: #f5f5f5; + +} + +table.action_form td { + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ +} + +/* create,modify,view 페이지용 */ + +/*조건검색*/ +nav.index_top nav.condition { + border-color: 1px solid red; +} + +/*검색*/ +nav.index_top nav.search { + position: relative; + border-color: 1px solid red; +} + +nav.index_top nav.search input[type="text"] { + width: 150px; +} + +/*검색submit*/ +nav.index_top nav.search input[type="submit"] { + font-weight: bold; + width: 70px; + color: white; + background-color: #555555; +} + +/*검색submit*/ +nav.index_top nav.search a.excel { + position: absolute; + top: -5px; + right: -45px; + /* border-color: 1px solid red; */ +} + +/*페이지정보*/ +nav.index_top nav.pageinfo { + font-weight: bold; + /* border-color: 1px solid red; */ +} + +/* Table 부분 */ +table.index_table { + width: 100%; + /* table-layout: fixed; */ + border-collapse: collapse; +} + +table.index_table thead tr th { + white-space: nowrap; + padding-top: 15px; + padding-bottom: 15px; + font-weight: bold; + border-top: 2px solid black; + border-bottom: 1px solid silver; + background-color: #f5f5f5; + text-align: center; + /* border:1px solid silver; */ +} + +table.index_table thead th.index_head_short_column { + width: 80px; +} + +table.index_table thead th:active { + cursor: grabbing; +} + +table.index_table tbody th { + text-align: center; + /* border:1px solid silver; */ +} + +div.index_batchjob { + padding-top: 15px; + text-align: center; + /* border: 1px solid red; */ +} + +div.index_batchjob ul.nav li.nav-item { + margin-left: 10px; + /* border: 1px solid red; */ +} + +div.index_pagination { + margin-top: 20px; + /* border: 1px solid red; */ +} + +div.index_pagination ul.pagination { + /* border: 1px solid green; */ + width: fit-content; + /* UL의 너비를 내용에 맞춤 */ + margin: 0 auto; + /* 좌우 마진을 자동으로 설정하여 중앙 배치 */ + padding: 0; + list-style: none; + /* 기본 점 스타일 제거 (옵션) */ +} + +/* pager의 template가 default_full일경우 사용 */ +/* div.index_pagination ul.pagination li { + margin-left: 5px; +} + +div.index_pagination ul.pagination li a { + padding: 5px 10px 5px 10px; + font-size: 1.5rem; + color: white; + background-color: #808080; +} + +div.index_pagination ul.pagination li a:hover { + border: 1px solid black; +} + +div.index_pagination ul.pagination li.active a { + color: black; + border: 1px solid #808080; +} */ + +div.index_bottom { + padding-top: 15px; + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ + /* border: 1px solid red; */ +} \ No newline at end of file diff --git a/public/css/admin/left_menu.css b/public/css/admin/left_menu.css new file mode 100644 index 0000000..80ca2aa --- /dev/null +++ b/public/css/admin/left_menu.css @@ -0,0 +1,56 @@ +div#left_menu { + position: fixed; + margin-top: 70px; + z-index: 100; + border: 1px solid silver; +} + +div#left_menu div#menu_button { + position: absolute; + top: -1px; + right: -20px; + width: 20px; + height: 160px; + cursor: pointer; + writing-mode: vertical-rl; + /* 세로로 글자를 출력 */ + text-orientation: upright; + /* 글자가 직립되도록 설정 */ + border-top: 1px solid silver; + border-right: 1px solid silver; + border-bottom: 1px solid silver; + border-radius: 0px 5px 5px 0px; + background-color: #e8e9ea; +} + +div#left_menu div.accordion { + display: none; + width: 20px; +} + +div#left_menu div.accordion div.main { + height: 50px; + padding-top: 15px; + padding-left: 10px; + background-color: white; + border-bottom: 1px solid silver; +} + +div#left_menu div.accordion div.accordion-item { + height: 50px; + padding-top: 15px; + background-color: #eeeeee; + border-bottom: 1px solid silver; +} + +div#left_menu div.accordion div.accordion-item:hover { + background-color: silver; +} + +div#left_menu div.accordion div.accordion-item a { + padding-left: 20px; +} + +div#left_menu div.accordion div.accordion-collapse a { + padding-left: 30px; +} \ No newline at end of file diff --git a/public/css/admin/member_link.css b/public/css/admin/member_link.css new file mode 100644 index 0000000..38ed839 --- /dev/null +++ b/public/css/admin/member_link.css @@ -0,0 +1,17 @@ +nav.top_menu ul.member-link{ + /* border:1px solid red; */ + color:#3a37f3; + padding-right:20px; +} + +nav.top_menu ul.member-link a{ + color:#3a37f3; +} + +nav.top_menu ul.member-link ul.dropdown-menu li:hover{ + background-color: #eaeaea; +} + +nav.top_menu ul.member-link ul.dropdown-menu li a{ + padding-left:10px; +} \ No newline at end of file diff --git a/public/css/admin/resizeTable.css b/public/css/admin/resizeTable.css new file mode 100644 index 0000000..8c4e075 --- /dev/null +++ b/public/css/admin/resizeTable.css @@ -0,0 +1,67 @@ +.rtc-wrapper table.rtc-table { + border-collapse: collapse; + white-space: nowrap; + margin: 0; +} + +.rtc-wrapper table.rtc-table thead, +.rtc-wrapper table.rtc-table tbody, +.rtc-wrapper table.rtc-table tfoot { + margin: 0; +} + +.rtc-wrapper table.rtc-table thead tr, +.rtc-wrapper table.rtc-table tbody tr, +.rtc-wrapper table.rtc-table tfoot tr { + margin: 0; +} + +.rtc-wrapper table.rtc-table thead tr th, +.rtc-wrapper table.rtc-table thead tr td, +.rtc-wrapper table.rtc-table tbody tr th, +.rtc-wrapper table.rtc-table tbody tr td, +.rtc-wrapper table.rtc-table tfoot tr th, +.rtc-wrapper table.rtc-table tfoot tr td { + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rtc-wrapper table.rtc-table.rtc-table-resizing { + cursor: col-resize; +} + +.rtc-wrapper table.rtc-table.rtc-table-resizing thead, +.rtc-wrapper table.rtc-table.rtc-table-resizing thead > th, +.rtc-wrapper table.rtc-table.rtc-table-resizing thead > th > a { + cursor: col-resize; +} + +.rtc-wrapper table.rtc-table thead tr.invisible, +.rtc-wrapper table.rtc-table thead tr.invisible th { + border: none; + margin: 0; + padding: 0; + height: 0 !important; +} + +.rtc-wrapper .rtc-handle-container { + position: relative; + padding: 0; + margin: 0; + border: 0; +} + +.rtc-wrapper .rtc-handle-container .rtc-handle { + position: absolute; + width: 6.5px; + margin-left: -3.575px; + z-index: 2; + cursor: col-resize; +} + +.rtc-wrapper .rtc-handle-container .rtc-handle:last-of-type { + width: 4.5px; + margin-left: -4.95px; +} \ No newline at end of file diff --git a/public/css/admin/server_partinfo.css b/public/css/admin/server_partinfo.css new file mode 100644 index 0000000..5e069fb --- /dev/null +++ b/public/css/admin/server_partinfo.css @@ -0,0 +1,56 @@ +.server_partinfo_layer_btn { + background-color: #0d6efd; /* Bootstrap 5 primary color */ + color: #fff; /* 흰색 텍스트 */ + border: 1px solid #0d6efd; + padding: 0.375rem 0.75rem; /* 기본 패딩 */ + font-size: 1rem; + line-height: 1.5; + border-radius: 0.375rem; /* 기본 border-radius */ + cursor: pointer; + text-align: center; + display: inline-block; + text-decoration: none; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; +} + +.server_partinfo_layer_btn:hover { + background-color: #0b5ed7; + border-color: #0a58ca; +} +.server_partinfo_layer { + position: fixed; + top: 20%; + left: 50%; + transform: translateX(-50%); + background: #fff; + border: 1px solid #ccc; + padding: 20px; + z-index: 1000; + box-shadow: 0 0 10px rgba(0,0,0,0.2); +} + +.server_partinfo_layer_inner { + text-align:center; +} + +.server_partinfo_item_list li { + margin: 5px 0; +} + +.server_partinfo_item_list label { + cursor: pointer; + color: #007bff; + text-decoration: underline; +} + +.server_partinfo_items { + margin-top: 20px; +} + +.server_partinfo_item { + padding: 5px 10px; + background: #f0f0f0; + margin: 5px 0; + display: flex; + align-items: left; +} diff --git a/public/css/common/style.css b/public/css/common/style.css new file mode 100644 index 0000000..54b28ae --- /dev/null +++ b/public/css/common/style.css @@ -0,0 +1,55 @@ +/* ------------------------------------------------------------ + * Name : admin.css + * Desc : Admin StyleSheet + * Created : 2016/9/11 Tri-aBility by Junheum,Choi + * Updated : + ------------------------------------------------------------ */ +@charset "utf-8"; + +/* user class */ +h1, +h2, +h3, +h4, +h5, +h6, +strong, +th, +.bold { + font-weight: 500; +} + +input[type=text], +input[type=password] { + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + white-space: nowrap; +} + +select, +textarea, +button { + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + white-space: nowrap; +} + +a:link { + text-decoration: none; +} + +a:visited { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:active { + text-decoration: underline; +} \ No newline at end of file diff --git a/public/css/empty.css b/public/css/empty.css new file mode 100644 index 0000000..1e4df2e --- /dev/null +++ b/public/css/empty.css @@ -0,0 +1,11 @@ +/* ------------------------------------------------------------ + * Name : admin.css + * Desc : Admin StyleSheet + * Created : 2016/9/11 Tri-aBility by Junheum,Choi + * Updated : + ------------------------------------------------------------ */ +* { + margin: 0px; + padding: 0px; + border: 0px; +} \ No newline at end of file diff --git a/public/css/front.css b/public/css/front.css new file mode 100644 index 0000000..e2255e4 --- /dev/null +++ b/public/css/front.css @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------ + * Name : front.css + * Desc : Admin StyleSheet + * Created : 2016/9/11 Tri-aBility by Junheum,Choi + * Updated : + ------------------------------------------------------------ */ +* { + margin: 0px; + padding: 0px; + border: 0px; +} + +html, +body { + height: 100%; +} + +div.layout_top { + height: 51px; + margin-bottom: 10px; + border-top: 1px solid gray; + border-bottom: 1px solid gray; + background-color: #e8e9ea; +} + +div.layout_bottom { + height: 51px; + margin-top: 10px; + border-top: 1px solid gray; + border-bottom: 1px solid gray; + background-color: #efefef; + background-color: #e8e9ea; +} + +table.layout_middle { + width: 100%; + /* border: 1px solid blue; */ +} + +table.layout_middle td.layout_left { + vertical-align: top; + /* border: 1px solid red; */ +} + +table.layout_middle td.layout_right { + padding-top: 5px; + padding-left: 23px; + padding-right: 5px; + padding-bottom: 5px; + /* overflow: auto; */ + /* border: 1px solid blue; */ +} + +table.layout_middle td.layout_right div.layout_header { + /*content 헤더라인*/ + height: 55px; + padding-top: 15px; + background-color: #e7e7e7; + border-top: 1px solid gray; + border-left: 1px solid gray; + border-right: 1px solid gray; + border-radius: 15px 15px 0px 0px; +} + +table.layout_middle td.layout_right div.layout_header li.nav-item {} + +table.layout_middle td.layout_right div.layout_footer { + /*content 하단라인*/ + height: 20px; + border-left: 1px solid gray; + border-right: 1px solid gray; + border-bottom: 1px solid gray; + border-radius: 0px 0px 15px 15px; +} + +table.layout_middle td.layout_right div.layout_content { + /*content 부분*/ + padding: 5px; + border-left: 1px solid gray; + border-right: 1px solid gray; +} \ No newline at end of file diff --git a/public/css/front/copyright.css b/public/css/front/copyright.css new file mode 100644 index 0000000..45a4a77 --- /dev/null +++ b/public/css/front/copyright.css @@ -0,0 +1,29 @@ +div#copyright{ + width:100%; + height:300px; + padding-top:30px; + padding-bottom:30px; + background-color:#2d2e2e; + color:white; +} + +div#copyright div#content_bottom{ + color:white; + text-align:left; + /* border-left:1px solid silver; + border-right:1px solid silver; */ +} + +div#copyright div#content_bottom .item{ + padding-top:5px; + padding-left:5px; + padding-right:5px; + border-top:1px solid silver; + border-left:1px solid silver; + border-right:1px solid silver; +} + +div#copyright div#content_bottom div.company_info{ + padding:10px; + border:1px solid silver; +} diff --git a/public/css/front/form.css b/public/css/front/form.css new file mode 100644 index 0000000..8276f4f --- /dev/null +++ b/public/css/front/form.css @@ -0,0 +1,34 @@ +/* create,modify,view 페이지용 */ +div.action_form { + /* 블록 요소로 변경 */ + margin-left: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + margin-right: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + /* border: 1px solid blue; */ + /* table-layout: fixed; 고정 레이아웃 */ + border-collapse: collapse; + /* 테두리 결합 */ +} + +div.action_form table {} + +div.action_form table th { + background-color: #f0f0f0; +} + +div.action_form table td { + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ +} + +/* create,modify,view 페이지용 */ + +/*Form 안에서 datepicker 사용시*/ +/* datepicker의 z-index를 높여서 다른 요소 위에 표시되도록 설정 tinyMCE와 겹치는 문제 처리용용*/ +.ui-datepicker { + z-index: 9999 !important; +} \ No newline at end of file diff --git a/public/css/front/index.css b/public/css/front/index.css new file mode 100644 index 0000000..94c68a2 --- /dev/null +++ b/public/css/front/index.css @@ -0,0 +1,157 @@ +/* create,modify,view 페이지용 */ +table.action_form { + /* 블록 요소로 변경 */ + margin-left: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + margin-right: auto; + /* 자동 여백을 이용한 가로 가운데 정렬 */ + /* border: 1px solid blue; */ + /* table-layout: fixed; 고정 레이아웃 */ + border-collapse: collapse; + /* 테두리 결합 */ +} + +table.action_form th { + text-align: center; + background-color: #f5f5f5; +} + +table.action_form td { + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ +} + +/* create,modify,view 페이지용 */ + +/*조건검색*/ +nav.index_top nav.condition { + border-color: 1px solid red; +} + +/*검색*/ +nav.index_top nav.search { + position: relative; + height: 30px; + border-color: 1px solid red; +} + +nav.index_top nav.search input[type="text"] { + width: 200px; + height: 30px; +} + +/*검색submit*/ +nav.index_top nav.search input[type="submit"] { + font-weight: bold; + width: 80px; + height: 30px; + color: white; + background-color: #555555; +} + +/*검색submit*/ +nav.index_top nav.search a.excel { + position: absolute; + top: -9px; + right: -45px; + /* border-color: 1px solid red; */ +} + +/*페이지정보*/ +nav.index_top nav.pageinfo { + font-weight: bold; + /* border-color: 1px solid red; */ +} + +/* Table 부분 */ +table.index_table { + width: 100%; + /* table-layout: fixed; */ + border-collapse: collapse; +} + +table.index_table thead th { + white-space: nowrap; + padding-top: 15px; + padding-bottom: 15px; + font-weight: bold; + border-top: 2px solid black; + border-bottom: 1px solid silver; + background-color: #f5f5f5; + text-align: center; + /* border:1px solid silver; */ +} + +table.index_table thead th.index_head_short_column { + width: 80px; +} + +table.index_table thead th:active { + cursor: grabbing; +} + +table.index_table tbody th { + text-align: center; + /* border:1px solid silver; */ +} + +div.index_batchjob { + padding-top: 15px; + text-align: center; + /* border: 1px solid red; */ +} + +div.index_batchjob ul.nav li.nav-item { + margin-left: 10px; + /* border: 1px solid red; */ +} + +div.index_pagination { + margin-top: 20px; + /* border: 1px solid red; */ +} + +div.index_pagination ul.pagination { + /* border: 1px solid green; */ + width: fit-content; + /* UL의 너비를 내용에 맞춤 */ + margin: 0 auto; + /* 좌우 마진을 자동으로 설정하여 중앙 배치 */ + padding: 0; + list-style: none; + /* 기본 점 스타일 제거 (옵션) */ +} + +/* pager의 template가 default_full일경우 사용 */ +/* div.index_pagination ul.pagination li { + margin-left: 5px; +} + +div.index_pagination ul.pagination li a { + padding: 5px 10px 5px 10px; + font-size: 1.5rem; + color: white; + background-color: #808080; +} + +div.index_pagination ul.pagination li a:hover { + border: 1px solid black; +} + +div.index_pagination ul.pagination li.active a { + color: black; + border: 1px solid #808080; +} */ + +div.index_bottom { + padding-top: 15px; + text-align: center; + word-wrap: break-word; + /* 긴 단어 강제 줄바꿈 */ + white-space: normal; + /* 자동 줄바꿈 */ + /* border: 1px solid red; */ +} \ No newline at end of file diff --git a/public/css/front/left_menu.css b/public/css/front/left_menu.css new file mode 100644 index 0000000..cc89e40 --- /dev/null +++ b/public/css/front/left_menu.css @@ -0,0 +1,56 @@ +div#left_menu { + position: fixed; + margin-top: 60px; + z-index: 100; + border: 1px solid silver; +} + +div#left_menu div#menu_button { + position: absolute; + top: -1px; + right: -21px; + width: 20px; + height: 160px; + cursor: pointer; + writing-mode: vertical-rl; + /* 세로로 글자를 출력 */ + text-orientation: upright; + /* 글자가 직립되도록 설정 */ + border-top: 1px solid silver; + border-right: 1px solid silver; + border-bottom: 1px solid silver; + border-radius: 0px 5px 5px 0px; + background-color: #e8e9ea; +} + +div#left_menu div.accordion { + display: none; + width: 20px; +} + +div#left_menu div.accordion div.main { + height: 50px; + padding-top: 15px; + padding-left: 10px; + background-color: white; + border-bottom: 1px solid silver; +} + +div#left_menu div.accordion div.accordion-item { + height: 50px; + padding-top: 15px; + background-color: #eeeeee; + border-bottom: 1px solid silver; +} + +div#left_menu div.accordion div.accordion-item:hover { + background-color: silver; +} + +div#left_menu div.accordion div.accordion-item a { + padding-left: 20px; +} + +div#left_menu div.accordion div.accordion-collapse a { + padding-left: 30px; +} \ No newline at end of file diff --git a/public/css/front/login.css b/public/css/front/login.css new file mode 100644 index 0000000..3f80970 --- /dev/null +++ b/public/css/front/login.css @@ -0,0 +1,80 @@ +.login-page { + background-image: url('/images/login-background.jpg'); + background-size: cover; + background-position: center; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.login-content { + background-color: rgba(255, 255, 255, 0.9); + border-radius: 10px; + padding: 40px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); + width: 400px; +} + +.login-title { + text-align: center; + margin-bottom: 30px; + color: #333; +} + +.input-group { + margin-bottom: 20px; +} + +.input-group label { + display: block; + margin-bottom: 5px; + color: #555; +} + +.input-group input { + width: 100%; + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; +} + +.btn-login { + width: 100%; + padding: 12px; + background-color: #007bff; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + margin-bottom: 15px; +} + +.login-options { + display: flex; + justify-content: space-between; +} + +.btn-google, +.btn-facebook { + flex: 1; + padding: 10px; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + text-align: center; + text-decoration: none; +} + +.btn-google { + background-color: #db4437; + color: white; + margin-right: 10px; +} + +.btn-facebook { + background-color: #28a745; + color: white; +} \ No newline at end of file diff --git a/public/css/front/member_link.css b/public/css/front/member_link.css new file mode 100644 index 0000000..38ed839 --- /dev/null +++ b/public/css/front/member_link.css @@ -0,0 +1,17 @@ +nav.top_menu ul.member-link{ + /* border:1px solid red; */ + color:#3a37f3; + padding-right:20px; +} + +nav.top_menu ul.member-link a{ + color:#3a37f3; +} + +nav.top_menu ul.member-link ul.dropdown-menu li:hover{ + background-color: #eaeaea; +} + +nav.top_menu ul.member-link ul.dropdown-menu li a{ + padding-left:10px; +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7ecfce2 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/images/common/com_icon3.png b/public/images/common/com_icon3.png new file mode 100644 index 0000000..eb662cd Binary files /dev/null and b/public/images/common/com_icon3.png differ diff --git a/public/images/common/discord.png b/public/images/common/discord.png new file mode 100644 index 0000000..4a2233c Binary files /dev/null and b/public/images/common/discord.png differ diff --git a/public/images/common/excel.png b/public/images/common/excel.png new file mode 100644 index 0000000..e67fc00 Binary files /dev/null and b/public/images/common/excel.png differ diff --git a/public/images/common/kakaotalk.png b/public/images/common/kakaotalk.png new file mode 100644 index 0000000..cf7a87c Binary files /dev/null and b/public/images/common/kakaotalk.png differ diff --git a/public/images/common/news.png b/public/images/common/news.png new file mode 100644 index 0000000..fa77ce2 Binary files /dev/null and b/public/images/common/news.png differ diff --git a/public/images/common/pdf.png b/public/images/common/pdf.png new file mode 100644 index 0000000..bb18a19 Binary files /dev/null and b/public/images/common/pdf.png differ diff --git a/public/images/common/telegram.png b/public/images/common/telegram.png new file mode 100644 index 0000000..c88f3fa Binary files /dev/null and b/public/images/common/telegram.png differ diff --git a/public/images/common/top.png b/public/images/common/top.png new file mode 100644 index 0000000..84d194d Binary files /dev/null and b/public/images/common/top.png differ diff --git a/public/images/common/top_skype.png b/public/images/common/top_skype.png new file mode 100644 index 0000000..90355c0 Binary files /dev/null and b/public/images/common/top_skype.png differ diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..a0a20db --- /dev/null +++ b/public/index.php @@ -0,0 +1,59 @@ +systemDirectory . '/Boot.php'; + +exit(Boot::bootWeb($paths)); diff --git a/public/js/admin.js b/public/js/admin.js new file mode 100644 index 0000000..a485a38 --- /dev/null +++ b/public/js/admin.js @@ -0,0 +1,73 @@ +/* ------------------------------------------------------------ + * Name : admin.js + * Desc : Admin Javascrip + * Created : 2016/9/11 Tri-aBility by Junheum,Choi + * Updated : + ------------------------------------------------------------ */ + +function is_NumericKey(evt,obj){ + var charCode = (evt.which) ? evt.which : event.keyCode; + switch(charCode){ + case 48://0 + case 49://1 + case 50://2 + case 51://3 + case 52://4 + case 53://5 + case 54://6 + case 55://7 + case 56://8 + case 57://9 + case 96://KeyPad:0 + case 97://KeyPad:1 + case 98://KeyPad:2 + case 99://KeyPad:3 + case 100://KeyPad:4 + case 101://KeyPad:5 + case 102://KeyPad:6 + case 103://KeyPad:7 + case 104://KeyPad:8 + case 105://KeyPad:9 + break; + default: + alert('숫자만 가능합니다['+charCode+']'); + obj.value = obj.value.substring(0,obj.value.length-1); + break; + } +} +function is_NumericType(data){ + if(!data.match(/^[0-9]+$/)){ + throw (new Error('숫자가 아닌값['+data+']이 있습니다')); + } + return true; +}// +function change_CurrencyFormat(obj,currencies){ + //var currencies = document.getElementsByClassName("currency"); + var total_currency = 0; + for(i=0; i { alert("복사가 완료되었습니다."); }) + .catch(err => { console.log('복사가 오류', err); }) +} \ No newline at end of file diff --git a/public/js/admin/clipboard.js b/public/js/admin/clipboard.js new file mode 100644 index 0000000..4c53982 --- /dev/null +++ b/public/js/admin/clipboard.js @@ -0,0 +1,30 @@ +function copyServerPartToClipboard(text) { + try { + if (navigator.clipboard && navigator.clipboard.writeText) { + // HTTPS 환경 + navigator.clipboard.writeText(text) + .then(() => alert(text + "\n 복사되었습니다.")) + .catch(err => alert("실패: " + err)); + } else { + // HTTP 환경 fallback + const temp = document.createElement("textarea"); + temp.value = text; + document.body.appendChild(temp); + temp.select(); + // document.execCommand("copy"); --- DEPRECATED --- + document.body.removeChild(temp); + alert(text + "\n 복사되었습니다."); + } + } catch (err) { + alert("복사 실패: " + err); + } +} + +function copyServerPartsToClipboard() { + // 모든 .serverparts div에서 text-data 속성값 수집 + const elements = document.querySelectorAll(".serverparts"); + const texts = Array.from(elements).map(el => el.getAttribute("text-data") || ""); + // console.log(texts); + const combined = texts.join("\n"); + copyServerPartToClipboard(combined); +} \ No newline at end of file diff --git a/public/js/admin/form.js b/public/js/admin/form.js new file mode 100644 index 0000000..1977628 --- /dev/null +++ b/public/js/admin/form.js @@ -0,0 +1,55 @@ +// /public/js/admin/form.js +window.initFormModal = function (context) { + const $context = context ? $(context) : $(document); + + // ✅ 캘린더 + $context.find(".calender").datepicker({ + changeYear: true, + changeMonth: true, + yearRange: "-10:+0", + dateFormat: "yy-mm-dd" + }); + + // ✅ TinyMCE + if ($context.find(".tinymce").length) { + if (typeof tinymce !== "undefined") { + tinymce.remove(); // 기존 인스턴스 제거 + tinymce.init({ + selector: ".tinymce", + license_key: "gpl", + height: 250, + plugins: "advlist autolink lists link image charmap preview anchor", + toolbar: "undo redo | bold italic underline | align | link image | code fullscreen preview", + menubar: "file edit view insert format tools table help" + }); + } + } + + // ✅ Select2 (입력 허용) + if ($context.find(".select-field").length) { + $context.find(".select-field").select2({ + theme: "bootstrap-5", + tags: true, + allowClear: true, + width: '100%', + dropdownAutoWidth: true, + language: { + noResults: function () { + return "직접 입력 후 Enter"; // 사용자 안내 + } + } + }); + } + + console.log("✅ Form initialized inside modal:", context); +}; + +// ✅ DOM 전체 로드 후 (페이지 최초 로드시 실행) +document.addEventListener("DOMContentLoaded", function () { + window.initFormModal(document); +}); + +// ✅ Modal 로드 시점에 재초기화 +$(document).on("shown.bs.modal", ".modal", function (e) { + window.initFormModal(this); +}); \ No newline at end of file diff --git a/public/js/admin/index.js b/public/js/admin/index.js new file mode 100644 index 0000000..a41c934 --- /dev/null +++ b/public/js/admin/index.js @@ -0,0 +1,33 @@ +document.addEventListener('DOMContentLoaded', function() { + //class가 calender인 inputbox용,날짜field용 + if (document.querySelector(".calender")) { + $(".calender").datepicker({ + changeYear: true, + changeMonth: true, + yearRange: "-10:+0", + dateFormat: "yy-mm-dd" + }); + } + if (document.querySelector(".batchjobuids_checkboxs")) { + //id가 batchjobuids_checkbox인 버튼을 클릭시 class가 batchjobuids_checkboxs인 checkbox용 + $('#batchjobuids_checkbox').click(function (event) { + if (this.checked) { + $('.batchjobuids_checkboxs').each(function () { //loop checkbox + $(this).prop('checked', true); //check + }); + } else { + $('.batchjobuids_checkboxs').each(function () { //loop checkbox + $(this).prop('checked', false); //uncheck + }); + } + }); + } + if (document.querySelector(".select-field")) { + //class가 select-field인 SelectBox용 + $(".select-field").select2({ + theme: "classic", + width: 'style', + dropdownAutoWidth: true + }); + } +}); \ No newline at end of file diff --git a/public/js/admin/left_menu.js b/public/js/admin/left_menu.js new file mode 100644 index 0000000..1cfc98e --- /dev/null +++ b/public/js/admin/left_menu.js @@ -0,0 +1,13 @@ +function sideMenuToggle(left_menu) { + $accordion = $("#accordion")[0]; + if (accordion.clientWidth == 0){ + accordion.style.display = "block"; + $("#accordion").css({ "width": '217px' }) + $("#menu_button").html("메뉴닫기"); + } + else { + accordion.style.display = "none"; + $("#accordion").css({"width":'20px'}) + $("#menu_button").html("메뉴열기"); + } +}//toggleMenu \ No newline at end of file diff --git a/public/js/admin/resizeTable.js b/public/js/admin/resizeTable.js new file mode 100644 index 0000000..58a8c6b --- /dev/null +++ b/public/js/admin/resizeTable.js @@ -0,0 +1,843 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.validide_resizableTableColumns = {})); +})(this, (function (exports) { + 'use strict'; + + var ResizableConstants = /** @class */ (function () { + function ResizableConstants() { + } + ResizableConstants.dataPropertyName = 'validide_rtc_data_object'; + ResizableConstants.classes = { + table: 'rtc-table', + wrapper: 'rtc-wrapper', + handleContainer: 'rtc-handle-container', + handle: 'rtc-handle', + tableResizing: 'rtc-table-resizing', + columnResizing: 'rtc-column-resizing', + }; + ResizableConstants.attributes = { + dataResizable: 'data-rtc-resizable', + dataResizableTable: 'data-rtc-resizable-table' + }; + ResizableConstants.data = { + resizable: 'rtcResizable', + resizableTable: 'rtcResizableTable' + }; + ResizableConstants.events = { + pointerDown: ['mousedown', 'touchstart'], + pointerMove: ['mousemove', 'touchmove'], + pointerUp: ['mouseup', 'touchend'], + windowResize: ['resize'], + eventResizeStart: 'eventResizeStart.rtc', + eventResize: 'eventResize.rtc', + eventResizeStop: 'eventResizeStop.rtc' + }; + return ResizableConstants; + }()); + + var WidthsData = /** @class */ (function () { + function WidthsData() { + this.column = 0; + this.table = 0; + } + return WidthsData; + }()); + var PointerData = /** @class */ (function () { + function PointerData() { + this.x = null; + this.isDoubleClick = false; + } + return PointerData; + }()); + var ResizableEventData = /** @class */ (function () { + function ResizableEventData(column, dragHandler) { + this.pointer = new PointerData(); + this.originalWidths = new WidthsData(); + this.newWidths = new WidthsData(); + this.column = column; + this.dragHandler = dragHandler; + } + return ResizableEventData; + }()); + + var Utilities = /** @class */ (function () { + function Utilities() { + } + Utilities.kebabCaseToCamelCase = function (str) { + return str.replace(Utilities.kebabCaseRegex, function (m) { return m[1].toUpperCase(); }); + }; + Utilities.parseStringToType = function (str) { + if (str.length == 0 || Utilities.onlyWhiteSpace.test(str)) + return str; + if (Utilities.trueRegex.test(str)) + return true; + if (Utilities.falseRegex.test(str)) + return false; + if (Utilities.notEmptyOrWhiteSpace.test(str)) { + var temp = +str; + if (!isNaN(temp)) + return temp; + } + return str; + }; + Utilities.regexEscapeRegex = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; + Utilities.kebabCaseRegex = /(\-\w)/g; + Utilities.trueRegex = /^true$/i; + Utilities.falseRegex = /^false$/i; + Utilities.onlyWhiteSpace = /^\s$/; + Utilities.notEmptyOrWhiteSpace = /\S/; + return Utilities; + }()); + + var UtilitiesDOM = /** @class */ (function () { + function UtilitiesDOM() { + } + UtilitiesDOM.getDataAttributesValues = function (el) { + if (!el) + return null; + var returnValue = {}; + if (el.dataset) { + for (var prop in el.dataset) { + if (el.dataset.hasOwnProperty(prop)) { + returnValue[prop] = Utilities.parseStringToType(el.dataset[prop] || ''); + } + } + } + else { + for (var i = 0; i < el.attributes.length; i++) { + if (!/^data\-/.test(el.attributes[i].name)) + continue; + var name_1 = Utilities.kebabCaseToCamelCase(el.attributes[i].name.replace('data-', '')); + returnValue[name_1] = Utilities.parseStringToType(el.attributes[i].value); + } + } + return returnValue; + }; + return UtilitiesDOM; + }()); + + var ResizableOptions = /** @class */ (function () { + function ResizableOptions(options, element) { + if (options === void 0) { options = null; } + if (element === void 0) { element = null; } + this.resizeFromBody = true; + this.minWidth = 40; + this.maxWidth = null; + this.doubleClickDelay = 500; + this.maxInitialWidthHint = null; + this.store = null; + this.overrideValues(options); + this.overrideValuesFromElement(element); + } + ResizableOptions.prototype.overrideValues = function (options) { + if (options === void 0) { options = null; } + if (!options) + return; + for (var prop in options) { + if (this.hasOwnProperty(prop)) { + this[prop] = options[prop]; + } + } + }; + ResizableOptions.prototype.overrideValuesFromElement = function (element) { + if (element === void 0) { element = null; } + if (!element) + return; + var elementOptions = UtilitiesDOM.getDataAttributesValues(element); + this.overrideValues(elementOptions); + }; + return ResizableOptions; + }()); + + var ResizableTableColumns = /** @class */ (function () { + function ResizableTableColumns(table, options) { + if (typeof table !== 'object' || table === null || table.toString() !== '[object HTMLTableElement]') + throw 'Invalid argument: "table".\nResizableTableColumns requires that the table element is a not null HTMLTableElement object!'; + if (typeof table[ResizableConstants.dataPropertyName] !== 'undefined') + throw "Existing \"".concat(ResizableConstants.dataPropertyName, "\" property.\nTable element already has a '").concat(ResizableConstants.dataPropertyName, "' attached object!"); + this.id = ResizableTableColumns.getInstanceId(); + this.table = table; + this.options = new ResizableOptions(options, table); + this.wrapper = null; + this.ownerDocument = table.ownerDocument; + this.tableHeaders = []; + this.dragHandlesContainer = null; + this.originalWidths = []; + this.eventData = null; + this.lastPointerDown = 0; + this.init(); + this.table[ResizableConstants.dataPropertyName] = this; + } + ResizableTableColumns.prototype.init = function () { + this.validateMarkup(); + this.createHandlerReferences(); + this.wrapTable(); + this.assignTableHeaders(); + this.storeOriginalWidths(); + this.setHeaderWidths(); + this.createDragHandles(); + this.restoreColumnWidths(); + this.checkTableWidth(); + this.syncHandleWidths(); + this.registerWindowResizeHandler(); + }; + ResizableTableColumns.prototype.dispose = function () { + this.destroyDragHandles(); + this.restoreOriginalWidths(); + this.unwrapTable(); + this.onPointerDownRef = null; + this.onPointerMoveRef = null; + this.onPointerUpRef = null; + this.table[ResizableConstants.dataPropertyName] = void (0); + }; + ResizableTableColumns.prototype.validateMarkup = function () { + var theadCount = 0; + var tbodyCount = 0; + var thead = null; + for (var index = 0; index < this.table.childNodes.length; index++) { + var element = this.table.childNodes[index]; + if (element.nodeName === 'THEAD') { + theadCount++; + thead = element; + } + else if (element.nodeName === 'TBODY') { + tbodyCount++; + } + } + if (thead === null || theadCount !== 1) + throw "Markup validation: thead count.\nResizableTableColumns requires that the table element has one(1) table head element. Current count: ".concat(theadCount); + if (tbodyCount !== 1) + throw "Markup validation: tbody count.\nResizableTableColumns requires that the table element has one(1) table body element. Current count: ".concat(tbodyCount); + var theadRowCount = 0; + var firstRow = null; + for (var index = 0; index < thead.childNodes.length; index++) { + var element = thead.childNodes[index]; + if (element.nodeName === 'TR') { + theadRowCount++; + if (firstRow === null) { + firstRow = element; + } + } + } + if (firstRow === null || theadRowCount < 1) + throw "Markup validation: thead row count.\nResizableTableColumns requires that the table head element has at least one(1) table row element. Current count: ".concat(theadRowCount); + var headerCellsCount = 0; + var invalidHeaderCellsCount = 0; + for (var index = 0; index < firstRow.childNodes.length; index++) { + var element = firstRow.childNodes[index]; + if (element.nodeName === 'TH') { + headerCellsCount++; + } + else if (element.nodeName === 'TD') { + invalidHeaderCellsCount++; + } + } + if (headerCellsCount < 1) + throw "Markup validation: thead first row cells count.\nResizableTableColumns requires that the table head's first row element has at least one(1) table header cell element. Current count: ".concat(headerCellsCount); + if (invalidHeaderCellsCount !== 0) + throw "Markup validation: thead first row invalid.\nResizableTableColumns requires that the table head's first row element has no(0) table cell(TD) elements. Current count: ".concat(invalidHeaderCellsCount); + }; + ResizableTableColumns.prototype.wrapTable = function () { + if (this.wrapper) + return; + this.wrapper = this.ownerDocument.createElement('div'); + this.wrapper.classList.add(ResizableConstants.classes.wrapper); + var tableOriginalParent = this.table.parentNode; + tableOriginalParent.insertBefore(this.wrapper, this.table); + tableOriginalParent.removeChild(this.table); + this.wrapper.appendChild(this.table); + this.table.classList.add(ResizableConstants.classes.table); + }; + ResizableTableColumns.prototype.unwrapTable = function () { + this.table.classList.remove(ResizableConstants.classes.table); + if (!this.wrapper) + return; + var tableOriginalParent = this.wrapper.parentNode; + tableOriginalParent.insertBefore(this.table, this.wrapper); + tableOriginalParent.removeChild(this.wrapper); + this.wrapper = null; + }; + ResizableTableColumns.prototype.assignTableHeaders = function () { + var tableHeader; + var firstTableRow; + for (var index = 0; index < this.table.childNodes.length; index++) { + var element = this.table.childNodes[index]; + if (element.nodeName === 'THEAD') { + tableHeader = element; + break; + } + } + if (!tableHeader) + return; + for (var index = 0; index < tableHeader.childNodes.length; index++) { + var element = tableHeader.childNodes[index]; + if (element.nodeName === 'TR') { + firstTableRow = element; + break; + } + } + if (!firstTableRow) + return; + for (var index = 0; index < firstTableRow.childNodes.length; index++) { + var element = firstTableRow.childNodes[index]; + if (element.nodeName === 'TH') { + this.tableHeaders.push(element); + } + } + }; + ResizableTableColumns.prototype.storeOriginalWidths = function () { + var _this = this; + this.tableHeaders + .forEach(function (el) { + _this.originalWidths.push({ + el: el, + detail: el.style.width + }); + }); + this.originalWidths.push({ + el: this.table, + detail: this.table.style.width + }); + }; + ResizableTableColumns.prototype.restoreOriginalWidths = function () { + this.originalWidths + .forEach(function (itm) { + itm.el.style.width = itm.detail; + }); + }; + ResizableTableColumns.prototype.setHeaderWidths = function () { + var _this = this; + this.tableHeaders + .forEach(function (el) { + var width = el.offsetWidth; + var constrainedWidth = _this.constrainWidth(el, width); + if (typeof _this.options.maxInitialWidthHint === 'number') { + constrainedWidth = Math.min(constrainedWidth, _this.options.maxInitialWidthHint); + } + _this.updateWidth(el, constrainedWidth, true, false); + }); + }; + ResizableTableColumns.prototype.constrainWidth = function (el, width) { + var result = width; + result = Math.max(result, this.options.minWidth || -Infinity); + result = Math.min(result, this.options.maxWidth || +Infinity); + return result; + }; + ResizableTableColumns.prototype.createDragHandles = function () { + var _this = this; + var _a; + if (this.dragHandlesContainer != null) + throw 'Drag handlers already created. Call if you wish to recreate them'; + this.dragHandlesContainer = this.ownerDocument.createElement('div'); + (_a = this.wrapper) === null || _a === void 0 ? void 0 : _a.insertBefore(this.dragHandlesContainer, this.table); + this.dragHandlesContainer.classList.add(ResizableConstants.classes.handleContainer); + this.getResizableHeaders() + .forEach(function () { + var _a; + var handler = _this.ownerDocument.createElement('div'); + handler.classList.add(ResizableConstants.classes.handle); + (_a = _this.dragHandlesContainer) === null || _a === void 0 ? void 0 : _a.appendChild(handler); + }); + ResizableConstants.events.pointerDown + .forEach(function (evt, evtIdx) { + var _a; + (_a = _this.dragHandlesContainer) === null || _a === void 0 ? void 0 : _a.addEventListener(evt, _this.onPointerDownRef, false); + }); + }; + ResizableTableColumns.prototype.destroyDragHandles = function () { + var _this = this; + var _a, _b; + if (this.dragHandlesContainer !== null) { + ResizableConstants.events.pointerDown + .forEach(function (evt, evtIdx) { + var _a; + (_a = _this.dragHandlesContainer) === null || _a === void 0 ? void 0 : _a.removeEventListener(evt, _this.onPointerDownRef, false); + }); + (_b = (_a = this.dragHandlesContainer) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.removeChild(this.dragHandlesContainer); + } + }; + ResizableTableColumns.prototype.getDragHandlers = function () { + var nodes = this.dragHandlesContainer == null + ? null + : this.dragHandlesContainer.querySelectorAll(".".concat(ResizableConstants.classes.handle)); + return nodes + ? Array.prototype.slice.call(nodes).filter(function (el) { return el.nodeName === 'DIV'; }) + : new Array(); + }; + ResizableTableColumns.prototype.restoreColumnWidths = function () { + if (!this.options.store) + return; + var tableId = ResizableTableColumns.generateTableId(this.table); + if (tableId.length === 0) + return; + var data = this.options.store.get(tableId); + if (!data) + return; + this.getResizableHeaders() + .forEach(function (el) { + var width = data.columns[ResizableTableColumns.generateColumnId(el)]; + if (typeof width !== 'undefined') { + ResizableTableColumns.setWidth(el, width); + } + }); + if (typeof data.table !== 'undefined') { + ResizableTableColumns.setWidth(this.table, data.table); + } + }; + ResizableTableColumns.prototype.checkTableWidth = function () { + var _a; + var wrapperWidth = this.wrapper.clientWidth; + var tableWidth = this.table.offsetWidth; + var difference = wrapperWidth - tableWidth; + if (difference <= 0) + return; + var resizableWidth = 0; + var addedWidth = 0; + var headersDetails = []; + this.tableHeaders + .forEach(function (el, idx) { + if (el.hasAttribute(ResizableConstants.attributes.dataResizable)) { + var detail = { + el: el, + detail: el.offsetWidth + }; + headersDetails.push(detail); + resizableWidth += detail.detail; + } + }); + var leftToAdd = 0; + var lastResizableCell = null; + var currentDetail; + while ((currentDetail = headersDetails.shift())) { + leftToAdd = difference - addedWidth; + lastResizableCell = currentDetail.el; + var extraWidth = Math.floor((currentDetail.detail / resizableWidth) * difference); + extraWidth = Math.min(extraWidth, leftToAdd); + var newWidth = this.updateWidth(currentDetail.el, currentDetail.detail + extraWidth, false, true); + addedWidth += (newWidth - currentDetail.detail); + if (addedWidth >= difference) + break; + } + leftToAdd = difference - addedWidth; + if (leftToAdd > 0) { + var lastCell = ((_a = headersDetails[0]) === null || _a === void 0 ? void 0 : _a.el) || lastResizableCell || this.tableHeaders[this.tableHeaders.length - 1]; + var lastCellWidth = lastCell.offsetWidth; + this.updateWidth(lastCell, lastCellWidth, true, true); + } + ResizableTableColumns.setWidth(this.table, wrapperWidth); + }; + ResizableTableColumns.prototype.syncHandleWidths = function () { + var _this = this; + var tableWidth = this.table.clientWidth; + ResizableTableColumns.setWidth(this.dragHandlesContainer, tableWidth); + this.dragHandlesContainer.style.minWidth = "".concat(tableWidth, "px"); + var headers = this.getResizableHeaders(); + this.getDragHandlers() + .forEach(function (el, idx) { + var height = (_this.options.resizeFromBody ? _this.table : _this.table.tHead).clientHeight; + if (idx < headers.length) { + var th = headers[idx]; + var left = th.offsetWidth; + left += ResizableTableColumns.getOffset(th).left; + left -= ResizableTableColumns.getOffset(_this.dragHandlesContainer).left; + el.style.left = "".concat(left, "px"); + el.style.height = "".concat(height, "px"); + } + }); + }; + ResizableTableColumns.prototype.getResizableHeaders = function () { + return this.tableHeaders + .filter(function (el, idx) { + return el.hasAttribute(ResizableConstants.attributes.dataResizable); + }); + }; + ResizableTableColumns.prototype.handlePointerDown = function (event) { + this.handlePointerUp(); + var target = event ? event.target : null; + if (target == null) + return; + if (target.nodeName !== 'DIV' || !target.classList.contains(ResizableConstants.classes.handle)) + return; + if (typeof event.button === 'number' && event.button !== 0) + return; // this is not a left click + var dragHandler = target; + var gripIndex = this.getDragHandlers().indexOf(dragHandler); + var resizableHeaders = this.getResizableHeaders(); + if (gripIndex >= resizableHeaders.length) + return; + var millisecondsNow = (new Date()).getTime(); + var isDoubleClick = (millisecondsNow - this.lastPointerDown) < this.options.doubleClickDelay; + var column = resizableHeaders[gripIndex]; + var columnWidth = column.offsetWidth; + var widths = { + column: columnWidth, + table: this.table.offsetWidth + }; + var eventData = new ResizableEventData(column, dragHandler); + eventData.pointer = { + x: ResizableTableColumns.getPointerX(event), + isDoubleClick: isDoubleClick + }; + eventData.originalWidths = widths; + eventData.newWidths = widths; + this.detachHandlers(); //make sure we do not have extra handlers + this.attachHandlers(); + this.table.classList.add(ResizableConstants.classes.tableResizing); + this.wrapper.classList.add(ResizableConstants.classes.tableResizing); + dragHandler.classList.add(ResizableConstants.classes.columnResizing); + column.classList.add(ResizableConstants.classes.columnResizing); + this.lastPointerDown = millisecondsNow; + this.eventData = eventData; + var eventToDispatch = new CustomEvent(ResizableConstants.events.eventResizeStart, { + detail: { + column: column, + columnWidth: columnWidth, + table: this.table, + tableWidth: this.table.clientWidth + } + }); + this.table.dispatchEvent(eventToDispatch); + event.preventDefault(); + }; + ResizableTableColumns.prototype.handlePointerMove = function (event) { + if (!this.eventData || !event) + return; + var difference = (ResizableTableColumns.getPointerX(event) || 0) - (this.eventData.pointer.x || 0); + if (difference === 0) { + return; + } + var tableWidth = this.eventData.originalWidths.table + difference; + var columnWidth = this.constrainWidth(this.eventData.column, this.eventData.originalWidths.column + difference); + ResizableTableColumns.setWidth(this.table, tableWidth); + ResizableTableColumns.setWidth(this.eventData.column, columnWidth); + this.eventData.newWidths = { + column: columnWidth, + table: tableWidth + }; + var eventToDispatch = new CustomEvent(ResizableConstants.events.eventResize, { + detail: { + column: this.eventData.column, + columnWidth: columnWidth, + table: this.table, + tableWidth: tableWidth + } + }); + this.table.dispatchEvent(eventToDispatch); + }; + ResizableTableColumns.prototype.handlePointerUp = function () { + this.detachHandlers(); + if (!this.eventData) + return; + if (this.eventData.pointer.isDoubleClick) { + this.handleDoubleClick(); + } + this.table.classList.remove(ResizableConstants.classes.tableResizing); + this.wrapper.classList.remove(ResizableConstants.classes.tableResizing); + this.eventData.dragHandler.classList.remove(ResizableConstants.classes.columnResizing); + this.eventData.column.classList.remove(ResizableConstants.classes.columnResizing); + this.checkTableWidth(); + this.syncHandleWidths(); + this.refreshWrapperStyle(); + this.saveColumnWidths(); + var widths = this.eventData.newWidths || this.eventData.originalWidths; + var eventToDispatch = new CustomEvent(ResizableConstants.events.eventResizeStop, { + detail: { + column: this.eventData.column, + columnWidth: widths.column, + table: this.table, + tableWidth: widths.table + } + }); + this.table.dispatchEvent(eventToDispatch); + this.eventData = null; + }; + ResizableTableColumns.prototype.handleDoubleClick = function () { + if (!this.eventData || !this.eventData.column) + return; + var column = this.eventData.column; + var colIndex = this.tableHeaders.indexOf(column); + var maxWidth = 0; + var indicesToSkip = []; + this.tableHeaders + .forEach(function (el, idx) { + if (!el.hasAttribute(ResizableConstants.attributes.dataResizable)) { + indicesToSkip.push(idx); + } + }); + var span = this.ownerDocument.createElement('span'); + span.style.position = 'absolute'; + span.style.left = '-99999px'; + span.style.top = '-99999px'; + span.style.visibility = 'hidden'; + this.ownerDocument.body.appendChild(span); + var rows = this.table.querySelectorAll('tr'); + for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) { + var element = rows[rowIndex]; + var cells = element.querySelectorAll('td, th'); + var currentIndex = 0; + for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) { + var cell = cells[cellIndex]; + var colSpan = 1; + if (cell.hasAttribute('colspan')) { + var colSpanString = cell.getAttribute('colspan') || '1'; + var parsed = parseInt(colSpanString); + if (!isNaN(parsed)) { + colSpan = parsed; + } + else { + colSpan = 1; + } + } + if (indicesToSkip.indexOf(cellIndex) === -1 + && colSpan === 1 + && currentIndex === colIndex) { + maxWidth = Math.max(maxWidth, ResizableTableColumns.getTextWidth(cell, span)); + break; + } + currentIndex += colSpan; + } + } + this.ownerDocument.body.removeChild(span); + var difference = maxWidth - column.offsetWidth; + if (difference === 0) { + return; + } + var tableWidth = this.eventData.originalWidths.table + difference; + var columnWidth = this.constrainWidth(this.eventData.column, this.eventData.originalWidths.column + difference); + ResizableTableColumns.setWidth(this.table, tableWidth); + ResizableTableColumns.setWidth(this.eventData.column, columnWidth); + this.eventData.newWidths = { + column: columnWidth, + table: tableWidth, + }; + var eventToDispatch = new CustomEvent(ResizableConstants.events.eventResize, { + detail: { + column: this.eventData.column, + columnWidth: columnWidth, + table: this.table, + tableWidth: tableWidth + } + }); + this.table.dispatchEvent(eventToDispatch); + this.checkTableWidth(); + this.syncHandleWidths(); + this.saveColumnWidths(); + }; + ResizableTableColumns.prototype.attachHandlers = function () { + var _this = this; + ResizableConstants.events.pointerMove + .forEach(function (evt, evtIdx) { + _this.ownerDocument.addEventListener(evt, _this.onPointerMoveRef, false); + }); + ResizableConstants.events.pointerUp + .forEach(function (evt, evtIdx) { + _this.ownerDocument.addEventListener(evt, _this.onPointerUpRef, false); + }); + }; + ResizableTableColumns.prototype.detachHandlers = function () { + var _this = this; + ResizableConstants.events.pointerMove + .forEach(function (evt, evtIdx) { + _this.ownerDocument.removeEventListener(evt, _this.onPointerMoveRef, false); + }); + ResizableConstants.events.pointerUp + .forEach(function (evt, evtIdx) { + _this.ownerDocument.removeEventListener(evt, _this.onPointerUpRef, false); + }); + }; + ResizableTableColumns.prototype.refreshWrapperStyle = function () { + if (this.wrapper == null) + return; + var original = this.wrapper.style.overflowX; + this.wrapper.style.overflowX = 'hidden'; + this.wrapper.style.overflowX = original; + }; + ResizableTableColumns.prototype.saveColumnWidths = function () { + if (!this.options.store) + return; + var tableId = ResizableTableColumns.generateTableId(this.table); + if (tableId.length === 0) + return; + var data = { + table: this.table.offsetWidth, + columns: {} + }; + this.getResizableHeaders() + .forEach(function (el) { + data.columns[ResizableTableColumns.generateColumnId(el)] = el.offsetWidth; + }); + this.options.store.set(tableId, data); + }; + ResizableTableColumns.prototype.createHandlerReferences = function () { + var _this = this; + if (!this.onPointerDownRef) { + this.onPointerDownRef = ResizableTableColumns.debounce(function (evt) { + _this.handlePointerDown(evt); + }, 100, true); + } + if (!this.onPointerMoveRef) { + this.onPointerMoveRef = ResizableTableColumns.debounce(function (evt) { + _this.handlePointerMove(evt); + }, 5, false); + } + if (!this.onPointerUpRef) { + this.onPointerUpRef = ResizableTableColumns.debounce(function (evt) { + _this.handlePointerUp(); + }, 100, true); + } + }; + ResizableTableColumns.prototype.registerWindowResizeHandler = function () { + var win = this.ownerDocument.defaultView; + if (ResizableTableColumns.windowResizeHandlerRef) + return; + ResizableTableColumns.windowResizeHandlerRef = ResizableTableColumns.debounce(ResizableTableColumns.onWindowResize, 50, false); + ResizableConstants.events.windowResize + .forEach(function (evt, idx) { + win === null || win === void 0 ? void 0 : win.addEventListener(evt, ResizableTableColumns.windowResizeHandlerRef, false); + }); + }; + ResizableTableColumns.prototype.handleWindowResize = function () { + this.checkTableWidth(); + this.syncHandleWidths(); + this.saveColumnWidths(); + }; + ResizableTableColumns.prototype.updateWidth = function (cell, suggestedWidth, skipConstrainCheck, skipTableResize) { + var originalCellWidth = cell.offsetWidth; + var columnWidth = skipConstrainCheck + ? suggestedWidth + : this.constrainWidth(cell, suggestedWidth); + ResizableTableColumns.setWidth(cell, columnWidth); + if (!skipTableResize) { + var difference = columnWidth - originalCellWidth; + var tableWidth = this.table.offsetWidth + difference; + ResizableTableColumns.setWidth(this.table, tableWidth); + } + return columnWidth; + }; + ResizableTableColumns.onWindowResize = function (event) { + var win = event ? event.target : null; + if (win == null) + return; + var tables = win.document.querySelectorAll(".".concat(ResizableConstants.classes.table)); + for (var index = 0; index < tables.length; index++) { + var table = tables[index]; + if (typeof table[ResizableConstants.dataPropertyName] !== 'object') + continue; + table[ResizableConstants.dataPropertyName].handleWindowResize(); + } + }; + ResizableTableColumns.generateColumnId = function (el) { + var columnId = (el.getAttribute(ResizableConstants.attributes.dataResizable) || '') + .trim() + .replace(/\./g, '_'); + return columnId; + }; + ResizableTableColumns.generateTableId = function (table) { + var tableId = (table.getAttribute(ResizableConstants.attributes.dataResizableTable) || '') + .trim() + .replace(/\./g, '_'); + return tableId.length + ? "rtc/".concat(tableId) + : tableId; + }; + ResizableTableColumns.setWidth = function (element, width) { + var strWidth = width.toFixed(2); + strWidth = width > 0 ? strWidth : '0'; + element.style.width = "".concat(strWidth, "px"); + }; + ResizableTableColumns.getInstanceId = function () { + return ResizableTableColumns.instancesCount++; + }; + ResizableTableColumns.getPointerX = function (event) { + if (event.type.indexOf('touch') === 0) { + var tEvent = event; + if (tEvent.touches && tEvent.touches.length) { + return tEvent.touches[0].pageX; + } + if (tEvent.changedTouches && tEvent.changedTouches.length) { + return tEvent.changedTouches[0].pageX; + } + } + return event.pageX; + }; + ResizableTableColumns.getTextWidth = function (contentElement, measurementElement) { + var _a, _b; + if (!contentElement || !measurementElement) + return 0; + var text = ((_a = contentElement.textContent) === null || _a === void 0 ? void 0 : _a.trim().replace(/\s/g, ' ')) + ' '; //add extra space to ensure we are not add the `...` + var styles = (_b = contentElement.ownerDocument.defaultView) === null || _b === void 0 ? void 0 : _b.getComputedStyle(contentElement); + ['fontFamily', 'fontSize', 'fontWeight', 'padding', 'border', 'boxSizing'] + .forEach(function (prop) { + measurementElement.style[prop] = styles[prop]; + }); + measurementElement.innerHTML = text; + return measurementElement.offsetWidth; + }; + ResizableTableColumns.getOffset = function (el) { + if (!el) + return { top: 0, left: 0 }; + var rect = el.getBoundingClientRect(); + return { + top: rect.top + el.ownerDocument.body.scrollTop, + left: rect.left + el.ownerDocument.body.scrollLeft + }; + }; + ResizableTableColumns.instancesCount = 0; + ResizableTableColumns.windowResizeHandlerRef = null; + ResizableTableColumns.debounce = function (func, wait, immediate) { + var timeout = null; + var debounced = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var later = function () { + timeout = null; + if (!immediate) { + func.apply(void 0, args); + } + }; + var callNow = immediate && !timeout; + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(later, wait); + if (callNow) { + func.apply(void 0, args); + } + }; + return debounced; + }; + return ResizableTableColumns; + }()); + + exports.PointerData = PointerData; + exports.ResizableConstants = ResizableConstants; + exports.ResizableEventData = ResizableEventData; + exports.ResizableOptions = ResizableOptions; + exports.ResizableTableColumns = ResizableTableColumns; + exports.Utilities = Utilities; + exports.UtilitiesDOM = UtilitiesDOM; + exports.WidthsData = WidthsData; + +})); +(function (window, ResizableTableColumns, undefined) { + var store = window.store && window.store.enabled ? window.store : null; + var els = document.querySelectorAll('table.data'); + for (var index = 0; index < els.length; index++) { + var table = els[index]; + if (table['rtc_data_object']) { + continue; + } + var options = { + store: store + }; + if (table.querySelectorAll('thead > tr').length > 1) { + options.resizeFromBody = false; + } + new ResizableTableColumns(els[index], options); + } +})(window, window.validide_resizableTableColumns.ResizableTableColumns, void (0)); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/public/js/admin/server/ipAutoComplete.js b/public/js/admin/server/ipAutoComplete.js new file mode 100644 index 0000000..6389d17 --- /dev/null +++ b/public/js/admin/server/ipAutoComplete.js @@ -0,0 +1,172 @@ + +// 기본 실행 (페이지 로드시 전체 document) +class IpAutoComplete { + /** + * @param {HTMLElement|string} input + * @param {HTMLElement|string} panel + * @param {string[]} ipArray + * @param {object} opts + */ + constructor(input, panel, ipArray = [], opts = {}) { + this.inputEl = typeof input === 'string' ? document.querySelector(input) : input; + this.panelEl = typeof panel === 'string' ? document.querySelector(panel) : panel; + if (!this.inputEl || !this.panelEl) throw new Error('Invalid input/panel element'); + + this.opts = Object.assign({ + validateIPv4: true, + enforceListOnly: false, + previewOnFocus: true, // 포커스 시 상위 N개 미리보기 + previewCount: 30, // 미리보기 개수 + resultLimit: 1000 // 너무 많을 때 렌더 최대치(성능 안전장치) + }, opts); + + this.errorEl = document.getElementById('ipError'); + this.ipv4Re = /^(25[0-5]|2[0-4]\d|[01]?\d\d?)(\.(25[0-5]|2[0-4]\d|[01]?\d\d?)){3}$/; + this.setArray(ipArray); + + // 상태 + this.activeIndex = -1; + + // 이벤트 + this.onInput = this._handleInput.bind(this); + this.onChange = this._handleChange.bind(this); + this.onFocus = this._handleFocus.bind(this); + this.onKeydown = this._handleKeydown.bind(this); + this.onClickOutside = this._handleClickOutside.bind(this); + + this.inputEl.addEventListener('input', this.onInput); + this.inputEl.addEventListener('change', this.onChange); + this.inputEl.addEventListener('focus', this.onFocus); + this.inputEl.addEventListener('keydown', this.onKeydown); + document.addEventListener('mousedown', this.onClickOutside); + } + + destroy(){ + this.inputEl.removeEventListener('input', this.onInput); + this.inputEl.removeEventListener('change', this.onChange); + this.inputEl.removeEventListener('focus', this.onFocus); + this.inputEl.removeEventListener('keydown', this.onKeydown); + document.removeEventListener('mousedown', this.onClickOutside); + this.hidePanel(); + } + + setArray(arr){ this.ipArray = Array.from(new Set((arr||[]).filter(Boolean))); } + + _handleInput(e){ + const q = (e.target.value||'').trim(); + this._filterAndRender(q); + this._checkIPv4(q); + } + + _handleChange(){ + const v = (this.inputEl.value||'').trim(); + this._checkIPv4(v); + if (this.opts.enforceListOnly && v && !this.ipArray.includes(v)) { + this._setError('❌ 목록에 없는 IP입니다.'); + } + } + + _handleFocus(){ + if (!this.inputEl.value && this.opts.previewOnFocus) { + this._renderPanel(this.ipArray.slice(0, this.opts.resultLimit)); + this.showPanel(); + } + } + + _handleKeydown(e){ + if (this.panelEl.hidden) return; + const items = this._items(); + if (!items.length) return; + + if (e.key === 'ArrowDown'){ e.preventDefault(); this._move(1); } + else if (e.key === 'ArrowUp'){ e.preventDefault(); this._move(-1); } + else if (e.key === 'Enter'){ + if (this.activeIndex >= 0){ e.preventDefault(); this._pick(items[this.activeIndex].dataset.value); } + } else if (e.key === 'Escape'){ this.hidePanel(); } + } + + _handleClickOutside(e){ + if (!this.inputEl.closest('.ac-wrap')?.contains(e.target)) this.hidePanel(); + } + + _filterAndRender(query){ + if (!query){ + this.hidePanel(); + return; + } + const q = query.toLowerCase(); + let results = this.ipArray.filter(ip => ip.toLowerCase().includes(q)); + if (this.opts.resultLimit > 0) results = results.slice(0, this.opts.resultLimit); + if (results.length){ this._renderPanel(results); this.showPanel(); } + else { this.hidePanel(); } + } + + _renderPanel(list){ + this.panelEl.innerHTML = ''; + this.activeIndex = -1; + + const frag = document.createDocumentFragment(); + list.forEach((ip) => { + const item = document.createElement('div'); + item.className = 'ac-item'; + item.textContent = ip; + item.dataset.value = ip; + item.addEventListener('mousedown', (ev) => { ev.preventDefault(); this._pick(ip); }); + frag.appendChild(item); + }); + this.panelEl.appendChild(frag); + } + + _items(){ return Array.from(this.panelEl.querySelectorAll('.ac-item')); } + + _move(delta){ + const items = this._items(); + if (!items.length) return; + this.activeIndex = (this.activeIndex + delta + items.length) % items.length; + items.forEach(el => el.classList.remove('active')); + const el = items[this.activeIndex]; + el.classList.add('active'); + el.scrollIntoView({ block: 'nearest' }); + } + + _pick(value){ + this.inputEl.value = value; + this.hidePanel(); + this._checkIPv4(value); + } + + showPanel(){ this.panelEl.hidden = false; } + hidePanel(){ this.panelEl.hidden = true; this.activeIndex = -1; } + + _checkIPv4(val){ + if (!this.opts.validateIPv4 || !val){ this._setError(''); return; } + this._setError(this.ipv4Re.test(val) ? '' : '❌ IPv4 형식이 아닙니다.'); + } + + _setError(text){ if (this.errorEl) this.errorEl.textContent = text || ''; } +} + +/* ipData(JSON) 읽기: (이미 페이지에 있다면) */ +function parseEmbeddedJson(id){ + const el=document.getElementById(id); if(!el) return []; + let txt=el.textContent||''; + txt = txt + .replace(/\/\*[\s\S]*?\*\//g,'') + .replace(/(^|[^:])\/\/.*$/mg,'$1') + .replace(/,\s*([}\]])/g,'$1'); + try { const data=JSON.parse(txt); return Array.isArray(data)?data:[]; } + catch(e){ console.error('ipData parse error',e); return []; } +} + +/* 초기화 */ +document.addEventListener('DOMContentLoaded', () => { + // ipData