From 08c76bc6ddb11897cbdc8783ec122eea82bfe0e3 Mon Sep 17 00:00:00 2001 From: seo Date: Sun, 7 Jun 2026 00:33:58 +0900 Subject: [PATCH] Initial seoul project import --- .gitignore | 12 + README.md | 33 +++ assets/apple-touch-icon.png | Bin 0 -> 3423 bytes assets/favicon.svg | 4 + assets/icon-192.png | Bin 0 -> 3641 bytes assets/icon-32.png | Bin 0 -> 695 bytes assets/icon-512.png | Bin 0 -> 10530 bytes index.php | 472 ++++++++++++++++++++++++++++++++++++ manifest.webmanifest | 25 ++ sw.js | 50 ++++ 10 files changed, 596 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/apple-touch-icon.png create mode 100644 assets/favicon.svg create mode 100644 assets/icon-192.png create mode 100644 assets/icon-32.png create mode 100644 assets/icon-512.png create mode 100644 index.php create mode 100644 manifest.webmanifest create mode 100644 sw.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9134392 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +car/ +.agents/ +.codex/ +.env +*.log +*.db +*.sqlite +*.sql +cache/ +tmp/ +secrets/ +secret/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3b4ee95 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Seoul + +PHP dashboard that builds a service shortcut list from nginx configuration and local PHP entry points. + +## Main Features + +- Reads nginx configuration paths and extracts HTTPS server blocks. +- Finds shallow PHP entries under document roots. +- Filters self links, internal names, IP literals, variables, and regex-like host values. +- Provides PWA metadata and service worker assets. + +## Main Entry Points + +- `index.php` +- `manifest.webmanifest` +- `sw.js` + +## Structure + +- `index.php`: dashboard renderer. +- `manifest.webmanifest`: PWA manifest. +- `sw.js`: service worker. +- `assets/`: icons and static assets. + +## Notes + +The `car/` directory is maintained as a separate repository and is intentionally ignored here. + +## Security + +- nginx configuration is read for display only. +- Generated host and link values are escaped before rendering. + diff --git a/assets/apple-touch-icon.png b/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a8384f59c0a304ffa5b41da21db97384983473f0 GIT binary patch literal 3423 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 literal 0 HcmV?d00001 diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 0000000..8d8b731 --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icon-192.png b/assets/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..dc947d18073aae4b220220be9a163993b6e8de21 GIT binary patch literal 3641 zcmb_f={wX77yb=n8T&3v))AqyD`GH`B}udy

