commit db313f5eef44694fac49c6c9f2405543b7ffed07 Author: Ming Tsay Date: Mon Jul 11 18:09:15 2022 +0800 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..406c0af --- /dev/null +++ b/.env.example @@ -0,0 +1,14 @@ +# Discord OAuth Token +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= +DISCORD_OAUTH_EXPIRES=86400 + +# Minecraft Query +MINECRAFT_QUERY_HOST=127.0.0.1 +MINECRAFT_QUERY_PORT=25565 + +# Minecraft Remote Control +MINECRAFT_RCON_HOST=127.0.0.1 +MINECRAFT_RCON_PORT=25575 +MINECRAFT_RCON_PASSWORD= +MINECRAFT_RCON_TIMEOUT=3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a504811 --- /dev/null +++ b/.gitignore @@ -0,0 +1,203 @@ +.env +sodium_key + +# Created by https://www.toptal.com/developers/gitignore/api/phpstorm,windows,linux,macos,composer +# Edit at https://www.toptal.com/developers/gitignore?templates=phpstorm,windows,linux,macos,composer + +### Composer ### +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### PhpStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PhpStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/phpstorm,windows,linux,macos,composer + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/akanyan.iml b/.idea/akanyan.iml new file mode 100644 index 0000000..e064976 --- /dev/null +++ b/.idea/akanyan.iml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..d323fc8 --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..33fe132 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..eaaba6f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..40e05a1 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/webResources.xml b/.idea/webResources.xml new file mode 100644 index 0000000..c1d91d2 --- /dev/null +++ b/.idea/webResources.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/App.php b/app/App.php new file mode 100644 index 0000000..0e3fb49 --- /dev/null +++ b/app/App.php @@ -0,0 +1,198 @@ +addData([ + 'me' => $me, + 'whoami' => "$me->username#$me->userDiscriminator", + 'noStatus' => false, + 'isLogin' => $isLogin, + 'isAllowed' => $isLogin && $me !== null && self::isAllowed($me->userId), + ]); + return $templates; + } + + public static function getU(): ?Token + { + return Auth::decrypt($_COOKIE['u'] ?? ''); + } + + public static function checkU(): bool + { + $token = self::getU(); + if ($token === null) return false; + if ($token->timestamp + $token->expires_in < time()) return false; + return self::me() !== null; + } + + public static function setU(Token $token): void + { + try { + setcookie('u', Auth::encrypt($token), [ + 'expires' => $token->timestamp + $token->expires_in, + 'path' => '/', + 'domain' => 'akanyan.oho.tw', + 'samesite' => 'None', + 'secure' => true, + 'httponly' => true, + ]); + } catch (Exception) { + error_log('Failed to setU.'); + } + } + + public static function unsetU(): void + { + try { + setcookie('u', null, [ + 'expires' => time() - 3600, + 'path' => '/', + 'domain' => 'akanyan.oho.tw', + 'samesite' => 'None', + 'secure' => true, + 'httponly' => true, + ]); + } catch (Exception) { + error_log('Failed to unsetU.'); + } + } + + public static function requireAuth(): void + { + if (!self::checkU()) { + header('location: /login.php'); + http_response_code(302); + exit; + } + } + + public static function requireNonAuth(): void + { + if (self::checkU()) { + header('location: /'); + http_response_code(302); + exit; + } + } + + public static function requireAllowed(): void + { + self::requireAuth(); + $me = self::me(); + if ($me === null || !self::isAllowed($me->userId)) { + self::template([ + 'title' => '您無權限使用本系統', + 'body' => << +

您的 Discord 帳號不在白名單中。

+

若您認為這是個錯誤,請聯絡 小喵#3521 並提供您的使用者編號 $me->userId

+ +HTML, + ]); + } + } + + public static function auth(string $code): void + { + try { + $token = Auth::getTokenByCode($code); + if ($token !== null) self::setU($token); + } catch (Exception) { + error_log('Failed to getTokenByCode.'); + } + } + + public static function me(): ?Me + { + try { + $u = self::getU(); + if ($u === null) return null; + return Auth::getMe($u); + } catch (Exception) { + error_log('Failed to getMe.'); + return null; + } + } + + #[NoReturn] + public static function template(array $params): void + { + $status = ''; + if (self::checkU() && !$params['no_status']) { + $me = self::me(); + $status = <<您已使用 $me->username#$me->userDiscriminator 登入,點選此處以登出系統。 +HTML; + } + + $html = << + + + + + + $params[title] + + + + +

$params[title]

+ $status +
$params[body]
+ + + + +HTML; + + header('Content-Type: text/html; charset=utf-8'); + header('Content-Length: ' . strlen($html)); + http_response_code(200); + echo($html); + exit; + } + + public static function authUrl(): string + { + return Auth::authorize(); + } + + public static function isAllowed(string $userId): bool + { + return in_array($userId, self::allowedUsers); + } + + #[NoReturn] + public static function render(string $name, array $data = []): void + { + $html = self::getTemplate()->render($name, $data); + + header('Content-Type: text/html; charset=utf-8'); + header('Content-Length: ' . strlen($html)); + http_response_code(200); + echo($html); + exit; + } +} diff --git a/app/Auth.php b/app/Auth.php new file mode 100644 index 0000000..e982b89 --- /dev/null +++ b/app/Auth.php @@ -0,0 +1,80 @@ + $_ENV['DISCORD_CLIENT_ID'], + 'redirect_uri' => self::DISCORD_REDIRECT_URI, + 'response_type' => 'code', + 'scope' => 'identify', + ]); + return "https://discord.com/api/oauth2/authorize?$query_string"; + } + + public static function getTokenByCode(string $code): ?Token + { + $payload = json_decode(file_get_contents('https://discord.com/api/oauth2/token', false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => 'Content-Type: application/x-www-form-urlencoded', + 'content' => http_build_query([ + 'client_id' => $_ENV['DISCORD_CLIENT_ID'], + 'client_secret' => $_ENV['DISCORD_CLIENT_SECRET'], + 'grant_type' => 'authorization_code', + 'code' => $code, + 'redirect_uri' => self::DISCORD_REDIRECT_URI, + ]), + ], + ]))); + if ($payload === null) return null; + return new Token($payload); + } + + public static function getMe(Token $getU): ?Me + { + return new Me(json_decode(file_get_contents('https://discord.com/api/oauth2/@me', false, stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'header' => "Authorization: Bearer $getU->access_token", + ], + ])))); + } +} diff --git a/app/ConfigFilesViewer.php b/app/ConfigFilesViewer.php new file mode 100644 index 0000000..d834778 --- /dev/null +++ b/app/ConfigFilesViewer.php @@ -0,0 +1,32 @@ +appId = $payload->application->id; + $this->appName = $payload->application->name; + $this->appIcon = $payload->application->icon; + $this->appDescription = $payload->application->description; + $this->appHook = $payload->application->hook; + $this->appVerifyKey = $payload->application->verify_key; + $this->scopes = $payload->scopes; + $this->expires = strtotime($payload->expires); + $this->userId = $payload->user->id; + $this->username = $payload->user->username; + $this->userAvatar = $payload->user->avatar; + $this->userDiscriminator = $payload->user->discriminator; + $this->userPublicFlags = $payload->user->public_flags; + } +} diff --git a/app/Discord/Token.php b/app/Discord/Token.php new file mode 100644 index 0000000..9fede32 --- /dev/null +++ b/app/Discord/Token.php @@ -0,0 +1,27 @@ +access_token = $payload->access_token; + $this->token_type = $payload->token_type; + $this->expires_in = $payload->expires_in; + $this->refresh_token = $payload->refresh_token; + $this->scope = $payload->scope; + + $this->timestamp = time(); + } +} diff --git a/app/FileViewer.php b/app/FileViewer.php new file mode 100644 index 0000000..5a72d3c --- /dev/null +++ b/app/FileViewer.php @@ -0,0 +1,33 @@ + ($whitelist === null || in_array($file, $whitelist)) && is_file("$directory/$file") + )); + } + + public static function read($file): ?string + { + $directory = static::directory(); + $filename = "$directory/$file"; + + if (!in_array($file, self::list()) || !file_exists($filename) || !is_file($filename)) return null; + + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $content = file_get_contents($filename); + return $extension === 'gz' ? gzdecode($content) : $content; + } +} diff --git a/app/LogsViewer.php b/app/LogsViewer.php new file mode 100644 index 0000000..5835706 --- /dev/null +++ b/app/LogsViewer.php @@ -0,0 +1,17 @@ +Query(); + } catch (MinecraftPingException) { + $result = null; + } finally { + $query->Close(); + } + return $result ?: null; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..489df28 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "mingtsay/akanyan", + "description": "description", + "minimum-stability": "stable", + "license": "proprietary", + "authors": [ + { + "name": "Ming Tsay", + "email": "mt@mingtsay.tw" + } + ], + "require": { + "ext-iconv": "*", + "ext-sodium": "*", + "ext-zlib": "*", + "components/font-awesome": "^6.1", + "league/plates": "^3.4", + "twbs/bootstrap": "^5.1", + "vlucas/phpdotenv": "^5.4", + "xpaw/php-minecraft-query": "^4.0", + "xpaw/php-source-query-class": "^2.1" + }, + "autoload": { + "psr-4": { + "MingTsay\\Akanyan\\": "app/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..167ae09 --- /dev/null +++ b/composer.lock @@ -0,0 +1,733 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "80d3ab8288e7bcecfa9435acfbd3bcc5", + "packages": [ + { + "name": "components/font-awesome", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/components/font-awesome.git", + "reference": "9c95a6a9572933bc964f05b810f0808a5de00ee1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/components/font-awesome/zipball/9c95a6a9572933bc964f05b810f0808a5de00ee1", + "reference": "9c95a6a9572933bc964f05b810f0808a5de00ee1", + "shasum": "" + }, + "type": "component", + "extra": { + "component": { + "styles": [ + "css/all.css" + ], + "files": [ + "css/all.min.css", + "webfonts/*" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "CC-BY-4.0", + "MIT", + "OFL-1.1" + ], + "description": "Font Awesome, the iconic SVG, font, and CSS framework.", + "support": { + "issues": "https://github.com/components/font-awesome/issues", + "source": "https://github.com/components/font-awesome/tree/6.1.1" + }, + "time": "2022-04-18T13:15:48+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "league/plates", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/plates.git", + "reference": "6d3ee31199b536a4e003b34a356ca20f6f75496a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/plates/zipball/6d3ee31199b536a4e003b34a356ca20f6f75496a", + "reference": "6d3ee31199b536a4e003b34a356ca20f6f75496a", + "shasum": "" + }, + "require": { + "php": "^7.0|^8.0" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Plates\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "role": "Developer" + }, + { + "name": "RJ Garcia", + "email": "ragboyjr@icloud.com", + "role": "Developer" + } + ], + "description": "Plates, the native PHP template system that's fast, easy to use and easy to extend.", + "homepage": "https://platesphp.com", + "keywords": [ + "league", + "package", + "templates", + "templating", + "views" + ], + "support": { + "issues": "https://github.com/thephpleague/plates/issues", + "source": "https://github.com/thephpleague/plates/tree/v3.4.0" + }, + "time": "2020-12-25T05:00:37+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "twbs/bootstrap", + "version": "v5.1.3", + "source": { + "type": "git", + "url": "https://github.com/twbs/bootstrap.git", + "reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twbs/bootstrap/zipball/1a6fdfae6be09b09eaced8f0e442ca6f7680a61e", + "reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e", + "shasum": "" + }, + "replace": { + "twitter/bootstrap": "self.version" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Otto", + "email": "markdotto@gmail.com" + }, + { + "name": "Jacob Thornton", + "email": "jacobthornton@gmail.com" + } + ], + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "homepage": "https://getbootstrap.com/", + "keywords": [ + "JS", + "css", + "framework", + "front-end", + "mobile-first", + "responsive", + "sass", + "web" + ], + "support": { + "issues": "https://github.com/twbs/bootstrap/issues", + "source": "https://github.com/twbs/bootstrap/tree/v5.1.3" + }, + "time": "2021-10-09T06:43:19+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + }, + { + "name": "xpaw/php-minecraft-query", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/xPaw/PHP-Minecraft-Query.git", + "reference": "c7257e7c3d18c14cd476910f1516f93f405f54ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xPaw/PHP-Minecraft-Query/zipball/c7257e7c3d18c14cd476910f1516f93f405f54ee", + "reference": "c7257e7c3d18c14cd476910f1516f93f405f54ee", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "xPaw\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP library to query Minecraft servers", + "keywords": [ + "minecraft" + ], + "support": { + "source": "https://github.com/xPaw/PHP-Minecraft-Query/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/xPaw", + "type": "github" + } + ], + "time": "2021-06-18T13:18:40+00:00" + }, + { + "name": "xpaw/php-source-query-class", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/xPaw/PHP-Source-Query.git", + "reference": "673e572233c3ab4b70b302d648ebaf5a8d9ba32b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xPaw/PHP-Source-Query/zipball/673e572233c3ab4b70b302d648ebaf5a8d9ba32b", + "reference": "673e572233c3ab4b70b302d648ebaf5a8d9ba32b", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "9.2", + "vimeo/psalm": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "xPaw\\SourceQuery\\": "SourceQuery/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "description": "PHP library to query and send RCON commands to servers based on \"Source Engine Query\" protocol", + "homepage": "https://github.com/xPaw/PHP-Source-Query", + "keywords": [ + "ark", + "counter-strike", + "csgo", + "gmod", + "minecraft", + "rcon", + "rust", + "starbound", + "team fortress" + ], + "support": { + "source": "https://github.com/xPaw/PHP-Source-Query/tree/2.1.0" + }, + "funding": [ + { + "url": "https://github.com/xPaw", + "type": "github" + } + ], + "time": "2020-12-04T08:20:42+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "ext-sodium": "*", + "ext-zlib": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/env.php b/env.php new file mode 100644 index 0000000..b567cb2 --- /dev/null +++ b/env.php @@ -0,0 +1,7 @@ +load(); diff --git a/templates/configurations/list.php b/templates/configurations/list.php new file mode 100644 index 0000000..5b77b79 --- /dev/null +++ b/templates/configurations/list.php @@ -0,0 +1,6 @@ +layout('partials/file/list', [ + 'title' => '選擇設定檔', + 'nav' => 'configurations', + 'list' => $list ?? [], +]); diff --git a/templates/configurations/not-found.php b/templates/configurations/not-found.php new file mode 100644 index 0000000..1777fb1 --- /dev/null +++ b/templates/configurations/not-found.php @@ -0,0 +1,6 @@ +layout('partials/file/not-found', [ + 'title' => '檢視設定檔', + 'nav' => 'configurations', + 'list' => $list ?? [], +]); diff --git a/templates/configurations/view.php b/templates/configurations/view.php new file mode 100644 index 0000000..9a7b30a --- /dev/null +++ b/templates/configurations/view.php @@ -0,0 +1,9 @@ +layout('partials/file/view', [ + 'title' => '檢視設定檔', + 'nav' => 'configurations', + 'list' => $list ?? [], + 'file' => $file ?? '', + 'type' => $type ?? 'plaintext', + 'content' => $content ?? '', +]); diff --git a/templates/index.php b/templates/index.php new file mode 100644 index 0000000..ce92289 --- /dev/null +++ b/templates/index.php @@ -0,0 +1,68 @@ +layout('template', ['title' => '首頁']) ?> + +
+
+
+
伺服器狀態:e($status ?? '') ?>
+ + +
伺服器版本:e($query['version']['name']) ?>
+ + + +
+ 線上人數: + e($query['players']['online']) ?> + / + e($query['players']['max']) ?> +
+ + 0): ?> +
    + +
  • e($player['name']) ?>
  • + +
+ + + +
+ + + + +
+ +
+ + +
+
+ +
+
+

【更新紀錄】

+
    +
  • +

    首頁現在可以看到伺服器版本與線上人數等資訊了。

    +

    更新日期:2022/07/05 02:02:02

    +
  • +
  • +

    首頁現在可以看到伺服器狀態了,也可以從首頁直接啟動伺服器。

    +

    更新日期:2022/07/04 16:52:30

    +
  • +
  • +

    查看檔案功能已移至新的網址,請透過上方選單進入。

    +

    更多其他功能將陸續推出,敬請期待。

    +

    更新日期:2022/07/04 13:44:30

    +
  • +
+
+
diff --git a/templates/login.php b/templates/login.php new file mode 100644 index 0000000..dc5e25c --- /dev/null +++ b/templates/login.php @@ -0,0 +1,8 @@ +layout('template', ['title' => '登入系統', 'noStatus' => true]) ?> + +

您尚未登入,請點選下方按鈕進行登入。

+ + 登入連結載入失敗,按此重新載入 + + 透過 Discord 帳號登入 + diff --git a/templates/logout/form.php b/templates/logout/form.php new file mode 100644 index 0000000..1f04bd8 --- /dev/null +++ b/templates/logout/form.php @@ -0,0 +1,6 @@ +layout('template', ['title' => '登出確認', 'noStatus' => true]) ?> + +

