first commit
This commit is contained in:
commit
788cf3e917
47 changed files with 2007 additions and 0 deletions
14
.env.example
Normal file
14
.env.example
Normal file
|
|
@ -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
|
||||
203
.gitignore
vendored
Normal file
203
.gitignore
vendored
Normal file
|
|
@ -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
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -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
|
||||
30
.idea/akanyan.iml
generated
Normal file
30
.idea/akanyan.iml
generated
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="MingTsay\Akanyan\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/graham-campbell/result-type" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/twbs/bootstrap" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/plates" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/components/font-awesome" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/xpaw/php-minecraft-query" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/xpaw/php-source-query-class" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="default" level="application" />
|
||||
<orderEntry type="library" name="highlight" level="application" />
|
||||
<orderEntry type="library" name="highlight" level="application" />
|
||||
<orderEntry type="library" name="default" level="application" />
|
||||
<orderEntry type="library" name="highlight.js" level="application" />
|
||||
<orderEntry type="library" name="highlight.js" level="application" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{default, highlight, highlight.js}" />
|
||||
</component>
|
||||
</project>
|
||||
13
.idea/misc.xml
generated
Normal file
13
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UnattendedHostPersistenceState">
|
||||
<option name="openedFilesInfos">
|
||||
<list>
|
||||
<OpenedFileInfo>
|
||||
<option name="caretOffset" value="163" />
|
||||
<option name="fileUrl" value="file://$PROJECT_DIR$/templates/partials/menu.php" />
|
||||
</OpenedFileInfo>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/akanyan.iml" filepath="$PROJECT_DIR$/.idea/akanyan.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
25
.idea/php.xml
generated
Normal file
25
.idea/php.xml
generated
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PhpIncludePathManager">
|
||||
<include_path>
|
||||
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<path value="$PROJECT_DIR$/vendor/graham-campbell/result-type" />
|
||||
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<path value="$PROJECT_DIR$/vendor/twbs/bootstrap" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/plates" />
|
||||
<path value="$PROJECT_DIR$/vendor/components/font-awesome" />
|
||||
<path value="$PROJECT_DIR$/vendor/xpaw/php-minecraft-query" />
|
||||
<path value="$PROJECT_DIR$/vendor/xpaw/php-source-query-class" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.1" />
|
||||
<component name="PhpUnit">
|
||||
<phpunit_settings>
|
||||
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
|
||||
</phpunit_settings>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/webResources.xml
generated
Normal file
14
.idea/webResources.xml
generated
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/webroot" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
||||
198
app/App.php
Normal file
198
app/App.php
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
use League\Plates\Engine;
|
||||
use MingTsay\Akanyan\Discord\Me;
|
||||
use MingTsay\Akanyan\Discord\Token;
|
||||
|
||||
require_once __DIR__ . '/../env.php';
|
||||
|
||||
class App
|
||||
{
|
||||
private const allowedUsers = [
|
||||
305331852225544193, // 小喵#3521
|
||||
307567957863694348, // Aᴋᴀɴʏᴀɴ#2870
|
||||
559026859547951104, // 2072#7474
|
||||
];
|
||||
|
||||
public static function getTemplate(): Engine
|
||||
{
|
||||
static $templates = new Engine(__DIR__ . '/../templates');
|
||||
$me = self::me();
|
||||
$isLogin = self::checkU();
|
||||
$templates->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' => <<<HTML
|
||||
<div class="mt-5">
|
||||
<p>您的 Discord 帳號不在白名單中。</p>
|
||||
<p>若您認為這是個錯誤,請聯絡 <a href="https://discordapp.com/users/305331852225544193" target="_blank">小喵#3521</a> 並提供您的使用者編號 <code>$me->userId</code>。</p>
|
||||
</div>
|
||||
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 = <<<HTML
|
||||
<div>您已使用 <code>$me->username#$me->userDiscriminator</code> 登入,點選此處以<a href="/logout.php">登出系統</a>。</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
$html = <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant-TW">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>$params[title]</title>
|
||||
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/style.css"/>
|
||||
</head>
|
||||
<body class="text-center">
|
||||
<h1 class="h3 mb-3 font-weight-normal">$params[title]</h1>
|
||||
$status
|
||||
<main>$params[body]</main>
|
||||
<footer class="mt-5 mb-3 text-muted">Copyright © 2022 Ming Tsay. All rights reserved.</footer>
|
||||
<script src="/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
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;
|
||||
}
|
||||
}
|
||||
80
app/Auth.php
Normal file
80
app/Auth.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
use Exception;
|
||||
use MingTsay\Akanyan\Discord\Me;
|
||||
use MingTsay\Akanyan\Discord\Token;
|
||||
|
||||
require_once __DIR__ . '/../env.php';
|
||||
|
||||
class Auth
|
||||
{
|
||||
private const DISCORD_REDIRECT_URI = 'https://akanyan.oho.tw/auth.php?source=discord';
|
||||
|
||||
public static function encrypt(Token $payload): ?string
|
||||
{
|
||||
$key = file_get_contents(__DIR__ . '/../sodium_key');
|
||||
try {
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
|
||||
$cipher = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(json_encode($payload), '', $nonce, $key);
|
||||
|
||||
return implode(';', array_map('base64_encode', [$nonce, $cipher]));
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function decrypt(string $u): ?Token
|
||||
{
|
||||
$key = file_get_contents(__DIR__ . '/../sodium_key');
|
||||
try {
|
||||
list($nonce, $cipher) = array_map('base64_decode', explode(';', $u));
|
||||
$payload = json_decode(sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($cipher, '', $nonce, $key));
|
||||
if ($payload === null) return null;
|
||||
return new Token($payload);
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function authorize(): string
|
||||
{
|
||||
$query_string = http_build_query([
|
||||
'client_id' => $_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",
|
||||
],
|
||||
]))));
|
||||
}
|
||||
}
|
||||
32
app/ConfigFilesViewer.php
Normal file
32
app/ConfigFilesViewer.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
class ConfigFilesViewer extends FileViewer
|
||||
{
|
||||
|
||||
protected static function directory(): string
|
||||
{
|
||||
return '/media/sdc/mt/minecraft-docker-image/data';
|
||||
}
|
||||
|
||||
protected static function whitelist(): ?array
|
||||
{
|
||||
return [
|
||||
'banned-ips.json',
|
||||
'banned-players.json',
|
||||
'ops.json',
|
||||
'server.properties',
|
||||
'usercache.json',
|
||||
'whitelist.json',
|
||||
];
|
||||
}
|
||||
|
||||
public static function read($file): ?string
|
||||
{
|
||||
$content = parent::read($file);
|
||||
if ($content !== null && $file === 'server.properties')
|
||||
$content = preg_replace('/^(rcon\.password)=(.*)$/m', '$1=***hidden***', $content);
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
39
app/Discord/Me.php
Normal file
39
app/Discord/Me.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan\Discord;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class Me extends stdClass
|
||||
{
|
||||
public string $appId;
|
||||
public string $appName;
|
||||
public ?string $appIcon;
|
||||
public string $appDescription;
|
||||
public bool $appHook;
|
||||
public string $appVerifyKey;
|
||||
public array $scopes;
|
||||
public int $expires;
|
||||
public string $userId;
|
||||
public string $username;
|
||||
public string $userAvatar;
|
||||
public string $userDiscriminator;
|
||||
public string $userPublicFlags;
|
||||
|
||||
public function __construct(stdClass $payload)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
27
app/Discord/Token.php
Normal file
27
app/Discord/Token.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan\Discord;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class Token extends stdClass
|
||||
{
|
||||
public string $access_token;
|
||||
public string $token_type;
|
||||
public int $expires_in;
|
||||
public string $refresh_token;
|
||||
public string $scope;
|
||||
|
||||
public int $timestamp;
|
||||
|
||||
public function __construct(stdClass $payload)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
}
|
||||
33
app/FileViewer.php
Normal file
33
app/FileViewer.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
abstract class FileViewer
|
||||
{
|
||||
protected abstract static function directory(): string;
|
||||
|
||||
protected abstract static function whitelist(): ?array;
|
||||
|
||||
public static function list(): array
|
||||
{
|
||||
$directory = static::directory();
|
||||
$whitelist = static::whitelist();
|
||||
|
||||
return array_values(array_filter(
|
||||
scandir($directory),
|
||||
fn($file) => ($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;
|
||||
}
|
||||
}
|
||||
17
app/LogsViewer.php
Normal file
17
app/LogsViewer.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
class LogsViewer extends FileViewer
|
||||
{
|
||||
|
||||
protected static function directory(): string
|
||||
{
|
||||
return '/media/sdc/mt/minecraft-docker-image/data/logs';
|
||||
}
|
||||
|
||||
protected static function whitelist(): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
25
app/Minecraft.php
Normal file
25
app/Minecraft.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace MingTsay\Akanyan;
|
||||
|
||||
use xPaw\MinecraftPing;
|
||||
use xPaw\MinecraftPingException;
|
||||
|
||||
class Minecraft
|
||||
{
|
||||
public static function query(): ?array
|
||||
{
|
||||
$host = $_ENV['MINECRAFT_QUERY_HOST'];
|
||||
$port = (int)$_ENV['MINECRAFT_QUERY_PORT'];
|
||||
|
||||
$query = new MinecraftPing($host, $port);
|
||||
try {
|
||||
$result = $query->Query();
|
||||
} catch (MinecraftPingException) {
|
||||
$result = null;
|
||||
} finally {
|
||||
$query->Close();
|
||||
}
|
||||
return $result ?: null;
|
||||
}
|
||||
}
|
||||
28
composer.json
Normal file
28
composer.json
Normal file
|
|
@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
734
composer.lock
generated
Normal file
734
composer.lock
generated
Normal file
|
|
@ -0,0 +1,734 @@
|
|||
{
|
||||
"_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": "528c716c3d44b77191eaef0461fe108d",
|
||||
"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-iconv": "*",
|
||||
"ext-sodium": "*",
|
||||
"ext-zlib": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
7
env.php
Normal file
7
env.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
Dotenv::createImmutable(__DIR__)->load();
|
||||
6
templates/configurations/list.php
Normal file
6
templates/configurations/list.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$this->layout('partials/file/list', [
|
||||
'title' => '選擇設定檔',
|
||||
'nav' => 'configurations',
|
||||
'list' => $list ?? [],
|
||||
]);
|
||||
6
templates/configurations/not-found.php
Normal file
6
templates/configurations/not-found.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$this->layout('partials/file/not-found', [
|
||||
'title' => '檢視設定檔',
|
||||
'nav' => 'configurations',
|
||||
'list' => $list ?? [],
|
||||
]);
|
||||
9
templates/configurations/view.php
Normal file
9
templates/configurations/view.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
$this->layout('partials/file/view', [
|
||||
'title' => '檢視設定檔',
|
||||
'nav' => 'configurations',
|
||||
'list' => $list ?? [],
|
||||
'file' => $file ?? '',
|
||||
'type' => $type ?? 'plaintext',
|
||||
'content' => $content ?? '',
|
||||
]);
|
||||
68
templates/index.php
Normal file
68
templates/index.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php $this->layout('template', ['title' => '首頁']) ?>
|
||||
|
||||
<div class="card m-3 mt-5 mx-auto w-fit-content">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-center flex-column flex-lg-row">
|
||||
<div class="mx-2">伺服器狀態:<code><?= $this->e($status ?? '') ?></code></div>
|
||||
<?php if (isset($query)): ?>
|
||||
<?php if (isset($query['version']) && isset($query['version']['name'])): ?>
|
||||
<div class="mx-2">伺服器版本:<code><?= $this->e($query['version']['name']) ?></code></div>
|
||||
<?php endif ?>
|
||||
<?php if (isset($query['players'])): ?>
|
||||
<?php if (isset($query['players']['online']) && isset($query['players']['max'])): ?>
|
||||
<div class="mx-2">
|
||||
線上人數:
|
||||
<code><?= $this->e($query['players']['online']) ?></code>
|
||||
/
|
||||
<code><?= $this->e($query['players']['max']) ?></code>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if (isset($query['players']['sample']) && count($query['players']['sample']) > 0): ?>
|
||||
<ul class="mx-2 list-group list-group-horizontal-lg">
|
||||
<?php foreach ($query['players']['sample'] as $player) : ?>
|
||||
<li class="py-0 list-group-item"><code><?= $this->e($player['name']) ?></code></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php if (isset($isStarting) && $isStarting): ?>
|
||||
<div class="alert alert-success my-3 mx-auto d-table" role="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor"
|
||||
class="bi bi-exclamation-triangle-fill flex-shrink-0 me-2" viewBox="0 0 16 16" role="img"
|
||||
aria-label="Success:">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||
</svg>
|
||||
已送出伺服器啟動訊號。
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php if (isset($status) && $status === 'exited'): ?>
|
||||
<form method="POST">
|
||||
<button class="btn btn-primary btn-block" name="start" type="submit">啟動伺服器</button>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card m-3 mx-auto w-fit-content">
|
||||
<div class="card-body">
|
||||
<p class="fs-5">【更新紀錄】</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<p>首頁現在可以看到伺服器版本與線上人數等資訊了。</p>
|
||||
<p>更新日期:2022/07/05 02:02:02</p>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<p>首頁現在可以看到伺服器狀態了,也可以從首頁直接啟動伺服器。</p>
|
||||
<p>更新日期:2022/07/04 16:52:30</p>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<p>查看檔案功能已移至新的網址,請透過上方選單進入。</p>
|
||||
<p>更多其他功能將陸續推出,敬請期待。</p>
|
||||
<p>更新日期:2022/07/04 13:44:30</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
8
templates/login.php
Normal file
8
templates/login.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php $this->layout('template', ['title' => '登入系統', 'noStatus' => true]) ?>
|
||||
|
||||
<p class="lead">您尚未登入,請點選下方按鈕進行登入。</p>
|
||||
<?php if (empty($authUrl)): ?>
|
||||
<a href="/login.php" class="btn btn-lg btn-warning btn-block">登入連結載入失敗,按此重新載入</a>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->e($authUrl) ?>" class="btn btn-lg btn-primary btn-block">透過 Discord 帳號登入</a>
|
||||
<?php endif ?>
|
||||
6
templates/logout/form.php
Normal file
6
templates/logout/form.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php $this->layout('template', ['title' => '登出確認', 'noStatus' => true]) ?>
|
||||
|
||||
<p class="lead">您確定要登出嗎?</p>
|
||||
<form action="/logout.php" method="POST">
|
||||
<button name="logout" type="submit" class="btn btn-lg btn-danger btn-block">確定登出</button>
|
||||
</form>
|
||||
3
templates/logout/success.php
Normal file
3
templates/logout/success.php
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<?php $this->layout('template', ['title' => '登出成功', 'noStatus' => true]) ?>
|
||||
|
||||
您已成功登出,<a href="/">按此重新登入</a>。
|
||||
6
templates/logs/list.php
Normal file
6
templates/logs/list.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$this->layout('partials/file/list', [
|
||||
'title' => '選擇日誌檔案',
|
||||
'nav' => 'logs',
|
||||
'list' => $list ?? [],
|
||||
]);
|
||||
6
templates/logs/not-found.php
Normal file
6
templates/logs/not-found.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$this->layout('partials/file/not-found', [
|
||||
'title' => '檢視日誌',
|
||||
'nav' => 'logs',
|
||||
'list' => $list ?? [],
|
||||
]);
|
||||
9
templates/logs/view.php
Normal file
9
templates/logs/view.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
$this->layout('partials/file/view', [
|
||||
'title' => '檢視日誌',
|
||||
'nav' => 'logs',
|
||||
'list' => $list ?? [],
|
||||
'file' => $file ?? '',
|
||||
'type' => $type ?? 'plaintext',
|
||||
'content' => $content ?? '',
|
||||
]);
|
||||
12
templates/partials/file/list.php
Normal file
12
templates/partials/file/list.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php $this->layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?>
|
||||
|
||||
<div class="list-group text-start mt-5 mx-auto w-fit-content">
|
||||
<div class="list-group-item">請選擇檔案:</div>
|
||||
<?php if (isset($list) && is_array($list)): ?>
|
||||
<?php foreach ($list as $file): ?>
|
||||
<a class="list-group-item list-group-item-action" href="view.php?file=<?= $this->e($file) ?>">
|
||||
<?= $this->e($file) ?>
|
||||
</a>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
35
templates/partials/file/not-found.php
Normal file
35
templates/partials/file/not-found.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php $this->layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?>
|
||||
|
||||
<div class="m-5">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="file-selector"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
請選擇檔案
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="file-selector">
|
||||
<?php if (isset($list) && is_array($list)): ?>
|
||||
<?php foreach ($list as $filename): ?>
|
||||
<li>
|
||||
<?php if (isset($file) && $file === $filename): ?>
|
||||
<a class="dropdown-item active" href="view.php?file=<?= $this->e($filename) ?>">
|
||||
<?= $this->e($filename) ?>*
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a class="dropdown-item" href="view.php?file=<?= $this->e($filename) ?>">
|
||||
<?= $this->e($filename) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="alert alert-warning mt-5 m-auto d-table" role="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor"
|
||||
class="bi bi-exclamation-triangle-fill flex-shrink-0 me-2" viewBox="0 0 16 16" role="img"
|
||||
aria-label="Warning:">
|
||||
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||
</svg>
|
||||
檔案不存在,請從上方選單選取欲查看之檔案。
|
||||
</div>
|
||||
</div>
|
||||
39
templates/partials/file/view.php
Normal file
39
templates/partials/file/view.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php $this->layout('template', ['title' => $title ?? null, 'nav' => $nav ?? null]) ?>
|
||||
|
||||
<?php $this->push('styles') ?>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css"/>
|
||||
<?php $this->end() ?>
|
||||
|
||||
<?php $this->push('scripts') ?>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
|
||||
<!--suppress JSUnresolvedVariable -->
|
||||
<script>hljs.highlightAll()</script>
|
||||
<?php $this->end() ?>
|
||||
|
||||
<div class="dropdown my-2">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle"
|
||||
type="button"
|
||||
id="file-selector"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<?= $this->e($file ?? '') ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="file-selector">
|
||||
<?php if (isset($list) && is_array($list)): ?>
|
||||
<?php foreach ($list as $filename): ?>
|
||||
<li>
|
||||
<?php if (isset($file) && $file === $filename): ?>
|
||||
<a class="dropdown-item active" href="view.php?file=<?= $this->e($filename) ?>">
|
||||
<?= $this->e($filename) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a class="dropdown-item" href="view.php?file=<?= $this->e($filename) ?>">
|
||||
<?= $this->e($filename) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
</div>
|
||||
<pre class="file-content"><code class="language-<?= $this->e($type ?? 'plaintext') ?>"><?= $this->e($content ?? '') ?></code></pre>
|
||||
56
templates/partials/menu.php
Normal file
56
templates/partials/menu.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<nav class="navbar navbar-expand-lg bg-light">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-brand">功能選單</div>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#nav"
|
||||
aria-controls="nav" aria-expanded="false" aria-label="顯示/隱藏選單">
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="nav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
<?php if (isset($nav) && $nav === 'index'): ?>
|
||||
class="nav-link active"
|
||||
aria-current="page"
|
||||
<?php else: ?>
|
||||
class="nav-link"
|
||||
<?php endif ?>
|
||||
href="/index.php"
|
||||
>
|
||||
<i class="fa-solid fa-house"></i>
|
||||
首頁
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
<?php if (isset($nav) && $nav === 'configurations'): ?>
|
||||
class="nav-link active"
|
||||
aria-current="page"
|
||||
<?php else: ?>
|
||||
class="nav-link"
|
||||
<?php endif ?>
|
||||
href="/configurations/list.php"
|
||||
>
|
||||
<i class="fa-solid fa-file-code"></i>
|
||||
檢視設定檔
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
<?php if (isset($nav) && $nav === 'logs'): ?>
|
||||
class="nav-link active"
|
||||
aria-current="page"
|
||||
<?php else: ?>
|
||||
class="nav-link"
|
||||
<?php endif ?>
|
||||
href="/logs/list.php"
|
||||
>
|
||||
<i class="fa-solid fa-file-lines"></i>
|
||||
檢視日誌
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div>您已使用 <code><?= $this->e($whoami ?? '') ?></code> 登入,點選此處以<a href="/logout.php">登出系統</a>。</div>
|
||||
23
templates/template.php
Normal file
23
templates/template.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant-TW">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title><?= $this->e($title ?? '無標題網頁') ?></title>
|
||||
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/font-awesome/css/all.min.css"/>
|
||||
<link rel="stylesheet" href="/style.css"/>
|
||||
<?= $this->section('styles') ?>
|
||||
</head>
|
||||
<body class="text-center">
|
||||
<h1 class="h3 mb-3 font-weight-normal"><?= $this->e($title ?? '無標題網頁') ?></h1>
|
||||
<?php if (isset($isLogin) && $isLogin && (!isset($noStatus) || !$noStatus)): ?>
|
||||
<?php $this->insert('partials/menu', ['nav' => $nav ?? '']) ?>
|
||||
<?php endif ?>
|
||||
<main><?= $this->section('content') ?></main>
|
||||
<footer class="my-2 text-muted">Copyright © 2022 Ming Tsay. All rights reserved.</footer>
|
||||
<script src="/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<?= $this->section('scripts') ?>
|
||||
</body>
|
||||
</html>
|
||||
15
webroot/auth.php
Normal file
15
webroot/auth.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (($_GET['source'] ?? '') !== 'discord' || empty($_GET['code'])) {
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
|
||||
App::auth($_GET['code']);
|
||||
|
||||
header('location: /');
|
||||
http_response_code(302);
|
||||
1
webroot/bootstrap
Symbolic link
1
webroot/bootstrap
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../vendor/twbs/bootstrap/dist/
|
||||
10
webroot/configurations/list.php
Normal file
10
webroot/configurations/list.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
use MingTsay\Akanyan\ConfigFilesViewer;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
App::requireAllowed();
|
||||
|
||||
App::render('configurations/list', ['list' => ConfigFilesViewer::list()]);
|
||||
27
webroot/configurations/view.php
Normal file
27
webroot/configurations/view.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
use MingTsay\Akanyan\ConfigFilesViewer;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
App::requireAllowed();
|
||||
|
||||
$file = $_GET['file'] ?? '';
|
||||
$content = ConfigFilesViewer::read($file);
|
||||
$list = ConfigFilesViewer::list();
|
||||
|
||||
if ($content === null)
|
||||
App::render('configurations/not-found', [
|
||||
'list' => $list,
|
||||
]);
|
||||
|
||||
App::render('configurations/view', [
|
||||
'list' => $list,
|
||||
'file' => $file,
|
||||
'content' => $content,
|
||||
'type' => [
|
||||
'json' => 'json',
|
||||
'properties' => '.properties',
|
||||
][pathinfo($file, PATHINFO_EXTENSION)],
|
||||
]);
|
||||
1
webroot/font-awesome
Symbolic link
1
webroot/font-awesome
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../vendor/components/font-awesome/
|
||||
23
webroot/index.php
Normal file
23
webroot/index.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
use MingTsay\Akanyan\Minecraft;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
App::requireAllowed();
|
||||
|
||||
$status = exec('sudo -u mt docker inspect -f \'{{.State.Status}}\' akanyan-server-1');
|
||||
|
||||
// start the server
|
||||
$isStarting = isset($_POST['start']);
|
||||
if ($isStarting) exec('sudo -u mt docker start akanyan-server-1');
|
||||
|
||||
// get query
|
||||
$query = $status === 'running' ? Minecraft::query() : null;
|
||||
|
||||
App::render('index', [
|
||||
'status' => $status,
|
||||
'isStarting' => $isStarting,
|
||||
'query' => $query,
|
||||
]);
|
||||
8
webroot/login.php
Normal file
8
webroot/login.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
App::requireNonAuth();
|
||||
App::render('login', ['authUrl' => App::authUrl()]);
|
||||
14
webroot/logout.php
Normal file
14
webroot/logout.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
App::requireAuth();
|
||||
|
||||
if (isset($_POST['logout'])) {
|
||||
App::unsetU();
|
||||
App::render('logout/success');
|
||||
}
|
||||
|
||||
App::render('logout/form');
|
||||
10
webroot/logs/list.php
Normal file
10
webroot/logs/list.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
use MingTsay\Akanyan\LogsViewer;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
App::requireAllowed();
|
||||
|
||||
App::render('logs/list', ['list' => LogsViewer::list()]);
|
||||
23
webroot/logs/view.php
Normal file
23
webroot/logs/view.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use MingTsay\Akanyan\App;
|
||||
use MingTsay\Akanyan\LogsViewer;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
App::requireAllowed();
|
||||
|
||||
$file = $_GET['file'] ?? '';
|
||||
$content = LogsViewer::read($file);
|
||||
$list = LogsViewer::list();
|
||||
|
||||
if ($content === null)
|
||||
App::render('configurations/not-found', [
|
||||
'list' => $list,
|
||||
]);
|
||||
|
||||
App::render('logs/view', [
|
||||
'list' => $list,
|
||||
'file' => $file,
|
||||
'content' => $content,
|
||||
]);
|
||||
36
webroot/style.css
Normal file
36
webroot/style.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue