Compare commits

..

1 Commits

Author SHA1 Message Date
Zephyr Lykos
178664ed83
refactor: migrate toolchain, part 1
- Switched to pnpm
- Updated to typescript 4.9.5
- JSX transforms switched to react-jsx
- Use swc for typescript transforms
- Remove deprecated dependencies
- Cleanup unused types
- Some ESM fixes
- PostCSS config now lives in package.json
- Bump support target

TODO:
- switch to vite or plain esbuild
- jest to vitest / uvu
- jquery to cash-dom
- pure ESM (lodash-es, etc.)
- drop prettier & husky (lint-staged is okay)
- drop wasm cli & xterm.js
- update bootstrap
2023-07-10 19:58:18 +08:00
187 changed files with 13839 additions and 12707 deletions

View File

@ -20,19 +20,20 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 8.3 php-version: 8.1
coverage: none coverage: none
extensions: mbstring, dom, fileinfo, gd, imagick extensions: mbstring, dom, fileinfo, gd
- 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
@ -44,24 +45,24 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: ['8.2', '8.3'] php: ['8.1', '8.2']
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup PHP only - name: Setup PHP only
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
if: matrix.php != '8.3' if: matrix.php != '8.0'
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
coverage: none coverage: none
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick extensions: mbstring, dom, fileinfo, sqlite, gd, zip
- name: Setup PHP with Xdebug - name: Setup PHP with Xdebug
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
if: matrix.php == '8.3' if: matrix.php == '8.0'
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
coverage: xdebug coverage: xdebug
extensions: mbstring, dom, fileinfo, sqlite, gd, zip, imagick extensions: mbstring, dom, fileinfo, sqlite, gd, zip
- name: Cache Composer dependencies - name: Cache Composer dependencies
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@ -71,14 +72,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.3' if: matrix.php != '8.0'
run: ./vendor/bin/phpunit run: ./vendor/bin/phpunit
- name: Run tests with coverage report - name: Run tests with coverage report
if: matrix.php == '8.3' if: matrix.php == '8.0'
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.3' && success() if: matrix.php == '8.0' && success()
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
name: github-actions name: github-actions
@ -87,7 +88,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: yarn install --frozen-lockfile run: yarn install --frozen-lockfile
- name: Run checks - name: Run checks
@ -100,7 +101,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: yarn run: yarn
- name: Run tests - name: Run tests
@ -114,14 +115,8 @@ 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@v4 uses: actions/checkout@v3
- name: Cache Node dependencies - name: Cache Node dependencies
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@ -143,13 +138,11 @@ 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@v3.0 - uses: benjlevesque/short-sha@v1.2
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@v4 uses: actions/upload-artifact@v3
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

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v3
- 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
View File

@ -25,5 +25,3 @@ 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
View File

@ -1,34 +1,38 @@
{ {
"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": ["<node_internals>/**"] "skipFiles": [
}, "<node_internals>/**"
{ ]
"type": "php", },
"request": "launch", {
"name": "Launch with XDebug", "type": "php",
"ignore": ["**/vendor/**/*.php"] "request": "launch",
}, "name": "Launch with XDebug",
{ "ignore": [
"type": "firefox", "**/vendor/**/*.php"
"request": "launch", ]
"reAttach": true, },
"name": "Launch with Firefox Debugger", {
"url": "http://localhost/", "type": "firefox",
"webRoot": "${workspaceFolder}", "request": "launch",
"pathMappings": [ "reAttach": true,
{ "name": "Launch with Firefox Debugger",
"url": "webpack:///", "url": "http://localhost/",
"path": "${workspaceFolder}/" "webRoot": "${workspaceFolder}",
} "pathMappings": [
] {
} "url": "webpack:///",
] "path": "${workspaceFolder}/"
}
]
}
]
} }

View File

@ -21,7 +21,7 @@ WORKDIR /app
COPY package.json yarn.lock ./ COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile RUN yarn install --frozen-lockfile
COPY postcss.config.js tsconfig.build.json tsconfig.json webpack.config.ts ./ COPY tsconfig.build.json tsconfig.json webpack.config.ts ./
COPY tools/*Plugin.ts ./tools/ COPY tools/*Plugin.ts ./tools/
COPY resources ./resources COPY resources ./resources

View File

@ -52,7 +52,6 @@ Blessing Skin 对您的服务器有一定的要求。在大多数情况下,下
- JSON - JSON
- fileinfo - fileinfo
- zip - zip
- Imagick
## 快速使用 ## 快速使用
@ -62,9 +61,105 @@ 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.app/build.html)。 详情可阅读 [这里](https://blessing.netlify.com/build.html)。
> 您可以订阅我们的 Telegram 频道 [Blessing Skin News](https://t.me/blessing_skin_news) 来获取最新开发动态。当有新的 Commit 被推送时,我们的机器人将会在频道内发送一条消息来提示您能否拉取最新代码,以及拉取后应该做什么。 > 您可以订阅我们的 Telegram 频道 [Blessing Skin News](https://t.me/blessing_skin_news) 来获取最新开发动态。当有新的 Commit 被推送时,我们的机器人将会在频道内发送一条消息来提示您能否拉取最新代码,以及拉取后应该做什么。
@ -76,7 +171,7 @@ Blessing Skin 可支持多种语言,当前支持英语、简体中文和西班
## 问题报告 ## 问题报告
请参阅 [报告问题的正确姿势](https://blessing.netlify.app/report.html)。 请参阅 [报告问题的正确姿势](https://blessing.netlify.com/report.html)。
## 相关链接 ## 相关链接

View File

@ -52,7 +52,6 @@ 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
@ -62,6 +61,102 @@ 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).

View File

@ -55,7 +55,6 @@ 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;
}) })

View File

@ -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([

View File

@ -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 Illuminate\Support\Facades\Auth; use Mail;
use Illuminate\Support\Facades\Cache; use Session;
use Illuminate\Support\Facades\Mail; use URL;
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',

View File

@ -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 */