您確定要登出嗎?

+
+ +
diff --git a/templates/logout/success.php b/templates/logout/success.php new file mode 100644 index 0000000..1382bd8 --- /dev/null +++ b/templates/logout/success.php @@ -0,0 +1,3 @@ +layout('template', ['title' => '登出成功', 'noStatus' => true]) ?> + +您已成功登出,按此重新登入。 diff --git a/templates/logs/list.php b/templates/logs/list.php new file mode 100644 index 0000000..2c61db6 --- /dev/null +++ b/templates/logs/list.php @@ -0,0 +1,6 @@ +layout('partials/file/list', [ + 'title' => '選擇日誌檔案', + 'nav' => 'logs', + 'list' => $list ?? [], +]); diff --git a/templates/logs/not-found.php b/templates/logs/not-found.php new file mode 100644 index 0000000..0cd7121 --- /dev/null +++ b/templates/logs/not-found.php @@ -0,0 +1,6 @@ +layout('partials/file/not-found', [ + 'title' => '檢視日誌', + 'nav' => 'logs', + 'list' => $list ?? [], +]); diff --git a/templates/logs/view.php b/templates/logs/view.php new file mode 100644 index 0000000..a8e16f4 --- /dev/null +++ b/templates/logs/view.php @@ -0,0 +1,9 @@ +layout('partials/file/view', [ + 'title' => '檢視日誌', + 'nav' => 'logs', + 'list' => $list ?? [], + 'file' => $file ?? '', + 'type' => $type ?? 'plaintext', + 'content' => $content ?? '', +]); diff --git a/templates/partials/file/list.php b/templates/partials/file/list.php new file mode 100644 index 0000000..d6bd06c --- /dev/null +++ b/templates/partials/file/list.php @@ -0,0 +1,12 @@ +layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?> + +
+
請選擇檔案:
+ + + + e($file) ?> + + + +
diff --git a/templates/partials/file/not-found.php b/templates/partials/file/not-found.php new file mode 100644 index 0000000..85bc332 --- /dev/null +++ b/templates/partials/file/not-found.php @@ -0,0 +1,35 @@ +layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?> + +
+ + +
diff --git a/templates/partials/file/view.php b/templates/partials/file/view.php new file mode 100644 index 0000000..5a15b3e --- /dev/null +++ b/templates/partials/file/view.php @@ -0,0 +1,39 @@ +layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?> + +push('styles') ?> + +end() ?> + +push('scripts') ?> + + + +end() ?> + + +
e($content ?? '') ?>
diff --git a/templates/partials/menu.php b/templates/partials/menu.php new file mode 100644 index 0000000..86f3e22 --- /dev/null +++ b/templates/partials/menu.php @@ -0,0 +1,56 @@ + +
您已使用 e($whoami ?? '') ?> 登入,點選此處以登出系統
diff --git a/templates/template.php b/templates/template.php new file mode 100644 index 0000000..f390ba9 --- /dev/null +++ b/templates/template.php @@ -0,0 +1,23 @@ + + + + + + + <?= $this->e($title ?? '無標題網頁') ?> + + + + section('styles') ?> + + +

