Compare commits
40 Commits
migrate-to
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52f6fefed0 | ||
|
|
840555df42 | ||
|
|
9aa867e8aa | ||
|
|
d70b39f445 | ||
|
|
33055ecbf9 | ||
|
|
2e39fbce77 | ||
|
|
1b3b020d52 | ||
|
|
33d805ee82 | ||
|
|
57c02dd51c | ||
|
|
5b6bb98860 | ||
|
|
c01112a6c1 | ||
|
|
064b0967fc | ||
|
|
9f4c59abec | ||
|
|
d8547a0a3d | ||
|
|
9c51bd602b | ||
|
|
bc3f504ca3 | ||
|
|
761cbb7828 | ||
|
|
01fe3eb4cb | ||
|
|
f03dd8122b | ||
|
|
cfda2a6bf8 | ||
|
|
74ce668221 | ||
|
|
5a18d24464 | ||
|
|
5125862f80 | ||
|
|
fa791857ec | ||
|
|
24ad29ea99 | ||
|
|
cdfb972bd0 | ||
|
|
1985ce6ff8 | ||
|
|
d84eb65d55 | ||
|
|
16474fb5d0 | ||
|
|
186138b884 | ||
|
|
9ca6e37e39 | ||
|
|
866e182c31 | ||
|
|
739b9c97d7 | ||
|
|
0a43c5aa67 | ||
|
|
35bd4524e6 | ||
|
|
eba91d5f1d | ||
|
|
d764836244 | ||
|
|
b1b4a71c3e | ||
|
|
fb74b43d64 | ||
|
|
2a24506c15 |
43
.github/workflows/CI.yml
vendored
43
.github/workflows/CI.yml
vendored
|
|
@ -20,20 +20,19 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.1
|
||||
php-version: 8.3
|
||||
coverage: none
|
||||
extensions: mbstring, dom, fileinfo, gd
|
||||
extensions: mbstring, dom, fileinfo, gd, imagick
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
composer install --prefer-dist --no-progress
|
||||
- name: Prepare
|
||||
run: |
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
mkdir -p resources/views/overrides
|
||||
- name: Validate Twig templates
|
||||
run: php artisan twig:lint -v
|
||||
|
|
@ -45,24 +44,24 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: ['8.1', '8.2']
|
||||
php: ['8.2', '8.3']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup PHP only
|
||||
uses: shivammathur/setup-php@v2
|
||||
if: matrix.php != '8.0'
|
||||
if: matrix.php != '8.3'
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: none
|
||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip
|
||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick
|
||||
- name: Setup PHP with Xdebug
|
||||
uses: shivammathur/setup-php@v2
|
||||
if: matrix.php == '8.0'
|
||||
if: matrix.php == '8.3'
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: xdebug
|
||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip
|
||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
|
|
@ -72,14 +71,14 @@ jobs:
|
|||
- name: Install Composer dependencies
|
||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
||||
- name: Run tests only
|
||||
if: matrix.php != '8.0'
|
||||
if: matrix.php != '8.3'
|
||||
run: ./vendor/bin/phpunit
|
||||
- name: Run tests with coverage report
|
||||
if: matrix.php == '8.0'
|
||||
if: matrix.php == '8.3'
|
||||
run: ./vendor/bin/phpunit --coverage-clover=coverage.xml
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v1
|
||||
if: matrix.php == '8.0' && success()
|
||||
if: matrix.php == '8.3' && success()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
name: github-actions
|
||||
|
|
@ -88,7 +87,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Run checks
|
||||
|
|
@ -101,7 +100,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Run tests
|
||||
|
|
@ -115,8 +114,14 @@ jobs:
|
|||
name: Snapshot Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.2
|
||||
coverage: none
|
||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Cache Node dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
|
|
@ -138,11 +143,13 @@ jobs:
|
|||
yarn build
|
||||
cp resources/assets/src/images/bg.webp public/app/
|
||||
cp resources/assets/src/images/favicon.ico public/app/
|
||||
- uses: benjlevesque/short-sha@v1.2
|
||||
- uses: benjlevesque/short-sha@v3.0
|
||||
id: short-sha
|
||||
- name: Archive release
|
||||
run: zip -9 -r blessing-skin-server-${{ steps.short-sha.outputs.sha }}.zip app bootstrap config database plugins public resources/lang resources/views resources/misc/textures routes storage vendor .env.example artisan LICENSE README.md README-zh.md index.html
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: blessing-skin-server-${{ steps.short-sha.outputs.sha }}.zip
|
||||
path: blessing-skin-server-${{ steps.short-sha.outputs.sha }}.zip
|
||||
|
|
|
|||
2
.github/workflows/Release.yml
vendored
2
.github/workflows/Release.yml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Build and create archive
|
||||
run: ./tools/release.ps1
|
||||
shell: pwsh
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -25,3 +25,5 @@ storage/options.php
|
|||
.phpunit.result.cache
|
||||
.php-cs-fixer.cache
|
||||
resources/views/overrides
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
68
.vscode/launch.json
vendored
68
.vscode/launch.json
vendored
|
|
@ -1,38 +1,34 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Jest Tests",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": ["${file}"],
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"name": "Launch with XDebug",
|
||||
"ignore": [
|
||||
"**/vendor/**/*.php"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"name": "Launch with Firefox Debugger",
|
||||
"url": "http://localhost/",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"pathMappings": [
|
||||
{
|
||||
"url": "webpack:///",
|
||||
"path": "${workspaceFolder}/"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Jest Tests",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": ["${file}"],
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
{
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"name": "Launch with XDebug",
|
||||
"ignore": ["**/vendor/**/*.php"]
|
||||
},
|
||||
{
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"name": "Launch with Firefox Debugger",
|
||||
"url": "http://localhost/",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"pathMappings": [
|
||||
{
|
||||
"url": "webpack:///",
|
||||
"path": "${workspaceFolder}/"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ WORKDIR /app
|
|||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
COPY tsconfig.build.json tsconfig.json webpack.config.ts ./
|
||||
COPY postcss.config.js tsconfig.build.json tsconfig.json webpack.config.ts ./
|
||||
COPY tools/*Plugin.ts ./tools/
|
||||
|
||||
COPY resources ./resources
|
||||
|
|
|
|||
101
README-zh.md
101
README-zh.md
|
|
@ -52,6 +52,7 @@ Blessing Skin 对您的服务器有一定的要求。在大多数情况下,下
|
|||
- JSON
|
||||
- fileinfo
|
||||
- zip
|
||||
- Imagick
|
||||
|
||||
## 快速使用
|
||||
|
||||
|
|
@ -61,105 +62,9 @@ Blessing Skin 对您的服务器有一定的要求。在大多数情况下,下
|
|||
|
||||
Blessing Skin 提供了强大的插件系统,您可以通过添加多种多样的插件来为您的皮肤站添加功能。
|
||||
|
||||
## 支持并赞助 Blessing Skin
|
||||
|
||||
如果您觉得这个软件对您很有帮助,欢迎通过赞助来支持开发!
|
||||
|
||||
目前可在 [爱发电](https://afdian.net/@blessing-skin) 上赞助。
|
||||
|
||||
### Sponsors
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@gao_cai_sheng">
|
||||
<img src="https://pic1.afdiancdn.com/user/2aac23481b1b11ea9f6e52540025c377/avatar/96a8b23d98cbac5aa36601db15a27e5e_w512_h512_s234.jpg" width="120" height="120">
|
||||
<br>
|
||||
gao_cai_sheng
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@LD_fantasy">
|
||||
<img src="https://pic1.afdiancdn.com/user/9bed7bb454f011eb821652540025c377/avatar/cb679e3eac693e0eea2eac527c7954e0_w700_h1307_s137.jpg" width="120" height="120">
|
||||
<br>
|
||||
K_LazyCat
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@nmzy2018">
|
||||
<img src="https://pic1.afdiancdn.com/user/a66f79d2f5a311e9af4e52540025c377/avatar/98682fb3c5914a39c8986bb1e97b5501_w512_h512_s248.jpg" width="120" height="120">
|
||||
<br>
|
||||
伊南
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="">
|
||||
<img src="https://pic1.afdiancdn.com/default/avatar/avatar-blue.png" width="120" height="120">
|
||||
<br>
|
||||
家乐
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@oar-01">
|
||||
<img src="https://pic1.afdiancdn.com/user/e391f6ccdfa911ebb0e352540025c377/avatar/74da4afa92fa2666c306d43ab7a8804b_w1920_h1080_s338.jpg" width="120" height="120">
|
||||
<br>
|
||||
黄金鞘翅的郡主
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://www.bilibili.plus/caucmc1.orz">
|
||||
<img src="https://pic1.afdiancdn.com/user/edde2efc879611e889f552540025c377/avatar/d6a712efd6560b28989ac33f99c8915d_w473_h454_s24.jpg" width="120" height="120">
|
||||
<br>
|
||||
睡觉塞牙
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Backers
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@ValiantShishu976400">
|
||||
<img src="https://pic1.afdiancdn.com/user/178a08963a5e11e9addd52540025c377/avatar/ece9f089aaf2c2f83204a8de11697caf_w350_h350_s16.jpg" width="75" height="75">
|
||||
<br>
|
||||
飒爽师叔
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@PAKingdom">
|
||||
<img src="https://pic1.afdiancdn.com/user/18ad3338e58a11e9b29352540025c377/avatar/1e8b6476b589ddac545ac1ce13166e59_w584_h797_s59.jpg" width="75" height="75">
|
||||
<br>
|
||||
皮皮帕
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@oar-01">
|
||||
<img src="https://pic1.afdiancdn.com/user/e391f6ccdfa911ebb0e352540025c377/avatar/74da4afa92fa2666c306d43ab7a8804b_w1920_h1080_s338.jpg" width="75" height="75">
|
||||
<br>
|
||||
黄金鞘翅的郡主
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="">
|
||||
<img src="https://pic1.afdiancdn.com/user/fc143860efa111ebb3e552540025c377/avatar/6e1d0f3f6ffb80b89b44269f59aa775f_w1080_h1080_s107.jpg" width="75" height="75">
|
||||
<br>
|
||||
♂sudo rm -rf /*[幼稚鬼]
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## 自行构建
|
||||
|
||||
详情可阅读 [这里](https://blessing.netlify.com/build.html)。
|
||||
详情可阅读 [这里](https://blessing.netlify.app/build.html)。
|
||||
|
||||
> 您可以订阅我们的 Telegram 频道 [Blessing Skin News](https://t.me/blessing_skin_news) 来获取最新开发动态。当有新的 Commit 被推送时,我们的机器人将会在频道内发送一条消息来提示您能否拉取最新代码,以及拉取后应该做什么。
|
||||
|
||||
|
|
@ -171,7 +76,7 @@ Blessing Skin 可支持多种语言,当前支持英语、简体中文和西班
|
|||
|
||||
## 问题报告
|
||||
|
||||
请参阅 [报告问题的正确姿势](https://blessing.netlify.com/report.html)。
|
||||
请参阅 [报告问题的正确姿势](https://blessing.netlify.app/report.html)。
|
||||
|
||||
## 相关链接
|
||||
|
||||
|
|
|
|||
97
README.md
97
README.md
|
|
@ -52,6 +52,7 @@ Blessing Skin has only a few system requirements. In most cases, these PHP exten
|
|||
- JSON
|
||||
- fileinfo
|
||||
- zip
|
||||
- Imagick
|
||||
|
||||
## Quick Install
|
||||
|
||||
|
|
@ -61,102 +62,6 @@ Please read [Installation Guide](https://blessing.netlify.app/en/setup.html).
|
|||
|
||||
Blessing Skin provides an elegant and powerful plugin system, and you can attach plenty of functions and customization to your site via installing plugins.
|
||||
|
||||
## Supporting Blessing Skin
|
||||
|
||||
Welcome to sponsoring Blessing Skin if this software is useful for you!
|
||||
|
||||
Currently you can sponsor us via [爱发电](https://afdian.net/@blessing-skin).
|
||||
|
||||
### Sponsors
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@gao_cai_sheng">
|
||||
<img src="https://pic1.afdiancdn.com/user/2aac23481b1b11ea9f6e52540025c377/avatar/96a8b23d98cbac5aa36601db15a27e5e_w512_h512_s234.jpg" width="120" height="120">
|
||||
<br>
|
||||
gao_cai_sheng
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@LD_fantasy">
|
||||
<img src="https://pic1.afdiancdn.com/user/9bed7bb454f011eb821652540025c377/avatar/cb679e3eac693e0eea2eac527c7954e0_w700_h1307_s137.jpg" width="120" height="120">
|
||||
<br>
|
||||
K_LazyCat
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@nmzy2018">
|
||||
<img src="https://pic1.afdiancdn.com/user/a66f79d2f5a311e9af4e52540025c377/avatar/98682fb3c5914a39c8986bb1e97b5501_w512_h512_s248.jpg" width="120" height="120">
|
||||
<br>
|
||||
伊南
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="">
|
||||
<img src="https://pic1.afdiancdn.com/default/avatar/avatar-blue.png" width="120" height="120">
|
||||
<br>
|
||||
家乐
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@oar-01">
|
||||
<img src="https://pic1.afdiancdn.com/user/e391f6ccdfa911ebb0e352540025c377/avatar/74da4afa92fa2666c306d43ab7a8804b_w1920_h1080_s338.jpg" width="120" height="120">
|
||||
<br>
|
||||
黄金鞘翅的郡主
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://www.bilibili.plus/caucmc1.orz">
|
||||
<img src="https://pic1.afdiancdn.com/user/edde2efc879611e889f552540025c377/avatar/d6a712efd6560b28989ac33f99c8915d_w473_h454_s24.jpg" width="120" height="120">
|
||||
<br>
|
||||
睡觉塞牙
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Backers
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@ValiantShishu976400">
|
||||
<img src="https://pic1.afdiancdn.com/user/178a08963a5e11e9addd52540025c377/avatar/ece9f089aaf2c2f83204a8de11697caf_w350_h350_s16.jpg" width="75" height="75">
|
||||
<br>
|
||||
飒爽师叔
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@PAKingdom">
|
||||
<img src="https://pic1.afdiancdn.com/user/18ad3338e58a11e9b29352540025c377/avatar/1e8b6476b589ddac545ac1ce13166e59_w584_h797_s59.jpg" width="75" height="75">
|
||||
<br>
|
||||
皮皮帕
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="https://afdian.net/@oar-01">
|
||||
<img src="https://pic1.afdiancdn.com/user/e391f6ccdfa911ebb0e352540025c377/avatar/74da4afa92fa2666c306d43ab7a8804b_w1920_h1080_s338.jpg" width="75" height="75">
|
||||
<br>
|
||||
黄金鞘翅的郡主
|
||||
</a>
|
||||
</td>
|
||||
<td align=center>
|
||||
<a href="">
|
||||
<img src="https://pic1.afdiancdn.com/user/fc143860efa111ebb3e552540025c377/avatar/6e1d0f3f6ffb80b89b44269f59aa775f_w1080_h1080_s107.jpg" width="75" height="75">
|
||||
<br>
|
||||
♂sudo rm -rf /*[幼稚鬼]
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Build From Source
|
||||
|
||||
Please refer to [Manual Build](https://blessing.netlify.app/build.html).
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class Handler extends ExceptionHandler
|
|||
// @codeCoverageIgnoreStart
|
||||
$isFromPlugins = !app()->runningUnitTests()
|
||||
&& Str::contains($trace['file'], resolve('plugins')->getPluginsDirs()->all());
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
return Str::startsWith($trace['file'], 'app') || $isFromPlugins;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class AdminController extends Controller
|
|||
Request $request,
|
||||
PluginManager $plugins,
|
||||
Filesystem $filesystem,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$db = config('database.connections.'.config('database.default'));
|
||||
$dbType = Arr::get([
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ use App\Mail\ForgotPassword;
|
|||
use App\Models\Player;
|
||||
use App\Models\User;
|
||||
use App\Rules;
|
||||
use Auth;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Http\Request;
|
||||
use Mail;
|
||||
use Session;
|
||||
use URL;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Vectorface\Whip\Whip;
|
||||
|
||||
class AuthController extends Controller
|
||||
|
|
@ -50,7 +50,7 @@ class AuthController extends Controller
|
|||
Request $request,
|
||||
Rules\Captcha $captcha,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'identification' => 'required',
|
||||
|
|
@ -151,7 +151,7 @@ class AuthController extends Controller
|
|||
Request $request,
|
||||
Rules\Captcha $captcha,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$can = $filter->apply('can_register', null);
|
||||
if ($can instanceof Rejection) {
|
||||
|
|
@ -248,7 +248,7 @@ class AuthController extends Controller
|
|||
Request $request,
|
||||
Rules\Captcha $captcha,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'email' => 'required|email',
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ClosetController extends Controller
|
||||
{
|
||||
|
|
@ -75,7 +75,7 @@ class ClosetController extends Controller
|
|||
public function add(
|
||||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
['tid' => $tid, 'name' => $name] = $request->validate([
|
||||
'tid' => 'required|integer',
|
||||
|
|
@ -132,7 +132,7 @@ class ClosetController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
$tid
|
||||
$tid,
|
||||
) {
|
||||
['name' => $name] = $request->validate(['name' => 'required']);
|
||||
/** @var User */
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ class OptionsController extends Controller
|
|||
->text('max_upload_file_size')->addon('KB')
|
||||
->hint(trans('options.general.max_upload_file_size.hint', ['size' => ini_get('upload_max_filesize')]));
|
||||
|
||||
$form->group('max_texture_width')
|
||||
->text('max_texture_width')->addon('px')
|
||||
->hint(trans('options.general.max_texture_width.hint'));
|
||||
|
||||
$form->select('player_name_rule')
|
||||
->option('official', trans('options.general.player_name_rule.official'))
|
||||
->option('cjk', trans('options.general.player_name_rule.cjk'))
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ use App\Models\Player;
|
|||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use App\Rules;
|
||||
use Auth;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PlayerController extends Controller
|
||||
|
|
@ -124,7 +124,7 @@ class PlayerController extends Controller
|
|||
public function delete(
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Player $player
|
||||
Player $player,
|
||||
) {
|
||||
/** @var User */
|
||||
$user = auth()->user();
|
||||
|
|
@ -157,7 +157,7 @@ class PlayerController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Player $player
|
||||
Player $player,
|
||||
) {
|
||||
$name = $request->validate([
|
||||
'name' => [
|
||||
|
|
@ -194,7 +194,7 @@ class PlayerController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Player $player
|
||||
Player $player,
|
||||
) {
|
||||
/** @var User */
|
||||
$user = auth()->user();
|
||||
|
|
@ -234,7 +234,7 @@ class PlayerController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Player $player
|
||||
Player $player,
|
||||
) {
|
||||
$types = $request->input('type', []);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class PlayersManagementController extends Controller
|
|||
public function name(
|
||||
Player $player,
|
||||
Request $request,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$name = $request->validate([
|
||||
'player_name' => [
|
||||
|
|
@ -70,7 +70,7 @@ class PlayersManagementController extends Controller
|
|||
public function owner(
|
||||
Player $player,
|
||||
Request $request,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$uid = $request->validate(['uid' => 'required|integer'])['uid'];
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ class PlayersManagementController extends Controller
|
|||
public function texture(
|
||||
Player $player,
|
||||
Request $request,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'tid' => 'required|integer',
|
||||
|
|
@ -123,7 +123,7 @@ class PlayersManagementController extends Controller
|
|||
|
||||
public function delete(
|
||||
Player $player,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$dispatcher->dispatch('player.deleting', [$player]);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class ReportController extends Controller
|
|||
public function review(
|
||||
Report $report,
|
||||
Request $request,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'action' => ['required', Rule::in(['delete', 'ban', 'reject'])],
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class SetupController extends Controller
|
|||
Request $request,
|
||||
Filesystem $filesystem,
|
||||
Connection $connection,
|
||||
DatabaseManager $manager
|
||||
DatabaseManager $manager,
|
||||
) {
|
||||
if ($request->isMethod('get')) {
|
||||
try {
|
||||
|
|
@ -121,7 +121,7 @@ class SetupController extends Controller
|
|||
'database/migrations',
|
||||
'vendor/laravel/passport/database/migrations',
|
||||
],
|
||||
]);
|
||||
]);
|
||||
|
||||
$siteUrl = url('/');
|
||||
if (Str::endsWith($siteUrl, '/index.php')) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
|
@ -12,10 +11,12 @@ use Illuminate\Database\Eloquent\Builder;
|
|||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
use Storage;
|
||||
|
||||
class SkinlibController extends Controller
|
||||
{
|
||||
|
|
@ -189,7 +190,7 @@ class SkinlibController extends Controller
|
|||
public function handleUpload(
|
||||
Request $request,
|
||||
Filter $filter,
|
||||
Dispatcher $dispatcher
|
||||
Dispatcher $dispatcher,
|
||||
) {
|
||||
$file = $request->file('file');
|
||||
if ($file && !$file->isValid()) {
|
||||
|
|
@ -220,6 +221,16 @@ class SkinlibController extends Controller
|
|||
$type = $data['type'];
|
||||
$size = getimagesize($file);
|
||||
|
||||
$maxWidth = option('max_texture_width', 8192);
|
||||
if ($size[0] > $maxWidth) {
|
||||
$message = trans('skinlib.upload.too-wide', [
|
||||
'width' => $size[0],
|
||||
'maxWidth' => $maxWidth,
|
||||
]);
|
||||
|
||||
return json($message, 1);
|
||||
}
|
||||
|
||||
if ($size[0] % 64 != 0 || $size[1] % 32 != 0) {
|
||||
$message = trans('skinlib.upload.invalid-size', [
|
||||
'type' => $type === 'cape' ? trans('general.cape') : trans('general.skin'),
|
||||
|
|
@ -253,8 +264,17 @@ class SkinlibController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$hash = hash_file('sha256', $file);
|
||||
$hash = $filter->apply('uploaded_texture_hash', $hash, [$file]);
|
||||
$image = Image::make($file);
|
||||
$imagick = $image->getCore();
|
||||
$imagick->setOption('png:compression-filter', '0');
|
||||
$imagick->setOption('png:compression-level', '9');
|
||||
$imagick->setOption('png:compression-strategy', '0');
|
||||
$imagick->setOption('png:exclude-chunk', 'all');
|
||||
$imagick->stripImage();
|
||||
$sanitized = $image->encode('png')->getEncoded();
|
||||
|
||||
$hash = hash('sha256', $image->encoded);
|
||||
$hash = $filter->apply('uploaded_texture_hash', $hash, [$image]);
|
||||
|
||||
/** @var User */
|
||||
$user = Auth::user();
|
||||
|
|
@ -270,11 +290,11 @@ class SkinlibController extends Controller
|
|||
return json(trans('skinlib.upload.repeated'), 2, ['tid' => $duplicated->tid]);
|
||||
}
|
||||
|
||||
$size = ceil($file->getSize() / 1024);
|
||||
$fileSize = ceil(strlen($sanitized) / 1024);
|
||||
$isPublic = is_string($data['public'])
|
||||
? $data['public'] === '1'
|
||||
: $data['public'];
|
||||
$cost = $size * (
|
||||
$cost = $fileSize * (
|
||||
$isPublic
|
||||
? option('score_per_storage')
|
||||
: option('private_score_per_storage')
|
||||
|
|
@ -285,13 +305,13 @@ class SkinlibController extends Controller
|
|||
return json(trans('skinlib.upload.lack-score'), 1);
|
||||
}
|
||||
|
||||
$dispatcher->dispatch('texture.uploading', [$file, $name, $hash]);
|
||||
$dispatcher->dispatch('texture.uploading', [$image, $name, $hash]);
|
||||
|
||||
$texture = new Texture();
|
||||
$texture->name = $name;
|
||||
$texture->type = $type;
|
||||
$texture->hash = $hash;
|
||||
$texture->size = $size;
|
||||
$texture->size = $fileSize;
|
||||
$texture->public = $isPublic;
|
||||
$texture->uploader = $user->uid;
|
||||
$texture->likes = 1;
|
||||
|
|
@ -300,14 +320,14 @@ class SkinlibController extends Controller
|
|||
/** @var FilesystemAdapter */
|
||||
$disk = Storage::disk('textures');
|
||||
if ($disk->missing($hash)) {
|
||||
$file->storePubliclyAs('', $hash, ['disk' => 'textures']);
|
||||
$disk->put($hash, $sanitized);
|
||||
}
|
||||
|
||||
$user->score -= $cost;
|
||||
$user->closet()->attach($texture->tid, ['item_name' => $name]);
|
||||
$user->save();
|
||||
|
||||
$dispatcher->dispatch('texture.uploaded', [$texture, $file]);
|
||||
$dispatcher->dispatch('texture.uploaded', [$texture, $image]);
|
||||
|
||||
return json(trans('skinlib.upload.success', ['name' => $name]), 0, [
|
||||
'tid' => $texture->tid,
|
||||
|
|
@ -386,7 +406,7 @@ class SkinlibController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Texture $texture
|
||||
Texture $texture,
|
||||
) {
|
||||
$data = $request->validate(['name' => [
|
||||
'required',
|
||||
|
|
@ -416,7 +436,7 @@ class SkinlibController extends Controller
|
|||
Request $request,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter,
|
||||
Texture $texture
|
||||
Texture $texture,
|
||||
) {
|
||||
$data = $request->validate([
|
||||
'type' => ['required', Rule::in(['steve', 'alex', 'cape'])],
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ use App\Models\Player;
|
|||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use Blessing\Minecraft;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Image;
|
||||
use Storage;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Intervention\Image\Facades\Image;
|
||||
|
||||
class TextureController extends Controller
|
||||
{
|
||||
|
|
@ -71,7 +71,8 @@ class TextureController extends Controller
|
|||
|
||||
$lastModified = $disk->lastModified($hash);
|
||||
|
||||
return Image::make($image)
|
||||
// TODO: refactor
|
||||
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make($image)
|
||||
->response($usePNG ? 'png' : 'webp', 100)
|
||||
->setLastModified(Carbon::createFromTimestamp($lastModified));
|
||||
}
|
||||
|
|
@ -145,7 +146,8 @@ class TextureController extends Controller
|
|||
|
||||
$disk = Storage::disk('textures');
|
||||
if (is_null($texture) || $disk->missing($texture->hash)) {
|
||||
return Image::make(resource_path("misc/textures/avatar$mode.png"))
|
||||
// TODO: refactor
|
||||
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make(resource_path("misc/textures/avatar$mode.png"))
|
||||
->resize($size, $size)
|
||||
->response($usePNG ? 'png' : 'webp', 100);
|
||||
}
|
||||
|
|
@ -165,7 +167,8 @@ class TextureController extends Controller
|
|||
|
||||
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
|
||||
|
||||
return Image::make($image)
|
||||
// TODO: refactor
|
||||
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make($image)
|
||||
->resize($size, $size)
|
||||
->response($usePNG ? 'png' : 'webp', 100)
|
||||
->setLastModified($lastModified);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class TranslationsController extends Controller
|
|||
Request $request,
|
||||
Application $app,
|
||||
JavaScript $js,
|
||||
LanguageLine $line
|
||||
LanguageLine $line,
|
||||
) {
|
||||
$data = $request->validate(['text' => 'required|string']);
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ class TranslationsController extends Controller
|
|||
public function delete(
|
||||
Application $app,
|
||||
JavaScript $js,
|
||||
LanguageLine $line
|
||||
LanguageLine $line,
|
||||
) {
|
||||
$line->delete();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ use App\Events\UserProfileUpdated;
|
|||
use App\Mail\EmailVerification;
|
||||
use App\Models\Texture;
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Blessing\Filter;
|
||||
use Blessing\Rejection;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
use Mail;
|
||||
use Session;
|
||||
use URL;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ class Kernel extends HttpKernel
|
|||
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
|
||||
\App\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\DetectLanguagePrefer::class,
|
||||
Middleware\ConvertEmptyStringsToNull::class,
|
||||
Middleware\DetectLanguagePrefer::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -33,8 +33,8 @@ class Kernel extends HttpKernel
|
|||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
|
||||
\App\Http\Middleware\EnforceEverGreen::class,
|
||||
\App\Http\Middleware\RedirectToSetup::class,
|
||||
Middleware\EnforceEverGreen::class,
|
||||
Middleware\RedirectToSetup::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
|
|
@ -44,9 +44,9 @@ class Kernel extends HttpKernel
|
|||
|
||||
'authorize' => [
|
||||
'auth:web',
|
||||
\App\Http\Middleware\RejectBannedUser::class,
|
||||
\App\Http\Middleware\EnsureEmailFilled::class,
|
||||
\App\Http\Middleware\FireUserAuthenticated::class,
|
||||
Middleware\RejectBannedUser::class,
|
||||
Middleware\EnsureEmailFilled::class,
|
||||
Middleware\FireUserAuthenticated::class,
|
||||
],
|
||||
];
|
||||
|
||||
|
|
@ -58,13 +58,13 @@ class Kernel extends HttpKernel
|
|||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth' => Middleware\Authenticate::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'role' => \App\Http\Middleware\CheckRole::class,
|
||||
'setup' => \App\Http\Middleware\CheckInstallation::class,
|
||||
'guest' => Middleware\RedirectIfAuthenticated::class,
|
||||
'role' => Middleware\CheckRole::class,
|
||||
'setup' => Middleware\CheckInstallation::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \App\Http\Middleware\CheckUserVerified::class,
|
||||
'verified' => Middleware\CheckUserVerified::class,
|
||||
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
|
||||
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ use Illuminate\Http\Request;
|
|||
class CheckRole
|
||||
{
|
||||
protected $roles = [
|
||||
'banned' => USER::BANNED,
|
||||
'normal' => USER::NORMAL,
|
||||
'admin' => USER::ADMIN,
|
||||
'super-admin' => USER::SUPER_ADMIN,
|
||||
'banned' => User::BANNED,
|
||||
'normal' => User::NORMAL,
|
||||
'admin' => User::ADMIN,
|
||||
'super-admin' => User::SUPER_ADMIN,
|
||||
];
|
||||
|
||||
public function handle(Request $request, Closure $next, $role)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ class DetectLanguagePrefer
|
|||
|
||||
/** @var Response */
|
||||
$response = $next($request);
|
||||
$response->cookie('locale', $locale, 120);
|
||||
if (!in_array('api', optional($request->route())->middleware() ?? [])) {
|
||||
$response->cookie('locale', $locale, 120);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class FootComposer
|
|||
Request $request,
|
||||
JavaScript $javascript,
|
||||
Dispatcher $dispatcher,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$this->request = $request;
|
||||
$this->javascript = $javascript;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class HeadComposer
|
|||
public function __construct(
|
||||
Dispatcher $dispatcher,
|
||||
Request $request,
|
||||
Filter $filter
|
||||
Filter $filter,
|
||||
) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->request = $request;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Listeners;
|
||||
|
||||
use App\Models\User;
|
||||
use Event;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class NotifyFailedPlugin
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Mail;
|
|||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Headers;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class EmailVerification extends Mailable
|
||||
|
|
@ -26,4 +27,13 @@ class EmailVerification extends Mailable
|
|||
->subject(trans('user.verification.mail.title', ['sitename' => $site_name]))
|
||||
->view('mails.email-verification');
|
||||
}
|
||||
|
||||
public function headers(): Headers
|
||||
{
|
||||
return new Headers(
|
||||
text: [
|
||||
'Auto-Submitted' => 'auto-generated',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Mail;
|
|||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Headers;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ForgotPassword extends Mailable
|
||||
|
|
@ -26,4 +27,13 @@ class ForgotPassword extends Mailable
|
|||
->subject(trans('auth.forgot.mail.title', ['sitename' => $site_name]))
|
||||
->view('mails.password-reset');
|
||||
}
|
||||
|
||||
public function headers(): Headers
|
||||
{
|
||||
return new Headers(
|
||||
text: [
|
||||
'Auto-Submitted' => 'auto-generated',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Events\PlayerProfileUpdated;
|
||||
use App\Models;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -57,17 +56,17 @@ class Player extends Model
|
|||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(Models\User::class, 'uid');
|
||||
return $this->belongsTo(User::class, 'uid');
|
||||
}
|
||||
|
||||
public function skin()
|
||||
{
|
||||
return $this->belongsTo(Models\Texture::class, 'tid_skin');
|
||||
return $this->belongsTo(Texture::class, 'tid_skin');
|
||||
}
|
||||
|
||||
public function cape()
|
||||
{
|
||||
return $this->belongsTo(Models\Texture::class, 'tid_cape');
|
||||
return $this->belongsTo(Texture::class, 'tid_cape');
|
||||
}
|
||||
|
||||
public function getModelAttribute()
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Scope;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Passport\Passport;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
|
|
@ -39,7 +41,19 @@ class AuthServiceProvider extends ServiceProvider
|
|||
'ReportsManagement.ReadWrite' => 'auth.oauth.scope.reports-management.readwrite',
|
||||
];
|
||||
|
||||
$scopes = Cache::get('scopes', []);
|
||||
/*
|
||||
* Return empty scopes if running unit tests or before installation.
|
||||
* In these cases, migrations aren’t run yet, so DB queries will fail.
|
||||
* OAuth isn’t tested in unit tests, so returning empty scopes should be fine...?
|
||||
* Maybe the best approach is to run migrations before bootstrap in tests,
|
||||
* but this seems impossible for DB_DATABASE=:memory:;
|
||||
* Or change how scopes are registered so they don't depend on the database,
|
||||
* but that may introduce BREAKING CHANGES and plugin incompatibility.
|
||||
* PRs welcome for better solutions!
|
||||
*/
|
||||
$scopes = (app()->runningUnitTests() || !Storage::disk('root')->exists('storage/install.lock')) ? [] : Cache::rememberForever('scopes', function () {
|
||||
return Scope::pluck('description', 'name')->toArray();
|
||||
});
|
||||
|
||||
Passport::tokensCan(array_merge($defaultScopes, $scopes));
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
|
||||
foreach ($router->getRoutes()->getRoutesByName() as $name => $route) {
|
||||
if (Str::startsWith($name, ['passport.authorizations', 'passport.tokens', 'passport.clients'])) {
|
||||
$route->middleware('verified');
|
||||
$route->middleware(['auth', 'verified']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use App\Events;
|
|||
use App\Notifications;
|
||||
use Blessing\Filter;
|
||||
use Closure;
|
||||
use Event;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Notification;
|
||||
|
||||
class Hook
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use DB;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Option
|
||||
{
|
||||
|
|
@ -20,13 +20,14 @@ class Option
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->items = DB::table('options')
|
||||
->get()
|
||||
->mapWithKeys(fn ($item) => [$item->option_name => $item->option_value]);
|
||||
} catch (QueryException $e) {
|
||||
if (!file_exists(storage_path('install.lock')) || app()->runningUnitTests()) {
|
||||
$this->items = collect();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->items = DB::table('options')
|
||||
->get()
|
||||
->mapWithKeys(fn ($item) => [$item->option_name => $item->option_value]);
|
||||
}
|
||||
|
||||
public function get($key, $default = null, $raw = false)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Services\Facades\Option;
|
||||
use BadMethodCallException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Option;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +59,7 @@ class OptionForm
|
|||
}
|
||||
|
||||
/**
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __call(string $method, array $params): OptionFormItem
|
||||
{
|
||||
|
|
@ -203,7 +203,7 @@ class OptionForm
|
|||
/**
|
||||
* Handle the HTTP post request and update modified options.
|
||||
*/
|
||||
public function handle(callable $callback = null): self
|
||||
public function handle(?callable $callback = null): self
|
||||
{
|
||||
$request = request();
|
||||
$allPostData = $request->all();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class PluginManager
|
|||
Application $app,
|
||||
Option $option,
|
||||
Dispatcher $dispatcher,
|
||||
Filesystem $filesystem
|
||||
Filesystem $filesystem,
|
||||
) {
|
||||
$this->app = $app;
|
||||
$this->option = $option;
|
||||
|
|
@ -366,7 +366,7 @@ class PluginManager
|
|||
*/
|
||||
public function formatUnresolved(
|
||||
Collection $unsatisfied,
|
||||
Collection $conflicts
|
||||
Collection $conflicts,
|
||||
): array {
|
||||
$unsatisfied = $unsatisfied->map(function ($detail, $name) {
|
||||
if ($name === 'blessing-skin-server') {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class JavaScript
|
|||
public function __construct(
|
||||
Filesystem $filesystem,
|
||||
Repository $cache,
|
||||
PluginManager $plugins
|
||||
PluginManager $plugins,
|
||||
) {
|
||||
$this->filesystem = $filesystem;
|
||||
$this->cache = $cache;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ ini_set('display_errors', true);
|
|||
'json',
|
||||
'fileinfo',
|
||||
'zip',
|
||||
'imagick',
|
||||
],
|
||||
'write_permission' => [
|
||||
'bootstrap/cache',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"php": "^8.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-imagick": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
|
|
|
|||
3861
composer.lock
generated
3861
composer.lock
generated
File diff suppressed because it is too large
Load Diff
20
config/image.php
Normal file
20
config/image.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Image Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Intervention Image supports "GD Library" and "Imagick" to process images
|
||||
| internally. You may choose one of them according to your PHP
|
||||
| configuration. By default PHP's "GD Library" implementation is used.
|
||||
|
|
||||
| Supported: "gd", "imagick"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => 'imagick'
|
||||
|
||||
];
|
||||
|
|
@ -33,7 +33,7 @@ return [
|
|||
*/
|
||||
'registry' => env(
|
||||
'PLUGINS_REGISTRY',
|
||||
'https://git.qvq.network/bs-community/plugins-dist/-/raw/master/registry_{lang}.json'
|
||||
'https://bs-plugins.littleservice.cn/registry_{lang}.json'
|
||||
),
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateAllTables extends Migration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
use App\Services\Facades\Option;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ImportOptions extends Migration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddVerificationToUsersTable extends Migration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use App\Services\Facades\Option;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Option::set('max_texture_width', 8192);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
}
|
||||
};
|
||||
160
package.json
160
package.json
|
|
@ -6,7 +6,6 @@
|
|||
"type": "git",
|
||||
"url": "https://github.com/bs-community/blessing-skin-server"
|
||||
},
|
||||
"type": "module",
|
||||
"author": "printempw",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
|
@ -22,86 +21,92 @@
|
|||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@hot-loader/react-dom": "^17.0.2",
|
||||
"@tweenjs/tween.js": "^21.0.0",
|
||||
"@emotion/react": "^11.0.0",
|
||||
"@emotion/styled": "^11.0.0",
|
||||
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||
"@hot-loader/react-dom": "^17.0.0",
|
||||
"@tweenjs/tween.js": "^18.5.0",
|
||||
"admin-lte": "^3.2.0",
|
||||
"blessing-skin-shell": "^0.3.4",
|
||||
"bootstrap": "^4.6.2",
|
||||
"cac": "6.7.14",
|
||||
"cli-spinners": "^2.9.0",
|
||||
"clsx": "^1.2.1",
|
||||
"echarts": "^5.4.2",
|
||||
"immer": "^10.0.2",
|
||||
"jquery": "^3.7.0",
|
||||
"bootstrap": "^4.6.1",
|
||||
"cac": "6.6.1",
|
||||
"cli-spinners": "^2.5.0",
|
||||
"clsx": "^1.1.1",
|
||||
"echarts": "^5.1.2",
|
||||
"events": "^3.2.0",
|
||||
"immer": "^7.0.4",
|
||||
"jquery": "^3.6.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"nanoid": "^4.0.2",
|
||||
"prompts": "^2.4.2",
|
||||
"react": "^17.0.2",
|
||||
"react-autosuggest": "^10.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^4.4.5",
|
||||
"react-hot-loader": "^4.13.1",
|
||||
"react-loading-skeleton": "^3.3.1",
|
||||
"nanoid": "^3.1.9",
|
||||
"prompts": "^2.4.0",
|
||||
"react": "^17.0.1",
|
||||
"react-autosuggest": "^10.0.2",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-draggable": "^4.4.2",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"react-loading-skeleton": "^2.1.1",
|
||||
"react-use": "^17.4.0",
|
||||
"reaptcha": "^1.12.1",
|
||||
"rxjs": "^7.8.1",
|
||||
"skinview-utils": "^0.7.0",
|
||||
"skinview3d": "3.0.0-alpha.1",
|
||||
"spectre.css": "^0.5.9",
|
||||
"use-immer": "^0.9.0",
|
||||
"xterm": "^5.2.1",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
"reaptcha": "^1.7.2",
|
||||
"rxjs": "^6.5.5",
|
||||
"skinview-utils": "^0.5.5",
|
||||
"skinview3d": "^3.0.0-alpha.1",
|
||||
"spectre.css": "^0.5.8",
|
||||
"use-immer": "^0.4.2",
|
||||
"xterm": "^4.6.0",
|
||||
"xterm-addon-fit": "^0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gplane/tsconfig": "^5.0.0",
|
||||
"@swc/register": "^0.1.10",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/bootstrap": "^5.2.6",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/jquery": "^3.5.16",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/prompts": "^2.4.4",
|
||||
"@types/react": "17",
|
||||
"@types/react-autosuggest": "^10.1.6",
|
||||
"@types/react-dom": "^17.0.20",
|
||||
"@types/testing-library__jest-dom": "^5.14.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
"@typescript-eslint/parser": "^5.61.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"css-loader": "^6.8.1",
|
||||
"css-minimizer-webpack-plugin": "^5.0.1",
|
||||
"eslint": "^8.44.0",
|
||||
"@gplane/tsconfig": "^4.2.0",
|
||||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"@types/bootstrap": "^4.3.3",
|
||||
"@types/css-minimizer-webpack-plugin": "^1.1.0",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/jquery": "^3.5.13",
|
||||
"@types/js-yaml": "^3.12.4",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/mini-css-extract-plugin": "^1.2.1",
|
||||
"@types/prompts": "^2.0.9",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-autosuggest": "^9.3.14",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/tween.js": "^18.5.0",
|
||||
"@types/webpack-dev-server": "^3.11.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
||||
"@typescript-eslint/parser": "^3.6.0",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"css-loader": "^5.2.6",
|
||||
"css-minimizer-webpack-plugin": "^3.0.1",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-formatter-beauty": "^3.0.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.6.1",
|
||||
"jest-environment-jsdom": "^29.6.1",
|
||||
"jest-extended": "^4.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"postcss": "^8.4.25",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"prettier": "^2.8.8",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^27.0.4",
|
||||
"jest-extended": "^0.11.5",
|
||||
"js-yaml": "^3.13.1",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"postcss": "^8.3.0",
|
||||
"postcss-loader": "^5.3.0",
|
||||
"prettier": "^2.3.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"style-loader": "^3.3.3",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-loader": "^9.4.4",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
"style-loader": "^2.0.0",
|
||||
"ts-jest": "^27.0.2",
|
||||
"ts-loader": "^9.2.2",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^4.3.2",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack-cli": "^4.7.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"kleur": "^4.1.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome >= 87",
|
||||
"Firefox >= 84",
|
||||
"iOS >= 12.5",
|
||||
"not dead"
|
||||
"> 1%",
|
||||
"not dead",
|
||||
"not ie 11",
|
||||
"Chrome > 52"
|
||||
],
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
|
|
@ -110,13 +115,17 @@
|
|||
"trailingComma": "all",
|
||||
"tabWidth": 2
|
||||
},
|
||||
"postcss": {
|
||||
"autoprefixer": {}
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"resetMocks": true,
|
||||
"testEnvironment": "jsdom",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"ts",
|
||||
"tsx",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.css$": "<rootDir>/resources/assets/tests/__mocks__/style.ts",
|
||||
"\\.(png|webp)$": "<rootDir>/resources/assets/tests/__mocks__/file.ts",
|
||||
|
|
@ -149,5 +158,6 @@
|
|||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
|
|
|||
11664
pnpm-lock.yaml
11664
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
5
postcss.config.js
Normal file
5
postcss.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer'),
|
||||
],
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
type AlertType = 'success' | 'info' | 'warning' | 'danger'
|
||||
|
||||
const icons = new Map<AlertType, string>([
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
onClick: React.MouseEventHandler<HTMLAnchorElement>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import * as fetch from '@/scripts/net'
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/** @jsxImportSource @emotion/react */
|
||||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import Autosuggest from 'react-autosuggest'
|
||||
import { css } from '@emotion/react'
|
||||
import { emit } from '@/scripts/event'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
const Loading = () => (
|
||||
<div className="container text-center" title="Loading...">
|
||||
<i className="fas fa-sync fa-spin"></i>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import $ from 'jquery'
|
||||
import 'bootstrap'
|
||||
import { t } from '../scripts/i18n'
|
||||
|
|
@ -66,14 +66,9 @@ const Modal: React.FC<ModalOptions & Props> = (props) => {
|
|||
return
|
||||
}
|
||||
|
||||
const onClose =
|
||||
props.onClose ||
|
||||
(() => {
|
||||
/* noop */
|
||||
})
|
||||
const onHidden = () => onClose()
|
||||
const onHidden = () => props.onClose?.()
|
||||
|
||||
const el = $(ref.current as HTMLElement)
|
||||
const el = $(ref.current!)
|
||||
el.on('hidden.bs.modal', onHidden)
|
||||
|
||||
return () => {
|
||||
|
|
@ -97,27 +92,28 @@ const Modal: React.FC<ModalOptions & Props> = (props) => {
|
|||
}
|
||||
|
||||
props.onConfirm?.({ value })
|
||||
$(ref.current as HTMLElement).modal('hide')
|
||||
$(ref.current!).modal('hide')
|
||||
|
||||
// The "hidden.bs.modal" event can't be trigged automatically when testing.
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
$(ref.current as HTMLElement).trigger('hidden.bs.modal')
|
||||
$(ref.current!).trigger('hidden.bs.modal')
|
||||
}
|
||||
}
|
||||
|
||||
const dismiss = () => {
|
||||
props.onDismiss?.()
|
||||
$(ref.current as HTMLElement).modal('hide')
|
||||
$(ref.current!).modal('hide')
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
$(ref.current as HTMLElement).trigger('hidden.bs.modal')
|
||||
$(ref.current!).trigger('hidden.bs.modal')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
setTimeout(() => $(ref.current as HTMLElement).modal('show'), 50)
|
||||
setTimeout(() => $(ref.current!).modal('show'), 50)
|
||||
}
|
||||
}, [show])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import ModalContent from './ModalContent'
|
||||
import ModalInput from './ModalInput'
|
||||
import type { Props as ContentProps } from './ModalContent'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
export interface Props {
|
||||
text?: string
|
||||
dangerousHTML?: string
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
export interface Props {
|
||||
flexFooter?: boolean
|
||||
okButtonText?: string
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
export interface Props {
|
||||
title?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { HTMLAttributes } from 'react'
|
||||
import React, { HTMLAttributes } from 'react'
|
||||
|
||||
export interface Props {
|
||||
inputType?: string
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import PaginationItem from './PaginationItem'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean
|
||||
active?: boolean
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/** @jsxImportSource @emotion/react */
|
||||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { css } from '@emotion/react'
|
||||
|
||||
export type ToastType = 'success' | 'info' | 'warning' | 'error'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/** @jsxImportSource @emotion/react */
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { useMeasure } from 'react-use'
|
||||
import { css } from '@emotion/react'
|
||||
import styled from '@emotion/styled'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
|
||||
const ViewerSkeleton: React.FC = () => (
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ if (route) {
|
|||
</React.Suspense>
|
||||
</React.StrictMode>
|
||||
)
|
||||
const c = document.querySelector(route.el)
|
||||
const c =
|
||||
typeof route.el === 'string' ? document.querySelector(route.el) : route.el
|
||||
ReactDOM.render(<Root />, c)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import styled from '@emotion/styled'
|
||||
import { Terminal } from 'xterm'
|
||||
|
|
@ -57,8 +57,10 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
|
|||
const terminal = new Terminal()
|
||||
const fitAddon = new FitAddon()
|
||||
terminal.loadAddon(fitAddon)
|
||||
terminal.options.fontFamily =
|
||||
'Monaco, Consolas, "Roboto Mono", "Noto Sans", "Droid Sans Mono"'
|
||||
terminal.setOption(
|
||||
'fontFamily',
|
||||
'Monaco, Consolas, "Roboto Mono", "Noto Sans", "Droid Sans Mono"',
|
||||
)
|
||||
terminal.open(el)
|
||||
fitAddon.fit()
|
||||
|
||||
|
|
@ -81,7 +83,6 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
|
|||
if (stack?.includes('outputHelp')) {
|
||||
terminal.writeln(data.replace(/\n/g, '\r\n'))
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
originalLogger(data, ...args)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default async function pacman(stdio: Stdio, args: string[]) {
|
|||
const { options } = program.parse(['', ''].concat(args), { run: false })
|
||||
|
||||
const opts: Options = options
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (opts.sync) {
|
||||
await install(opts.sync, stdio)
|
||||
} else if (opts.remove) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Stdio } from 'blessing-skin-shell'
|
||||
import * as event from '../event'
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function hackStdin() {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return process.stdin
|
||||
|
|
@ -23,6 +24,7 @@ export function hackStdin() {
|
|||
} as NodeJS.ReadStream & { _off(): void }
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function hackStdout(stdio: Stdio) {
|
||||
return {
|
||||
write(msg: string) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import * as React from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import EmailVerification from '@/views/widgets/EmailVerification'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export function getExtraData(): Record<string, any> {
|
||||
const jsonElement = document.querySelector('#blessing-extra')
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (jsonElement) {
|
||||
return JSON.parse(jsonElement.textContent ?? '{}')
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { getExtraData } from './extra'
|
|||
|
||||
export function scrollHander() {
|
||||
const header = document.querySelector('.navbar')
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (header) {
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.scrollY >= (window.innerHeight * 2) / 3) {
|
||||
|
|
@ -14,6 +14,7 @@ export function scrollHander() {
|
|||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const { transparent_navbar } = getExtraData() as {
|
||||
transparent_navbar: boolean
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import * as fetch from '../net'
|
||||
import { type Texture, TextureType } from '../types'
|
||||
import { Texture, TextureType } from '../types'
|
||||
|
||||
export default function useTexture() {
|
||||
const [tid, setTid] = useState(0)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export function t(key: string, parameters = Object.create(null)): string {
|
|||
let result = ''
|
||||
|
||||
for (const segment of segments) {
|
||||
/* istanbul ignore next */
|
||||
const middle = temp?.[segment]
|
||||
if (!middle) {
|
||||
return key
|
||||
|
|
@ -19,11 +20,9 @@ export function t(key: string, parameters = Object.create(null)): string {
|
|||
}
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
Object.keys(parameters).forEach(
|
||||
(slot) => (result = result.replace(`:${slot}`, parameters[slot])),
|
||||
)
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@ export async function logout() {
|
|||
}
|
||||
|
||||
const button = document.querySelector('#logout-button')
|
||||
|
||||
/* istanbul ignore next */
|
||||
button?.addEventListener('click', logout)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Modal, { type ModalOptions, type ModalResult } from '../components/Modal'
|
||||
import Modal, { ModalOptions, ModalResult } from '../components/Modal'
|
||||
|
||||
export function showModal(options: ModalOptions = {}): Promise<ModalResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export async function walkFetch(request: Request): Promise<any> {
|
|||
message = `${message}<br><details>${trace}</details>`
|
||||
}
|
||||
|
||||
throw new HTTPError(message || (body as string), cloned)
|
||||
throw new HTTPError(message || body, cloned)
|
||||
} catch (error: any) {
|
||||
emit('fetchError', error)
|
||||
await showModal({
|
||||
|
|
@ -95,10 +95,7 @@ export async function walkFetch(request: Request): Promise<any> {
|
|||
}
|
||||
}
|
||||
|
||||
export function get<T = any>(
|
||||
url: string,
|
||||
params: Record<string, any> = empty,
|
||||
): Promise<T> {
|
||||
export function get<T = any>(url: string, params = empty): Promise<T> {
|
||||
emit('beforeFetch', {
|
||||
method: 'GET',
|
||||
url,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import NotificationsList from '@/views/widgets/NotificationsList'
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Toast } from './toast'
|
|||
|
||||
export const toast = new Toast()
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
afterEach(() => {
|
||||
toast.clear()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import React from 'react'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'user',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { loadSkinToCanvas } from 'skinview-utils'
|
||||
|
||||
/* istanbul ignore next */
|
||||
function checkPixel(
|
||||
context: CanvasRenderingContext2D,
|
||||
x: number,
|
||||
|
|
@ -14,6 +15,7 @@ function checkPixel(
|
|||
)
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function isAlex(texture: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const image = new Image()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { nanoid } from 'nanoid'
|
||||
import * as emitter from './event'
|
||||
import ToastBox from '../components/Toast'
|
||||
import type { ToastType } from '../components/Toast'
|
||||
import ToastBox, { ToastType } from '../components/Toast'
|
||||
|
||||
type QueueElement = { id: string; type: ToastType; message: string }
|
||||
type ToastQueue = QueueElement[]
|
||||
|
|
|
|||
|
|
@ -38,13 +38,9 @@ export function registerNavbarPicker(
|
|||
|
||||
const navbar = document.querySelector<HTMLElement>('.wrapper > nav')
|
||||
const picker = document.querySelector<HTMLDivElement>('#navbar-color-picker')
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (navbar && picker) {
|
||||
registerNavbarPicker(
|
||||
navbar,
|
||||
picker,
|
||||
(blessing.extra.navbar as string) || 'white',
|
||||
)
|
||||
registerNavbarPicker(navbar, picker, blessing.extra.navbar || 'white')
|
||||
}
|
||||
|
||||
export function registerSidebarPicker(
|
||||
|
|
@ -77,10 +73,11 @@ const darkPicker = document.querySelector<HTMLDivElement>(
|
|||
const lightPicker = document.querySelector<HTMLDivElement>(
|
||||
'#sidebar-light-picker',
|
||||
)
|
||||
/* istanbul ignore next */
|
||||
if (sidebar && darkPicker && lightPicker) {
|
||||
registerSidebarPicker(
|
||||
sidebar,
|
||||
{ dark: darkPicker, light: lightPicker },
|
||||
(blessing.extra.sidebar as string) || 'dark-primary',
|
||||
blessing.extra.sidebar || 'dark-primary',
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import { showModal } from '@/scripts/notify'
|
||||
import type { Player } from '@/scripts/types'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import { Box } from './styles'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import { TextureType } from '@/scripts/types'
|
||||
import Modal from '@/components/Modal'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { Player } from '@/scripts/types'
|
||||
import ButtonEdit from '@/components/ButtonEdit'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useLayoutEffect } from 'react'
|
||||
import React, { useState, useEffect, useLayoutEffect } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { useImmer } from 'use-immer'
|
||||
import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen'
|
||||
|
|
@ -47,7 +47,6 @@ const PlayersManagement: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
getPlayers()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [page])
|
||||
|
||||
const handleModeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { Plugin } from './types'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { useImmer } from 'use-immer'
|
||||
import { t } from '@/scripts/i18n'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { Plugin } from './types'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useMemo } from 'react'
|
||||
import React, { useState, useEffect, useMemo } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { enableMapSet } from 'immer'
|
||||
import { useImmer } from 'use-immer'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { Texture } from '@/scripts/types'
|
||||
import { type Report, Status } from './types'
|
||||
import { Report, Status } from './types'
|
||||
|
||||
const Card = styled.div`
|
||||
width: 240px;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { hot } from 'react-hot-loader/root'
|
|||
import { useImmer } from 'use-immer'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import * as fetch from '@/scripts/net'
|
||||
import { type Paginator, type Texture, TextureType } from '@/scripts/types'
|
||||
import { Paginator, Texture, TextureType } from '@/scripts/types'
|
||||
import { toast, showModal } from '@/scripts/notify'
|
||||
import Loading from '@/components/Loading'
|
||||
import Pagination from '@/components/Pagination'
|
||||
|
|
@ -37,7 +37,6 @@ const ReportsManagement: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
getReports()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [page])
|
||||
|
||||
const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import styled from '@emotion/styled'
|
||||
|
||||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { Line } from './types'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { useImmer } from 'use-immer'
|
||||
import { t } from '@/scripts/i18n'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { post, type ResponseBody } from '../../scripts/net'
|
||||
import { post, ResponseBody } from '../../scripts/net'
|
||||
import { showModal } from '../../scripts/notify'
|
||||
import { t } from '../../scripts/i18n'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import { t } from '@/scripts/i18n'
|
||||
import type { User } from '@/scripts/types'
|
||||
import { Box, Icon, InfoTable } from './styles'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react'
|
||||
import styled from '@emotion/styled'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import { t } from '@/scripts/i18n'
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user