View File

@ -163,10 +163,6 @@ 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'))

View File

@ -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', []);

View File

@ -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]);

View File

@ -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'])],

View File

@ -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')) {

View File

@ -4,6 +4,7 @@ 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;
@ -11,12 +12,10 @@ 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
{ {
@ -190,7 +189,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()) {
@ -221,16 +220,6 @@ 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'),
@ -264,17 +253,8 @@ class SkinlibController extends Controller
} }
} }
$image = Image::make($file); $hash = hash_file('sha256', $file);
$imagick = $image->getCore(); $hash = $filter->apply('uploaded_texture_hash', $hash, [$file]);
$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();
@ -290,11 +270,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]);
} }
$fileSize = ceil(strlen($sanitized) / 1024); $size = ceil($file->getSize() / 1024);
$isPublic = is_string($data['public']) $isPublic = is_string($data['public'])
? $data['public'] === '1' ? $data['public'] === '1'
: $data['public']; : $data['public'];
$cost = $fileSize * ( $cost = $size * (
$isPublic $isPublic
? option('score_per_storage') ? option('score_per_storage')
: option('private_score_per_storage') : option('private_score_per_storage')
@ -305,13 +285,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', [$image, $name, $hash]); $dispatcher->dispatch('texture.uploading', [$file, $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 = $fileSize; $texture->size = $size;
$texture->public = $isPublic; $texture->public = $isPublic;
$texture->uploader = $user->uid; $texture->uploader = $user->uid;
$texture->likes = 1; $texture->likes = 1;
@ -320,14 +300,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)) {
$disk->put($hash, $sanitized); $file->storePubliclyAs('', $hash, ['disk' => 'textures']);
} }
$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, $image]); $dispatcher->dispatch('texture.uploaded', [$texture, $file]);
return json(trans('skinlib.upload.success', ['name' => $name]), 0, [ return json(trans('skinlib.upload.success', ['name' => $name]), 0, [
'tid' => $texture->tid, 'tid' => $texture->tid,
@ -406,7 +386,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',
@ -436,7 +416,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'])],

View File

@ -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 Illuminate\Support\Facades\Cache; use Image;
use Illuminate\Support\Facades\Storage; use Storage;
use Intervention\Image\Facades\Image;
class TextureController extends Controller class TextureController extends Controller
{ {
@ -71,8 +71,7 @@ class TextureController extends Controller
$lastModified = $disk->lastModified($hash); $lastModified = $disk->lastModified($hash);
// TODO: refactor return Image::make($image)
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));
} }
@ -146,8 +145,7 @@ 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)) {
// TODO: refactor return Image::make(resource_path("misc/textures/avatar$mode.png"))
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);
} }
@ -167,8 +165,7 @@ class TextureController extends Controller
$lastModified = Carbon::createFromTimestamp($disk->lastModified($hash)); $lastModified = Carbon::createFromTimestamp($disk->lastModified($hash));
// TODO: refactor return Image::make($image)
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);

View File

@ -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();

View File

@ -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
{ {

View File

@ -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,
Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\ConvertEmptyStringsToNull::class,
Middleware\DetectLanguagePrefer::class, \App\Http\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,
Middleware\EnforceEverGreen::class, \App\Http\Middleware\EnforceEverGreen::class,
Middleware\RedirectToSetup::class, \App\Http\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',
Middleware\RejectBannedUser::class, \App\Http\Middleware\RejectBannedUser::class,
Middleware\EnsureEmailFilled::class, \App\Http\Middleware\EnsureEmailFilled::class,
Middleware\FireUserAuthenticated::class, \App\Http\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' => Middleware\Authenticate::class, 'auth' => \App\Http\Middleware\Authenticate::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'guest' => Middleware\RedirectIfAuthenticated::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'role' => Middleware\CheckRole::class, 'role' => \App\Http\Middleware\CheckRole::class,
'setup' => Middleware\CheckInstallation::class, 'setup' => \App\Http\Middleware\CheckInstallation::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => Middleware\CheckUserVerified::class, 'verified' => \App\Http\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,
]; ];