e($title ?? '無標題網頁') ?>

+ + insert('partials/menu', ['nav' => $nav ?? '']) ?> + +
section('content') ?>
+ + +section('scripts') ?> + + diff --git a/webroot/auth.php b/webroot/auth.php new file mode 100644 index 0000000..326c924 --- /dev/null +++ b/webroot/auth.php @@ -0,0 +1,15 @@ + ConfigFilesViewer::list()]); diff --git a/webroot/configurations/view.php b/webroot/configurations/view.php new file mode 100644 index 0000000..1cfb604 --- /dev/null +++ b/webroot/configurations/view.php @@ -0,0 +1,27 @@ + $list, + ]); + +App::render('configurations/view', [ + 'list' => $list, + 'file' => $file, + 'content' => $content, + 'type' => [ + 'json' => 'json', + 'properties' => '.properties', + ][pathinfo($file, PATHINFO_EXTENSION)], +]); diff --git a/webroot/font-awesome b/webroot/font-awesome new file mode 120000 index 0000000..c2c31ee --- /dev/null +++ b/webroot/font-awesome @@ -0,0 +1 @@ +../vendor/components/font-awesome/ \ No newline at end of file diff --git a/webroot/index.php b/webroot/index.php new file mode 100644 index 0000000..1de6132 --- /dev/null +++ b/webroot/index.php @@ -0,0 +1,23 @@ + $status, + 'isStarting' => $isStarting, + 'query' => $query, +]); diff --git a/webroot/login.php b/webroot/login.php new file mode 100644 index 0000000..4961ce6 --- /dev/null +++ b/webroot/login.php @@ -0,0 +1,8 @@ + App::authUrl()]); diff --git a/webroot/logout.php b/webroot/logout.php new file mode 100644 index 0000000..1b58474 --- /dev/null +++ b/webroot/logout.php @@ -0,0 +1,14 @@ + LogsViewer::list()]); diff --git a/webroot/logs/view.php b/webroot/logs/view.php new file mode 100644 index 0000000..e2e96f4 --- /dev/null +++ b/webroot/logs/view.php @@ -0,0 +1,23 @@ + $list, + ]); + +App::render('logs/view', [ + 'list' => $list, + 'file' => $file, + 'content' => $content, +]); diff --git a/webroot/style.css b/webroot/style.css new file mode 100644 index 0000000..d10c3a2 --- /dev/null +++ b/webroot/style.css @@ -0,0 +1,36 @@ +html, +body { + height: 100%; + width: 100%; +} + +body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding-top: 8px; + padding-bottom: 8px; + background-color: #f5f5f5; +} + +main { + flex-grow: 1; + height: 0; + width: 100%; + overflow-y: scroll; +} + +pre.file-content { + background-color: white; + text-align: left; + padding: 8px; + width: 100%; + overflow-x: scroll; + white-space: pre-wrap; + word-wrap: break-word; +} + +.w-fit-content { + width: fit-content !important; +}