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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: 8.1
|
php-version: 8.3
|
||||||
coverage: none
|
coverage: none
|
||||||
extensions: mbstring, dom, fileinfo, gd
|
extensions: mbstring, dom, fileinfo, gd, imagick
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
composer install --prefer-dist --no-progress
|
composer install --prefer-dist --no-progress
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
php artisan key:generate
|
|
||||||
mkdir -p resources/views/overrides
|
mkdir -p resources/views/overrides
|
||||||
- name: Validate Twig templates
|
- name: Validate Twig templates
|
||||||
run: php artisan twig:lint -v
|
run: php artisan twig:lint -v
|
||||||
|
|
@ -45,24 +44,24 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php: ['8.1', '8.2']
|
php: ['8.2', '8.3']
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup PHP only
|
- name: Setup PHP only
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
if: matrix.php != '8.0'
|
if: matrix.php != '8.3'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
coverage: none
|
coverage: none
|
||||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip
|
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick
|
||||||
- name: Setup PHP with Xdebug
|
- name: Setup PHP with Xdebug
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
if: matrix.php == '8.0'
|
if: matrix.php == '8.3'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php }}
|
php-version: ${{ matrix.php }}
|
||||||
coverage: xdebug
|
coverage: xdebug
|
||||||
extensions: mbstring, dom, fileinfo, sqlite, gd, zip
|
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick
|
||||||
- name: Cache Composer dependencies
|
- name: Cache Composer dependencies
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
|
|
@ -72,14 +71,14 @@ jobs:
|
||||||
- name: Install Composer dependencies
|
- name: Install Composer dependencies
|
||||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
||||||
- name: Run tests only
|
- name: Run tests only
|
||||||
if: matrix.php != '8.0'
|
if: matrix.php != '8.3'
|
||||||
run: ./vendor/bin/phpunit
|
run: ./vendor/bin/phpunit
|
||||||
- name: Run tests with coverage report
|
- name: Run tests with coverage report
|
||||||
if: matrix.php == '8.0'
|
if: matrix.php == '8.3'
|
||||||
run: ./vendor/bin/phpunit --coverage-clover=coverage.xml
|
run: ./vendor/bin/phpunit --coverage-clover=coverage.xml
|
||||||
- name: Upload coverage report
|
- name: Upload coverage report
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
if: matrix.php == '8.0' && success()
|
if: matrix.php == '8.3' && success()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
name: github-actions
|
name: github-actions
|
||||||
|
|
@ -88,7 +87,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
- name: Run checks
|
- name: Run checks
|
||||||
|
|
@ -101,7 +100,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
run: yarn
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|
@ -115,8 +114,14 @@ jobs:
|
||||||
name: Snapshot Build
|
name: Snapshot Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
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
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Cache Node dependencies
|
- name: Cache Node dependencies
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
|
|
@ -138,11 +143,13 @@ jobs:
|
||||||
yarn build
|
yarn build
|
||||||
cp resources/assets/src/images/bg.webp public/app/
|
cp resources/assets/src/images/bg.webp public/app/
|
||||||
cp resources/assets/src/images/favicon.ico 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
|
id: short-sha
|
||||||
- name: Archive release
|
- 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
|
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
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
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
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Build and create archive
|
- name: Build and create archive
|
||||||
run: ./tools/release.ps1
|
run: ./tools/release.ps1
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -25,3 +25,5 @@ storage/options.php
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
.php-cs-fixer.cache
|
.php-cs-fixer.cache
|
||||||
resources/views/overrides
|
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",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Jest Tests",
|
"name": "Launch Jest Tests",
|
||||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||||
"args": ["${file}"],
|
"args": ["${file}"],
|
||||||
"internalConsoleOptions": "openOnSessionStart",
|
"internalConsoleOptions": "openOnSessionStart",
|
||||||
"skipFiles": [
|
"skipFiles": ["<node_internals>/**"]
|
||||||
"<node_internals>/**"
|
},
|
||||||
]
|
{
|
||||||
},
|
"type": "php",
|
||||||
{
|
"request": "launch",
|
||||||
"type": "php",
|
"name": "Launch with XDebug",
|
||||||
"request": "launch",
|
"ignore": ["**/vendor/**/*.php"]
|
||||||
"name": "Launch with XDebug",
|
},
|
||||||
"ignore": [
|
{
|
||||||
"**/vendor/**/*.php"
|
"type": "firefox",
|
||||||
]
|
"request": "launch",
|
||||||
},
|
"reAttach": true,
|
||||||
{
|
"name": "Launch with Firefox Debugger",
|
||||||
"type": "firefox",
|
"url": "http://localhost/",
|
||||||
"request": "launch",
|
"webRoot": "${workspaceFolder}",
|
||||||
"reAttach": true,
|
"pathMappings": [
|
||||||
"name": "Launch with Firefox Debugger",
|
{
|
||||||
"url": "http://localhost/",
|
"url": "webpack:///",
|
||||||
"webRoot": "${workspaceFolder}",
|
"path": "${workspaceFolder}/"
|
||||||
"pathMappings": [
|
}
|
||||||
{
|
]
|
||||||
"url": "webpack:///",
|
}
|
||||||
"path": "${workspaceFolder}/"
|
]
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
101
README-zh.md
101
README-zh.md
|
|
@ -52,6 +52,7 @@ Blessing Skin 对您的服务器有一定的要求。在大多数情况下,下
|
||||||
- JSON
|
- JSON
|
||||||
- fileinfo
|
- fileinfo
|
||||||
- zip
|
- zip
|
||||||
|
- Imagick
|
||||||
|
|
||||||
## 快速使用
|
## 快速使用
|
||||||
|
|
||||||
|
|
@ -61,105 +62,9 @@ Blessing Skin 对您的服务器有一定的要求。在大多数情况下,下
|
||||||
|
|
||||||
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 被推送时,我们的机器人将会在频道内发送一条消息来提示您能否拉取最新代码,以及拉取后应该做什么。
|
> 您可以订阅我们的 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
|
- JSON
|
||||||
- fileinfo
|
- fileinfo
|
||||||
- zip
|
- zip
|
||||||
|
- Imagick
|
||||||
|
|
||||||
## Quick Install
|
## 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.
|
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
|
## Build From Source
|
||||||
|
|
||||||
Please refer to [Manual Build](https://blessing.netlify.app/build.html).
|
Please refer to [Manual Build](https://blessing.netlify.app/build.html).
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ class Handler extends ExceptionHandler
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
$isFromPlugins = !app()->runningUnitTests()
|
$isFromPlugins = !app()->runningUnitTests()
|
||||||
&& Str::contains($trace['file'], resolve('plugins')->getPluginsDirs()->all());
|
&& Str::contains($trace['file'], resolve('plugins')->getPluginsDirs()->all());
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
return Str::startsWith($trace['file'], 'app') || $isFromPlugins;
|
return Str::startsWith($trace['file'], 'app') || $isFromPlugins;
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ class AdminController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
PluginManager $plugins,
|
PluginManager $plugins,
|
||||||
Filesystem $filesystem,
|
Filesystem $filesystem,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$db = config('database.connections.'.config('database.default'));
|
$db = config('database.connections.'.config('database.default'));
|
||||||
$dbType = Arr::get([
|
$dbType = Arr::get([
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ use App\Mail\ForgotPassword;
|
||||||
use App\Models\Player;
|
use App\Models\Player;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Rules;
|
use App\Rules;
|
||||||
use Auth;
|
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Cache;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Mail;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Session;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use URL;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use Vectorface\Whip\Whip;
|
use Vectorface\Whip\Whip;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
|
|
@ -50,7 +50,7 @@ class AuthController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Rules\Captcha $captcha,
|
Rules\Captcha $captcha,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'identification' => 'required',
|
'identification' => 'required',
|
||||||
|
|
@ -151,7 +151,7 @@ class AuthController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Rules\Captcha $captcha,
|
Rules\Captcha $captcha,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$can = $filter->apply('can_register', null);
|
$can = $filter->apply('can_register', null);
|
||||||
if ($can instanceof Rejection) {
|
if ($can instanceof Rejection) {
|
||||||
|
|
@ -248,7 +248,7 @@ class AuthController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Rules\Captcha $captcha,
|
Rules\Captcha $captcha,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'email' => 'required|email',
|
'email' => 'required|email',
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Auth;
|
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class ClosetController extends Controller
|
class ClosetController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -75,7 +75,7 @@ class ClosetController extends Controller
|
||||||
public function add(
|
public function add(
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
['tid' => $tid, 'name' => $name] = $request->validate([
|
['tid' => $tid, 'name' => $name] = $request->validate([
|
||||||
'tid' => 'required|integer',
|
'tid' => 'required|integer',
|
||||||
|
|
@ -132,7 +132,7 @@ class ClosetController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
$tid
|
$tid,
|
||||||
) {
|
) {
|
||||||
['name' => $name] = $request->validate(['name' => 'required']);
|
['name' => $name] = $request->validate(['name' => 'required']);
|
||||||
/** @var User */
|
/** @var User */
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,10 @@ class OptionsController extends Controller
|
||||||
->text('max_upload_file_size')->addon('KB')
|
->text('max_upload_file_size')->addon('KB')
|
||||||
->hint(trans('options.general.max_upload_file_size.hint', ['size' => ini_get('upload_max_filesize')]));
|
->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')
|
$form->select('player_name_rule')
|
||||||
->option('official', trans('options.general.player_name_rule.official'))
|
->option('official', trans('options.general.player_name_rule.official'))
|
||||||
->option('cjk', trans('options.general.player_name_rule.cjk'))
|
->option('cjk', trans('options.general.player_name_rule.cjk'))
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ use App\Models\Player;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Rules;
|
use App\Rules;
|
||||||
use Auth;
|
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class PlayerController extends Controller
|
class PlayerController extends Controller
|
||||||
|
|
@ -124,7 +124,7 @@ class PlayerController extends Controller
|
||||||
public function delete(
|
public function delete(
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Player $player
|
Player $player,
|
||||||
) {
|
) {
|
||||||
/** @var User */
|
/** @var User */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
@ -157,7 +157,7 @@ class PlayerController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Player $player
|
Player $player,
|
||||||
) {
|
) {
|
||||||
$name = $request->validate([
|
$name = $request->validate([
|
||||||
'name' => [
|
'name' => [
|
||||||
|
|
@ -194,7 +194,7 @@ class PlayerController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Player $player
|
Player $player,
|
||||||
) {
|
) {
|
||||||
/** @var User */
|
/** @var User */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
@ -234,7 +234,7 @@ class PlayerController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Player $player
|
Player $player,
|
||||||
) {
|
) {
|
||||||
$types = $request->input('type', []);
|
$types = $request->input('type', []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class PlayersManagementController extends Controller
|
||||||
public function name(
|
public function name(
|
||||||
Player $player,
|
Player $player,
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$name = $request->validate([
|
$name = $request->validate([
|
||||||
'player_name' => [
|
'player_name' => [
|
||||||
|
|
@ -70,7 +70,7 @@ class PlayersManagementController extends Controller
|
||||||
public function owner(
|
public function owner(
|
||||||
Player $player,
|
Player $player,
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$uid = $request->validate(['uid' => 'required|integer'])['uid'];
|
$uid = $request->validate(['uid' => 'required|integer'])['uid'];
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ class PlayersManagementController extends Controller
|
||||||
public function texture(
|
public function texture(
|
||||||
Player $player,
|
Player $player,
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'tid' => 'required|integer',
|
'tid' => 'required|integer',
|
||||||
|
|
@ -123,7 +123,7 @@ class PlayersManagementController extends Controller
|
||||||
|
|
||||||
public function delete(
|
public function delete(
|
||||||
Player $player,
|
Player $player,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$dispatcher->dispatch('player.deleting', [$player]);
|
$dispatcher->dispatch('player.deleting', [$player]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class ReportController extends Controller
|
||||||
public function review(
|
public function review(
|
||||||
Report $report,
|
Report $report,
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'action' => ['required', Rule::in(['delete', 'ban', 'reject'])],
|
'action' => ['required', Rule::in(['delete', 'ban', 'reject'])],
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class SetupController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Filesystem $filesystem,
|
Filesystem $filesystem,
|
||||||
Connection $connection,
|
Connection $connection,
|
||||||
DatabaseManager $manager
|
DatabaseManager $manager,
|
||||||
) {
|
) {
|
||||||
if ($request->isMethod('get')) {
|
if ($request->isMethod('get')) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -121,7 +121,7 @@ class SetupController extends Controller
|
||||||
'database/migrations',
|
'database/migrations',
|
||||||
'vendor/laravel/passport/database/migrations',
|
'vendor/laravel/passport/database/migrations',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$siteUrl = url('/');
|
$siteUrl = url('/');
|
||||||
if (Str::endsWith($siteUrl, '/index.php')) {
|
if (Str::endsWith($siteUrl, '/index.php')) {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Auth;
|
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
|
@ -12,10 +11,12 @@ use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Intervention\Image\Facades\Image;
|
||||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||||
use Storage;
|
|
||||||
|
|
||||||
class SkinlibController extends Controller
|
class SkinlibController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -189,7 +190,7 @@ class SkinlibController extends Controller
|
||||||
public function handleUpload(
|
public function handleUpload(
|
||||||
Request $request,
|
Request $request,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Dispatcher $dispatcher
|
Dispatcher $dispatcher,
|
||||||
) {
|
) {
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
if ($file && !$file->isValid()) {
|
if ($file && !$file->isValid()) {
|
||||||
|
|
@ -220,6 +221,16 @@ class SkinlibController extends Controller
|
||||||
$type = $data['type'];
|
$type = $data['type'];
|
||||||
$size = getimagesize($file);
|
$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) {
|
if ($size[0] % 64 != 0 || $size[1] % 32 != 0) {
|
||||||
$message = trans('skinlib.upload.invalid-size', [
|
$message = trans('skinlib.upload.invalid-size', [
|
||||||
'type' => $type === 'cape' ? trans('general.cape') : trans('general.skin'),
|
'type' => $type === 'cape' ? trans('general.cape') : trans('general.skin'),
|
||||||
|
|
@ -253,8 +264,17 @@ class SkinlibController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash = hash_file('sha256', $file);
|
$image = Image::make($file);
|
||||||
$hash = $filter->apply('uploaded_texture_hash', $hash, [$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 */
|
/** @var User */
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
@ -270,11 +290,11 @@ class SkinlibController extends Controller
|
||||||
return json(trans('skinlib.upload.repeated'), 2, ['tid' => $duplicated->tid]);
|
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'])
|
$isPublic = is_string($data['public'])
|
||||||
? $data['public'] === '1'
|
? $data['public'] === '1'
|
||||||
: $data['public'];
|
: $data['public'];
|
||||||
$cost = $size * (
|
$cost = $fileSize * (
|
||||||
$isPublic
|
$isPublic
|
||||||
? option('score_per_storage')
|
? option('score_per_storage')
|
||||||
: option('private_score_per_storage')
|
: option('private_score_per_storage')
|
||||||
|
|
@ -285,13 +305,13 @@ class SkinlibController extends Controller
|
||||||
return json(trans('skinlib.upload.lack-score'), 1);
|
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 = new Texture();
|
||||||
$texture->name = $name;
|
$texture->name = $name;
|
||||||
$texture->type = $type;
|
$texture->type = $type;
|
||||||
$texture->hash = $hash;
|
$texture->hash = $hash;
|
||||||
$texture->size = $size;
|
$texture->size = $fileSize;
|
||||||
$texture->public = $isPublic;
|
$texture->public = $isPublic;
|
||||||
$texture->uploader = $user->uid;
|
$texture->uploader = $user->uid;
|
||||||
$texture->likes = 1;
|
$texture->likes = 1;
|
||||||
|
|
@ -300,14 +320,14 @@ class SkinlibController extends Controller
|
||||||
/** @var FilesystemAdapter */
|
/** @var FilesystemAdapter */
|
||||||
$disk = Storage::disk('textures');
|
$disk = Storage::disk('textures');
|
||||||
if ($disk->missing($hash)) {
|
if ($disk->missing($hash)) {
|
||||||
$file->storePubliclyAs('', $hash, ['disk' => 'textures']);
|
$disk->put($hash, $sanitized);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->score -= $cost;
|
$user->score -= $cost;
|
||||||
$user->closet()->attach($texture->tid, ['item_name' => $name]);
|
$user->closet()->attach($texture->tid, ['item_name' => $name]);
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
$dispatcher->dispatch('texture.uploaded', [$texture, $file]);
|
$dispatcher->dispatch('texture.uploaded', [$texture, $image]);
|
||||||
|
|
||||||
return json(trans('skinlib.upload.success', ['name' => $name]), 0, [
|
return json(trans('skinlib.upload.success', ['name' => $name]), 0, [
|
||||||
'tid' => $texture->tid,
|
'tid' => $texture->tid,
|
||||||
|
|
@ -386,7 +406,7 @@ class SkinlibController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Texture $texture
|
Texture $texture,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate(['name' => [
|
$data = $request->validate(['name' => [
|
||||||
'required',
|
'required',
|
||||||
|
|
@ -416,7 +436,7 @@ class SkinlibController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter,
|
Filter $filter,
|
||||||
Texture $texture
|
Texture $texture,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'type' => ['required', Rule::in(['steve', 'alex', 'cape'])],
|
'type' => ['required', Rule::in(['steve', 'alex', 'cape'])],
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ use App\Models\Player;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Blessing\Minecraft;
|
use Blessing\Minecraft;
|
||||||
use Cache;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Image;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Intervention\Image\Facades\Image;
|
||||||
|
|
||||||
class TextureController extends Controller
|
class TextureController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -71,7 +71,8 @@ class TextureController extends Controller
|
||||||
|
|
||||||
$lastModified = $disk->lastModified($hash);
|
$lastModified = $disk->lastModified($hash);
|
||||||
|
|
||||||
return Image::make($image)
|
// TODO: refactor
|
||||||
|
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make($image)
|
||||||
->response($usePNG ? 'png' : 'webp', 100)
|
->response($usePNG ? 'png' : 'webp', 100)
|
||||||
->setLastModified(Carbon::createFromTimestamp($lastModified));
|
->setLastModified(Carbon::createFromTimestamp($lastModified));
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +146,8 @@ class TextureController extends Controller
|
||||||
|
|
||||||
$disk = Storage::disk('textures');
|
$disk = Storage::disk('textures');
|
||||||
if (is_null($texture) || $disk->missing($texture->hash)) {
|
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)
|
->resize($size, $size)
|
||||||
->response($usePNG ? 'png' : 'webp', 100);
|
->response($usePNG ? 'png' : 'webp', 100);
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +167,8 @@ class TextureController extends Controller
|
||||||
|
|
||||||
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
|
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
|
||||||
|
|
||||||
return Image::make($image)
|
// TODO: refactor
|
||||||
|
return \Intervention\Image\ImageManagerStatic::configure(['driver' => 'gd'])->make($image)
|
||||||
->resize($size, $size)
|
->resize($size, $size)
|
||||||
->response($usePNG ? 'png' : 'webp', 100)
|
->response($usePNG ? 'png' : 'webp', 100)
|
||||||
->setLastModified($lastModified);
|
->setLastModified($lastModified);
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class TranslationsController extends Controller
|
||||||
Request $request,
|
Request $request,
|
||||||
Application $app,
|
Application $app,
|
||||||
JavaScript $js,
|
JavaScript $js,
|
||||||
LanguageLine $line
|
LanguageLine $line,
|
||||||
) {
|
) {
|
||||||
$data = $request->validate(['text' => 'required|string']);
|
$data = $request->validate(['text' => 'required|string']);
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ class TranslationsController extends Controller
|
||||||
public function delete(
|
public function delete(
|
||||||
Application $app,
|
Application $app,
|
||||||
JavaScript $js,
|
JavaScript $js,
|
||||||
LanguageLine $line
|
LanguageLine $line,
|
||||||
) {
|
) {
|
||||||
$line->delete();
|
$line->delete();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@ use App\Events\UserProfileUpdated;
|
||||||
use App\Mail\EmailVerification;
|
use App\Mail\EmailVerification;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Auth;
|
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
use Illuminate\Http\Request;
|
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 League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||||
use Mail;
|
|
||||||
use Session;
|
|
||||||
use URL;
|
|
||||||
|
|
||||||
class UserController extends Controller
|
class UserController extends Controller
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ class Kernel extends HttpKernel
|
||||||
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
|
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
|
||||||
\App\Http\Middleware\ConvertEmptyStringsToNull::class,
|
Middleware\ConvertEmptyStringsToNull::class,
|
||||||
\App\Http\Middleware\DetectLanguagePrefer::class,
|
Middleware\DetectLanguagePrefer::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,8 +33,8 @@ class Kernel extends HttpKernel
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
|
\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\App\Http\Middleware\EnforceEverGreen::class,
|
Middleware\EnforceEverGreen::class,
|
||||||
\App\Http\Middleware\RedirectToSetup::class,
|
Middleware\RedirectToSetup::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -44,9 +44,9 @@ class Kernel extends HttpKernel
|
||||||
|
|
||||||
'authorize' => [
|
'authorize' => [
|
||||||
'auth:web',
|
'auth:web',
|
||||||
\App\Http\Middleware\RejectBannedUser::class,
|
Middleware\RejectBannedUser::class,
|
||||||
\App\Http\Middleware\EnsureEmailFilled::class,
|
Middleware\EnsureEmailFilled::class,
|
||||||
\App\Http\Middleware\FireUserAuthenticated::class,
|
Middleware\FireUserAuthenticated::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -58,13 +58,13 @@ class Kernel extends HttpKernel
|
||||||
* @var array<string, class-string|string>
|
* @var array<string, class-string|string>
|
||||||
*/
|
*/
|
||||||
protected $middlewareAliases = [
|
protected $middlewareAliases = [
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
'auth' => Middleware\Authenticate::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => Middleware\RedirectIfAuthenticated::class,
|
||||||
'role' => \App\Http\Middleware\CheckRole::class,
|
'role' => Middleware\CheckRole::class,
|
||||||
'setup' => \App\Http\Middleware\CheckInstallation::class,
|
'setup' => Middleware\CheckInstallation::class,
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
'verified' => \App\Http\Middleware\CheckUserVerified::class,
|
'verified' => Middleware\CheckUserVerified::class,
|
||||||
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
|
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
|
||||||
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
|
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ use Illuminate\Http\Request;
|
||||||
class CheckRole
|
class CheckRole
|
||||||
{
|
{
|
||||||
protected $roles = [
|
protected $roles = [
|
||||||
'banned' => USER::BANNED,
|
'banned' => User::BANNED,
|
||||||
'normal' => USER::NORMAL,
|
'normal' => User::NORMAL,
|
||||||
'admin' => USER::ADMIN,
|
'admin' => User::ADMIN,
|
||||||
'super-admin' => USER::SUPER_ADMIN,
|
'super-admin' => User::SUPER_ADMIN,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function handle(Request $request, Closure $next, $role)
|
public function handle(Request $request, Closure $next, $role)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ class DetectLanguagePrefer
|
||||||
|
|
||||||
/** @var Response */
|
/** @var Response */
|
||||||
$response = $next($request);
|
$response = $next($request);
|
||||||
$response->cookie('locale', $locale, 120);
|
if (!in_array('api', optional($request->route())->middleware() ?? [])) {
|
||||||
|
$response->cookie('locale', $locale, 120);
|
||||||
|
}
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class FootComposer
|
||||||
Request $request,
|
Request $request,
|
||||||
JavaScript $javascript,
|
JavaScript $javascript,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->javascript = $javascript;
|
$this->javascript = $javascript;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class HeadComposer
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Request $request,
|
Request $request,
|
||||||
Filter $filter
|
Filter $filter,
|
||||||
) {
|
) {
|
||||||
$this->dispatcher = $dispatcher;
|
$this->dispatcher = $dispatcher;
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Listeners;
|
namespace App\Listeners;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class NotifyFailedPlugin
|
class NotifyFailedPlugin
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Mail;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Headers;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class EmailVerification extends Mailable
|
class EmailVerification extends Mailable
|
||||||
|
|
@ -26,4 +27,13 @@ class EmailVerification extends Mailable
|
||||||
->subject(trans('user.verification.mail.title', ['sitename' => $site_name]))
|
->subject(trans('user.verification.mail.title', ['sitename' => $site_name]))
|
||||||
->view('mails.email-verification');
|
->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\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Headers;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ForgotPassword extends Mailable
|
class ForgotPassword extends Mailable
|
||||||
|
|
@ -26,4 +27,13 @@ class ForgotPassword extends Mailable
|
||||||
->subject(trans('auth.forgot.mail.title', ['sitename' => $site_name]))
|
->subject(trans('auth.forgot.mail.title', ['sitename' => $site_name]))
|
||||||
->view('mails.password-reset');
|
->view('mails.password-reset');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function headers(): Headers
|
||||||
|
{
|
||||||
|
return new Headers(
|
||||||
|
text: [
|
||||||
|
'Auto-Submitted' => 'auto-generated',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Events\PlayerProfileUpdated;
|
use App\Events\PlayerProfileUpdated;
|
||||||
use App\Models;
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -57,17 +56,17 @@ class Player extends Model
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Models\User::class, 'uid');
|
return $this->belongsTo(User::class, 'uid');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function skin()
|
public function skin()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Models\Texture::class, 'tid_skin');
|
return $this->belongsTo(Texture::class, 'tid_skin');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cape()
|
public function cape()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Models\Texture::class, 'tid_cape');
|
return $this->belongsTo(Texture::class, 'tid_cape');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getModelAttribute()
|
public function getModelAttribute()
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\Scope;
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Laravel\Passport\Passport;
|
use Laravel\Passport\Passport;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
|
@ -39,7 +41,19 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
'ReportsManagement.ReadWrite' => 'auth.oauth.scope.reports-management.readwrite',
|
'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));
|
Passport::tokensCan(array_merge($defaultScopes, $scopes));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
|
|
||||||
foreach ($router->getRoutes()->getRoutesByName() as $name => $route) {
|
foreach ($router->getRoutes()->getRoutesByName() as $name => $route) {
|
||||||
if (Str::startsWith($name, ['passport.authorizations', 'passport.tokens', 'passport.clients'])) {
|
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 App\Notifications;
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Event;
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Notification;
|
|
||||||
|
|
||||||
class Hook
|
class Hook
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use DB;
|
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class Option
|
class Option
|
||||||
{
|
{
|
||||||
|
|
@ -20,13 +20,14 @@ class Option
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (!file_exists(storage_path('install.lock')) || app()->runningUnitTests()) {
|
||||||
$this->items = DB::table('options')
|
|
||||||
->get()
|
|
||||||
->mapWithKeys(fn ($item) => [$item->option_name => $item->option_value]);
|
|
||||||
} catch (QueryException $e) {
|
|
||||||
$this->items = collect();
|
$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)
|
public function get($key, $default = null, $raw = false)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Services\Facades\Option;
|
||||||
use BadMethodCallException;
|
use BadMethodCallException;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Option;
|
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -59,7 +59,7 @@ class OptionForm
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \BadMethodCallException
|
* @throws BadMethodCallException
|
||||||
*/
|
*/
|
||||||
public function __call(string $method, array $params): OptionFormItem
|
public function __call(string $method, array $params): OptionFormItem
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +203,7 @@ class OptionForm
|
||||||
/**
|
/**
|
||||||
* Handle the HTTP post request and update modified options.
|
* 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();
|
$request = request();
|
||||||
$allPostData = $request->all();
|
$allPostData = $request->all();
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class PluginManager
|
||||||
Application $app,
|
Application $app,
|
||||||
Option $option,
|
Option $option,
|
||||||
Dispatcher $dispatcher,
|
Dispatcher $dispatcher,
|
||||||
Filesystem $filesystem
|
Filesystem $filesystem,
|
||||||
) {
|
) {
|
||||||
$this->app = $app;
|
$this->app = $app;
|
||||||
$this->option = $option;
|
$this->option = $option;
|
||||||
|
|
@ -366,7 +366,7 @@ class PluginManager
|
||||||
*/
|
*/
|
||||||
public function formatUnresolved(
|
public function formatUnresolved(
|
||||||
Collection $unsatisfied,
|
Collection $unsatisfied,
|
||||||
Collection $conflicts
|
Collection $conflicts,
|
||||||
): array {
|
): array {
|
||||||
$unsatisfied = $unsatisfied->map(function ($detail, $name) {
|
$unsatisfied = $unsatisfied->map(function ($detail, $name) {
|
||||||
if ($name === 'blessing-skin-server') {
|
if ($name === 'blessing-skin-server') {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class JavaScript
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Filesystem $filesystem,
|
Filesystem $filesystem,
|
||||||
Repository $cache,
|
Repository $cache,
|
||||||
PluginManager $plugins
|
PluginManager $plugins,
|
||||||
) {
|
) {
|
||||||
$this->filesystem = $filesystem;
|
$this->filesystem = $filesystem;
|
||||||
$this->cache = $cache;
|
$this->cache = $cache;
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ ini_set('display_errors', true);
|
||||||
'json',
|
'json',
|
||||||
'fileinfo',
|
'fileinfo',
|
||||||
'zip',
|
'zip',
|
||||||
|
'imagick',
|
||||||
],
|
],
|
||||||
'write_permission' => [
|
'write_permission' => [
|
||||||
'bootstrap/cache',
|
'bootstrap/cache',
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"php": "^8.1",
|
"php": "^8.1",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
|
"ext-imagick": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"ext-openssl": "*",
|
"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(
|
'registry' => env(
|
||||||
'PLUGINS_REGISTRY',
|
'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\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class CreateAllTables extends Migration
|
class CreateAllTables extends Migration
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Services\Facades\Option;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class ImportOptions extends Migration
|
class ImportOptions extends Migration
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class AddVerificationToUsersTable extends Migration
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -158,5 +158,6 @@
|
||||||
"isolatedModules": true
|
"isolatedModules": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,10 @@ general:
|
||||||
regs_per_ip: Max accounts of one IP
|
regs_per_ip: Max accounts of one IP
|
||||||
max_upload_file_size:
|
max_upload_file_size:
|
||||||
title: Max Upload Size
|
title: Max Upload Size
|
||||||
hint: "Limit specified in php.ini: :size"
|
hint: 'Limit specified in php.ini: :size'
|
||||||
|
max_texture_width:
|
||||||
|
title: Max Texture Width
|
||||||
|
hint: Maximum width of uploaded textures, must be an integer multiple of 64
|
||||||
player_name_rule:
|
player_name_rule:
|
||||||
title: Player Name Rule
|
title: Player Name Rule
|
||||||
official: Letters, numbers and underscores (Mojang's official rule)
|
official: Letters, numbers and underscores (Mojang's official rule)
|
||||||
|
|
@ -129,8 +132,8 @@ general:
|
||||||
label: Delete invalid textures automatically.
|
label: Delete invalid textures automatically.
|
||||||
hint: Delete textures records whose file no longer exists from skinlib.
|
hint: Delete textures records whose file no longer exists from skinlib.
|
||||||
allow_downloading_texture:
|
allow_downloading_texture:
|
||||||
title: Downloading Textures
|
title: Downloading Textures
|
||||||
label: Allow users to directly download the source file of a skinlib item.
|
label: Allow users to directly download the source file of a skinlib item.
|
||||||
status_code_for_private:
|
status_code_for_private:
|
||||||
title: HTTP Code for Rejecting Accessing Private Textures
|
title: HTTP Code for Rejecting Accessing Private Textures
|
||||||
texture_name_regexp:
|
texture_name_regexp:
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ upload:
|
||||||
private-score-notice: It will spend you more scores for setting it as private. You will be charged :score scores for per KB storage.
|
private-score-notice: It will spend you more scores for setting it as private. You will be charged :score scores for per KB storage.
|
||||||
invalid-size: Invalid :type file (width :width, height :height)
|
invalid-size: Invalid :type file (width :width, height :height)
|
||||||
invalid-hd-skin: Invalid HD skin (width and height should be divisible by 32)
|
invalid-hd-skin: Invalid HD skin (width and height should be divisible by 32)
|
||||||
|
too-wide: The texture is too wide (:widthpx). Maximum width allowed is :maxWidthpx
|
||||||
lack-score: You don't have enough score to upload this texture.
|
lack-score: You don't have enough score to upload this texture.
|
||||||
repeated: The texture is already uploaded by someone else. You can add it to your closet directly.
|
repeated: The texture is already uploaded by someone else. You can add it to your closet directly.
|
||||||
success: Texture :name was uploaded successfully.
|
success: Texture :name was uploaded successfully.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ homepage:
|
||||||
label: 开启后背景不会随页面滚动而滚动
|
label: 开启后背景不会随页面滚动而滚动
|
||||||
copyright_prefer:
|
copyright_prefer:
|
||||||
title: 程序版权信息
|
title: 程序版权信息
|
||||||
description: "每种支持的语言都可以对应不同的程序版权信息,如果想要编辑某种特定语言下的版权信息,请在右上角切换至该语言后再提交修改。<b>对于任何恶意修改页面右下角的版权信息(包括不限于删除、修改作者信息、修改链接指向)的用户,作者保留对其追究责任的权利。</b>"
|
description: '每种支持的语言都可以对应不同的程序版权信息,如果想要编辑某种特定语言下的版权信息,请在右上角切换至该语言后再提交修改。<b>对于任何恶意修改页面右下角的版权信息(包括不限于删除、修改作者信息、修改链接指向)的用户,作者保留对其追究责任的权利。</b>'
|
||||||
copyright_text:
|
copyright_text:
|
||||||
title: 自定义版权文字
|
title: 自定义版权文字
|
||||||
description: 自定义版权文字内可使用占位符,<code>{site_name}</code> 将会被自动替换为站点名称,<code>{site_url}</code> 会被替换为站点地址。每种支持的语言都可以对应不同的自定义版权文字,如果想要编辑某种特定语言下的版权文字,请在右上角切换至该语言后再提交修改。
|
description: 自定义版权文字内可使用占位符,<code>{site_name}</code> 将会被自动替换为站点名称,<code>{site_url}</code> 会被替换为站点地址。每种支持的语言都可以对应不同的自定义版权文字,如果想要编辑某种特定语言下的版权文字,请在右上角切换至该语言后再提交修改。
|
||||||
|
|
@ -97,7 +97,10 @@ general:
|
||||||
regs_per_ip: 每个 IP 限制注册数
|
regs_per_ip: 每个 IP 限制注册数
|
||||||
max_upload_file_size:
|
max_upload_file_size:
|
||||||
title: 最大允许上传大小
|
title: 最大允许上传大小
|
||||||
hint: "PHP 限制::size,定义在 php.ini 中。"
|
hint: 'PHP 限制::size,定义在 php.ini 中。'
|
||||||
|
max_texture_width:
|
||||||
|
title: 最大允许材质宽度
|
||||||
|
hint: 允许上传的材质的最大的宽度,必须是 64 的整数倍
|
||||||
player_name_rule:
|
player_name_rule:
|
||||||
title: 角色名规则
|
title: 角色名规则
|
||||||
official: 大小写字母数字下划线(Mojang 官方的用户名规则)
|
official: 大小写字母数字下划线(Mojang 官方的用户名规则)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ upload:
|
||||||
private-score-notice: 私密材质将会消耗更多的积分:每 KB 存储空间 :score 积分
|
private-score-notice: 私密材质将会消耗更多的积分:每 KB 存储空间 :score 积分
|
||||||
invalid-size: 不是有效的 :type 文件(宽 :width,高 :height)
|
invalid-size: 不是有效的 :type 文件(宽 :width,高 :height)
|
||||||
invalid-hd-skin: 不是有效的高清皮肤(宽和高不是 32 的整数倍)
|
invalid-hd-skin: 不是有效的高清皮肤(宽和高不是 32 的整数倍)
|
||||||
|
too-wide: 材质过宽(:widthpx),本站允许的最大宽度为 :maxWidthpx
|
||||||
lack-score: 积分不足
|
lack-score: 积分不足
|
||||||
repeated: 已经有人上传过这个材质了,直接添加到衣柜使用吧~
|
repeated: 已经有人上传过这个材质了,直接添加到衣柜使用吧~
|
||||||
success: 材质 :name 上传成功
|
success: 材质 :name 上传成功
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::any('', 'HomeController@apiRoot');
|
Route::any('', 'HomeController@apiRoot');
|
||||||
|
|
||||||
Route::prefix('user')->middleware('auth:oauth')->group(function () {
|
Route::prefix('user')->middleware('auth:oauth')->group(function () {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('{player}.json', 'TextureController@json');
|
Route::get('{player}.json', 'TextureController@json');
|
||||||
Route::get('csl/{player}.json', 'TextureController@json');
|
Route::get('csl/{player}.json', 'TextureController@json');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Middleware;
|
use App\Http\Middleware;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,18 @@ class UpdateCommandTest extends TestCase
|
||||||
->once()
|
->once()
|
||||||
->andReturn(true);
|
->andReturn(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Yeah I know it's FUCKING UGLY
|
||||||
|
* But it's the only FUCKING way that WORKS
|
||||||
|
* SOMEONE REFACTOR THIS SHIT PLEASE, I BEG
|
||||||
|
*/
|
||||||
Cache::partialMock()->shouldReceive('flush')->once();
|
Cache::partialMock()->shouldReceive('flush')->once();
|
||||||
|
$mock = \Mockery::mock(\Illuminate\Contracts\Cache\Repository::class);
|
||||||
|
$mock->shouldReceive('put');
|
||||||
|
$mock->shouldReceive('get');
|
||||||
|
$this->app->instance('cache.store', $mock);
|
||||||
|
|
||||||
option(['version' => '0.0.0']);
|
option(['version' => '0.0.0']);
|
||||||
config([
|
config([
|
||||||
'app.version' => '0.0.1',
|
'app.version' => '0.0.1',
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ use App\Models\Player;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Rules\Captcha;
|
use App\Rules\Captcha;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Cache;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\URL;
|
use Illuminate\Support\Facades\URL;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -25,7 +25,7 @@ class AuthControllerTest extends TestCase
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
app()->instance(Captcha::class, new class() extends Captcha {
|
app()->instance(Captcha::class, new class extends Captcha {
|
||||||
public function passes($attribute, $value)
|
public function passes($attribute, $value)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -35,7 +35,7 @@ class AuthControllerTest extends TestCase
|
||||||
|
|
||||||
public function testLogin()
|
public function testLogin()
|
||||||
{
|
{
|
||||||
$filter = Fakes\Filter::fake();
|
$filter = Filter::fake();
|
||||||
|
|
||||||
$this->get('/auth/login')->assertSee('Log in');
|
$this->get('/auth/login')->assertSee('Log in');
|
||||||
$filter->assertApplied('auth_page_rows:login');
|
$filter->assertApplied('auth_page_rows:login');
|
||||||
|
|
@ -60,28 +60,28 @@ class AuthControllerTest extends TestCase
|
||||||
// Should return a warning if length of `password` is lower than 6
|
// Should return a warning if length of `password` is lower than 6
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
'password' => '123',
|
'password' => '123',
|
||||||
])->assertJsonValidationErrors('password');
|
])->assertJsonValidationErrors('password');
|
||||||
|
|
||||||
// Should return a warning if length of `password` is greater than 32
|
// Should return a warning if length of `password` is greater than 32
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
'password' => Str::random(80),
|
'password' => Str::random(80),
|
||||||
])->assertJsonValidationErrors('password');
|
])->assertJsonValidationErrors('password');
|
||||||
|
|
||||||
$this->flushSession();
|
$this->flushSession();
|
||||||
|
|
||||||
// Should return a warning if user isn't existed
|
// Should return a warning if user isn't existed
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => 'nope@nope.net',
|
'identification' => 'nope@nope.net',
|
||||||
'password' => '12345678',
|
'password' => '12345678',
|
||||||
])->assertJson([
|
])->assertJson([
|
||||||
'code' => 2,
|
'code' => 2,
|
||||||
'message' => trans('auth.validation.user'),
|
'message' => trans('auth.validation.user'),
|
||||||
]);
|
]);
|
||||||
Event::assertDispatched('auth.login.attempt', function ($event, $payload) {
|
Event::assertDispatched('auth.login.attempt', function ($event, $payload) {
|
||||||
$this->assertEquals('nope@nope.net', $payload[0]);
|
$this->assertEquals('nope@nope.net', $payload[0]);
|
||||||
$this->assertEquals('12345678', $payload[1]);
|
$this->assertEquals('12345678', $payload[1]);
|
||||||
|
|
@ -103,15 +103,15 @@ class AuthControllerTest extends TestCase
|
||||||
// Logging in should be failed if password is wrong
|
// Logging in should be failed if password is wrong
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
'password' => 'wrong-password',
|
'password' => 'wrong-password',
|
||||||
])->assertJson(
|
])->assertJson(
|
||||||
[
|
[
|
||||||
'code' => 1,
|
'code' => 1,
|
||||||
'message' => trans('auth.validation.password'),
|
'message' => trans('auth.validation.password'),
|
||||||
'data' => ['login_fails' => 1],
|
'data' => ['login_fails' => 1],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$filter->assertApplied('client_ip', function ($value) use ($ip) {
|
$filter->assertApplied('client_ip', function ($value) use ($ip) {
|
||||||
$this->assertEquals($ip, $value);
|
$this->assertEquals($ip, $value);
|
||||||
|
|
||||||
|
|
@ -143,8 +143,8 @@ class AuthControllerTest extends TestCase
|
||||||
Cache::put($loginFailsCacheKey, 4);
|
Cache::put($loginFailsCacheKey, 4);
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $user->email,
|
'identification' => $user->email,
|
||||||
'password' => '12345678',
|
'password' => '12345678',
|
||||||
])->assertJsonValidationErrors('captcha');
|
])->assertJsonValidationErrors('captcha');
|
||||||
|
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
|
|
@ -183,9 +183,9 @@ class AuthControllerTest extends TestCase
|
||||||
auth()->logout();
|
auth()->logout();
|
||||||
$this->postJson(
|
$this->postJson(
|
||||||
'/auth/login', [
|
'/auth/login', [
|
||||||
'identification' => $player->name,
|
'identification' => $player->name,
|
||||||
'password' => '12345678',
|
'password' => '12345678',
|
||||||
]
|
]
|
||||||
)->assertJson(
|
)->assertJson(
|
||||||
[
|
[
|
||||||
'code' => 0,
|
'code' => 0,
|
||||||
|
|
@ -240,7 +240,7 @@ class AuthControllerTest extends TestCase
|
||||||
|
|
||||||
public function testRegister()
|
public function testRegister()
|
||||||
{
|
{
|
||||||
$filter = Fakes\Filter::fake();
|
$filter = Filter::fake();
|
||||||
|
|
||||||
$this->get('/auth/register')->assertSee('Register');
|
$this->get('/auth/register')->assertSee('Register');
|
||||||
$filter->assertApplied('auth_page_rows:register');
|
$filter->assertApplied('auth_page_rows:register');
|
||||||
|
|
|
||||||
|
|
@ -41,20 +41,20 @@ class ClosetControllerTest extends TestCase
|
||||||
$user->closet()->attach($cape->tid, ['item_name' => 'custom_name']);
|
$user->closet()->attach($cape->tid, ['item_name' => 'custom_name']);
|
||||||
$this->getJson('/user/closet/list?category=cape')
|
$this->getJson('/user/closet/list?category=cape')
|
||||||
->assertJson(['data' => [[
|
->assertJson(['data' => [[
|
||||||
'tid' => $cape->tid,
|
'tid' => $cape->tid,
|
||||||
'type' => 'cape',
|
'type' => 'cape',
|
||||||
'pivot' => ['item_name' => 'custom_name'],
|
'pivot' => ['item_name' => 'custom_name'],
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
|
|
||||||
// Search by keyword
|
// Search by keyword
|
||||||
$random = $textures->random();
|
$random = $textures->random();
|
||||||
$this->getJson('/user/closet/list?q='.$random->name)
|
$this->getJson('/user/closet/list?q='.$random->name)
|
||||||
->assertJson(['data' => [[
|
->assertJson(['data' => [[
|
||||||
'tid' => $random->tid,
|
'tid' => $random->tid,
|
||||||
'name' => $random->name,
|
'name' => $random->name,
|
||||||
'type' => $random->type,
|
'type' => $random->type,
|
||||||
],
|
],
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use App\Models\Player;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class PlayerControllerTest extends TestCase
|
class PlayerControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
@ -430,13 +430,13 @@ class PlayerControllerTest extends TestCase
|
||||||
|
|
||||||
// success
|
// success
|
||||||
$this->deleteJson(route('user.player.clear', ['player' => $player]), [
|
$this->deleteJson(route('user.player.clear', ['player' => $player]), [
|
||||||
'skin' => true,
|
'skin' => true,
|
||||||
'cape' => true,
|
'cape' => true,
|
||||||
'nope' => true, // invalid texture type is acceptable
|
'nope' => true, // invalid texture type is acceptable
|
||||||
])->assertJson([
|
])->assertJson([
|
||||||
'code' => 0,
|
'code' => 0,
|
||||||
'message' => trans('user.player.clear.success', ['name' => $player->name]),
|
'message' => trans('user.player.clear.success', ['name' => $player->name]),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals(0, Player::find($player->pid)->tid_skin);
|
$this->assertEquals(0, Player::find($player->pid)->tid_skin);
|
||||||
$this->assertEquals(0, Player::find($player->pid)->tid_cape);
|
$this->assertEquals(0, Player::find($player->pid)->tid_cape);
|
||||||
Event::assertDispatched(Events\PlayerProfileUpdated::class);
|
Event::assertDispatched(Events\PlayerProfileUpdated::class);
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ namespace Tests;
|
||||||
use App\Models\Player;
|
use App\Models\Player;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class PlayersManagementControllerTest extends TestCase
|
class PlayersManagementControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class ReportControllerTest extends TestCase
|
class ReportControllerTest extends TestCase
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Intervention\Image\Image;
|
||||||
|
|
||||||
class SkinlibControllerTest extends TestCase
|
class SkinlibControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
@ -303,6 +304,22 @@ class SkinlibControllerTest extends TestCase
|
||||||
'type' => 'steve',
|
'type' => 'steve',
|
||||||
])->assertJsonValidationErrors('public');
|
])->assertJsonValidationErrors('public');
|
||||||
|
|
||||||
|
// too wide texture
|
||||||
|
option(['max_texture_width' => 128]);
|
||||||
|
$this->postJson(route('texture.upload'), [
|
||||||
|
'name' => 'texture',
|
||||||
|
'file' => UploadedFile::fake()->image('wide.png', 256, 256),
|
||||||
|
'type' => 'steve',
|
||||||
|
'public' => true,
|
||||||
|
])->assertJson([
|
||||||
|
'code' => 1,
|
||||||
|
'message' => trans('skinlib.upload.too-wide', [
|
||||||
|
'width' => 256,
|
||||||
|
'maxWidth' => 128,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
option(['max_texture_width' => 8192]);
|
||||||
|
|
||||||
// invalid skin size
|
// invalid skin size
|
||||||
$this->postJson(route('texture.upload'), [
|
$this->postJson(route('texture.upload'), [
|
||||||
'name' => 'texture',
|
'name' => 'texture',
|
||||||
|
|
@ -437,7 +454,7 @@ class SkinlibControllerTest extends TestCase
|
||||||
'uploaded_texture_hash',
|
'uploaded_texture_hash',
|
||||||
function ($hash, $file) use ($texture) {
|
function ($hash, $file) use ($texture) {
|
||||||
$this->assertEquals($texture->hash, $hash);
|
$this->assertEquals($texture->hash, $hash);
|
||||||
$this->assertInstanceOf(UploadedFile::class, $file);
|
$this->assertInstanceOf(Image::class, $file);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -445,7 +462,7 @@ class SkinlibControllerTest extends TestCase
|
||||||
Event::assertDispatched(
|
Event::assertDispatched(
|
||||||
'texture.uploading',
|
'texture.uploading',
|
||||||
function ($eventName, $payload) use ($texture) {
|
function ($eventName, $payload) use ($texture) {
|
||||||
$this->assertInstanceOf(UploadedFile::class, $payload[0]);
|
$this->assertInstanceOf(Image::class, $payload[0]);
|
||||||
$this->assertEquals($texture->name, $payload[1]);
|
$this->assertEquals($texture->name, $payload[1]);
|
||||||
$this->assertEquals($texture->hash, $payload[2]);
|
$this->assertEquals($texture->hash, $payload[2]);
|
||||||
|
|
||||||
|
|
@ -456,7 +473,7 @@ class SkinlibControllerTest extends TestCase
|
||||||
'texture.uploaded',
|
'texture.uploaded',
|
||||||
function ($eventName, $payload) use ($texture) {
|
function ($eventName, $payload) use ($texture) {
|
||||||
$this->assertTrue($texture->is($payload[0]));
|
$this->assertTrue($texture->is($payload[0]));
|
||||||
$this->assertInstanceOf(UploadedFile::class, $payload[1]);
|
$this->assertInstanceOf(Image::class, $payload[1]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -480,15 +497,15 @@ class SkinlibControllerTest extends TestCase
|
||||||
$texture->uploader = $user->uid;
|
$texture->uploader = $user->uid;
|
||||||
$texture->save();
|
$texture->save();
|
||||||
$this->postJson(route('texture.upload'), [
|
$this->postJson(route('texture.upload'), [
|
||||||
'name' => 'texture',
|
'name' => 'texture',
|
||||||
'public' => true,
|
'public' => true,
|
||||||
'type' => 'steve',
|
'type' => 'steve',
|
||||||
'file' => $upload,
|
'file' => $upload,
|
||||||
])->assertJson([
|
])->assertJson([
|
||||||
'code' => 2,
|
'code' => 2,
|
||||||
'message' => trans('skinlib.upload.repeated'),
|
'message' => trans('skinlib.upload.repeated'),
|
||||||
'data' => ['tid' => $texture->tid],
|
'data' => ['tid' => $texture->tid],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// rejected
|
// rejected
|
||||||
$filter->add('can_upload_texture', function ($can, $file, $name) {
|
$filter->add('can_upload_texture', function ($can, $file, $name) {
|
||||||
|
|
@ -498,11 +515,11 @@ class SkinlibControllerTest extends TestCase
|
||||||
return new Rejection('rejected');
|
return new Rejection('rejected');
|
||||||
});
|
});
|
||||||
$this->postJson(route('texture.upload'), [
|
$this->postJson(route('texture.upload'), [
|
||||||
'name' => 'texture',
|
'name' => 'texture',
|
||||||
'public' => true,
|
'public' => true,
|
||||||
'type' => 'steve',
|
'type' => 'steve',
|
||||||
'file' => $upload,
|
'file' => $upload,
|
||||||
])->assertJson(['code' => 1, 'message' => 'rejected']);
|
])->assertJson(['code' => 1, 'message' => 'rejected']);
|
||||||
|
|
||||||
$disk->delete($texture->hash);
|
$disk->delete($texture->hash);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ use App\Models\Player;
|
||||||
use App\Models\Texture;
|
use App\Models\Texture;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Blessing\Minecraft;
|
use Blessing\Minecraft;
|
||||||
use Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Image;
|
use Intervention\Image\Facades\Image;
|
||||||
|
|
||||||
class TextureControllerTest extends TestCase
|
class TextureControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use App\Models\User;
|
||||||
use Blessing\Filter;
|
use Blessing\Filter;
|
||||||
use Blessing\Rejection;
|
use Blessing\Rejection;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||||
|
|
@ -489,12 +489,12 @@ class UserControllerTest extends TestCase
|
||||||
// Administrator cannot be deleted
|
// Administrator cannot be deleted
|
||||||
$this->actingAs(User::factory()->admin()->create())
|
$this->actingAs(User::factory()->admin()->create())
|
||||||
->postJson('/user/profile', [
|
->postJson('/user/profile', [
|
||||||
'action' => 'delete',
|
'action' => 'delete',
|
||||||
'password' => '87654321',
|
'password' => '87654321',
|
||||||
])->assertJson([
|
])->assertJson([
|
||||||
'code' => 1,
|
'code' => 1,
|
||||||
'message' => trans('user.profile.delete.admin'),
|
'message' => trans('user.profile.delete.admin'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetAvatar()
|
public function testSetAvatar()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace Tests;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
class CheckRole extends TestCase
|
class CheckRoleTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testHandle()
|
public function testHandle()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class FireUserAuthenticatedTest extends TestCase
|
class FireUserAuthenticatedTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ namespace Tests;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Translations\JavaScript;
|
use App\Services\Translations\JavaScript;
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Symfony\Component\DomCrawler\Crawler;
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
|
||||||
class FootComposerTest extends TestCase
|
class FootComposerTest extends TestCase
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use Event;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Symfony\Component\DomCrawler\Crawler;
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
|
||||||
class HeadComposerTest extends TestCase
|
class HeadComposerTest extends TestCase
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use App\Events;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Plugin;
|
use App\Services\Plugin;
|
||||||
use App\Services\PluginManager;
|
use App\Services\PluginManager;
|
||||||
use Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Symfony\Component\DomCrawler\Crawler;
|
use Symfony\Component\DomCrawler\Crawler;
|
||||||
|
|
||||||
class SideMenuComposerTest extends TestCase
|
class SideMenuComposerTest extends TestCase
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
class UserPanelComposerTest extends TestCase
|
class UserPanelComposerTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use App\Events;
|
||||||
use App\Services\Option;
|
use App\Services\Option;
|
||||||
use App\Services\Plugin;
|
use App\Services\Plugin;
|
||||||
use App\Services\PluginManager;
|
use App\Services\PluginManager;
|
||||||
use Event;
|
|
||||||
use Illuminate\Filesystem\Filesystem;
|
use Illuminate\Filesystem\Filesystem;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
class PluginManagerTest extends TestCase
|
class PluginManagerTest extends TestCase
|
||||||
|
|
@ -425,7 +425,7 @@ class PluginManagerTest extends TestCase
|
||||||
->with('/mayaka/callbacks.php')
|
->with('/mayaka/callbacks.php')
|
||||||
->once()
|
->once()
|
||||||
->andReturn([
|
->andReturn([
|
||||||
\App\Events\PluginWasDeleted::class => function ($plugin) {
|
Events\PluginWasDeleted::class => function ($plugin) {
|
||||||
$this->assertInstanceOf(Plugin::class, $plugin);
|
$this->assertInstanceOf(Plugin::class, $plugin);
|
||||||
$this->assertEquals('mayaka', $plugin->name);
|
$this->assertEquals('mayaka', $plugin->name);
|
||||||
},
|
},
|
||||||
|
|
@ -434,7 +434,7 @@ class PluginManagerTest extends TestCase
|
||||||
|
|
||||||
app()->forgetInstance(PluginManager::class);
|
app()->forgetInstance(PluginManager::class);
|
||||||
resolve(PluginManager::class)->boot();
|
resolve(PluginManager::class)->boot();
|
||||||
event(new \App\Events\PluginWasDeleted(new Plugin('/mayaka', ['name' => 'mayaka'])));
|
event(new Events\PluginWasDeleted(new Plugin('/mayaka', ['name' => 'mayaka'])));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRegisterAutoload()
|
public function testRegisterAutoload()
|
||||||
|
|
@ -598,7 +598,7 @@ class PluginManagerTest extends TestCase
|
||||||
$this->assertTrue($manager->getEnabledPlugins()->has('fake'));
|
$this->assertTrue($manager->getEnabledPlugins()->has('fake'));
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'fake',
|
'fake',
|
||||||
json_decode(resolve(\App\Services\Option::class)->get('plugins_enabled'), true)[0]['name']
|
json_decode(resolve(Option::class)->get('plugins_enabled'), true)[0]['name']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertTrue($manager->enable('dep')['unsatisfied']->isNotEmpty());
|
$this->assertTrue($manager->enable('dep')['unsatisfied']->isNotEmpty());
|
||||||
|
|
@ -624,7 +624,7 @@ class PluginManagerTest extends TestCase
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
$this->assertFalse($manager->getEnabledPlugins()->has('fake'));
|
$this->assertFalse($manager->getEnabledPlugins()->has('fake'));
|
||||||
$this->assertCount(0, json_decode(resolve(\App\Services\Option::class)->get('plugins_enabled'), true));
|
$this->assertCount(0, json_decode(resolve(Option::class)->get('plugins_enabled'), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDelete()
|
public function testDelete()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user