FE`5;bySd+ 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 literal 0 HcmV?d00001 diff --git a/assets/icon-32.png b/assets/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7a4bc49dc63f1d495aae253e07898d7bd7afa7 GIT binary patch literal 695 zcmV;o0!aOdP)4LTo&^rXfW8nV@^R$qg z4Hh$OTLnkUVTCz{p|^eQ^$yS)jrSJJ-U7Whw)NAZqS{ zXEgSK#rfcNh$#V$g+3|pZ0*$1!I%{j^9xN&m7{VR0UwBdOgtn1v>i$(17pn znA3aU^5G-_%v47ooM~1XR<8rkHPCAQ!!)e0!t**Y3qqQh60JO^hiEDQciO?_g->4i zJPaL8P?#6}uqC-*(t~#_y!Tt4BY-XwZoY=0@8P|(_u;|G=vUe^piHijU};_g9=Op4 z^^cT$rG;>OJJ^cAs0TSHAe}C_-7c;@q-q;HJe2@|EW?58;wNObTC@P(H9}E-0@F~E z;BhU~?h@lQUX@wk&H32LXVr$DQE2S~r(2lv>jeDxCA?zLfyE@sQf(76Le-}@{ac;} dNCPBOe*nXz-+@j&8AJd8002ovPDHLkV1gYSG&=wQ literal 0 HcmV?d00001 diff --git a/assets/icon-512.png b/assets/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..be4ac923657a52854739f41a771b7fbe5add13dd GIT binary patch literal 10530 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 KUM<(M3;$n7@qmv2 literal 0 HcmV?d00001 diff --git a/index.php b/index.php new file mode 100644 index 0000000..43644aa --- /dev/null +++ b/index.php @@ -0,0 +1,472 @@ + true, + 'localhost' => true, +]; + +$denyRootHosts = [ + 'seo.chaegeon.com' => true, +]; + +function collectFiles(array $paths): array +{ + $files = []; + + foreach ($paths as $path) { + if (is_file($path)) { + $files[] = $path; + continue; + } + + if (!is_dir($path)) { + continue; + } + + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS) + ); + + foreach ($it as $file) { + if (!$file->isFile()) { + continue; + } + + $name = $file->getFilename(); + + if ( + str_ends_with($name, '.conf') || + str_contains($name, 'ReverseProxy') || + str_contains($name, 'nginx') || + str_contains($file->getPathname(), 'sites-enabled') + ) { + $files[] = $file->getPathname(); + } + } + } + + return array_values(array_unique($files)); +} + +function extractServerBlocks(string $text): array +{ + $blocks = []; + $len = strlen($text); + $pos = 0; + + while (($serverPos = strpos($text, 'server', $pos)) !== false) { + $before = $serverPos > 0 ? $text[$serverPos - 1] : ' '; + $after = $text[$serverPos + 6] ?? ' '; + + if (preg_match('/[a-zA-Z0-9_\-]/', $before) || preg_match('/[a-zA-Z0-9_\-]/', $after)) { + $pos = $serverPos + 6; + continue; + } + + $brace = strpos($text, '{', $serverPos); + if ($brace === false) { + break; + } + + $depth = 0; + for ($i = $brace; $i < $len; $i++) { + if ($text[$i] === '{') { + $depth++; + } elseif ($text[$i] === '}') { + $depth--; + if ($depth === 0) { + $blocks[] = substr($text, $serverPos, $i - $serverPos + 1); + $pos = $i + 1; + break; + } + } + } + + if ($pos <= $serverPos) { + break; + } + } + + return $blocks; +} + +function cleanNginxText(string $text): string +{ + $text = preg_replace('/#.*$/m', '', $text); + return $text ?? ''; +} + +function extractServerNames(string $block): array +{ + preg_match_all('/server_name\s+([^;]+);/i', $block, $matches); + + $names = []; + + foreach ($matches[1] ?? [] as $line) { + foreach (preg_split('/\s+/', trim($line)) as $host) { + $host = trim($host); + + if ($host === '' || $host === '_' || str_contains($host, '$')) { + continue; + } + + if (str_starts_with($host, '~')) { + continue; + } + + $host = trim($host, '.'); + + if (filter_var($host, FILTER_VALIDATE_IP)) { + continue; + } + + if (preg_match('/^[a-zA-Z0-9.-]+$/', $host)) { + $names[] = strtolower($host); + } + } + } + + return array_values(array_unique($names)); +} + +function detectScheme(string $block): string +{ + if (preg_match('/listen\s+[^;]*443[^;]*ssl/i', $block)) { + return 'https'; + } + + if (preg_match('/ssl_certificate\s+/i', $block)) { + return 'https'; + } + + return 'http'; +} + +function isBlockedServer(string $block): bool +{ + if (preg_match('/return\s+444\s*;/i', $block)) { + return true; + } + + if (preg_match('/deny\s+all\s*;/i', $block) && !preg_match('/proxy_pass|fastcgi_pass|root\s+/i', $block)) { + return true; + } + + return false; +} + +function extractLocations(string $block): array +{ + return ['/']; +} + +function extractDocumentRoot(string $block): ?string +{ + if (!preg_match('/^\s*root\s+([^;]+);/mi', $block, $matches)) { + return null; + } + + $root = trim($matches[1], " \t\n\r\0\x0B\"'"); + + if ($root === '' || str_contains($root, '$') || !is_dir($root)) { + return null; + } + + $root = rtrim($root, '/'); + + if ($root === '') { + return null; + } + + return $root; +} + +function discoverPhpEntryPaths(?string $root): array +{ + if ($root === null) { + return []; + } + + $paths = []; + $allowedFiles = [ + 'index.php' => true, + 'monitor.php' => true, + ]; + + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS) + ); + + foreach ($it as $file) { + if (!$file->isFile()) { + continue; + } + + $filename = $file->getFilename(); + if (!isset($allowedFiles[$filename])) { + continue; + } + + $relative = substr($file->getPathname(), strlen($root)); + if ($relative === false || $relative === '') { + continue; + } + + $relative = str_replace(DIRECTORY_SEPARATOR, '/', $relative); + if ($relative === '/index.php') { + continue; + } + + $depth = substr_count(trim($relative, '/'), '/'); + + if ($depth > 1) { + continue; + } + + $paths[] = $relative; + } + + sort($paths, SORT_NATURAL); + + return array_values(array_unique($paths)); +} + +$items = []; +$files = collectFiles($nginxFiles); + +foreach ($files as $file) { + $raw = @file_get_contents($file); + if ($raw === false) { + continue; + } + + $text = cleanNginxText($raw); + $blocks = extractServerBlocks($text); + + foreach ($blocks as $block) { + if (isBlockedServer($block)) { + continue; + } + + $hosts = extractServerNames($block); + if (!$hosts) { + continue; + } + + $scheme = detectScheme($block); + + if ($scheme !== 'https') { + continue; + } + + $paths = array_values(array_unique(array_merge( + extractLocations($block), + discoverPhpEntryPaths(extractDocumentRoot($block)) + ))); + + foreach ($hosts as $host) { + if (str_contains($host, 'webdav')) { + continue; + } + + if (isset($denyHosts[$host])) { + continue; + } + + foreach ($paths as $path) { + if ($path === '/' && isset($denyRootHosts[$host])) { + continue; + } + + $url = $scheme . '://' . $host . $path; + + $items[$url] = [ + 'url' => $url, + 'host' => $host, + 'scheme' => $scheme, + ]; + } + } + } +} + +ksort($items, SORT_NATURAL); +?> + + + + + Nginx 바로가기 + + + + + + + + + + + + + + + + + + +

+
+ +
표시할 URL이 없습니다.
+ + +
+
+ + + +
+ + +
+
+ + + diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000..49b9d03 --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,25 @@ +{ + "name": "Seoul", + "short_name": "Seoul", + "description": "Seoul server shortcuts", + "start_url": ".", + "scope": ".", + "display": "standalone", + "orientation": "any", + "background_color": "#ffffff", + "theme_color": "#ffffff", + "icons": [ + { + "src": "assets/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "assets/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..e75ae8d --- /dev/null +++ b/sw.js @@ -0,0 +1,50 @@ +const CACHE_NAME = 'seoul-shortcuts-v1'; +const CORE_ASSETS = [ + './', + './assets/favicon.svg', + './assets/icon-32.png', + './assets/icon-192.png', + './assets/icon-512.png', + './assets/apple-touch-icon.png', + './manifest.webmanifest' +]; + +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => cache.addAll(CORE_ASSETS)) + .catch(() => undefined) + ); + self.skipWaiting(); +}); + +self.addEventListener('activate', event => { + event.waitUntil( + caches.keys().then(keys => Promise.all( + keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key)) + )) + ); + self.clients.claim(); +}); + +self.addEventListener('fetch', event => { + if (event.request.method !== 'GET') { + return; + } + + const url = new URL(event.request.url); + + if (url.origin !== location.origin) { + return; + } + + event.respondWith( + fetch(event.request) + .then(response => { + const copy = response.clone(); + caches.open(CACHE_NAME).then(cache => cache.put(event.request, copy)); + return response; + }) + .catch(() => caches.match(event.request)) + ); +});