View File

@ -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)

View File

@ -28,9 +28,7 @@ class DetectLanguagePrefer
/** @var Response */ /** @var Response */
$response = $next($request); $response = $next($request);
if (!in_array('api', optional($request->route())->middleware() ?? [])) { $response->cookie('locale', $locale, 120);
$response->cookie('locale', $locale, 120);
}
return $response; return $response;
} }

View File

@ -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;

View File

@ -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;

View File

@ -3,7 +3,7 @@
namespace App\Listeners; namespace App\Listeners;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Event; use Event;
class NotifyFailedPlugin class NotifyFailedPlugin
{ {

View File

@ -4,7 +4,6 @@ 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
@ -27,13 +26,4 @@ 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',
]
);
}
} }

View File

@ -4,7 +4,6 @@ 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
@ -27,13 +26,4 @@ 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',
]
);
}
} }

View File

@ -3,6 +3,7 @@
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;
@ -56,17 +57,17 @@ class Player extends Model
public function user() public function user()
{ {
return $this->belongsTo(User::class, 'uid'); return $this->belongsTo(Models\User::class, 'uid');
} }
public function skin() public function skin()
{ {
return $this->belongsTo(Texture::class, 'tid_skin'); return $this->belongsTo(Models\Texture::class, 'tid_skin');
} }
public function cape() public function cape()
{ {
return $this->belongsTo(Texture::class, 'tid_cape'); return $this->belongsTo(Models\Texture::class, 'tid_cape');
} }
public function getModelAttribute() public function getModelAttribute()

View File

@ -2,10 +2,8 @@
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
@ -41,19 +39,7 @@ 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 arent run yet, so DB queries will fail.
* OAuth isnt 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));

View File

@ -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(['auth', 'verified']); $route->middleware('verified');
} }
} }

View File

@ -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
{ {

View File

@ -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,14 +20,13 @@ class Option
return; return;
} }
if (!file_exists(storage_path('install.lock')) || app()->runningUnitTests()) { try {
$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)

View File

@ -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();

View File

@ -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') {

View File

@ -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;

View File

@ -51,7 +51,6 @@ ini_set('display_errors', true);
'json', 'json',
'fileinfo', 'fileinfo',
'zip', 'zip',
'imagick',
], ],
'write_permission' => [ 'write_permission' => [
'bootstrap/cache', 'bootstrap/cache',

View File

@ -6,7 +6,6 @@
"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": "*",

3833
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
<?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'
];

View File

@ -33,7 +33,7 @@ return [
*/ */
'registry' => env( 'registry' => env(
'PLUGINS_REGISTRY', 'PLUGINS_REGISTRY',
'https://bs-plugins.littleservice.cn/registry_{lang}.json' 'https://git.qvq.network/bs-community/plugins-dist/-/raw/master/registry_{lang}.json'
), ),
/* /*

View File

@ -2,7 +2,6 @@
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
{ {

View File

@ -1,8 +1,6 @@
<?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
{ {

View File

@ -2,7 +2,6 @@
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
{ {

View File

@ -1,21 +0,0 @@
<?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
{
}
};

View File

@ -6,6 +6,7 @@
"type": "git", "type": "git",
"url": "https://github.com/bs-community/blessing-skin-server" "url": "https://github.com/bs-community/blessing-skin-server"
}, },
"type": "module",
"author": "printempw", "author": "printempw",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
@ -21,92 +22,86 @@
"prepare": "husky install" "prepare": "husky install"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.0.0", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.0.0", "@emotion/styled": "^11.11.0",
"@fortawesome/fontawesome-free": "^6.3.0", "@fortawesome/fontawesome-free": "^6.4.0",
"@hot-loader/react-dom": "^17.0.0", "@hot-loader/react-dom": "^17.0.2",
"@tweenjs/tween.js": "^18.5.0", "@tweenjs/tween.js": "^21.0.0",
"admin-lte": "^3.2.0", "admin-lte": "^3.2.0",
"blessing-skin-shell": "^0.3.4", "blessing-skin-shell": "^0.3.4",
"bootstrap": "^4.6.1", "bootstrap": "^4.6.2",
"cac": "6.6.1", "cac": "6.7.14",
"cli-spinners": "^2.5.0", "cli-spinners": "^2.9.0",
"clsx": "^1.1.1", "clsx": "^1.2.1",
"echarts": "^5.1.2", "echarts": "^5.4.2",
"events": "^3.2.0", "immer": "^10.0.2",
"immer": "^7.0.4", "jquery": "^3.7.0",
"jquery": "^3.6.0",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"nanoid": "^3.1.9", "nanoid": "^4.0.2",
"prompts": "^2.4.0", "prompts": "^2.4.2",
"react": "^17.0.1", "react": "^17.0.2",
"react-autosuggest": "^10.0.2", "react-autosuggest": "^10.1.0",
"react-dom": "^17.0.1", "react-dom": "^17.0.2",
"react-draggable": "^4.4.2", "react-draggable": "^4.4.5",
"react-hot-loader": "^4.12.21", "react-hot-loader": "^4.13.1",
"react-loading-skeleton": "^2.1.1", "react-loading-skeleton": "^3.3.1",
"react-use": "^17.4.0", "react-use": "^17.4.0",
"reaptcha": "^1.7.2", "reaptcha": "^1.12.1",
"rxjs": "^6.5.5", "rxjs": "^7.8.1",
"skinview-utils": "^0.5.5", "skinview-utils": "^0.7.0",
"skinview3d": "^3.0.0-alpha.1", "skinview3d": "3.0.0-alpha.1",
"spectre.css": "^0.5.8", "spectre.css": "^0.5.9",
"use-immer": "^0.4.2", "use-immer": "^0.9.0",
"xterm": "^4.6.0", "xterm": "^5.2.1",
"xterm-addon-fit": "^0.4.0" "xterm-addon-fit": "^0.7.0"
}, },
"devDependencies": { "devDependencies": {
"@gplane/tsconfig": "^4.2.0", "@gplane/tsconfig": "^5.0.0",
"@testing-library/jest-dom": "^5.11.10", "@swc/register": "^0.1.10",
"@testing-library/react": "^11.2.6", "@testing-library/jest-dom": "^5.16.5",
"@types/bootstrap": "^4.3.3", "@testing-library/react": "^12.1.5",
"@types/css-minimizer-webpack-plugin": "^1.1.0", "@types/bootstrap": "^5.2.6",
"@types/jest": "^26.0.23", "@types/jest": "^29.5.2",
"@types/jquery": "^3.5.13", "@types/jquery": "^3.5.16",
"@types/js-yaml": "^3.12.4", "@types/js-yaml": "^4.0.5",
"@types/lodash.debounce": "^4.0.6", "@types/lodash.debounce": "^4.0.7",
"@types/mini-css-extract-plugin": "^1.2.1", "@types/prompts": "^2.4.4",
"@types/prompts": "^2.0.9", "@types/react": "17",
"@types/react": "^16.9.35", "@types/react-autosuggest": "^10.1.6",
"@types/react-autosuggest": "^9.3.14", "@types/react-dom": "^17.0.20",
"@types/react-dom": "^16.9.8", "@types/testing-library__jest-dom": "^5.14.7",
"@types/tween.js": "^18.5.0", "@typescript-eslint/eslint-plugin": "^5.61.0",
"@types/webpack-dev-server": "^3.11.0", "@typescript-eslint/parser": "^5.61.0",
"@typescript-eslint/eslint-plugin": "^3.6.0", "autoprefixer": "^10.4.14",
"@typescript-eslint/parser": "^3.6.0", "css-loader": "^6.8.1",
"autoprefixer": "^10.2.6", "css-minimizer-webpack-plugin": "^5.0.1",
"css-loader": "^5.2.6", "eslint": "^8.44.0",
"css-minimizer-webpack-plugin": "^3.0.1",
"eslint": "^7.4.0",
"eslint-formatter-beauty": "^3.0.0", "eslint-formatter-beauty": "^3.0.0",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.6.0",
"html-webpack-plugin": "^5.3.1", "html-webpack-plugin": "^5.5.3",
"husky": "^7.0.4", "husky": "^8.0.3",
"jest": "^27.0.4", "jest": "^29.6.1",
"jest-extended": "^0.11.5", "jest-environment-jsdom": "^29.6.1",
"js-yaml": "^3.13.1", "jest-extended": "^4.0.0",
"mini-css-extract-plugin": "^1.6.0", "js-yaml": "^4.1.0",
"postcss": "^8.3.0", "mini-css-extract-plugin": "^2.7.6",
"postcss-loader": "^5.3.0", "postcss": "^8.4.25",
"prettier": "^2.3.0", "postcss-loader": "^7.3.3",
"prettier": "^2.8.8",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.1.3",
"style-loader": "^2.0.0", "style-loader": "^3.3.3",
"ts-jest": "^27.0.2", "ts-jest": "^29.1.1",
"ts-loader": "^9.2.2", "ts-loader": "^9.4.4",
"ts-node": "^10.0.0", "typescript": "^4.9.5",
"typescript": "^4.3.2", "webpack": "^5.88.1",
"webpack": "^5.38.1", "webpack-cli": "^5.1.4",
"webpack-cli": "^4.7.0", "webpack-dev-server": "^4.15.1"
"webpack-dev-server": "^3.11.2"
},
"resolutions": {
"kleur": "^4.1.3"
}, },
"browserslist": [ "browserslist": [
"> 1%", "Chrome >= 87",
"not dead", "Firefox >= 84",
"not ie 11", "iOS >= 12.5",
"Chrome > 52" "not dead"
], ],
"prettier": { "prettier": {
"printWidth": 80, "printWidth": 80,
@ -115,17 +110,13 @@
"trailingComma": "all", "trailingComma": "all",
"tabWidth": 2 "tabWidth": 2
}, },
"postcss": {
"autoprefixer": {}
},
"jest": { "jest": {
"preset": "ts-jest", "preset": "ts-jest",
"resetMocks": true, "resetMocks": true,
"testEnvironment": "jsdom", "testEnvironment": "jsdom",
"moduleFileExtensions": [
"js",
"ts",
"tsx",
"json",
"node"
],
"moduleNameMapper": { "moduleNameMapper": {
"\\.css$": "<rootDir>/resources/assets/tests/__mocks__/style.ts", "\\.css$": "<rootDir>/resources/assets/tests/__mocks__/style.ts",
"\\.(png|webp)$": "<rootDir>/resources/assets/tests/__mocks__/file.ts", "\\.(png|webp)$": "<rootDir>/resources/assets/tests/__mocks__/file.ts",
@ -158,6 +149,5 @@
"isolatedModules": true "isolatedModules": true
} }
} }
}, }
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
} }

11664
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
module.exports = {
plugins: [
require('autoprefixer'),
],
}

View File

@ -1,5 +1,3 @@
import React from 'react'
type AlertType = 'success' | 'info' | 'warning' | 'danger' type AlertType = 'success' | 'info' | 'warning' | 'danger'
const icons = new Map<AlertType, string>([ const icons = new Map<AlertType, string>([

View File

@ -1,5 +1,3 @@
import React from 'react'
interface Props { interface Props {
title?: string title?: string
onClick: React.MouseEventHandler<HTMLAnchorElement> onClick: React.MouseEventHandler<HTMLAnchorElement>

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import { useState } from 'react'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
interface Props { interface Props {

View File

@ -1,5 +1,5 @@
/** @jsxImportSource @emotion/react */ /** @jsxImportSource @emotion/react */
import React, { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import Autosuggest from 'react-autosuggest' import Autosuggest from 'react-autosuggest'
import { css } from '@emotion/react' import { css } from '@emotion/react'
import { emit } from '@/scripts/event' import { emit } from '@/scripts/event'

View File

@ -1,5 +1,3 @@
import React from 'react'
const Loading = () => ( const Loading = () => (
<div className="container text-center" title="Loading..."> <div className="container text-center" title="Loading...">
<i className="fas fa-sync fa-spin"></i> <i className="fas fa-sync fa-spin"></i>

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react' import { useState, useEffect, useRef } from 'react'
import $ from 'jquery' import $ from 'jquery'
import 'bootstrap' import 'bootstrap'
import { t } from '../scripts/i18n' import { t } from '../scripts/i18n'
@ -66,9 +66,14 @@ const Modal: React.FC<ModalOptions & Props> = (props) => {
return return
} }
const onHidden = () => props.onClose?.() const onClose =
props.onClose ||
(() => {
/* noop */
})
const onHidden = () => onClose()
const el = $(ref.current!) const el = $(ref.current as HTMLElement)
el.on('hidden.bs.modal', onHidden) el.on('hidden.bs.modal', onHidden)
return () => { return () => {
@ -92,28 +97,27 @@ const Modal: React.FC<ModalOptions & Props> = (props) => {
} }
props.onConfirm?.({ value }) props.onConfirm?.({ value })
$(ref.current!).modal('hide') $(ref.current as HTMLElement).modal('hide')
// The "hidden.bs.modal" event can't be trigged automatically when testing. // The "hidden.bs.modal" event can't be trigged automatically when testing.
/* istanbul ignore next */
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
$(ref.current!).trigger('hidden.bs.modal') $(ref.current as HTMLElement).trigger('hidden.bs.modal')
} }
} }
const dismiss = () => { const dismiss = () => {
props.onDismiss?.() props.onDismiss?.()
$(ref.current!).modal('hide') $(ref.current as HTMLElement).modal('hide')
/* istanbul ignore next */
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
$(ref.current!).trigger('hidden.bs.modal') $(ref.current as HTMLElement).trigger('hidden.bs.modal')
} }
} }
useEffect(() => { useEffect(() => {
if (show) { if (show) {
setTimeout(() => $(ref.current!).modal('show'), 50) setTimeout(() => $(ref.current as HTMLElement).modal('show'), 50)
} }
}, [show]) }, [show])

View File

@ -1,4 +1,3 @@
import React from 'react'
import ModalContent from './ModalContent' import ModalContent from './ModalContent'
import ModalInput from './ModalInput' import ModalInput from './ModalInput'
import type { Props as ContentProps } from './ModalContent' import type { Props as ContentProps } from './ModalContent'

View File

@ -1,5 +1,3 @@
import React from 'react'
export interface Props { export interface Props {
text?: string text?: string
dangerousHTML?: string dangerousHTML?: string

View File

@ -1,5 +1,3 @@
import React from 'react'
export interface Props { export interface Props {
flexFooter?: boolean flexFooter?: boolean
okButtonText?: string okButtonText?: string

View File

@ -1,5 +1,3 @@
import React from 'react'
export interface Props { export interface Props {
title?: string title?: string
} }

View File

@ -1,4 +1,4 @@
import React, { HTMLAttributes } from 'react' import type { HTMLAttributes } from 'react'
export interface Props { export interface Props {
inputType?: string inputType?: string

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import PaginationItem from './PaginationItem' import PaginationItem from './PaginationItem'

View File

@ -1,5 +1,3 @@
import React from 'react'
interface Props { interface Props {
disabled?: boolean disabled?: boolean
active?: boolean active?: boolean

View File

@ -1,5 +1,5 @@
/** @jsxImportSource @emotion/react */ /** @jsxImportSource @emotion/react */
import React, { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { css } from '@emotion/react' import { css } from '@emotion/react'
export type ToastType = 'success' | 'info' | 'warning' | 'error' export type ToastType = 'success' | 'info' | 'warning' | 'error'

View File

@ -1,5 +1,5 @@
/** @jsxImportSource @emotion/react */ /** @jsxImportSource @emotion/react */
import React, { useState, useEffect, useRef } from 'react' import { useState, useEffect, useRef } from 'react'
import { useMeasure } from 'react-use' import { useMeasure } from 'react-use'
import { css } from '@emotion/react' import { css } from '@emotion/react'
import styled from '@emotion/styled' import styled from '@emotion/styled'

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
const ViewerSkeleton: React.FC = () => ( const ViewerSkeleton: React.FC = () => (

View File

@ -30,8 +30,7 @@ if (route) {
</React.Suspense> </React.Suspense>
</React.StrictMode> </React.StrictMode>
) )
const c = const c = document.querySelector(route.el)
typeof route.el === 'string' ? document.querySelector(route.el) : route.el
ReactDOM.render(<Root />, c) ReactDOM.render(<Root />, c)
} }
} }

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { Terminal } from 'xterm' import { Terminal } from 'xterm'
@ -57,10 +57,8 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
const terminal = new Terminal() const terminal = new Terminal()
const fitAddon = new FitAddon() const fitAddon = new FitAddon()
terminal.loadAddon(fitAddon) terminal.loadAddon(fitAddon)
terminal.setOption( terminal.options.fontFamily =
'fontFamily', 'Monaco, Consolas, "Roboto Mono", "Noto Sans", "Droid Sans Mono"'
'Monaco, Consolas, "Roboto Mono", "Noto Sans", "Droid Sans Mono"',
)
terminal.open(el) terminal.open(el)
fitAddon.fit() fitAddon.fit()
@ -83,6 +81,7 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
if (stack?.includes('outputHelp')) { if (stack?.includes('outputHelp')) {
terminal.writeln(data.replace(/\n/g, '\r\n')) terminal.writeln(data.replace(/\n/g, '\r\n'))
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
originalLogger(data, ...args) originalLogger(data, ...args)
} }
} }

View File

@ -22,7 +22,7 @@ export default async function pacman(stdio: Stdio, args: string[]) {
const { options } = program.parse(['', ''].concat(args), { run: false }) const { options } = program.parse(['', ''].concat(args), { run: false })
const opts: Options = options const opts: Options = options
/* istanbul ignore else */
if (opts.sync) { if (opts.sync) {
await install(opts.sync, stdio) await install(opts.sync, stdio)
} else if (opts.remove) { } else if (opts.remove) {

View File

@ -1,7 +1,6 @@
import type { Stdio } from 'blessing-skin-shell' import type { Stdio } from 'blessing-skin-shell'
import * as event from '../event' import * as event from '../event'
/* istanbul ignore next */
export function hackStdin() { export function hackStdin() {
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
return process.stdin return process.stdin
@ -24,7 +23,6 @@ export function hackStdin() {
} as NodeJS.ReadStream & { _off(): void } } as NodeJS.ReadStream & { _off(): void }
} }
/* istanbul ignore next */
export function hackStdout(stdio: Stdio) { export function hackStdout(stdio: Stdio) {
return { return {
write(msg: string) { write(msg: string) {

View File

@ -1,4 +1,3 @@
import * as React from 'react'
import * as ReactDOM from 'react-dom' import * as ReactDOM from 'react-dom'
import DarkModeButton from '@/components/DarkModeButton' import DarkModeButton from '@/components/DarkModeButton'

View File

@ -1,4 +1,3 @@
import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import EmailVerification from '@/views/widgets/EmailVerification' import EmailVerification from '@/views/widgets/EmailVerification'

View File

@ -1,6 +1,6 @@
export function getExtraData(): Record<string, any> { export function getExtraData(): Record<string, any> {
const jsonElement = document.querySelector('#blessing-extra') const jsonElement = document.querySelector('#blessing-extra')
/* istanbul ignore next */
if (jsonElement) { if (jsonElement) {
return JSON.parse(jsonElement.textContent ?? '{}') return JSON.parse(jsonElement.textContent ?? '{}')
} else { } else {

View File

@ -2,7 +2,7 @@ import { getExtraData } from './extra'
export function scrollHander() { export function scrollHander() {
const header = document.querySelector('.navbar') const header = document.querySelector('.navbar')
/* istanbul ignore else */
if (header) { if (header) {
window.addEventListener('scroll', () => { window.addEventListener('scroll', () => {
if (window.scrollY >= (window.innerHeight * 2) / 3) { if (window.scrollY >= (window.innerHeight * 2) / 3) {
@ -14,7 +14,6 @@ export function scrollHander() {
} }
} }
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') { if (process.env.NODE_ENV !== 'test') {
const { transparent_navbar } = getExtraData() as { const { transparent_navbar } = getExtraData() as {
transparent_navbar: boolean transparent_navbar: boolean

View File

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import * as fetch from '../net' import * as fetch from '../net'
import { Texture, TextureType } from '../types' import { type Texture, TextureType } from '../types'
export default function useTexture() { export default function useTexture() {
const [tid, setTid] = useState(0) const [tid, setTid] = useState(0)

View File

@ -8,7 +8,6 @@ export function t(key: string, parameters = Object.create(null)): string {
let result = '' let result = ''
for (const segment of segments) { for (const segment of segments) {
/* istanbul ignore next */
const middle = temp?.[segment] const middle = temp?.[segment]
if (!middle) { if (!middle) {
return key return key
@ -20,9 +19,11 @@ export function t(key: string, parameters = Object.create(null)): string {
} }
} }
/* eslint-disable @typescript-eslint/no-unsafe-argument */
Object.keys(parameters).forEach( Object.keys(parameters).forEach(
(slot) => (result = result.replace(`:${slot}`, parameters[slot])), (slot) => (result = result.replace(`:${slot}`, parameters[slot])),
) )
/* eslint-enable @typescript-eslint/no-unsafe-argument */
return result return result
} }

View File

@ -18,5 +18,5 @@ export async function logout() {
} }
const button = document.querySelector('#logout-button') const button = document.querySelector('#logout-button')
/* istanbul ignore next */
button?.addEventListener('click', logout) button?.addEventListener('click', logout)

View File

@ -1,6 +1,5 @@
import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import Modal, { ModalOptions, ModalResult } from '../components/Modal' import Modal, { type ModalOptions, type ModalResult } from '../components/Modal'
export function showModal(options: ModalOptions = {}): Promise<ModalResult> { export function showModal(options: ModalOptions = {}): Promise<ModalResult> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -80,7 +80,7 @@ export async function walkFetch(request: Request): Promise<any> {
message = `${message}<br><details>${trace}</details>` message = `${message}<br><details>${trace}</details>`
} }
throw new HTTPError(message || body, cloned) throw new HTTPError(message || (body as string), cloned)
} catch (error: any) { } catch (error: any) {
emit('fetchError', error) emit('fetchError', error)
await showModal({ await showModal({
@ -95,7 +95,10 @@ export async function walkFetch(request: Request): Promise<any> {
} }
} }
export function get<T = any>(url: string, params = empty): Promise<T> { export function get<T = any>(
url: string,
params: Record<string, any> = empty,
): Promise<T> {
emit('beforeFetch', { emit('beforeFetch', {
method: 'GET', method: 'GET',
url, url,

View File

@ -1,4 +1,3 @@
import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import NotificationsList from '@/views/widgets/NotificationsList' import NotificationsList from '@/views/widgets/NotificationsList'

View File

@ -3,7 +3,6 @@ import { Toast } from './toast'
export const toast = new Toast() export const toast = new Toast()
/* istanbul ignore next */
if (process.env.NODE_ENV === 'test') { if (process.env.NODE_ENV === 'test') {
afterEach(() => { afterEach(() => {
toast.clear() toast.clear()

View File

@ -1,5 +1,3 @@
import React from 'react'
export default [ export default [
{ {
path: 'user', path: 'user',

View File

@ -1,6 +1,5 @@
import { loadSkinToCanvas } from 'skinview-utils' import { loadSkinToCanvas } from 'skinview-utils'
/* istanbul ignore next */
function checkPixel( function checkPixel(
context: CanvasRenderingContext2D, context: CanvasRenderingContext2D,
x: number, x: number,
@ -15,7 +14,6 @@ function checkPixel(
) )
} }
/* istanbul ignore next */
export function isAlex(texture: string): Promise<boolean> { export function isAlex(texture: string): Promise<boolean> {
return new Promise((resolve) => { return new Promise((resolve) => {
const image = new Image() const image = new Image()

View File

@ -1,8 +1,9 @@
import React, { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import * as emitter from './event' import * as emitter from './event'
import ToastBox, { ToastType } from '../components/Toast' import ToastBox from '../components/Toast'
import type { ToastType } from '../components/Toast'
type QueueElement = { id: string; type: ToastType; message: string } type QueueElement = { id: string; type: ToastType; message: string }
type ToastQueue = QueueElement[] type ToastQueue = QueueElement[]

View File

@ -38,9 +38,13 @@ export function registerNavbarPicker(
const navbar = document.querySelector<HTMLElement>('.wrapper > nav') const navbar = document.querySelector<HTMLElement>('.wrapper > nav')
const picker = document.querySelector<HTMLDivElement>('#navbar-color-picker') const picker = document.querySelector<HTMLDivElement>('#navbar-color-picker')
/* istanbul ignore next */
if (navbar && picker) { if (navbar && picker) {
registerNavbarPicker(navbar, picker, blessing.extra.navbar || 'white') registerNavbarPicker(
navbar,
picker,
(blessing.extra.navbar as string) || 'white',
)
} }
export function registerSidebarPicker( export function registerSidebarPicker(
@ -73,11 +77,10 @@ const darkPicker = document.querySelector<HTMLDivElement>(
const lightPicker = document.querySelector<HTMLDivElement>( const lightPicker = document.querySelector<HTMLDivElement>(
'#sidebar-light-picker', '#sidebar-light-picker',
) )
/* istanbul ignore next */
if (sidebar && darkPicker && lightPicker) { if (sidebar && darkPicker && lightPicker) {
registerSidebarPicker( registerSidebarPicker(
sidebar, sidebar,
{ dark: darkPicker, light: lightPicker }, { dark: darkPicker, light: lightPicker },
blessing.extra.sidebar || 'dark-primary', (blessing.extra.sidebar as string) || 'dark-primary',
) )
} }

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { showModal } from '@/scripts/notify' import { showModal } from '@/scripts/notify'
import type { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'

View File

@ -1,4 +1,3 @@
import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import Skeleton from 'react-loading-skeleton' import Skeleton from 'react-loading-skeleton'
import { Box } from './styles' import { Box } from './styles'

View File

@ -1,4 +1,3 @@
import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import Skeleton from 'react-loading-skeleton' import Skeleton from 'react-loading-skeleton'

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import { useState } from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { TextureType } from '@/scripts/types' import { TextureType } from '@/scripts/types'
import Modal from '@/components/Modal' import Modal from '@/components/Modal'

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import ButtonEdit from '@/components/ButtonEdit' import ButtonEdit from '@/components/ButtonEdit'

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useLayoutEffect } from 'react' import { useState, useEffect, useLayoutEffect } from 'react'
import { hot } from 'react-hot-loader/root' import { hot } from 'react-hot-loader/root'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'
import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen' import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen'
@ -47,6 +47,7 @@ const PlayersManagement: React.FC = () => {
useEffect(() => { useEffect(() => {
getPlayers() getPlayers()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page]) }, [page])
const handleModeChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleModeChange = (event: React.ChangeEvent<HTMLInputElement>) => {

View File

@ -1,4 +1,3 @@
import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { Plugin } from './types' import type { Plugin } from './types'

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { hot } from 'react-hot-loader/root' import { hot } from 'react-hot-loader/root'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { Plugin } from './types' import type { Plugin } from './types'

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react' import { useState, useEffect, useMemo } from 'react'
import { hot } from 'react-hot-loader/root' import { hot } from 'react-hot-loader/root'
import { enableMapSet } from 'immer' import { enableMapSet } from 'immer'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'

View File

@ -1,8 +1,7 @@
import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { Texture } from '@/scripts/types' import type { Texture } from '@/scripts/types'
import { Report, Status } from './types' import { type Report, Status } from './types'
const Card = styled.div` const Card = styled.div`
width: 240px; width: 240px;

View File

@ -3,7 +3,7 @@ import { hot } from 'react-hot-loader/root'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { Paginator, Texture, TextureType } from '@/scripts/types' import { type Paginator, type Texture, TextureType } from '@/scripts/types'
import { toast, showModal } from '@/scripts/notify' import { toast, showModal } from '@/scripts/notify'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
@ -37,6 +37,7 @@ const ReportsManagement: React.FC = () => {
useEffect(() => { useEffect(() => {
getReports() getReports()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page]) }, [page])
const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled' import styled from '@emotion/styled'
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { Line } from './types' import type { Line } from './types'

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { hot } from 'react-hot-loader/root' import { hot } from 'react-hot-loader/root'
import { useImmer } from 'use-immer' import { useImmer } from 'use-immer'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'

View File

@ -1,4 +1,4 @@
import { post, ResponseBody } from '../../scripts/net' import { post, type ResponseBody } from '../../scripts/net'
import { showModal } from '../../scripts/notify' import { showModal } from '../../scripts/notify'
import { t } from '../../scripts/i18n' import { t } from '../../scripts/i18n'

View File

@ -1,4 +1,3 @@
import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import type { User } from '@/scripts/types' import type { User } from '@/scripts/types'
import { Box, Icon, InfoTable } from './styles' import { Box, Icon, InfoTable } from './styles'

View File

@ -1,4 +1,3 @@
import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import Skeleton from 'react-loading-skeleton' import Skeleton from 'react-loading-skeleton'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'

Some files were not shown because too many files have changed in this diff Show More